@axlsdk/axl 0.2.0 → 0.4.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 +31 -60
- package/dist/index.cjs +61 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +61 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,16 +108,16 @@ const history = await session.history();
|
|
|
108
108
|
|
|
109
109
|
### Context Primitives
|
|
110
110
|
|
|
111
|
-
All available on `ctx` inside workflow handlers
|
|
111
|
+
All available on `ctx` inside workflow handlers. See the [API Reference](../../docs/api-reference.md) for complete option types, valid values, and defaults.
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
114
|
// Invoke an agent
|
|
115
115
|
const answer = await ctx.ask(agent, 'prompt', { schema, retries });
|
|
116
116
|
|
|
117
|
-
// Run
|
|
117
|
+
// Run 3 agents in parallel — each gets the same question independently
|
|
118
118
|
const results = await ctx.spawn(3, async (i) => ctx.ask(agent, prompts[i]));
|
|
119
119
|
|
|
120
|
-
//
|
|
120
|
+
// Pick the answer that appeared most often (pure aggregation, no LLM involved)
|
|
121
121
|
const winner = ctx.vote(results, { strategy: 'majority', key: 'answer' });
|
|
122
122
|
|
|
123
123
|
// Self-correcting validation
|
|
@@ -144,7 +144,7 @@ const [a, b] = await ctx.parallel([
|
|
|
144
144
|
() => ctx.ask(agentB, promptB),
|
|
145
145
|
]);
|
|
146
146
|
|
|
147
|
-
// Map with bounded concurrency
|
|
147
|
+
// Map with bounded concurrency — resolve when 3 of N succeed, cancel the rest
|
|
148
148
|
const mapped = await ctx.map(items, async (item) => ctx.ask(agent, item), {
|
|
149
149
|
concurrency: 5,
|
|
150
150
|
quorum: 3,
|
|
@@ -166,21 +166,27 @@ Automatic span emission for every `ctx.*` primitive with cost-per-span attributi
|
|
|
166
166
|
|
|
167
167
|
```typescript
|
|
168
168
|
import { defineConfig, AxlRuntime } from '@axlsdk/axl';
|
|
169
|
+
import { BasicTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
169
170
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
170
171
|
|
|
172
|
+
const tracerProvider = new BasicTracerProvider();
|
|
173
|
+
tracerProvider.addSpanProcessor(new SimpleSpanProcessor(
|
|
174
|
+
new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
|
|
175
|
+
));
|
|
176
|
+
|
|
171
177
|
const config = defineConfig({
|
|
172
178
|
telemetry: {
|
|
173
179
|
enabled: true,
|
|
174
180
|
serviceName: 'my-app',
|
|
175
|
-
|
|
181
|
+
tracerProvider,
|
|
176
182
|
},
|
|
177
183
|
});
|
|
178
184
|
|
|
179
185
|
const runtime = new AxlRuntime(config);
|
|
180
|
-
runtime.initializeTelemetry();
|
|
186
|
+
await runtime.initializeTelemetry();
|
|
181
187
|
```
|
|
182
188
|
|
|
183
|
-
**Span model:** `axl.workflow.execute` > `axl.agent.ask` > `axl.tool.call`. Also: `axl.ctx.spawn`, `axl.ctx.race`, `axl.ctx.vote`, `axl.ctx.budget`, `axl.ctx.
|
|
189
|
+
**Span model:** `axl.workflow.execute` > `axl.agent.ask` > `axl.tool.call`. Also: `axl.ctx.spawn`, `axl.ctx.race`, `axl.ctx.vote`, `axl.ctx.budget`, `axl.ctx.awaitHuman`. Each span includes relevant attributes (cost, duration, token counts, etc.).
|
|
184
190
|
|
|
185
191
|
When disabled (default), `NoopSpanManager` provides zero overhead.
|
|
186
192
|
|
|
@@ -210,7 +216,7 @@ import { AxlRuntime, InMemoryVectorStore, OpenAIEmbedder } from '@axlsdk/axl';
|
|
|
210
216
|
|
|
211
217
|
const runtime = new AxlRuntime({
|
|
212
218
|
memory: {
|
|
213
|
-
|
|
219
|
+
vectorStore: new InMemoryVectorStore(),
|
|
214
220
|
embedder: new OpenAIEmbedder({ model: 'text-embedding-3-small' }),
|
|
215
221
|
},
|
|
216
222
|
});
|
|
@@ -226,9 +232,13 @@ Vector store implementations: `InMemoryVectorStore` (testing), `SqliteVectorStor
|
|
|
226
232
|
|
|
227
233
|
### Agent Guardrails
|
|
228
234
|
|
|
229
|
-
Input and output validation at the agent boundary:
|
|
235
|
+
Input and output validation at the agent boundary. You define your own validation logic — Axl calls it before and after each LLM turn:
|
|
230
236
|
|
|
231
237
|
```typescript
|
|
238
|
+
// Your validation functions — Axl doesn't ship these, you bring your own
|
|
239
|
+
const containsPII = (text: string) => /\b\d{3}-\d{2}-\d{4}\b/.test(text);
|
|
240
|
+
const isOffTopic = (text: string) => !text.toLowerCase().includes('support');
|
|
241
|
+
|
|
232
242
|
const safe = agent({
|
|
233
243
|
model: 'openai:gpt-4o',
|
|
234
244
|
system: 'You are a helpful assistant.',
|
|
@@ -254,10 +264,11 @@ When `onBlock` is `'retry'`, the LLM sees the block reason and self-corrects (sa
|
|
|
254
264
|
```typescript
|
|
255
265
|
const session = runtime.session('user-123', {
|
|
256
266
|
history: {
|
|
257
|
-
maxMessages: 100,
|
|
258
|
-
summarize: true,
|
|
267
|
+
maxMessages: 100, // Trim oldest messages when exceeded
|
|
268
|
+
summarize: true, // Auto-summarize trimmed messages
|
|
269
|
+
summaryModel: 'openai:gpt-4o-mini', // Model for summarization
|
|
259
270
|
},
|
|
260
|
-
persist: true,
|
|
271
|
+
persist: true, // Save to StateStore (default: true)
|
|
261
272
|
});
|
|
262
273
|
```
|
|
263
274
|
|
|
@@ -267,6 +278,7 @@ const session = runtime.session('user-123', {
|
|
|
267
278
|
|--------|------|---------|-------------|
|
|
268
279
|
| `history.maxMessages` | `number` | unlimited | Max messages to retain |
|
|
269
280
|
| `history.summarize` | `boolean` | `false` | Summarize trimmed messages |
|
|
281
|
+
| `history.summaryModel` | `string` | — | Model URI for summarization (required when `summarize: true`) |
|
|
270
282
|
| `persist` | `boolean` | `true` | Persist history to StateStore |
|
|
271
283
|
|
|
272
284
|
### Error Hierarchy
|
|
@@ -306,58 +318,17 @@ const runtime = new AxlRuntime({
|
|
|
306
318
|
|
|
307
319
|
### Provider URIs
|
|
308
320
|
|
|
309
|
-
Four built-in providers
|
|
321
|
+
Four built-in providers using the `provider:model` URI scheme:
|
|
310
322
|
|
|
311
323
|
```
|
|
312
|
-
# OpenAI
|
|
313
|
-
openai:gpt-4o
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
openai:gpt-4.1-mini # GPT-4.1 small
|
|
317
|
-
openai:gpt-4.1-nano # GPT-4.1 cheapest
|
|
318
|
-
openai:gpt-5 # GPT-5
|
|
319
|
-
openai:gpt-5-mini # GPT-5 small
|
|
320
|
-
openai:gpt-5-nano # GPT-5 cheapest
|
|
321
|
-
openai:gpt-5.1 # GPT-5.1
|
|
322
|
-
openai:gpt-5.2 # GPT-5.2
|
|
323
|
-
openai:o1 # Reasoning
|
|
324
|
-
openai:o1-mini # Reasoning (small)
|
|
325
|
-
openai:o1-pro # Reasoning (pro)
|
|
326
|
-
openai:o3 # Reasoning
|
|
327
|
-
openai:o3-mini # Reasoning (small)
|
|
328
|
-
openai:o3-pro # Reasoning (pro)
|
|
329
|
-
openai:o4-mini # Reasoning (small)
|
|
330
|
-
openai:gpt-4-turbo # Legacy
|
|
331
|
-
openai:gpt-4 # Legacy
|
|
332
|
-
openai:gpt-3.5-turbo # Legacy
|
|
333
|
-
|
|
334
|
-
# OpenAI — Responses API (same models, better caching, native reasoning)
|
|
335
|
-
openai-responses:gpt-4o
|
|
336
|
-
openai-responses:o3
|
|
337
|
-
|
|
338
|
-
# Anthropic
|
|
339
|
-
anthropic:claude-opus-4-6 # Most capable
|
|
340
|
-
anthropic:claude-sonnet-4-5 # Balanced
|
|
341
|
-
anthropic:claude-haiku-4-5 # Fast and affordable
|
|
342
|
-
anthropic:claude-sonnet-4 # Previous gen
|
|
343
|
-
anthropic:claude-opus-4 # Previous gen
|
|
344
|
-
anthropic:claude-3-7-sonnet # Legacy
|
|
345
|
-
anthropic:claude-3-5-sonnet # Legacy
|
|
346
|
-
anthropic:claude-3-5-haiku # Legacy
|
|
347
|
-
anthropic:claude-3-opus # Legacy
|
|
348
|
-
anthropic:claude-3-sonnet # Legacy
|
|
349
|
-
anthropic:claude-3-haiku # Legacy
|
|
350
|
-
|
|
351
|
-
# Google Gemini
|
|
352
|
-
google:gemini-2.5-pro # Most capable
|
|
353
|
-
google:gemini-2.5-flash # Fast
|
|
354
|
-
google:gemini-2.5-flash-lite # Cheapest 2.5
|
|
355
|
-
google:gemini-2.0-flash # Previous gen
|
|
356
|
-
google:gemini-2.0-flash-lite # Previous gen (lite)
|
|
357
|
-
google:gemini-3-pro-preview # Next gen (preview)
|
|
358
|
-
google:gemini-3-flash-preview # Next gen fast (preview)
|
|
324
|
+
openai:gpt-4o # OpenAI Chat Completions
|
|
325
|
+
openai-responses:gpt-4o # OpenAI Responses API
|
|
326
|
+
anthropic:claude-sonnet-4-5 # Anthropic
|
|
327
|
+
google:gemini-2.5-pro # Google Gemini
|
|
359
328
|
```
|
|
360
329
|
|
|
330
|
+
See [docs/providers.md](../../docs/providers.md) for the full model list including reasoning models.
|
|
331
|
+
|
|
361
332
|
## License
|
|
362
333
|
|
|
363
334
|
[Apache 2.0](../../LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -1890,6 +1890,15 @@ function zodToJsonSchema(schema) {
|
|
|
1890
1890
|
function estimateTokens(text) {
|
|
1891
1891
|
return Math.ceil(text.length / 4);
|
|
1892
1892
|
}
|
|
1893
|
+
function stripMarkdownFences(text) {
|
|
1894
|
+
const trimmed = text.trim();
|
|
1895
|
+
if (trimmed.startsWith("```")) {
|
|
1896
|
+
const withoutOpening = trimmed.replace(/^```\w*\s*\n?/, "");
|
|
1897
|
+
const withoutClosing = withoutOpening.replace(/\n?```\s*$/, "");
|
|
1898
|
+
return withoutClosing.trim();
|
|
1899
|
+
}
|
|
1900
|
+
return trimmed;
|
|
1901
|
+
}
|
|
1893
1902
|
function estimateMessagesTokens(messages) {
|
|
1894
1903
|
let total = 0;
|
|
1895
1904
|
for (const msg of messages) {
|
|
@@ -2583,7 +2592,7 @@ Please fix and try again.`;
|
|
|
2583
2592
|
}
|
|
2584
2593
|
if (options?.schema) {
|
|
2585
2594
|
try {
|
|
2586
|
-
const parsed = JSON.parse(content);
|
|
2595
|
+
const parsed = JSON.parse(stripMarkdownFences(content));
|
|
2587
2596
|
const validated = options.schema.parse(parsed);
|
|
2588
2597
|
return validated;
|
|
2589
2598
|
} catch (err) {
|
|
@@ -4722,6 +4731,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
4722
4731
|
executions = /* @__PURE__ */ new Map();
|
|
4723
4732
|
pendingDecisionResolvers = /* @__PURE__ */ new Map();
|
|
4724
4733
|
abortControllers = /* @__PURE__ */ new Map();
|
|
4734
|
+
registeredEvals = /* @__PURE__ */ new Map();
|
|
4725
4735
|
mcpManager;
|
|
4726
4736
|
memoryManager;
|
|
4727
4737
|
spanManager = new NoopSpanManager();
|
|
@@ -4817,6 +4827,52 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
4817
4827
|
getAgent(name) {
|
|
4818
4828
|
return this.agents.get(name);
|
|
4819
4829
|
}
|
|
4830
|
+
/**
|
|
4831
|
+
* Register an eval config for Studio introspection and execution.
|
|
4832
|
+
* The config should be the result of `defineEval()` from `@axlsdk/eval`.
|
|
4833
|
+
* An optional `executeWorkflow` function can override the default behavior
|
|
4834
|
+
* of calling `runtime.execute()`.
|
|
4835
|
+
*/
|
|
4836
|
+
registerEval(name, config, executeWorkflow) {
|
|
4837
|
+
this.registeredEvals.set(name, { config, executeWorkflow });
|
|
4838
|
+
}
|
|
4839
|
+
/** Get metadata about all registered evals. */
|
|
4840
|
+
getRegisteredEvals() {
|
|
4841
|
+
const result = [];
|
|
4842
|
+
for (const [name, { config }] of this.registeredEvals) {
|
|
4843
|
+
const cfg = config;
|
|
4844
|
+
result.push({
|
|
4845
|
+
name,
|
|
4846
|
+
workflow: cfg.workflow ?? "unknown",
|
|
4847
|
+
dataset: cfg.dataset?.name ?? "unknown",
|
|
4848
|
+
scorers: (cfg.scorers ?? []).map((s) => s.name ?? "unknown")
|
|
4849
|
+
});
|
|
4850
|
+
}
|
|
4851
|
+
return result;
|
|
4852
|
+
}
|
|
4853
|
+
/** Get a registered eval config by name. */
|
|
4854
|
+
getRegisteredEval(name) {
|
|
4855
|
+
return this.registeredEvals.get(name);
|
|
4856
|
+
}
|
|
4857
|
+
/** Run a registered eval by name. */
|
|
4858
|
+
async runRegisteredEval(name) {
|
|
4859
|
+
const entry = this.registeredEvals.get(name);
|
|
4860
|
+
if (!entry) throw new Error(`Eval "${name}" is not registered`);
|
|
4861
|
+
if (entry.executeWorkflow) {
|
|
4862
|
+
let runEvalFn;
|
|
4863
|
+
try {
|
|
4864
|
+
({ runEval: runEvalFn } = await import("@axlsdk/eval"));
|
|
4865
|
+
} catch {
|
|
4866
|
+
throw new Error(
|
|
4867
|
+
"axl-eval is required for AxlRuntime.runRegisteredEval(). Install it with: npm install @axlsdk/eval"
|
|
4868
|
+
);
|
|
4869
|
+
}
|
|
4870
|
+
return runEvalFn(entry.config, entry.executeWorkflow);
|
|
4871
|
+
}
|
|
4872
|
+
return this.eval(
|
|
4873
|
+
entry.config
|
|
4874
|
+
);
|
|
4875
|
+
}
|
|
4820
4876
|
/** Get all execution info (running + completed). */
|
|
4821
4877
|
getExecutions() {
|
|
4822
4878
|
return [...this.executions.values()];
|
|
@@ -5200,10 +5256,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5200
5256
|
async eval(config) {
|
|
5201
5257
|
let runEval;
|
|
5202
5258
|
try {
|
|
5203
|
-
({ runEval } = await import("
|
|
5259
|
+
({ runEval } = await import("@axlsdk/eval"));
|
|
5204
5260
|
} catch {
|
|
5205
5261
|
throw new Error(
|
|
5206
|
-
"axl-eval is required for AxlRuntime.eval(). Install it with: npm install
|
|
5262
|
+
"axl-eval is required for AxlRuntime.eval(). Install it with: npm install @axlsdk/eval"
|
|
5207
5263
|
);
|
|
5208
5264
|
}
|
|
5209
5265
|
const executeWorkflow = async (input) => {
|
|
@@ -5230,10 +5286,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5230
5286
|
async evalCompare(baseline, candidate) {
|
|
5231
5287
|
let evalCompareFn;
|
|
5232
5288
|
try {
|
|
5233
|
-
({ evalCompare: evalCompareFn } = await import("
|
|
5289
|
+
({ evalCompare: evalCompareFn } = await import("@axlsdk/eval"));
|
|
5234
5290
|
} catch {
|
|
5235
5291
|
throw new Error(
|
|
5236
|
-
"axl-eval is required for AxlRuntime.evalCompare(). Install it with: npm install
|
|
5292
|
+
"axl-eval is required for AxlRuntime.evalCompare(). Install it with: npm install @axlsdk/eval"
|
|
5237
5293
|
);
|
|
5238
5294
|
}
|
|
5239
5295
|
return evalCompareFn(baseline, candidate);
|