@falai/agent 3.2.5 → 3.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.
Files changed (71) hide show
  1. package/dist/cjs/index.d.ts +4 -0
  2. package/dist/cjs/index.d.ts.map +1 -1
  3. package/dist/cjs/index.js +6 -2
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/providers/AnthropicProvider.d.ts +6 -2
  6. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  7. package/dist/cjs/providers/AnthropicProvider.js +2 -0
  8. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  9. package/dist/cjs/providers/FallbackAiProvider.d.ts +26 -0
  10. package/dist/cjs/providers/FallbackAiProvider.d.ts.map +1 -0
  11. package/dist/cjs/providers/FallbackAiProvider.js +54 -0
  12. package/dist/cjs/providers/FallbackAiProvider.js.map +1 -0
  13. package/dist/cjs/providers/GeminiProvider.d.ts +5 -1
  14. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  15. package/dist/cjs/providers/GeminiProvider.js +2 -0
  16. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  17. package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  18. package/dist/cjs/providers/OpenAICompatibleProvider.js +2 -0
  19. package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
  20. package/dist/cjs/providers/ProviderAdapter.d.ts +8 -1
  21. package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
  22. package/dist/cjs/providers/ProviderAdapter.js +23 -1
  23. package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
  24. package/dist/cjs/providers/ZaiProvider.d.ts +45 -0
  25. package/dist/cjs/providers/ZaiProvider.d.ts.map +1 -0
  26. package/dist/cjs/providers/ZaiProvider.js +51 -0
  27. package/dist/cjs/providers/ZaiProvider.js.map +1 -0
  28. package/dist/cjs/providers/index.d.ts +2 -0
  29. package/dist/cjs/providers/index.d.ts.map +1 -1
  30. package/dist/cjs/providers/index.js +3 -1
  31. package/dist/cjs/providers/index.js.map +1 -1
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +2 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/providers/AnthropicProvider.d.ts +6 -2
  37. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  38. package/dist/providers/AnthropicProvider.js +2 -0
  39. package/dist/providers/AnthropicProvider.js.map +1 -1
  40. package/dist/providers/FallbackAiProvider.d.ts +26 -0
  41. package/dist/providers/FallbackAiProvider.d.ts.map +1 -0
  42. package/dist/providers/FallbackAiProvider.js +50 -0
  43. package/dist/providers/FallbackAiProvider.js.map +1 -0
  44. package/dist/providers/GeminiProvider.d.ts +5 -1
  45. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  46. package/dist/providers/GeminiProvider.js +2 -0
  47. package/dist/providers/GeminiProvider.js.map +1 -1
  48. package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
  49. package/dist/providers/OpenAICompatibleProvider.js +2 -0
  50. package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
  51. package/dist/providers/ProviderAdapter.d.ts +8 -1
  52. package/dist/providers/ProviderAdapter.d.ts.map +1 -1
  53. package/dist/providers/ProviderAdapter.js +24 -2
  54. package/dist/providers/ProviderAdapter.js.map +1 -1
  55. package/dist/providers/ZaiProvider.d.ts +45 -0
  56. package/dist/providers/ZaiProvider.d.ts.map +1 -0
  57. package/dist/providers/ZaiProvider.js +47 -0
  58. package/dist/providers/ZaiProvider.js.map +1 -0
  59. package/dist/providers/index.d.ts +2 -0
  60. package/dist/providers/index.d.ts.map +1 -1
  61. package/dist/providers/index.js +1 -0
  62. package/dist/providers/index.js.map +1 -1
  63. package/package.json +2 -2
  64. package/src/index.ts +4 -0
  65. package/src/providers/AnthropicProvider.ts +8 -3
  66. package/src/providers/FallbackAiProvider.ts +94 -0
  67. package/src/providers/GeminiProvider.ts +7 -2
  68. package/src/providers/OpenAICompatibleProvider.ts +2 -0
  69. package/src/providers/ProviderAdapter.ts +31 -1
  70. package/src/providers/ZaiProvider.ts +72 -0
  71. package/src/providers/index.ts +3 -0
