@arnilo/prism-provider-zai 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/models.js +1 -1
- package/dist/provider.d.ts +3 -2
- package/dist/provider.js +26 -86
- package/package.json +2 -2
- package/dist/sse.d.ts +0 -1
- package/dist/sse.js +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.0.4] - 2026-07-14
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Uses shared bounded transport/OpenAI helpers with GLM thinking/reasoning, structured-output, multimodal, tool-stream, telemetry, and protected-header behavior documented per model.
|
|
15
|
+
|
|
10
16
|
## [0.0.2] - 2026-07-05
|
|
11
17
|
|
|
12
18
|
### Added
|
package/dist/models.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function defineZaiModel(config) {
|
|
2
|
-
return { ...config, provider: "zai", capabilities: { input: ["text"], output: ["text"], reasoning: true, tools: true, streaming: true, ...config.capabilities } };
|
|
2
|
+
return { ...config, provider: "zai", capabilities: { input: ["text"], output: ["text"], reasoning: true, tools: true, streaming: true, structuredOutput: "json_schema", ...config.capabilities } };
|
|
3
3
|
}
|
|
4
4
|
export const zaiModels = [
|
|
5
5
|
defineZaiModel({
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AIProvider,
|
|
1
|
+
import type { AIProvider, JsonObject, ProviderEvent, ProviderRequest } from "@arnilo/prism";
|
|
2
|
+
import { type CredentialValueSource } from "@arnilo/prism";
|
|
2
3
|
export interface ZaiProviderOptions {
|
|
3
4
|
readonly id?: string;
|
|
4
5
|
readonly baseUrl?: string;
|
|
@@ -7,4 +8,4 @@ export interface ZaiProviderOptions {
|
|
|
7
8
|
}
|
|
8
9
|
export declare function createZaiProvider(options?: ZaiProviderOptions): AIProvider;
|
|
9
10
|
export declare function zaiBody(request: ProviderRequest): JsonObject;
|
|
10
|
-
export declare function zaiEvents(body: ReadableStream<Uint8Array
|
|
11
|
+
export declare function zaiEvents(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncIterable<ProviderEvent>;
|
package/dist/provider.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, resolveCredentialValue, toolCallContent } from "@arnilo/prism";
|
|
2
|
-
import {
|
|
1
|
+
import { assertStructuredOutputRequestSupported, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, resolveCredentialValue, toolCallContent } from "@arnilo/prism";
|
|
2
|
+
import { applyOpenAIChatStructuredOutput, mapOpenAIChatUsage, serializeOpenAIChatMessage, serializeOpenAITool, } from "@arnilo/prism/providers/openai";
|
|
3
|
+
import { parseJsonObjectArguments, readBoundedResponseText, readSseData, } from "@arnilo/prism/providers/transport";
|
|
3
4
|
import { zaiReasoningEffort, zaiThinking, zaiToolStream } from "./thinking.js";
|
|
4
5
|
export function createZaiProvider(options = {}) {
|
|
5
6
|
const id = options.id ?? "zai";
|
|
@@ -18,11 +19,12 @@ export function createZaiProvider(options = {}) {
|
|
|
18
19
|
body: JSON.stringify(zaiBody(request)),
|
|
19
20
|
signal: request.signal,
|
|
20
21
|
});
|
|
21
|
-
if (!response.ok)
|
|
22
|
-
return yield providerError(new Error(`Z.AI request failed: ${response.status} ${await
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
return yield providerError(new Error(`Z.AI request failed: ${response.status} ${await readBoundedResponseText(response, { secrets })}`), secrets);
|
|
24
|
+
}
|
|
23
25
|
if (!response.body)
|
|
24
26
|
return yield providerError(new Error("Z.AI response had no body"), secrets);
|
|
25
|
-
yield* zaiEvents(response.body);
|
|
27
|
+
yield* zaiEvents(response.body, request.signal);
|
|
26
28
|
}
|
|
27
29
|
catch (error) {
|
|
28
30
|
yield providerError(error, secrets);
|
|
@@ -31,11 +33,12 @@ export function createZaiProvider(options = {}) {
|
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
export function zaiBody(request) {
|
|
36
|
+
assertStructuredOutputRequestSupported(request.model, request.options);
|
|
34
37
|
const { maxTokens, ...parameters } = request.model.parameters ?? {};
|
|
35
|
-
|
|
38
|
+
const body = {
|
|
36
39
|
model: request.model.model,
|
|
37
|
-
messages: request.messages.map((message) =>
|
|
38
|
-
tools: request.tools?.map(
|
|
40
|
+
messages: request.messages.map((message) => serializeOpenAIChatMessage(message, request.model.capabilities ?? {})),
|
|
41
|
+
tools: request.tools?.map(serializeOpenAITool),
|
|
39
42
|
stream: true,
|
|
40
43
|
tool_stream: zaiToolStream(request),
|
|
41
44
|
thinking: zaiThinking(request),
|
|
@@ -44,18 +47,23 @@ export function zaiBody(request) {
|
|
|
44
47
|
max_tokens: maxTokens ?? request.model.limits?.maxOutputTokens,
|
|
45
48
|
...request.options?.compat,
|
|
46
49
|
...request.options?.extra,
|
|
47
|
-
}
|
|
50
|
+
};
|
|
51
|
+
applyOpenAIChatStructuredOutput(body, request.options?.structuredOutput);
|
|
52
|
+
return clean(body);
|
|
48
53
|
}
|
|
49
|
-
export async function* zaiEvents(body) {
|
|
54
|
+
export async function* zaiEvents(body, signal) {
|
|
50
55
|
const tools = new Map();
|
|
51
56
|
let usage;
|
|
52
|
-
for await (const data of readSseData(body)) {
|
|
57
|
+
for await (const data of readSseData(body, { signal })) {
|
|
53
58
|
if (data === "[DONE]")
|
|
54
59
|
break;
|
|
55
60
|
const chunk = JSON.parse(data);
|
|
56
|
-
usage =
|
|
57
|
-
if (chunk.usage)
|
|
58
|
-
|
|
61
|
+
usage = mapOpenAIChatUsage(chunk.usage) ?? usage;
|
|
62
|
+
if (chunk.usage) {
|
|
63
|
+
const mapped = mapOpenAIChatUsage(chunk.usage);
|
|
64
|
+
if (mapped)
|
|
65
|
+
yield providerUsage(mapped);
|
|
66
|
+
}
|
|
59
67
|
for (const choice of chunk.choices ?? []) {
|
|
60
68
|
const delta = choice.delta ?? {};
|
|
61
69
|
if (delta.content)
|
|
@@ -73,82 +81,14 @@ export async function* zaiEvents(body) {
|
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
83
|
}
|
|
76
|
-
for (const call of tools.values())
|
|
77
|
-
if (call.id && call.name)
|
|
78
|
-
yield providerToolCall(toolCallContent(call.id, call.name,
|
|
79
|
-
yield providerDone(usage);
|
|
80
|
-
}
|
|
81
|
-
function toMessage(message, capabilities = {}) {
|
|
82
|
-
if (message.role === "tool") {
|
|
83
|
-
const result = message.content.find((part) => part.type === "tool_result");
|
|
84
|
-
return {
|
|
85
|
-
role: "tool",
|
|
86
|
-
tool_call_id: result?.toolCallId ?? "",
|
|
87
|
-
content: result ? JSON.stringify(result.result ?? result.error ?? null) : "",
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
if (message.role === "assistant") {
|
|
91
|
-
const toolCalls = message.content.filter((part) => part.type === "tool_call");
|
|
92
|
-
const textParts = message.content.filter((part) => part.type === "text" || part.type === "thinking");
|
|
93
|
-
if (toolCalls.length > 0) {
|
|
94
|
-
return {
|
|
95
|
-
role: "assistant",
|
|
96
|
-
content: textParts.map((part) => part.text).join("\n") || null,
|
|
97
|
-
tool_calls: toolCalls.map((call) => ({
|
|
98
|
-
id: call.id,
|
|
99
|
-
type: "function",
|
|
100
|
-
function: { name: call.name, arguments: JSON.stringify(call.arguments) },
|
|
101
|
-
})),
|
|
102
|
-
};
|
|
84
|
+
for (const call of tools.values()) {
|
|
85
|
+
if (call.id && call.name) {
|
|
86
|
+
yield providerToolCall(toolCallContent(call.id, call.name, parseJsonObjectArguments(call.argumentsText, { toolName: call.name })));
|
|
103
87
|
}
|
|
104
88
|
}
|
|
105
|
-
|
|
106
|
-
for (const part of message.content) {
|
|
107
|
-
if (part.type === "text" || part.type === "thinking") {
|
|
108
|
-
content.push({ type: "text", text: part.text });
|
|
109
|
-
}
|
|
110
|
-
else if (part.type === "image") {
|
|
111
|
-
if (!capabilities.input?.includes("image")) {
|
|
112
|
-
throw new Error(`Z.AI request includes image but model does not declare image input capability`);
|
|
113
|
-
}
|
|
114
|
-
const url = part.url ?? (part.data ? `data:${part.mimeType ?? "image/png"};base64,${part.data}` : undefined);
|
|
115
|
-
if (!url)
|
|
116
|
-
throw new Error("Z.AI image block missing url or data");
|
|
117
|
-
content.push({ type: "image_url", image_url: { url } });
|
|
118
|
-
}
|
|
119
|
-
else if (part.type === "tool_call") {
|
|
120
|
-
throw new Error("Z.AI assistant tool_call blocks must be the only content on the message");
|
|
121
|
-
}
|
|
122
|
-
else if (part.type === "tool_result") {
|
|
123
|
-
throw new Error("Z.AI tool_result blocks must appear in role=tool messages");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (content.length === 1 && content[0].type === "text") {
|
|
127
|
-
return { role: message.role, content: content[0].text };
|
|
128
|
-
}
|
|
129
|
-
return { role: message.role, content };
|
|
130
|
-
}
|
|
131
|
-
function toTool(tool) {
|
|
132
|
-
return clean({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.parameters ?? { type: "object" } } });
|
|
133
|
-
}
|
|
134
|
-
function toUsage(usage) {
|
|
135
|
-
return usage ? { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens, totalTokens: usage.total_tokens, cacheReadTokens: usage.prompt_tokens_details?.cached_tokens, cacheWriteTokens: usage.prompt_tokens_details?.cache_write_tokens } : undefined;
|
|
136
|
-
}
|
|
137
|
-
function parseArgs(text) {
|
|
138
|
-
if (!text)
|
|
139
|
-
return {};
|
|
140
|
-
const parsed = JSON.parse(text);
|
|
141
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
89
|
+
yield providerDone(usage);
|
|
142
90
|
}
|
|
143
91
|
function clean(value) {
|
|
144
92
|
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
145
93
|
}
|
|
146
|
-
async function safeText(response) {
|
|
147
|
-
try {
|
|
148
|
-
return await response.text();
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return "";
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
94
|
//# sourceMappingURL=provider.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism-provider-zai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Z.AI provider package for Prism.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"pack:dry-run": "npm pack --dry-run"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@arnilo/prism": "0.0.
|
|
28
|
+
"@arnilo/prism": "0.0.4"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@arnilo/prism": "file:../.."
|
package/dist/sse.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function readSseData(body: ReadableStream<Uint8Array>): AsyncIterable<string>;
|
package/dist/sse.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export async function* readSseData(body) {
|
|
2
|
-
const reader = body.getReader();
|
|
3
|
-
const decoder = new TextDecoder();
|
|
4
|
-
let buffer = "";
|
|
5
|
-
while (true) {
|
|
6
|
-
const { done, value } = await reader.read();
|
|
7
|
-
if (done)
|
|
8
|
-
break;
|
|
9
|
-
buffer += decoder.decode(value, { stream: true });
|
|
10
|
-
const parts = buffer.split(/\r?\n\r?\n/);
|
|
11
|
-
buffer = parts.pop() ?? "";
|
|
12
|
-
for (const part of parts)
|
|
13
|
-
yield* eventData(part);
|
|
14
|
-
}
|
|
15
|
-
buffer += decoder.decode();
|
|
16
|
-
if (buffer)
|
|
17
|
-
yield* eventData(buffer);
|
|
18
|
-
}
|
|
19
|
-
function* eventData(event) {
|
|
20
|
-
const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n").trim();
|
|
21
|
-
if (data)
|
|
22
|
-
yield data;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=sse.js.map
|