@bubblebrain-ai/bubble 0.0.24 → 0.0.26
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/README.md +5 -3
- package/dist/agent.js +1 -1
- package/dist/clipboard.d.ts +14 -0
- package/dist/clipboard.js +132 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +22 -6
- package/dist/goal/format.js +34 -4
- package/dist/goal/store.d.ts +3 -0
- package/dist/goal/store.js +14 -1
- package/dist/goal/usage.d.ts +2 -0
- package/dist/goal/usage.js +3 -0
- package/dist/main.js +23 -42
- package/dist/model-catalog.d.ts +3 -1
- package/dist/model-catalog.js +17 -28
- package/dist/prompt/compose.js +1 -1
- package/dist/provider-anthropic.d.ts +4 -0
- package/dist/provider-anthropic.js +31 -0
- package/dist/provider-ark-responses.d.ts +17 -0
- package/dist/provider-ark-responses.js +462 -0
- package/dist/provider-transform.js +7 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.js +170 -27
- package/dist/slash-commands/commands.js +22 -0
- package/dist/tools/todo.js +22 -38
- package/dist/tui/detect-theme.d.ts +1 -0
- package/dist/tui/detect-theme.js +23 -0
- package/dist/tui/image-display.d.ts +13 -0
- package/dist/tui/image-display.js +49 -0
- package/dist/tui/input-history.d.ts +37 -6
- package/dist/tui/input-history.js +194 -23
- package/dist/tui/model-switch.d.ts +42 -0
- package/dist/tui/model-switch.js +55 -0
- package/dist/tui-ink/app.d.ts +32 -2
- package/dist/tui-ink/app.js +1409 -549
- package/dist/tui-ink/approval/select.js +10 -0
- package/dist/tui-ink/detect-theme.d.ts +1 -2
- package/dist/tui-ink/detect-theme.js +1 -87
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +11 -0
- package/dist/tui-ink/feedback-dialog.js +10 -0
- package/dist/tui-ink/feishu-setup-picker.js +10 -0
- package/dist/tui-ink/footer.d.ts +1 -0
- package/dist/tui-ink/footer.js +8 -2
- package/dist/tui-ink/input-box.d.ts +71 -9
- package/dist/tui-ink/input-box.js +359 -121
- package/dist/tui-ink/input-history.d.ts +1 -16
- package/dist/tui-ink/input-history.js +1 -79
- package/dist/tui-ink/input-queue.d.ts +12 -0
- package/dist/tui-ink/input-queue.js +17 -0
- package/dist/tui-ink/key-events.d.ts +9 -0
- package/dist/tui-ink/key-events.js +8 -0
- package/dist/tui-ink/markdown.js +1 -1
- package/dist/tui-ink/message-list.d.ts +19 -1
- package/dist/tui-ink/message-list.js +111 -32
- package/dist/tui-ink/model-picker.d.ts +25 -2
- package/dist/tui-ink/model-picker.js +237 -20
- package/dist/tui-ink/plan-confirm.js +10 -0
- package/dist/tui-ink/question-dialog.js +46 -10
- package/dist/tui-ink/run.d.ts +10 -1
- package/dist/tui-ink/run.js +27 -42
- package/dist/tui-ink/session-picker.js +3 -0
- package/dist/tui-ink/submit-dedupe.d.ts +5 -0
- package/dist/tui-ink/submit-dedupe.js +25 -0
- package/dist/tui-ink/terminal-mouse.d.ts +24 -1
- package/dist/tui-ink/terminal-mouse.js +76 -21
- package/dist/tui-ink/theme.d.ts +6 -3
- package/dist/tui-ink/theme.js +10 -4
- package/dist/tui-ink/welcome.d.ts +1 -0
- package/dist/tui-ink/welcome.js +34 -27
- package/dist/variant/variant-resolver.js +4 -1
- package/package.json +1 -5
- package/dist/tui/clipboard.d.ts +0 -1
- package/dist/tui/clipboard.js +0 -53
- package/dist/tui/escape-confirmation.d.ts +0 -15
- package/dist/tui/escape-confirmation.js +0 -30
- package/dist/tui/global-key-router.d.ts +0 -3
- package/dist/tui/global-key-router.js +0 -87
- package/dist/tui/markdown-inline.d.ts +0 -22
- package/dist/tui/markdown-inline.js +0 -68
- package/dist/tui/markdown-theme-rules.d.ts +0 -23
- package/dist/tui/markdown-theme-rules.js +0 -164
- package/dist/tui/markdown-theme.d.ts +0 -5
- package/dist/tui/markdown-theme.js +0 -27
- package/dist/tui/opencode-spinner.d.ts +0 -22
- package/dist/tui/opencode-spinner.js +0 -216
- package/dist/tui/prompt-keybindings.d.ts +0 -42
- package/dist/tui/prompt-keybindings.js +0 -35
- package/dist/tui/render-signature.d.ts +0 -1
- package/dist/tui/render-signature.js +0 -7
- package/dist/tui/run.d.ts +0 -67
- package/dist/tui/run.js +0 -10166
- package/dist/tui/sidebar-mcp.d.ts +0 -31
- package/dist/tui/sidebar-mcp.js +0 -62
- package/dist/tui/sidebar-state.d.ts +0 -12
- package/dist/tui/sidebar-state.js +0 -69
- package/dist/tui/streaming-tool-args.d.ts +0 -15
- package/dist/tui/streaming-tool-args.js +0 -30
- package/dist/tui/tool-renderers/fallback.d.ts +0 -2
- package/dist/tui/tool-renderers/fallback.js +0 -75
- package/dist/tui/tool-renderers/registry.d.ts +0 -3
- package/dist/tui/tool-renderers/registry.js +0 -11
- package/dist/tui/tool-renderers/subagent.d.ts +0 -2
- package/dist/tui/tool-renderers/subagent.js +0 -135
- package/dist/tui/tool-renderers/types.d.ts +0 -36
- package/dist/tui/tool-renderers/types.js +0 -1
- package/dist/tui/tool-renderers/write-preview.d.ts +0 -12
- package/dist/tui/tool-renderers/write-preview.js +0 -32
- package/dist/tui/tool-renderers/write.d.ts +0 -6
- package/dist/tui/tool-renderers/write.js +0 -88
- package/dist/tui/transcript-scroll.d.ts +0 -25
- package/dist/tui/transcript-scroll.js +0 -20
- package/dist/tui-ink/transcript-viewport-math.d.ts +0 -11
- package/dist/tui-ink/transcript-viewport-math.js +0 -17
- package/dist/tui-ink/transcript-viewport.d.ts +0 -24
- package/dist/tui-ink/transcript-viewport.js +0 -83
- package/dist/tui-opentui/app.d.ts +0 -54
- package/dist/tui-opentui/app.js +0 -1371
- package/dist/tui-opentui/approval/approval-dialog.d.ts +0 -15
- package/dist/tui-opentui/approval/approval-dialog.js +0 -155
- package/dist/tui-opentui/approval/diff-view.d.ts +0 -9
- package/dist/tui-opentui/approval/diff-view.js +0 -43
- package/dist/tui-opentui/approval/select.d.ts +0 -37
- package/dist/tui-opentui/approval/select.js +0 -91
- package/dist/tui-opentui/detect-theme.d.ts +0 -2
- package/dist/tui-opentui/detect-theme.js +0 -87
- package/dist/tui-opentui/display-history.d.ts +0 -56
- package/dist/tui-opentui/display-history.js +0 -130
- package/dist/tui-opentui/edit-diff.d.ts +0 -11
- package/dist/tui-opentui/edit-diff.js +0 -57
- package/dist/tui-opentui/feedback-dialog.d.ts +0 -21
- package/dist/tui-opentui/feedback-dialog.js +0 -164
- package/dist/tui-opentui/feishu-setup-picker.d.ts +0 -7
- package/dist/tui-opentui/feishu-setup-picker.js +0 -272
- package/dist/tui-opentui/file-mentions.d.ts +0 -29
- package/dist/tui-opentui/file-mentions.js +0 -174
- package/dist/tui-opentui/footer.d.ts +0 -26
- package/dist/tui-opentui/footer.js +0 -40
- package/dist/tui-opentui/image-paste.d.ts +0 -54
- package/dist/tui-opentui/image-paste.js +0 -288
- package/dist/tui-opentui/input-box.d.ts +0 -32
- package/dist/tui-opentui/input-box.js +0 -462
- package/dist/tui-opentui/input-history.d.ts +0 -16
- package/dist/tui-opentui/input-history.js +0 -79
- package/dist/tui-opentui/markdown.d.ts +0 -66
- package/dist/tui-opentui/markdown.js +0 -127
- package/dist/tui-opentui/message-list.d.ts +0 -31
- package/dist/tui-opentui/message-list.js +0 -131
- package/dist/tui-opentui/model-picker.d.ts +0 -63
- package/dist/tui-opentui/model-picker.js +0 -450
- package/dist/tui-opentui/plan-confirm.d.ts +0 -9
- package/dist/tui-opentui/plan-confirm.js +0 -124
- package/dist/tui-opentui/question-dialog.d.ts +0 -10
- package/dist/tui-opentui/question-dialog.js +0 -110
- package/dist/tui-opentui/recent-activity.d.ts +0 -8
- package/dist/tui-opentui/recent-activity.js +0 -71
- package/dist/tui-opentui/run-session-picker.d.ts +0 -10
- package/dist/tui-opentui/run-session-picker.js +0 -28
- package/dist/tui-opentui/run.d.ts +0 -38
- package/dist/tui-opentui/run.js +0 -48
- package/dist/tui-opentui/session-picker.d.ts +0 -12
- package/dist/tui-opentui/session-picker.js +0 -120
- package/dist/tui-opentui/theme.d.ts +0 -89
- package/dist/tui-opentui/theme.js +0 -157
- package/dist/tui-opentui/todos.d.ts +0 -9
- package/dist/tui-opentui/todos.js +0 -45
- package/dist/tui-opentui/trace-groups.d.ts +0 -27
- package/dist/tui-opentui/trace-groups.js +0 -455
- package/dist/tui-opentui/use-terminal-size.d.ts +0 -4
- package/dist/tui-opentui/use-terminal-size.js +0 -5
- package/dist/tui-opentui/welcome.d.ts +0 -25
- package/dist/tui-opentui/welcome.js +0 -77
|
@@ -17,6 +17,25 @@ const MINIMAX_PROMPT_CACHE_MODELS = new Set([
|
|
|
17
17
|
"minimax-m2",
|
|
18
18
|
"m2-her",
|
|
19
19
|
]);
|
|
20
|
+
function anthropicEffortForLevel(level) {
|
|
21
|
+
switch (level) {
|
|
22
|
+
case "off":
|
|
23
|
+
return undefined;
|
|
24
|
+
case "minimal":
|
|
25
|
+
case "low":
|
|
26
|
+
return "low";
|
|
27
|
+
case "medium":
|
|
28
|
+
return "medium";
|
|
29
|
+
case "high":
|
|
30
|
+
return "high";
|
|
31
|
+
case "xhigh":
|
|
32
|
+
return "xhigh";
|
|
33
|
+
case "max":
|
|
34
|
+
return "max";
|
|
35
|
+
default:
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
20
39
|
export function createAnthropicMessagesProvider(options) {
|
|
21
40
|
async function* streamChat(messages, chatOptions) {
|
|
22
41
|
const body = buildAnthropicRequest(options, messages, {
|
|
@@ -87,6 +106,18 @@ export function buildAnthropicRequest(options, messages, chatOptions) {
|
|
|
87
106
|
}
|
|
88
107
|
if (effectiveThinkingLevel !== "off") {
|
|
89
108
|
body.thinking = { type: "adaptive" };
|
|
109
|
+
// Apply the selected reasoning depth via output_config.effort. Without this
|
|
110
|
+
// every thinking request silently ran at Anthropic's default (high),
|
|
111
|
+
// ignoring the chosen level. effort is an official-API feature, so only
|
|
112
|
+
// send it to the official endpoint — anthropic-compatible third parties
|
|
113
|
+
// (e.g. MiniMax) reject it. Levels are already clamped to the model's
|
|
114
|
+
// supported set, so the value is always a valid effort for this model.
|
|
115
|
+
if (isOfficialAnthropicBaseUrl(options.baseURL)) {
|
|
116
|
+
const effort = anthropicEffortForLevel(effectiveThinkingLevel);
|
|
117
|
+
if (effort) {
|
|
118
|
+
body.output_config = { effort };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
90
121
|
}
|
|
91
122
|
return body;
|
|
92
123
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Provider, ProviderMessage, StreamChunk, ThinkingLevel, TokenUsage, ToolChoiceMode, ToolDefinition } from "./types.js";
|
|
2
|
+
export interface ArkResponsesProviderOptions {
|
|
3
|
+
providerId?: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
baseURL: string;
|
|
6
|
+
thinkingLevel?: ThinkingLevel;
|
|
7
|
+
}
|
|
8
|
+
export declare function createArkResponsesProvider(options: ArkResponsesProviderOptions): Provider;
|
|
9
|
+
export declare function buildArkResponsesBody(messages: ProviderMessage[], options: {
|
|
10
|
+
model: string;
|
|
11
|
+
tools?: ToolDefinition[];
|
|
12
|
+
toolChoice?: ToolChoiceMode;
|
|
13
|
+
thinkingLevel?: ThinkingLevel;
|
|
14
|
+
stream: boolean;
|
|
15
|
+
}): Record<string, unknown>;
|
|
16
|
+
export declare function translateArkResponsesStream(response: Response): AsyncIterable<StreamChunk>;
|
|
17
|
+
export declare function normalizeArkResponsesUsage(usage: any): TokenUsage;
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { createProviderProtocolArtifactFilter } from "./provider-artifacts.js";
|
|
2
|
+
import { RateLimitError } from "./network/errors.js";
|
|
3
|
+
import { computeRetryDelayMs, getProviderMaxRetries, isRetryableHttpStatus, retryAfterMsFromResponse, sleepBeforeRetry, } from "./network/retry.js";
|
|
4
|
+
const DEFAULT_ARK_MODEL = "doubao-seed-2-1-pro-260628";
|
|
5
|
+
export function createArkResponsesProvider(options) {
|
|
6
|
+
async function* streamChat(messages, chatOptions) {
|
|
7
|
+
const body = buildArkResponsesBody(messages, {
|
|
8
|
+
model: chatOptions.model,
|
|
9
|
+
tools: chatOptions.tools,
|
|
10
|
+
toolChoice: chatOptions.toolChoice,
|
|
11
|
+
thinkingLevel: chatOptions.thinkingLevel ?? options.thinkingLevel ?? "high",
|
|
12
|
+
stream: true,
|
|
13
|
+
});
|
|
14
|
+
for (let attempt = 0;; attempt += 1) {
|
|
15
|
+
const response = await sendArkResponsesRequest(options, body, {
|
|
16
|
+
signal: chatOptions.abortSignal,
|
|
17
|
+
rateLimitPolicy: chatOptions.rateLimitPolicy,
|
|
18
|
+
attempt,
|
|
19
|
+
});
|
|
20
|
+
yield* translateArkResponsesStream(response);
|
|
21
|
+
yield { type: "done" };
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function complete(messages, chatOptions) {
|
|
26
|
+
const body = buildArkResponsesBody(messages, {
|
|
27
|
+
model: chatOptions?.model ?? DEFAULT_ARK_MODEL,
|
|
28
|
+
thinkingLevel: chatOptions?.thinkingLevel ?? options.thinkingLevel ?? "high",
|
|
29
|
+
stream: false,
|
|
30
|
+
});
|
|
31
|
+
const response = await sendArkResponsesRequest(options, body, {
|
|
32
|
+
signal: chatOptions?.abortSignal,
|
|
33
|
+
attempt: 0,
|
|
34
|
+
});
|
|
35
|
+
const payload = await response.json().catch(() => undefined);
|
|
36
|
+
return extractArkResponsesText(payload);
|
|
37
|
+
}
|
|
38
|
+
return { streamChat, complete };
|
|
39
|
+
}
|
|
40
|
+
export function buildArkResponsesBody(messages, options) {
|
|
41
|
+
const body = {
|
|
42
|
+
model: options.model,
|
|
43
|
+
input: messages.flatMap((message) => convertMessageToArkResponsesInput(message)),
|
|
44
|
+
store: false,
|
|
45
|
+
stream: options.stream,
|
|
46
|
+
thinking: {
|
|
47
|
+
type: shouldDisableArkThinking(options.thinkingLevel) ? "disabled" : "enabled",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
if (options.tools && options.tools.length > 0) {
|
|
51
|
+
body.tools = options.tools.map((tool) => ({
|
|
52
|
+
type: "function",
|
|
53
|
+
name: tool.name,
|
|
54
|
+
description: tool.description,
|
|
55
|
+
parameters: tool.parameters,
|
|
56
|
+
}));
|
|
57
|
+
if (options.toolChoice === "none") {
|
|
58
|
+
body.tool_choice = "none";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return body;
|
|
62
|
+
}
|
|
63
|
+
function shouldDisableArkThinking(level) {
|
|
64
|
+
return level === "off" || level === "minimal";
|
|
65
|
+
}
|
|
66
|
+
function convertMessageToArkResponsesInput(message) {
|
|
67
|
+
if (message.role === "user") {
|
|
68
|
+
if (typeof message.content === "string") {
|
|
69
|
+
return [{
|
|
70
|
+
type: "message",
|
|
71
|
+
role: "user",
|
|
72
|
+
content: message.content,
|
|
73
|
+
}];
|
|
74
|
+
}
|
|
75
|
+
return [{
|
|
76
|
+
type: "message",
|
|
77
|
+
role: "user",
|
|
78
|
+
content: message.content.map((part) => {
|
|
79
|
+
if (part.type === "text") {
|
|
80
|
+
return { type: "input_text", text: part.text };
|
|
81
|
+
}
|
|
82
|
+
return { type: "input_image", image_url: part.image_url.url };
|
|
83
|
+
}),
|
|
84
|
+
}];
|
|
85
|
+
}
|
|
86
|
+
if (message.role === "system") {
|
|
87
|
+
return [{
|
|
88
|
+
type: "message",
|
|
89
|
+
role: "system",
|
|
90
|
+
content: message.content,
|
|
91
|
+
}];
|
|
92
|
+
}
|
|
93
|
+
if (message.role === "assistant") {
|
|
94
|
+
const items = [];
|
|
95
|
+
if (message.content) {
|
|
96
|
+
items.push({
|
|
97
|
+
type: "message",
|
|
98
|
+
role: "assistant",
|
|
99
|
+
status: "completed",
|
|
100
|
+
content: [{ type: "output_text", text: message.content, annotations: [] }],
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
for (const toolCall of message.toolCalls ?? []) {
|
|
104
|
+
items.push({
|
|
105
|
+
type: "function_call",
|
|
106
|
+
call_id: toolCall.id,
|
|
107
|
+
name: toolCall.name,
|
|
108
|
+
arguments: toolCall.arguments || "{}",
|
|
109
|
+
status: "completed",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return items;
|
|
113
|
+
}
|
|
114
|
+
return [{
|
|
115
|
+
type: "function_call_output",
|
|
116
|
+
call_id: message.toolCallId,
|
|
117
|
+
output: message.content,
|
|
118
|
+
}];
|
|
119
|
+
}
|
|
120
|
+
async function sendArkResponsesRequest(options, body, requestOptions) {
|
|
121
|
+
const response = await fetch(resolveArkResponsesUrl(options.baseURL), {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: {
|
|
124
|
+
"Authorization": `Bearer ${options.apiKey}`,
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
"Accept": body.stream ? "text/event-stream" : "application/json",
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify(body),
|
|
129
|
+
signal: requestOptions.signal,
|
|
130
|
+
});
|
|
131
|
+
if (response.ok)
|
|
132
|
+
return response;
|
|
133
|
+
const errorText = await response.text().catch(() => "");
|
|
134
|
+
if (response.status === 429) {
|
|
135
|
+
const retryAfterMs = retryAfterMsFromResponse(response);
|
|
136
|
+
if (requestOptions.rateLimitPolicy !== "defer"
|
|
137
|
+
&& requestOptions.attempt < getProviderMaxRetries()) {
|
|
138
|
+
await sleepBeforeRetry(computeRetryDelayMs(requestOptions.attempt + 1, { retryAfterMs }), requestOptions.signal);
|
|
139
|
+
return sendArkResponsesRequest(options, body, {
|
|
140
|
+
...requestOptions,
|
|
141
|
+
attempt: requestOptions.attempt + 1,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
throw new RateLimitError(errorText || "Rate limited (429)", {
|
|
145
|
+
status: 429,
|
|
146
|
+
retryAfterMs,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (isRetryableHttpStatus(response.status) && requestOptions.attempt < getProviderMaxRetries()) {
|
|
150
|
+
await sleepBeforeRetry(computeRetryDelayMs(requestOptions.attempt + 1), requestOptions.signal);
|
|
151
|
+
return sendArkResponsesRequest(options, body, {
|
|
152
|
+
...requestOptions,
|
|
153
|
+
attempt: requestOptions.attempt + 1,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`${response.status} status code${errorText ? `: ${errorText}` : " (no body)"}`);
|
|
157
|
+
}
|
|
158
|
+
function resolveArkResponsesUrl(baseURL) {
|
|
159
|
+
return `${baseURL.trim().replace(/\/+$/, "")}/responses`;
|
|
160
|
+
}
|
|
161
|
+
export async function* translateArkResponsesStream(response) {
|
|
162
|
+
const toolCalls = new Map();
|
|
163
|
+
const textFilter = createProviderProtocolArtifactFilter();
|
|
164
|
+
for await (const event of parseArkResponsesSse(response)) {
|
|
165
|
+
const type = typeof event.type === "string" ? event.type : undefined;
|
|
166
|
+
if (!type)
|
|
167
|
+
continue;
|
|
168
|
+
if (type === "error") {
|
|
169
|
+
const message = typeof event.message === "string" ? event.message : JSON.stringify(event);
|
|
170
|
+
throw new Error(message);
|
|
171
|
+
}
|
|
172
|
+
if (type === "response.failed") {
|
|
173
|
+
const message = typeof event.response?.error?.message === "string"
|
|
174
|
+
? event.response.error.message
|
|
175
|
+
: "Ark Responses request failed";
|
|
176
|
+
throw new Error(message);
|
|
177
|
+
}
|
|
178
|
+
if (type === "response.output_item.added") {
|
|
179
|
+
const item = event.item;
|
|
180
|
+
if (item?.type === "function_call" && typeof item.call_id === "string" && typeof item.name === "string") {
|
|
181
|
+
const key = toolCallEventKey(event, item);
|
|
182
|
+
const entry = {
|
|
183
|
+
id: item.call_id,
|
|
184
|
+
name: item.name,
|
|
185
|
+
args: typeof item.arguments === "string" ? item.arguments : "",
|
|
186
|
+
started: true,
|
|
187
|
+
};
|
|
188
|
+
toolCalls.set(key, entry);
|
|
189
|
+
yield {
|
|
190
|
+
type: "tool_call",
|
|
191
|
+
id: entry.id,
|
|
192
|
+
name: entry.name,
|
|
193
|
+
arguments: "",
|
|
194
|
+
isStart: true,
|
|
195
|
+
isEnd: false,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (type === "response.output_text.delta" || type === "response.refusal.delta") {
|
|
201
|
+
const delta = typeof event.delta === "string" ? event.delta : "";
|
|
202
|
+
if (delta) {
|
|
203
|
+
const cleaned = textFilter.push(delta);
|
|
204
|
+
if (cleaned)
|
|
205
|
+
yield { type: "text", content: cleaned };
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (type === "response.reasoning_summary_text.delta") {
|
|
210
|
+
const delta = typeof event.delta === "string" ? event.delta : "";
|
|
211
|
+
if (delta) {
|
|
212
|
+
yield { type: "reasoning_delta", content: delta };
|
|
213
|
+
}
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (type === "response.function_call_arguments.delta") {
|
|
217
|
+
const entry = toolCalls.get(toolCallEventKey(event));
|
|
218
|
+
const delta = typeof event.delta === "string" ? event.delta : "";
|
|
219
|
+
if (entry && delta) {
|
|
220
|
+
entry.args += delta;
|
|
221
|
+
yield {
|
|
222
|
+
type: "tool_call",
|
|
223
|
+
id: entry.id,
|
|
224
|
+
name: entry.name,
|
|
225
|
+
arguments: delta,
|
|
226
|
+
isStart: false,
|
|
227
|
+
isEnd: false,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (type === "response.function_call_arguments.done") {
|
|
233
|
+
const entry = toolCalls.get(toolCallEventKey(event));
|
|
234
|
+
if (entry) {
|
|
235
|
+
const finalArgs = typeof event.arguments === "string" ? event.arguments : entry.args;
|
|
236
|
+
if (finalArgs.startsWith(entry.args)) {
|
|
237
|
+
const tail = finalArgs.slice(entry.args.length);
|
|
238
|
+
if (tail) {
|
|
239
|
+
yield {
|
|
240
|
+
type: "tool_call",
|
|
241
|
+
id: entry.id,
|
|
242
|
+
name: entry.name,
|
|
243
|
+
arguments: tail,
|
|
244
|
+
isStart: false,
|
|
245
|
+
isEnd: false,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
entry.args = finalArgs;
|
|
250
|
+
}
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (type === "response.output_item.done") {
|
|
254
|
+
const item = event.item;
|
|
255
|
+
if (item?.type === "function_call") {
|
|
256
|
+
const key = toolCallEventKey(event, item);
|
|
257
|
+
const entry = toolCalls.get(key);
|
|
258
|
+
if (entry) {
|
|
259
|
+
const finalArgs = typeof item.arguments === "string" ? item.arguments : entry.args;
|
|
260
|
+
const normalized = normalizeToolArgsDetailed(finalArgs);
|
|
261
|
+
yield {
|
|
262
|
+
type: "tool_call",
|
|
263
|
+
id: entry.id,
|
|
264
|
+
name: entry.name,
|
|
265
|
+
arguments: "",
|
|
266
|
+
argumentsFull: normalized.args,
|
|
267
|
+
argumentsCorrupt: normalized.corrupt || undefined,
|
|
268
|
+
isStart: false,
|
|
269
|
+
isEnd: true,
|
|
270
|
+
};
|
|
271
|
+
toolCalls.delete(key);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
|
|
277
|
+
const remainingText = textFilter.flush();
|
|
278
|
+
if (remainingText) {
|
|
279
|
+
yield { type: "text", content: remainingText };
|
|
280
|
+
}
|
|
281
|
+
for (const [key, entry] of toolCalls) {
|
|
282
|
+
const normalized = normalizeToolArgsDetailed(entry.args);
|
|
283
|
+
yield {
|
|
284
|
+
type: "tool_call",
|
|
285
|
+
id: entry.id,
|
|
286
|
+
name: entry.name,
|
|
287
|
+
arguments: "",
|
|
288
|
+
argumentsFull: normalized.args,
|
|
289
|
+
argumentsCorrupt: normalized.corrupt || type === "response.incomplete" || undefined,
|
|
290
|
+
isStart: false,
|
|
291
|
+
isEnd: true,
|
|
292
|
+
};
|
|
293
|
+
toolCalls.delete(key);
|
|
294
|
+
}
|
|
295
|
+
const usage = event.response?.usage;
|
|
296
|
+
if (usage) {
|
|
297
|
+
yield {
|
|
298
|
+
type: "usage",
|
|
299
|
+
usage: normalizeArkResponsesUsage(usage),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const remainingText = textFilter.flush();
|
|
306
|
+
if (remainingText) {
|
|
307
|
+
yield { type: "text", content: remainingText };
|
|
308
|
+
}
|
|
309
|
+
for (const [, entry] of toolCalls) {
|
|
310
|
+
const normalized = normalizeToolArgsDetailed(entry.args);
|
|
311
|
+
yield {
|
|
312
|
+
type: "tool_call",
|
|
313
|
+
id: entry.id,
|
|
314
|
+
name: entry.name,
|
|
315
|
+
arguments: "",
|
|
316
|
+
argumentsFull: normalized.args,
|
|
317
|
+
argumentsCorrupt: normalized.corrupt || undefined,
|
|
318
|
+
isStart: false,
|
|
319
|
+
isEnd: true,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function toolCallEventKey(event, item) {
|
|
324
|
+
if (typeof event.output_index === "number")
|
|
325
|
+
return `output:${event.output_index}`;
|
|
326
|
+
const itemId = typeof event.item_id === "string"
|
|
327
|
+
? event.item_id
|
|
328
|
+
: typeof item?.id === "string"
|
|
329
|
+
? item.id
|
|
330
|
+
: undefined;
|
|
331
|
+
if (itemId)
|
|
332
|
+
return itemId;
|
|
333
|
+
return "output:0";
|
|
334
|
+
}
|
|
335
|
+
async function* parseArkResponsesSse(response) {
|
|
336
|
+
if (!response.body)
|
|
337
|
+
return;
|
|
338
|
+
const reader = response.body.getReader();
|
|
339
|
+
const decoder = new TextDecoder();
|
|
340
|
+
let buffer = "";
|
|
341
|
+
try {
|
|
342
|
+
while (true) {
|
|
343
|
+
const { done, value } = await reader.read();
|
|
344
|
+
if (done)
|
|
345
|
+
break;
|
|
346
|
+
buffer += decoder.decode(value, { stream: true });
|
|
347
|
+
let boundary = buffer.indexOf("\n\n");
|
|
348
|
+
while (boundary >= 0) {
|
|
349
|
+
const chunk = buffer.slice(0, boundary);
|
|
350
|
+
buffer = buffer.slice(boundary + 2);
|
|
351
|
+
const data = chunk
|
|
352
|
+
.split("\n")
|
|
353
|
+
.filter((line) => line.startsWith("data:"))
|
|
354
|
+
.map((line) => line.slice(5).trim())
|
|
355
|
+
.join("\n")
|
|
356
|
+
.trim();
|
|
357
|
+
if (data && data !== "[DONE]") {
|
|
358
|
+
try {
|
|
359
|
+
yield JSON.parse(data);
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// Ignore malformed event frames; the provider will surface failure
|
|
363
|
+
// through response.failed when the request itself failed.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
boundary = buffer.indexOf("\n\n");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
finally {
|
|
371
|
+
try {
|
|
372
|
+
await reader.cancel();
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
// Ignore cleanup errors.
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
export function normalizeArkResponsesUsage(usage) {
|
|
380
|
+
const promptTokens = typeof usage?.input_tokens === "number" ? usage.input_tokens : 0;
|
|
381
|
+
const cachedTokens = typeof usage?.input_tokens_details?.cached_tokens === "number"
|
|
382
|
+
? usage.input_tokens_details.cached_tokens
|
|
383
|
+
: undefined;
|
|
384
|
+
return {
|
|
385
|
+
promptTokens,
|
|
386
|
+
completionTokens: typeof usage?.output_tokens === "number" ? usage.output_tokens : 0,
|
|
387
|
+
promptCacheHitTokens: cachedTokens,
|
|
388
|
+
promptCacheMissTokens: cachedTokens !== undefined ? Math.max(0, promptTokens - cachedTokens) : undefined,
|
|
389
|
+
reasoningTokens: typeof usage?.output_tokens_details?.reasoning_tokens === "number"
|
|
390
|
+
? usage.output_tokens_details.reasoning_tokens
|
|
391
|
+
: undefined,
|
|
392
|
+
totalTokens: typeof usage?.total_tokens === "number" ? usage.total_tokens : undefined,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function extractArkResponsesText(payload) {
|
|
396
|
+
const output = Array.isArray(payload?.output) ? payload.output : [];
|
|
397
|
+
const text = [];
|
|
398
|
+
for (const item of output) {
|
|
399
|
+
if (item?.type !== "message" || !Array.isArray(item.content))
|
|
400
|
+
continue;
|
|
401
|
+
for (const part of item.content) {
|
|
402
|
+
if ((part?.type === "output_text" || part?.type === "text") && typeof part.text === "string") {
|
|
403
|
+
text.push(part.text);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return text.join("");
|
|
408
|
+
}
|
|
409
|
+
function normalizeToolArgsDetailed(raw) {
|
|
410
|
+
const s = (raw ?? "").trim();
|
|
411
|
+
if (!s)
|
|
412
|
+
return { args: "{}", corrupt: false };
|
|
413
|
+
try {
|
|
414
|
+
JSON.parse(s);
|
|
415
|
+
return { args: s, corrupt: false };
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
const balanced = extractBalancedJson(s, 0);
|
|
419
|
+
if (!balanced)
|
|
420
|
+
return { args: "{}", corrupt: true };
|
|
421
|
+
try {
|
|
422
|
+
JSON.parse(balanced);
|
|
423
|
+
return { args: balanced, corrupt: false };
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return { args: "{}", corrupt: true };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function extractBalancedJson(s, start) {
|
|
431
|
+
if (s[start] !== "{")
|
|
432
|
+
return null;
|
|
433
|
+
let depth = 0;
|
|
434
|
+
let inStr = false;
|
|
435
|
+
let escape = false;
|
|
436
|
+
for (let i = start; i < s.length; i += 1) {
|
|
437
|
+
const c = s[i];
|
|
438
|
+
if (escape) {
|
|
439
|
+
escape = false;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (c === "\\" && inStr) {
|
|
443
|
+
escape = true;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (c === "\"") {
|
|
447
|
+
inStr = !inStr;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (inStr)
|
|
451
|
+
continue;
|
|
452
|
+
if (c === "{") {
|
|
453
|
+
depth += 1;
|
|
454
|
+
}
|
|
455
|
+
else if (c === "}") {
|
|
456
|
+
depth -= 1;
|
|
457
|
+
if (depth === 0)
|
|
458
|
+
return s.slice(start, i + 1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
@@ -47,6 +47,13 @@ export function resolveProviderRequestConfig(providerId, modelId, requestedLevel
|
|
|
47
47
|
: { reasoning_effort: effectiveThinkingLevel },
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
if (providerId === "doubao") {
|
|
51
|
+
return {
|
|
52
|
+
effectiveThinkingLevel,
|
|
53
|
+
parallelToolCalls: false,
|
|
54
|
+
extraBody: { reasoning_effort: effectiveThinkingLevel },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
50
57
|
if (providerId === "minimax" || providerId === "minimax-openai") {
|
|
51
58
|
const extraBody = { reasoning_split: true };
|
|
52
59
|
if (MINIMAX_M3_FAMILY.has(modelId)) {
|
package/dist/provider.d.ts
CHANGED
|
@@ -38,6 +38,13 @@ export interface NormalizedToolArgs {
|
|
|
38
38
|
}
|
|
39
39
|
export declare function normalizeToolArgsDetailed(raw: string): NormalizedToolArgs;
|
|
40
40
|
export declare function normalizeToolArgs(raw: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Convert a non-streaming OpenAI-compatible chat-completions response into the
|
|
43
|
+
* same chunk protocol used by the streaming adapter. This is used for provider
|
|
44
|
+
* tool-call paths where streamed function arguments are not reliable enough to
|
|
45
|
+
* execute safely.
|
|
46
|
+
*/
|
|
47
|
+
export declare function translateOpenAIFullResponse(response: any): AsyncIterable<StreamChunk>;
|
|
41
48
|
/**
|
|
42
49
|
* Convert an OpenAI-compatible chat-completions stream into our internal StreamChunk events.
|
|
43
50
|
*
|