92 lines
3.8 KiB
C++
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;
|
|
}
|