@@ -8,18 +8,21 @@
8
8
  * `@providerkit/core`; this file is the constructor.
9
9
  */
10
10
 
11
- import { createAnthropicProvider } from "@providerkit/core";
11
+ import { createAnthropicProvider, type FallbackOptions, type Provider } from "@providerkit/core";
12
12
 
13
- import type { ProviderCapabilities } from "../types/ai.js";
13
+ import type { AiProvider, ProviderCapabilities } from "../types/ai.js";
14
14
  import { ProviderAdapter, type RequestConfig } from "./ProviderAdapter.js";
15
15
 
16
16
  export interface AnthropicProviderOptions {
17
17
  /** Anthropic API key */
18
18
  apiKey: string;
19
- /** Model to use (required) — e.g. "claude-sonnet-5", "claude-opus-5" */
19
+ /** Model to use (required) — e.g. "claude-sonnet-5" or "claude-opus-5" */
20
20
  model: string;
21
21
  /** Backup models to try if the primary fails (default: []) */
22
22
  backupModels?: string[];
23
+ /** Fallback providers to try if this provider fails/exhausts */
24
+ fallbacks?: Array<AiProvider | Provider>;
25
+ fallbackOptions?: FallbackOptions<Provider>;
23
26
  /** Any endpoint speaking the Anthropic Messages dialect. */
24
27
  baseUrl?: string;
25
28
  /** Request defaults sent with every call */
@@ -65,6 +68,8 @@ export class AnthropicProvider extends ProviderAdapter {
65
68
  model: options.model,
66
69
  ...(options.config ? { defaults: options.config } : {}),
67
70
  ...(options.backupModels ? { backupModels: options.backupModels } : {}),
71
+ ...(options.fallbacks ? { fallbacks: options.fallbacks } : {}),
72
+ ...(options.fallbackOptions ? { fallbackOptions: options.fallbackOptions } : {}),
68
73
  ...(options.retryConfig ? { retryConfig: options.retryConfig } : {}),
69
74
  });
