214 lines
7.1 KiB
JavaScript
214 lines
7.1 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.InMemoryRuntimeAdapter = void 0;
|
|
const nanoid_1 = require("nanoid");
|
|
const http_1 = require("../utils/http");
|
|
class InMemoryRuntimeAdapter {
|
|
runs = [];
|
|
tasks = [];
|
|
traces = [];
|
|
idempotencyCache = new Map();
|
|
now() {
|
|
return new Date().toISOString();
|
|
}
|
|
cacheKey(action, key) {
|
|
if (!key || !key.trim())
|
|
return null;
|
|
return `${action}:${key.trim()}`;
|
|
}
|
|
readIdempotency(action, idempotencyKey) {
|
|
const cache = this.cacheKey(action, idempotencyKey);
|
|
if (!cache)
|
|
return null;
|
|
return this.idempotencyCache.get(cache) ?? null;
|
|
}
|
|
writeIdempotency(action, idempotencyKey, value) {
|
|
const cache = this.cacheKey(action, idempotencyKey);
|
|
if (!cache)
|
|
return;
|
|
this.idempotencyCache.set(cache, value);
|
|
}
|
|
pushEvent(input) {
|
|
this.traces.push({
|
|
timestamp: this.now(),
|
|
level: input.level ?? "info",
|
|
service: "llm_normalizer_backend",
|
|
sessionId: input.sessionId,
|
|
runId: input.runId,
|
|
taskId: input.taskId ?? null,
|
|
eventType: input.eventType,
|
|
payload: input.payload
|
|
});
|
|
}
|
|
startRun(input) {
|
|
const cached = this.readIdempotency("startRun", input.idempotencyKey);
|
|
if (cached)
|
|
return cached;
|
|
const record = {
|
|
sessionId: input.sessionId ?? `session_${(0, nanoid_1.nanoid)(8)}`,
|
|
runId: `run_${(0, nanoid_1.nanoid)(10)}`,
|
|
status: "RUNNING",
|
|
initiator: input.initiator ?? "operator",
|
|
source: input.source ?? "gui",
|
|
createdAt: this.now(),
|
|
updatedAt: this.now(),
|
|
metadata: input.metadata ?? {}
|
|
};
|
|
this.runs.unshift(record);
|
|
this.pushEvent({
|
|
runId: record.runId,
|
|
sessionId: record.sessionId,
|
|
eventType: "RUN_STARTED",
|
|
payload: record.metadata
|
|
});
|
|
this.writeIdempotency("startRun", input.idempotencyKey, record);
|
|
return record;
|
|
}
|
|
finishRun(input) {
|
|
const cached = this.readIdempotency("finishRun", input.idempotencyKey);
|
|
if (cached)
|
|
return cached;
|
|
const record = this.runs.find((item) => item.runId === input.runId);
|
|
if (!record) {
|
|
throw new http_1.ApiError("RUN_NOT_FOUND", `Run not found: ${input.runId}`, 404);
|
|
}
|
|
record.status = input.status;
|
|
record.updatedAt = this.now();
|
|
record.source = input.source ?? record.source;
|
|
record.metadata = {
|
|
...(record.metadata ?? {}),
|
|
...(input.metadata ?? {}),
|
|
reason: input.reason ?? null
|
|
};
|
|
this.pushEvent({
|
|
runId: record.runId,
|
|
sessionId: record.sessionId,
|
|
eventType: `RUN_FINISHED_${input.status}`,
|
|
level: input.status === "ERROR" ? "error" : "info",
|
|
payload: { reason: input.reason ?? null }
|
|
});
|
|
this.writeIdempotency("finishRun", input.idempotencyKey, record);
|
|
return record;
|
|
}
|
|
listRuns() {
|
|
return [...this.runs];
|
|
}
|
|
getRun(runId) {
|
|
return this.runs.find((item) => item.runId === runId) ?? null;
|
|
}
|
|
enqueueTask(input) {
|
|
const cached = this.readIdempotency("enqueueTask", input.idempotencyKey);
|
|
if (cached)
|
|
return cached;
|
|
const run = this.getRun(input.runId);
|
|
if (!run) {
|
|
throw new http_1.ApiError("RUN_NOT_FOUND", `Run not found: ${input.runId}`, 404);
|
|
}
|
|
const task = {
|
|
taskId: `task_${(0, nanoid_1.nanoid)(10)}`,
|
|
runId: run.runId,
|
|
status: "QUEUED",
|
|
payload: input.payload,
|
|
source: input.source ?? "gui",
|
|
createdAt: this.now(),
|
|
updatedAt: this.now()
|
|
};
|
|
this.tasks.unshift(task);
|
|
this.pushEvent({
|
|
runId: run.runId,
|
|
sessionId: run.sessionId,
|
|
taskId: task.taskId,
|
|
eventType: "TASK_ENQUEUED",
|
|
payload: task.payload
|
|
});
|
|
this.writeIdempotency("enqueueTask", input.idempotencyKey, task);
|
|
return task;
|
|
}
|
|
claimTask() {
|
|
const task = this.tasks.find((item) => item.status === "QUEUED");
|
|
if (!task) {
|
|
return null;
|
|
}
|
|
task.status = "RUNNING";
|
|
task.updatedAt = this.now();
|
|
const run = this.getRun(task.runId);
|
|
if (run) {
|
|
this.pushEvent({
|
|
runId: run.runId,
|
|
sessionId: run.sessionId,
|
|
taskId: task.taskId,
|
|
eventType: "TASK_CLAIMED"
|
|
});
|
|
}
|
|
return task;
|
|
}
|
|
completeTask(input) {
|
|
const cached = this.readIdempotency("completeTask", input.idempotencyKey);
|
|
if (cached)
|
|
return cached;
|
|
const task = this.tasks.find((item) => item.taskId === input.taskId);
|
|
if (!task) {
|
|
throw new http_1.ApiError("TASK_NOT_FOUND", `Task not found: ${input.taskId}`, 404);
|
|
}
|
|
task.status = "DONE";
|
|
task.updatedAt = this.now();
|
|
task.result = input.result;
|
|
task.source = input.source ?? task.source;
|
|
const run = this.getRun(task.runId);
|
|
if (run) {
|
|
this.pushEvent({
|
|
runId: run.runId,
|
|
sessionId: run.sessionId,
|
|
taskId: task.taskId,
|
|
eventType: "TASK_DONE",
|
|
payload: input.result
|
|
});
|
|
}
|
|
this.writeIdempotency("completeTask", input.idempotencyKey, task);
|
|
return task;
|
|
}
|
|
failTask(input) {
|
|
const cached = this.readIdempotency("failTask", input.idempotencyKey);
|
|
if (cached)
|
|
return cached;
|
|
const task = this.tasks.find((item) => item.taskId === input.taskId);
|
|
if (!task) {
|
|
throw new http_1.ApiError("TASK_NOT_FOUND", `Task not found: ${input.taskId}`, 404);
|
|
}
|
|
task.status = "ERROR";
|
|
task.updatedAt = this.now();
|
|
task.error = input.error;
|
|
task.source = input.source ?? task.source;
|
|
const run = this.getRun(task.runId);
|
|
if (run) {
|
|
this.pushEvent({
|
|
runId: run.runId,
|
|
sessionId: run.sessionId,
|
|
taskId: task.taskId,
|
|
eventType: "TASK_ERROR",
|
|
level: "error",
|
|
payload: {
|
|
errorCode: input.error.code,
|
|
errorMessage: input.error.message
|
|
}
|
|
});
|
|
}
|
|
this.writeIdempotency("failTask", input.idempotencyKey, task);
|
|
return task;
|
|
}
|
|
getResults() {
|
|
return this.tasks.filter((item) => item.status === "DONE" || item.status === "ERROR");
|
|
}
|
|
getRunTrace(runId) {
|
|
return this.traces.filter((item) => item.runId === runId);
|
|
}
|
|
health() {
|
|
return {
|
|
ok: true,
|
|
queueDepth: this.tasks.filter((item) => item.status === "QUEUED").length,
|
|
runsTotal: this.runs.length
|
|
};
|
|
}
|
|
}
|
|
exports.InMemoryRuntimeAdapter = InMemoryRuntimeAdapter;
|