octaflow 0.17.3 → 0.19.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.
@@ -1,8 +1,9 @@
1
1
  import { E as RetryPolicy, g as TypedWorkflow, h as TypedStepContext, m as TypedStep, nt as Result, tt as FlowErrorShape } from "../index-BURAYZui.js";
2
2
  import { n as Logger } from "../logger-BCus3AOa.js";
3
3
  import { i as BuildStepContextArgs, o as WorkflowHooks } from "../hooks-BdxIcc92.js";
4
+ import { a as AiProviderMeta, c as DEFAULT_AI_PROVIDER, d as EMBEDDING_PROVIDER_IDS, f as EmbeddingProviderId, i as AiProviderId, l as DEFAULT_EMBEDDING_PROVIDER, m as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as AiProviderModelOption, p as EmbeddingProviderMeta, r as AiProcessingRegion, s as AiVendorResidency, t as AI_PROVIDERS, u as EMBEDDING_PROVIDERS } from "../provider-registry-bXGOmyeh.js";
4
5
  import { z } from "zod";
5
- import { EmbeddingModelV4, LanguageModelV4 } from "@ai-sdk/provider";
6
+ import { EmbeddingModelV4, LanguageModelV4, SharedV4ProviderMetadata } from "@ai-sdk/provider";
6
7
  //#region src/ai/instrumented-model.d.ts