70
75
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Fallback AI Provider: runs an ordered list of AiProviders.
3
+ *
4
+ * Each child AiProvider is tried in turn using `@providerkit/core`'s FallbackPool,
5
+ * preserving per-provider cooldowns (e.g. 5h or weekly rate limit / quota resets)
6
+ * across calls.
7
+ */
8
+
9
+ import {
10
+ FallbackPool,
11
+ type FallbackOptions,
12
+ type ErrorKind,
13
+ } from "@providerkit/core";
14
+
15
+ import type {
16
+ AgentStructuredResponse,
17
+ AiProvider,
18
+ GenerateMessageInput,
19
+ GenerateMessageOutput,
20
+ GenerateMessageStreamChunk,
21
+ ProviderCapabilities,
22
+ } from "../types/ai.js";
23
+ import { logger } from "../utils/logger.js";
24
+
25
+ export interface FallbackAiProviderOptions {
26
+ /** The ordered list of providers to try. The first is primary. */
27
+ providers: AiProvider[];
28
+ /** Optional cooldown configuration overrides. */
29
+ cooldownMs?: Partial<Record<ErrorKind, number | null>>;
30
+ /** Optional callback when a provider enters cooldown. */
31
+ onCooldown?: FallbackOptions<AiProvider>["onCooldown"];
32
+ }
33
+
34
+ export class FallbackAiProvider implements AiProvider {
35
+ public readonly name: string;
36
+ public readonly capabilities: ProviderCapabilities;
37
+ public readonly pool: FallbackPool<AiProvider>;
38
+
39
+ constructor(options: FallbackAiProviderOptions) {
40
+ if (!options.providers || options.providers.length === 0) {
41
+ throw new Error("FallbackAiProvider requires at least one provider");
42
+ }
43
+
44
+ this.name = `fallback(${options.providers.map((p) => p.name).join("->")})`;
45
+
46
+ // Capabilities represent the intersection of supported critical features
47
+ // while inheriting primary's defaults.
48
+ this.capabilities = {
49
+ supportsTools: options.providers.every((p) => p.capabilities.supportsTools),
50
+ supportsNativeJsonSchema: options.providers.every(
51
+ (p) => p.capabilities.supportsNativeJsonSchema,
52
+ ),
53
+ supportsStreaming: options.providers.every((p) => p.capabilities.supportsStreaming),
54
+ supportsStreamingToolCalls: options.providers.every(
55
+ (p) => p.capabilities.supportsStreamingToolCalls,
56
+ ),
57
+ supportsPromptCaching: options.providers.every((p) => p.capabilities.supportsPromptCaching),
58
+ };
59
+
60
+ this.pool = new FallbackPool<AiProvider>(options.providers, {
61
+ ...(options.cooldownMs ? { cooldownMs: options.cooldownMs } : {}),
62
+ onCooldown: (info) => {
63
+ logger.warn(
64
+ `[${this.name}] Provider "${info.candidate.name}" cooled down until ${new Date(
65
+ info.retryAtMs,
66
+ ).toISOString()} (${info.kind}${info.window ? ` / ${info.window}` : ""})`,
67
+ );
68
+ options.onCooldown?.(info);
69
+ },
70
+ });
71
+ }
72
+
73
+ async generateMessage<TContext = unknown, TStructured = AgentStructuredResponse>(
74
+ input: GenerateMessageInput<TContext>,
75
+ ): Promise<GenerateMessageOutput<TStructured>> {
76
+ return this.pool.with(async (provider, signal) => {
77
+ return provider.generateMessage<TContext, TStructured>({
78
+ ...input,
79
+ signal,
80
+ });
81
+ }, input.signal);
82
+ }
83
+
84
+ async *generateMessageStream<TContext = unknown, TStructured = AgentStructuredResponse>(
85
+ input: GenerateMessageInput<TContext>,
86
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
87
+ yield* this.pool.stream(async function* (provider, signal) {
88
+ yield* provider.generateMessageStream<TContext, TStructured>({
89
+ ...input,
90
+ signal,
91
+ });
92
+ }, input.signal);
93
+ }
94
+ }
@@ -10,9 +10,9 @@
10
10
  * holds — a sibling codebase runs the combination in production today.
11
11
  */
12
12
 
13
- import { createGeminiProvider } from "@providerkit/core";
13
+ import { createGeminiProvider, type FallbackOptions, type Provider } from "@providerkit/core";
14
14
 
15
- import type { ProviderCapabilities } from "../types/ai.js";
15
+ import type { AiProvider, ProviderCapabilities } from "../types/ai.js";
16
16
  import type { JsonWithTools } from "./OpenAICompatibleProvider.js";
17
17
  import { ProviderAdapter, type RequestConfig } from "./ProviderAdapter.js";
18
18
 
