@xiaozhiclaw/provider-core 0.1.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 (93) hide show
  1. package/dist/adapters/aliyun-oss-file-upload-adapter.d.ts +44 -0
  2. package/dist/adapters/aliyun-oss-file-upload-adapter.js +96 -0
  3. package/dist/adapters/gemini-file-upload-adapter.d.ts +26 -0
  4. package/dist/adapters/gemini-file-upload-adapter.js +92 -0
  5. package/dist/adapters/hub-oss-file-upload-adapter.d.ts +29 -0
  6. package/dist/adapters/hub-oss-file-upload-adapter.js +53 -0
  7. package/dist/adapters/index.d.ts +10 -0
  8. package/dist/adapters/index.js +10 -0
  9. package/dist/adapters/openai-file-upload-adapter.d.ts +38 -0
  10. package/dist/adapters/openai-file-upload-adapter.js +56 -0
  11. package/dist/adapters/volcengine-file-upload-adapter.d.ts +24 -0
  12. package/dist/adapters/volcengine-file-upload-adapter.js +45 -0
  13. package/dist/builtin-providers.d.ts +8 -0
  14. package/dist/builtin-providers.js +2237 -0
  15. package/dist/constants.d.ts +1 -0
  16. package/dist/constants.js +1 -0
  17. package/dist/credentials.d.ts +1 -0
  18. package/dist/credentials.js +8 -0
  19. package/dist/debug-transport.d.ts +12 -0
  20. package/dist/debug-transport.js +99 -0
  21. package/dist/errors.d.ts +11 -0
  22. package/dist/errors.js +12 -0
  23. package/dist/events.d.ts +48 -0
  24. package/dist/events.js +1 -0
  25. package/dist/file-upload-service.d.ts +68 -0
  26. package/dist/file-upload-service.js +110 -0
  27. package/dist/gemini-schema-utils.d.ts +17 -0
  28. package/dist/gemini-schema-utils.js +76 -0
  29. package/dist/index.d.ts +37 -0
  30. package/dist/index.js +33 -0
  31. package/dist/llm-client.d.ts +43 -0
  32. package/dist/llm-client.js +217 -0
  33. package/dist/media-client.d.ts +42 -0
  34. package/dist/media-client.js +174 -0
  35. package/dist/media-transport.d.ts +176 -0
  36. package/dist/media-transport.js +16 -0
  37. package/dist/media.d.ts +2 -0
  38. package/dist/media.js +1 -0
  39. package/dist/model-detection.d.ts +22 -0
  40. package/dist/model-detection.js +28 -0
  41. package/dist/paths.d.ts +2 -0
  42. package/dist/paths.js +11 -0
  43. package/dist/provider-def.d.ts +220 -0
  44. package/dist/provider-def.js +9 -0
  45. package/dist/provider-registry.d.ts +51 -0
  46. package/dist/provider-registry.js +130 -0
  47. package/dist/provider-tool-api.d.ts +44 -0
  48. package/dist/provider-tool-api.js +9 -0
  49. package/dist/provider-variant-resolver.d.ts +35 -0
  50. package/dist/provider-variant-resolver.js +174 -0
  51. package/dist/retry.d.ts +37 -0
  52. package/dist/retry.js +71 -0
  53. package/dist/transport.d.ts +281 -0
  54. package/dist/transport.js +27 -0
  55. package/dist/transports/anthropic-messages.d.ts +65 -0
  56. package/dist/transports/anthropic-messages.js +1004 -0
  57. package/dist/transports/gemini-cache-api.d.ts +86 -0
  58. package/dist/transports/gemini-cache-api.js +141 -0
  59. package/dist/transports/gemini-file-api.d.ts +90 -0
  60. package/dist/transports/gemini-file-api.js +164 -0
  61. package/dist/transports/gemini-generatecontent.d.ts +56 -0
  62. package/dist/transports/gemini-generatecontent.js +688 -0
  63. package/dist/transports/gemini-lyria-realtime.d.ts +117 -0
  64. package/dist/transports/gemini-lyria-realtime.js +295 -0
  65. package/dist/transports/gemini-media.d.ts +53 -0
  66. package/dist/transports/gemini-media.js +383 -0
  67. package/dist/transports/media-resolve.d.ts +50 -0
  68. package/dist/transports/media-resolve.js +91 -0
  69. package/dist/transports/minimax-media.d.ts +56 -0
  70. package/dist/transports/minimax-media.js +433 -0
  71. package/dist/transports/openai-chat.d.ts +81 -0
  72. package/dist/transports/openai-chat.js +782 -0
  73. package/dist/transports/openai-media.d.ts +24 -0
  74. package/dist/transports/openai-media.js +118 -0
  75. package/dist/transports/openai-responses.d.ts +63 -0
  76. package/dist/transports/openai-responses.js +778 -0
  77. package/dist/transports/qwen-media.d.ts +59 -0
  78. package/dist/transports/qwen-media.js +411 -0
  79. package/dist/transports/realtime-transport.d.ts +183 -0
  80. package/dist/transports/realtime-transport.js +332 -0
  81. package/dist/transports/volcengine-grounding.d.ts +58 -0
  82. package/dist/transports/volcengine-grounding.js +69 -0
  83. package/dist/transports/volcengine-media.d.ts +94 -0
  84. package/dist/transports/volcengine-media.js +801 -0
  85. package/dist/transports/volcengine-responses.d.ts +64 -0
  86. package/dist/transports/volcengine-responses.js +797 -0
  87. package/dist/transports/zhipu-media.d.ts +82 -0
  88. package/dist/transports/zhipu-media.js +522 -0
  89. package/dist/transports/zhipu-tool-api.d.ts +35 -0
  90. package/dist/transports/zhipu-tool-api.js +126 -0
  91. package/dist/wire-types.d.ts +51 -0
  92. package/dist/wire-types.js +1 -0
  93. package/package.json +33 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Model detection helpers shared between OpenAI transport implementations.
