@qjp88995/provider-client 0.0.0-stage → 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.
- package/dist/center-fetch.d.ts +29 -0
- package/dist/center-fetch.d.ts.map +1 -0
- package/dist/center-fetch.js +45 -0
- package/dist/center-fetch.js.map +1 -0
- package/dist/direct-resolver.d.ts +29 -0
- package/dist/direct-resolver.d.ts.map +1 -0
- package/dist/direct-resolver.js +47 -0
- package/dist/direct-resolver.js.map +1 -0
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +57 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/model-handle.d.ts +92 -0
- package/dist/model-handle.d.ts.map +1 -0
- package/dist/model-handle.js +72 -0
- package/dist/model-handle.js.map +1 -0
- package/dist/provider-client.d.ts +40 -0
- package/dist/provider-client.d.ts.map +1 -0
- package/dist/provider-client.js +96 -0
- package/dist/provider-client.js.map +1 -0
- package/dist/to-error.d.ts +12 -0
- package/dist/to-error.d.ts.map +1 -0
- package/dist/to-error.js +33 -0
- package/dist/to-error.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +75 -0
- package/dist/types.js.map +1 -0
- package/dist/vendors/alibaba.adapter.d.ts +47 -0
- package/dist/vendors/alibaba.adapter.d.ts.map +1 -0
- package/dist/vendors/alibaba.adapter.js +79 -0
- package/dist/vendors/alibaba.adapter.js.map +1 -0
- package/dist/vendors/ark-responses-shim.d.ts +25 -0
- package/dist/vendors/ark-responses-shim.d.ts.map +1 -0
- package/dist/vendors/ark-responses-shim.js +60 -0
- package/dist/vendors/ark-responses-shim.js.map +1 -0
- package/dist/vendors/deepseek.adapter.d.ts +50 -0
- package/dist/vendors/deepseek.adapter.d.ts.map +1 -0
- package/dist/vendors/deepseek.adapter.js +123 -0
- package/dist/vendors/deepseek.adapter.js.map +1 -0
- package/dist/vendors/dots.adapter.d.ts +32 -0
- package/dist/vendors/dots.adapter.d.ts.map +1 -0
- package/dist/vendors/dots.adapter.js +53 -0
- package/dist/vendors/dots.adapter.js.map +1 -0
- package/dist/vendors/generic.adapter.d.ts +33 -0
- package/dist/vendors/generic.adapter.d.ts.map +1 -0
- package/dist/vendors/generic.adapter.js +82 -0
- package/dist/vendors/generic.adapter.js.map +1 -0
- package/dist/vendors/google-signature.d.ts +17 -0
- package/dist/vendors/google-signature.d.ts.map +1 -0
- package/dist/vendors/google-signature.js +69 -0
- package/dist/vendors/google-signature.js.map +1 -0
- package/dist/vendors/google.adapter.d.ts +41 -0
- package/dist/vendors/google.adapter.d.ts.map +1 -0
- package/dist/vendors/google.adapter.js +164 -0
- package/dist/vendors/google.adapter.js.map +1 -0
- package/dist/vendors/index.d.ts +11 -0
- package/dist/vendors/index.d.ts.map +1 -0
- package/dist/vendors/index.js +39 -0
- package/dist/vendors/index.js.map +1 -0
- package/dist/vendors/minimax.adapter.d.ts +98 -0
- package/dist/vendors/minimax.adapter.d.ts.map +1 -0
- package/dist/vendors/minimax.adapter.js +163 -0
- package/dist/vendors/minimax.adapter.js.map +1 -0
- package/dist/vendors/moonshotai.adapter.d.ts +31 -0
- package/dist/vendors/moonshotai.adapter.d.ts.map +1 -0
- package/dist/vendors/moonshotai.adapter.js +57 -0
- package/dist/vendors/moonshotai.adapter.js.map +1 -0
- package/dist/vendors/openai-responses.d.ts +49 -0
- package/dist/vendors/openai-responses.d.ts.map +1 -0
- package/dist/vendors/openai-responses.js +64 -0
- package/dist/vendors/openai-responses.js.map +1 -0
- package/dist/vendors/openai.adapter.d.ts +27 -0
- package/dist/vendors/openai.adapter.d.ts.map +1 -0
- package/dist/vendors/openai.adapter.js +196 -0
- package/dist/vendors/openai.adapter.js.map +1 -0
- package/dist/vendors/responses-reasoning-text-shim.d.ts +50 -0
- package/dist/vendors/responses-reasoning-text-shim.d.ts.map +1 -0
- package/dist/vendors/responses-reasoning-text-shim.js +170 -0
- package/dist/vendors/responses-reasoning-text-shim.js.map +1 -0
- package/dist/vendors/vendor-adapter.d.ts +214 -0
- package/dist/vendors/vendor-adapter.d.ts.map +1 -0
- package/dist/vendors/vendor-adapter.js +150 -0
- package/dist/vendors/vendor-adapter.js.map +1 -0
- package/dist/vendors/volcengine.adapter.d.ts +74 -0
- package/dist/vendors/volcengine.adapter.d.ts.map +1 -0
- package/dist/vendors/volcengine.adapter.js +139 -0
- package/dist/vendors/volcengine.adapter.js.map +1 -0
- package/dist/vendors/zhipu.adapter.d.ts +81 -0
- package/dist/vendors/zhipu.adapter.d.ts.map +1 -0
- package/dist/vendors/zhipu.adapter.js +133 -0
- package/dist/vendors/zhipu.adapter.js.map +1 -0
- package/package.json +53 -3
- package/src/center-fetch.ts +69 -0
- package/src/direct-resolver.ts +90 -0
- package/src/errors.ts +88 -0
- package/src/index.ts +54 -0
- package/src/model-handle.ts +197 -0
- package/src/provider-client.ts +136 -0
- package/src/to-error.ts +39 -0
- package/src/types.ts +126 -0
- package/src/vendors/alibaba.adapter.ts +101 -0
- package/src/vendors/ark-responses-shim.ts +60 -0
- package/src/vendors/deepseek.adapter.ts +155 -0
- package/src/vendors/dots.adapter.ts +69 -0
- package/src/vendors/generic.adapter.ts +103 -0
- package/src/vendors/google-signature.ts +87 -0
- package/src/vendors/google.adapter.ts +197 -0
- package/src/vendors/index.ts +56 -0
- package/src/vendors/minimax.adapter.ts +193 -0
- package/src/vendors/moonshotai.adapter.ts +84 -0
- package/src/vendors/openai-responses.ts +82 -0
- package/src/vendors/openai.adapter.ts +235 -0
- package/src/vendors/responses-reasoning-text-shim.ts +174 -0
- package/src/vendors/vendor-adapter.ts +294 -0
- package/src/vendors/volcengine.adapter.ts +166 -0
- package/src/vendors/zhipu.adapter.ts +169 -0
- package/README.md +0 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
|
+
import type { LlmProtocol } from '../types.js';
|
|
3
|
+
import { type CreateModelInput, LlmVendorAdapter, type ProviderOptions, type ReasoningEffort } from './vendor-adapter.js';
|
|
4
|
+
/**
|
|
5
|
+
* 智谱的推理开关是**模型级**的两种形态(2026-08-27 用真实 API 探针钉住,
|
|
6
|
+
* 文档见 <https://docs.bigmodel.cn/cn/guide/capabilities/thinking-mode.md>):
|
|
7
|
+
*
|
|
8
|
+
* - `forced`:glm-5.3 / glm-5.3-flash。**思考关不掉**——发 `thinking:{type:'disabled'}`
|
|
9
|
+
* 直接 400(code 1210「该模型始终思考,不支持关闭思考;请使用 low、high 或 max」),
|
|
10
|
+
* 只认 `reasoning_effort`。
|
|
11
|
+
* - `toggle`:glm-5.2 及更早的推理模型(5.1 / 5 / 4.7 / 4.6 / 4.5)。`thinking` 的
|
|
12
|
+
* `enabled`/`disabled` 都合法,且**默认是 enabled**(不发 = 开着推理,探针
|
|
13
|
+
* `reasoning_tokens: 62`),档位同样走 `reasoning_effort`(4.7、5.2 实测都收)。
|
|
14
|
+
*
|
|
15
|
+
* 模型名换了就改这一个函数。没有推理能力的老模型(glm-4-flash 那批)走不到这里
|
|
16
|
+
* ——resolver 只对标了 `reasoning` 能力的模型问推理选项。
|
|
17
|
+
*/
|
|
18
|
+
type ThinkingStyle = 'forced' | 'toggle';
|
|
19
|
+
export declare function zhipuThinkingStyle(modelId: string): ThinkingStyle;
|
|
20
|
+
/**
|
|
21
|
+
* 智谱 BigModel(GLM 系)。
|
|
22
|
+
*
|
|
23
|
+
* 承载包是 `@ai-sdk/openai-compatible`,**不是**社区的 `zhipu-ai-provider`(维护
|
|
24
|
+
* 停滞、跟不上 AI SDK 大版本)。选它的依据是一条实测过的 SDK 行为:它把
|
|
25
|
+
* `providerOptions[name]` 里**不在自己 schema 里的键原样展开进请求体**
|
|
26
|
+
* (dist 里 `...Object.fromEntries(Object.entries(providerOptions[...]).filter(...))`),
|
|
27
|
+
* 所以智谱私有的 `thinking` 参数不必写一整套 LanguageModel 就能下发;
|
|
28
|
+
* `reasoningEffort` 则在 schema 里、由 SDK 翻成 `reasoning_effort`。响应侧
|
|
29
|
+
* `reasoning_content`(非流式 message 与流式 delta 都是这个字段名)也正是
|
|
30
|
+
* 该包原生读取的字段——同目录 `zhipu.sdk-contract.spec.ts` 跑真实 SDK 钉这两条。
|
|
31
|
+
*
|
|
32
|
+
* 接入面:文档的「HTTP 原生」与「OpenAI 兼容」是**同一个端点**
|
|
33
|
+
* `/api/paas/v4/chat/completions`,前者只是多了几个私有参数,合为 chat-completions
|
|
34
|
+
* 一个分支;Responses 端点(`/api/v1/responses`)**没有任何文档页**,只在模型页的
|
|
35
|
+
* 接入方式表与 Codex 接入指南里各出现一次,全部判据来自 2026-08-27 的探针(见
|
|
36
|
+
* `reasoning()` 注释与 providers.md);Anthropic 兼容面(`/api/anthropic`)本仓
|
|
37
|
+
* 尚无 `anthropic-messages` 分支,用户拍板先不做。
|
|
38
|
+
*
|
|
39
|
+
* Responses 面的两条运维事实:① GLM-5.3 模型页接入表下的 `<Note>` 原文——「如果您
|
|
40
|
+
* 有订阅过 GLM Coding Plan(含已过期),那么**暂时**您只能通过 OpenAI Chat Completion
|
|
41
|
+
* 协议调用模型 API,我们将在近期迭代优化」,是临时限制,那种账号在 responses 端点
|
|
42
|
+
* 失败时先想到它;② 探针约 30 次调用里有 3 次非流式请求挂到超时(重打即成功),
|
|
43
|
+
* 是厂商侧偶发,不是形状问题。
|
|
44
|
+
*
|
|
45
|
+
* **`temperature` 只收 `[0,1]`**(超出直接 400「temperature参数非法」,实测),
|
|
46
|
+
* 与 OpenAI 的 `[0,2]` 不同;`sampling` 只有「发不发」的粒度,这条限制目前靠
|
|
47
|
+
* 运营录参数时自觉,未做钳位。
|
|
48
|
+
*/
|
|
49
|
+
export declare class ZhipuAdapter extends LlmVendorAdapter {
|
|
50
|
+
readonly kind: "zhipu";
|
|
51
|
+
readonly supportedProtocols: readonly LlmProtocol[];
|
|
52
|
+
/**
|
|
53
|
+
* responses 走 `@ai-sdk/openai` + 与 DeepSeek 共用的响应侧垫片:智谱的推理正文
|
|
54
|
+
* 同样以 reasoning item 的 `content[{reasoning_text}]` 返回、流式事件同样是
|
|
55
|
+
* `response.reasoning_text.delta`(探针逐字段核对过),不垫就是推理整段消失。
|
|
56
|
+
* 请求侧不动:四种回传形状(原样 output / 只回 function_call / `item_reference`
|
|
57
|
+
* / 流式)全部 200;与 DeepSeek 不同的是它**会消费**发回去的 reasoning item
|
|
58
|
+
* (input_tokens 恰好多出上一轮的 reasoning_tokens),SDK 默认发什么就发什么。
|
|
59
|
+
*/
|
|
60
|
+
createModel(input: CreateModelInput): LanguageModel;
|
|
61
|
+
/**
|
|
62
|
+
* **chat-completions**:`off` 在 toggle 模型上必须发**显式的**
|
|
63
|
+
* `thinking:{type:'disabled'}`——它默认开着推理,「不发 ≠ 关闭」在这家再次成立;
|
|
64
|
+
* forced 模型没有关闭开关,off 只能不发(如实反映厂商限制,不是漏了关闭指令)。
|
|
65
|
+
* 非 off 一律 `thinking:enabled` + `reasoning_effort`:厂商原生三档 `low/high/max`
|
|
66
|
+
* (glm-5.3 默认 `max`),我们的五档压进去;`max` 直通。`medium` 实测 5.2 也收,
|
|
67
|
+
* 但 5.3 的 400 文案只认三档,统一映射不赌宽容度。
|
|
68
|
+
*
|
|
69
|
+
* **responses**:只有 `reasoning.effort` 一个开关,且**这个面上推理关不掉**——
|
|
70
|
+
* 探针在 glm-5.2 上把 `effort:'none'`、体内 `thinking:{type:'disabled'}`、两者同发
|
|
71
|
+
* 全试过,reasoning_tokens 纹丝不动(100);glm-5.3 系收到 `none` 则直接 400
|
|
72
|
+
* 「该模型始终思考」。所以 `off` 在 responses 下**不发**:发 `none` 在 5.2 上
|
|
73
|
+
* 是无效指令、在 5.3 上是错误,两边都不是「关闭」。这是本协议面的已知限制,
|
|
74
|
+
* 要真关推理请给模型只声明 chat-completions。档位 key 随承载包换成 `openai`,
|
|
75
|
+
* 并带 `COMPATIBLE_RESPONSES_OPTIONS`(否则 SDK 按模型名判成非推理模型、
|
|
76
|
+
* `reasoning` 整个不发;`developer` 角色实测也收,但只保证 `system`)。
|
|
77
|
+
*/
|
|
78
|
+
reasoning(modelId: string, effort: ReasoningEffort, protocol: LlmProtocol): ProviderOptions | undefined;
|
|
79
|
+
}
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=zhipu.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zhipu.adapter.d.ts","sourceRoot":"","sources":["../../src/vendors/zhipu.adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,OAAO,EACL,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,eAAe,EAErB,MAAM,qBAAqB,CAAC;AAS7B;;;;;;;;;;;;;GAaG;AACH,KAAK,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAIjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,YAAa,SAAQ,gBAAgB;IAChD,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IAEjC,SAAkB,kBAAkB,EAAE,SAAS,WAAW,EAAE,CAG1D;IAEF;;;;;;;OAOG;IACH,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,aAAa;IAqBnD;;;;;;;;;;;;;;;;OAgBG;IACM,SAAS,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,WAAW,GACpB,eAAe,GAAG,SAAS;CA4B/B"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
2
|
+
import { COMPATIBLE_RESPONSES_OPTIONS, createResponsesModel, } from './openai-responses.js';
|
|
3
|
+
import { withReasoningTextResponsesShim } from './responses-reasoning-text-shim.js';
|
|
4
|
+
import { LlmVendorAdapter, unsupportedProtocol, } from './vendor-adapter.js';
|
|
5
|
+
/**
|
|
6
|
+
* 工厂 `name`。它同时决定 provider id(`zhipu.chat`)与 providerOptions 的
|
|
7
|
+
* 命名空间 key(SDK 认 `name` 与 `toCamelCase(name)`,单词无连字符两者相同),
|
|
8
|
+
* 恰好等于我们的 `kind`,故基类默认的 `optionsKey` 直接可用。
|
|
9
|
+
*/
|
|
10
|
+
const FACTORY_NAME = 'zhipu';
|
|
11
|
+
export function zhipuThinkingStyle(modelId) {
|
|
12
|
+
const id = modelId.toLowerCase();
|
|
13
|
+
if (id.startsWith('glm-5.3'))
|
|
14
|
+
return 'forced';
|
|
15
|
+
return 'toggle';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 智谱 BigModel(GLM 系)。
|
|
19
|
+
*
|
|
20
|
+
* 承载包是 `@ai-sdk/openai-compatible`,**不是**社区的 `zhipu-ai-provider`(维护
|
|
21
|
+
* 停滞、跟不上 AI SDK 大版本)。选它的依据是一条实测过的 SDK 行为:它把
|
|
22
|
+
* `providerOptions[name]` 里**不在自己 schema 里的键原样展开进请求体**
|
|
23
|
+
* (dist 里 `...Object.fromEntries(Object.entries(providerOptions[...]).filter(...))`),
|
|
24
|
+
* 所以智谱私有的 `thinking` 参数不必写一整套 LanguageModel 就能下发;
|
|
25
|
+
* `reasoningEffort` 则在 schema 里、由 SDK 翻成 `reasoning_effort`。响应侧
|
|
26
|
+
* `reasoning_content`(非流式 message 与流式 delta 都是这个字段名)也正是
|
|
27
|
+
* 该包原生读取的字段——同目录 `zhipu.sdk-contract.spec.ts` 跑真实 SDK 钉这两条。
|
|
28
|
+
*
|
|
29
|
+
* 接入面:文档的「HTTP 原生」与「OpenAI 兼容」是**同一个端点**
|
|
30
|
+
* `/api/paas/v4/chat/completions`,前者只是多了几个私有参数,合为 chat-completions
|
|
31
|
+
* 一个分支;Responses 端点(`/api/v1/responses`)**没有任何文档页**,只在模型页的
|
|
32
|
+
* 接入方式表与 Codex 接入指南里各出现一次,全部判据来自 2026-08-27 的探针(见
|
|
33
|
+
* `reasoning()` 注释与 providers.md);Anthropic 兼容面(`/api/anthropic`)本仓
|
|
34
|
+
* 尚无 `anthropic-messages` 分支,用户拍板先不做。
|
|
35
|
+
*
|
|
36
|
+
* Responses 面的两条运维事实:① GLM-5.3 模型页接入表下的 `<Note>` 原文——「如果您
|
|
37
|
+
* 有订阅过 GLM Coding Plan(含已过期),那么**暂时**您只能通过 OpenAI Chat Completion
|
|
38
|
+
* 协议调用模型 API,我们将在近期迭代优化」,是临时限制,那种账号在 responses 端点
|
|
39
|
+
* 失败时先想到它;② 探针约 30 次调用里有 3 次非流式请求挂到超时(重打即成功),
|
|
40
|
+
* 是厂商侧偶发,不是形状问题。
|
|
41
|
+
*
|
|
42
|
+
* **`temperature` 只收 `[0,1]`**(超出直接 400「temperature参数非法」,实测),
|
|
43
|
+
* 与 OpenAI 的 `[0,2]` 不同;`sampling` 只有「发不发」的粒度,这条限制目前靠
|
|
44
|
+
* 运营录参数时自觉,未做钳位。
|
|
45
|
+
*/
|
|
46
|
+
export class ZhipuAdapter extends LlmVendorAdapter {
|
|
47
|
+
kind = 'zhipu';
|
|
48
|
+
supportedProtocols = [
|
|
49
|
+
'openai-chat-completions',
|
|
50
|
+
'openai-responses',
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* responses 走 `@ai-sdk/openai` + 与 DeepSeek 共用的响应侧垫片:智谱的推理正文
|
|
54
|
+
* 同样以 reasoning item 的 `content[{reasoning_text}]` 返回、流式事件同样是
|
|
55
|
+
* `response.reasoning_text.delta`(探针逐字段核对过),不垫就是推理整段消失。
|
|
56
|
+
* 请求侧不动:四种回传形状(原样 output / 只回 function_call / `item_reference`
|
|
57
|
+
* / 流式)全部 200;与 DeepSeek 不同的是它**会消费**发回去的 reasoning item
|
|
58
|
+
* (input_tokens 恰好多出上一轮的 reasoning_tokens),SDK 默认发什么就发什么。
|
|
59
|
+
*/
|
|
60
|
+
createModel(input) {
|
|
61
|
+
switch (input.protocol) {
|
|
62
|
+
case 'openai-chat-completions': {
|
|
63
|
+
const { baseUrl, apiKey, modelId, fetch } = input;
|
|
64
|
+
return createOpenAICompatible({
|
|
65
|
+
name: FACTORY_NAME,
|
|
66
|
+
baseURL: baseUrl,
|
|
67
|
+
apiKey,
|
|
68
|
+
fetch,
|
|
69
|
+
})(modelId);
|
|
70
|
+
}
|
|
71
|
+
case 'openai-responses':
|
|
72
|
+
return createResponsesModel(input, withReasoningTextResponsesShim(input.fetch));
|
|
73
|
+
default:
|
|
74
|
+
throw unsupportedProtocol(this, input.protocol);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* **chat-completions**:`off` 在 toggle 模型上必须发**显式的**
|
|
79
|
+
* `thinking:{type:'disabled'}`——它默认开着推理,「不发 ≠ 关闭」在这家再次成立;
|
|
80
|
+
* forced 模型没有关闭开关,off 只能不发(如实反映厂商限制,不是漏了关闭指令)。
|
|
81
|
+
* 非 off 一律 `thinking:enabled` + `reasoning_effort`:厂商原生三档 `low/high/max`
|
|
82
|
+
* (glm-5.3 默认 `max`),我们的五档压进去;`max` 直通。`medium` 实测 5.2 也收,
|
|
83
|
+
* 但 5.3 的 400 文案只认三档,统一映射不赌宽容度。
|
|
84
|
+
*
|
|
85
|
+
* **responses**:只有 `reasoning.effort` 一个开关,且**这个面上推理关不掉**——
|
|
86
|
+
* 探针在 glm-5.2 上把 `effort:'none'`、体内 `thinking:{type:'disabled'}`、两者同发
|
|
87
|
+
* 全试过,reasoning_tokens 纹丝不动(100);glm-5.3 系收到 `none` 则直接 400
|
|
88
|
+
* 「该模型始终思考」。所以 `off` 在 responses 下**不发**:发 `none` 在 5.2 上
|
|
89
|
+
* 是无效指令、在 5.3 上是错误,两边都不是「关闭」。这是本协议面的已知限制,
|
|
90
|
+
* 要真关推理请给模型只声明 chat-completions。档位 key 随承载包换成 `openai`,
|
|
91
|
+
* 并带 `COMPATIBLE_RESPONSES_OPTIONS`(否则 SDK 按模型名判成非推理模型、
|
|
92
|
+
* `reasoning` 整个不发;`developer` 角色实测也收,但只保证 `system`)。
|
|
93
|
+
*/
|
|
94
|
+
reasoning(modelId, effort, protocol) {
|
|
95
|
+
switch (protocol) {
|
|
96
|
+
case 'openai-chat-completions': {
|
|
97
|
+
const style = zhipuThinkingStyle(modelId);
|
|
98
|
+
if (effort === 'off') {
|
|
99
|
+
if (style === 'forced')
|
|
100
|
+
return undefined;
|
|
101
|
+
return { [this.optionsKey]: { thinking: { type: 'disabled' } } };
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
[this.optionsKey]: {
|
|
105
|
+
thinking: { type: 'enabled' },
|
|
106
|
+
reasoningEffort: toZhipuEffort(effort),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
case 'openai-responses': {
|
|
111
|
+
if (effort === 'off')
|
|
112
|
+
return undefined;
|
|
113
|
+
return {
|
|
114
|
+
openai: {
|
|
115
|
+
reasoningEffort: toZhipuEffort(effort),
|
|
116
|
+
...COMPATIBLE_RESPONSES_OPTIONS,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
default:
|
|
121
|
+
throw unsupportedProtocol(this, protocol);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** 我们的档位 → 智谱原生三档 `low/high/max`。`max` 直通——用户选了最高档就该拿到最高档。 */
|
|
126
|
+
function toZhipuEffort(effort) {
|
|
127
|
+
if (effort === 'minimal' || effort === 'low')
|
|
128
|
+
return 'low';
|
|
129
|
+
if (effort === 'max')
|
|
130
|
+
return 'max';
|
|
131
|
+
return 'high';
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=zhipu.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zhipu.adapter.js","sourceRoot":"","sources":["../../src/vendors/zhipu.adapter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qjp88995/provider-client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Client for the provider center: vendor adapters for the AI SDK, model resolution through the center's catalog and passthrough proxy, and a direct mode for local development.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/qjp88995/provider-center.git",
|
|
10
|
+
"directory": "packages/client"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org/"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"!src/**/*.spec.ts"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.12"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@ai-sdk/alibaba": "^2.0.75",
|
|
32
|
+
"@ai-sdk/deepseek": "^3.0.72",
|
|
33
|
+
"@ai-sdk/google": "^4.0.104",
|
|
34
|
+
"@ai-sdk/moonshotai": "^3.0.76",
|
|
35
|
+
"@ai-sdk/openai": "^4.0.101",
|
|
36
|
+
"@ai-sdk/openai-compatible": "^3.0.77"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"ai": "^7.0.150"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^25.9.5",
|
|
43
|
+
"ai": "^7.0.150",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"vitest": "^4.1.10",
|
|
46
|
+
"zod": "^4.4.3",
|
|
47
|
+
"@pc/shared": "0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "rm -rf dist *.tsbuildinfo && tsc -p tsconfig.build.json",
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"clean": "rm -rf dist .turbo *.tsbuildinfo"
|
|
55
|
+
}
|
|
6
56
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { PC_HEADERS, PROXY_ROUTE_PREFIX } from './types.js';
|
|
2
|
+
import type { VendorFetch } from './vendors/index.js';
|
|
3
|
+
|
|
4
|
+
/** 厂商 SDK 可能放厂商 key 的头。中心反正会剥,这里先剥掉:假 key 不出本进程。 */
|
|
5
|
+
const VENDOR_AUTH_HEADERS = [
|
|
6
|
+
'authorization',
|
|
7
|
+
'x-goog-api-key',
|
|
8
|
+
'x-api-key',
|
|
9
|
+
'api-key',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export interface CenterFetchOptions {
|
|
13
|
+
/** 中心的根地址,如 `https://provider.example.com`。 */
|
|
14
|
+
centerUrl: string;
|
|
15
|
+
/** 接入方 API key(`pc_…`)。 */
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/** 厂商 slug(透传路由段)。 */
|
|
18
|
+
vendor: string;
|
|
19
|
+
/**
|
|
20
|
+
* 这家所有端点的 baseUrl。只改写落在这些 origin 上的请求;别的 origin 直接报错——那是适配器拼错了地址,
|
|
21
|
+
* 悄悄发出去就是带着中心的 key 打到别处。
|
|
22
|
+
*/
|
|
23
|
+
baseUrls: readonly string[];
|
|
24
|
+
/** 会话粘性键(`x-pc-affinity`)。 */
|
|
25
|
+
affinity?: string;
|
|
26
|
+
/** 钉住某把 key(`x-pc-pin`)。 */
|
|
27
|
+
pin?: string;
|
|
28
|
+
/** 每次响应回来时告诉调用方实际用的是哪把 key(`x-pc-credential`)。 */
|
|
29
|
+
onCredential?: (ref: string) => void;
|
|
30
|
+
/** 发往中心用的 fetch;默认全局 fetch。 */
|
|
31
|
+
fetch?: VendorFetch;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 中心模式的出站 fetch,交给适配器的 `CreateModelInput.fetch`:把厂商 URL 的 origin 换成
|
|
36
|
+
* `<中心>/v1/proxy/<slug>`(pathname 与查询串原样),剥掉厂商鉴权头,加上 `x-pc-key` 与粘性 / pin 头。
|
|
37
|
+
* 适配器本身一个字不改——这就是「厂商的每一条出站请求都走 `CreateModelInput.fetch`」那条红线的用处。
|
|
38
|
+
*/
|
|
39
|
+
export function createCenterFetch(options: CenterFetchOptions): VendorFetch {
|
|
40
|
+
const center = options.centerUrl.replace(/\/+$/, '');
|
|
41
|
+
const prefix = `${center}${PROXY_ROUTE_PREFIX}/${encodeURIComponent(options.vendor)}`;
|
|
42
|
+
const origins = new Set(options.baseUrls.map(u => new URL(u).origin));
|
|
43
|
+
const send = options.fetch ?? globalThis.fetch;
|
|
44
|
+
|
|
45
|
+
return async (input, init) => {
|
|
46
|
+
const request = input instanceof Request ? input : null;
|
|
47
|
+
const url = new URL(request ? request.url : input.toString());
|
|
48
|
+
if (!origins.has(url.origin)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`refusing to route ${url.origin} through the provider center: not an endpoint of vendor "${options.vendor}"`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const headers = new Headers(request?.headers);
|
|
54
|
+
new Headers(init?.headers).forEach((v, k) => headers.set(k, v));
|
|
55
|
+
for (const h of VENDOR_AUTH_HEADERS) headers.delete(h);
|
|
56
|
+
url.searchParams.delete('key');
|
|
57
|
+
headers.set(PC_HEADERS.key, options.apiKey);
|
|
58
|
+
if (options.affinity) headers.set(PC_HEADERS.affinity, options.affinity);
|
|
59
|
+
if (options.pin) headers.set(PC_HEADERS.pin, options.pin);
|
|
60
|
+
|
|
61
|
+
const target = `${prefix}${url.pathname}${url.search}`;
|
|
62
|
+
const res = request
|
|
63
|
+
? await send(new Request(target, request), { ...init, headers })
|
|
64
|
+
: await send(target, { ...init, headers });
|
|
65
|
+
const ref = res.headers.get(PC_HEADERS.credential);
|
|
66
|
+
if (ref) options.onCredential?.(ref);
|
|
67
|
+
return res;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildHandle,
|
|
3
|
+
findModel,
|
|
4
|
+
type ModelHandle,
|
|
5
|
+
type ModelRef,
|
|
6
|
+
type ModelResolver,
|
|
7
|
+
type ResolveOptions,
|
|
8
|
+
} from './model-handle.js';
|
|
9
|
+
import type {
|
|
10
|
+
CatalogEndpoint,
|
|
11
|
+
CatalogModel,
|
|
12
|
+
CatalogVendor,
|
|
13
|
+
LlmCapability,
|
|
14
|
+
VendorKind,
|
|
15
|
+
} from './types.js';
|
|
16
|
+
import type { VendorFetch } from './vendors/index.js';
|
|
17
|
+
|
|
18
|
+
/** 直连模式下的一家厂商:目录条目的形状,外加厂商 key。 */
|
|
19
|
+
export interface DirectVendor {
|
|
20
|
+
slug: string;
|
|
21
|
+
/** 显示名,默认 = slug。 */
|
|
22
|
+
name?: string;
|
|
23
|
+
kind: VendorKind;
|
|
24
|
+
/** 厂商的 API key。 */
|
|
25
|
+
apiKey: string;
|
|
26
|
+
endpoints: CatalogEndpoint[];
|
|
27
|
+
/** 只写 `modelId` 与 `protocols` 也行,其余取默认(无能力、无上限、无单价)。 */
|
|
28
|
+
models: DirectModel[];
|
|
29
|
+
/** 出站 fetch(网络代理);默认全局 fetch。 */
|
|
30
|
+
fetch?: VendorFetch;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type DirectModel = Pick<CatalogModel, 'modelId' | 'protocols'> &
|
|
34
|
+
Partial<Omit<CatalogModel, 'modelId' | 'protocols'>>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 直连模式:不经中心,拿厂商 key 直接调。给本地开发、测试与没有中心的环境;与 `ProviderClient` 同一个
|
|
38
|
+
* `ModelResolver` 接口、同一套适配器,换模式不改调用方。没有 key 池、没有故障切换。
|
|
39
|
+
*/
|
|
40
|
+
export class DirectModelResolver implements ModelResolver {
|
|
41
|
+
private readonly vendors: (CatalogVendor & {
|
|
42
|
+
apiKey: string;
|
|
43
|
+
fetch?: VendorFetch;
|
|
44
|
+
})[];
|
|
45
|
+
|
|
46
|
+
constructor(vendors: readonly DirectVendor[]) {
|
|
47
|
+
this.vendors = vendors.map(v => ({
|
|
48
|
+
slug: v.slug,
|
|
49
|
+
name: v.name ?? v.slug,
|
|
50
|
+
kind: v.kind,
|
|
51
|
+
apiKey: v.apiKey,
|
|
52
|
+
fetch: v.fetch,
|
|
53
|
+
endpoints: v.endpoints,
|
|
54
|
+
models: v.models.map(m => ({
|
|
55
|
+
displayName: m.modelId,
|
|
56
|
+
capabilities: [],
|
|
57
|
+
maxContextTokens: null,
|
|
58
|
+
maxOutputTokens: null,
|
|
59
|
+
pricing: null,
|
|
60
|
+
...m,
|
|
61
|
+
})),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// async:找不到厂商 / 模型时以 rejected promise 报出,与中心模式一致。
|
|
66
|
+
|
|
67
|
+
async resolve(
|
|
68
|
+
ref: ModelRef,
|
|
69
|
+
options: ResolveOptions = {}
|
|
70
|
+
): Promise<ModelHandle> {
|
|
71
|
+
findModel(this.vendors, ref);
|
|
72
|
+
const vendor = this.vendors.find(v => v.slug === ref.vendor)!;
|
|
73
|
+
const model = vendor.models.find(m => m.modelId === ref.modelId)!;
|
|
74
|
+
return buildHandle({
|
|
75
|
+
ref,
|
|
76
|
+
vendor,
|
|
77
|
+
model,
|
|
78
|
+
apiKey: vendor.apiKey,
|
|
79
|
+
fetchFor: () => vendor.fetch,
|
|
80
|
+
reasoningEffort: options.reasoningEffort,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
capabilitiesOf(ref: ModelRef): Promise<LlmCapability[]> {
|
|
85
|
+
const model = this.vendors
|
|
86
|
+
.find(v => v.slug === ref.vendor)
|
|
87
|
+
?.models.find(m => m.modelId === ref.modelId);
|
|
88
|
+
return Promise.resolve(model ? [...model.capabilities] : []);
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CENTER_ERROR_CODES, type CenterErrorCode } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* - 中心的错误码(`CenterErrorCode`),如 `unauthorized`(key 不对或已吊销)、`vendor_not_allowed`、
|
|
5
|
+
* `no_credential`(池里没有可用的 key)、`pinned_credential_unavailable`。
|
|
6
|
+
* - `unknown_vendor` / `unknown_model`:目录里没有(或这个接入方看不到)这家 / 这个模型。
|
|
7
|
+
* - `protocol_unavailable`:模型声明的协议里,没有一个是这家有端点且适配器已实现的。
|
|
8
|
+
* - `unsupported_vendor_kind`:这个版本的 client 没有该厂商类型的适配器(中心比 client 新)。
|
|
9
|
+
* - `network`:连不上中心(`cause` 里是原始错误)。
|
|
10
|
+
* - `unexpected`:回了不认识的东西(非 JSON 的错误页、不认识的错误码……),`status` 照填。
|
|
11
|
+
*/
|
|
12
|
+
export type ProviderClientErrorCode =
|
|
13
|
+
| CenterErrorCode
|
|
14
|
+
| 'unknown_vendor'
|
|
15
|
+
| 'unknown_model'
|
|
16
|
+
| 'protocol_unavailable'
|
|
17
|
+
| 'unsupported_vendor_kind'
|
|
18
|
+
| 'network'
|
|
19
|
+
| 'unexpected';
|
|
20
|
+
|
|
21
|
+
export class ProviderClientError extends Error {
|
|
22
|
+
override readonly name: string = 'ProviderClientError';
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
readonly code: ProviderClientErrorCode,
|
|
26
|
+
message: string,
|
|
27
|
+
/** HTTP 状态码;不是 HTTP 层的错误(或没拿到响应)时为 null。 */
|
|
28
|
+
readonly status: number | null,
|
|
29
|
+
options?: { cause?: unknown }
|
|
30
|
+
) {
|
|
31
|
+
super(message, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 模型声明的协议与这家可用的端点 / 适配器没有交集。不静默退回某个协议。 */
|
|
36
|
+
export class ProtocolUnavailableError extends ProviderClientError {
|
|
37
|
+
override readonly name = 'ProtocolUnavailableError';
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
readonly modelId: string,
|
|
41
|
+
readonly protocols: readonly string[],
|
|
42
|
+
readonly vendorKind: string
|
|
43
|
+
) {
|
|
44
|
+
super(
|
|
45
|
+
'protocol_unavailable',
|
|
46
|
+
`Model "${modelId}" declares protocols [${protocols.join(', ')}] but vendor "${vendorKind}" has no usable endpoint for any of them`,
|
|
47
|
+
null
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const KNOWN = new Set<string>(CENTER_ERROR_CODES);
|
|
53
|
+
|
|
54
|
+
/** 从中心的错误响应体(`{ code, message }`)认出错误码;认不出给 null。 */
|
|
55
|
+
export function parseCenterError(
|
|
56
|
+
body: unknown
|
|
57
|
+
): { code: CenterErrorCode; message: string } | null {
|
|
58
|
+
if (!body || typeof body !== 'object') return null;
|
|
59
|
+
const { code, message } = body as { code?: unknown; message?: unknown };
|
|
60
|
+
if (typeof code !== 'string' || !KNOWN.has(code)) return null;
|
|
61
|
+
return {
|
|
62
|
+
code: code as CenterErrorCode,
|
|
63
|
+
message: typeof message === 'string' ? message : code,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 把中心的一个非 2xx 响应变成错误。会读掉响应体。 */
|
|
68
|
+
export async function errorFromResponse(
|
|
69
|
+
res: Response
|
|
70
|
+
): Promise<ProviderClientError> {
|
|
71
|
+
const text = await res.text().catch(() => '');
|
|
72
|
+
let body: unknown = null;
|
|
73
|
+
try {
|
|
74
|
+
body = JSON.parse(text);
|
|
75
|
+
} catch {
|
|
76
|
+
// 不是 JSON:落到 unexpected。
|
|
77
|
+
}
|
|
78
|
+
const known = parseCenterError(body);
|
|
79
|
+
if (known) {
|
|
80
|
+
return new ProviderClientError(known.code, known.message, res.status);
|
|
81
|
+
}
|
|
82
|
+
const snippet = text.slice(0, 200).trim();
|
|
83
|
+
return new ProviderClientError(
|
|
84
|
+
'unexpected',
|
|
85
|
+
`provider center answered ${res.status}${snippet ? `: ${snippet}` : ''}`,
|
|
86
|
+
res.status
|
|
87
|
+
);
|
|
88
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export { type CenterFetchOptions, createCenterFetch } from './center-fetch.js';
|
|
2
|
+
export {
|
|
3
|
+
type DirectModel,
|
|
4
|
+
DirectModelResolver,
|
|
5
|
+
type DirectVendor,
|
|
6
|
+
} from './direct-resolver.js';
|
|
7
|
+
export {
|
|
8
|
+
ProtocolUnavailableError,
|
|
9
|
+
ProviderClientError,
|
|
10
|
+
type ProviderClientErrorCode,
|
|
11
|
+
} from './errors.js';
|
|
12
|
+
export {
|
|
13
|
+
type ModelFileStore,
|
|
14
|
+
type ModelHandle,
|
|
15
|
+
type ModelRef,
|
|
16
|
+
type ModelResolver,
|
|
17
|
+
type ResolveOptions,
|
|
18
|
+
type UploadedFile,
|
|
19
|
+
} from './model-handle.js';
|
|
20
|
+
export {
|
|
21
|
+
ProviderClient,
|
|
22
|
+
type ProviderClientOptions,
|
|
23
|
+
} from './provider-client.js';
|
|
24
|
+
export { toProviderClientError } from './to-error.js';
|
|
25
|
+
export {
|
|
26
|
+
type CatalogEndpoint,
|
|
27
|
+
type CatalogModel,
|
|
28
|
+
type CatalogResponse,
|
|
29
|
+
type CatalogVendor,
|
|
30
|
+
CENTER_ERROR_CODES,
|
|
31
|
+
type CenterErrorCode,
|
|
32
|
+
LLM_CAPABILITIES,
|
|
33
|
+
LLM_PROTOCOLS,
|
|
34
|
+
type LlmCapability,
|
|
35
|
+
type LlmProtocol,
|
|
36
|
+
type ModelPricing,
|
|
37
|
+
PC_HEADERS,
|
|
38
|
+
PROXY_ROUTE_PREFIX,
|
|
39
|
+
REASONING_EFFORTS,
|
|
40
|
+
type ReasoningEffort,
|
|
41
|
+
VENDOR_KINDS,
|
|
42
|
+
type VendorKind,
|
|
43
|
+
} from './types.js';
|
|
44
|
+
export {
|
|
45
|
+
type AdviceModelView,
|
|
46
|
+
findVendorAdapter,
|
|
47
|
+
getVendorAdapter,
|
|
48
|
+
LlmVendorAdapter,
|
|
49
|
+
type ModelAdvice,
|
|
50
|
+
type ProviderOptions,
|
|
51
|
+
type SamplingParams,
|
|
52
|
+
VENDOR_ADAPTERS,
|
|
53
|
+
type VendorFetch,
|
|
54
|
+
} from './vendors/index.js';
|