@@ -23,6 +23,9 @@ export interface GeminiProviderOptions {
23
23
  model: string;
24
24
  /** Backup models to try if the primary fails (default: []) */
25
25
  backupModels?: string[];
26
+ /** Fallback providers to try if this provider fails/exhausts */
27
+ fallbacks?: Array<AiProvider | Provider>;
28
+ fallbackOptions?: FallbackOptions<Provider>;
26
29
  /** Any endpoint speaking the Gemini generateContent dialect. */
27
30
  baseUrl?: string;
28
31
  /**
@@ -71,6 +74,8 @@ export class GeminiProvider extends ProviderAdapter {
71
74
  model: options.model,
72
75
  ...(options.config ? { defaults: options.config } : {}),
73
76
  ...(options.backupModels ? { backupModels: options.backupModels } : {}),
77
+ ...(options.fallbacks ? { fallbacks: options.fallbacks } : {}),
78
+ ...(options.fallbackOptions ? { fallbackOptions: options.fallbackOptions } : {}),
74
79
  ...(options.retryConfig ? { retryConfig: options.retryConfig } : {}),
75
80
  });
76
81
  }
@@ -72,6 +72,8 @@ export abstract class OpenAICompatibleProvider extends ProviderAdapter {
72
72
  model: init.model,
73
73
  ...(init.config ? { defaults: init.config } : {}),
74
74
  ...(init.backupModels ? { backupModels: init.backupModels } : {}),
75
+ ...(init.fallbacks ? { fallbacks: init.fallbacks } : {}),
76
+ ...(init.fallbackOptions ? { fallbackOptions: init.fallbackOptions } : {}),
75
77
  ...(init.retryConfig ? { retryConfig: init.retryConfig } : {}),
76
78
  });
77
79
  this.config = init.config;
@@ -27,9 +27,11 @@ import {
27
27
  streamWatch,
28
28
  streamWithBackupModels,
29
29
  watchChunks,
30
+ withFallbackProviders,
30
31
  withStreamRetry,
31
32
  type ChatMessage,
32
33
  type Effort,
34
+ type FallbackOptions,
33
35
  type JsonObjectSchema,
34
36
  type JsonWithToolsProbe,
35
37
  type ProbeOptions,
@@ -119,6 +121,12 @@ export interface ProviderAdapterInit {
119
121
  defaults?: RequestConfig;
120
122
  /** Tried in order after the primary. */
121
123
  backupModels?: string[];
124
+ /**
125
+ * Fallback providers to try if this provider fails/exhausts.
126
+ * Accepts both @falai/agent AiProviders (like ZaiProvider) and @providerkit/core Providers.
127
+ */
128
+ fallbacks?: Array<AiProvider | Provider>;
129
+ fallbackOptions?: FallbackOptions<Provider>;
122
130
  retryConfig?: { timeout?: number; retries?: number };
123
131
  }
124
132
 
@@ -212,6 +220,9 @@ interface Accumulator {
212
220
  }
213
221
 