7
8
  export interface AccumulatedUsage {
8
9
  inputTokens: number;
@@ -71,7 +72,15 @@ export interface TokenUsage {
71
72
  cacheReadTokens: number;
72
73
  cacheWriteTokens: number;
73
74
  }
74
- /** Default pricing (USD per million tokens), as of 2026-04. */
75
+ /**
76
+ * Default pricing, USD per million tokens — Anthropic as of 2026-09, Mistral and
77
+ * the OpenAI embeddings as of 2026-08. A best-effort estimate, not a billing
78
+ * record: a BYOK caller can name any model id, and an unknown id prices at the
79
+ * priciest row (safe overcounting — see {@link createCostEstimator}). Retired ids
80
+ * are kept rather than deleted: dropping one silently reprices every historical
81
+ * usage row at the fallback rate. Only the Mistral `-latest` aliases are listed;
82
+ * pinned ids (`mistral-medium-2508`) fall to the fallback.
83
+ */
75
84
  export declare const DEFAULT_MODEL_PRICING: Record<string, ModelPricing>;
76
85
  export interface CostEstimatorOptions {
77
86
  /** Pricing table keyed by model id. Defaults to `DEFAULT_MODEL_PRICING`. */
@@ -393,4 +402,145 @@ export interface AiUsageAggregationService {
393
402
  }
394
403
  /** Build a store-agnostic AI usage-aggregation service. */
395
404
  export declare function createAiUsageAggregationService(deps: CreateAiUsageAggregationServiceDeps): AiUsageAggregationService;
396
- //#endregion
405
+ //#endregion
406
+ //#region src/ai/refusal.d.ts
407
+ /** The unified finish reason the AI SDK reports for a declined generation. */
408
+ export declare const REFUSAL_FINISH_REASON = "content-filter";
409
+ /**
410
+ * What the provider said about a refusal. Every field is optional except the
411
+ * model: providers other than Anthropic report the refusal without the
412
+ * `stop_details` breakdown, and Anthropic itself only sends `category` and
413
+ * `explanation` for some categories.
414
+ */
415
+ export interface AiRefusalDetails {
416
+ /** The model that declined. */
417
+ modelId: string;
418
+ /** Provider's refusal category — Anthropic's `stop_details.category`. */
419
+ category?: string;
420
+ /** Provider's prose reason, when it sends one. */
421
+ explanation?: string;
422
+ /** The model the provider suggests retrying on (`stop_details.recommended_model`). */
423
+ recommendedModel?: string;
424
+ /** The provider's own stop reason (`refusal` on Anthropic), before unification. */
425
+ rawFinishReason?: string;
426
+ }
427
+ /**
428
+ * A generation the model declined to produce.
429
+ *
430
+ * Distinct from a transport failure on purpose: it is deterministic, so
431
+ * retrying the same prompt on the same model produces the same refusal. Callers
432
+ * that classify retryability must treat it as terminal — `defineAiStep` does
433
+ * this for every AI workflow step.
434
+ */
435
+ export declare class AiRefusalError extends Error {
436
+ readonly name = "AiRefusalError";
437
+ readonly details: AiRefusalDetails;
438
+ constructor(details: AiRefusalDetails);
439
+ }
440
+ /**
441
+ * True when `error` is an {@link AiRefusalError}, or wraps one as its `cause`.
442
+ *
443
+ * Walks the chain because the SDK layer between the middleware and the caller
444
+ * may re-wrap: `generateObject`, for instance, reports parse failures as
445
+ * `NoObjectGeneratedError` with the original as `cause`. Depth-capped and
446
+ * cycle-safe.
447
+ */
448
+ export declare function isAiRefusalError(error: unknown): error is AiRefusalError;
449
+ /**
450
+ * Pull the provider's refusal breakdown out of `providerMetadata`.
451
+ *
452
+ * Scans every provider namespace for a `stopDetails` record rather than reading
453
+ * `providerMetadata.anthropic` directly — the shape is Anthropic's today, and
454
+ * the surrounding module is deliberately provider-agnostic. A provider that
455
+ * reports a refusal with no breakdown simply yields no fields.
456
+ */
457
+ export declare function readRefusalDetails(providerMetadata: SharedV4ProviderMetadata | undefined): Omit<AiRefusalDetails, 'modelId'>;
458
+ /**
459
+ * Wrap a model so a declined generation throws {@link AiRefusalError} instead
460
+ * of returning empty content.
461
+ *
462
+ * **`doGenerate` only, on purpose.** One-shot calls — every AI workflow step,
463
+ * every ad-hoc generation service, both classifiers — have no way to present a
464
+ * refusal to anyone: their contract is "return the value or fail", so a refusal
465
+ * that does not throw becomes an empty field or an unparseable object several
466
+ * frames away from its cause. The streaming path is the opposite case: the help
467
+ * chat is a conversation, where a decline is a legitimate turn the operator
468
+ * should *read*, and it already has a refusal surface (the `refusal` data part)
469
+ * for exactly that. `ChatAgentService` branches on the finish reason in
470
+ * `onFinish` instead, so the stream stays a stream.
471
+ */
472
+ export declare function createRefusalAwareModel(model: LanguageModelV4): LanguageModelV4;
473
+ //#endregion
474
+ //#region src/ai/capabilities.d.ts
475
+ /**
476
+ * Per-model API capability switches.
477
+ *
478
+ * Claude 4.6+ models (Opus 4.6/4.7/4.8/5, Sonnet 4.6/5, Fable/Mythos 5) use
479
+ * adaptive thinking; on 4.7+ the legacy `thinking: { type: 'enabled',
480
+ * budget_tokens }` shape and non-default sampling params (`temperature`,
481
+ * `top_p`, `top_k`) are rejected with a 400. Older models (Haiku 4.5,
482
+ * Sonnet 4.5, …) still require `enabled` + budgetTokens for extended
483
+ * thinking and accept `temperature`.
484
+ *
485
+ * A BYOK deployment lets a caller name any model id, so call sites must
486
+ * branch on the resolved id rather than assume one API shape — these
487
+ * functions are that branch, written once. `providerId` is a plain string:
488
+ * only `'anthropic'` has provider-specific shapes today.
489
+ */
490
+ /** True for Claude 4.6+ / 5-family models: adaptive thinking, no budgetTokens, no sampling params. */
491
+ export declare function supportsAdaptiveThinking(modelId: string): boolean;
492
+ export interface AnthropicThinkingSettings {
493
+ temperature?: number;
494
+ headers?: Record<string, string>;
495
+ providerOptions: {
496
+ anthropic: {
497
+ thinking: {
498
+ type: 'adaptive';
499
+ display: 'summarized';
500
+ } | {
501
+ type: 'enabled';
502
+ budgetTokens: number;
503
+ };
504
+ };
505
+ };
506
+ }
507
+ /**
508
+ * Spreadable `streamText`/`generateText` settings for a thinking-enabled
509
+ * agent loop, shaped for the target model's API generation.
510
+ *
511
+ * - Adaptive models: `thinking: { type: 'adaptive' }` with summarized display
512
+ * (the default `omitted` streams empty reasoning parts to the client).
513
+ * Interleaved thinking is implicit — no beta header, no temperature.
514
+ * - Legacy models: extended thinking with a token budget, the interleaved
515
+ * thinking beta header, and the caller's temperature.
516
+ */
517
+ export declare function anthropicThinkingSettings(modelId: string, opts: {
518
+ temperature: number;
519
+ budgetTokens: number;
520
+ }): AnthropicThinkingSettings;
521
+ /**
522
+ * True when the provider+model pair supports Anthropic's tool search tool.
523
+ * Anthropic-only — like the thinking/beta-header shapes, `deferLoading`
524
+ * provider options and the search tool must never be emitted for another
525
+ * vendor. Unknown or pre-4.5 Anthropic model ids return false so callers
526
+ * fall back to shipping the full tool registry undeferred.
527
+ */
528
+ export declare function supportsToolSearch(providerId: string, modelId: string): boolean;
529
+ export interface ThinkingSettings {
530
+ temperature?: number;
531
+ headers?: Record<string, string>;
532
+ providerOptions?: AnthropicThinkingSettings['providerOptions'];
533
+ }
534
+ /**
535
+ * Provider-neutral wrapper around {@link anthropicThinkingSettings}. Other
536
+ * providers have no thinking/beta-header surface — they just get the caller's
537
+ * temperature. Never emit `providerOptions.anthropic` (or the beta header) for
538
+ * another vendor's model, even though it would be ignored on the wire — it
539
+ * pollutes request logs and breaks request-shape tests.
540
+ */
541
+ export declare function thinkingSettingsFor(providerId: string, modelId: string, opts: {
542
+ temperature: number;
543
+ budgetTokens: number;
544
+ }): ThinkingSettings;
545
+ //#endregion
546
+ export { AI_PROVIDERS, AI_PROVIDER_IDS, AiProcessingRegion, AiProviderId, AiProviderMeta, AiProviderModelOption, AiVendorResidency, DEFAULT_AI_PROVIDER, DEFAULT_EMBEDDING_PROVIDER, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, EmbeddingProviderId, EmbeddingProviderMeta, embeddingDimensionsFor };
package/dist/ai/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { S as ok, b as noopLogger, f as defineStep, l as buildWorkflow, x as err } from "../core-DNWPwFaE.js";
2
+ import { a as readRefusalDetails, i as isAiRefusalError, n as REFUSAL_FINISH_REASON, r as createRefusalAwareModel, t as AiRefusalError } from "../refusal-BsL2HPDb.js";
3
+ import { a as EMBEDDING_PROVIDERS, i as DEFAULT_EMBEDDING_PROVIDER, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as embeddingDimensionsFor, t as AI_PROVIDERS } from "../provider-registry-DcRDsjtT.js";
2
4
  import { wrapEmbeddingModel, wrapLanguageModel } from "ai";
3
5
  //#region src/ai/instrumented-model.ts
4
6
  function createUsageAccumulator() {
@@ -121,7 +123,15 @@ function createInstrumentedEmbeddingModel(model, accumulator) {
121
123
  }
122
124
  //#endregion
123
125
  //#region src/ai/cost.ts
124
- /** Default pricing (USD per million tokens), as of 2026-04. */
126
+ /**
127
+ * Default pricing, USD per million tokens — Anthropic as of 2026-09, Mistral and
128
+ * the OpenAI embeddings as of 2026-08. A best-effort estimate, not a billing
129
+ * record: a BYOK caller can name any model id, and an unknown id prices at the
130
+ * priciest row (safe overcounting — see {@link createCostEstimator}). Retired ids
131
+ * are kept rather than deleted: dropping one silently reprices every historical
132
+ * usage row at the fallback rate. Only the Mistral `-latest` aliases are listed;
133
+ * pinned ids (`mistral-medium-2508`) fall to the fallback.
134
+ */
125
135
  const DEFAULT_MODEL_PRICING = {
126
136
  "text-embedding-3-small": {
127
137
  inputPerMillion: .02,
@@ -135,6 +145,48 @@ const DEFAULT_MODEL_PRICING = {
135
145
  cacheReadPerMillion: 0,
136
146
  cacheWritePerMillion: 0
137
147
  },
148
+ "claude-fable-5": {
149
+ inputPerMillion: 10,
150
+ outputPerMillion: 50,
151
+ cacheReadPerMillion: 1,
152
+ cacheWritePerMillion: 12.5
153
+ },
154
+ "claude-mythos-5": {
155
+ inputPerMillion: 10,
156
+ outputPerMillion: 50,
157
+ cacheReadPerMillion: 1,
158
+ cacheWritePerMillion: 12.5
159
+ },
160
+ "claude-fable-5-1": {
161
+ inputPerMillion: 10,
162
+ outputPerMillion: 50,
163
+ cacheReadPerMillion: .25,
164
+ cacheWritePerMillion: 12.5
165
+ },
166
+ "claude-mythos-5-1": {
167
+ inputPerMillion: 10,
168
+ outputPerMillion: 50,
169
+ cacheReadPerMillion: .25,
170
+ cacheWritePerMillion: 12.5
171
+ },
172
+ "claude-opus-5": {
173
+ inputPerMillion: 5,
174
+ outputPerMillion: 25,
175
+ cacheReadPerMillion: .5,
176
+ cacheWritePerMillion: 6.25
177
+ },
178
+ "claude-sonnet-5": {
179
+ inputPerMillion: 2,
180
+ outputPerMillion: 10,
181
+ cacheReadPerMillion: .2,
182
+ cacheWritePerMillion: 2.5
183
+ },
184
+ "claude-opus-4-8": {
185
+ inputPerMillion: 5,
186
+ outputPerMillion: 25,
187
+ cacheReadPerMillion: .5,
188
+ cacheWritePerMillion: 6.25
189
+ },
138
190
  "claude-opus-4-7": {
139
191
  inputPerMillion: 5,
140
192
  outputPerMillion: 25,
@@ -142,10 +194,16 @@ const DEFAULT_MODEL_PRICING = {
142
194
  cacheWritePerMillion: 6.25
143
195
  },
144
196
  "claude-opus-4-6": {
145
- inputPerMillion: 15,
146
- outputPerMillion: 75,
147
- cacheReadPerMillion: 1.5,
148
- cacheWritePerMillion: 18.75
197
+ inputPerMillion: 5,
198
+ outputPerMillion: 25,
199
+ cacheReadPerMillion: .5,
200
+ cacheWritePerMillion: 6.25
201
+ },
202
+ "claude-opus-4-5": {
203
+ inputPerMillion: 5,
204
+ outputPerMillion: 25,
205
+ cacheReadPerMillion: .5,
206
+ cacheWritePerMillion: 6.25
149
207
  },
150
208
  "claude-sonnet-4-6": {
151
209
  inputPerMillion: 3,
@@ -153,17 +211,59 @@ const DEFAULT_MODEL_PRICING = {
153
211
  cacheReadPerMillion: .3,
154
212
  cacheWritePerMillion: 3.75
155
213
  },
214
+ "claude-sonnet-4-5": {
215
+ inputPerMillion: 3,
216
+ outputPerMillion: 15,
217
+ cacheReadPerMillion: .3,
218
+ cacheWritePerMillion: 3.75
219
+ },
156
220
  "claude-sonnet-4-5-20250929": {
157
221
  inputPerMillion: 3,
158
222
  outputPerMillion: 15,
159
223
  cacheReadPerMillion: .3,
160
224
  cacheWritePerMillion: 3.75
161
225
  },
226
+ "claude-haiku-4-5": {
227
+ inputPerMillion: 1,
228
+ outputPerMillion: 5,
229
+ cacheReadPerMillion: .1,
230
+ cacheWritePerMillion: 1.25
231
+ },
162
232
  "claude-haiku-4-5-20251001": {
163
- inputPerMillion: .8,
164
- outputPerMillion: 4,
165
- cacheReadPerMillion: .08,
166
- cacheWritePerMillion: 1
233
+ inputPerMillion: 1,
234
+ outputPerMillion: 5,
235
+ cacheReadPerMillion: .1,
236
+ cacheWritePerMillion: 1.25
237
+ },
238
+ "mistral-medium-latest": {
239
+ inputPerMillion: 1.5,
240
+ outputPerMillion: 7.5,
241
+ cacheReadPerMillion: .15,
242
+ cacheWritePerMillion: 1.5
243
+ },
244
+ "mistral-large-latest": {
245
+ inputPerMillion: .5,
246
+ outputPerMillion: 1.5,
247
+ cacheReadPerMillion: .05,
248
+ cacheWritePerMillion: .5
249
+ },
250
+ "mistral-small-latest": {
251
+ inputPerMillion: .15,
252
+ outputPerMillion: .6,
253
+ cacheReadPerMillion: .015,
254
+ cacheWritePerMillion: .15
255
+ },
256
+ "magistral-medium-latest": {
257
+ inputPerMillion: 2,
258
+ outputPerMillion: 5,
259
+ cacheReadPerMillion: .2,
260
+ cacheWritePerMillion: 2
261
+ },
262
+ "mistral-embed": {
263
+ inputPerMillion: .1,
264
+ outputPerMillion: 0,
265
+ cacheReadPerMillion: 0,
266
+ cacheWritePerMillion: 0
167
267
  }
168
268
  };
169
269
  function priciest(pricing) {
@@ -463,4 +563,91 @@ function createAiUsageAggregationService(deps) {
463
563
  };
464
564
  }
465
565
  //#endregion
466
- export { DEFAULT_AI_QUOTA, DEFAULT_MODEL_PRICING, buildAiWorkflow, createAiQuotaService, createAiUsageAggregationService, createAiWorkflowHooks, createCostEstimator, createEmbeddingUsageAccumulator, createInstrumentedEmbeddingModel, createInstrumentedModel, createUsageAccumulator, defineAiStep, estimateCostMicros };
566
+ //#region src/ai/capabilities.ts
567
+ /**
568
+ * Per-model API capability switches.
569
+ *
570
+ * Claude 4.6+ models (Opus 4.6/4.7/4.8/5, Sonnet 4.6/5, Fable/Mythos 5) use
571
+ * adaptive thinking; on 4.7+ the legacy `thinking: { type: 'enabled',
572
+ * budget_tokens }` shape and non-default sampling params (`temperature`,
573
+ * `top_p`, `top_k`) are rejected with a 400. Older models (Haiku 4.5,
574
+ * Sonnet 4.5, …) still require `enabled` + budgetTokens for extended
575
+ * thinking and accept `temperature`.
576
+ *
577
+ * A BYOK deployment lets a caller name any model id, so call sites must
578
+ * branch on the resolved id rather than assume one API shape — these
579
+ * functions are that branch, written once. `providerId` is a plain string:
580
+ * only `'anthropic'` has provider-specific shapes today.
581
+ */
582
+ const ADAPTIVE_THINKING_MODEL_PREFIXES = [
583
+ "claude-fable-5",
584
+ "claude-mythos-5",
585
+ "claude-opus-5",
586
+ "claude-opus-4-8",
587
+ "claude-opus-4-7",
588
+ "claude-opus-4-6",
589
+ "claude-sonnet-5",
590
+ "claude-sonnet-4-6"
591
+ ];
592
+ /** True for Claude 4.6+ / 5-family models: adaptive thinking, no budgetTokens, no sampling params. */
593
+ function supportsAdaptiveThinking(modelId) {
594
+ return ADAPTIVE_THINKING_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix));
595
+ }
596
+ /**
597
+ * Spreadable `streamText`/`generateText` settings for a thinking-enabled
598
+ * agent loop, shaped for the target model's API generation.
599
+ *
600
+ * - Adaptive models: `thinking: { type: 'adaptive' }` with summarized display
601
+ * (the default `omitted` streams empty reasoning parts to the client).
602
+ * Interleaved thinking is implicit — no beta header, no temperature.
603
+ * - Legacy models: extended thinking with a token budget, the interleaved
604
+ * thinking beta header, and the caller's temperature.
605
+ */
606
+ function anthropicThinkingSettings(modelId, opts) {
607
+ if (supportsAdaptiveThinking(modelId)) return { providerOptions: { anthropic: { thinking: {
608
+ type: "adaptive",
609
+ display: "summarized"
610
+ } } } };
611
+ return {
612
+ temperature: opts.temperature,
613
+ headers: { "anthropic-beta": "interleaved-thinking-2025-05-14" },
614
+ providerOptions: { anthropic: { thinking: {
615
+ type: "enabled",
616
+ budgetTokens: opts.budgetTokens
617
+ } } }
618
+ };
619
+ }
620
+ /**
621
+ * Models supporting Anthropic's server-side tool search
622
+ * (`tool_search_tool_regex_20251119`) with deferred tool loading — every
623
+ * adaptive-thinking model plus the 4.5 generation.
624
+ */
625
+ const TOOL_SEARCH_MODEL_PREFIXES = [
626
+ ...ADAPTIVE_THINKING_MODEL_PREFIXES,
627
+ "claude-opus-4-5",
628
+ "claude-sonnet-4-5",
629
+ "claude-haiku-4-5"
630
+ ];
631
+ /**
632
+ * True when the provider+model pair supports Anthropic's tool search tool.
633
+ * Anthropic-only — like the thinking/beta-header shapes, `deferLoading`
634
+ * provider options and the search tool must never be emitted for another
635
+ * vendor. Unknown or pre-4.5 Anthropic model ids return false so callers
636
+ * fall back to shipping the full tool registry undeferred.
637
+ */
638
+ function supportsToolSearch(providerId, modelId) {
639
+ return providerId === "anthropic" && TOOL_SEARCH_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix));
640
+ }
641
+ /**
642
+ * Provider-neutral wrapper around {@link anthropicThinkingSettings}. Other
643
+ * providers have no thinking/beta-header surface — they just get the caller's
644
+ * temperature. Never emit `providerOptions.anthropic` (or the beta header) for
645
+ * another vendor's model, even though it would be ignored on the wire — it
646
+ * pollutes request logs and breaks request-shape tests.
647
+ */
648
+ function thinkingSettingsFor(providerId, modelId, opts) {
649
+ if (providerId === "anthropic") return anthropicThinkingSettings(modelId, opts);
650
+ return { temperature: opts.temperature };
651
+ }
652
+ //#endregion
653
+ export { AI_PROVIDERS, AI_PROVIDER_IDS, AiRefusalError, DEFAULT_AI_PROVIDER, DEFAULT_AI_QUOTA, DEFAULT_EMBEDDING_PROVIDER, DEFAULT_MODEL_PRICING, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, REFUSAL_FINISH_REASON, anthropicThinkingSettings, buildAiWorkflow, createAiQuotaService, createAiUsageAggregationService, createAiWorkflowHooks, createCostEstimator, createEmbeddingUsageAccumulator, createInstrumentedEmbeddingModel, createInstrumentedModel, createRefusalAwareModel, createUsageAccumulator, defineAiStep, embeddingDimensionsFor, estimateCostMicros, isAiRefusalError, readRefusalDetails, supportsAdaptiveThinking, supportsToolSearch, thinkingSettingsFor };
@@ -0,0 +1,69 @@
1
+ import { f as EmbeddingProviderId, i as AiProviderId } from "../../provider-registry-bXGOmyeh.js";
2
+ import { EmbeddingModelV4, LanguageModelV4 } from "@ai-sdk/provider";
3
+ //#region src/ai/providers/index.d.ts
4
+ export type LanguageModelFactory = (modelId: string) => LanguageModelV4;
5
+ export type EmbeddingModelFactory = (modelId: string) => EmbeddingModelV4;
6
+ /** A language-model factory for a provider, loading its SDK on first use. */
7
+ export declare function createLanguageModelFactory(providerId: AiProviderId, apiKey: string, baseURL?: string): Promise<LanguageModelFactory>;
8
+ /** An embedding-model factory for a provider, loading its SDK on first use. */
9
+ export declare function createEmbeddingModelFactory(providerId: EmbeddingProviderId, apiKey: string, baseURL?: string): Promise<EmbeddingModelFactory>;
10
+ export interface LanguageModelProvider {
11
+ /** Which vendor the key belongs to — gate provider-specific request shapes on this (see `thinkingSettingsFor`). */
12
+ providerId: AiProviderId;
13
+ /** Cheap model for classifier-style passes, from the registry. */
14
+ classifierModelId: string;
15
+ /**
16
+ * A model wrapped for refusal detection: a declined generation throws
17
+ * `AiRefusalError` from `doGenerate` instead of returning empty content.
18
+ * Make every one-shot call site go through this and the behaviour is universal.
19
+ */
20
+ languageModel(modelId?: string): LanguageModelV4;
21
+ }
22
+ export interface LanguageModelProviderConfig {
23
+ provider?: AiProviderId;
24
+ apiKey: string;
25
+ /** Overrides the registry's default model. */
26
+ defaultModel?: string;
27
+ /** Region-pinned endpoint, when the vendor serves one. Undefined = SDK default. */
28
+ baseUrl?: string;
29
+ }
30
+ /**
31
+ * The seam a BYOK deployment resolves per scope: key + provider → models.
32
+ *
33
+ * ```ts
34
+ * const models = await createLanguageModelProvider({ provider: 'mistral', apiKey });
35
+ * const result = await generateText({ model: models.languageModel(), prompt });
36
+ * ```
37
+ */
38
+ export declare function createLanguageModelProvider(config: LanguageModelProviderConfig): Promise<LanguageModelProvider>;
39
+ export interface EmbeddingModelProvider {
40
+ providerId: EmbeddingProviderId;
41
+ /** The resolved model id — what `embeddingDimensionsFor` should be asked about. */
42
+ modelId: string;
43
+ embeddingModel(): EmbeddingModelV4;
44
+ }
45
+ export interface EmbeddingModelProviderConfig {
46
+ provider?: EmbeddingProviderId;
47
+ apiKey: string;
48
+ /** Overrides the registry's default model. */
49
+ model?: string;
50
+ /** Region-pinned endpoint, when the vendor serves one. Undefined = SDK default. */
51
+ baseUrl?: string;
52
+ }
53
+ export declare function createEmbeddingModelProvider(config: EmbeddingModelProviderConfig): Promise<EmbeddingModelProvider>;
54
+ export interface LazyEmbeddingModelMeta {
55
+ /** Logged as the model's `provider`. */
56
+ provider: string;
57
+ modelId: string;
58
+ }
59
+ /**
60
+ * An `EmbeddingModelV4` whose provider is resolved on first use — for
61
+ * synchronous wiring (a DI registration, a registry built at boot) that
62
+ * cannot await {@link createEmbeddingModelProvider}. The V4 contract allows
63
+ * `maxEmbeddingsPerCall` / `supportsParallelCalls` to be promises, so the
64
+ * laziness is expressible without a wrapper the caller has to unwrap: `embed()`
65
+ * awaits them itself. `load` runs at most once; a failure is retried on the
66
+ * next call rather than cached.
67
+ */
68
+ export declare function createLazyEmbeddingModel(meta: LazyEmbeddingModelMeta, load: () => Promise<EmbeddingModelV4>): EmbeddingModelV4;
69
+ //#endregion
@@ -0,0 +1,107 @@
1
+ import { r as createRefusalAwareModel } from "../../refusal-BsL2HPDb.js";
2
+ import { a as EMBEDDING_PROVIDERS, t as AI_PROVIDERS } from "../../provider-registry-DcRDsjtT.js";
3
+ //#region src/ai/providers/index.ts
4
+ const LLM_LOADERS = {
5
+ anthropic: async (apiKey, baseURL) => {
6
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
7
+ return createAnthropic({
8
+ apiKey,
9
+ baseURL
10
+ });
11
+ },
12
+ mistral: async (apiKey, baseURL) => {
13
+ const { createMistral } = await import("@ai-sdk/mistral");
14
+ return createMistral({
15
+ apiKey,
16
+ baseURL
17
+ });
18
+ }
19
+ };
20
+ const EMBEDDING_LOADERS = {
21
+ openai: async (apiKey, baseURL) => {
22
+ const { createOpenAI } = await import("@ai-sdk/openai");
23
+ const provider = createOpenAI({
24
+ apiKey,
25
+ baseURL
26
+ });
27
+ return (modelId) => provider.embedding(modelId);
28
+ },
29
+ mistral: async (apiKey, baseURL) => {
30
+ const { createMistral } = await import("@ai-sdk/mistral");
31
+ const provider = createMistral({
32
+ apiKey,
33
+ baseURL
34
+ });
35
+ return (modelId) => provider.embedding(modelId);
36
+ }
37
+ };
38
+ /** A language-model factory for a provider, loading its SDK on first use. */
39
+ function createLanguageModelFactory(providerId, apiKey, baseURL) {
40
+ return LLM_LOADERS[providerId](apiKey, baseURL);
41
+ }
42
+ /** An embedding-model factory for a provider, loading its SDK on first use. */
43
+ function createEmbeddingModelFactory(providerId, apiKey, baseURL) {
44
+ return EMBEDDING_LOADERS[providerId](apiKey, baseURL);
45
+ }
46
+ /**
47
+ * The seam a BYOK deployment resolves per scope: key + provider → models.
48
+ *
49
+ * ```ts
50
+ * const models = await createLanguageModelProvider({ provider: 'mistral', apiKey });
51
+ * const result = await generateText({ model: models.languageModel(), prompt });
52
+ * ```
53
+ */
54
+ async function createLanguageModelProvider(config) {
55
+ const providerId = config.provider ?? "anthropic";
56
+ const meta = AI_PROVIDERS[providerId];
57
+ const factory = await createLanguageModelFactory(providerId, config.apiKey, config.baseUrl);
58
+ const defaultModel = config.defaultModel ?? meta.defaultModel;
59
+ return {
60
+ providerId,
61
+ classifierModelId: meta.classifierModel,
62
+ languageModel: (modelId) => createRefusalAwareModel(factory(modelId ?? defaultModel))
63
+ };
64
+ }
65
+ async function createEmbeddingModelProvider(config) {
66
+ const providerId = config.provider ?? "openai";
67
+ const factory = await createEmbeddingModelFactory(providerId, config.apiKey, config.baseUrl);
68
+ const modelId = config.model ?? EMBEDDING_PROVIDERS[providerId].defaultModel;
69
+ return {
70
+ providerId,
71
+ modelId,
72
+ embeddingModel: () => factory(modelId)
73
+ };
74
+ }
75
+ /**
76
+ * An `EmbeddingModelV4` whose provider is resolved on first use — for
77
+ * synchronous wiring (a DI registration, a registry built at boot) that
78
+ * cannot await {@link createEmbeddingModelProvider}. The V4 contract allows
79
+ * `maxEmbeddingsPerCall` / `supportsParallelCalls` to be promises, so the
80
+ * laziness is expressible without a wrapper the caller has to unwrap: `embed()`
81
+ * awaits them itself. `load` runs at most once; a failure is retried on the
82
+ * next call rather than cached.
83
+ */
84
+ function createLazyEmbeddingModel(meta, load) {
85
+ let pending;
86
+ const model = () => {
87
+ pending ??= load().catch((error) => {
88
+ pending = void 0;
89
+ throw error;
90
+ });
91
+ return pending;
92
+ };
93
+ return {
94
+ specificationVersion: "v4",
95
+ provider: meta.provider,
96
+ modelId: meta.modelId,
97
+ get maxEmbeddingsPerCall() {
98
+ return model().then((m) => m.maxEmbeddingsPerCall);
99
+ },
100
+ get supportsParallelCalls() {
101
+ return model().then((m) => m.supportsParallelCalls);
102
+ },
103
+ doEmbed: (options) => model().then((m) => m.doEmbed(options))
104
+ };
105
+ }
106
+ //#endregion
107
+ export { createEmbeddingModelFactory, createEmbeddingModelProvider, createLanguageModelFactory, createLanguageModelProvider, createLazyEmbeddingModel };
@@ -0,0 +1,2 @@
1
+ import { a as AiProviderMeta, c as DEFAULT_AI_PROVIDER, d as EMBEDDING_PROVIDER_IDS, f as EmbeddingProviderId, i as AiProviderId, l as DEFAULT_EMBEDDING_PROVIDER, m as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as AiProviderModelOption, p as EmbeddingProviderMeta, r as AiProcessingRegion, s as AiVendorResidency, t as AI_PROVIDERS, u as EMBEDDING_PROVIDERS } from "../provider-registry-bXGOmyeh.js";
2
+ export { AI_PROVIDERS, AI_PROVIDER_IDS, AiProcessingRegion, AiProviderId, AiProviderMeta, AiProviderModelOption, AiVendorResidency, DEFAULT_AI_PROVIDER, DEFAULT_EMBEDDING_PROVIDER, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, EmbeddingProviderId, EmbeddingProviderMeta, embeddingDimensionsFor };
@@ -0,0 +1,2 @@
1
+ import { a as EMBEDDING_PROVIDERS, i as DEFAULT_EMBEDDING_PROVIDER, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as embeddingDimensionsFor, t as AI_PROVIDERS } from "../provider-registry-DcRDsjtT.js";
2
+ export { AI_PROVIDERS, AI_PROVIDER_IDS, DEFAULT_AI_PROVIDER, DEFAULT_EMBEDDING_PROVIDER, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, embeddingDimensionsFor };
@@ -0,0 +1,121 @@
1
+ //#region src/ai/provider-registry.ts
2
+ /**
3
+ * The provider registry — pure metadata about the vendors a BYOK deployment
4
+ * can point at: ids, default and cheap models, where a key is created, and
5
+ * **where the vendor processes what you send it**. Dependency-free, so a
6
+ * browser bundle (a provider/model picker, a residency badge) can import it;
7
+ * the SDK factories that need `@ai-sdk/*` live in `octaflow/ai/providers`.
8
+ *
9
+ * Adding a provider: an id here with its metadata, a factory in
10
+ * `providers/`, and pricing rows in `cost.ts`.
11
+ */
12
+ const AI_PROVIDER_IDS = ["anthropic", "mistral"];
13
+ const EMBEDDING_PROVIDER_IDS = ["openai", "mistral"];
14
+ /** What an unset provider resolves to. */
15
+ const DEFAULT_AI_PROVIDER = "anthropic";
16
+ const DEFAULT_EMBEDDING_PROVIDER = "openai";
17
+ const AI_PROVIDERS = {
18
+ anthropic: {
19
+ id: "anthropic",
20
+ label: "Anthropic",
21
+ residency: {
22
+ region: "third-country",
23
+ legalName: "Anthropic PBC",
24
+ country: "US"
25
+ },
26
+ defaultModel: "claude-sonnet-5",
27
+ classifierModel: "claude-haiku-4-5",
28
+ testModel: "claude-haiku-4-5",
29
+ keyPrefix: "sk-ant-",
30
+ consoleKeyUrl: "https://console.anthropic.com/settings/keys",
31
+ modelOptions: [
32
+ {
33
+ label: "Claude Opus 5",
34
+ value: "claude-opus-5"
35
+ },
36
+ {
37
+ label: "Claude Opus 4.8",
38
+ value: "claude-opus-4-8"
39
+ },
40
+ {
41
+ label: "Claude Sonnet 5",
42
+ value: "claude-sonnet-5"
43
+ },
44
+ {
45
+ label: "Claude Haiku 4.5",
46
+ value: "claude-haiku-4-5"
47
+ }
48
+ ]
49
+ },
50
+ mistral: {
51
+ id: "mistral",
52
+ label: "Mistral",
53
+ residency: {
54
+ region: "eu",
55
+ legalName: "Mistral AI SAS",
56
+ country: "FR"
57
+ },
58
+ defaultModel: "mistral-medium-latest",
59
+ classifierModel: "mistral-small-latest",
60
+ testModel: "mistral-small-latest",
61
+ keyPrefix: null,
62
+ consoleKeyUrl: "https://console.mistral.ai/api-keys",
63
+ modelOptions: [
64
+ {
65
+ label: "Mistral Medium 3.5",
66
+ value: "mistral-medium-latest"
67
+ },
68
+ {
69
+ label: "Mistral Large 3",
70
+ value: "mistral-large-latest"
71
+ },
72
+ {
73
+ label: "Mistral Small 4",
74
+ value: "mistral-small-latest"
75
+ }
76
+ ]
77
+ }
78
+ };
79
+ const EMBEDDING_PROVIDERS = {
80
+ openai: {
81
+ id: "openai",
82
+ label: "OpenAI",
83
+ residency: {
84
+ region: "third-country",
85
+ legalName: "OpenAI, L.L.C.",
86
+ country: "US"
87
+ },
88
+ defaultModel: "text-embedding-3-small",
89
+ consoleKeyUrl: "https://platform.openai.com/api-keys",
90
+ dimensionsByModel: {
91
+ "text-embedding-3-small": 1536,
92
+ "text-embedding-3-large": 3072
93
+ }
94
+ },
95
+ mistral: {
96
+ id: "mistral",
97
+ label: "Mistral",
98
+ residency: {
99
+ region: "eu",
100
+ legalName: "Mistral AI SAS",
101
+ country: "FR"
102
+ },
103
+ defaultModel: "mistral-embed",
104
+ consoleKeyUrl: "https://console.mistral.ai/api-keys",
105
+ dimensionsByModel: { "mistral-embed": 1024 }
106
+ }
107
+ };
108
+ /**
109
+ * Vector dimensions for an embedding model id. Unknown ids fall back to
110
+ * `fallback` (default 1536, the common OpenAI-compatible dimension) so
111
+ * free-form model ids keep a working column.
112
+ */
113
+ function embeddingDimensionsFor(modelId, fallback = 1536) {
114
+ for (const meta of Object.values(EMBEDDING_PROVIDERS)) {
115
+ const dims = meta.dimensionsByModel[modelId];
116
+ if (dims !== void 0) return dims;
117
+ }
118
+ return fallback;
119
+ }
120
+ //#endregion
121
+ export { EMBEDDING_PROVIDERS as a, DEFAULT_EMBEDDING_PROVIDER as i, AI_PROVIDER_IDS as n, EMBEDDING_PROVIDER_IDS as o, DEFAULT_AI_PROVIDER as r, embeddingDimensionsFor as s, AI_PROVIDERS as t };
@@ -0,0 +1,81 @@
1
+ //#region src/ai/provider-registry.d.ts
2
+ /**
3
+ * The provider registry — pure metadata about the vendors a BYOK deployment
4
+ * can point at: ids, default and cheap models, where a key is created, and
5
+ * **where the vendor processes what you send it**. Dependency-free, so a
6
+ * browser bundle (a provider/model picker, a residency badge) can import it;
7
+ * the SDK factories that need `@ai-sdk/*` live in `octaflow/ai/providers`.
8
+ *
9
+ * Adding a provider: an id here with its metadata, a factory in
10
+ * `providers/`, and pricing rows in `cost.ts`.
11
+ */
12
+ declare const AI_PROVIDER_IDS: readonly ['anthropic', 'mistral'];
13
+ type AiProviderId = (typeof AI_PROVIDER_IDS)[number];
14
+ declare const EMBEDDING_PROVIDER_IDS: readonly ['openai', 'mistral'];
15
+ type EmbeddingProviderId = (typeof EMBEDDING_PROVIDER_IDS)[number];
16
+ /** What an unset provider resolves to. */
17
+ declare const DEFAULT_AI_PROVIDER: AiProviderId;
18
+ declare const DEFAULT_EMBEDDING_PROVIDER: EmbeddingProviderId;
19
+ /**
20
+ * Where a provider processes the payloads you send it.
21
+ *
22
+ * `'eu'` means the vendor is established in the EU/EEA and serves the API from
23
+ * there, so a call carries no third-country transfer under Art. 44 ff. GDPR.
24
+ * `'third-country'` means it does — your data-processing agreement then has
25
+ * to carry a transfer mechanism for that vendor.
26
+ *
27
+ * This is metadata about the *vendor endpoint called*, not a promise about a
28
+ * customer's own BYOK key: a customer who brings their own key contracts with
29
+ * the vendor directly.
30
+ */
31
+ type AiProcessingRegion = 'eu' | 'third-country';
32
+ interface AiVendorResidency {
33
+ region: AiProcessingRegion;
34
+ /** Legal entity, as it appears in a subprocessor annex. */
35
+ legalName: string;
36
+ /** Country of establishment, ISO 3166-1 alpha-2. */
37
+ country: string;
38
+ }
39
+ interface AiProviderModelOption {
40
+ label: string;
41
+ value: string;
42
+ }
43
+ interface AiProviderMeta {
44
+ id: AiProviderId;
45
+ label: string;
46
+ /** Where calls to this provider are processed — drives a residency badge and the subprocessor analysis. */
47
+ residency: AiVendorResidency;
48
+ /** Default chat/workflow model when nothing sets one. */
49
+ defaultModel: string;
50
+ /** Cheap model for classifier-style passes. */
51
+ classifierModel: string;
52
+ /** Cheap model for a "test connection" ping. */
53
+ testModel: string;
54
+ /** Well-known API key prefix for a soft format hint, or null when the provider has none. */
55
+ keyPrefix: string | null;
56
+ /** Where the operator creates an API key. */
57
+ consoleKeyUrl: string;
58
+ /** Curated model choices for a picker (free-form custom ids stay allowed). */
59
+ modelOptions: AiProviderModelOption[];
60
+ }
61
+ declare const AI_PROVIDERS: Record<AiProviderId, AiProviderMeta>;
62
+ interface EmbeddingProviderMeta {
63
+ id: EmbeddingProviderId;
64
+ label: string;
65
+ /** See {@link AiProviderMeta.residency}. */
66
+ residency: AiVendorResidency;
67
+ defaultModel: string;
68
+ /** Where the operator creates an API key. */
69
+ consoleKeyUrl: string;
70
+ /** Vector dimensions per known model id — must match the `vector(N)` column in your schema. */
71
+ dimensionsByModel: Record<string, number>;
72
+ }
73
+ declare const EMBEDDING_PROVIDERS: Record<EmbeddingProviderId, EmbeddingProviderMeta>;
74
+ /**
75
+ * Vector dimensions for an embedding model id. Unknown ids fall back to
76
+ * `fallback` (default 1536, the common OpenAI-compatible dimension) so
77
+ * free-form model ids keep a working column.
78
+ */
79
+ declare function embeddingDimensionsFor(modelId: string, fallback?: number): number;
80
+ //#endregion
81
+ export { AiProviderMeta as a, DEFAULT_AI_PROVIDER as c, EMBEDDING_PROVIDER_IDS as d, EmbeddingProviderId as f, AiProviderId as i, DEFAULT_EMBEDDING_PROVIDER as l, embeddingDimensionsFor as m, AI_PROVIDER_IDS as n, AiProviderModelOption as o, EmbeddingProviderMeta as p, AiProcessingRegion as r, AiVendorResidency as s, AI_PROVIDERS as t, EMBEDDING_PROVIDERS as u };
@@ -0,0 +1,104 @@
1
+ import { wrapLanguageModel } from "ai";
2
+ //#region src/ai/refusal.ts
3
+ /** The unified finish reason the AI SDK reports for a declined generation. */
4
+ const REFUSAL_FINISH_REASON = "content-filter";
5
+ /**
6
+ * A generation the model declined to produce.
7
+ *
8
+ * Distinct from a transport failure on purpose: it is deterministic, so
9
+ * retrying the same prompt on the same model produces the same refusal. Callers
10
+ * that classify retryability must treat it as terminal — `defineAiStep` does
11
+ * this for every AI workflow step.
12
+ */
13
+ var AiRefusalError = class extends Error {
14
+ name = "AiRefusalError";
15
+ details;
16
+ constructor(details) {
17
+ super(refusalMessage(details));
18
+ this.details = details;
19
+ }
20
+ };
21
+ function refusalMessage(details) {
22
+ const parts = [`${details.modelId} declined to answer`];
23
+ if (details.category) parts.push(`(${details.category})`);
24
+ if (details.explanation) parts.push(`— ${details.explanation}`);
25
+ return parts.join(" ");
26
+ }
27
+ /** How deep to follow `cause` before giving up. Guards against pathological chains. */
28
+ const MAX_CAUSE_DEPTH = 8;
29
+ /**
30
+ * True when `error` is an {@link AiRefusalError}, or wraps one as its `cause`.
31
+ *
32
+ * Walks the chain because the SDK layer between the middleware and the caller
33
+ * may re-wrap: `generateObject`, for instance, reports parse failures as
34
+ * `NoObjectGeneratedError` with the original as `cause`. Depth-capped and
35
+ * cycle-safe.
36
+ */
37
+ function isAiRefusalError(error) {
38
+ const seen = /* @__PURE__ */ new Set();
39
+ let current = error;
40
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {
41
+ if (typeof current !== "object" || current === null || seen.has(current)) return false;
42
+ if (current instanceof AiRefusalError) return true;
43
+ seen.add(current);
44
+ current = current.cause;
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * Pull the provider's refusal breakdown out of `providerMetadata`.
50
+ *
51
+ * Scans every provider namespace for a `stopDetails` record rather than reading
52
+ * `providerMetadata.anthropic` directly — the shape is Anthropic's today, and
53
+ * the surrounding module is deliberately provider-agnostic. A provider that
54
+ * reports a refusal with no breakdown simply yields no fields.
55
+ */
56
+ function readRefusalDetails(providerMetadata) {
57
+ for (const namespace of Object.values(providerMetadata ?? {})) {
58
+ const stopDetails = namespace?.stopDetails;
59
+ if (typeof stopDetails !== "object" || stopDetails === null || Array.isArray(stopDetails)) continue;
60
+ const read = (key) => {
61
+ const value = stopDetails[key];
62
+ return typeof value === "string" && value.length > 0 ? value : void 0;
63
+ };
64
+ return {
65
+ ...read("category") ? { category: read("category") } : {},
66
+ ...read("explanation") ? { explanation: read("explanation") } : {},
67
+ ...read("recommendedModel") ? { recommendedModel: read("recommendedModel") } : {}
68
+ };
69
+ }
70
+ return {};
71
+ }
72
+ /**
73
+ * Wrap a model so a declined generation throws {@link AiRefusalError} instead
74
+ * of returning empty content.
75
+ *
76
+ * **`doGenerate` only, on purpose.** One-shot calls — every AI workflow step,
77
+ * every ad-hoc generation service, both classifiers — have no way to present a
78
+ * refusal to anyone: their contract is "return the value or fail", so a refusal
79
+ * that does not throw becomes an empty field or an unparseable object several
80
+ * frames away from its cause. The streaming path is the opposite case: the help
81
+ * chat is a conversation, where a decline is a legitimate turn the operator
82
+ * should *read*, and it already has a refusal surface (the `refusal` data part)
83
+ * for exactly that. `ChatAgentService` branches on the finish reason in
84
+ * `onFinish` instead, so the stream stays a stream.
85
+ */
86
+ function createRefusalAwareModel(model) {
87
+ return wrapLanguageModel({
88
+ model,
89
+ middleware: {
90
+ specificationVersion: "v4",
91
+ wrapGenerate: async ({ doGenerate }) => {
92
+ const result = await doGenerate();
93
+ if (result.finishReason.unified === "content-filter") throw new AiRefusalError({
94
+ modelId: model.modelId,
95
+ ...readRefusalDetails(result.providerMetadata),
96
+ ...result.finishReason.raw ? { rawFinishReason: result.finishReason.raw } : {}
97
+ });
98
+ return result;
99
+ }
100
+ }
101
+ });
102
+ }
103
+ //#endregion
104
+ export { readRefusalDetails as a, isAiRefusalError as i, REFUSAL_FINISH_REASON as n, createRefusalAwareModel as r, AiRefusalError as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octaflow",
3
- "version": "0.17.3",
3
+ "version": "0.19.0",
4
4
  "description": "Durable DAG workflow engine: Zod-typed steps over Postgres + pg-boss, with an optional AI add-on (token/cost/quota). Layered single package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,6 +24,16 @@
24
24
  "types": "./dist/dispatcher-pgboss/index.d.ts",
25
25
  "import": "./dist/dispatcher-pgboss/index.js",
26
26
  "default": "./dist/dispatcher-pgboss/index.js"
27
+ },
28
+ "./ai/providers": {
29
+ "types": "./dist/ai/providers/index.d.ts",
30
+ "import": "./dist/ai/providers/index.js",
31
+ "default": "./dist/ai/providers/index.js"
32
+ },
33
+ "./ai/registry": {
34
+ "types": "./dist/ai/registry.d.ts",
35
+ "import": "./dist/ai/registry.js",
36
+ "default": "./dist/ai/registry.js"
27
37
  }
28
38
  },
29
39
  "engines": {
@@ -58,6 +68,9 @@
58
68
  "zod"
59
69
  ],
60
70
  "peerDependencies": {
71
+ "@ai-sdk/anthropic": "^4.0.0",
72
+ "@ai-sdk/mistral": "^4.0.0",
73
+ "@ai-sdk/openai": "^4.0.0",
61
74
  "@ai-sdk/provider": "^4.0.0",
62
75
  "ai": "^7.0.0",
63
76
  "pg": "^8.0.0",
@@ -66,6 +79,15 @@
66
79
  "zod": "^4.0.0"
67
80
  },
68
81
  "peerDependenciesMeta": {
82
+ "@ai-sdk/anthropic": {
83
+ "optional": true
84
+ },
85
+ "@ai-sdk/mistral": {
86
+ "optional": true
87
+ },
88
+ "@ai-sdk/openai": {
89
+ "optional": true
90
+ },
69
91
  "@ai-sdk/provider": {
70
92
  "optional": true
71
93
  },
@@ -80,6 +102,9 @@
80
102
  }
81
103
  },
82
104
  "devDependencies": {
105
+ "@ai-sdk/anthropic": "^4.0.49",
106
+ "@ai-sdk/mistral": "^4.0.39",
107
+ "@ai-sdk/openai": "^4.0.58",
83
108
  "@ai-sdk/provider": "^4.0.10",
84
109
  "@testcontainers/postgresql": "^12.1.0",
85
110
  "@types/node": "^26.4.1",