@averyyy/pi-ai 0.80.4 → 0.80.6-piclient.4
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 +4 -2
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +7 -4
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +3 -1
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +9 -5
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +10 -2
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +2 -1
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +7 -5
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +3 -1
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/simple-options.d.ts +1 -1
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +1 -1
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +4 -1
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +176 -176
- package/dist/image-models.generated.js.map +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +1242 -369
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.js +15 -6
- package/dist/models.js.map +1 -1
- package/dist/providers/amazon-bedrock.models.d.ts +100 -5
- package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.models.js +70 -23
- package/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/dist/providers/anthropic.models.d.ts +11 -171
- package/dist/providers/anthropic.models.d.ts.map +1 -1
- package/dist/providers/anthropic.models.js +9 -177
- package/dist/providers/anthropic.models.js.map +1 -1
- package/dist/providers/azure-openai-responses.models.d.ts +66 -0
- package/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.models.js +54 -0
- package/dist/providers/azure-openai-responses.models.js.map +1 -1
- package/dist/providers/cerebras.models.d.ts +21 -0
- package/dist/providers/cerebras.models.d.ts.map +1 -1
- package/dist/providers/cerebras.models.js +18 -0
- package/dist/providers/cerebras.models.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +32 -1
- package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.js +24 -4
- package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
- package/dist/providers/deepseek.models.d.ts +2 -2
- package/dist/providers/deepseek.models.d.ts.map +1 -1
- package/dist/providers/deepseek.models.js +2 -2
- package/dist/providers/deepseek.models.js.map +1 -1
- package/dist/providers/fireworks.models.d.ts +11 -6
- package/dist/providers/fireworks.models.d.ts.map +1 -1
- package/dist/providers/fireworks.models.js +6 -5
- package/dist/providers/fireworks.models.js.map +1 -1
- package/dist/providers/github-copilot.models.d.ts +89 -1
- package/dist/providers/github-copilot.models.d.ts.map +1 -1
- package/dist/providers/github-copilot.models.js +67 -9
- package/dist/providers/github-copilot.models.js.map +1 -1
- package/dist/providers/huggingface.models.d.ts +20 -0
- package/dist/providers/huggingface.models.d.ts.map +1 -1
- package/dist/providers/huggingface.models.js +18 -0
- package/dist/providers/huggingface.models.js.map +1 -1
- package/dist/providers/mistral.models.d.ts +1 -1
- package/dist/providers/mistral.models.d.ts.map +1 -1
- package/dist/providers/mistral.models.js +4 -4
- package/dist/providers/mistral.models.js.map +1 -1
- package/dist/providers/nvidia.models.d.ts +1 -1
- package/dist/providers/nvidia.models.d.ts.map +1 -1
- package/dist/providers/nvidia.models.js +4 -4
- package/dist/providers/nvidia.models.js.map +1 -1
- package/dist/providers/openai-codex.models.d.ts +101 -0
- package/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/dist/providers/openai-codex.models.js +59 -0
- package/dist/providers/openai-codex.models.js.map +1 -1
- package/dist/providers/openai.models.d.ts +115 -0
- package/dist/providers/openai.models.d.ts.map +1 -1
- package/dist/providers/openai.models.js +61 -0
- package/dist/providers/openai.models.js.map +1 -1
- package/dist/providers/opencode-go.models.d.ts +3 -3
- package/dist/providers/opencode-go.models.d.ts.map +1 -1
- package/dist/providers/opencode-go.models.js +5 -5
- package/dist/providers/opencode-go.models.js.map +1 -1
- package/dist/providers/opencode.models.d.ts +146 -4
- package/dist/providers/opencode.models.d.ts.map +1 -1
- package/dist/providers/opencode.models.js +119 -8
- package/dist/providers/opencode.models.js.map +1 -1
- package/dist/providers/openrouter.models.d.ts +349 -24
- package/dist/providers/openrouter.models.d.ts.map +1 -1
- package/dist/providers/openrouter.models.js +351 -91
- package/dist/providers/openrouter.models.js.map +1 -1
- package/dist/providers/together.models.d.ts.map +1 -1
- package/dist/providers/together.models.js +2 -2
- package/dist/providers/together.models.js.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.d.ts +129 -1
- package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.js +117 -8
- package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/dist/providers/xai.models.d.ts +22 -0
- package/dist/providers/xai.models.d.ts.map +1 -1
- package/dist/providers/xai.models.js +18 -0
- package/dist/providers/xai.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/dist/providers/zai-coding-cn.models.d.ts +1 -1
- package/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
- package/dist/providers/zai-coding-cn.models.js +1 -1
- package/dist/providers/zai-coding-cn.models.js.map +1 -1
- package/dist/providers/zai.models.d.ts +1 -1
- package/dist/providers/zai.models.d.ts.map +1 -1
- package/dist/providers/zai.models.js +1 -1
- package/dist/providers/zai.models.js.map +1 -1
- package/dist/types.d.ts +16 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/estimate.d.ts +3 -3
- package/dist/utils/estimate.d.ts.map +1 -1
- package/dist/utils/estimate.js +17 -9
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/oauth/device-code.d.ts +2 -0
- package/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/dist/utils/oauth/device-code.js +15 -2
- package/dist/utils/oauth/device-code.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +3 -2
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +3 -0
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +4 -0
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +9 -21
- package/dist/utils/validation.js.map +1 -1
- package/package.json +5 -2
- package/dist/api-registry.d.ts +0 -20
- package/dist/api-registry.d.ts.map +0 -1
- package/dist/api-registry.js +0 -44
- package/dist/api-registry.js.map +0 -1
- package/dist/providers/cloudflare.d.ts +0 -13
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js +0 -26
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/github-copilot-headers.d.ts +0 -8
- package/dist/providers/github-copilot-headers.d.ts.map +0 -1
- package/dist/providers/github-copilot-headers.js +0 -29
- package/dist/providers/github-copilot-headers.js.map +0 -1
- package/dist/providers/google-shared.d.ts +0 -70
- package/dist/providers/google-shared.d.ts.map +0 -1
- package/dist/providers/google-shared.js +0 -329
- package/dist/providers/google-shared.js.map +0 -1
- package/dist/providers/images/openrouter.d.ts +0 -3
- package/dist/providers/images/openrouter.d.ts.map +0 -1
- package/dist/providers/images/openrouter.js +0 -128
- package/dist/providers/images/openrouter.js.map +0 -1
- package/dist/providers/openai-codex-responses.d.ts +0 -30
- package/dist/providers/openai-codex-responses.d.ts.map +0 -1
- package/dist/providers/openai-codex-responses.js +0 -1173
- package/dist/providers/openai-codex-responses.js.map +0 -1
- package/dist/providers/openai-completions.d.ts +0 -19
- package/dist/providers/openai-completions.d.ts.map +0 -1
- package/dist/providers/openai-completions.js +0 -977
- package/dist/providers/openai-completions.js.map +0 -1
- package/dist/providers/openai-prompt-cache.d.ts +0 -3
- package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
- package/dist/providers/openai-prompt-cache.js +0 -10
- package/dist/providers/openai-prompt-cache.js.map +0 -1
- package/dist/providers/openai-responses-shared.d.ts +0 -18
- package/dist/providers/openai-responses-shared.d.ts.map +0 -1
- package/dist/providers/openai-responses-shared.js +0 -496
- package/dist/providers/openai-responses-shared.js.map +0 -1
- package/dist/providers/openai-responses.d.ts +0 -13
- package/dist/providers/openai-responses.d.ts.map +0 -1
- package/dist/providers/openai-responses.js +0 -234
- package/dist/providers/openai-responses.js.map +0 -1
- package/dist/providers/register-builtins.d.ts +0 -35
- package/dist/providers/register-builtins.d.ts.map +0 -1
- package/dist/providers/register-builtins.js +0 -254
- package/dist/providers/register-builtins.js.map +0 -1
- package/dist/providers/simple-options.d.ts +0 -8
- package/dist/providers/simple-options.d.ts.map +0 -1
- package/dist/providers/simple-options.js +0 -42
- package/dist/providers/simple-options.js.map +0 -1
- package/dist/providers/transform-messages.d.ts +0 -8
- package/dist/providers/transform-messages.d.ts.map +0 -1
- package/dist/providers/transform-messages.js +0 -184
- package/dist/providers/transform-messages.js.map +0 -1
- package/dist/stream.d.ts +0 -8
- package/dist/stream.d.ts.map +0 -1
- package/dist/stream.js +0 -39
- package/dist/stream.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@averyyy/pi-ai",
|
|
3
|
-
"version": "0.80.4",
|
|
3
|
+
"version": "0.80.6-piclient.4",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"license": "MIT",
|
|
79
79
|
"repository": {
|
|
80
80
|
"type": "git",
|
|
81
|
-
"url": "
|
|
81
|
+
"url": "https://github.com/Averyyy/pi-client",
|
|
82
82
|
"directory": "packages/ai"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
@@ -88,5 +88,8 @@
|
|
|
88
88
|
"@types/node": "24.12.4",
|
|
89
89
|
"canvas": "3.2.3",
|
|
90
90
|
"vitest": "4.1.9"
|
|
91
|
+
},
|
|
92
|
+
"publishConfig": {
|
|
93
|
+
"access": "public"
|
|
91
94
|
}
|
|
92
95
|
}
|
package/dist/api-registry.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Api, AssistantMessageEventStream, Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "./types.ts";
|
|
2
|
-
export type ApiStreamFunction = (model: Model<Api>, context: Context, options?: StreamOptions) => AssistantMessageEventStream;
|
|
3
|
-
export type ApiStreamSimpleFunction = (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
|
|
4
|
-
export interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {
|
|
5
|
-
api: TApi;
|
|
6
|
-
stream: StreamFunction<TApi, TOptions>;
|
|
7
|
-
streamSimple: StreamFunction<TApi, SimpleStreamOptions>;
|
|
8
|
-
}
|
|
9
|
-
interface ApiProviderInternal {
|
|
10
|
-
api: Api;
|
|
11
|
-
stream: ApiStreamFunction;
|
|
12
|
-
streamSimple: ApiStreamSimpleFunction;
|
|
13
|
-
}
|
|
14
|
-
export declare function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(provider: ApiProvider<TApi, TOptions>, sourceId?: string): void;
|
|
15
|
-
export declare function getApiProvider(api: Api): ApiProviderInternal | undefined;
|
|
16
|
-
export declare function getApiProviders(): ApiProviderInternal[];
|
|
17
|
-
export declare function unregisterApiProviders(sourceId: string): void;
|
|
18
|
-
export declare function clearApiProviders(): void;
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=api-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-registry.d.ts","sourceRoot":"","sources":["../src/api-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,GAAG,EACH,2BAA2B,EAC3B,OAAO,EACP,KAAK,EACL,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,aAAa,KACnB,2BAA2B,CAAC;AAEjC,MAAM,MAAM,uBAAuB,GAAG,CACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,KACzB,2BAA2B,CAAC;AAEjC,MAAM,WAAW,WAAW,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,EAAE,QAAQ,SAAS,aAAa,GAAG,aAAa;IAClG,GAAG,EAAE,IAAI,CAAC;IACV,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,YAAY,EAAE,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;CACxD;AAED,UAAU,mBAAmB;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,uBAAuB,CAAC;CACtC;AAiCD,wBAAgB,mBAAmB,CAAC,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,aAAa,EACnF,QAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,GACf,IAAI,CASN;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,mBAAmB,GAAG,SAAS,CAExE;AAED,wBAAgB,eAAe,IAAI,mBAAmB,EAAE,CAEvD;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAM7D;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC","sourcesContent":["import type {\n\tApi,\n\tAssistantMessageEventStream,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n} from \"./types.ts\";\n\nexport type ApiStreamFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: StreamOptions,\n) => AssistantMessageEventStream;\n\nexport type ApiStreamSimpleFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n) => AssistantMessageEventStream;\n\nexport interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {\n\tapi: TApi;\n\tstream: StreamFunction<TApi, TOptions>;\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>;\n}\n\ninterface ApiProviderInternal {\n\tapi: Api;\n\tstream: ApiStreamFunction;\n\tstreamSimple: ApiStreamSimpleFunction;\n}\n\ntype RegisteredApiProvider = {\n\tprovider: ApiProviderInternal;\n\tsourceId?: string;\n};\n\nconst apiProviderRegistry = new Map<string, RegisteredApiProvider>();\n\nfunction wrapStream<TApi extends Api, TOptions extends StreamOptions>(\n\tapi: TApi,\n\tstream: StreamFunction<TApi, TOptions>,\n): ApiStreamFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn stream(model as Model<TApi>, context, options as TOptions);\n\t};\n}\n\nfunction wrapStreamSimple<TApi extends Api>(\n\tapi: TApi,\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>,\n): ApiStreamSimpleFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn streamSimple(model as Model<TApi>, context, options);\n\t};\n}\n\nexport function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(\n\tprovider: ApiProvider<TApi, TOptions>,\n\tsourceId?: string,\n): void {\n\tapiProviderRegistry.set(provider.api, {\n\t\tprovider: {\n\t\t\tapi: provider.api,\n\t\t\tstream: wrapStream(provider.api, provider.stream),\n\t\t\tstreamSimple: wrapStreamSimple(provider.api, provider.streamSimple),\n\t\t},\n\t\tsourceId,\n\t});\n}\n\nexport function getApiProvider(api: Api): ApiProviderInternal | undefined {\n\treturn apiProviderRegistry.get(api)?.provider;\n}\n\nexport function getApiProviders(): ApiProviderInternal[] {\n\treturn Array.from(apiProviderRegistry.values(), (entry) => entry.provider);\n}\n\nexport function unregisterApiProviders(sourceId: string): void {\n\tfor (const [api, entry] of apiProviderRegistry.entries()) {\n\t\tif (entry.sourceId === sourceId) {\n\t\t\tapiProviderRegistry.delete(api);\n\t\t}\n\t}\n}\n\nexport function clearApiProviders(): void {\n\tapiProviderRegistry.clear();\n}\n"]}
|
package/dist/api-registry.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const apiProviderRegistry = new Map();
|
|
2
|
-
function wrapStream(api, stream) {
|
|
3
|
-
return (model, context, options) => {
|
|
4
|
-
if (model.api !== api) {
|
|
5
|
-
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
6
|
-
}
|
|
7
|
-
return stream(model, context, options);
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
function wrapStreamSimple(api, streamSimple) {
|
|
11
|
-
return (model, context, options) => {
|
|
12
|
-
if (model.api !== api) {
|
|
13
|
-
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
14
|
-
}
|
|
15
|
-
return streamSimple(model, context, options);
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export function registerApiProvider(provider, sourceId) {
|
|
19
|
-
apiProviderRegistry.set(provider.api, {
|
|
20
|
-
provider: {
|
|
21
|
-
api: provider.api,
|
|
22
|
-
stream: wrapStream(provider.api, provider.stream),
|
|
23
|
-
streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
|
|
24
|
-
},
|
|
25
|
-
sourceId,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function getApiProvider(api) {
|
|
29
|
-
return apiProviderRegistry.get(api)?.provider;
|
|
30
|
-
}
|
|
31
|
-
export function getApiProviders() {
|
|
32
|
-
return Array.from(apiProviderRegistry.values(), (entry) => entry.provider);
|
|
33
|
-
}
|
|
34
|
-
export function unregisterApiProviders(sourceId) {
|
|
35
|
-
for (const [api, entry] of apiProviderRegistry.entries()) {
|
|
36
|
-
if (entry.sourceId === sourceId) {
|
|
37
|
-
apiProviderRegistry.delete(api);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export function clearApiProviders() {
|
|
42
|
-
apiProviderRegistry.clear();
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=api-registry.js.map
|
package/dist/api-registry.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-registry.js","sourceRoot":"","sources":["../src/api-registry.ts"],"names":[],"mappings":"AAuCA,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAiC,CAAC;AAErE,SAAS,UAAU,CAClB,GAAS,EACT,MAAsC,EAClB;IACpB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,GAAG,aAAa,GAAG,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAmB,CAAC,CAAC;IAAA,CAClE,CAAC;AAAA,CACF;AAED,SAAS,gBAAgB,CACxB,GAAS,EACT,YAAuD,EAC7B;IAC1B,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,GAAG,aAAa,GAAG,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,YAAY,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAAA,CAC5D,CAAC;AAAA,CACF;AAED,MAAM,UAAU,mBAAmB,CAClC,QAAqC,EACrC,QAAiB,EACV;IACP,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE;QACrC,QAAQ,EAAE;YACT,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC;YACjD,YAAY,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,YAAY,CAAC;SACnE;QACD,QAAQ;KACR,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,cAAc,CAAC,GAAQ,EAAmC;IACzE,OAAO,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;AAAA,CAC9C;AAED,MAAM,UAAU,eAAe,GAA0B;IACxD,OAAO,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAAA,CAC3E;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAgB,EAAQ;IAC9D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1D,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;AAAA,CACD;AAED,MAAM,UAAU,iBAAiB,GAAS;IACzC,mBAAmB,CAAC,KAAK,EAAE,CAAC;AAAA,CAC5B","sourcesContent":["import type {\n\tApi,\n\tAssistantMessageEventStream,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n} from \"./types.ts\";\n\nexport type ApiStreamFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: StreamOptions,\n) => AssistantMessageEventStream;\n\nexport type ApiStreamSimpleFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n) => AssistantMessageEventStream;\n\nexport interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {\n\tapi: TApi;\n\tstream: StreamFunction<TApi, TOptions>;\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>;\n}\n\ninterface ApiProviderInternal {\n\tapi: Api;\n\tstream: ApiStreamFunction;\n\tstreamSimple: ApiStreamSimpleFunction;\n}\n\ntype RegisteredApiProvider = {\n\tprovider: ApiProviderInternal;\n\tsourceId?: string;\n};\n\nconst apiProviderRegistry = new Map<string, RegisteredApiProvider>();\n\nfunction wrapStream<TApi extends Api, TOptions extends StreamOptions>(\n\tapi: TApi,\n\tstream: StreamFunction<TApi, TOptions>,\n): ApiStreamFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn stream(model as Model<TApi>, context, options as TOptions);\n\t};\n}\n\nfunction wrapStreamSimple<TApi extends Api>(\n\tapi: TApi,\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>,\n): ApiStreamSimpleFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn streamSimple(model as Model<TApi>, context, options);\n\t};\n}\n\nexport function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(\n\tprovider: ApiProvider<TApi, TOptions>,\n\tsourceId?: string,\n): void {\n\tapiProviderRegistry.set(provider.api, {\n\t\tprovider: {\n\t\t\tapi: provider.api,\n\t\t\tstream: wrapStream(provider.api, provider.stream),\n\t\t\tstreamSimple: wrapStreamSimple(provider.api, provider.streamSimple),\n\t\t},\n\t\tsourceId,\n\t});\n}\n\nexport function getApiProvider(api: Api): ApiProviderInternal | undefined {\n\treturn apiProviderRegistry.get(api)?.provider;\n}\n\nexport function getApiProviders(): ApiProviderInternal[] {\n\treturn Array.from(apiProviderRegistry.values(), (entry) => entry.provider);\n}\n\nexport function unregisterApiProviders(sourceId: string): void {\n\tfor (const [api, entry] of apiProviderRegistry.entries()) {\n\t\tif (entry.sourceId === sourceId) {\n\t\t\tapiProviderRegistry.delete(api);\n\t\t}\n\t}\n}\n\nexport function clearApiProviders(): void {\n\tapiProviderRegistry.clear();\n}\n"]}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Api, Model } from "../types.ts";
|
|
2
|
-
/** Workers AI direct endpoint. */
|
|
3
|
-
export declare const CLOUDFLARE_WORKERS_AI_BASE_URL = "https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/ai/v1";
|
|
4
|
-
/** AI Gateway Unified API. https://developers.cloudflare.com/ai-gateway/usage/unified-api/ */
|
|
5
|
-
export declare const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/compat";
|
|
6
|
-
/** AI Gateway → OpenAI passthrough. Used until /compat supports /v1/responses. */
|
|
7
|
-
export declare const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/openai";
|
|
8
|
-
/** AI Gateway → Anthropic passthrough. */
|
|
9
|
-
export declare const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic";
|
|
10
|
-
export declare function isCloudflareProvider(provider: string): boolean;
|
|
11
|
-
/** Substitute `{VAR}` placeholders in a Cloudflare baseUrl from process.env. */
|
|
12
|
-
export declare function resolveCloudflareBaseUrl(model: Model<Api>): string;
|
|
13
|
-
//# sourceMappingURL=cloudflare.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/providers/cloudflare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE9C,kCAAkC;AAClC,eAAO,MAAM,8BAA8B,gFACmC,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,qCAAqC,gGAC4C,CAAC;AAE/F,oFAAkF;AAClF,eAAO,MAAM,qCAAqC,gGAC4C,CAAC;AAE/F,4CAA0C;AAC1C,eAAO,MAAM,wCAAwC,mGAC4C,CAAC;AAElG,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,gFAAgF;AAChF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAWlE","sourcesContent":["import type { Api, Model } from \"../types.ts\";\n\n/** Workers AI direct endpoint. */\nexport const CLOUDFLARE_WORKERS_AI_BASE_URL =\n\t\"https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/ai/v1\";\n\n/** AI Gateway Unified API. https://developers.cloudflare.com/ai-gateway/usage/unified-api/ */\nexport const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/compat\";\n\n/** AI Gateway → OpenAI passthrough. Used until /compat supports /v1/responses. */\nexport const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/openai\";\n\n/** AI Gateway → Anthropic passthrough. */\nexport const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic\";\n\nexport function isCloudflareProvider(provider: string): boolean {\n\treturn provider === \"cloudflare-workers-ai\" || provider === \"cloudflare-ai-gateway\";\n}\n\n/** Substitute `{VAR}` placeholders in a Cloudflare baseUrl from process.env. */\nexport function resolveCloudflareBaseUrl(model: Model<Api>): string {\n\tconst url = model.baseUrl;\n\tif (!url.includes(\"{\")) return url;\n\tconst baseUrl = url.replace(/\\{([A-Z_][A-Z0-9_]*)\\}/g, (_match, name: string) => {\n\t\tconst value = process.env[name];\n\t\tif (!value) {\n\t\t\tthrow new Error(`${name} is required for provider ${model.provider} but is not set.`);\n\t\t}\n\t\treturn value;\n\t});\n\treturn baseUrl;\n}\n"]}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Workers AI direct endpoint. */
|
|
2
|
-
export const CLOUDFLARE_WORKERS_AI_BASE_URL = "https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/ai/v1";
|
|
3
|
-
/** AI Gateway Unified API. https://developers.cloudflare.com/ai-gateway/usage/unified-api/ */
|
|
4
|
-
export const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/compat";
|
|
5
|
-
/** AI Gateway → OpenAI passthrough. Used until /compat supports /v1/responses. */
|
|
6
|
-
export const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/openai";
|
|
7
|
-
/** AI Gateway → Anthropic passthrough. */
|
|
8
|
-
export const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic";
|
|
9
|
-
export function isCloudflareProvider(provider) {
|
|
10
|
-
return provider === "cloudflare-workers-ai" || provider === "cloudflare-ai-gateway";
|
|
11
|
-
}
|
|
12
|
-
/** Substitute `{VAR}` placeholders in a Cloudflare baseUrl from process.env. */
|
|
13
|
-
export function resolveCloudflareBaseUrl(model) {
|
|
14
|
-
const url = model.baseUrl;
|
|
15
|
-
if (!url.includes("{"))
|
|
16
|
-
return url;
|
|
17
|
-
const baseUrl = url.replace(/\{([A-Z_][A-Z0-9_]*)\}/g, (_match, name) => {
|
|
18
|
-
const value = process.env[name];
|
|
19
|
-
if (!value) {
|
|
20
|
-
throw new Error(`${name} is required for provider ${model.provider} but is not set.`);
|
|
21
|
-
}
|
|
22
|
-
return value;
|
|
23
|
-
});
|
|
24
|
-
return baseUrl;
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=cloudflare.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/providers/cloudflare.ts"],"names":[],"mappings":"AAEA,kCAAkC;AAClC,MAAM,CAAC,MAAM,8BAA8B,GAC1C,6EAA6E,CAAC;AAE/E,8FAA8F;AAC9F,MAAM,CAAC,MAAM,qCAAqC,GACjD,6FAA6F,CAAC;AAE/F,oFAAkF;AAClF,MAAM,CAAC,MAAM,qCAAqC,GACjD,6FAA6F,CAAC;AAE/F,4CAA0C;AAC1C,MAAM,CAAC,MAAM,wCAAwC,GACpD,gGAAgG,CAAC;AAElG,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAW;IAC/D,OAAO,QAAQ,KAAK,uBAAuB,IAAI,QAAQ,KAAK,uBAAuB,CAAC;AAAA,CACpF;AAED,gFAAgF;AAChF,MAAM,UAAU,wBAAwB,CAAC,KAAiB,EAAU;IACnE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAAC;QAChF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6BAA6B,KAAK,CAAC,QAAQ,kBAAkB,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AAAA,CACf","sourcesContent":["import type { Api, Model } from \"../types.ts\";\n\n/** Workers AI direct endpoint. */\nexport const CLOUDFLARE_WORKERS_AI_BASE_URL =\n\t\"https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/ai/v1\";\n\n/** AI Gateway Unified API. https://developers.cloudflare.com/ai-gateway/usage/unified-api/ */\nexport const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/compat\";\n\n/** AI Gateway → OpenAI passthrough. Used until /compat supports /v1/responses. */\nexport const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/openai\";\n\n/** AI Gateway → Anthropic passthrough. */\nexport const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL =\n\t\"https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic\";\n\nexport function isCloudflareProvider(provider: string): boolean {\n\treturn provider === \"cloudflare-workers-ai\" || provider === \"cloudflare-ai-gateway\";\n}\n\n/** Substitute `{VAR}` placeholders in a Cloudflare baseUrl from process.env. */\nexport function resolveCloudflareBaseUrl(model: Model<Api>): string {\n\tconst url = model.baseUrl;\n\tif (!url.includes(\"{\")) return url;\n\tconst baseUrl = url.replace(/\\{([A-Z_][A-Z0-9_]*)\\}/g, (_match, name: string) => {\n\t\tconst value = process.env[name];\n\t\tif (!value) {\n\t\t\tthrow new Error(`${name} is required for provider ${model.provider} but is not set.`);\n\t\t}\n\t\treturn value;\n\t});\n\treturn baseUrl;\n}\n"]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Message } from "../types.ts";
|
|
2
|
-
export declare function inferCopilotInitiator(messages: Message[]): "user" | "agent";
|
|
3
|
-
export declare function hasCopilotVisionInput(messages: Message[]): boolean;
|
|
4
|
-
export declare function buildCopilotDynamicHeaders(params: {
|
|
5
|
-
messages: Message[];
|
|
6
|
-
hasImages: boolean;
|
|
7
|
-
}): Record<string, string>;
|
|
8
|
-
//# sourceMappingURL=github-copilot-headers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"github-copilot-headers.d.ts","sourceRoot":"","sources":["../../src/providers/github-copilot-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,OAAO,CAG3E;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAUlE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWzB","sourcesContent":["import type { Message } from \"../types.ts\";\n\n// Copilot expects X-Initiator to indicate whether the request is user-initiated\n// or agent-initiated (e.g. follow-up after assistant/tool messages).\nexport function inferCopilotInitiator(messages: Message[]): \"user\" | \"agent\" {\n\tconst last = messages[messages.length - 1];\n\treturn last && last.role !== \"user\" ? \"agent\" : \"user\";\n}\n\n// Copilot requires Copilot-Vision-Request header when sending images\nexport function hasCopilotVisionInput(messages: Message[]): boolean {\n\treturn messages.some((msg) => {\n\t\tif (msg.role === \"user\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\tif (msg.role === \"toolResult\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\treturn false;\n\t});\n}\n\nexport function buildCopilotDynamicHeaders(params: {\n\tmessages: Message[];\n\thasImages: boolean;\n}): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"X-Initiator\": inferCopilotInitiator(params.messages),\n\t\t\"Openai-Intent\": \"conversation-edits\",\n\t};\n\n\tif (params.hasImages) {\n\t\theaders[\"Copilot-Vision-Request\"] = \"true\";\n\t}\n\n\treturn headers;\n}\n"]}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// Copilot expects X-Initiator to indicate whether the request is user-initiated
|
|
2
|
-
// or agent-initiated (e.g. follow-up after assistant/tool messages).
|
|
3
|
-
export function inferCopilotInitiator(messages) {
|
|
4
|
-
const last = messages[messages.length - 1];
|
|
5
|
-
return last && last.role !== "user" ? "agent" : "user";
|
|
6
|
-
}
|
|
7
|
-
// Copilot requires Copilot-Vision-Request header when sending images
|
|
8
|
-
export function hasCopilotVisionInput(messages) {
|
|
9
|
-
return messages.some((msg) => {
|
|
10
|
-
if (msg.role === "user" && Array.isArray(msg.content)) {
|
|
11
|
-
return msg.content.some((c) => c.type === "image");
|
|
12
|
-
}
|
|
13
|
-
if (msg.role === "toolResult" && Array.isArray(msg.content)) {
|
|
14
|
-
return msg.content.some((c) => c.type === "image");
|
|
15
|
-
}
|
|
16
|
-
return false;
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
export function buildCopilotDynamicHeaders(params) {
|
|
20
|
-
const headers = {
|
|
21
|
-
"X-Initiator": inferCopilotInitiator(params.messages),
|
|
22
|
-
"Openai-Intent": "conversation-edits",
|
|
23
|
-
};
|
|
24
|
-
if (params.hasImages) {
|
|
25
|
-
headers["Copilot-Vision-Request"] = "true";
|
|
26
|
-
}
|
|
27
|
-
return headers;
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=github-copilot-headers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"github-copilot-headers.js","sourceRoot":"","sources":["../../src/providers/github-copilot-headers.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB,EAAoB;IAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,CACvD;AAED,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB,EAAW;IACnE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,0BAA0B,CAAC,MAG1C,EAA0B;IAC1B,MAAM,OAAO,GAA2B;QACvC,aAAa,EAAE,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC;QACrD,eAAe,EAAE,oBAAoB;KACrC,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,wBAAwB,CAAC,GAAG,MAAM,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AAAA,CACf","sourcesContent":["import type { Message } from \"../types.ts\";\n\n// Copilot expects X-Initiator to indicate whether the request is user-initiated\n// or agent-initiated (e.g. follow-up after assistant/tool messages).\nexport function inferCopilotInitiator(messages: Message[]): \"user\" | \"agent\" {\n\tconst last = messages[messages.length - 1];\n\treturn last && last.role !== \"user\" ? \"agent\" : \"user\";\n}\n\n// Copilot requires Copilot-Vision-Request header when sending images\nexport function hasCopilotVisionInput(messages: Message[]): boolean {\n\treturn messages.some((msg) => {\n\t\tif (msg.role === \"user\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\tif (msg.role === \"toolResult\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\treturn false;\n\t});\n}\n\nexport function buildCopilotDynamicHeaders(params: {\n\tmessages: Message[];\n\thasImages: boolean;\n}): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"X-Initiator\": inferCopilotInitiator(params.messages),\n\t\t\"Openai-Intent\": \"conversation-edits\",\n\t};\n\n\tif (params.hasImages) {\n\t\theaders[\"Copilot-Vision-Request\"] = \"true\";\n\t}\n\n\treturn headers;\n}\n"]}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared utilities for Google Generative AI and Google Vertex providers.
|
|
3
|
-
*/
|
|
4
|
-
import { type Content, FinishReason, FunctionCallingConfigMode, type Part } from "@google/genai";
|
|
5
|
-
import type { Context, Model, StopReason, Tool } from "../types.ts";
|
|
6
|
-
type GoogleApiType = "google-generative-ai" | "google-vertex";
|
|
7
|
-
/**
|
|
8
|
-
* Thinking level for Gemini 3 models.
|
|
9
|
-
* Mirrors Google's ThinkingLevel enum values.
|
|
10
|
-
*/
|
|
11
|
-
export type GoogleThinkingLevel = "THINKING_LEVEL_UNSPECIFIED" | "MINIMAL" | "LOW" | "MEDIUM" | "HIGH";
|
|
12
|
-
/**
|
|
13
|
-
* Determines whether a streamed Gemini `Part` should be treated as "thinking".
|
|
14
|
-
*
|
|
15
|
-
* Protocol note (Gemini / Vertex AI thought signatures):
|
|
16
|
-
* - `thought: true` is the definitive marker for thinking content (thought summaries).
|
|
17
|
-
* - `thoughtSignature` is an encrypted representation of the model's internal thought process
|
|
18
|
-
* used to preserve reasoning context across multi-turn interactions.
|
|
19
|
-
* - `thoughtSignature` can appear on ANY part type (text, functionCall, etc.) - it does NOT
|
|
20
|
-
* indicate the part itself is thinking content.
|
|
21
|
-
* - For non-functionCall responses, the signature appears on the last part for context replay.
|
|
22
|
-
* - When persisting/replaying model outputs, signature-bearing parts must be preserved as-is;
|
|
23
|
-
* do not merge/move signatures across parts.
|
|
24
|
-
*
|
|
25
|
-
* See: https://ai.google.dev/gemini-api/docs/thought-signatures
|
|
26
|
-
*/
|
|
27
|
-
export declare function isThinkingPart(part: Pick<Part, "thought" | "thoughtSignature">): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Retain thought signatures during streaming.
|
|
30
|
-
*
|
|
31
|
-
* Some backends only send `thoughtSignature` on the first delta for a given part/block; later deltas may omit it.
|
|
32
|
-
* This helper preserves the last non-empty signature for the current block.
|
|
33
|
-
*
|
|
34
|
-
* Note: this does NOT merge or move signatures across distinct response parts. It only prevents
|
|
35
|
-
* a signature from being overwritten with `undefined` within the same streamed block.
|
|
36
|
-
*/
|
|
37
|
-
export declare function retainThoughtSignature(existing: string | undefined, incoming: string | undefined): string | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* Models via Google APIs that require explicit tool call IDs in function calls/responses.
|
|
40
|
-
*/
|
|
41
|
-
export declare function requiresToolCallId(modelId: string): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Convert internal messages to Gemini Content[] format.
|
|
44
|
-
*/
|
|
45
|
-
export declare function convertMessages<T extends GoogleApiType>(model: Model<T>, context: Context): Content[];
|
|
46
|
-
/**
|
|
47
|
-
* Convert tools to Gemini function declarations format.
|
|
48
|
-
*
|
|
49
|
-
* By default uses `parametersJsonSchema` which supports full JSON Schema (including
|
|
50
|
-
* anyOf, oneOf, const, etc.). Set `useParameters` to true to use the legacy `parameters`
|
|
51
|
-
* field instead (OpenAPI 3.03 Schema). This is needed for Cloud Code Assist with Claude
|
|
52
|
-
* models, where the API translates `parameters` into Anthropic's `input_schema`.
|
|
53
|
-
*/
|
|
54
|
-
export declare function convertTools(tools: Tool[], useParameters?: boolean): {
|
|
55
|
-
functionDeclarations: Record<string, unknown>[];
|
|
56
|
-
}[] | undefined;
|
|
57
|
-
/**
|
|
58
|
-
* Map tool choice string to Gemini FunctionCallingConfigMode.
|
|
59
|
-
*/
|
|
60
|
-
export declare function mapToolChoice(choice: string): FunctionCallingConfigMode;
|
|
61
|
-
/**
|
|
62
|
-
* Map Gemini FinishReason to our StopReason.
|
|
63
|
-
*/
|
|
64
|
-
export declare function mapStopReason(reason: FinishReason): StopReason;
|
|
65
|
-
/**
|
|
66
|
-
* Map string finish reason to our StopReason (for raw API responses).
|
|
67
|
-
*/
|
|
68
|
-
export declare function mapStopReasonString(reason: string): StopReason;
|
|
69
|
-
export {};
|
|
70
|
-
//# sourceMappingURL=google-shared.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"google-shared.d.ts","sourceRoot":"","sources":["../../src/providers/google-shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,KAAK,EAAE,OAAO,EAAgB,KAAK,EAAE,UAAU,EAAe,IAAI,EAAE,MAAM,aAAa,CAAC;AAI/F,KAAK,aAAa,GAAG,sBAAsB,GAAG,eAAe,CAAC;AAE9D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,4BAA4B,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEvG;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,kBAAkB,CAAC,GAAG,OAAO,CAExF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGrH;AAkBD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3D;AAgBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAgJrG;AA6BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,UAAQ,GACnB;IAAE,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,EAAE,GAAG,SAAS,CAanE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,yBAAyB,CAWvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CA2B9D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAS9D","sourcesContent":["/**\n * Shared utilities for Google Generative AI and Google Vertex providers.\n */\n\nimport { type Content, FinishReason, FunctionCallingConfigMode, type Part } from \"@google/genai\";\nimport type { Context, ImageContent, Model, StopReason, TextContent, Tool } from \"../types.ts\";\nimport { sanitizeSurrogates } from \"../utils/sanitize-unicode.ts\";\nimport { transformMessages } from \"./transform-messages.ts\";\n\ntype GoogleApiType = \"google-generative-ai\" | \"google-vertex\";\n\n/**\n * Thinking level for Gemini 3 models.\n * Mirrors Google's ThinkingLevel enum values.\n */\nexport type GoogleThinkingLevel = \"THINKING_LEVEL_UNSPECIFIED\" | \"MINIMAL\" | \"LOW\" | \"MEDIUM\" | \"HIGH\";\n\n/**\n * Determines whether a streamed Gemini `Part` should be treated as \"thinking\".\n *\n * Protocol note (Gemini / Vertex AI thought signatures):\n * - `thought: true` is the definitive marker for thinking content (thought summaries).\n * - `thoughtSignature` is an encrypted representation of the model's internal thought process\n * used to preserve reasoning context across multi-turn interactions.\n * - `thoughtSignature` can appear on ANY part type (text, functionCall, etc.) - it does NOT\n * indicate the part itself is thinking content.\n * - For non-functionCall responses, the signature appears on the last part for context replay.\n * - When persisting/replaying model outputs, signature-bearing parts must be preserved as-is;\n * do not merge/move signatures across parts.\n *\n * See: https://ai.google.dev/gemini-api/docs/thought-signatures\n */\nexport function isThinkingPart(part: Pick<Part, \"thought\" | \"thoughtSignature\">): boolean {\n\treturn part.thought === true;\n}\n\n/**\n * Retain thought signatures during streaming.\n *\n * Some backends only send `thoughtSignature` on the first delta for a given part/block; later deltas may omit it.\n * This helper preserves the last non-empty signature for the current block.\n *\n * Note: this does NOT merge or move signatures across distinct response parts. It only prevents\n * a signature from being overwritten with `undefined` within the same streamed block.\n */\nexport function retainThoughtSignature(existing: string | undefined, incoming: string | undefined): string | undefined {\n\tif (typeof incoming === \"string\" && incoming.length > 0) return incoming;\n\treturn existing;\n}\n\n// Thought signatures must be base64 for Google APIs (TYPE_BYTES).\nconst base64SignaturePattern = /^[A-Za-z0-9+/]+={0,2}$/;\n\nfunction isValidThoughtSignature(signature: string | undefined): boolean {\n\tif (!signature) return false;\n\tif (signature.length % 4 !== 0) return false;\n\treturn base64SignaturePattern.test(signature);\n}\n\n/**\n * Only keep signatures from the same provider/model and with valid base64.\n */\nfunction resolveThoughtSignature(isSameProviderAndModel: boolean, signature: string | undefined): string | undefined {\n\treturn isSameProviderAndModel && isValidThoughtSignature(signature) ? signature : undefined;\n}\n\n/**\n * Models via Google APIs that require explicit tool call IDs in function calls/responses.\n */\nexport function requiresToolCallId(modelId: string): boolean {\n\treturn modelId.startsWith(\"claude-\") || modelId.startsWith(\"gpt-oss-\");\n}\n\nfunction getGeminiMajorVersion(modelId: string): number | undefined {\n\tconst match = modelId.toLowerCase().match(/^gemini(?:-live)?-(\\d+)/);\n\tif (!match) return undefined;\n\treturn Number.parseInt(match[1], 10);\n}\n\nfunction supportsMultimodalFunctionResponse(modelId: string): boolean {\n\tconst geminiMajorVersion = getGeminiMajorVersion(modelId);\n\tif (geminiMajorVersion !== undefined) {\n\t\treturn geminiMajorVersion >= 3;\n\t}\n\treturn true;\n}\n\n/**\n * Convert internal messages to Gemini Content[] format.\n */\nexport function convertMessages<T extends GoogleApiType>(model: Model<T>, context: Context): Content[] {\n\tconst contents: Content[] = [];\n\tconst normalizeToolCallId = (id: string): string => {\n\t\tif (!requiresToolCallId(model.id)) return id;\n\t\treturn id.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 64);\n\t};\n\n\tconst transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);\n\n\tfor (const msg of transformedMessages) {\n\t\tif (msg.role === \"user\") {\n\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\tcontents.push({\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tparts: [{ text: sanitizeSurrogates(msg.content) }],\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst parts: Part[] = msg.content.map((item) => {\n\t\t\t\t\tif (item.type === \"text\") {\n\t\t\t\t\t\treturn { text: sanitizeSurrogates(item.text) };\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tinlineData: {\n\t\t\t\t\t\t\t\tmimeType: item.mimeType,\n\t\t\t\t\t\t\t\tdata: item.data,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (parts.length === 0) continue;\n\t\t\t\tcontents.push({\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tparts,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tconst parts: Part[] = [];\n\t\t\t// Check if message is from same provider and model - only then keep thinking blocks\n\t\t\tconst isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;\n\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t// Skip empty text blocks\n\t\t\t\t\tif (!block.text || block.text.trim() === \"\") continue;\n\t\t\t\t\tconst thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.textSignature);\n\t\t\t\t\tparts.push({\n\t\t\t\t\t\ttext: sanitizeSurrogates(block.text),\n\t\t\t\t\t\t...(thoughtSignature && { thoughtSignature }),\n\t\t\t\t\t});\n\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t// Skip empty thinking blocks\n\t\t\t\t\tif (!block.thinking || block.thinking.trim() === \"\") continue;\n\t\t\t\t\t// Only keep as thinking block if same provider AND same model\n\t\t\t\t\t// Otherwise convert to plain text (no tags to avoid model mimicking them)\n\t\t\t\t\tif (isSameProviderAndModel) {\n\t\t\t\t\t\tconst thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thinkingSignature);\n\t\t\t\t\t\tparts.push({\n\t\t\t\t\t\t\tthought: true,\n\t\t\t\t\t\t\ttext: sanitizeSurrogates(block.thinking),\n\t\t\t\t\t\t\t...(thoughtSignature && { thoughtSignature }),\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparts.push({\n\t\t\t\t\t\t\ttext: sanitizeSurrogates(block.thinking),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\tconst thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thoughtSignature);\n\t\t\t\t\tconst part: Part = {\n\t\t\t\t\t\tfunctionCall: {\n\t\t\t\t\t\t\tname: block.name,\n\t\t\t\t\t\t\targs: block.arguments ?? {},\n\t\t\t\t\t\t\t...(requiresToolCallId(model.id) ? { id: block.id } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...(thoughtSignature && { thoughtSignature }),\n\t\t\t\t\t};\n\t\t\t\t\tparts.push(part);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (parts.length === 0) continue;\n\t\t\tcontents.push({\n\t\t\t\trole: \"model\",\n\t\t\t\tparts,\n\t\t\t});\n\t\t} else if (msg.role === \"toolResult\") {\n\t\t\t// Extract text and image content\n\t\t\tconst textContent = msg.content.filter((c): c is TextContent => c.type === \"text\");\n\t\t\tconst textResult = textContent.map((c) => c.text).join(\"\\n\");\n\t\t\tconst imageContent = model.input.includes(\"image\")\n\t\t\t\t? msg.content.filter((c): c is ImageContent => c.type === \"image\")\n\t\t\t\t: [];\n\n\t\t\tconst hasText = textResult.length > 0;\n\t\t\tconst hasImages = imageContent.length > 0;\n\n\t\t\t// Gemini 3+ models support multimodal function responses with images nested inside\n\t\t\t// functionResponse.parts. Claude and other non-Gemini models behind Cloud Code Assist /\n\t\t\t// Gemini < 3 still needs a separate user image turn.\n\t\t\tconst modelSupportsMultimodalFunctionResponse = supportsMultimodalFunctionResponse(model.id);\n\n\t\t\t// Use \"output\" key for success, \"error\" key for errors as per SDK documentation\n\t\t\tconst responseValue = hasText ? sanitizeSurrogates(textResult) : hasImages ? \"(see attached image)\" : \"\";\n\n\t\t\tconst imageParts: Part[] = imageContent.map((imageBlock) => ({\n\t\t\t\tinlineData: {\n\t\t\t\t\tmimeType: imageBlock.mimeType,\n\t\t\t\t\tdata: imageBlock.data,\n\t\t\t\t},\n\t\t\t}));\n\n\t\t\tconst includeId = requiresToolCallId(model.id);\n\t\t\tconst functionResponsePart: Part = {\n\t\t\t\tfunctionResponse: {\n\t\t\t\t\tname: msg.toolName,\n\t\t\t\t\tresponse: msg.isError ? { error: responseValue } : { output: responseValue },\n\t\t\t\t\t...(hasImages && modelSupportsMultimodalFunctionResponse && { parts: imageParts }),\n\t\t\t\t\t...(includeId ? { id: msg.toolCallId } : {}),\n\t\t\t\t},\n\t\t\t};\n\n\t\t\t// Cloud Code Assist API requires all function responses to be in a single user turn.\n\t\t\t// Check if the last content is already a user turn with function responses and merge.\n\t\t\tconst lastContent = contents[contents.length - 1];\n\t\t\tif (lastContent?.role === \"user\" && lastContent.parts?.some((p) => p.functionResponse)) {\n\t\t\t\tlastContent.parts.push(functionResponsePart);\n\t\t\t} else {\n\t\t\t\tcontents.push({\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tparts: [functionResponsePart],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For Gemini < 3, add images in a separate user message\n\t\t\tif (hasImages && !modelSupportsMultimodalFunctionResponse) {\n\t\t\t\tcontents.push({\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tparts: [{ text: \"Tool result image:\" }, ...imageParts],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents;\n}\n\nconst JSON_SCHEMA_META_DECLARATIONS = new Set([\n\t\"$schema\",\n\t\"$id\",\n\t\"$anchor\",\n\t\"$dynamicAnchor\",\n\t\"$vocabulary\",\n\t\"$comment\",\n\t\"$defs\",\n\t\"definitions\", // pre-draft-2019-09 equivalent of $defs\n]);\n\n/**\n * Strip meta-declarations from a schema obj\n */\nfunction sanitizeForOpenApi(schema: unknown): unknown {\n\tif (typeof schema !== \"object\" || schema === null || Array.isArray(schema)) {\n\t\treturn schema;\n\t}\n\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(schema)) {\n\t\tif (JSON_SCHEMA_META_DECLARATIONS.has(key)) continue;\n\t\tresult[key] = sanitizeForOpenApi(value);\n\t}\n\treturn result;\n}\n\n/**\n * Convert tools to Gemini function declarations format.\n *\n * By default uses `parametersJsonSchema` which supports full JSON Schema (including\n * anyOf, oneOf, const, etc.). Set `useParameters` to true to use the legacy `parameters`\n * field instead (OpenAPI 3.03 Schema). This is needed for Cloud Code Assist with Claude\n * models, where the API translates `parameters` into Anthropic's `input_schema`.\n */\nexport function convertTools(\n\ttools: Tool[],\n\tuseParameters = false,\n): { functionDeclarations: Record<string, unknown>[] }[] | undefined {\n\tif (tools.length === 0) return undefined;\n\treturn [\n\t\t{\n\t\t\tfunctionDeclarations: tools.map((tool) => ({\n\t\t\t\tname: tool.name,\n\t\t\t\tdescription: tool.description,\n\t\t\t\t...(useParameters\n\t\t\t\t\t? { parameters: sanitizeForOpenApi(tool.parameters as unknown) }\n\t\t\t\t\t: { parametersJsonSchema: tool.parameters }),\n\t\t\t})),\n\t\t},\n\t];\n}\n\n/**\n * Map tool choice string to Gemini FunctionCallingConfigMode.\n */\nexport function mapToolChoice(choice: string): FunctionCallingConfigMode {\n\tswitch (choice) {\n\t\tcase \"auto\":\n\t\t\treturn FunctionCallingConfigMode.AUTO;\n\t\tcase \"none\":\n\t\t\treturn FunctionCallingConfigMode.NONE;\n\t\tcase \"any\":\n\t\t\treturn FunctionCallingConfigMode.ANY;\n\t\tdefault:\n\t\t\treturn FunctionCallingConfigMode.AUTO;\n\t}\n}\n\n/**\n * Map Gemini FinishReason to our StopReason.\n */\nexport function mapStopReason(reason: FinishReason): StopReason {\n\tswitch (reason) {\n\t\tcase FinishReason.STOP:\n\t\t\treturn \"stop\";\n\t\tcase FinishReason.MAX_TOKENS:\n\t\t\treturn \"length\";\n\t\tcase FinishReason.BLOCKLIST:\n\t\tcase FinishReason.PROHIBITED_CONTENT:\n\t\tcase FinishReason.SPII:\n\t\tcase FinishReason.SAFETY:\n\t\tcase FinishReason.IMAGE_SAFETY:\n\t\tcase FinishReason.IMAGE_PROHIBITED_CONTENT:\n\t\tcase FinishReason.IMAGE_RECITATION:\n\t\tcase FinishReason.IMAGE_OTHER:\n\t\tcase FinishReason.RECITATION:\n\t\tcase FinishReason.FINISH_REASON_UNSPECIFIED:\n\t\tcase FinishReason.OTHER:\n\t\tcase FinishReason.LANGUAGE:\n\t\tcase FinishReason.MALFORMED_FUNCTION_CALL:\n\t\tcase FinishReason.UNEXPECTED_TOOL_CALL:\n\t\tcase FinishReason.NO_IMAGE:\n\t\t\treturn \"error\";\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = reason;\n\t\t\tthrow new Error(`Unhandled stop reason: ${_exhaustive}`);\n\t\t}\n\t}\n}\n\n/**\n * Map string finish reason to our StopReason (for raw API responses).\n */\nexport function mapStopReasonString(reason: string): StopReason {\n\tswitch (reason) {\n\t\tcase \"STOP\":\n\t\t\treturn \"stop\";\n\t\tcase \"MAX_TOKENS\":\n\t\t\treturn \"length\";\n\t\tdefault:\n\t\t\treturn \"error\";\n\t}\n}\n"]}
|