@odla-ai/ai 0.3.1 → 0.3.2

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
@@ -363,6 +363,13 @@ Every error is an `OdlaAIError` subclass carrying a stable `code` — branch on
363
363
  `CapabilityError`, `ContextWindowError`, `InvalidRequestError`, `ToolInputError`,
364
364
  `CancelledError`, `DeadlineExceededError`, `ProviderError`.
365
365
 
366
+ `OdlaAIError.usage` is present only when a provider response was already
367
+ received and a later forced-tool or schema check failed. It is a frozen,
368
+ sanitized token-usage record so accounting can charge a failed structured
369
+ response accurately; it may be absent for transport, authentication, timeout,
370
+ or preflight failures. `toShape()` and streaming `OracleErrorShape` deliberately
371
+ omit it.
372
+
366
373
  ## Develop
367
374
 
368
375
  ```bash
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  toolInput
3
- } from "./chunk-OX4TA4ZH.js";
3
+ } from "./chunk-6YJM5YZG.js";
4
4
  import {
5
5
  addUsage,
6
6
  emptyUsage,
7
7
  mapProviderError,
8
8
  normalizeError,
9
9
  signalWithDeadline
10
- } from "./chunk-PXXCN2EU.js";
10
+ } from "./chunk-XVU2W4TU.js";
11
11
 
12
12
  // src/providers/anthropic.ts
13
13
  import Anthropic from "@anthropic-ai/sdk";
@@ -232,4 +232,4 @@ var AnthropicProvider = class {
232
232
  export {
233
233
  AnthropicProvider
234
234
  };
235
- //# sourceMappingURL=anthropic-2NPMHJZT.js.map
235
+ //# sourceMappingURL=anthropic-THZJJEGB.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ToolInputError
3
- } from "./chunk-PXXCN2EU.js";
3
+ } from "./chunk-XVU2W4TU.js";
4
4
 
5
5
  // src/providers/tool-input.ts
6
6
  function toolInput(value, provider, name) {
@@ -13,4 +13,4 @@ function toolInput(value, provider, name) {
13
13
  export {
14
14
  toolInput
15
15
  };
16
- //# sourceMappingURL=chunk-OX4TA4ZH.js.map
16
+ //# sourceMappingURL=chunk-6YJM5YZG.js.map
@@ -18,12 +18,16 @@ var OdlaAIError = class extends Error {
18
18
  code;
19
19
  providerStatus;
20
20
  retryAfter;
21
+ /** Sanitized provider usage when a response was received before a later
22
+ * validation/tool-shape failure. Omitted when usage is genuinely unknown. */
23
+ usage;
21
24
  constructor(code, message, opts) {
22
25
  super(message, opts?.cause !== void 0 ? { cause: opts.cause } : void 0);
23
26
  this.name = new.target.name;
24
27
  this.code = code;
25
28
  this.providerStatus = opts?.providerStatus;
26
29
  this.retryAfter = opts?.retryAfter;
30
+ this.usage = opts?.usage ? Object.freeze({ ...opts.usage }) : void 0;
27
31
  }
28
32
  toShape() {
29
33
  return {
@@ -67,7 +71,7 @@ var InvalidRequestError = class extends OdlaAIError {
67
71
  var ToolInputError = class extends OdlaAIError {
68
72
  tool;
69
73
  constructor(message, opts) {
70
- super("tool_input_invalid", message, { cause: opts?.cause });
74
+ super("tool_input_invalid", message, { cause: opts?.cause, usage: opts?.usage });
71
75
  this.tool = opts?.tool;
72
76
  }
73
77
  };
@@ -189,4 +193,4 @@ export {
189
193
  signalWithDeadline,
190
194
  throwIfAborted
191
195
  };
192
- //# sourceMappingURL=chunk-PXXCN2EU.js.map
196
+ //# sourceMappingURL=chunk-XVU2W4TU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shape/usage.ts","../src/shape/errors.ts","../src/providers/errors.ts","../src/client/abort.ts"],"sourcesContent":["// Token usage accounting. The one runtime island with no dependencies.\n\nexport interface OracleUsage {\n inputTokens: number;\n outputTokens: number;\n cacheCreationTokens?: number;\n cacheReadTokens?: number;\n}\n\n/** A fresh zeroed usage tally (0 input/output tokens, no cache fields) — the seed for accumulation. */\nexport function emptyUsage(): OracleUsage {\n return { inputTokens: 0, outputTokens: 0 };\n}\n\n/** Accumulate `more` into `into`, in place. Cache fields sum when present. */\nexport function addUsage(into: OracleUsage, more: OracleUsage): void {\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n if (more.cacheCreationTokens !== undefined) {\n into.cacheCreationTokens = (into.cacheCreationTokens ?? 0) + more.cacheCreationTokens;\n }\n if (more.cacheReadTokens !== undefined) {\n into.cacheReadTokens = (into.cacheReadTokens ?? 0) + more.cacheReadTokens;\n }\n}\n","// The normalized error taxonomy and the class hierarchy every adapter throws.\nimport type { OracleUsage } from \"./usage\";\n\n/** The closed taxonomy of normalized error codes carried by every OdlaAIError\n * and by the streaming `error` event. */\nexport type OracleErrorType =\n | \"invalid_request\"\n | \"auth\"\n | \"rate_limit\"\n | \"context_window\"\n | \"cancelled\"\n | \"deadline_exceeded\"\n | \"tool_input_invalid\"\n | \"capability_unsupported\"\n | \"config\"\n | \"provider_error\";\n\n/** The plain-data error shape used inside the streaming `error` event. */\nexport interface OracleErrorShape {\n code: OracleErrorType;\n message: string;\n /** HTTP status from the upstream provider, when applicable. */\n providerStatus?: number;\n /** Seconds until a retry might succeed (rate limit / overload). */\n retryAfter?: number;\n}\n\n/** Base class for every error odla-ai throws. Carries a stable string `code`\n * (the odla ecosystem convention — see odla-db's AuthError/RuleError) so\n * callers branch on `err.code`, never on message text. */\nexport class OdlaAIError extends Error {\n readonly code: OracleErrorType;\n readonly providerStatus?: number;\n readonly retryAfter?: number;\n /** Sanitized provider usage when a response was received before a later\n * validation/tool-shape failure. Omitted when usage is genuinely unknown. */\n readonly usage?: OracleUsage;\n\n constructor(\n code: OracleErrorType,\n message: string,\n opts?: { providerStatus?: number; retryAfter?: number; cause?: unknown; usage?: OracleUsage },\n ) {\n super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n this.code = code;\n this.providerStatus = opts?.providerStatus;\n this.retryAfter = opts?.retryAfter;\n this.usage = opts?.usage ? Object.freeze({ ...opts.usage }) : undefined;\n }\n\n toShape(): OracleErrorShape {\n return {\n code: this.code,\n message: this.message,\n providerStatus: this.providerStatus,\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/** A provider/model is not configured — e.g. no API key for its provider, or an\n * unknown canonical model id. The library analog of odla-db's 501 gating. */\nexport class ConfigError extends OdlaAIError {\n constructor(message: string) {\n super(\"config\", message);\n }\n}\n\n/** Authentication with the provider failed (bad/missing key). */\nexport class AuthError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"auth\", message, opts);\n }\n}\n\n/** The provider rate-limited or is overloaded; check `retryAfter`. */\nexport class RateLimitError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; retryAfter?: number; cause?: unknown }) {\n super(\"rate_limit\", message, opts);\n }\n}\n\n/** The request asked a model to do something it cannot — audio to Claude, tools\n * to a text-only model, web search where unsupported, etc. Raised before any\n * network call by `validateRequest`. */\nexport class CapabilityError extends OdlaAIError {\n constructor(message: string) {\n super(\"capability_unsupported\", message);\n }\n}\n\n/** The request exceeded the model's context window. */\nexport class ContextWindowError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"context_window\", message, opts);\n }\n}\n\n/** The request was malformed / rejected as invalid by the provider. */\nexport class InvalidRequestError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"invalid_request\", message, opts);\n }\n}\n\n/** A provider returned tool arguments that are malformed or do not satisfy the\n * declared JSON Schema. Tool handlers are never invoked for this error. */\nexport class ToolInputError extends OdlaAIError {\n readonly tool?: string;\n\n constructor(message: string, opts?: { tool?: string; cause?: unknown; usage?: OracleUsage }) {\n super(\"tool_input_invalid\", message, { cause: opts?.cause, usage: opts?.usage });\n this.tool = opts?.tool;\n }\n}\n\n/** The caller cancelled a request. This is not an upstream failure and should\n * not be retried unless the caller explicitly starts a new operation. */\nexport class CancelledError extends OdlaAIError {\n constructor(message = \"AI request was cancelled.\", opts?: { cause?: unknown }) {\n super(\"cancelled\", message, opts);\n }\n}\n\n/** The request's absolute deadline elapsed. Callers may choose a new deadline,\n * but should not classify this as a provider outage. */\nexport class DeadlineExceededError extends OdlaAIError {\n constructor(message = \"AI request deadline was exceeded.\", opts?: { cause?: unknown }) {\n super(\"deadline_exceeded\", message, opts);\n }\n}\n\n/** Any other upstream provider failure (5xx, transport, unexpected shape). */\nexport class ProviderError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown; usage?: OracleUsage }) {\n super(\"provider_error\", message, opts);\n }\n}\n","// Normalize a thrown provider-SDK error into the odla-ai error taxonomy. Kept\n// dependency-light: it duck-types the HTTP status / retry-after off the error\n// object rather than importing each SDK's error classes, so it works uniformly\n// across @anthropic-ai/sdk, openai, and @google/genai.\n\nimport {\n AuthError,\n CancelledError,\n ContextWindowError,\n DeadlineExceededError,\n InvalidRequestError,\n OdlaAIError,\n ProviderError,\n RateLimitError,\n type ProviderId,\n} from \"../shape/index\";\n\nfunction statusOf(err: unknown): number | undefined {\n if (err && typeof err === \"object\") {\n const rec = err as Record<string, unknown>;\n for (const k of [\"status\", \"statusCode\", \"code\"]) {\n const v = rec[k];\n if (typeof v === \"number\") return v;\n }\n }\n return undefined;\n}\n\nfunction retryAfterOf(err: unknown): number | undefined {\n if (err && typeof err === \"object\") {\n const headers = (err as { headers?: unknown }).headers;\n if (headers && typeof headers === \"object\") {\n const raw = (headers as Record<string, unknown>)[\"retry-after\"];\n const n = typeof raw === \"string\" ? Number(raw) : typeof raw === \"number\" ? raw : NaN;\n if (Number.isFinite(n)) return n;\n }\n }\n return undefined;\n}\n\nfunction messageOf(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === \"string\") return err;\n return \"unknown provider error\";\n}\n\n/** Map any provider error to an OdlaAIError, returning it (does not throw).\n * Re-returns an OdlaAIError unchanged (already normalized). */\nexport function normalizeError(err: unknown, provider: ProviderId, signal?: AbortSignal): OdlaAIError {\n if (err instanceof OdlaAIError) return err;\n const cancellation = cancellationError(signal?.aborted ? signal.reason : err);\n if (signal?.aborted || cancellation) return cancellation ?? new CancelledError(undefined, { cause: err });\n\n const status = statusOf(err);\n const message = messageOf(err);\n const tagged = `[${provider}] ${message}`;\n\n if (status === 401 || status === 403) return new AuthError(tagged, { providerStatus: status, cause: err });\n if (status === 429) {\n return new RateLimitError(tagged, { providerStatus: status, retryAfter: retryAfterOf(err), cause: err });\n }\n if (status === 400 || status === 422) {\n if (/context|token limit|too long|maximum context/i.test(message)) {\n return new ContextWindowError(tagged, { providerStatus: status, cause: err });\n }\n return new InvalidRequestError(tagged, { providerStatus: status, cause: err });\n }\n return new ProviderError(tagged, { providerStatus: status, cause: err });\n}\n\n/**\n * Normalize and throw. The `never` return lets call sites write\n * `catch (e) { mapProviderError(e, id); }` with no fallthrough.\n */\nexport function mapProviderError(err: unknown, provider: ProviderId, signal?: AbortSignal): never {\n throw normalizeError(err, provider, signal);\n}\n\nfunction cancellationError(reason: unknown): CancelledError | DeadlineExceededError | undefined {\n if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) return reason;\n if (!reason || typeof reason !== \"object\") return undefined;\n const rec = reason as { name?: unknown; code?: unknown; message?: unknown };\n if (rec.name === \"TimeoutError\") return new DeadlineExceededError(undefined, { cause: reason });\n if (rec.name === \"AbortError\" || rec.name === \"APIUserAbortError\" || rec.code === \"ABORT_ERR\") {\n return new CancelledError(undefined, { cause: reason });\n }\n return undefined;\n}\n","import { CancelledError, ConfigError, DeadlineExceededError } from \"../shape/errors\";\n\n/** Combine a caller signal with an absolute deadline. Kept provider-neutral so\n * every adapter and the agent loop observes the same cancellation contract. */\nexport function signalWithDeadline(signal?: AbortSignal, deadline?: number): AbortSignal | undefined {\n if (deadline === undefined) return signal;\n if (!Number.isSafeInteger(deadline)) throw new ConfigError(\"deadline must be a finite integer Unix timestamp in milliseconds.\");\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n const expired = new AbortController();\n expired.abort(new DeadlineExceededError());\n return signal ? AbortSignal.any([signal, expired.signal]) : expired.signal;\n }\n const timeout = AbortSignal.timeout(remaining);\n return signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\n/** Fail synchronously before beginning a local side effect when a run was\n * cancelled or its deadline elapsed. Handlers must still observe the signal\n * while in flight; this closes the already-aborted race at invocation time. */\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n const reason = signal.reason;\n if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) throw reason;\n if (reason && typeof reason === \"object\" && (reason as { name?: unknown }).name === \"TimeoutError\") {\n throw new DeadlineExceededError(undefined, { cause: reason });\n }\n throw new CancelledError(undefined, { cause: reason });\n}\n"],"mappings":"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","names":[]}
@@ -3,14 +3,14 @@ import {
3
3
  } from "./chunk-U7F6VJCV.js";
4
4
  import {
5
5
  toolInput
6
- } from "./chunk-OX4TA4ZH.js";
6
+ } from "./chunk-6YJM5YZG.js";
7
7
  import {
8
8
  CapabilityError,
9
9
  emptyUsage,
10
10
  mapProviderError,
11
11
  normalizeError,
12
12
  signalWithDeadline
13
- } from "./chunk-PXXCN2EU.js";
13
+ } from "./chunk-XVU2W4TU.js";
14
14
 
15
15
  // src/providers/google.ts
16
16
  import { GoogleGenAI } from "@google/genai";
@@ -222,4 +222,4 @@ export {
222
222
  mapResponse,
223
223
  toContents
224
224
  };
225
- //# sourceMappingURL=google-3XV2VWVB.js.map
225
+ //# sourceMappingURL=google-SXQOKV2S.js.map
package/dist/index.cjs CHANGED
@@ -85,12 +85,16 @@ var init_errors = __esm({
85
85
  code;
86
86
  providerStatus;
87
87
  retryAfter;
88
+ /** Sanitized provider usage when a response was received before a later
89
+ * validation/tool-shape failure. Omitted when usage is genuinely unknown. */
90
+ usage;
88
91
  constructor(code, message, opts) {
89
92
  super(message, opts?.cause !== void 0 ? { cause: opts.cause } : void 0);
90
93
  this.name = new.target.name;
91
94
  this.code = code;
92
95
  this.providerStatus = opts?.providerStatus;
93
96
  this.retryAfter = opts?.retryAfter;
97
+ this.usage = opts?.usage ? Object.freeze({ ...opts.usage }) : void 0;
94
98
  }
95
99
  toShape() {
96
100
  return {
@@ -134,7 +138,7 @@ var init_errors = __esm({
134
138
  ToolInputError = class extends OdlaAIError {
135
139
  tool;
136
140
  constructor(message, opts) {
137
- super("tool_input_invalid", message, { cause: opts?.cause });
141
+ super("tool_input_invalid", message, { cause: opts?.cause, usage: opts?.usage });
138
142
  this.tool = opts?.tool;
139
143
  }
140
144
  };
@@ -1920,12 +1924,12 @@ function init(opts = {}) {
1920
1924
  const res = await provider.create(spec, req);
1921
1925
  const block = res.content.find((b) => b.type === "tool_use" && b.name === c.tool.name);
1922
1926
  if (!block || block.type !== "tool_use") {
1923
- throw new ProviderError(`Model "${model}" did not return the forced tool call "${c.tool.name}".`);
1927
+ throw new ProviderError(`Model "${model}" did not return the forced tool call "${c.tool.name}".`, { usage: res.usage });
1924
1928
  }
1925
1929
  const validation = validateJsonSchema(c.tool.parameters, block.input);
1926
1930
  if (!validation.valid) {
1927
1931
  const detail = validation.issues.slice(0, 5).map((i) => `${i.path} ${i.message}`).join("; ");
1928
- throw new ToolInputError(`Tool "${c.tool.name}" returned invalid input: ${detail}`, { tool: c.tool.name });
1932
+ throw new ToolInputError(`Tool "${c.tool.name}" returned invalid input: ${detail}`, { tool: c.tool.name, usage: res.usage });
1929
1933
  }
1930
1934
  return { value: block.input, usage: res.usage };
1931
1935
  };
@@ -2102,6 +2106,11 @@ Every error is an OdlaAIError subclass with a stable \`code\` \u2014 branch on e
2102
2106
  config, auth, rate_limit, capability_unsupported, context_window, invalid_request,
2103
2107
  tool_input_invalid, cancelled, deadline_exceeded, provider_error.
2104
2108
 
2109
+ OdlaAIError.usage is an optional frozen token-usage record. It exists only when
2110
+ the provider returned a response before a later forced-tool/schema validation
2111
+ failure, and may be absent for every earlier failure. It is intentionally not
2112
+ included in toShape() or streaming OracleErrorShape.
2113
+
2105
2114
  ## Models
2106
2115
 
2107
2116
  ${models}