@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 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 N concurrent tasks
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
- // Consensus vote
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
- exporter: new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }),
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.checkpoint`, `axl.ctx.awaitHuman`. Each span includes relevant attributes (cost, duration, token counts, etc.).
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
- vector: new InMemoryVectorStore(),
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, // Trim oldest messages when exceeded
258
- summarize: true, // Auto-summarize trimmed messages
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, // Save to StateStore (default: 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 are supported:
321
+ Four built-in providers using the `provider:model` URI scheme:
310
322
 
311
323
  ```
312
- # OpenAI Chat Completions API
313
- openai:gpt-4o # Flagship multimodal
314
- openai:gpt-4o-mini # Fast and affordable
315
- openai:gpt-4.1 # GPT-4.1
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("axl-eval"));
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 axl-eval"
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("axl-eval"));
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 axl-eval"
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);