@agent-proxy/zcode 0.11.3 → 0.12.1
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/index.d.ts +4 -6
- package/dist/index.js +99 -120
- package/dist/request.d.ts +12 -1
- package/dist/request.js +244 -234
- package/dist/resources/models.js +3260 -910
- package/dist/response.d.ts +8 -0
- package/dist/response.js +65 -20
- package/dist/wire.d.ts +2 -4
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HostServices, GatewayPlugin } from "@agent-proxy/plugin-api";
|
|
1
|
+
import type { HostServices, GatewayPlugin, ClientProtocol } from "@agent-proxy/plugin-api";
|
|
2
2
|
import type { ZcodeConfigCache, ZcodeSelection } from "./config.ts";
|
|
3
3
|
import { ZcodeEndpointRouting } from "./endpoint-routing.ts";
|
|
4
4
|
import { ZcodeClientSigning } from "./client-signing.ts";
|
|
@@ -32,15 +32,13 @@ export interface ZcodeDependencies {
|
|
|
32
32
|
export type GatewayHandler = ((request: Request) => Promise<Response>) & {
|
|
33
33
|
close(): void;
|
|
34
34
|
};
|
|
35
|
-
|
|
36
|
-
export declare function zcodeEnabled<Config extends AgentConfig>(config: Config): boolean;
|
|
37
|
-
export declare function validateZcodeConfig<Config extends AgentConfig>(config: Config): void;
|
|
35
|
+
export declare function validateZcodeConfig(config: AgentConfig): void;
|
|
38
36
|
export declare function zcodeError(status: number, message: string, type?: string): Response;
|
|
39
|
-
export declare function createZcodeAdapter
|
|
37
|
+
export declare function createZcodeAdapter(config: AgentConfig, dependencies?: ZcodeDependencies): {
|
|
40
38
|
catalog(): Promise<ModelCatalog>;
|
|
41
39
|
refreshCatalog: () => Promise<ModelCatalog>;
|
|
42
40
|
reloadCatalog(): Promise<ModelCatalog>;
|
|
43
|
-
forward(request: Request, input: Record<string, unknown>, mapResult?: (payload: Record<string, unknown>) => Response): Promise<Response>;
|
|
41
|
+
forward(request: Request, input: Record<string, unknown>, to?: ClientProtocol, mapResult?: (payload: Record<string, unknown>) => Response): Promise<Response>;
|
|
44
42
|
close(): void;
|
|
45
43
|
};
|
|
46
44
|
export declare const ZCODE_MODEL_PATTERNS: readonly string[];
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createZcodeConfigCache, readZcodeAPIKeyProviders, zcodeConfigPresent, Z
|
|
|
6
6
|
import { clearModelsCacheEntries, invalidateModelsCache, withAgentSystemPrompt } from "@agent-proxy/plugin-api";
|
|
7
7
|
import { buildZcodeVendorCatalog, createZcodeCatalog, isZcodeModel, writeZcodeServedCatalog, zcodeAPIProviderIDFromModel, zcodeModelPlan, zcodeUpstreamModel, } from "./catalog.js";
|
|
8
8
|
import { translateZcodeRequest, ZcodeRequestError } from "./request.js";
|
|
9
|
-
import {
|
|
9
|
+
import { createZcodeClientResponse } from "./response.js";
|
|
10
10
|
import { executeZcodeAnalyzeImage, matchZcodeAnalyzeImage } from "./vision.js";
|
|
11
11
|
import { ZcodeEndpointRouting } from "./endpoint-routing.js";
|
|
12
12
|
import { clientSigningVerifyRejection, ZcodeClientSigning } from "./client-signing.js";
|
|
@@ -14,37 +14,22 @@ import { isZcodeRecord } from "./wire.js";
|
|
|
14
14
|
import { buildZcodeModelHeaders, createZcodeContexts, decorateZcodeBody, readZcodeIdentity, zcodePlan, ZCODE_AGENT_SYSTEM_PROMPT } from "./request-context.js";
|
|
15
15
|
import { localTime, logExchange, logGroupFromPath, requestLogDir } from "@agent-proxy/plugin-api";
|
|
16
16
|
import { logGatewayError, logRequestSummary } from "@agent-proxy/plugin-api";
|
|
17
|
-
/** 插件启停仅由 ZCode 开关决定,default 上游路由策略不参与。 */
|
|
18
|
-
export function zcodeEnabled(config) {
|
|
19
|
-
return config.zcode === undefined || config.zcode === true;
|
|
20
|
-
}
|
|
21
17
|
export function validateZcodeConfig(config) {
|
|
22
|
-
if (config.zcode !== undefined && typeof config.zcode !== "boolean")
|
|
23
|
-
throw new Error("zcode 必须为 boolean");
|
|
24
|
-
if (!zcodeEnabled(config))
|
|
25
|
-
return;
|
|
26
18
|
const host = config.host;
|
|
27
19
|
if (!(host === "localhost" || host === "::1" || host === "[::1]" || (isIP(host) === 4 && host.startsWith("127.")))) {
|
|
28
20
|
throw new Error("启用 ZCode 时网关只能监听环回地址");
|
|
29
21
|
}
|
|
30
|
-
if (config.prefix && ("zcode/".startsWith(config.prefix) || config.prefix.startsWith("zcode/")
|
|
31
|
-
|| config.prefix.startsWith("zcode-"))) {
|
|
32
|
-
throw new Error("启用 ZCode 时 zcode/ 与 zcode- 前缀保留给 ZCode,请调整第三方 prefix");
|
|
33
|
-
}
|
|
34
22
|
}
|
|
35
23
|
export function zcodeError(status, message, type = "invalid_request_error") {
|
|
36
24
|
return Response.json({ error: { type, message } }, { status });
|
|
37
25
|
}
|
|
38
26
|
function buildZcodeAdapter(config, dependencies = {}) {
|
|
39
27
|
validateZcodeConfig(config);
|
|
40
|
-
const enabled = zcodeEnabled(config);
|
|
41
28
|
const manual = dependencies.catalogMode === "manual";
|
|
42
29
|
// 自管目录文件一律放运行主目录,不从 catalogPath 位置倒推。
|
|
43
30
|
const cacheDirectory = dependencies.cacheDirectory ?? resolvePaths().runtimeHome;
|
|
44
31
|
// 厂商元数据只取模型包预设;账号或套餐刷新仅重新投影支持集合。
|
|
45
|
-
const vendorCatalog =
|
|
46
|
-
? buildZcodeVendorCatalog()
|
|
47
|
-
: undefined;
|
|
32
|
+
const vendorCatalog = buildZcodeVendorCatalog();
|
|
48
33
|
/** 套餐或选择变化时立即撤下 Codex 缓存里的旧 ZCode 条目,避免旧套餐模型继续可选。 */
|
|
49
34
|
const dropCachedZcodeModels = () => {
|
|
50
35
|
if (dependencies.codexModelsCacheFile) {
|
|
@@ -84,8 +69,6 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
84
69
|
];
|
|
85
70
|
let synchronizeAPIKeyRoutes = () => { };
|
|
86
71
|
const republishFromPlans = (force = false) => {
|
|
87
|
-
if (!vendorCatalog)
|
|
88
|
-
return;
|
|
89
72
|
const current = planRoutes.map((route) => route.snapshot);
|
|
90
73
|
const previous = publishedSnapshots;
|
|
91
74
|
if (!force && previous && current.length === previous.length
|
|
@@ -108,121 +91,119 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
108
91
|
}
|
|
109
92
|
publishServedCatalog({ models });
|
|
110
93
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
94
|
+
// 注入模式(测试)只为显式给出的套餐建缓存,避免读到真实的 ~/.zcode。
|
|
95
|
+
const injected = dependencies.planCaches
|
|
96
|
+
?? (dependencies.apiKeyCaches ? {} : dependencies.configCache ? { "api-key": dependencies.configCache } : undefined);
|
|
97
|
+
for (const route of planRoutes) {
|
|
98
|
+
route.cache = injected?.[route.plan]
|
|
99
|
+
?? (injected ? undefined : createZcodeConfigCache(home, {
|
|
100
|
+
plan: route.plan,
|
|
101
|
+
refreshOnStart: false,
|
|
102
|
+
// 选择变化只撤下本套餐的快照并重发并集;另一个套餐的条目不受影响。
|
|
103
|
+
onSelectionChange: () => {
|
|
104
|
+
if (manual)
|
|
105
|
+
return;
|
|
106
|
+
route.snapshot = undefined;
|
|
107
|
+
republishFromPlans();
|
|
108
|
+
},
|
|
109
|
+
onSnapshotChange: (snapshot) => {
|
|
110
|
+
if (manual)
|
|
111
|
+
return;
|
|
112
|
+
route.snapshot = snapshot;
|
|
113
|
+
republishFromPlans();
|
|
114
|
+
},
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
if (injected) {
|
|
118
|
+
const apiCaches = dependencies.apiKeyCaches
|
|
119
|
+
?? (dependencies.planCaches?.["api-key"] || dependencies.configCache
|
|
120
|
+
? { current: dependencies.planCaches?.["api-key"] ?? dependencies.configCache }
|
|
121
|
+
: {});
|
|
122
|
+
for (const [apiProviderID, cache] of Object.entries(apiCaches)) {
|
|
123
|
+
planRoutes.push({ plan: "api-key", ...(apiProviderID === "current" ? {} : { apiProviderID }), cache });
|
|
134
124
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const createAPIRoute = (provider) => {
|
|
128
|
+
const route = { plan: "api-key", apiProviderID: provider.providerID };
|
|
129
|
+
route.cache = createZcodeConfigCache(home, {
|
|
130
|
+
plan: "api-key",
|
|
131
|
+
apiProviderID: provider.providerID,
|
|
132
|
+
refreshOnStart: false,
|
|
133
|
+
onSelectionChange: () => {
|
|
134
|
+
if (manual)
|
|
135
|
+
return;
|
|
136
|
+
route.snapshot = undefined;
|
|
137
|
+
republishFromPlans();
|
|
138
|
+
},
|
|
139
|
+
onSnapshotChange: (snapshot) => {
|
|
140
|
+
if (manual)
|
|
141
|
+
return;
|
|
142
|
+
route.snapshot = snapshot;
|
|
143
|
+
republishFromPlans();
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
return route;
|
|
147
|
+
};
|
|
148
|
+
const synchronize = (republish = true, strict = false) => {
|
|
149
|
+
let providers = [];
|
|
150
|
+
try {
|
|
151
|
+
providers = readZcodeAPIKeyProviders(home);
|
|
142
152
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
onSnapshotChange: (snapshot) => {
|
|
158
|
-
if (manual)
|
|
159
|
-
return;
|
|
160
|
-
route.snapshot = snapshot;
|
|
161
|
-
republishFromPlans();
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
return route;
|
|
165
|
-
};
|
|
166
|
-
const synchronize = (republish = true, strict = false) => {
|
|
167
|
-
let providers = [];
|
|
168
|
-
try {
|
|
169
|
-
providers = readZcodeAPIKeyProviders(home);
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
if (strict)
|
|
173
|
-
throw error;
|
|
174
|
-
/* 动态目录撤下无效 API Key;套餐路由不受影响。 */
|
|
175
|
-
}
|
|
176
|
-
const targetIDs = new Set(providers.map((provider) => provider.providerID));
|
|
177
|
-
let changed = false;
|
|
178
|
-
const removed = planRoutes.filter((route) => route.plan === "api-key" && route.apiProviderID && !targetIDs.has(route.apiProviderID));
|
|
179
|
-
for (const route of removed)
|
|
180
|
-
route.cache?.close();
|
|
181
|
-
if (removed.length) {
|
|
182
|
-
for (let index = planRoutes.length - 1; index >= 0; index--) {
|
|
183
|
-
if (removed.includes(planRoutes[index]))
|
|
184
|
-
planRoutes.splice(index, 1);
|
|
185
|
-
}
|
|
186
|
-
changed = true;
|
|
187
|
-
}
|
|
188
|
-
for (const provider of providers) {
|
|
189
|
-
if (planRoutes.some((route) => route.plan === "api-key" && route.apiProviderID === provider.providerID))
|
|
190
|
-
continue;
|
|
191
|
-
planRoutes.push(createAPIRoute(provider));
|
|
192
|
-
changed = true;
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (strict)
|
|
155
|
+
throw error;
|
|
156
|
+
/* 动态目录撤下无效 API Key;套餐路由不受影响。 */
|
|
157
|
+
}
|
|
158
|
+
const targetIDs = new Set(providers.map((provider) => provider.providerID));
|
|
159
|
+
let changed = false;
|
|
160
|
+
const removed = planRoutes.filter((route) => route.plan === "api-key" && route.apiProviderID && !targetIDs.has(route.apiProviderID));
|
|
161
|
+
for (const route of removed)
|
|
162
|
+
route.cache?.close();
|
|
163
|
+
if (removed.length) {
|
|
164
|
+
for (let index = planRoutes.length - 1; index >= 0; index--) {
|
|
165
|
+
if (removed.includes(planRoutes[index]))
|
|
166
|
+
planRoutes.splice(index, 1);
|
|
193
167
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
168
|
+
changed = true;
|
|
169
|
+
}
|
|
170
|
+
for (const provider of providers) {
|
|
171
|
+
if (planRoutes.some((route) => route.plan === "api-key" && route.apiProviderID === provider.providerID))
|
|
172
|
+
continue;
|
|
173
|
+
planRoutes.push(createAPIRoute(provider));
|
|
174
|
+
changed = true;
|
|
175
|
+
}
|
|
176
|
+
if (changed && republish)
|
|
177
|
+
republishFromPlans();
|
|
178
|
+
};
|
|
179
|
+
synchronizeAPIKeyRoutes = (strict) => synchronize(!manual, strict);
|
|
180
|
+
synchronize(false);
|
|
181
|
+
// /models 与未知 API 前缀请求都会重新同步,支持新增/删除 provider;现有
|
|
182
|
+
// provider 的 Key 与模型变化由各自 cache 的 provider_config watcher 驱动。
|
|
202
183
|
}
|
|
203
184
|
const fetchUpstream = dependencies.fetch ?? ((url, init) => fetch(url, init));
|
|
204
185
|
const activeRequests = new Set();
|
|
205
|
-
const identity =
|
|
206
|
-
const endpointRouting =
|
|
186
|
+
const identity = dependencies.identity ?? readZcodeIdentity();
|
|
187
|
+
const endpointRouting = dependencies.endpointRouting !== null
|
|
207
188
|
? dependencies.endpointRouting ?? new ZcodeEndpointRouting({
|
|
208
|
-
identity
|
|
189
|
+
identity,
|
|
209
190
|
fetch: (url, init) => fetchUpstream(url, init),
|
|
210
191
|
})
|
|
211
192
|
: undefined;
|
|
212
193
|
// coding plan 官方客户端签名:逐请求补 X-Client-* 签名头;fail-open,失败按未签名继续。
|
|
213
|
-
const clientSigning =
|
|
194
|
+
const clientSigning = dependencies.clientSigning !== null
|
|
214
195
|
? dependencies.clientSigning ?? new ZcodeClientSigning({ fetch: (url, init) => fetchUpstream(url, init) })
|
|
215
196
|
: undefined;
|
|
216
197
|
const contexts = createZcodeContexts();
|
|
217
198
|
const sink = config.requestLogging === true ? {
|
|
218
|
-
dir: requestLogDir(
|
|
199
|
+
dir: requestLogDir(),
|
|
219
200
|
maxLogs: Math.max(0, Math.trunc(config.maxRequestLogs ?? 0)),
|
|
220
201
|
processLog: dependencies.processLog,
|
|
221
202
|
} : undefined;
|
|
222
203
|
let closed = false;
|
|
223
204
|
let refreshPromise;
|
|
224
205
|
const readCatalog = async (mode) => {
|
|
225
|
-
if (closed
|
|
206
|
+
if (closed)
|
|
226
207
|
return { models: [] };
|
|
227
208
|
synchronizeAPIKeyRoutes(manual && mode !== "get");
|
|
228
209
|
const neverPublished = publishedSnapshots === undefined;
|
|
@@ -279,7 +260,7 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
279
260
|
return promise;
|
|
280
261
|
};
|
|
281
262
|
/** 启动首读仍允许凭据授权,手动策略仅停止后续目录自动发布。 */
|
|
282
|
-
const startup =
|
|
263
|
+
const startup = dependencies.refreshCatalogOnStart !== false
|
|
283
264
|
? refreshCatalog() : Promise.resolve({ models: [] });
|
|
284
265
|
// 构造适配器与等待 modelsReady 之间允许异步间隙;失败由公开目录接口传递。
|
|
285
266
|
void startup.catch(() => { });
|
|
@@ -297,7 +278,7 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
297
278
|
await refreshPromise;
|
|
298
279
|
return readCatalog("reload");
|
|
299
280
|
},
|
|
300
|
-
async forward(request, input, mapResult) {
|
|
281
|
+
async forward(request, input, to = "responses", mapResult) {
|
|
301
282
|
const start = Date.now();
|
|
302
283
|
const requestTime = localTime();
|
|
303
284
|
const incoming = new URL(request.url);
|
|
@@ -426,7 +407,7 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
426
407
|
cleanup();
|
|
427
408
|
return fail(404, "此模型不属于 ZCode 当前套餐或厂商目录");
|
|
428
409
|
}
|
|
429
|
-
const translated = translateZcodeRequest(input, model);
|
|
410
|
+
const translated = await translateZcodeRequest(input, model, to);
|
|
430
411
|
const baseUpstreamUrl = `${snapshot.baseURL}${snapshot.baseURL.endsWith("/v1") ? "/messages" : "/v1/messages"}`;
|
|
431
412
|
const routedUrl = endpointRouting
|
|
432
413
|
// 跟随 z.ai 服务端下发的端点映射(如 zcode.z.ai ultra 中转);resolve 内部 fail-open,失败保持原 URL。
|
|
@@ -562,7 +543,7 @@ function buildZcodeAdapter(config, dependencies = {}) {
|
|
|
562
543
|
}
|
|
563
544
|
let chunks = "";
|
|
564
545
|
const stream = !mapResult && input.stream === true;
|
|
565
|
-
const response = await
|
|
546
|
+
const response = await createZcodeClientResponse(to, upstream, {
|
|
566
547
|
model: String(input.model), tools: translated.tools, stream, signal: abort.signal,
|
|
567
548
|
gatewayTools,
|
|
568
549
|
sanitizeError: (error) => ({
|
|
@@ -637,9 +618,7 @@ export function createZcodePlugin(host, dependencies = {}) {
|
|
|
637
618
|
const input = await context.body.json();
|
|
638
619
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
639
620
|
return Response.json({ error: { type: "invalid_request_error", message: "请求正文必须是 JSON 对象" } }, { status: 400 });
|
|
640
|
-
|
|
641
|
-
return Response.json({ error: { type: "invalid_request_error", message: "Zcode 模型仅支持 Responses API" } }, { status: 400 });
|
|
642
|
-
return adapter.forward(context.request, input);
|
|
621
|
+
return adapter.forward(context.request, input, context.to);
|
|
643
622
|
},
|
|
644
623
|
getCatalog: () => adapter.catalog(),
|
|
645
624
|
refreshCatalog: () => adapter.refreshCatalog(),
|
package/dist/request.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
import type { ClientProtocol } from "@agent-proxy/plugin-api";
|
|
1
2
|
import type { ZcodeTranslatedRequest } from "./wire.ts";
|
|
3
|
+
/**
|
|
4
|
+
* ZCode 请求准备:标准消息/工具/参数统一经 @agent-proxy/llm-bridge 的
|
|
5
|
+
* convertRequest(to, "anthropic");z.ai 私有形状(web_search_20250305 服务端工具、
|
|
6
|
+
* thinking 预算表、zcode-thinking 信封回放、analyze_image 代执行)作为插件前后处理。
|
|
7
|
+
*
|
|
8
|
+
* Responses 源的私有 history 项(reasoning / web_search_call / 其它服务器调用)不能
|
|
9
|
+
* 进标准转换:按项把 input 切成「标准段 + 私有项」,标准段逐段调用公共入口,私有段
|
|
10
|
+
* 由本模块转换,最后按 main 的同角色合并语义拼装。
|
|
11
|
+
*/
|
|
2
12
|
export declare class ZcodeRequestError extends Error {
|
|
3
13
|
constructor(message: string);
|
|
4
14
|
}
|
|
5
|
-
|
|
15
|
+
/** to 为路由端点(客户端入口)协议:请求方向 to → anthropic,响应方向 anthropic → to。 */
|
|
16
|
+
export declare function translateZcodeRequest(body: Record<string, unknown>, upstreamModel: string, to?: ClientProtocol): Promise<ZcodeTranslatedRequest>;
|