agent: scheduler, tui control client, e2e and scheduler differential tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user