214
222
  function fold(acc: Accumulator, chunk: ProviderChunk): string {
223
+ if (chunk.source) {
224
+ acc.model = chunk.source.model;
225
+ }
215
226
  if (chunk.usage) {
216
227
  acc.promptTokens = chunk.usage.inputTokens;
217
228
  acc.completionTokens = chunk.usage.outputTokens;
@@ -256,12 +267,31 @@ export abstract class ProviderAdapter implements AiProvider {
256
267
  protected readonly retryConfig: RetryConfig;
257
268
  private readonly defaults: ProviderAdapterInit["defaults"];
258
269
 
270
+ public get coreProvider(): Provider {
271
+ return this.provider;
272
+ }
273
+
259
274
  protected constructor(init: ProviderAdapterInit) {
260
275
  this.defaults = init.defaults;
261
- this.provider = init.provider;
262
276
  this.primaryModel = init.model;
263
277
  this.backupModels = init.backupModels ?? [];
264
278
  this.retryConfig = resolveRetryConfig(init.retryConfig);
279
+
280
+ if (init.fallbacks && init.fallbacks.length > 0) {
281
+ const coreProviders: Provider[] = [
282
+ init.provider,
283
+ ...init.fallbacks.map((f) => {
284
+ if (f instanceof ProviderAdapter) return f.coreProvider;
285
+ if ("coreProvider" in f && (f as { coreProvider: Provider }).coreProvider) {
286
+ return (f as { coreProvider: Provider }).coreProvider;
287
+ }
288
+ return f as Provider;
289
+ }),
290
+ ];
291
+ this.provider = withFallbackProviders(coreProviders, init.fallbackOptions);
292
+ } else {
293
+ this.provider = init.provider;
294
+ }
265
295
  }
266
296
 
267
297
  /**
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Z.ai Coding Plan.
3
+ *
4
+ * The flat-rate plan hosts GLM on an Anthropic-compatible endpoint with
5
+ * Bearer auth and its own thinking dialect — all of that lives in
6
+ * `@providerkit/core` (adapter + preset); this file is the constructor, the
7
+ * family default for cheap high-volume work.
8
+ *
9
+ * Dialect notes (measured 2026-09-13, see the core preset for detail):
10
+ * model ids are BARE (`glm-5.3-flash`, not `z-ai/glm-5.3-flash`), and
11
+ * "no thinking" must be an explicit marker — silence means the model default,
12
+ * which is thinking ON.
13
+ */
14
+
15
+ import { createZaiCodingProvider, type FallbackOptions, type Provider } from "@providerkit/core";
16
+
17
+ import type { AiProvider, ProviderCapabilities } from "../types/ai.js";
18
+ import { ProviderAdapter, type RequestConfig } from "./ProviderAdapter.js";
19
+
20
+ export interface ZaiProviderOptions {
21
+ /** Z.ai Coding Plan key */
22
+ apiKey: string;
23
+ /** Model to use. Defaults to the fleet workhorse `glm-5.3-flash`. */
24
+ model?: string;
25
+ /** Backup models to try on this provider if the primary fails (default: []) */
26
+ backupModels?: string[];
27
+ /** Fallback providers to try if this provider fails/exhausts */
28
+ fallbacks?: Array<AiProvider | Provider>;
29
+ fallbackOptions?: FallbackOptions<Provider>;
30
+ /** Any endpoint speaking the Anthropic Messages dialect with the plan's
31
+ * auth. Defaults to the coding endpoint. */
32
+ baseUrl?: string;
33
+ /** Request defaults sent with every call */
34
+ config?: RequestConfig;
35
+ /** Idle-stream deadline and retry budget */
36
+ retryConfig?: { timeout?: number; retries?: number };
37
+ /** Replacement `fetch`, for tests that script the wire. */
38
+ fetchImpl?: typeof fetch;
39
+ }
40
+
41
+ export class ZaiProvider extends ProviderAdapter {
42
+ public readonly name = "zai";
43
+ public readonly capabilities: ProviderCapabilities = {
44
+ supportsTools: true,
45
+ supportsNativeJsonSchema: false,
46
+ supportsStreaming: true,
47
+ supportsStreamingToolCalls: true,
48
+ supportsPromptCaching: true,
49
+ };
50
+
51
+ constructor(options: ZaiProviderOptions) {
52
+ if (!options.apiKey) throw new Error("Z.ai Coding Plan API key is required");
53
+
54
+ const model = options.model ?? "glm-5.3-flash";
55
+ super({
56
+ provider: createZaiCodingProvider({
57
+ apiKey: options.apiKey,
58
+ model,
59
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
60
+ ...(options.config?.maxTokens ? { maxTokens: options.config.maxTokens } : {}),
61
+ ...(options.config?.effort ? { effort: options.config.effort } : {}),
62
+ ...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
63
+ }),
64
+ model,
65
+ ...(options.config ? { defaults: options.config } : {}),
66
+ ...(options.backupModels ? { backupModels: options.backupModels } : {}),
67
+ ...(options.fallbacks ? { fallbacks: options.fallbacks } : {}),
68
+ ...(options.fallbackOptions ? { fallbackOptions: options.fallbackOptions } : {}),
69
+ ...(options.retryConfig ? { retryConfig: options.retryConfig } : {}),
70
+ });
71
+ }
72
+ }
@@ -33,3 +33,6 @@ export type { OpenAICompatibleOptions } from "./GenericOpenAICompatibleProvider.
33
33
 
34
34
  export { ProviderAdapter, resolveRetryConfig } from "./ProviderAdapter.js";
35
35
  export type { ProviderAdapterInit, RequestConfig, RetryConfig } from "./ProviderAdapter.js";
36
+
37
+ export { ZaiProvider } from "./ZaiProvider.js";
38
+ export type { ZaiProviderOptions } from "./ZaiProvider.js";