@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,29 @@
|
|
|
1
|
+
import type { VendorFetch } from './vendors/index.js';
|
|
2
|
+
export interface CenterFetchOptions {
|
|
3
|
+
/** 中心的根地址,如 `https://provider.example.com`。 */
|
|
4
|
+
centerUrl: string;
|
|
5
|
+
/** 接入方 API key(`pc_…`)。 */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** 厂商 slug(透传路由段)。 */
|
|
8
|
+
vendor: string;
|
|
9
|
+
/**
|
|
10
|
+
* 这家所有端点的 baseUrl。只改写落在这些 origin 上的请求;别的 origin 直接报错——那是适配器拼错了地址,
|
|
11
|
+
* 悄悄发出去就是带着中心的 key 打到别处。
|
|
12
|
+
*/
|
|
13
|
+
baseUrls: readonly string[];
|
|
14
|
+
/** 会话粘性键(`x-pc-affinity`)。 */
|
|
15
|
+
affinity?: string;
|
|
16
|
+
/** 钉住某把 key(`x-pc-pin`)。 */
|
|
17
|
+
pin?: string;
|
|
18
|
+
/** 每次响应回来时告诉调用方实际用的是哪把 key(`x-pc-credential`)。 */
|
|
19
|
+
onCredential?: (ref: string) => void;
|
|
20
|
+
/** 发往中心用的 fetch;默认全局 fetch。 */
|
|
21
|
+
fetch?: VendorFetch;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 中心模式的出站 fetch,交给适配器的 `CreateModelInput.fetch`:把厂商 URL 的 origin 换成
|
|
25
|
+
* `<中心>/v1/proxy/<slug>`(pathname 与查询串原样),剥掉厂商鉴权头,加上 `x-pc-key` 与粘性 / pin 头。
|
|
26
|
+
* 适配器本身一个字不改——这就是「厂商的每一条出站请求都走 `CreateModelInput.fetch`」那条红线的用处。
|
|
27
|
+
*/
|
|
28
|
+
export declare function createCenterFetch(options: CenterFetchOptions): VendorFetch;
|
|
29
|
+
//# sourceMappingURL=center-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"center-fetch.d.ts","sourceRoot":"","sources":["../src/center-fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAUtD,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,+BAA+B;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA8B1E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PC_HEADERS, PROXY_ROUTE_PREFIX } from './types.js';
|
|
2
|
+
/** 厂商 SDK 可能放厂商 key 的头。中心反正会剥,这里先剥掉:假 key 不出本进程。 */
|
|
3
|
+
const VENDOR_AUTH_HEADERS = [
|
|
4
|
+
'authorization',
|
|
5
|
+
'x-goog-api-key',
|
|
6
|
+
'x-api-key',
|
|
7
|
+
'api-key',
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* 中心模式的出站 fetch,交给适配器的 `CreateModelInput.fetch`:把厂商 URL 的 origin 换成
|
|
11
|
+
* `<中心>/v1/proxy/<slug>`(pathname 与查询串原样),剥掉厂商鉴权头,加上 `x-pc-key` 与粘性 / pin 头。
|
|
12
|
+
* 适配器本身一个字不改——这就是「厂商的每一条出站请求都走 `CreateModelInput.fetch`」那条红线的用处。
|
|
13
|
+
*/
|
|
14
|
+
export function createCenterFetch(options) {
|
|
15
|
+
const center = options.centerUrl.replace(/\/+$/, '');
|
|
16
|
+
const prefix = `${center}${PROXY_ROUTE_PREFIX}/${encodeURIComponent(options.vendor)}`;
|
|
17
|
+
const origins = new Set(options.baseUrls.map(u => new URL(u).origin));
|
|
18
|
+
const send = options.fetch ?? globalThis.fetch;
|
|
19
|
+
return async (input, init) => {
|
|
20
|
+
const request = input instanceof Request ? input : null;
|
|
21
|
+
const url = new URL(request ? request.url : input.toString());
|
|
22
|
+
if (!origins.has(url.origin)) {
|
|
23
|
+
throw new Error(`refusing to route ${url.origin} through the provider center: not an endpoint of vendor "${options.vendor}"`);
|
|
24
|
+
}
|
|
25
|
+
const headers = new Headers(request?.headers);
|
|
26
|
+
new Headers(init?.headers).forEach((v, k) => headers.set(k, v));
|
|
27
|
+
for (const h of VENDOR_AUTH_HEADERS)
|
|
28
|
+
headers.delete(h);
|
|
29
|
+
url.searchParams.delete('key');
|
|
30
|
+
headers.set(PC_HEADERS.key, options.apiKey);
|
|
31
|
+
if (options.affinity)
|
|
32
|
+
headers.set(PC_HEADERS.affinity, options.affinity);
|
|
33
|
+
if (options.pin)
|
|
34
|
+
headers.set(PC_HEADERS.pin, options.pin);
|
|
35
|
+
const target = `${prefix}${url.pathname}${url.search}`;
|
|
36
|
+
const res = request
|
|
37
|
+
? await send(new Request(target, request), { ...init, headers })
|
|
38
|
+
: await send(target, { ...init, headers });
|
|
39
|
+
const ref = res.headers.get(PC_HEADERS.credential);
|
|
40
|
+
if (ref)
|
|
41
|
+
options.onCredential?.(ref);
|
|
42
|
+
return res;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=center-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"center-fetch.js","sourceRoot":"","sources":["../src/center-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ModelHandle, type ModelRef, type ModelResolver, type ResolveOptions } from './model-handle.js';
|
|
2
|
+
import type { CatalogEndpoint, CatalogModel, LlmCapability, VendorKind } from './types.js';
|
|
3
|
+
import type { VendorFetch } from './vendors/index.js';
|
|
4
|
+
/** 直连模式下的一家厂商:目录条目的形状,外加厂商 key。 */
|
|
5
|
+
export interface DirectVendor {
|
|
6
|
+
slug: string;
|
|
7
|
+
/** 显示名,默认 = slug。 */
|
|
8
|
+
name?: string;
|
|
9
|
+
kind: VendorKind;
|
|
10
|
+
/** 厂商的 API key。 */
|
|
11
|
+
apiKey: string;
|
|
12
|
+
endpoints: CatalogEndpoint[];
|
|
13
|
+
/** 只写 `modelId` 与 `protocols` 也行,其余取默认(无能力、无上限、无单价)。 */
|
|
14
|
+
models: DirectModel[];
|
|
15
|
+
/** 出站 fetch(网络代理);默认全局 fetch。 */
|
|
16
|
+
fetch?: VendorFetch;
|
|
17
|
+
}
|
|
18
|
+
export type DirectModel = Pick<CatalogModel, 'modelId' | 'protocols'> & Partial<Omit<CatalogModel, 'modelId' | 'protocols'>>;
|
|
19
|
+
/**
|
|
20
|
+
* 直连模式:不经中心,拿厂商 key 直接调。给本地开发、测试与没有中心的环境;与 `ProviderClient` 同一个
|
|
21
|
+
* `ModelResolver` 接口、同一套适配器,换模式不改调用方。没有 key 池、没有故障切换。
|
|
22
|
+
*/
|
|
23
|
+
export declare class DirectModelResolver implements ModelResolver {
|
|
24
|
+
private readonly vendors;
|
|
25
|
+
constructor(vendors: readonly DirectVendor[]);
|
|
26
|
+
resolve(ref: ModelRef, options?: ResolveOptions): Promise<ModelHandle>;
|
|
27
|
+
capabilitiesOf(ref: ModelRef): Promise<LlmCapability[]>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=direct-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct-resolver.d.ts","sourceRoot":"","sources":["../src/direct-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAEZ,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,wDAAwD;IACxD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iCAAiC;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC,GACnE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC;AAEvD;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAGnB;gBAEO,OAAO,EAAE,SAAS,YAAY,EAAE;IAqBtC,OAAO,CACX,GAAG,EAAE,QAAQ,EACb,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC;IAcvB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAMxD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { buildHandle, findModel, } from './model-handle.js';
|
|
2
|
+
/**
|
|
3
|
+
* 直连模式:不经中心,拿厂商 key 直接调。给本地开发、测试与没有中心的环境;与 `ProviderClient` 同一个
|
|
4
|
+
* `ModelResolver` 接口、同一套适配器,换模式不改调用方。没有 key 池、没有故障切换。
|
|
5
|
+
*/
|
|
6
|
+
export class DirectModelResolver {
|
|
7
|
+
vendors;
|
|
8
|
+
constructor(vendors) {
|
|
9
|
+
this.vendors = vendors.map(v => ({
|
|
10
|
+
slug: v.slug,
|
|
11
|
+
name: v.name ?? v.slug,
|
|
12
|
+
kind: v.kind,
|
|
13
|
+
apiKey: v.apiKey,
|
|
14
|
+
fetch: v.fetch,
|
|
15
|
+
endpoints: v.endpoints,
|
|
16
|
+
models: v.models.map(m => ({
|
|
17
|
+
displayName: m.modelId,
|
|
18
|
+
capabilities: [],
|
|
19
|
+
maxContextTokens: null,
|
|
20
|
+
maxOutputTokens: null,
|
|
21
|
+
pricing: null,
|
|
22
|
+
...m,
|
|
23
|
+
})),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
// async:找不到厂商 / 模型时以 rejected promise 报出,与中心模式一致。
|
|
27
|
+
async resolve(ref, options = {}) {
|
|
28
|
+
findModel(this.vendors, ref);
|
|
29
|
+
const vendor = this.vendors.find(v => v.slug === ref.vendor);
|
|
30
|
+
const model = vendor.models.find(m => m.modelId === ref.modelId);
|
|
31
|
+
return buildHandle({
|
|
32
|
+
ref,
|
|
33
|
+
vendor,
|
|
34
|
+
model,
|
|
35
|
+
apiKey: vendor.apiKey,
|
|
36
|
+
fetchFor: () => vendor.fetch,
|
|
37
|
+
reasoningEffort: options.reasoningEffort,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
capabilitiesOf(ref) {
|
|
41
|
+
const model = this.vendors
|
|
42
|
+
.find(v => v.slug === ref.vendor)
|
|
43
|
+
?.models.find(m => m.modelId === ref.modelId);
|
|
44
|
+
return Promise.resolve(model ? [...model.capabilities] : []);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=direct-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct-resolver.js","sourceRoot":"","sources":["../src/direct-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,SAAS,GAKV,MAAM,mBAAmB,CAAC;AA4B3B;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACb,OAAO,CAGnB;IAEL,YAAY,OAAgC;QAC1C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI;YACtB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACzB,WAAW,EAAE,CAAC,CAAC,OAAO;gBACtB,YAAY,EAAE,EAAE;gBAChB,gBAAgB,EAAE,IAAI;gBACtB,eAAe,EAAE,IAAI;gBACrB,OAAO,EAAE,IAAI;gBACb,GAAG,CAAC;aACL,CAAC,CAAC;SACJ,CAAC,CAAC,CAAC;IACN,CAAC;IAED,kDAAkD;IAElD,KAAK,CAAC,OAAO,CACX,GAAa,EACb,UAA0B,EAAE;QAE5B,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,CAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,CAAE,CAAC;QAClE,OAAO,WAAW,CAAC;YACjB,GAAG;YACH,MAAM;YACN,KAAK;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK;YAC5B,eAAe,EAAE,OAAO,CAAC,eAAe;SACzC,CAAC,CAAC;IACL,CAAC;IAED,cAAc,CAAC,GAAa;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO;aACvB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,CAAC;YACjC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;QAChD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;CACF"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type CenterErrorCode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* - 中心的错误码(`CenterErrorCode`),如 `unauthorized`(key 不对或已吊销)、`vendor_not_allowed`、
|
|
4
|
+
* `no_credential`(池里没有可用的 key)、`pinned_credential_unavailable`。
|
|
5
|
+
* - `unknown_vendor` / `unknown_model`:目录里没有(或这个接入方看不到)这家 / 这个模型。
|
|
6
|
+
* - `protocol_unavailable`:模型声明的协议里,没有一个是这家有端点且适配器已实现的。
|
|
7
|
+
* - `unsupported_vendor_kind`:这个版本的 client 没有该厂商类型的适配器(中心比 client 新)。
|
|
8
|
+
* - `network`:连不上中心(`cause` 里是原始错误)。
|
|
9
|
+
* - `unexpected`:回了不认识的东西(非 JSON 的错误页、不认识的错误码……),`status` 照填。
|
|
10
|
+
*/
|
|
11
|
+
export type ProviderClientErrorCode = CenterErrorCode | 'unknown_vendor' | 'unknown_model' | 'protocol_unavailable' | 'unsupported_vendor_kind' | 'network' | 'unexpected';
|
|
12
|
+
export declare class ProviderClientError extends Error {
|
|
13
|
+
readonly code: ProviderClientErrorCode;
|
|
14
|
+
/** HTTP 状态码;不是 HTTP 层的错误(或没拿到响应)时为 null。 */
|
|
15
|
+
readonly status: number | null;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
constructor(code: ProviderClientErrorCode, message: string,
|
|
18
|
+
/** HTTP 状态码;不是 HTTP 层的错误(或没拿到响应)时为 null。 */
|
|
19
|
+
status: number | null, options?: {
|
|
20
|
+
cause?: unknown;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** 模型声明的协议与这家可用的端点 / 适配器没有交集。不静默退回某个协议。 */
|
|
24
|
+
export declare class ProtocolUnavailableError extends ProviderClientError {
|
|
25
|
+
readonly modelId: string;
|
|
26
|
+
readonly protocols: readonly string[];
|
|
27
|
+
readonly vendorKind: string;
|
|
28
|
+
readonly name = "ProtocolUnavailableError";
|
|
29
|
+
constructor(modelId: string, protocols: readonly string[], vendorKind: string);
|
|
30
|
+
}
|
|
31
|
+
/** 从中心的错误响应体(`{ code, message }`)认出错误码;认不出给 null。 */
|
|
32
|
+
export declare function parseCenterError(body: unknown): {
|
|
33
|
+
code: CenterErrorCode;
|
|
34
|
+
message: string;
|
|
35
|
+
} | null;
|
|
36
|
+
/** 把中心的一个非 2xx 响应变成错误。会读掉响应体。 */
|
|
37
|
+
export declare function errorFromResponse(res: Response): Promise<ProviderClientError>;
|
|
38
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEtE;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,sBAAsB,GACtB,yBAAyB,GACzB,SAAS,GACT,YAAY,CAAC;AAEjB,qBAAa,mBAAoB,SAAQ,KAAK;IAI1C,QAAQ,CAAC,IAAI,EAAE,uBAAuB;IAEtC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IANhC,SAAkB,IAAI,EAAE,MAAM,CAAyB;gBAG5C,IAAI,EAAE,uBAAuB,EACtC,OAAO,EAAE,MAAM;IACf,4CAA4C;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,EAC9B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC;AAED,2CAA2C;AAC3C,qBAAa,wBAAyB,SAAQ,mBAAmB;IAI7D,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM;IAL7B,SAAkB,IAAI,8BAA8B;gBAGzC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,UAAU,EAAE,MAAM;CAQ9B;AAID,qDAAqD;AACrD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,GACZ;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQnD;AAED,iCAAiC;AACjC,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,mBAAmB,CAAC,CAkB9B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CENTER_ERROR_CODES } from './types.js';
|
|
2
|
+
export class ProviderClientError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
name = 'ProviderClientError';
|
|
6
|
+
constructor(code, message,
|
|
7
|
+
/** HTTP 状态码;不是 HTTP 层的错误(或没拿到响应)时为 null。 */
|
|
8
|
+
status, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** 模型声明的协议与这家可用的端点 / 适配器没有交集。不静默退回某个协议。 */
|
|
15
|
+
export class ProtocolUnavailableError extends ProviderClientError {
|
|
16
|
+
modelId;
|
|
17
|
+
protocols;
|
|
18
|
+
vendorKind;
|
|
19
|
+
name = 'ProtocolUnavailableError';
|
|
20
|
+
constructor(modelId, protocols, vendorKind) {
|
|
21
|
+
super('protocol_unavailable', `Model "${modelId}" declares protocols [${protocols.join(', ')}] but vendor "${vendorKind}" has no usable endpoint for any of them`, null);
|
|
22
|
+
this.modelId = modelId;
|
|
23
|
+
this.protocols = protocols;
|
|
24
|
+
this.vendorKind = vendorKind;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const KNOWN = new Set(CENTER_ERROR_CODES);
|
|
28
|
+
/** 从中心的错误响应体(`{ code, message }`)认出错误码;认不出给 null。 */
|
|
29
|
+
export function parseCenterError(body) {
|
|
30
|
+
if (!body || typeof body !== 'object')
|
|
31
|
+
return null;
|
|
32
|
+
const { code, message } = body;
|
|
33
|
+
if (typeof code !== 'string' || !KNOWN.has(code))
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
code: code,
|
|
37
|
+
message: typeof message === 'string' ? message : code,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** 把中心的一个非 2xx 响应变成错误。会读掉响应体。 */
|
|
41
|
+
export async function errorFromResponse(res) {
|
|
42
|
+
const text = await res.text().catch(() => '');
|
|
43
|
+
let body = null;
|
|
44
|
+
try {
|
|
45
|
+
body = JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// 不是 JSON:落到 unexpected。
|
|
49
|
+
}
|
|
50
|
+
const known = parseCenterError(body);
|
|
51
|
+
if (known) {
|
|
52
|
+
return new ProviderClientError(known.code, known.message, res.status);
|
|
53
|
+
}
|
|
54
|
+
const snippet = text.slice(0, 200).trim();
|
|
55
|
+
return new ProviderClientError('unexpected', `provider center answered ${res.status}${snippet ? `: ${snippet}` : ''}`, res.status);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAwB,MAAM,YAAY,CAAC;AAoBtE,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAIjC;IAGA;IANO,IAAI,GAAW,qBAAqB,CAAC;IAEvD,YACW,IAA6B,EACtC,OAAe;IACf,4CAA4C;IACnC,MAAqB,EAC9B,OAA6B;QAE7B,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QANf,SAAI,GAAJ,IAAI,CAAyB;QAG7B,WAAM,GAAN,MAAM,CAAe;IAIhC,CAAC;CACF;AAED,2CAA2C;AAC3C,MAAM,OAAO,wBAAyB,SAAQ,mBAAmB;IAIpD;IACA;IACA;IALO,IAAI,GAAG,0BAA0B,CAAC;IAEpD,YACW,OAAe,EACf,SAA4B,EAC5B,UAAkB;QAE3B,KAAK,CACH,sBAAsB,EACtB,UAAU,OAAO,yBAAyB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,UAAU,0CAA0C,EACnI,IAAI,CACL,CAAC;QARO,YAAO,GAAP,OAAO,CAAQ;QACf,cAAS,GAAT,SAAS,CAAmB;QAC5B,eAAU,GAAV,UAAU,CAAQ;IAO7B,CAAC;CACF;AAED,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,kBAAkB,CAAC,CAAC;AAElD,qDAAqD;AACrD,MAAM,UAAU,gBAAgB,CAC9B,IAAa;IAEb,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAA6C,CAAC;IACxE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO;QACL,IAAI,EAAE,IAAuB;QAC7B,OAAO,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KACtD,CAAC;AACJ,CAAC;AAED,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAa;IAEb,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAY,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,yBAAyB;IAC3B,CAAC;IACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,IAAI,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,OAAO,IAAI,mBAAmB,CAC5B,YAAY,EACZ,4BAA4B,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACxE,GAAG,CAAC,MAAM,CACX,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type CenterFetchOptions, createCenterFetch } from './center-fetch.js';
|
|
2
|
+
export { type DirectModel, DirectModelResolver, type DirectVendor, } from './direct-resolver.js';
|
|
3
|
+
export { ProtocolUnavailableError, ProviderClientError, type ProviderClientErrorCode, } from './errors.js';
|
|
4
|
+
export { type ModelFileStore, type ModelHandle, type ModelRef, type ModelResolver, type ResolveOptions, type UploadedFile, } from './model-handle.js';
|
|
5
|
+
export { ProviderClient, type ProviderClientOptions, } from './provider-client.js';
|
|
6
|
+
export { toProviderClientError } from './to-error.js';
|
|
7
|
+
export { type CatalogEndpoint, type CatalogModel, type CatalogResponse, type CatalogVendor, CENTER_ERROR_CODES, type CenterErrorCode, LLM_CAPABILITIES, LLM_PROTOCOLS, type LlmCapability, type LlmProtocol, type ModelPricing, PC_HEADERS, PROXY_ROUTE_PREFIX, REASONING_EFFORTS, type ReasoningEffort, VENDOR_KINDS, type VendorKind, } from './types.js';
|
|
8
|
+
export { type AdviceModelView, findVendorAdapter, getVendorAdapter, LlmVendorAdapter, type ModelAdvice, type ProviderOptions, type SamplingParams, VENDOR_ADAPTERS, type VendorFetch, } from './vendors/index.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EACL,KAAK,WAAW,EAChB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,eAAe,EACpB,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,eAAe,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,eAAe,EACf,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createCenterFetch } from './center-fetch.js';
|
|
2
|
+
export { DirectModelResolver, } from './direct-resolver.js';
|
|
3
|
+
export { ProtocolUnavailableError, ProviderClientError, } from './errors.js';
|
|
4
|
+
export { ProviderClient, } from './provider-client.js';
|
|
5
|
+
export { toProviderClientError } from './to-error.js';
|
|
6
|
+
export { CENTER_ERROR_CODES, LLM_CAPABILITIES, LLM_PROTOCOLS, PC_HEADERS, PROXY_ROUTE_PREFIX, REASONING_EFFORTS, VENDOR_KINDS, } from './types.js';
|
|
7
|
+
export { findVendorAdapter, getVendorAdapter, LlmVendorAdapter, VENDOR_ADAPTERS, } from './vendors/index.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAEL,mBAAmB,GAEpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AASrB,OAAO,EACL,cAAc,GAEf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAKL,kBAAkB,EAElB,gBAAgB,EAChB,aAAa,EAIb,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EAEjB,YAAY,GAEb,MAAM,YAAY,CAAC;AACpB,OAAO,EAEL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAIhB,eAAe,GAEhB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
|
+
import { type CatalogModel, type CatalogVendor, type LlmCapability, type LlmProtocol, type ModelPricing } from './types.js';
|
|
3
|
+
import { type LlmVendorAdapter, type ProviderOptions, type SamplingParams, type VendorFetch, type VendorFileStore } from './vendors/index.js';
|
|
4
|
+
/** 指向一个模型:厂商 slug + 厂商侧的模型名。 */
|
|
5
|
+
export interface ModelRef {
|
|
6
|
+
vendor: string;
|
|
7
|
+
modelId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolveOptions {
|
|
10
|
+
/**
|
|
11
|
+
* 推理档位(`REASONING_EFFORTS`)。省略或不认识的值 = **什么都不发**(不是关闭);非推理模型也什么都不发
|
|
12
|
+
* ——给不会推理的模型发关闭指令有 400 风险。是 `string` 不是 `ReasoningEffort`:调用方的配置常是裸字符串,
|
|
13
|
+
* 不认识时不替它猜档位。
|
|
14
|
+
*/
|
|
15
|
+
reasoningEffort?: string;
|
|
16
|
+
/** 会话 id。中心据此把同一会话固定在同一把 key 上(前缀缓存、厂商侧文件与状态都绑账号)。直连模式忽略。 */
|
|
17
|
+
affinity?: string;
|
|
18
|
+
/**
|
|
19
|
+
* 钉住某把 key(上传文件时 `fileStore.upload` 回的 `credentialRef`)。那把 key 不可用时请求报
|
|
20
|
+
* `pinned_credential_unavailable`,不切换——调用方重新上传后不带 pin 重试。直连模式忽略。
|
|
21
|
+
*/
|
|
22
|
+
pin?: string;
|
|
23
|
+
}
|
|
24
|
+
/** 上传结果;`credentialRef` 是中心模式下文件落在哪把 key 的账号上(直连为 null),之后引用它的请求要 pin 这把。 */
|
|
25
|
+
export interface UploadedFile {
|
|
26
|
+
fileId: string;
|
|
27
|
+
expiresAt: Date | null;
|
|
28
|
+
credentialRef: string | null;
|
|
29
|
+
}
|
|
30
|
+
export interface ModelFileStore {
|
|
31
|
+
/** `FilePart.data.reference` 的命名空间 key(跟承载包走,不跟厂商走)。 */
|
|
32
|
+
referenceKey: string;
|
|
33
|
+
upload(input: Parameters<VendorFileStore['upload']>[0]): Promise<UploadedFile>;
|
|
34
|
+
delete(fileId: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/** 一轮要的全部模型材料,一次解析出来、同源。 */
|
|
37
|
+
export interface ModelHandle {
|
|
38
|
+
ref: ModelRef;
|
|
39
|
+
/** `厂商名 / 模型显示名`。 */
|
|
40
|
+
label: string;
|
|
41
|
+
model: LanguageModel;
|
|
42
|
+
/** 实际选中的线协议(按模型声明的顺序取第一个「有端点且适配器已实现」的)。 */
|
|
43
|
+
protocol: LlmProtocol;
|
|
44
|
+
/** 推理档位已翻译成这家的参数;可以是 undefined(见 `ResolveOptions.reasoningEffort`)。 */
|
|
45
|
+
providerOptions: ProviderOptions | undefined;
|
|
46
|
+
/** 调用方给的档位,原样。 */
|
|
47
|
+
reasoningEffort: string | undefined;
|
|
48
|
+
limits: {
|
|
49
|
+
maxContextTokens: number | null;
|
|
50
|
+
maxOutputTokens: number | null;
|
|
51
|
+
};
|
|
52
|
+
capabilities: LlmCapability[];
|
|
53
|
+
/** 单价(每百万 token)。给接入方自己算用量。 */
|
|
54
|
+
pricing: ModelPricing | null;
|
|
55
|
+
/**
|
|
56
|
+
* 工具调用 `providerMetadata` 里要随 part 落库、重放时放回 `providerOptions` 的命名空间(google 的思考签名)。
|
|
57
|
+
* 别家为空:它们的 metadata 原样回放会改变请求形状。
|
|
58
|
+
*/
|
|
59
|
+
replayedMetadataKeys: readonly string[];
|
|
60
|
+
/** 厂商 Files API;没有为 null(调用方退到公网 URL / base64)。 */
|
|
61
|
+
fileStore: ModelFileStore | null;
|
|
62
|
+
/** 剥掉这家不接受的采样参数(Kimi 禁 temperature / top_p,AI SDK 不替我们挡)。不抛,调用方按 `dropped` 记 warn。 */
|
|
63
|
+
filterSampling(params: SamplingParams): {
|
|
64
|
+
accepted: SamplingParams;
|
|
65
|
+
dropped: (keyof SamplingParams)[];
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface ModelResolver {
|
|
69
|
+
/** 解析出一轮要用的句柄。厂商 / 模型不在目录里、协议对不上 → `ProviderClientError`。 */
|
|
70
|
+
resolve(ref: ModelRef, options?: ResolveOptions): Promise<ModelHandle>;
|
|
71
|
+
/** 只要能力、不造句柄。模型不在目录里给空数组。 */
|
|
72
|
+
capabilitiesOf(ref: ModelRef): Promise<LlmCapability[]>;
|
|
73
|
+
}
|
|
74
|
+
export declare function findModel(vendors: readonly CatalogVendor[], ref: ModelRef): {
|
|
75
|
+
vendor: CatalogVendor;
|
|
76
|
+
model: CatalogModel;
|
|
77
|
+
};
|
|
78
|
+
export declare function adapterFor(vendor: CatalogVendor): LlmVendorAdapter;
|
|
79
|
+
/** 两种模式共用:从目录条目、凭证与出站 fetch 造句柄。 */
|
|
80
|
+
export declare function buildHandle(input: {
|
|
81
|
+
ref: ModelRef;
|
|
82
|
+
vendor: CatalogVendor;
|
|
83
|
+
model: CatalogModel;
|
|
84
|
+
apiKey: string;
|
|
85
|
+
/**
|
|
86
|
+
* 造出站 fetch。中心模式下 `onCredential` 收到这次实际用的 key;上传文件时每次单独造一个,
|
|
87
|
+
* 并发的请求互不串(文件落在哪把 key 上要拿准,之后引用它要 pin 这把)。
|
|
88
|
+
*/
|
|
89
|
+
fetchFor: (onCredential?: (ref: string) => void) => VendorFetch | undefined;
|
|
90
|
+
reasoningEffort: string | undefined;
|
|
91
|
+
}): ModelHandle;
|
|
92
|
+
//# sourceMappingURL=model-handle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-handle.d.ts","sourceRoot":"","sources":["../src/model-handle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ProviderClientError } from './errors.js';
|
|
2
|
+
import { REASONING_EFFORTS, } from './types.js';
|
|
3
|
+
import { findVendorAdapter, } from './vendors/index.js';
|
|
4
|
+
const EFFORTS = new Set(REASONING_EFFORTS);
|
|
5
|
+
export function findModel(vendors, ref) {
|
|
6
|
+
const vendor = vendors.find(v => v.slug === ref.vendor);
|
|
7
|
+
if (!vendor) {
|
|
8
|
+
throw new ProviderClientError('unknown_vendor', `vendor "${ref.vendor}" is not in the catalog`, null);
|
|
9
|
+
}
|
|
10
|
+
const model = vendor.models.find(m => m.modelId === ref.modelId);
|
|
11
|
+
if (!model) {
|
|
12
|
+
throw new ProviderClientError('unknown_model', `model "${ref.modelId}" is not in vendor "${ref.vendor}"`, null);
|
|
13
|
+
}
|
|
14
|
+
return { vendor, model };
|
|
15
|
+
}
|
|
16
|
+
export function adapterFor(vendor) {
|
|
17
|
+
const adapter = findVendorAdapter(vendor.kind);
|
|
18
|
+
if (!adapter) {
|
|
19
|
+
throw new ProviderClientError('unsupported_vendor_kind', `this provider-client has no adapter for vendor kind "${vendor.kind}" (vendor "${vendor.slug}"); upgrade the package`, null);
|
|
20
|
+
}
|
|
21
|
+
return adapter;
|
|
22
|
+
}
|
|
23
|
+
/** 两种模式共用:从目录条目、凭证与出站 fetch 造句柄。 */
|
|
24
|
+
export function buildHandle(input) {
|
|
25
|
+
const { ref, vendor, model, reasoningEffort } = input;
|
|
26
|
+
const adapter = adapterFor(vendor);
|
|
27
|
+
const { protocol, baseUrl } = adapter.chooseEndpoint(model, vendor.endpoints);
|
|
28
|
+
const createInput = {
|
|
29
|
+
protocol,
|
|
30
|
+
baseUrl,
|
|
31
|
+
apiKey: input.apiKey,
|
|
32
|
+
modelId: model.modelId,
|
|
33
|
+
fetch: input.fetchFor(),
|
|
34
|
+
};
|
|
35
|
+
const store = adapter.createFileStore(createInput);
|
|
36
|
+
return {
|
|
37
|
+
ref,
|
|
38
|
+
label: `${vendor.name} / ${model.displayName}`,
|
|
39
|
+
model: adapter.createModel(createInput),
|
|
40
|
+
protocol,
|
|
41
|
+
providerOptions: model.capabilities.includes('reasoning') &&
|
|
42
|
+
reasoningEffort !== undefined &&
|
|
43
|
+
EFFORTS.has(reasoningEffort)
|
|
44
|
+
? adapter.reasoning(model.modelId, reasoningEffort, protocol)
|
|
45
|
+
: undefined,
|
|
46
|
+
reasoningEffort,
|
|
47
|
+
limits: {
|
|
48
|
+
maxContextTokens: model.maxContextTokens,
|
|
49
|
+
maxOutputTokens: model.maxOutputTokens,
|
|
50
|
+
},
|
|
51
|
+
capabilities: [...model.capabilities],
|
|
52
|
+
pricing: model.pricing,
|
|
53
|
+
replayedMetadataKeys: adapter.replayedMetadataKeys,
|
|
54
|
+
fileStore: store && {
|
|
55
|
+
referenceKey: store.referenceKey,
|
|
56
|
+
async upload(file) {
|
|
57
|
+
let credentialRef = null;
|
|
58
|
+
const once = adapter.createFileStore({
|
|
59
|
+
...createInput,
|
|
60
|
+
fetch: input.fetchFor(ref => (credentialRef = ref)),
|
|
61
|
+
});
|
|
62
|
+
if (!once)
|
|
63
|
+
throw new Error('file store disappeared');
|
|
64
|
+
const r = await once.upload(file);
|
|
65
|
+
return { ...r, credentialRef };
|
|
66
|
+
},
|
|
67
|
+
delete: fileId => store.delete(fileId),
|
|
68
|
+
},
|
|
69
|
+
filterSampling: params => adapter.filterSampling(params),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=model-handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-handle.js","sourceRoot":"","sources":["../src/model-handle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ModelHandle, type ModelRef, type ModelResolver, type ResolveOptions } from './model-handle.js';
|
|
2
|
+
import { type CatalogResponse } from './types.js';
|
|
3
|
+
import type { VendorFetch } from './vendors/index.js';
|
|
4
|
+
export interface ProviderClientOptions {
|
|
5
|
+
/** 中心的根地址,如 `https://provider.example.com`。 */
|
|
6
|
+
centerUrl: string;
|
|
7
|
+
/** 接入方 API key(`pc_…`)。 */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* 目录在这段时间内直接用缓存、不去中心复核(毫秒,默认 60 秒)。过期后带 `If-None-Match` 复核,
|
|
11
|
+
* 没变只回 304。中心改了目录最多这么久才被看到。
|
|
12
|
+
*/
|
|
13
|
+
catalogTtlMs?: number;
|
|
14
|
+
/** 目录请求的超时(毫秒,默认 10 秒)。透传请求不受它管(流式回答可以很长)。 */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** 发往中心用的 fetch(测试替身、自定义代理);默认全局 fetch。 */
|
|
17
|
+
fetch?: VendorFetch;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 中心模式:模型目录从中心拉,所有厂商请求经中心透传(中心换上真 key、坏了自动换下一把)。
|
|
21
|
+
* 本进程从头到尾不碰厂商 key。
|
|
22
|
+
*/
|
|
23
|
+
export declare class ProviderClient implements ModelResolver {
|
|
24
|
+
private readonly options;
|
|
25
|
+
private readonly center;
|
|
26
|
+
private readonly send;
|
|
27
|
+
private readonly ttlMs;
|
|
28
|
+
private readonly timeoutMs;
|
|
29
|
+
private cached;
|
|
30
|
+
private inflight;
|
|
31
|
+
constructor(options: ProviderClientOptions);
|
|
32
|
+
/** 这个接入方能用的厂商与模型(按白名单过滤过)。 */
|
|
33
|
+
catalog(): Promise<CatalogResponse>;
|
|
34
|
+
/** 丢掉目录缓存(中心改了配置、想立刻看到时)。 */
|
|
35
|
+
invalidateCatalog(): void;
|
|
36
|
+
resolve(ref: ModelRef, options?: ResolveOptions): Promise<ModelHandle>;
|
|
37
|
+
capabilitiesOf(ref: ModelRef): Promise<("vision" | "video" | "reasoning")[]>;
|
|
38
|
+
private fetchCatalog;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=provider-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-client.d.ts","sourceRoot":"","sources":["../src/provider-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAc,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,aAAa;IAYtC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAXpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAIE;IAChB,OAAO,CAAC,QAAQ,CAAyC;gBAE5B,OAAO,EAAE,qBAAqB;IAO3D,8BAA8B;IAC9B,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAUnC,6BAA6B;IAC7B,iBAAiB,IAAI,IAAI;IAInB,OAAO,CACX,GAAG,EAAE,QAAQ,EACb,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC;IAwBjB,cAAc,CAAC,GAAG,EAAE,QAAQ;YAQpB,YAAY;CAgC3B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createCenterFetch } from './center-fetch.js';
|
|
2
|
+
import { errorFromResponse, ProviderClientError } from './errors.js';
|
|
3
|
+
import { buildHandle, findModel, } from './model-handle.js';
|
|
4
|
+
import { PC_HEADERS } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* 中心模式:模型目录从中心拉,所有厂商请求经中心透传(中心换上真 key、坏了自动换下一把)。
|
|
7
|
+
* 本进程从头到尾不碰厂商 key。
|
|
8
|
+
*/
|
|
9
|
+
export class ProviderClient {
|
|
10
|
+
options;
|
|
11
|
+
center;
|
|
12
|
+
send;
|
|
13
|
+
ttlMs;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
cached = null;
|
|
16
|
+
inflight = null;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.center = options.centerUrl.replace(/\/+$/, '');
|
|
20
|
+
this.send = options.fetch ?? globalThis.fetch;
|
|
21
|
+
this.ttlMs = options.catalogTtlMs ?? 60_000;
|
|
22
|
+
this.timeoutMs = options.timeoutMs ?? 10_000;
|
|
23
|
+
}
|
|
24
|
+
/** 这个接入方能用的厂商与模型(按白名单过滤过)。 */
|
|
25
|
+
catalog() {
|
|
26
|
+
if (this.cached && Date.now() - this.cached.checkedAt < this.ttlMs) {
|
|
27
|
+
return Promise.resolve(this.cached.body);
|
|
28
|
+
}
|
|
29
|
+
this.inflight ??= this.fetchCatalog().finally(() => {
|
|
30
|
+
this.inflight = null;
|
|
31
|
+
});
|
|
32
|
+
return this.inflight;
|
|
33
|
+
}
|
|
34
|
+
/** 丢掉目录缓存(中心改了配置、想立刻看到时)。 */
|
|
35
|
+
invalidateCatalog() {
|
|
36
|
+
this.cached = null;
|
|
37
|
+
}
|
|
38
|
+
async resolve(ref, options = {}) {
|
|
39
|
+
const { vendor, model } = findModel((await this.catalog()).vendors, ref);
|
|
40
|
+
const baseUrls = vendor.endpoints.map(e => e.baseUrl);
|
|
41
|
+
return buildHandle({
|
|
42
|
+
ref,
|
|
43
|
+
vendor,
|
|
44
|
+
model,
|
|
45
|
+
// 厂商 SDK 要求有 key 才肯造句柄;它放进的鉴权头会被中心模式 fetch 剥掉。
|
|
46
|
+
apiKey: 'via-provider-center',
|
|
47
|
+
fetchFor: onCredential => createCenterFetch({
|
|
48
|
+
centerUrl: this.center,
|
|
49
|
+
apiKey: this.options.apiKey,
|
|
50
|
+
vendor: vendor.slug,
|
|
51
|
+
baseUrls,
|
|
52
|
+
affinity: options.affinity,
|
|
53
|
+
pin: options.pin,
|
|
54
|
+
onCredential,
|
|
55
|
+
fetch: this.send,
|
|
56
|
+
}),
|
|
57
|
+
reasoningEffort: options.reasoningEffort,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async capabilitiesOf(ref) {
|
|
61
|
+
const vendor = (await this.catalog()).vendors.find(v => v.slug === ref.vendor);
|
|
62
|
+
const model = vendor?.models.find(m => m.modelId === ref.modelId);
|
|
63
|
+
return model ? [...model.capabilities] : [];
|
|
64
|
+
}
|
|
65
|
+
async fetchCatalog() {
|
|
66
|
+
const headers = {
|
|
67
|
+
[PC_HEADERS.key]: this.options.apiKey,
|
|
68
|
+
};
|
|
69
|
+
if (this.cached?.etag)
|
|
70
|
+
headers['if-none-match'] = this.cached.etag;
|
|
71
|
+
let res;
|
|
72
|
+
try {
|
|
73
|
+
res = await this.send(`${this.center}/v1/catalog`, {
|
|
74
|
+
headers,
|
|
75
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
throw new ProviderClientError('network', `provider center unreachable: ${err instanceof Error ? err.message : String(err)}`, null, { cause: err });
|
|
80
|
+
}
|
|
81
|
+
if (res.status === 304 && this.cached) {
|
|
82
|
+
this.cached.checkedAt = Date.now();
|
|
83
|
+
return this.cached.body;
|
|
84
|
+
}
|
|
85
|
+
if (!res.ok)
|
|
86
|
+
throw await errorFromResponse(res);
|
|
87
|
+
const body = (await res.json());
|
|
88
|
+
this.cached = {
|
|
89
|
+
etag: res.headers.get('etag'),
|
|
90
|
+
body,
|
|
91
|
+
checkedAt: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
return body;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=provider-client.js.map
|