3
+ *
4
+ * These are used by both openai-chat.ts and openai-responses.ts to detect
5
+ * model families and apply family-specific constraints:
6
+ * - GPT-5.x: unified reasoning, temperature allowed, reasoning object format
7
+ * - GPT-5.4-nano: reasoning effort capped at medium
8
+ * - o-series (legacy): reasoning_effort flat string, temperature suppressed
9
+ */
10
+ /** GPT-5.x models (new generation with unified reasoning). */
11
+ export function isGPT5xModel(model) {
12
+ return model.toLowerCase().startsWith("gpt-5");
13
+ }
14
+ /**
15
+ * GPT-5.4-nano models 鈥?reasoning effort capped at medium.
16
+ * openai-ProviderMax 搂3: gpt-5.4-nano only supports none/low/medium effort.
17
+ */
18
+ export function isGPT5NanoModel(model) {
19
+ return model.toLowerCase().includes("5.4-nano");
20
+ }
21
+ /**
22
+ * OpenAI o-series reasoning models (legacy, kept for 3rd-party provider compat).
23
+ * These suppress temperature/top_p and use reasoning_effort as flat string.
24
+ * Matches: o1, o1-mini, o1-pro, o3, o3-mini, o3-pro, o4-mini, etc.
25
+ */
26
+ export function isOpenAIReasoningModel(model) {
27
+ return /^o[1-4](-|$)/.test(model.toLowerCase());
28
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getUserCacheDir(): string;
2
+ export declare function getUserDebugLogsDir(): string;
package/dist/paths.js ADDED
@@ -0,0 +1,11 @@
1
+ import { join } from "node:path";
2
+ import { homedir, tmpdir } from "node:os";
3
+ function baseDir() {
4
+ return process.env.QLOGICAGENT_HOME ?? join(homedir() || tmpdir(), ".qlogicagent");
5
+ }
6
+ export function getUserCacheDir() {
7
+ return process.env.QLOGICAGENT_CACHE_DIR ?? join(baseDir(), "cache");
8
+ }
9
+ export function getUserDebugLogsDir() {
10
+ return process.env.QLOGICAGENT_DEBUG_LOG_DIR ?? join(baseDir(), "debug-logs");
11
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * ProviderDef 鈥?defines how to connect to an LLM provider.
3
+ *
4
+ * Aligned with Hermes `ProviderDef` dataclass pattern:
5
+ * id + name + transport type + baseUrl + auth config + model list
6
+ *
7
+ * Single curated model catalog plus optional user provider overrides.
8
+ */
9
+ export type TransportType = "openai-chat" | "openai-responses" | "anthropic-messages" | "volcengine-responses" | "gemini-generatecontent";
10
+ export type AuthType = "bearer" | "x-api-key" | "none";
11
+ export type MediaCapability = "image" | "video" | "music" | "music_realtime" | "tts" | "3d" | "stt" | "embedding" | "video_understanding" | "image_understanding" | "voice_clone" | "rerank" | "document_parsing" | "realtime_audio" | "realtime_video";
12
+ export type ProviderVariantKind = "standard" | "openai-compatible" | "anthropic-compatible" | "coding-plan" | "media-plan" | "realtime";
13
+ export type ProviderBillingChannelKind = "paygo" | "plan" | "discount" | "official";
14
+ export type ProviderVariantCapability = "thinking" | "reasoning_split" | "tool_stream" | "builtin_tools" | "vision" | "media" | "coding" | "realtime";
15
+ export type VideoOperation = "text2video" | "img2video" | "video2video" | "edit" | "merge" | "upscale";
16
+ export type ImageOperation = "text2image" | "img2img" | "inpainting" | "outpainting";
17
+ export type MusicOperation = "text2music" | "cover" | "realtime";
18
+ export type TtsOperation = "text2speech" | "voice_clone";
19
+ export type ThreeDOperation = "text2_3d" | "img2_3d";
20
+ export interface VideoCapabilities {
21
+ type: "video";
22
+ operations: VideoOperation[];
23
+ maxDurationSeconds?: number;
24
+ resolutions?: string[];
25
+ aspectRatios?: string[];
26
+ fps?: number[];
27
+ }
28
+ export interface ImageCapabilities {
29
+ type: "image";
30
+ operations: ImageOperation[];
31
+ sizes?: string[];
32
+ transparentBackground?: boolean;
33
+ }
34
+ export interface MusicCapabilities {
35
+ type: "music";
36
+ operations: MusicOperation[];
37
+ maxDurationSeconds?: number;
38
+ formats?: string[];
39
+ }
40
+ export interface TtsCapabilities {
41
+ type: "tts";
42
+ operations?: TtsOperation[];
43
+ voices?: string[];
44
+ maxCharacters?: number;
45
+ formats?: string[];
46
+ }
47
+ export interface ThreeDCapabilities {
48
+ type: "3d";
49
+ operations: ThreeDOperation[];
50
+ outputFormats?: string[];
51
+ }
52
+ export interface SttCapabilities {
53
+ type: "stt";
54
+ languages?: string[];
55
+ maxDurationSeconds?: number;
56
+ formats?: string[];
57
+ }
58
+ export interface EmbeddingCapabilities {
59
+ type: "embedding";
60
+ dimensions?: number;
61
+ maxTokens?: number;
62
+ }
63
+ export interface VideoUnderstandingCapabilities {
64
+ type: "video_understanding";
65
+ maxDurationSeconds?: number;
66
+ formats?: string[];
67
+ }
68
+ export interface ImageUnderstandingCapabilities {
69
+ type: "image_understanding";
70
+ formats?: string[];
71
+ }
72
+ export interface VoiceCloneCapabilities {
73
+ type: "voice_clone";
74
+ maxSampleDurationSeconds?: number;
75
+ maxSampleSizeMB?: number;
76
+ formats?: string[];
77
+ }
78
+ export interface RerankCapabilities {
79
+ type: "rerank";
80
+ maxDocuments?: number;
81
+ maxQueryLength?: number;
82
+ maxDocumentLength?: number;
83
+ }
84
+ export interface DocumentParsingCapabilities {
85
+ type: "document_parsing";
86
+ supportedFormats?: string[];
87
+ maxPageCount?: number;
88
+ maxFileSizeMB?: number;
89
+ }
90
+ export interface RealtimeAudioCapabilities {
91
+ type: "realtime_audio";
92
+ voices?: string[];
93
+ modalities?: Array<"text" | "audio">;
94
+ vad?: boolean;
95
+ toolCalling?: boolean;
96
+ }
97
+ export interface RealtimeVideoCapabilities {
98
+ type: "realtime_video";
99
+ modalities?: Array<"text" | "audio" | "video">;
100
+ vad?: boolean;
101
+ toolCalling?: boolean;
102
+ maxDurationSeconds?: number;
103
+ }
104
+ export type MediaCapabilities = VideoCapabilities | ImageCapabilities | MusicCapabilities | TtsCapabilities | ThreeDCapabilities | SttCapabilities | EmbeddingCapabilities | VideoUnderstandingCapabilities | ImageUnderstandingCapabilities | VoiceCloneCapabilities | RerankCapabilities | DocumentParsingCapabilities | RealtimeAudioCapabilities | RealtimeVideoCapabilities;
105
+ /**
106
+ * Provider-specific quirks 鈥?drives conditional logic in transports.
107
+ * CC parity: provider detection via quirks flags instead of hardcoded if/else.
108
+ * altcode parity: provider auto-detect + per-provider parameter translation.
109
+ */
110
+ export interface ProviderQuirks {
111
+ /** Provider doesn't support thinking content blocks (Qwen) */
112
+ filterThinkingBlocks?: boolean;
113
+ /** Provider doesn't support image content blocks 鈥?strip imageUrls before sending (DeepSeek, MiniMax) */
114
+ filterImageBlocks?: boolean;
115
+ /** DeepSeek: budget_tokens ignored, use output_config.effort instead */
116
+ useEffortInsteadOfBudget?: boolean;
117
+ /** Provider natively supports PDF/document content blocks (Anthropic document, Gemini fileData).
118
+ * When false, PDFs are annotated as text labels and the agent must use tools to extract content. */
119
+ supportsDocumentVision?: boolean;
120
+ /** Provider supports reasoning_effort param (Kimi K2, OpenAI o-series) */
121
+ supportsReasoningEffort?: boolean;
122
+ /** Provider has built-in web search (Kimi: builtin_function.$web_search, GLM: web_search) */
123
+ builtinWebSearch?: boolean;
124
+ /** Provider has built-in code interpreter */
125
+ builtinCodeInterpreter?: boolean;
126
+ /** Provider supports native URL context fetching (Gemini urlContext tool) */
127
+ builtinUrlContext?: boolean;
128
+ /** Provider supports Google Maps Grounding (Gemini googleMaps tool) */
129
+ builtinMapsGrounding?: boolean;
130
+ /** Provider supports native file search (Gemini fileSearch tool) */
131
+ builtinFileSearch?: boolean;
132
+ /** Supports thinking.type="enabled"/"disabled" body param (Kimi K2, GLM).
133
+ * Disambiguation: GLM also sets supportsToolStream; Kimi does not. */
134
+ supportsThinkingParam?: boolean;
135
+ /** When true, send thinking.type="disabled" unless the caller explicitly requests reasoning. */
136
+ disableThinkingByDefault?: boolean;
137
+ /** GLM-only: supports tool_stream=true for incremental tool call streaming */
138
+ supportsToolStream?: boolean;
139
+ /** DeepSeek only maps to "high"|"max"; low/medium鈫抙igh */
140
+ maxReasoningEffort?: "high" | "max";
141
+ /** Supports prefix completion via /beta endpoint (DeepSeek Beta) */
142
+ supportsPrefixCompletion?: boolean;
143
+ /** MiniMax OpenAI route: inject reasoning_split=true to split thinking into reasoning_details.
144
+ * Streaming uses cumulative string updates (not incremental deltas). */
145
+ supportsReasoningSplit?: boolean;
146
+ }
147
+ export interface ProviderDef {
148
+ /** Unique provider id, e.g. "deepseek", "openai", "anthropic" */
149
+ id: string;
150
+ /** Display name, e.g. "DeepSeek" */
151
+ name: string;
152
+ /** Which transport to use for LLM calls */
153
+ transport: TransportType;
154
+ /** API base URL, e.g. "https://api.deepseek.com" */
155
+ baseUrl: string;
156
+ /**
157
+ * Logical provider group 鈥?links protocol variants of the same vendor.
158
+ * e.g. both "zhipu" (anthropic) and "zhipu-openai" share group "zhipu".
159
+ * Defaults to provider id if unset.
160
+ */
161
+ group?: string;
162
+ /** Technical protocol variant kind for resolver ranking. */
163
+ variantKind?: ProviderVariantKind;
164
+ /** Channel type hint only; commercial cost selection is owned by llmrouter. */
165
+ billingChannelKind?: ProviderBillingChannelKind;
166
+ /** Provider-level capability hints used by ProviderVariantResolver. */
167
+ capabilities?: ProviderVariantCapability[];
168
+ /** Env var names for API key (priority order) */
169
+ apiKeyEnvVars: string[];
170
+ /** Auth header style */
171
+ authType: AuthType;
172
+ /** Is an aggregator (OpenRouter, 纭呭熀) 鈥?model ids may have prefix */
173
+ isAggregator: boolean;
174
+ /** Recommended default model */
175
+ defaultModel?: string;
176
+ /** Known models for this provider */
177
+ models?: ModelInfo[];
178
+ /** Extra headers to send with every request (e.g. aggregator-specific) */
179
+ extraHeaders?: Record<string, string>;
180
+ /** Whether this provider supports stream_options (default true for openai-chat) */
181
+ supportsStreamOptions?: boolean;
182
+ /** Whether to omit temperature when it equals 0 (some providers reject 0) */
183
+ omitZeroTemperature?: boolean;
184
+ /** Provider-specific quirks for transport-level conditional logic */
185
+ quirks?: ProviderQuirks;
186
+ }
187
+ export interface ModelInfo {
188
+ /** Model id, e.g. "deepseek-v4-flash" */
189
+ id: string;
190
+ /** Stable public aliases exposed by llmrouter or older qlogicagent configs */
191
+ aliases?: string[];
192
+ /** Display name, e.g. "DeepSeek Chat V3" */
193
+ name: string;
194
+ /** Context window in tokens */
195
+ contextWindow: number;
196
+ /** Max output tokens */
197
+ maxOutput: number;
198
+ /** Supports function/tool calling */
199
+ toolCall: boolean;
200
+ /** Has reasoning/thinking mode */
201
+ reasoning: boolean;
202
+ /** Thinking is forced on 鈥?cannot be toggled off (e.g. QwQ, DeepSeek-R1) */
203
+ reasoningRequired?: boolean;
204
+ /** Model only supports streaming (non-stream requests will fail) */
205
+ streamRequired?: boolean;
206
+ /** Supports vision (image input) */
207
+ vision: boolean;
208
+ /** Cost per 1M input tokens (USD) */
209
+ costInput?: number;
210
+ /** Cost per 1M output tokens (USD) */
211
+ costOutput?: number;
212
+ /** Cost per 1M cache read tokens (USD) */
213
+ costCacheRead?: number;
214
+ /** Cost per 1M cache write tokens (USD) */
215
+ costCacheWrite?: number;
216
+ /** Media generation capability 鈥?undefined means chat/reasoning model */
217
+ mediaType?: MediaCapability;
218
+ /** Fine-grained media capabilities 鈥?operations, formats, limits */
219
+ mediaCapabilities?: MediaCapabilities;
220
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * ProviderDef 鈥?defines how to connect to an LLM provider.
3
+ *
4
+ * Aligned with Hermes `ProviderDef` dataclass pattern:
5
+ * id + name + transport type + baseUrl + auth config + model list
6
+ *
7
+ * Single curated model catalog plus optional user provider overrides.
8
+ */
9
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ProviderRegistry 鈥?single-source curated registry for LLM providers.
3
+ *
4
+ * Layer 1: builtin-providers.ts curated providers and models
5
+ * Layer 2: user config override (from agent.turn.config)
6
+ *
7
+ * Model IDs are intentionally not enriched from external catalogs. llmrouter and
8
+ * OpenClaw consume the same curated Provider Core catalog, and upstream request
9
+ * IDs are resolved through model aliases/native IDs.
10
+ *
11
+ * Aligned with Hermes provider_registry.py.
12
+ */
13
+ import type { ModelInfo, ProviderDef } from "./provider-def.js";
14
+ export declare class ProviderRegistry {
15
+ /** Curated providers and models */
16
+ private builtins;
17
+ /** User overrides (from agent.turn.config) */
18
+ private overrides;
19
+ constructor();
20
+ /**
21
+ * Apply user config override for a provider.
22
+ * Typically called when agent.turn.config has baseUrl/apiKey overrides.
23
+ */
24
+ applyOverride(providerId: string, override: Partial<ProviderDef>): void;
25
+ /**
26
+ * Get merged ProviderDef by id (Layer 3 > Layer 1).
27
+ * Returns undefined if provider not found.
28
+ * Supports common aliases (e.g., "claude" 鈫?"anthropic").
29
+ */
30
+ getProvider(id: string): ProviderDef | undefined;
31
+ getBuiltinProvider(id: string): ProviderDef | undefined;
32
+ /**
33
+ * List all known provider ids.
34
+ */
35
+ listProviders(): ProviderDef[];
36
+ /**
37
+ * List models for a specific provider.
38
+ */
39
+ listModels(providerId: string): ModelInfo[];
40
+ /**
41
+ * Look up a single model's info by provider + model id.
42
+ * Returns undefined if the model is not found.
43
+ */
44
+ getModelInfo(providerId: string, modelId: string): ModelInfo | undefined;
45
+ /**
46
+ * Resolve API key for a provider:
47
+ * 1. Explicit key (from agent.turn.config)
48
+ * 2. Environment variables (ProviderDef.apiKeyEnvVars)
49
+ */
50
+ resolveApiKey(providerId: string, explicitKey?: string): string | undefined;
51
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * ProviderRegistry 鈥?single-source curated registry for LLM providers.
3
+ *
4
+ * Layer 1: builtin-providers.ts curated providers and models
5
+ * Layer 2: user config override (from agent.turn.config)
6
+ *
7
+ * Model IDs are intentionally not enriched from external catalogs. llmrouter and
8
+ * OpenClaw consume the same curated Provider Core catalog, and upstream request
9
+ * IDs are resolved through model aliases/native IDs.
10
+ *
11
+ * Aligned with Hermes provider_registry.py.
12
+ */
13
+ import { BUILTIN_PROVIDERS } from "./builtin-providers.js";
14
+ // Provider alias map (Hermes-aligned): common user names 鈫?canonical ids
15
+ const PROVIDER_ALIASES = {
16
+ claude: "anthropic",
17
+ gemini: "google",
18
+ doubao: "volcengine",
19
+ };
20
+ export class ProviderRegistry {
21
+ /** Curated providers and models */
22
+ builtins = new Map();
23
+ /** User overrides (from agent.turn.config) */
24
+ overrides = new Map();
25
+ constructor() {
26
+ for (const p of BUILTIN_PROVIDERS) {
27
+ this.builtins.set(p.id, p);
28
+ }
29
+ }
30
+ /**
31
+ * Apply user config override for a provider.
32
+ * Typically called when agent.turn.config has baseUrl/apiKey overrides.
33
+ */
34
+ applyOverride(providerId, override) {
35
+ this.overrides.set(providerId, {
36
+ ...this.overrides.get(providerId),
37
+ ...override,
38
+ });
39
+ }
40
+ /**
41
+ * Get merged ProviderDef by id (Layer 3 > Layer 1).
42
+ * Returns undefined if provider not found.
43
+ * Supports common aliases (e.g., "claude" 鈫?"anthropic").
44
+ */
45
+ getProvider(id) {
46
+ const resolvedId = PROVIDER_ALIASES[id] ?? id;
47
+ const builtin = this.builtins.get(resolvedId);
48
+ const override = this.overrides.get(resolvedId);
49
+ if (!builtin && !override)
50
+ return undefined;
51
+ if (!builtin && override) {
52
+ // User defined a custom provider entirely
53
+ if (!override.id || !override.transport || !override.baseUrl) {
54
+ return undefined;
55
+ }
56
+ return {
57
+ id: override.id,
58
+ name: override.name ?? override.id,
59
+ transport: override.transport,
60
+ baseUrl: override.baseUrl,
61
+ apiKeyEnvVars: override.apiKeyEnvVars ?? [],
62
+ authType: override.authType ?? "bearer",
63
+ isAggregator: override.isAggregator ?? false,
64
+ defaultModel: override.defaultModel,
65
+ models: override.models,
66
+ };
67
+ }
68
+ if (builtin && !override)
69
+ return builtin;
70
+ // Merge: override fields take precedence
71
+ return {
72
+ ...builtin,
73
+ ...override,
74
+ // Merge models: override models replace if provided
75
+ models: override.models ?? builtin.models,
76
+ };
77
+ }
78
+ getBuiltinProvider(id) {
79
+ const resolvedId = PROVIDER_ALIASES[id] ?? id;
80
+ return this.builtins.get(resolvedId);
81
+ }
82
+ /**
83
+ * List all known provider ids.
84
+ */
85
+ listProviders() {
86
+ const all = new Map();
87
+ for (const [id, p] of this.builtins) {
88
+ all.set(id, p);
89
+ }
90
+ // Override/custom providers
91
+ for (const [id] of this.overrides) {
92
+ const merged = this.getProvider(id);
93
+ if (merged)
94
+ all.set(id, merged);
95
+ }
96
+ return [...all.values()];
97
+ }
98
+ /**
99
+ * List models for a specific provider.
100
+ */
101
+ listModels(providerId) {
102
+ const provider = this.getProvider(providerId);
103
+ return provider?.models ?? [];
104
+ }
105
+ /**
106
+ * Look up a single model's info by provider + model id.
107
+ * Returns undefined if the model is not found.
108
+ */
109
+ getModelInfo(providerId, modelId) {
110
+ return this.listModels(providerId).find(m => m.id === modelId);
111
+ }
112
+ /**
113
+ * Resolve API key for a provider:
114
+ * 1. Explicit key (from agent.turn.config)
115
+ * 2. Environment variables (ProviderDef.apiKeyEnvVars)
116
+ */
117
+ resolveApiKey(providerId, explicitKey) {
118
+ if (explicitKey)
119
+ return explicitKey;
120
+ const provider = this.getProvider(providerId);
121
+ if (!provider)
122
+ return undefined;
123
+ for (const envVar of provider.apiKeyEnvVars) {
124
+ const value = process.env[envVar];
125
+ if (value?.trim())
126
+ return value.trim();
127
+ }
128
+ return undefined;
129
+ }
130
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ProviderToolAPI 鈥?interface for provider-specific utility endpoints
3
+ * that are neither LLM chat nor media generation.
4
+ *
5
+ * Examples: web search, content reader, tokenizer, moderation, realtime voice.
6
+ * Each provider can expose its own set of tool APIs; the agent's tool cascade
7
+ * mechanism (Q1) routes to these when the provider has a native capability.
8
+ */
9
+ export interface WebSearchResult {
10
+ title: string;
11
+ url: string;
12
+ snippet: string;
13
+ /** Full page content if available */
14
+ content?: string;
15
+ }
16
+ export interface ReaderResult {
17
+ title: string;
18
+ content: string;
19
+ url: string;
20
+ }
21
+ export interface TokenizerResult {
22
+ tokenCount: number;
23
+ model: string;
24
+ }
25
+ export interface ModerationResult {
26
+ flagged: boolean;
27
+ categories: Record<string, boolean>;
28
+ scores?: Record<string, number>;
29
+ }
30
+ export interface ProviderToolAPI {
31
+ /** Which tool APIs this provider supports */
32
+ readonly capabilities: readonly ProviderToolCapability[];
33
+ /** Web search 鈥?returns search result list */
34
+ webSearch?(query: string, options?: {
35
+ maxResults?: number;
36
+ }): Promise<WebSearchResult[]>;
37
+ /** URL reader 鈥?extracts content from a web page */
38
+ reader?(url: string): Promise<ReaderResult>;
39
+ /** Tokenizer 鈥?count tokens for given text/model */
40
+ tokenize?(text: string, model: string): Promise<TokenizerResult>;
41
+ /** Content moderation 鈥?check text for policy violations */
42
+ moderate?(text: string): Promise<ModerationResult>;
43
+ }
44
+ export type ProviderToolCapability = "web_search" | "reader" | "tokenizer" | "moderations";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * ProviderToolAPI 鈥?interface for provider-specific utility endpoints
3
+ * that are neither LLM chat nor media generation.
4
+ *
5
+ * Examples: web search, content reader, tokenizer, moderation, realtime voice.
6
+ * Each provider can expose its own set of tool APIs; the agent's tool cascade
7
+ * mechanism (Q1) routes to these when the provider has a native capability.
8
+ */
9
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { ModelInfo, ProviderBillingChannelKind, ProviderDef, ProviderVariantCapability, ProviderVariantKind, TransportType } from "./provider-def.js";
2
+ import type { ProviderRegistry } from "./provider-registry.js";
3
+ export type RequestedProviderProtocol = TransportType | "openai" | "anthropic";
4
+ export interface ProviderVariantResolverInput {
5
+ publicModel: string;
6
+ requestedProtocol?: RequestedProviderProtocol;
7
+ capabilities?: ProviderVariantCapability[];
8
+ purpose?: string;
9
+ userPreference?: {
10
+ providerIds?: string[];
11
+ preferProviderId?: string;
12
+ preferVariantKind?: ProviderVariantKind;
13
+ };
14
+ }
15
+ export interface ProviderVariantResolution {
16
+ provider: string;
17
+ group: string;
18
+ publicModel: string;
19
+ nativeModelId: string;
20
+ displayName: string;
21
+ transport: TransportType;
22
+ variantKind: ProviderVariantKind;
23
+ billingChannelKind: ProviderBillingChannelKind;
24
+ capabilities: ProviderVariantCapability[];
25
+ score: number;
26
+ reasons: string[];
27
+ providerDef: ProviderDef;
28
+ modelInfo: ModelInfo;
29
+ }
30
+ export declare class ProviderVariantResolver {
31
+ private readonly registry;
32
+ constructor(registry: Pick<ProviderRegistry, "listProviders" | "listModels">);
33
+ resolve(input: ProviderVariantResolverInput): ProviderVariantResolution[];
34
+ resolveBest(input: ProviderVariantResolverInput): ProviderVariantResolution | undefined;
35
+ }