@tonyclaw/agent-inspector 2.0.19 → 2.0.21
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/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-D-90PIPw.js → CompareDrawer-ztKMd-FE.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-BT8Ilsbs.js +114 -0
- package/.output/public/assets/{ReplayDialog-DzlLYoTO.js → ReplayDialog-RNhfLSsW.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-0hbyf6oa.js → RequestAnatomy-fsUI5HhH.js} +1 -1
- package/.output/public/assets/{ResponseView-CKe0tNqr.js → ResponseView-B3YTDBBX.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-enhfdeNh.js → StreamingChunkSequence-CbGqQ0mO.js} +1 -1
- package/.output/public/assets/_sessionId-C9cpyfLX.js +1 -0
- package/.output/public/assets/index-0mTTpvA2.js +1 -0
- package/.output/public/assets/index-DynnYt7S.css +1 -0
- package/.output/public/assets/{main-o9-hg8g1.js → main-lTQKpxiU.js} +2 -2
- package/.output/server/{_sessionId-F8lm6PIb.mjs → _sessionId-DsF6LFJG.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-Cf1yfwfd.mjs → CompareDrawer-DgdZcMpg.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-Bmu9867L.mjs → ProxyViewerContainer-DaFW1IVu.mjs} +99 -23
- package/.output/server/_ssr/{ReplayDialog-DxZfLBOe.mjs → ReplayDialog-BOaTX1ir.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-CbdERGZd.mjs → RequestAnatomy-C-2_dejd.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-CvLd6qxI.mjs → ResponseView-BtRVhgzy.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-B_lkRB6o.mjs → StreamingChunkSequence-DZaerowX.mjs} +2 -2
- package/.output/server/_ssr/{index-ejrSItfz.mjs → index-B3_fgwFy.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-SbCnbDc2.mjs → router-CDOnHAWy.mjs} +191 -6
- package/.output/server/{_tanstack-start-manifest_v-yx8ftqWz.mjs → _tanstack-start-manifest_v-Brrx1NEa.mjs} +1 -1
- package/.output/server/index.mjs +61 -61
- package/package.json +1 -1
- package/src/components/providers/ProviderForm.tsx +107 -16
- package/src/components/ui/scroll-area.tsx +2 -2
- package/src/lib/providerModelMetadata.ts +67 -3
- package/src/proxy/formats/index.ts +1 -0
- package/src/proxy/formats/openai/index.ts +1 -0
- package/src/proxy/formats/openai/zhipuProvider.ts +31 -0
- package/src/proxy/providerImporters.ts +47 -0
- package/src/proxy/providers.ts +55 -0
- package/src/proxy/upstream.ts +25 -0
- package/.output/public/assets/ProxyViewerContainer-CxRDWacg.js +0 -114
- package/.output/public/assets/_sessionId-CJ-SusES.js +0 -1
- package/.output/public/assets/index-BbFsmQRC.js +0 -1
- package/.output/public/assets/index-BfBgrkgU.css +0 -1
|
@@ -5,23 +5,78 @@ import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
|
|
|
5
5
|
import type { ProviderConfig, ProviderModelMetadata } from "../../lib/providerContract";
|
|
6
6
|
import { maskApiKey } from "../../lib/mask";
|
|
7
7
|
|
|
8
|
+
const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
|
|
9
|
+
const ZHIPU_CODING_PROVIDER_KEYWORDS = [
|
|
10
|
+
"zhipu coding",
|
|
11
|
+
"glm coding",
|
|
12
|
+
"bigmodel coding",
|
|
13
|
+
"z.ai coding",
|
|
14
|
+
"zai coding",
|
|
15
|
+
];
|
|
16
|
+
|
|
8
17
|
// Known provider presets - maps provider name keywords to their API URLs
|
|
9
18
|
const KNOWN_PROVIDER_PRESETS: Record<
|
|
10
19
|
string,
|
|
11
|
-
{
|
|
20
|
+
{ anthropicBaseUrl?: string; openaiBaseUrl?: string; apiDocsUrl?: string }
|
|
12
21
|
> = {
|
|
13
22
|
deepseek: {
|
|
14
|
-
|
|
15
|
-
baseUrl: "https://api.deepseek.com",
|
|
23
|
+
openaiBaseUrl: "https://api.deepseek.com",
|
|
16
24
|
},
|
|
17
25
|
minimax: {
|
|
18
|
-
|
|
19
|
-
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
26
|
+
anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
|
|
20
27
|
apiDocsUrl: "https://platform.minimaxi.com/docs/api-reference/api-overview",
|
|
21
28
|
},
|
|
29
|
+
"zhipu coding": {
|
|
30
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
31
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
32
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
33
|
+
},
|
|
34
|
+
"glm coding": {
|
|
35
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
36
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
37
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
38
|
+
},
|
|
39
|
+
"bigmodel coding": {
|
|
40
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
41
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
42
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
43
|
+
},
|
|
44
|
+
"z.ai coding": {
|
|
45
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
46
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
47
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
48
|
+
},
|
|
49
|
+
"zai coding": {
|
|
50
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
51
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
52
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
53
|
+
},
|
|
54
|
+
zhipu: {
|
|
55
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
56
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
57
|
+
},
|
|
58
|
+
zhipuai: {
|
|
59
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
60
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
61
|
+
},
|
|
62
|
+
bigmodel: {
|
|
63
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
64
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
65
|
+
},
|
|
66
|
+
"z.ai": {
|
|
67
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
68
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
69
|
+
},
|
|
70
|
+
zai: {
|
|
71
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
72
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
73
|
+
},
|
|
74
|
+
glm: {
|
|
75
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
76
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
77
|
+
},
|
|
22
78
|
alibaba: {
|
|
23
|
-
|
|
24
|
-
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode",
|
|
79
|
+
openaiBaseUrl: "https://dashscope.aliyuncs.com/compatible-mode",
|
|
25
80
|
},
|
|
26
81
|
};
|
|
27
82
|
|
|
@@ -40,6 +95,22 @@ const MINIMAX_MODELS = [
|
|
|
40
95
|
// Alibaba model options
|
|
41
96
|
const ALIBABA_MODELS = ["glm-5", "glm-5.1", "glm-5.2", "qwen3.6-plus", "qwen3.7-max"];
|
|
42
97
|
|
|
98
|
+
// ZhipuAI / Z.AI model options
|
|
99
|
+
const ZHIPU_MODELS = [
|
|
100
|
+
"glm-5.2",
|
|
101
|
+
"glm-5.1",
|
|
102
|
+
"glm-5",
|
|
103
|
+
"glm-5-turbo",
|
|
104
|
+
"glm-4.7",
|
|
105
|
+
"glm-4.7-flashx",
|
|
106
|
+
"glm-4.6",
|
|
107
|
+
"glm-4.5",
|
|
108
|
+
"glm-4.5-air",
|
|
109
|
+
"glm-4.5-x",
|
|
110
|
+
"glm-4.5-airx",
|
|
111
|
+
"glm-4.5-flash",
|
|
112
|
+
];
|
|
113
|
+
|
|
43
114
|
type ProviderFormProps = {
|
|
44
115
|
provider?: ProviderConfig;
|
|
45
116
|
onSubmit: (data: {
|
|
@@ -151,6 +222,16 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
151
222
|
const isMiniMax = name.toLowerCase().includes("minimax");
|
|
152
223
|
// Check if Alibaba is detected
|
|
153
224
|
const isAlibaba = name.toLowerCase().includes("alibaba");
|
|
225
|
+
const isZhipu = [...ZHIPU_PROVIDER_KEYWORDS, ...ZHIPU_CODING_PROVIDER_KEYWORDS].some((keyword) =>
|
|
226
|
+
name.toLowerCase().includes(keyword),
|
|
227
|
+
);
|
|
228
|
+
const suggestedModels = isMiniMax
|
|
229
|
+
? MINIMAX_MODELS
|
|
230
|
+
: isZhipu
|
|
231
|
+
? ZHIPU_MODELS
|
|
232
|
+
: isAlibaba
|
|
233
|
+
? ALIBABA_MODELS
|
|
234
|
+
: [];
|
|
154
235
|
const hasContextMetadata =
|
|
155
236
|
provider?.modelMetadata?.some((metadata) => metadata.contextWindow !== undefined) ?? false;
|
|
156
237
|
|
|
@@ -182,10 +263,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
182
263
|
const lowerName = name.toLowerCase();
|
|
183
264
|
for (const [keyword, preset] of Object.entries(KNOWN_PROVIDER_PRESETS)) {
|
|
184
265
|
if (lowerName.includes(keyword)) {
|
|
185
|
-
if (preset.
|
|
186
|
-
setAnthropicBaseUrl(preset.
|
|
187
|
-
}
|
|
188
|
-
|
|
266
|
+
if (preset.anthropicBaseUrl !== undefined && !manualAnthropicUrlOverride) {
|
|
267
|
+
setAnthropicBaseUrl(preset.anthropicBaseUrl);
|
|
268
|
+
}
|
|
269
|
+
if (preset.openaiBaseUrl !== undefined && !manualOpenaiUrlOverride) {
|
|
270
|
+
setOpenaiBaseUrl(preset.openaiBaseUrl);
|
|
189
271
|
}
|
|
190
272
|
if (preset.apiDocsUrl !== undefined && !apiDocsUrl) {
|
|
191
273
|
setApiDocsUrl(preset.apiDocsUrl);
|
|
@@ -198,6 +280,15 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
198
280
|
if (keyword === "alibaba" && models.length === 1 && models[0] === "") {
|
|
199
281
|
setModels([ALIBABA_MODELS[0] ?? ""]);
|
|
200
282
|
}
|
|
283
|
+
// For ZhipuAI, auto-select the first model if not already set
|
|
284
|
+
if (
|
|
285
|
+
(ZHIPU_PROVIDER_KEYWORDS.includes(keyword) ||
|
|
286
|
+
ZHIPU_CODING_PROVIDER_KEYWORDS.includes(keyword)) &&
|
|
287
|
+
models.length === 1 &&
|
|
288
|
+
models[0] === ""
|
|
289
|
+
) {
|
|
290
|
+
setModels([ZHIPU_MODELS[0] ?? ""]);
|
|
291
|
+
}
|
|
201
292
|
break;
|
|
202
293
|
}
|
|
203
294
|
}
|
|
@@ -396,8 +487,8 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
396
487
|
)}
|
|
397
488
|
{modelMetadataUrl.trim() === "" && (
|
|
398
489
|
<div className="rounded-md border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
|
|
399
|
-
Built-in limits cover DeepSeek v4 pro/flash
|
|
400
|
-
registry URL for private or custom model limits.
|
|
490
|
+
Built-in limits cover DeepSeek v4 pro/flash, MiniMax M3/M2.7/M2.5/M2.1, and GLM coding
|
|
491
|
+
models. Use a registry URL for private or custom model limits.
|
|
401
492
|
</div>
|
|
402
493
|
)}
|
|
403
494
|
{provider !== undefined && !hasContextMetadata && modelMetadataUrl.trim() === "" && (
|
|
@@ -472,11 +563,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
472
563
|
value={m}
|
|
473
564
|
onChange={(e) => updateModel(i, e.target.value)}
|
|
474
565
|
placeholder={
|
|
475
|
-
|
|
566
|
+
suggestedModels.length > 0 ? "Type or select a model..." : "Model name"
|
|
476
567
|
}
|
|
477
568
|
className="w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
478
569
|
/>
|
|
479
|
-
{
|
|
570
|
+
{suggestedModels.length > 0 && (
|
|
480
571
|
<>
|
|
481
572
|
<button
|
|
482
573
|
type="button"
|
|
@@ -488,7 +579,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
488
579
|
</button>
|
|
489
580
|
{openModelDropdown === i && (
|
|
490
581
|
<div className="absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto">
|
|
491
|
-
{
|
|
582
|
+
{suggestedModels.map((opt) => (
|
|
492
583
|
<button
|
|
493
584
|
key={opt}
|
|
494
585
|
type="button"
|
|
@@ -11,12 +11,12 @@ function ScrollArea({
|
|
|
11
11
|
return (
|
|
12
12
|
<ScrollAreaPrimitive.Root
|
|
13
13
|
data-slot="scroll-area"
|
|
14
|
-
className={cn("relative", className)}
|
|
14
|
+
className={cn("relative overflow-hidden", className)}
|
|
15
15
|
{...props}
|
|
16
16
|
>
|
|
17
17
|
<ScrollAreaPrimitive.Viewport
|
|
18
18
|
data-slot="scroll-area-viewport"
|
|
19
|
-
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
|
19
|
+
className="focus-visible:ring-ring/50 size-full max-h-[inherit] rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
|
20
20
|
>
|
|
21
21
|
{children}
|
|
22
22
|
</ScrollAreaPrimitive.Viewport>
|
|
@@ -47,6 +47,10 @@ export const HF_MINIMAX_M3_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M3";
|
|
|
47
47
|
export const HF_MINIMAX_M2_7_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.7";
|
|
48
48
|
export const HF_MINIMAX_M2_5_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.5";
|
|
49
49
|
export const HF_MINIMAX_M2_1_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.1";
|
|
50
|
+
export const ZHIPU_GLM_4_5_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.5";
|
|
51
|
+
export const ZHIPU_GLM_4_6_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.6";
|
|
52
|
+
export const ZHIPU_GLM_4_7_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.7";
|
|
53
|
+
export const ZHIPU_GLM_5_TURBO_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-5-turbo";
|
|
50
54
|
export const HF_GLM_5_URL = "https://huggingface.co/zai-org/GLM-5";
|
|
51
55
|
export const HF_GLM_5_1_URL = "https://huggingface.co/zai-org/GLM-5.1";
|
|
52
56
|
export const HF_GLM_5_2_URL = "https://huggingface.co/zai-org/GLM-5.2";
|
|
@@ -124,10 +128,63 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
124
128
|
],
|
|
125
129
|
},
|
|
126
130
|
{
|
|
127
|
-
name: "
|
|
128
|
-
aliases: ["glm", "zai", "zhipu", "bigmodel"],
|
|
129
|
-
baseUrls: [
|
|
131
|
+
name: "ZhipuAI",
|
|
132
|
+
aliases: ["glm", "zai", "zhipu", "zhipuai", "bigmodel"],
|
|
133
|
+
baseUrls: [
|
|
134
|
+
"https://open.bigmodel.cn/api/paas/v4",
|
|
135
|
+
"https://open.bigmodel.cn/api/coding/paas/v4",
|
|
136
|
+
"https://open.bigmodel.cn/api/anthropic",
|
|
137
|
+
],
|
|
130
138
|
models: [
|
|
139
|
+
{
|
|
140
|
+
id: "glm-4.5",
|
|
141
|
+
aliases: ["GLM-4.5"],
|
|
142
|
+
contextWindow: 131_072,
|
|
143
|
+
outputLimit: 98_304,
|
|
144
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "glm-4.5-air",
|
|
148
|
+
aliases: ["GLM-4.5-Air"],
|
|
149
|
+
contextWindow: 131_072,
|
|
150
|
+
outputLimit: 98_304,
|
|
151
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "glm-4.5-x",
|
|
155
|
+
aliases: ["GLM-4.5-X"],
|
|
156
|
+
contextWindow: 131_072,
|
|
157
|
+
outputLimit: 98_304,
|
|
158
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "glm-4.5-airx",
|
|
162
|
+
aliases: ["GLM-4.5-AirX"],
|
|
163
|
+
contextWindow: 131_072,
|
|
164
|
+
outputLimit: 98_304,
|
|
165
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "glm-4.5-flash",
|
|
169
|
+
aliases: ["GLM-4.5-Flash"],
|
|
170
|
+
contextWindow: 131_072,
|
|
171
|
+
outputLimit: 98_304,
|
|
172
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "glm-4.6",
|
|
176
|
+
aliases: ["GLM-4.6"],
|
|
177
|
+
contextWindow: 202_752,
|
|
178
|
+
outputLimit: 65_536,
|
|
179
|
+
sourceUrl: ZHIPU_GLM_4_6_URL,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "glm-4.7",
|
|
183
|
+
aliases: ["GLM-4.7"],
|
|
184
|
+
contextWindow: 202_752,
|
|
185
|
+
outputLimit: 131_072,
|
|
186
|
+
sourceUrl: ZHIPU_GLM_4_7_URL,
|
|
187
|
+
},
|
|
131
188
|
{
|
|
132
189
|
id: "glm-5",
|
|
133
190
|
aliases: ["GLM-5"],
|
|
@@ -146,6 +203,13 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
146
203
|
contextWindow: 1_048_576,
|
|
147
204
|
sourceUrl: HF_GLM_5_2_URL,
|
|
148
205
|
},
|
|
206
|
+
{
|
|
207
|
+
id: "glm-5-turbo",
|
|
208
|
+
aliases: ["GLM-5-Turbo"],
|
|
209
|
+
contextWindow: 202_752,
|
|
210
|
+
outputLimit: 131_072,
|
|
211
|
+
sourceUrl: ZHIPU_GLM_5_TURBO_URL,
|
|
212
|
+
},
|
|
149
213
|
],
|
|
150
214
|
},
|
|
151
215
|
],
|
|
@@ -5,6 +5,7 @@ import "./openai/index";
|
|
|
5
5
|
// Import providers to trigger self-registration
|
|
6
6
|
import "./anthropic/anthropicProvider";
|
|
7
7
|
import "./openai/provider";
|
|
8
|
+
import "./openai/zhipuProvider";
|
|
8
9
|
import "./openai/alibabaProvider";
|
|
9
10
|
|
|
10
11
|
export type { FormatHandler, ParsedRequest } from "./handler";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CapturedLog } from "../../schemas";
|
|
2
|
+
import type { ProviderProtocol } from "../protocol";
|
|
3
|
+
import { registry } from "../providers";
|
|
4
|
+
import { extractOpenAIStream } from "./stream";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_ZHIPU_UPSTREAM = "https://open.bigmodel.cn/api/paas/v4";
|
|
7
|
+
const ZHIPU_MODEL_PREFIXES = ["glm-", "charglm-", "cogview-", "embedding-", "rerank-"];
|
|
8
|
+
|
|
9
|
+
export const zhipuProvider: ProviderProtocol = {
|
|
10
|
+
name: "zhipu",
|
|
11
|
+
|
|
12
|
+
matches(model: string): boolean {
|
|
13
|
+
const normalized = model.toLowerCase().replace(/\s+/g, "-");
|
|
14
|
+
return ZHIPU_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
getUpstreamBase(_isChatCompletions: boolean, providerConfig?: { baseUrl?: string }): string {
|
|
18
|
+
return providerConfig?.baseUrl ?? DEFAULT_ZHIPU_UPSTREAM;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
extractStream(
|
|
22
|
+
raw: string,
|
|
23
|
+
log: CapturedLog,
|
|
24
|
+
fallbackModel?: string,
|
|
25
|
+
collectChunks?: boolean,
|
|
26
|
+
): string {
|
|
27
|
+
return extractOpenAIStream(raw, log, fallbackModel, collectChunks);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
registry.register(zhipuProvider);
|
|
@@ -17,6 +17,10 @@ export type ExternalProvider = {
|
|
|
17
17
|
alreadyExists: boolean;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const ZHIPU_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
21
|
+
const ZHIPU_CODING_OPENAI_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
|
|
22
|
+
const ZHIPU_CODING_ANTHROPIC_BASE_URL = "https://open.bigmodel.cn/api/anthropic";
|
|
23
|
+
|
|
20
24
|
function readJsonSafe(filePath: string): Record<string, unknown> | null {
|
|
21
25
|
try {
|
|
22
26
|
if (!existsSync(filePath)) return null;
|
|
@@ -51,6 +55,9 @@ const KNOWN_PROVIDER_NAMES: Record<string, string> = {
|
|
|
51
55
|
azure: "Azure",
|
|
52
56
|
moonshot: "Moonshot",
|
|
53
57
|
zhipu: "ZhipuAI",
|
|
58
|
+
zhipuai: "ZhipuAI",
|
|
59
|
+
bigmodel: "ZhipuAI",
|
|
60
|
+
zai: "Z.AI",
|
|
54
61
|
qwen: "Qwen",
|
|
55
62
|
baichuan: "Baichuan",
|
|
56
63
|
iflytek: "iFlytek",
|
|
@@ -399,6 +406,46 @@ function detectMiMoCodeProviders(): ExternalProvider[] {
|
|
|
399
406
|
openaiBaseUrl: "https://api.together.xyz",
|
|
400
407
|
format: "openai",
|
|
401
408
|
},
|
|
409
|
+
zhipu: {
|
|
410
|
+
anthropicBaseUrl: "",
|
|
411
|
+
openaiBaseUrl: ZHIPU_OPENAI_BASE_URL,
|
|
412
|
+
format: "openai",
|
|
413
|
+
},
|
|
414
|
+
zhipuai: {
|
|
415
|
+
anthropicBaseUrl: "",
|
|
416
|
+
openaiBaseUrl: ZHIPU_OPENAI_BASE_URL,
|
|
417
|
+
format: "openai",
|
|
418
|
+
},
|
|
419
|
+
bigmodel: {
|
|
420
|
+
anthropicBaseUrl: "",
|
|
421
|
+
openaiBaseUrl: ZHIPU_OPENAI_BASE_URL,
|
|
422
|
+
format: "openai",
|
|
423
|
+
},
|
|
424
|
+
zai: {
|
|
425
|
+
anthropicBaseUrl: "",
|
|
426
|
+
openaiBaseUrl: ZHIPU_OPENAI_BASE_URL,
|
|
427
|
+
format: "openai",
|
|
428
|
+
},
|
|
429
|
+
glm: {
|
|
430
|
+
anthropicBaseUrl: ZHIPU_CODING_ANTHROPIC_BASE_URL,
|
|
431
|
+
openaiBaseUrl: ZHIPU_CODING_OPENAI_BASE_URL,
|
|
432
|
+
format: "anthropic",
|
|
433
|
+
},
|
|
434
|
+
"glm-coding-plan": {
|
|
435
|
+
anthropicBaseUrl: ZHIPU_CODING_ANTHROPIC_BASE_URL,
|
|
436
|
+
openaiBaseUrl: ZHIPU_CODING_OPENAI_BASE_URL,
|
|
437
|
+
format: "anthropic",
|
|
438
|
+
},
|
|
439
|
+
"zhipu-coding-plan": {
|
|
440
|
+
anthropicBaseUrl: ZHIPU_CODING_ANTHROPIC_BASE_URL,
|
|
441
|
+
openaiBaseUrl: ZHIPU_CODING_OPENAI_BASE_URL,
|
|
442
|
+
format: "anthropic",
|
|
443
|
+
},
|
|
444
|
+
"zai-coding-plan": {
|
|
445
|
+
anthropicBaseUrl: ZHIPU_CODING_ANTHROPIC_BASE_URL,
|
|
446
|
+
openaiBaseUrl: ZHIPU_CODING_OPENAI_BASE_URL,
|
|
447
|
+
format: "anthropic",
|
|
448
|
+
},
|
|
402
449
|
};
|
|
403
450
|
|
|
404
451
|
for (const [key, cred] of Object.entries(auth)) {
|
package/src/proxy/providers.ts
CHANGED
|
@@ -665,6 +665,56 @@ function normalizeModelName(name: string): string {
|
|
|
665
665
|
return name.toLowerCase().replace(/\s+/g, "-");
|
|
666
666
|
}
|
|
667
667
|
|
|
668
|
+
const ZHIPU_MODEL_PREFIXES = ["glm-", "charglm-", "cogview-", "embedding-", "rerank-"];
|
|
669
|
+
const ZHIPU_PROVIDER_ALIASES = ["zhipu", "zhipuai", "bigmodel", "zai", "glm"];
|
|
670
|
+
const ZHIPU_BASE_URL_MARKERS = [
|
|
671
|
+
"open.bigmodel.cn/api/paas/v4",
|
|
672
|
+
"open.bigmodel.cn/api/coding/paas/v4",
|
|
673
|
+
"open.bigmodel.cn/api/anthropic",
|
|
674
|
+
];
|
|
675
|
+
|
|
676
|
+
function normalizeProviderAlias(name: string): string {
|
|
677
|
+
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function providerRoutingUrls(provider: ProviderConfig): string[] {
|
|
681
|
+
const urls: string[] = [];
|
|
682
|
+
if (provider.anthropicBaseUrl !== undefined && provider.anthropicBaseUrl.trim() !== "") {
|
|
683
|
+
urls.push(provider.anthropicBaseUrl);
|
|
684
|
+
}
|
|
685
|
+
if (provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl.trim() !== "") {
|
|
686
|
+
urls.push(provider.openaiBaseUrl);
|
|
687
|
+
}
|
|
688
|
+
if (provider.baseUrl !== undefined && provider.baseUrl.trim() !== "") {
|
|
689
|
+
urls.push(provider.baseUrl);
|
|
690
|
+
}
|
|
691
|
+
return urls;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function isZhipuProvider(provider: ProviderConfig): boolean {
|
|
695
|
+
const normalizedName = normalizeProviderAlias(provider.name);
|
|
696
|
+
const nameMatches = ZHIPU_PROVIDER_ALIASES.some((alias) => {
|
|
697
|
+
const normalizedAlias = normalizeProviderAlias(alias);
|
|
698
|
+
return normalizedName.includes(normalizedAlias) || normalizedAlias.includes(normalizedName);
|
|
699
|
+
});
|
|
700
|
+
if (nameMatches) return true;
|
|
701
|
+
|
|
702
|
+
return providerRoutingUrls(provider).some((url) => {
|
|
703
|
+
const normalizedUrl = url.toLowerCase().replace(/\/+$/, "");
|
|
704
|
+
return ZHIPU_BASE_URL_MARKERS.some((marker) => normalizedUrl.includes(marker));
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function modelMatchesKnownProviderPrefix(
|
|
709
|
+
modelNormalized: string,
|
|
710
|
+
provider: ProviderConfig,
|
|
711
|
+
): boolean {
|
|
712
|
+
if (isZhipuProvider(provider)) {
|
|
713
|
+
return ZHIPU_MODEL_PREFIXES.some((prefix) => modelNormalized.startsWith(prefix));
|
|
714
|
+
}
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
|
|
668
718
|
/**
|
|
669
719
|
* Finds a provider by model name using three strategies:
|
|
670
720
|
* 1. Case-insensitive prefix match against "{provider.name}-" (e.g., "deepseek-" matches "deepseek-*")
|
|
@@ -683,6 +733,11 @@ export function findProviderByModel(model: string): ProviderConfig | null {
|
|
|
683
733
|
if (modelLower.startsWith(providerPrefix)) {
|
|
684
734
|
return provider;
|
|
685
735
|
}
|
|
736
|
+
// Strategy 1b: known model-family prefixes for providers whose public
|
|
737
|
+
// model names do not start with the provider brand, e.g. ZhipuAI -> glm-*.
|
|
738
|
+
if (modelMatchesKnownProviderPrefix(modelNormalized, provider)) {
|
|
739
|
+
return provider;
|
|
740
|
+
}
|
|
686
741
|
// Strategy 2: match against provider.models array with normalization
|
|
687
742
|
const modelList = provider.models ?? (provider.model !== undefined ? [provider.model] : []);
|
|
688
743
|
for (const m of modelList) {
|
package/src/proxy/upstream.ts
CHANGED
|
@@ -60,6 +60,10 @@ export function selectUpstreamBase(route: ApiRoute, provider: ProviderConfig | n
|
|
|
60
60
|
export function buildUpstreamUrl(upstreamBase: string, normalizedPath: string): string {
|
|
61
61
|
const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
|
|
62
62
|
|
|
63
|
+
if (isZhipuVersionedOpenAIBase(base) && isVersionedChatCompletionsPath(normalizedPath)) {
|
|
64
|
+
return base + normalizedPath.slice(3);
|
|
65
|
+
}
|
|
66
|
+
|
|
63
67
|
// Many OpenAI-compatible base URLs already include /v1. Avoid producing /v1/v1.
|
|
64
68
|
if (base.endsWith("/v1") && normalizedPath.startsWith("/v1/")) {
|
|
65
69
|
return base + normalizedPath.slice(3);
|
|
@@ -67,6 +71,27 @@ export function buildUpstreamUrl(upstreamBase: string, normalizedPath: string):
|
|
|
67
71
|
return base + normalizedPath;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
function isVersionedChatCompletionsPath(normalizedPath: string): boolean {
|
|
75
|
+
return (
|
|
76
|
+
normalizedPath === PATH_V1_CHAT_COMPLETIONS ||
|
|
77
|
+
normalizedPath.startsWith(`${PATH_V1_CHAT_COMPLETIONS}?`) ||
|
|
78
|
+
normalizedPath.startsWith(`${PATH_V1_CHAT_COMPLETIONS}/`)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isZhipuVersionedOpenAIBase(base: string): boolean {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = new URL(base);
|
|
85
|
+
const path = parsed.pathname.replace(/\/+$/, "");
|
|
86
|
+
return (
|
|
87
|
+
parsed.hostname === "open.bigmodel.cn" &&
|
|
88
|
+
(path === "/api/paas/v4" || path === "/api/coding/paas/v4")
|
|
89
|
+
);
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
70
95
|
export function setUpstreamHost(headers: Headers, upstreamBase: string): void {
|
|
71
96
|
try {
|
|
72
97
|
headers.set(HEADER_HOST, new URL(upstreamBase).host);
|