@axlsdk/axl 0.11.6 → 0.13.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 +278 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +278 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -505,7 +505,7 @@ const result = await ctx.ask(extractAgent, 'Extract user from this text', {
|
|
|
505
505
|
|
|
506
506
|
### State Stores
|
|
507
507
|
|
|
508
|
-
Three built-in implementations. All persist the same data: workflow execution checkpoints, `awaitHuman` decisions, session history, memory entries, and the execution state needed for suspend/resume.
|
|
508
|
+
Three built-in implementations. All persist the same data: workflow execution checkpoints, `awaitHuman` decisions, session history, memory entries, execution history, eval history, and the execution state needed for suspend/resume.
|
|
509
509
|
|
|
510
510
|
**Memory** (default) — in-process, no persistence. Use for development and stateless workflows.
|
|
511
511
|
|
package/dist/index.cjs
CHANGED
|
@@ -2666,6 +2666,11 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
2666
2666
|
model: modelUri,
|
|
2667
2667
|
promptVersion: agent2._config.version,
|
|
2668
2668
|
cost: response.cost,
|
|
2669
|
+
tokens: response.usage ? {
|
|
2670
|
+
input: response.usage.prompt_tokens,
|
|
2671
|
+
output: response.usage.completion_tokens,
|
|
2672
|
+
reasoning: response.usage.reasoning_tokens
|
|
2673
|
+
} : void 0,
|
|
2669
2674
|
duration: Date.now() - startTime,
|
|
2670
2675
|
data: { prompt, response: response.content }
|
|
2671
2676
|
});
|
|
@@ -4053,6 +4058,8 @@ var MemoryStore = class {
|
|
|
4053
4058
|
decisions = /* @__PURE__ */ new Map();
|
|
4054
4059
|
executionStates = /* @__PURE__ */ new Map();
|
|
4055
4060
|
memories = /* @__PURE__ */ new Map();
|
|
4061
|
+
executionHistory = /* @__PURE__ */ new Map();
|
|
4062
|
+
evalHistory = /* @__PURE__ */ new Map();
|
|
4056
4063
|
constructor() {
|
|
4057
4064
|
this.loadPersistedState();
|
|
4058
4065
|
}
|
|
@@ -4149,11 +4156,36 @@ var MemoryStore = class {
|
|
|
4149
4156
|
async getAllMemory(scope) {
|
|
4150
4157
|
const scopeMap = this.memories.get(scope);
|
|
4151
4158
|
if (!scopeMap) return [];
|
|
4152
|
-
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4159
|
+
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4160
|
+
key,
|
|
4161
|
+
value: structuredClone(value)
|
|
4162
|
+
}));
|
|
4153
4163
|
}
|
|
4154
4164
|
async deleteMemory(scope, key) {
|
|
4155
4165
|
this.memories.get(scope)?.delete(key);
|
|
4156
4166
|
}
|
|
4167
|
+
// ── Execution History ──────────────────────────────────────────────
|
|
4168
|
+
async saveExecution(execution) {
|
|
4169
|
+
this.executionHistory.set(execution.executionId, structuredClone(execution));
|
|
4170
|
+
}
|
|
4171
|
+
async getExecution(executionId) {
|
|
4172
|
+
const exec = this.executionHistory.get(executionId);
|
|
4173
|
+
return exec ? structuredClone(exec) : null;
|
|
4174
|
+
}
|
|
4175
|
+
async listExecutions(limit) {
|
|
4176
|
+
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
4177
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4178
|
+
return result.map((e) => structuredClone(e));
|
|
4179
|
+
}
|
|
4180
|
+
// ── Eval History ──────────────────────────────────────────────────
|
|
4181
|
+
async saveEvalResult(entry) {
|
|
4182
|
+
this.evalHistory.set(entry.id, structuredClone(entry));
|
|
4183
|
+
}
|
|
4184
|
+
async listEvalResults(limit) {
|
|
4185
|
+
const sorted = [...this.evalHistory.values()].sort((a, b) => b.timestamp - a.timestamp);
|
|
4186
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4187
|
+
return result.map((e) => structuredClone(e));
|
|
4188
|
+
}
|
|
4157
4189
|
// ── Sessions (Studio introspection) ─────────────────────────────────
|
|
4158
4190
|
async listSessions() {
|
|
4159
4191
|
return [...this.sessions.keys()];
|
|
@@ -4213,6 +4245,19 @@ function safeJsonParse(data) {
|
|
|
4213
4245
|
return null;
|
|
4214
4246
|
}
|
|
4215
4247
|
}
|
|
4248
|
+
function rowToExecutionInfo(row) {
|
|
4249
|
+
return {
|
|
4250
|
+
executionId: row.execution_id,
|
|
4251
|
+
workflow: row.workflow,
|
|
4252
|
+
status: row.status,
|
|
4253
|
+
totalCost: row.total_cost,
|
|
4254
|
+
startedAt: row.started_at,
|
|
4255
|
+
completedAt: row.completed_at ?? void 0,
|
|
4256
|
+
duration: row.duration,
|
|
4257
|
+
error: row.error ?? void 0,
|
|
4258
|
+
steps: safeJsonParse(row.steps) ?? []
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4216
4261
|
var SQLiteStore = class {
|
|
4217
4262
|
db;
|
|
4218
4263
|
constructor(filePath) {
|
|
@@ -4273,6 +4318,28 @@ var SQLiteStore = class {
|
|
|
4273
4318
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4274
4319
|
PRIMARY KEY (scope, key)
|
|
4275
4320
|
);
|
|
4321
|
+
|
|
4322
|
+
CREATE TABLE IF NOT EXISTS execution_history (
|
|
4323
|
+
execution_id TEXT PRIMARY KEY,
|
|
4324
|
+
workflow TEXT NOT NULL,
|
|
4325
|
+
status TEXT NOT NULL,
|
|
4326
|
+
total_cost REAL NOT NULL DEFAULT 0,
|
|
4327
|
+
started_at INTEGER NOT NULL,
|
|
4328
|
+
completed_at INTEGER,
|
|
4329
|
+
duration INTEGER NOT NULL DEFAULT 0,
|
|
4330
|
+
error TEXT,
|
|
4331
|
+
steps TEXT NOT NULL
|
|
4332
|
+
);
|
|
4333
|
+
|
|
4334
|
+
CREATE TABLE IF NOT EXISTS eval_history (
|
|
4335
|
+
id TEXT PRIMARY KEY,
|
|
4336
|
+
eval_name TEXT NOT NULL,
|
|
4337
|
+
timestamp INTEGER NOT NULL,
|
|
4338
|
+
data TEXT NOT NULL
|
|
4339
|
+
);
|
|
4340
|
+
|
|
4341
|
+
CREATE INDEX IF NOT EXISTS idx_exec_history_started ON execution_history (started_at DESC);
|
|
4342
|
+
CREATE INDEX IF NOT EXISTS idx_eval_history_timestamp ON eval_history (timestamp DESC);
|
|
4276
4343
|
`);
|
|
4277
4344
|
}
|
|
4278
4345
|
// ── Checkpoints ────────────────────────────────────────────────────────
|
|
@@ -4386,6 +4453,47 @@ var SQLiteStore = class {
|
|
|
4386
4453
|
const rows = stmt.all();
|
|
4387
4454
|
return rows.map((r) => r.execution_id);
|
|
4388
4455
|
}
|
|
4456
|
+
// ── Execution History ────────────────────────────────────────────────────
|
|
4457
|
+
async saveExecution(execution) {
|
|
4458
|
+
this.db.prepare(
|
|
4459
|
+
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, steps) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4460
|
+
).run(
|
|
4461
|
+
execution.executionId,
|
|
4462
|
+
execution.workflow,
|
|
4463
|
+
execution.status,
|
|
4464
|
+
execution.totalCost,
|
|
4465
|
+
execution.startedAt,
|
|
4466
|
+
execution.completedAt ?? null,
|
|
4467
|
+
execution.duration,
|
|
4468
|
+
execution.error ?? null,
|
|
4469
|
+
JSON.stringify(execution.steps)
|
|
4470
|
+
);
|
|
4471
|
+
}
|
|
4472
|
+
async getExecution(executionId) {
|
|
4473
|
+
const row = this.db.prepare("SELECT * FROM execution_history WHERE execution_id = ?").get(executionId);
|
|
4474
|
+
return row ? rowToExecutionInfo(row) : null;
|
|
4475
|
+
}
|
|
4476
|
+
async listExecutions(limit) {
|
|
4477
|
+
const sql = limit ? "SELECT * FROM execution_history ORDER BY started_at DESC LIMIT ?" : "SELECT * FROM execution_history ORDER BY started_at DESC";
|
|
4478
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4479
|
+
return rows.map(rowToExecutionInfo);
|
|
4480
|
+
}
|
|
4481
|
+
// ── Eval History ────────────────────────────────────────────────────────
|
|
4482
|
+
async saveEvalResult(entry) {
|
|
4483
|
+
this.db.prepare(
|
|
4484
|
+
"INSERT OR REPLACE INTO eval_history (id, eval_name, timestamp, data) VALUES (?, ?, ?, ?)"
|
|
4485
|
+
).run(entry.id, entry.eval, entry.timestamp, JSON.stringify(entry.data));
|
|
4486
|
+
}
|
|
4487
|
+
async listEvalResults(limit) {
|
|
4488
|
+
const sql = limit ? "SELECT * FROM eval_history ORDER BY timestamp DESC LIMIT ?" : "SELECT * FROM eval_history ORDER BY timestamp DESC";
|
|
4489
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4490
|
+
return rows.map((r) => ({
|
|
4491
|
+
id: r.id,
|
|
4492
|
+
eval: r.eval_name,
|
|
4493
|
+
timestamp: r.timestamp,
|
|
4494
|
+
data: safeJsonParse(r.data)
|
|
4495
|
+
}));
|
|
4496
|
+
}
|
|
4389
4497
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
4390
4498
|
async listSessions() {
|
|
4391
4499
|
const rows = this.db.prepare("SELECT session_id FROM sessions").all();
|
|
@@ -5271,6 +5379,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5271
5379
|
mcpManager;
|
|
5272
5380
|
memoryManager;
|
|
5273
5381
|
spanManager = new NoopSpanManager();
|
|
5382
|
+
historicalExecutions = /* @__PURE__ */ new Map();
|
|
5383
|
+
historicalExecutionsLoadPromise = null;
|
|
5384
|
+
evalHistory = [];
|
|
5385
|
+
evalHistoryLoadPromise = null;
|
|
5274
5386
|
constructor(config) {
|
|
5275
5387
|
super();
|
|
5276
5388
|
this.config = resolveConfig(config ?? {});
|
|
@@ -5315,6 +5427,20 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5315
5427
|
return new MemoryStore();
|
|
5316
5428
|
}
|
|
5317
5429
|
}
|
|
5430
|
+
/**
|
|
5431
|
+
* Persist a completed/failed execution to the state store (fire-and-forget)
|
|
5432
|
+
* and move it from the active executions map to the historical cache.
|
|
5433
|
+
*/
|
|
5434
|
+
persistExecution(execInfo) {
|
|
5435
|
+
const snapshot = structuredClone(execInfo);
|
|
5436
|
+
if (this.stateStore.saveExecution) {
|
|
5437
|
+
this.stateStore.saveExecution(snapshot).catch(() => {
|
|
5438
|
+
});
|
|
5439
|
+
}
|
|
5440
|
+
const id = execInfo.executionId;
|
|
5441
|
+
this.historicalExecutions.set(id, snapshot);
|
|
5442
|
+
this.executions.delete(id);
|
|
5443
|
+
}
|
|
5318
5444
|
/** Register a workflow with the runtime. */
|
|
5319
5445
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5320
5446
|
register(workflow2) {
|
|
@@ -5393,6 +5519,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5393
5519
|
async runRegisteredEval(name) {
|
|
5394
5520
|
const entry = this.registeredEvals.get(name);
|
|
5395
5521
|
if (!entry) throw new Error(`Eval "${name}" is not registered`);
|
|
5522
|
+
let result;
|
|
5396
5523
|
if (entry.executeWorkflow) {
|
|
5397
5524
|
let runEvalFn;
|
|
5398
5525
|
try {
|
|
@@ -5404,20 +5531,50 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5404
5531
|
}
|
|
5405
5532
|
const originalExecuteFn = entry.executeWorkflow;
|
|
5406
5533
|
const wrappedExecuteFn = async (input, runtime) => {
|
|
5407
|
-
const { result, cost: trackedCost } = await this.trackCost(async () => {
|
|
5534
|
+
const { result: result2, cost: trackedCost } = await this.trackCost(async () => {
|
|
5408
5535
|
return originalExecuteFn(input, runtime);
|
|
5409
5536
|
});
|
|
5410
|
-
return { output:
|
|
5537
|
+
return { output: result2.output, cost: result2.cost ?? trackedCost };
|
|
5411
5538
|
};
|
|
5412
|
-
|
|
5539
|
+
result = await runEvalFn(entry.config, wrappedExecuteFn, this);
|
|
5540
|
+
} else {
|
|
5541
|
+
result = await this.eval(
|
|
5542
|
+
entry.config
|
|
5543
|
+
);
|
|
5413
5544
|
}
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5545
|
+
const resultObj = result;
|
|
5546
|
+
try {
|
|
5547
|
+
await this.saveEvalResult({
|
|
5548
|
+
id: resultObj.id ?? (0, import_node_crypto2.randomUUID)(),
|
|
5549
|
+
eval: name,
|
|
5550
|
+
timestamp: Date.now(),
|
|
5551
|
+
data: structuredClone(result)
|
|
5552
|
+
});
|
|
5553
|
+
} catch {
|
|
5554
|
+
}
|
|
5555
|
+
return result;
|
|
5417
5556
|
}
|
|
5418
|
-
/** Get all execution info (running + completed). */
|
|
5419
|
-
getExecutions() {
|
|
5420
|
-
|
|
5557
|
+
/** Get all execution info (running + completed + historical). */
|
|
5558
|
+
async getExecutions() {
|
|
5559
|
+
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
5560
|
+
this.historicalExecutionsLoadPromise = this.stateStore.listExecutions().then((stored) => {
|
|
5561
|
+
for (const exec of stored) {
|
|
5562
|
+
if (!this.executions.has(exec.executionId) && !this.historicalExecutions.has(exec.executionId)) {
|
|
5563
|
+
this.historicalExecutions.set(exec.executionId, exec);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
}).catch(() => {
|
|
5567
|
+
this.historicalExecutionsLoadPromise = null;
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
if (this.historicalExecutionsLoadPromise) {
|
|
5571
|
+
await this.historicalExecutionsLoadPromise;
|
|
5572
|
+
}
|
|
5573
|
+
const merged = new Map(this.historicalExecutions);
|
|
5574
|
+
for (const [id, exec] of this.executions) {
|
|
5575
|
+
merged.set(id, exec);
|
|
5576
|
+
}
|
|
5577
|
+
return [...merged.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
5421
5578
|
}
|
|
5422
5579
|
/**
|
|
5423
5580
|
* Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
|
|
@@ -5458,6 +5615,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5458
5615
|
registerProvider(name, provider) {
|
|
5459
5616
|
this.providerRegistry.registerInstance(name, provider);
|
|
5460
5617
|
}
|
|
5618
|
+
/** Resolve a provider:model URI to a Provider instance and model name. */
|
|
5619
|
+
resolveProvider(uri) {
|
|
5620
|
+
return this.providerRegistry.resolve(uri, this.config);
|
|
5621
|
+
}
|
|
5461
5622
|
/** Execute a workflow and return the result. */
|
|
5462
5623
|
async execute(name, input, options) {
|
|
5463
5624
|
const workflow2 = this.workflows.get(name);
|
|
@@ -5549,6 +5710,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5549
5710
|
}
|
|
5550
5711
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5551
5712
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5713
|
+
this.persistExecution(execInfo);
|
|
5552
5714
|
return output;
|
|
5553
5715
|
} catch (err) {
|
|
5554
5716
|
execInfo.status = "failed";
|
|
@@ -5556,6 +5718,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5556
5718
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5557
5719
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5558
5720
|
ctx.log("workflow_end", { workflow: name, status: "failed", error: execInfo.error });
|
|
5721
|
+
this.persistExecution(execInfo);
|
|
5559
5722
|
throw err;
|
|
5560
5723
|
} finally {
|
|
5561
5724
|
this.abortControllers.delete(executionId);
|
|
@@ -5569,6 +5732,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5569
5732
|
const controller = new AbortController();
|
|
5570
5733
|
axlStream.on("close", () => controller.abort());
|
|
5571
5734
|
let execInfo;
|
|
5735
|
+
let ctx;
|
|
5572
5736
|
const run = async () => {
|
|
5573
5737
|
const workflow2 = this.workflows.get(name);
|
|
5574
5738
|
if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
|
|
@@ -5587,7 +5751,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5587
5751
|
duration: 0
|
|
5588
5752
|
};
|
|
5589
5753
|
this.executions.set(executionId, execInfo);
|
|
5590
|
-
const
|
|
5754
|
+
const wfCtx = new WorkflowContext({
|
|
5591
5755
|
input: validated,
|
|
5592
5756
|
executionId,
|
|
5593
5757
|
metadata: options?.metadata,
|
|
@@ -5675,6 +5839,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5675
5839
|
policy: "finish_and_stop"
|
|
5676
5840
|
}
|
|
5677
5841
|
});
|
|
5842
|
+
ctx = wfCtx;
|
|
5678
5843
|
return this.spanManager.withSpanAsync(
|
|
5679
5844
|
"axl.workflow.execute",
|
|
5680
5845
|
{
|
|
@@ -5684,16 +5849,23 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5684
5849
|
},
|
|
5685
5850
|
async (span) => {
|
|
5686
5851
|
try {
|
|
5687
|
-
const rawResult = await workflow2.handler(
|
|
5852
|
+
const rawResult = await workflow2.handler(wfCtx);
|
|
5688
5853
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(rawResult) : rawResult;
|
|
5689
5854
|
execInfo.status = "completed";
|
|
5690
5855
|
execInfo.completedAt = Date.now();
|
|
5691
5856
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5857
|
+
wfCtx.log("workflow_end", {
|
|
5858
|
+
workflow: name,
|
|
5859
|
+
status: "completed",
|
|
5860
|
+
duration: execInfo.duration,
|
|
5861
|
+
cost: execInfo.totalCost
|
|
5862
|
+
});
|
|
5692
5863
|
if (this.stateStore.deleteCheckpoints) {
|
|
5693
5864
|
await this.stateStore.deleteCheckpoints(executionId);
|
|
5694
5865
|
}
|
|
5695
5866
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5696
5867
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5868
|
+
this.persistExecution(execInfo);
|
|
5697
5869
|
return result;
|
|
5698
5870
|
} finally {
|
|
5699
5871
|
this.abortControllers.delete(executionId);
|
|
@@ -5707,6 +5879,12 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5707
5879
|
execInfo.completedAt = Date.now();
|
|
5708
5880
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5709
5881
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5882
|
+
ctx?.log("workflow_end", {
|
|
5883
|
+
workflow: name,
|
|
5884
|
+
status: "failed",
|
|
5885
|
+
error: execInfo.error
|
|
5886
|
+
});
|
|
5887
|
+
this.persistExecution(execInfo);
|
|
5710
5888
|
}
|
|
5711
5889
|
axlStream._error(err instanceof Error ? err : new Error(String(err)));
|
|
5712
5890
|
});
|
|
@@ -5750,7 +5928,45 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5750
5928
|
}
|
|
5751
5929
|
/** Get execution details by ID. */
|
|
5752
5930
|
async getExecution(executionId) {
|
|
5753
|
-
|
|
5931
|
+
const inMemory = this.executions.get(executionId);
|
|
5932
|
+
if (inMemory) return inMemory;
|
|
5933
|
+
const cached = this.historicalExecutions.get(executionId);
|
|
5934
|
+
if (cached) return cached;
|
|
5935
|
+
if (this.stateStore.getExecution) {
|
|
5936
|
+
const stored = await this.stateStore.getExecution(executionId);
|
|
5937
|
+
if (stored) {
|
|
5938
|
+
this.historicalExecutions.set(executionId, stored);
|
|
5939
|
+
return stored;
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
return void 0;
|
|
5943
|
+
}
|
|
5944
|
+
/** Save an eval result to history. */
|
|
5945
|
+
async saveEvalResult(entry) {
|
|
5946
|
+
this.evalHistory.unshift(entry);
|
|
5947
|
+
if (this.stateStore.saveEvalResult) {
|
|
5948
|
+
await this.stateStore.saveEvalResult(entry);
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
/** Get eval result history (most recent first). */
|
|
5952
|
+
async getEvalHistory() {
|
|
5953
|
+
if (!this.evalHistoryLoadPromise && this.stateStore.listEvalResults) {
|
|
5954
|
+
this.evalHistoryLoadPromise = this.stateStore.listEvalResults().then((stored) => {
|
|
5955
|
+
const ids = new Set(this.evalHistory.map((e) => e.id));
|
|
5956
|
+
for (const entry of stored) {
|
|
5957
|
+
if (!ids.has(entry.id)) {
|
|
5958
|
+
this.evalHistory.push(entry);
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
this.evalHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
5962
|
+
}).catch(() => {
|
|
5963
|
+
this.evalHistoryLoadPromise = null;
|
|
5964
|
+
});
|
|
5965
|
+
}
|
|
5966
|
+
if (this.evalHistoryLoadPromise) {
|
|
5967
|
+
await this.evalHistoryLoadPromise;
|
|
5968
|
+
}
|
|
5969
|
+
return [...this.evalHistory];
|
|
5754
5970
|
}
|
|
5755
5971
|
/** List pending human decisions. */
|
|
5756
5972
|
async getPendingDecisions() {
|
|
@@ -5859,7 +6075,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5859
6075
|
});
|
|
5860
6076
|
return { output: result, cost };
|
|
5861
6077
|
};
|
|
5862
|
-
return runEvalFn(config, executeWorkflow,
|
|
6078
|
+
return runEvalFn(config, executeWorkflow, this);
|
|
5863
6079
|
}
|
|
5864
6080
|
/**
|
|
5865
6081
|
* Compare two evaluation results to detect regressions and improvements.
|
|
@@ -6058,6 +6274,18 @@ var RedisStore = class _RedisStore {
|
|
|
6058
6274
|
pendingExecSetKey() {
|
|
6059
6275
|
return "axl:pending-executions";
|
|
6060
6276
|
}
|
|
6277
|
+
execHistoryKey(executionId) {
|
|
6278
|
+
return `axl:exec-history:${executionId}`;
|
|
6279
|
+
}
|
|
6280
|
+
execHistorySetKey() {
|
|
6281
|
+
return "axl:exec-history-ids";
|
|
6282
|
+
}
|
|
6283
|
+
evalHistoryKey(id) {
|
|
6284
|
+
return `axl:eval-history:${id}`;
|
|
6285
|
+
}
|
|
6286
|
+
evalHistorySetKey() {
|
|
6287
|
+
return "axl:eval-history-ids";
|
|
6288
|
+
}
|
|
6061
6289
|
// ── Checkpoints ──────────────────────────────────────────────────────
|
|
6062
6290
|
async saveCheckpoint(executionId, step, data) {
|
|
6063
6291
|
await this.client.hSet(this.checkpointKey(executionId), String(step), JSON.stringify(data));
|
|
@@ -6129,6 +6357,42 @@ var RedisStore = class _RedisStore {
|
|
|
6129
6357
|
async listPendingExecutions() {
|
|
6130
6358
|
return this.client.sMembers(this.pendingExecSetKey());
|
|
6131
6359
|
}
|
|
6360
|
+
// ── Execution History ────────────────────────────────────────────────
|
|
6361
|
+
async saveExecution(execution) {
|
|
6362
|
+
await this.client.sAdd(this.execHistorySetKey(), execution.executionId);
|
|
6363
|
+
await this.client.set(this.execHistoryKey(execution.executionId), JSON.stringify(execution));
|
|
6364
|
+
}
|
|
6365
|
+
async getExecution(executionId) {
|
|
6366
|
+
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
6367
|
+
return raw ? JSON.parse(raw) : null;
|
|
6368
|
+
}
|
|
6369
|
+
async listExecutions(limit) {
|
|
6370
|
+
const ids = await this.client.sMembers(this.execHistorySetKey());
|
|
6371
|
+
if (ids.length === 0) return [];
|
|
6372
|
+
const entries = [];
|
|
6373
|
+
for (const id of ids) {
|
|
6374
|
+
const raw = await this.client.get(this.execHistoryKey(id));
|
|
6375
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6376
|
+
}
|
|
6377
|
+
entries.sort((a, b) => b.startedAt - a.startedAt);
|
|
6378
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6379
|
+
}
|
|
6380
|
+
// ── Eval History ────────────────────────────────────────────────────
|
|
6381
|
+
async saveEvalResult(entry) {
|
|
6382
|
+
await this.client.sAdd(this.evalHistorySetKey(), entry.id);
|
|
6383
|
+
await this.client.set(this.evalHistoryKey(entry.id), JSON.stringify(entry));
|
|
6384
|
+
}
|
|
6385
|
+
async listEvalResults(limit) {
|
|
6386
|
+
const ids = await this.client.sMembers(this.evalHistorySetKey());
|
|
6387
|
+
if (ids.length === 0) return [];
|
|
6388
|
+
const entries = [];
|
|
6389
|
+
for (const id of ids) {
|
|
6390
|
+
const raw = await this.client.get(this.evalHistoryKey(id));
|
|
6391
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6392
|
+
}
|
|
6393
|
+
entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
6394
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6395
|
+
}
|
|
6132
6396
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
6133
6397
|
async listSessions() {
|
|
6134
6398
|
return this.client.sMembers("axl:session-ids");
|