@axlsdk/axl 0.11.6 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +273 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -3
- package/dist/index.d.ts +59 -3
- package/dist/index.js +273 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2520,6 +2520,11 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
2520
2520
|
model: modelUri,
|
|
2521
2521
|
promptVersion: agent2._config.version,
|
|
2522
2522
|
cost: response.cost,
|
|
2523
|
+
tokens: response.usage ? {
|
|
2524
|
+
input: response.usage.prompt_tokens,
|
|
2525
|
+
output: response.usage.completion_tokens,
|
|
2526
|
+
reasoning: response.usage.reasoning_tokens
|
|
2527
|
+
} : void 0,
|
|
2523
2528
|
duration: Date.now() - startTime,
|
|
2524
2529
|
data: { prompt, response: response.content }
|
|
2525
2530
|
});
|
|
@@ -3907,6 +3912,8 @@ var MemoryStore = class {
|
|
|
3907
3912
|
decisions = /* @__PURE__ */ new Map();
|
|
3908
3913
|
executionStates = /* @__PURE__ */ new Map();
|
|
3909
3914
|
memories = /* @__PURE__ */ new Map();
|
|
3915
|
+
executionHistory = /* @__PURE__ */ new Map();
|
|
3916
|
+
evalHistory = /* @__PURE__ */ new Map();
|
|
3910
3917
|
constructor() {
|
|
3911
3918
|
this.loadPersistedState();
|
|
3912
3919
|
}
|
|
@@ -4003,11 +4010,36 @@ var MemoryStore = class {
|
|
|
4003
4010
|
async getAllMemory(scope) {
|
|
4004
4011
|
const scopeMap = this.memories.get(scope);
|
|
4005
4012
|
if (!scopeMap) return [];
|
|
4006
|
-
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4013
|
+
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4014
|
+
key,
|
|
4015
|
+
value: structuredClone(value)
|
|
4016
|
+
}));
|
|
4007
4017
|
}
|
|
4008
4018
|
async deleteMemory(scope, key) {
|
|
4009
4019
|
this.memories.get(scope)?.delete(key);
|
|
4010
4020
|
}
|
|
4021
|
+
// ── Execution History ──────────────────────────────────────────────
|
|
4022
|
+
async saveExecution(execution) {
|
|
4023
|
+
this.executionHistory.set(execution.executionId, structuredClone(execution));
|
|
4024
|
+
}
|
|
4025
|
+
async getExecution(executionId) {
|
|
4026
|
+
const exec = this.executionHistory.get(executionId);
|
|
4027
|
+
return exec ? structuredClone(exec) : null;
|
|
4028
|
+
}
|
|
4029
|
+
async listExecutions(limit) {
|
|
4030
|
+
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
4031
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4032
|
+
return result.map((e) => structuredClone(e));
|
|
4033
|
+
}
|
|
4034
|
+
// ── Eval History ──────────────────────────────────────────────────
|
|
4035
|
+
async saveEvalResult(entry) {
|
|
4036
|
+
this.evalHistory.set(entry.id, structuredClone(entry));
|
|
4037
|
+
}
|
|
4038
|
+
async listEvalResults(limit) {
|
|
4039
|
+
const sorted = [...this.evalHistory.values()].sort((a, b) => b.timestamp - a.timestamp);
|
|
4040
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4041
|
+
return result.map((e) => structuredClone(e));
|
|
4042
|
+
}
|
|
4011
4043
|
// ── Sessions (Studio introspection) ─────────────────────────────────
|
|
4012
4044
|
async listSessions() {
|
|
4013
4045
|
return [...this.sessions.keys()];
|
|
@@ -4067,6 +4099,19 @@ function safeJsonParse(data) {
|
|
|
4067
4099
|
return null;
|
|
4068
4100
|
}
|
|
4069
4101
|
}
|
|
4102
|
+
function rowToExecutionInfo(row) {
|
|
4103
|
+
return {
|
|
4104
|
+
executionId: row.execution_id,
|
|
4105
|
+
workflow: row.workflow,
|
|
4106
|
+
status: row.status,
|
|
4107
|
+
totalCost: row.total_cost,
|
|
4108
|
+
startedAt: row.started_at,
|
|
4109
|
+
completedAt: row.completed_at ?? void 0,
|
|
4110
|
+
duration: row.duration,
|
|
4111
|
+
error: row.error ?? void 0,
|
|
4112
|
+
steps: safeJsonParse(row.steps) ?? []
|
|
4113
|
+
};
|
|
4114
|
+
}
|
|
4070
4115
|
var SQLiteStore = class {
|
|
4071
4116
|
db;
|
|
4072
4117
|
constructor(filePath) {
|
|
@@ -4127,6 +4172,28 @@ var SQLiteStore = class {
|
|
|
4127
4172
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4128
4173
|
PRIMARY KEY (scope, key)
|
|
4129
4174
|
);
|
|
4175
|
+
|
|
4176
|
+
CREATE TABLE IF NOT EXISTS execution_history (
|
|
4177
|
+
execution_id TEXT PRIMARY KEY,
|
|
4178
|
+
workflow TEXT NOT NULL,
|
|
4179
|
+
status TEXT NOT NULL,
|
|
4180
|
+
total_cost REAL NOT NULL DEFAULT 0,
|
|
4181
|
+
started_at INTEGER NOT NULL,
|
|
4182
|
+
completed_at INTEGER,
|
|
4183
|
+
duration INTEGER NOT NULL DEFAULT 0,
|
|
4184
|
+
error TEXT,
|
|
4185
|
+
steps TEXT NOT NULL
|
|
4186
|
+
);
|
|
4187
|
+
|
|
4188
|
+
CREATE TABLE IF NOT EXISTS eval_history (
|
|
4189
|
+
id TEXT PRIMARY KEY,
|
|
4190
|
+
eval_name TEXT NOT NULL,
|
|
4191
|
+
timestamp INTEGER NOT NULL,
|
|
4192
|
+
data TEXT NOT NULL
|
|
4193
|
+
);
|
|
4194
|
+
|
|
4195
|
+
CREATE INDEX IF NOT EXISTS idx_exec_history_started ON execution_history (started_at DESC);
|
|
4196
|
+
CREATE INDEX IF NOT EXISTS idx_eval_history_timestamp ON eval_history (timestamp DESC);
|
|
4130
4197
|
`);
|
|
4131
4198
|
}
|
|
4132
4199
|
// ── Checkpoints ────────────────────────────────────────────────────────
|
|
@@ -4240,6 +4307,47 @@ var SQLiteStore = class {
|
|
|
4240
4307
|
const rows = stmt.all();
|
|
4241
4308
|
return rows.map((r) => r.execution_id);
|
|
4242
4309
|
}
|
|
4310
|
+
// ── Execution History ────────────────────────────────────────────────────
|
|
4311
|
+
async saveExecution(execution) {
|
|
4312
|
+
this.db.prepare(
|
|
4313
|
+
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, steps) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4314
|
+
).run(
|
|
4315
|
+
execution.executionId,
|
|
4316
|
+
execution.workflow,
|
|
4317
|
+
execution.status,
|
|
4318
|
+
execution.totalCost,
|
|
4319
|
+
execution.startedAt,
|
|
4320
|
+
execution.completedAt ?? null,
|
|
4321
|
+
execution.duration,
|
|
4322
|
+
execution.error ?? null,
|
|
4323
|
+
JSON.stringify(execution.steps)
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
async getExecution(executionId) {
|
|
4327
|
+
const row = this.db.prepare("SELECT * FROM execution_history WHERE execution_id = ?").get(executionId);
|
|
4328
|
+
return row ? rowToExecutionInfo(row) : null;
|
|
4329
|
+
}
|
|
4330
|
+
async listExecutions(limit) {
|
|
4331
|
+
const sql = limit ? "SELECT * FROM execution_history ORDER BY started_at DESC LIMIT ?" : "SELECT * FROM execution_history ORDER BY started_at DESC";
|
|
4332
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4333
|
+
return rows.map(rowToExecutionInfo);
|
|
4334
|
+
}
|
|
4335
|
+
// ── Eval History ────────────────────────────────────────────────────────
|
|
4336
|
+
async saveEvalResult(entry) {
|
|
4337
|
+
this.db.prepare(
|
|
4338
|
+
"INSERT OR REPLACE INTO eval_history (id, eval_name, timestamp, data) VALUES (?, ?, ?, ?)"
|
|
4339
|
+
).run(entry.id, entry.eval, entry.timestamp, JSON.stringify(entry.data));
|
|
4340
|
+
}
|
|
4341
|
+
async listEvalResults(limit) {
|
|
4342
|
+
const sql = limit ? "SELECT * FROM eval_history ORDER BY timestamp DESC LIMIT ?" : "SELECT * FROM eval_history ORDER BY timestamp DESC";
|
|
4343
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4344
|
+
return rows.map((r) => ({
|
|
4345
|
+
id: r.id,
|
|
4346
|
+
eval: r.eval_name,
|
|
4347
|
+
timestamp: r.timestamp,
|
|
4348
|
+
data: safeJsonParse(r.data)
|
|
4349
|
+
}));
|
|
4350
|
+
}
|
|
4243
4351
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
4244
4352
|
async listSessions() {
|
|
4245
4353
|
const rows = this.db.prepare("SELECT session_id FROM sessions").all();
|
|
@@ -5124,6 +5232,10 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5124
5232
|
mcpManager;
|
|
5125
5233
|
memoryManager;
|
|
5126
5234
|
spanManager = new NoopSpanManager();
|
|
5235
|
+
historicalExecutions = /* @__PURE__ */ new Map();
|
|
5236
|
+
historicalExecutionsLoadPromise = null;
|
|
5237
|
+
evalHistory = [];
|
|
5238
|
+
evalHistoryLoadPromise = null;
|
|
5127
5239
|
constructor(config) {
|
|
5128
5240
|
super();
|
|
5129
5241
|
this.config = resolveConfig(config ?? {});
|
|
@@ -5168,6 +5280,20 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5168
5280
|
return new MemoryStore();
|
|
5169
5281
|
}
|
|
5170
5282
|
}
|
|
5283
|
+
/**
|
|
5284
|
+
* Persist a completed/failed execution to the state store (fire-and-forget)
|
|
5285
|
+
* and move it from the active executions map to the historical cache.
|
|
5286
|
+
*/
|
|
5287
|
+
persistExecution(execInfo) {
|
|
5288
|
+
const snapshot = structuredClone(execInfo);
|
|
5289
|
+
if (this.stateStore.saveExecution) {
|
|
5290
|
+
this.stateStore.saveExecution(snapshot).catch(() => {
|
|
5291
|
+
});
|
|
5292
|
+
}
|
|
5293
|
+
const id = execInfo.executionId;
|
|
5294
|
+
this.historicalExecutions.set(id, snapshot);
|
|
5295
|
+
this.executions.delete(id);
|
|
5296
|
+
}
|
|
5171
5297
|
/** Register a workflow with the runtime. */
|
|
5172
5298
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5173
5299
|
register(workflow2) {
|
|
@@ -5246,6 +5372,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5246
5372
|
async runRegisteredEval(name) {
|
|
5247
5373
|
const entry = this.registeredEvals.get(name);
|
|
5248
5374
|
if (!entry) throw new Error(`Eval "${name}" is not registered`);
|
|
5375
|
+
let result;
|
|
5249
5376
|
if (entry.executeWorkflow) {
|
|
5250
5377
|
let runEvalFn;
|
|
5251
5378
|
try {
|
|
@@ -5257,20 +5384,50 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5257
5384
|
}
|
|
5258
5385
|
const originalExecuteFn = entry.executeWorkflow;
|
|
5259
5386
|
const wrappedExecuteFn = async (input, runtime) => {
|
|
5260
|
-
const { result, cost: trackedCost } = await this.trackCost(async () => {
|
|
5387
|
+
const { result: result2, cost: trackedCost } = await this.trackCost(async () => {
|
|
5261
5388
|
return originalExecuteFn(input, runtime);
|
|
5262
5389
|
});
|
|
5263
|
-
return { output:
|
|
5390
|
+
return { output: result2.output, cost: result2.cost ?? trackedCost };
|
|
5264
5391
|
};
|
|
5265
|
-
|
|
5392
|
+
result = await runEvalFn(entry.config, wrappedExecuteFn, void 0, this);
|
|
5393
|
+
} else {
|
|
5394
|
+
result = await this.eval(
|
|
5395
|
+
entry.config
|
|
5396
|
+
);
|
|
5266
5397
|
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5398
|
+
const resultObj = result;
|
|
5399
|
+
try {
|
|
5400
|
+
await this.saveEvalResult({
|
|
5401
|
+
id: resultObj.id ?? randomUUID2(),
|
|
5402
|
+
eval: name,
|
|
5403
|
+
timestamp: Date.now(),
|
|
5404
|
+
data: structuredClone(result)
|
|
5405
|
+
});
|
|
5406
|
+
} catch {
|
|
5407
|
+
}
|
|
5408
|
+
return result;
|
|
5270
5409
|
}
|
|
5271
|
-
/** Get all execution info (running + completed). */
|
|
5272
|
-
getExecutions() {
|
|
5273
|
-
|
|
5410
|
+
/** Get all execution info (running + completed + historical). */
|
|
5411
|
+
async getExecutions() {
|
|
5412
|
+
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
5413
|
+
this.historicalExecutionsLoadPromise = this.stateStore.listExecutions().then((stored) => {
|
|
5414
|
+
for (const exec of stored) {
|
|
5415
|
+
if (!this.executions.has(exec.executionId) && !this.historicalExecutions.has(exec.executionId)) {
|
|
5416
|
+
this.historicalExecutions.set(exec.executionId, exec);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
}).catch(() => {
|
|
5420
|
+
this.historicalExecutionsLoadPromise = null;
|
|
5421
|
+
});
|
|
5422
|
+
}
|
|
5423
|
+
if (this.historicalExecutionsLoadPromise) {
|
|
5424
|
+
await this.historicalExecutionsLoadPromise;
|
|
5425
|
+
}
|
|
5426
|
+
const merged = new Map(this.historicalExecutions);
|
|
5427
|
+
for (const [id, exec] of this.executions) {
|
|
5428
|
+
merged.set(id, exec);
|
|
5429
|
+
}
|
|
5430
|
+
return [...merged.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
5274
5431
|
}
|
|
5275
5432
|
/**
|
|
5276
5433
|
* Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
|
|
@@ -5402,6 +5559,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5402
5559
|
}
|
|
5403
5560
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5404
5561
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5562
|
+
this.persistExecution(execInfo);
|
|
5405
5563
|
return output;
|
|
5406
5564
|
} catch (err) {
|
|
5407
5565
|
execInfo.status = "failed";
|
|
@@ -5409,6 +5567,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5409
5567
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5410
5568
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5411
5569
|
ctx.log("workflow_end", { workflow: name, status: "failed", error: execInfo.error });
|
|
5570
|
+
this.persistExecution(execInfo);
|
|
5412
5571
|
throw err;
|
|
5413
5572
|
} finally {
|
|
5414
5573
|
this.abortControllers.delete(executionId);
|
|
@@ -5422,6 +5581,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5422
5581
|
const controller = new AbortController();
|
|
5423
5582
|
axlStream.on("close", () => controller.abort());
|
|
5424
5583
|
let execInfo;
|
|
5584
|
+
let ctx;
|
|
5425
5585
|
const run = async () => {
|
|
5426
5586
|
const workflow2 = this.workflows.get(name);
|
|
5427
5587
|
if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
|
|
@@ -5440,7 +5600,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5440
5600
|
duration: 0
|
|
5441
5601
|
};
|
|
5442
5602
|
this.executions.set(executionId, execInfo);
|
|
5443
|
-
const
|
|
5603
|
+
const wfCtx = new WorkflowContext({
|
|
5444
5604
|
input: validated,
|
|
5445
5605
|
executionId,
|
|
5446
5606
|
metadata: options?.metadata,
|
|
@@ -5528,6 +5688,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5528
5688
|
policy: "finish_and_stop"
|
|
5529
5689
|
}
|
|
5530
5690
|
});
|
|
5691
|
+
ctx = wfCtx;
|
|
5531
5692
|
return this.spanManager.withSpanAsync(
|
|
5532
5693
|
"axl.workflow.execute",
|
|
5533
5694
|
{
|
|
@@ -5537,16 +5698,23 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5537
5698
|
},
|
|
5538
5699
|
async (span) => {
|
|
5539
5700
|
try {
|
|
5540
|
-
const rawResult = await workflow2.handler(
|
|
5701
|
+
const rawResult = await workflow2.handler(wfCtx);
|
|
5541
5702
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(rawResult) : rawResult;
|
|
5542
5703
|
execInfo.status = "completed";
|
|
5543
5704
|
execInfo.completedAt = Date.now();
|
|
5544
5705
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5706
|
+
wfCtx.log("workflow_end", {
|
|
5707
|
+
workflow: name,
|
|
5708
|
+
status: "completed",
|
|
5709
|
+
duration: execInfo.duration,
|
|
5710
|
+
cost: execInfo.totalCost
|
|
5711
|
+
});
|
|
5545
5712
|
if (this.stateStore.deleteCheckpoints) {
|
|
5546
5713
|
await this.stateStore.deleteCheckpoints(executionId);
|
|
5547
5714
|
}
|
|
5548
5715
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5549
5716
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5717
|
+
this.persistExecution(execInfo);
|
|
5550
5718
|
return result;
|
|
5551
5719
|
} finally {
|
|
5552
5720
|
this.abortControllers.delete(executionId);
|
|
@@ -5560,6 +5728,12 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5560
5728
|
execInfo.completedAt = Date.now();
|
|
5561
5729
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5562
5730
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5731
|
+
ctx?.log("workflow_end", {
|
|
5732
|
+
workflow: name,
|
|
5733
|
+
status: "failed",
|
|
5734
|
+
error: execInfo.error
|
|
5735
|
+
});
|
|
5736
|
+
this.persistExecution(execInfo);
|
|
5563
5737
|
}
|
|
5564
5738
|
axlStream._error(err instanceof Error ? err : new Error(String(err)));
|
|
5565
5739
|
});
|
|
@@ -5603,7 +5777,45 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
5603
5777
|
}
|
|
5604
5778
|
/** Get execution details by ID. */
|
|
5605
5779
|
async getExecution(executionId) {
|
|
5606
|
-
|
|
5780
|
+
const inMemory = this.executions.get(executionId);
|
|
5781
|
+
if (inMemory) return inMemory;
|
|
5782
|
+
const cached = this.historicalExecutions.get(executionId);
|
|
5783
|
+
if (cached) return cached;
|
|
5784
|
+
if (this.stateStore.getExecution) {
|
|
5785
|
+
const stored = await this.stateStore.getExecution(executionId);
|
|
5786
|
+
if (stored) {
|
|
5787
|
+
this.historicalExecutions.set(executionId, stored);
|
|
5788
|
+
return stored;
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
return void 0;
|
|
5792
|
+
}
|
|
5793
|
+
/** Save an eval result to history. */
|
|
5794
|
+
async saveEvalResult(entry) {
|
|
5795
|
+
this.evalHistory.unshift(entry);
|
|
5796
|
+
if (this.stateStore.saveEvalResult) {
|
|
5797
|
+
await this.stateStore.saveEvalResult(entry);
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
/** Get eval result history (most recent first). */
|
|
5801
|
+
async getEvalHistory() {
|
|
5802
|
+
if (!this.evalHistoryLoadPromise && this.stateStore.listEvalResults) {
|
|
5803
|
+
this.evalHistoryLoadPromise = this.stateStore.listEvalResults().then((stored) => {
|
|
5804
|
+
const ids = new Set(this.evalHistory.map((e) => e.id));
|
|
5805
|
+
for (const entry of stored) {
|
|
5806
|
+
if (!ids.has(entry.id)) {
|
|
5807
|
+
this.evalHistory.push(entry);
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
this.evalHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
5811
|
+
}).catch(() => {
|
|
5812
|
+
this.evalHistoryLoadPromise = null;
|
|
5813
|
+
});
|
|
5814
|
+
}
|
|
5815
|
+
if (this.evalHistoryLoadPromise) {
|
|
5816
|
+
await this.evalHistoryLoadPromise;
|
|
5817
|
+
}
|
|
5818
|
+
return [...this.evalHistory];
|
|
5607
5819
|
}
|
|
5608
5820
|
/** List pending human decisions. */
|
|
5609
5821
|
async getPendingDecisions() {
|
|
@@ -5911,6 +6123,18 @@ var RedisStore = class _RedisStore {
|
|
|
5911
6123
|
pendingExecSetKey() {
|
|
5912
6124
|
return "axl:pending-executions";
|
|
5913
6125
|
}
|
|
6126
|
+
execHistoryKey(executionId) {
|
|
6127
|
+
return `axl:exec-history:${executionId}`;
|
|
6128
|
+
}
|
|
6129
|
+
execHistorySetKey() {
|
|
6130
|
+
return "axl:exec-history-ids";
|
|
6131
|
+
}
|
|
6132
|
+
evalHistoryKey(id) {
|
|
6133
|
+
return `axl:eval-history:${id}`;
|
|
6134
|
+
}
|
|
6135
|
+
evalHistorySetKey() {
|
|
6136
|
+
return "axl:eval-history-ids";
|
|
6137
|
+
}
|
|
5914
6138
|
// ── Checkpoints ──────────────────────────────────────────────────────
|
|
5915
6139
|
async saveCheckpoint(executionId, step, data) {
|
|
5916
6140
|
await this.client.hSet(this.checkpointKey(executionId), String(step), JSON.stringify(data));
|
|
@@ -5982,6 +6206,42 @@ var RedisStore = class _RedisStore {
|
|
|
5982
6206
|
async listPendingExecutions() {
|
|
5983
6207
|
return this.client.sMembers(this.pendingExecSetKey());
|
|
5984
6208
|
}
|
|
6209
|
+
// ── Execution History ────────────────────────────────────────────────
|
|
6210
|
+
async saveExecution(execution) {
|
|
6211
|
+
await this.client.sAdd(this.execHistorySetKey(), execution.executionId);
|
|
6212
|
+
await this.client.set(this.execHistoryKey(execution.executionId), JSON.stringify(execution));
|
|
6213
|
+
}
|
|
6214
|
+
async getExecution(executionId) {
|
|
6215
|
+
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
6216
|
+
return raw ? JSON.parse(raw) : null;
|
|
6217
|
+
}
|
|
6218
|
+
async listExecutions(limit) {
|
|
6219
|
+
const ids = await this.client.sMembers(this.execHistorySetKey());
|
|
6220
|
+
if (ids.length === 0) return [];
|
|
6221
|
+
const entries = [];
|
|
6222
|
+
for (const id of ids) {
|
|
6223
|
+
const raw = await this.client.get(this.execHistoryKey(id));
|
|
6224
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6225
|
+
}
|
|
6226
|
+
entries.sort((a, b) => b.startedAt - a.startedAt);
|
|
6227
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6228
|
+
}
|
|
6229
|
+
// ── Eval History ────────────────────────────────────────────────────
|
|
6230
|
+
async saveEvalResult(entry) {
|
|
6231
|
+
await this.client.sAdd(this.evalHistorySetKey(), entry.id);
|
|
6232
|
+
await this.client.set(this.evalHistoryKey(entry.id), JSON.stringify(entry));
|
|
6233
|
+
}
|
|
6234
|
+
async listEvalResults(limit) {
|
|
6235
|
+
const ids = await this.client.sMembers(this.evalHistorySetKey());
|
|
6236
|
+
if (ids.length === 0) return [];
|
|
6237
|
+
const entries = [];
|
|
6238
|
+
for (const id of ids) {
|
|
6239
|
+
const raw = await this.client.get(this.evalHistoryKey(id));
|
|
6240
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6241
|
+
}
|
|
6242
|
+
entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
6243
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6244
|
+
}
|
|
5985
6245
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
5986
6246
|
async listSessions() {
|
|
5987
6247
|
return this.client.sMembers("axl:session-ids");
|