Files
tai/taicpp/tests/agent_sched.cpp
T

92 lines
3.8 KiB
C++

// Scheduler driver: reads {"rows": [...]} from stdin, runs scheduler_tick over
// an in-memory schedules table with a stub runner and prints the final table.
#include "../src/agent/scheduler.h"
#include "../src/llm/py.h"
#include <chrono>
#include <iostream>
#include <thread>
using namespace detz;
using json::Value;
namespace {
struct Row {
int64_t id; std::string name, prompt, profile; int64_t every_sec; std::string next_run; int64_t timeout;
std::string status, last_status, last_result, updated;
};
class MemSchedulerDb : public agent::SchedulerDbPort {
public:
std::vector<Row> rows;
std::vector<agent::DueSchedule> select_due(const std::string& now_iso) override {
std::vector<agent::DueSchedule> out;
for (const auto& r : rows) {
if (r.status == "pending" && r.next_run <= now_iso) {
out.push_back({r.id, r.prompt, r.profile, r.every_sec, r.next_run, r.timeout, r.last_status, r.updated});
}
}
std::stable_sort(out.begin(), out.end(), [](const auto& a, const auto& b) { return a.next_run < b.next_run; });
return out;
}
bool claim_every(int64_t id, const std::string& new_next, const std::string& stamp, const std::string& expected) override {
for (auto& r : rows) {
if (r.id == id && r.status == "pending" && r.next_run == expected) {
r.next_run = new_next; r.updated = stamp; r.last_status = "running";
return true;
}
}
return false;
}
bool claim_once(int64_t id, const std::string& stamp) override {
for (auto& r : rows) {
if (r.id == id && r.status == "pending") { r.status = "done"; r.updated = stamp; return true; }
}
return false;
}
std::string unlock_prompt(const std::string& prompt) override {
if (prompt.rfind("SEALED:", 0) == 0) {
if (prompt == "SEALED:bad") { throw agent::SealFailure("cannot unlock"); }
return prompt.substr(7);
}
return prompt;
}
void release_every(int64_t id, const std::string& next_run, const std::string& stamp, const std::string& last_status) override {
for (auto& r : rows) { if (r.id == id) { r.next_run = next_run; r.updated = stamp; r.last_status = last_status; } }
}
void release_once(int64_t id, const std::string& stamp) override {
for (auto& r : rows) { if (r.id == id) { r.status = "pending"; r.updated = stamp; } }
}
void note_result(int64_t id, const std::string& status, const std::string& result) override {
for (auto& r : rows) { if (r.id == id) { r.last_status = status; r.last_result = result; r.updated = py::utc_now_iso(); } }
}
};
} // namespace
int main() {
std::string text((std::istreambuf_iterator<char>(std::cin)), std::istreambuf_iterator<char>());
Value in = Value::loads(text);
MemSchedulerDb db;
for (const auto& r : in.get("rows").items()) {
db.rows.push_back({r.get("id").as_int(), r.get("name").str(), r.get("prompt").str(), r.get("profile").str(), r.get("every_sec").as_int(),
r.get("next_run").str(), r.get("timeout").as_int(), "pending", r.get("last_status").str(), "", r.get("updated").str()});
}
agent::AgentConfig cfg;
int fired = 0;
agent::Runner runner = [](const std::string& task, const std::string&, double) { return "ran: " + task; };
fired = agent::scheduler_tick(cfg, db, runner);
// let the watcher threads record their outcome
for (int i = 0; i < 100 && agent::running_agents() > 0; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(20)); }
std::this_thread::sleep_for(std::chrono::milliseconds(300));
Value out = Value::object();
out.set("fired", fired);
Value rows = Value::array();
for (const auto& r : db.rows) {
Value o = Value::object();
o.set("id", r.id); o.set("status", r.status); o.set("next_run", r.next_run);
o.set("last_status", r.last_status); o.set("last_result", r.last_result);
rows.push(o);
}
out.set("rows", rows);
std::cout << out.dumps() << "\n";
return 0;
}