@bubblebrain-ai/bubble 0.0.55 → 0.0.56
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/context/budget.js +7 -2
- package/dist/model-catalog.d.ts +1 -1
- package/dist/model-catalog.js +5 -0
- package/dist/model-pricing.js +0 -0
- package/dist/provider-ark-responses.d.ts +12 -2
- package/dist/provider-ark-responses.js +68 -13
- package/dist/provider.js +7 -1
- package/dist/tui/model/image-paste.js +9 -10
- package/dist/types.d.ts +3 -1
- package/package.json +1 -1
package/dist/context/budget.js
CHANGED
|
@@ -36,8 +36,11 @@ export function estimateMessageTokens(message, providerId) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
function estimateProviderMetadataOverhead(metadata, providerId) {
|
|
39
|
-
const blocks =
|
|
40
|
-
|
|
39
|
+
const blocks = [
|
|
40
|
+
...(metadata?.anthropic?.contentBlocks ?? []),
|
|
41
|
+
...(metadata?.openai?.contentBlocks ?? []),
|
|
42
|
+
];
|
|
43
|
+
if (blocks.length === 0)
|
|
41
44
|
return 0;
|
|
42
45
|
const estimate = (text) => estimateTextTokens(text, providerId);
|
|
43
46
|
return blocks.reduce((sum, block) => {
|
|
@@ -46,6 +49,8 @@ function estimateProviderMetadataOverhead(metadata, providerId) {
|
|
|
46
49
|
overhead += estimate(block.signature);
|
|
47
50
|
if (block.type === "redacted_thinking" && typeof block.data === "string")
|
|
48
51
|
overhead += estimate(block.data);
|
|
52
|
+
if (block.type === "reasoning" && typeof block.encrypted_content === "string")
|
|
53
|
+
overhead += estimate(block.encrypted_content);
|
|
49
54
|
return sum + overhead;
|
|
50
55
|
}, 0);
|
|
51
56
|
}
|
package/dist/model-catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReasoningEffort } from "./types.js";
|
|
2
|
-
export type ProviderProtocol = "openai-chat" | "anthropic-messages" | "ark-responses" | "ai-sdk";
|
|
2
|
+
export type ProviderProtocol = "openai-chat" | "openai-responses" | "anthropic-messages" | "ark-responses" | "ai-sdk";
|
|
3
3
|
export interface BuiltinProviderDefinition {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
package/dist/model-catalog.js
CHANGED
|
@@ -3,6 +3,7 @@ export const BUILTIN_PROVIDERS = [
|
|
|
3
3
|
{ id: "openrouter", name: "OpenRouter", baseURL: "https://openrouter.ai/api/v1" },
|
|
4
4
|
{ id: "openai", name: "OpenAI", baseURL: "https://api.openai.com/v1", supportsOAuth: true },
|
|
5
5
|
{ id: "openai-codex", name: "OpenAI Codex (ChatGPT)", baseURL: "https://chatgpt.com/backend-api" },
|
|
6
|
+
{ id: "opencode-zen", name: "OpenCode Zen", baseURL: "https://opencode.ai/zen/v1", protocol: "openai-responses" },
|
|
6
7
|
// Grok subscription models via the CLI chat proxy. OAuth-only: the proxy
|
|
7
8
|
// accepts xAI session bearers (from /login grok), not api.x.ai API keys.
|
|
8
9
|
{ id: "grok", name: "Grok Subscription", baseURL: "https://cli-chat-proxy.grok.com/v1", supportsOAuth: true },
|
|
@@ -39,6 +40,7 @@ const GPT51_LEVELS = ["off", "low", "medium", "high"];
|
|
|
39
40
|
const GPT51_CODEX_MAX_LEVELS = ["off", "low", "medium", "high", "xhigh"];
|
|
40
41
|
const GPT51_CODEX_MINI_LEVELS = ["off", "medium", "high"];
|
|
41
42
|
const OPENAI_CHAT_LEVELS = ["off"];
|
|
43
|
+
const MUSE_SPARK_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
42
44
|
// Ox Alpha declares mandatory reasoning with exactly low/high/max through
|
|
43
45
|
// OpenRouter's live model metadata. The endpoint rejects disabled reasoning.
|
|
44
46
|
const OPENROUTER_OX_REASONING_LEVELS = ["low", "high", "max"];
|
|
@@ -117,6 +119,9 @@ export const BUILTIN_MODELS = [
|
|
|
117
119
|
{ id: "o1-preview", name: "o1-preview", providerId: "openai", tier: "strong", reasoningLevels: ["off", "low", "medium", "high"], contextWindow: 128000 },
|
|
118
120
|
{ id: "o1-mini", name: "o1-mini", providerId: "openai", tier: "fast", reasoningLevels: ["off", "low", "medium", "high"], contextWindow: 128000 },
|
|
119
121
|
{ id: "gpt-4-turbo", name: "gpt-4-turbo", providerId: "openai", reasoningLevels: OPENAI_CHAT_LEVELS, contextWindow: 128000 },
|
|
122
|
+
{ id: "muse-spark-1.3-contributor-free", name: "Muse Spark 1.3 Free", providerId: "opencode-zen", reasoningLevels: MUSE_SPARK_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
|
|
123
|
+
{ id: "muse-spark-1.2", name: "Muse Spark 1.2", providerId: "opencode-zen", reasoningLevels: MUSE_SPARK_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
|
|
124
|
+
{ id: "muse-spark-1.2-contributor-free", name: "Muse Spark 1.2 Free", providerId: "opencode-zen", reasoningLevels: MUSE_SPARK_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
|
|
120
125
|
{ id: "claude-fable-5", name: "Claude Fable 5", providerId: "anthropic", tier: "strong", reasoningLevels: ANTHROPIC_FABLE_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
|
|
121
126
|
{ id: "claude-opus-4-8", name: "Claude Opus 4.8", providerId: "anthropic", tier: "strong", reasoningLevels: ANTHROPIC_OPUS_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
|
|
122
127
|
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providerId: "anthropic", tier: "balanced", reasoningLevels: ANTHROPIC_SONNET_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
|
package/dist/model-pricing.js
CHANGED
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Provider, ProviderMessage, StreamChunk, ThinkingLevel, TokenUsage, ToolChoiceMode, ToolDefinition } from "./types.js";
|
|
2
|
-
export interface
|
|
2
|
+
export interface ResponsesProviderOptions {
|
|
3
3
|
providerId?: string;
|
|
4
4
|
apiKey: string;
|
|
5
5
|
baseURL: string;
|
|
@@ -7,7 +7,9 @@ export interface ArkResponsesProviderOptions {
|
|
|
7
7
|
/** User-configured extra headers, merged last (client-identity gates). */
|
|
8
8
|
headers?: Record<string, string>;
|
|
9
9
|
}
|
|
10
|
+
export type ArkResponsesProviderOptions = ResponsesProviderOptions;
|
|
10
11
|
export declare function createArkResponsesProvider(options: ArkResponsesProviderOptions): Provider;
|
|
12
|
+
export declare function createOpenAIResponsesProvider(options: ResponsesProviderOptions): Provider;
|
|
11
13
|
export declare function buildArkResponsesBody(messages: ProviderMessage[], options: {
|
|
12
14
|
model: string;
|
|
13
15
|
tools?: ToolDefinition[];
|
|
@@ -15,5 +17,13 @@ export declare function buildArkResponsesBody(messages: ProviderMessage[], optio
|
|
|
15
17
|
thinkingLevel?: ThinkingLevel;
|
|
16
18
|
stream: boolean;
|
|
17
19
|
}): Record<string, unknown>;
|
|
18
|
-
export declare function
|
|
20
|
+
export declare function buildOpenAIResponsesBody(messages: ProviderMessage[], options: {
|
|
21
|
+
model: string;
|
|
22
|
+
tools?: ToolDefinition[];
|
|
23
|
+
toolChoice?: ToolChoiceMode;
|
|
24
|
+
temperature?: number;
|
|
25
|
+
thinkingLevel?: ThinkingLevel;
|
|
26
|
+
stream: boolean;
|
|
27
|
+
}): Record<string, unknown>;
|
|
28
|
+
export declare function translateArkResponsesStream(response: Response, captureOpenAIReasoning?: boolean): AsyncIterable<StreamChunk>;
|
|
19
29
|
export declare function normalizeArkResponsesUsage(usage: any): TokenUsage;
|
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import { createProviderProtocolArtifactFilter } from "./provider-artifacts.js";
|
|
2
2
|
import { RateLimitError } from "./network/errors.js";
|
|
3
3
|
import { computeRetryDelayMs, getProviderMaxRetries, isRetryableHttpStatus, retryAfterMsFromResponse, sleepBeforeRetry, } from "./network/retry.js";
|
|
4
|
+
import { providerFetch } from "./network/provider-transport.js";
|
|
4
5
|
const DEFAULT_ARK_MODEL = "doubao-seed-2-1-pro-260628";
|
|
6
|
+
const DEFAULT_MUSE_MODEL = "muse-spark-1.3-contributor-free";
|
|
5
7
|
export function createArkResponsesProvider(options) {
|
|
8
|
+
return createResponsesProvider(options, "ark", DEFAULT_ARK_MODEL);
|
|
9
|
+
}
|
|
10
|
+
export function createOpenAIResponsesProvider(options) {
|
|
11
|
+
return createResponsesProvider(options, "openai", DEFAULT_MUSE_MODEL);
|
|
12
|
+
}
|
|
13
|
+
function createResponsesProvider(options, dialect, defaultModel) {
|
|
6
14
|
async function* streamChat(messages, chatOptions) {
|
|
7
|
-
const body =
|
|
15
|
+
const body = buildResponsesBody(messages, {
|
|
8
16
|
model: chatOptions.model,
|
|
9
17
|
tools: chatOptions.tools,
|
|
10
18
|
toolChoice: chatOptions.toolChoice,
|
|
19
|
+
temperature: chatOptions.temperature,
|
|
11
20
|
thinkingLevel: chatOptions.thinkingLevel ?? options.thinkingLevel ?? "high",
|
|
12
21
|
stream: true,
|
|
13
|
-
});
|
|
22
|
+
}, dialect);
|
|
14
23
|
for (let attempt = 0;; attempt += 1) {
|
|
15
24
|
const response = await sendArkResponsesRequest(options, body, {
|
|
16
25
|
signal: chatOptions.abortSignal,
|
|
17
26
|
rateLimitPolicy: chatOptions.rateLimitPolicy,
|
|
18
27
|
attempt,
|
|
19
28
|
});
|
|
20
|
-
yield* translateArkResponsesStream(response);
|
|
29
|
+
yield* translateArkResponsesStream(response, dialect === "openai");
|
|
21
30
|
yield { type: "done" };
|
|
22
31
|
return;
|
|
23
32
|
}
|
|
24
33
|
}
|
|
25
34
|
async function complete(messages, chatOptions) {
|
|
26
|
-
const body =
|
|
27
|
-
model: chatOptions?.model ??
|
|
35
|
+
const body = buildResponsesBody(messages, {
|
|
36
|
+
model: chatOptions?.model ?? defaultModel,
|
|
37
|
+
temperature: chatOptions?.temperature,
|
|
28
38
|
thinkingLevel: chatOptions?.thinkingLevel ?? options.thinkingLevel ?? "high",
|
|
29
39
|
stream: false,
|
|
30
|
-
});
|
|
40
|
+
}, dialect);
|
|
31
41
|
const response = await sendArkResponsesRequest(options, body, {
|
|
32
42
|
signal: chatOptions?.abortSignal,
|
|
33
43
|
attempt: 0,
|
|
@@ -38,15 +48,34 @@ export function createArkResponsesProvider(options) {
|
|
|
38
48
|
return { streamChat, complete };
|
|
39
49
|
}
|
|
40
50
|
export function buildArkResponsesBody(messages, options) {
|
|
51
|
+
return buildResponsesBody(messages, options, "ark");
|
|
52
|
+
}
|
|
53
|
+
export function buildOpenAIResponsesBody(messages, options) {
|
|
54
|
+
return buildResponsesBody(messages, options, "openai");
|
|
55
|
+
}
|
|
56
|
+
function buildResponsesBody(messages, options, dialect) {
|
|
41
57
|
const body = {
|
|
42
58
|
model: options.model,
|
|
43
|
-
input: messages.flatMap((message) =>
|
|
59
|
+
input: messages.flatMap((message) => convertMessageToResponsesInput(message, dialect)),
|
|
44
60
|
store: false,
|
|
45
61
|
stream: options.stream,
|
|
46
|
-
thinking: {
|
|
47
|
-
type: shouldDisableArkThinking(options.thinkingLevel) ? "disabled" : "enabled",
|
|
48
|
-
},
|
|
49
62
|
};
|
|
63
|
+
if (dialect === "ark") {
|
|
64
|
+
body.thinking = {
|
|
65
|
+
type: shouldDisableArkThinking(options.thinkingLevel) ? "disabled" : "enabled",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
body.include = ["reasoning.encrypted_content"];
|
|
70
|
+
if (options.thinkingLevel && options.thinkingLevel !== "off") {
|
|
71
|
+
body.reasoning = {
|
|
72
|
+
effort: options.thinkingLevel === "ultra" ? "xhigh" : options.thinkingLevel,
|
|
73
|
+
summary: "auto",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (options.temperature !== undefined)
|
|
77
|
+
body.temperature = options.temperature;
|
|
78
|
+
}
|
|
50
79
|
if (options.tools && options.tools.length > 0) {
|
|
51
80
|
body.tools = options.tools.map((tool) => ({
|
|
52
81
|
type: "function",
|
|
@@ -63,7 +92,7 @@ export function buildArkResponsesBody(messages, options) {
|
|
|
63
92
|
function shouldDisableArkThinking(level) {
|
|
64
93
|
return level === "off" || level === "minimal";
|
|
65
94
|
}
|
|
66
|
-
function
|
|
95
|
+
function convertMessageToResponsesInput(message, dialect) {
|
|
67
96
|
if (message.role === "user") {
|
|
68
97
|
if (typeof message.content === "string") {
|
|
69
98
|
return [{
|
|
@@ -92,6 +121,17 @@ function convertMessageToArkResponsesInput(message) {
|
|
|
92
121
|
}
|
|
93
122
|
if (message.role === "assistant") {
|
|
94
123
|
const items = [];
|
|
124
|
+
if (dialect === "openai") {
|
|
125
|
+
for (const block of message.providerMetadata?.openai?.contentBlocks ?? []) {
|
|
126
|
+
if (block.type !== "reasoning" || typeof block.encrypted_content !== "string")
|
|
127
|
+
continue;
|
|
128
|
+
items.push({
|
|
129
|
+
type: "reasoning",
|
|
130
|
+
summary: Array.isArray(block.summary) ? block.summary : [],
|
|
131
|
+
encrypted_content: block.encrypted_content,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
95
135
|
if (message.content) {
|
|
96
136
|
items.push({
|
|
97
137
|
type: "message",
|
|
@@ -118,7 +158,7 @@ function convertMessageToArkResponsesInput(message) {
|
|
|
118
158
|
}];
|
|
119
159
|
}
|
|
120
160
|
async function sendArkResponsesRequest(options, body, requestOptions) {
|
|
121
|
-
const response = await
|
|
161
|
+
const response = await providerFetch(resolveArkResponsesUrl(options.baseURL), {
|
|
122
162
|
method: "POST",
|
|
123
163
|
headers: {
|
|
124
164
|
"Authorization": `Bearer ${options.apiKey}`,
|
|
@@ -128,6 +168,8 @@ async function sendArkResponsesRequest(options, body, requestOptions) {
|
|
|
128
168
|
},
|
|
129
169
|
body: JSON.stringify(body),
|
|
130
170
|
signal: requestOptions.signal,
|
|
171
|
+
}, {
|
|
172
|
+
providerName: options.providerId ?? "Responses provider",
|
|
131
173
|
});
|
|
132
174
|
if (response.ok)
|
|
133
175
|
return response;
|
|
@@ -159,7 +201,7 @@ async function sendArkResponsesRequest(options, body, requestOptions) {
|
|
|
159
201
|
function resolveArkResponsesUrl(baseURL) {
|
|
160
202
|
return `${baseURL.trim().replace(/\/+$/, "")}/responses`;
|
|
161
203
|
}
|
|
162
|
-
export async function* translateArkResponsesStream(response) {
|
|
204
|
+
export async function* translateArkResponsesStream(response, captureOpenAIReasoning = false) {
|
|
163
205
|
const toolCalls = new Map();
|
|
164
206
|
const textFilter = createProviderProtocolArtifactFilter();
|
|
165
207
|
for await (const event of parseArkResponsesSse(response)) {
|
|
@@ -253,6 +295,19 @@ export async function* translateArkResponsesStream(response) {
|
|
|
253
295
|
}
|
|
254
296
|
if (type === "response.output_item.done") {
|
|
255
297
|
const item = event.item;
|
|
298
|
+
if (captureOpenAIReasoning
|
|
299
|
+
&& item?.type === "reasoning"
|
|
300
|
+
&& typeof item.encrypted_content === "string") {
|
|
301
|
+
yield {
|
|
302
|
+
type: "provider_content_block",
|
|
303
|
+
provider: "openai",
|
|
304
|
+
block: {
|
|
305
|
+
type: "reasoning",
|
|
306
|
+
summary: Array.isArray(item.summary) ? item.summary : [],
|
|
307
|
+
encrypted_content: item.encrypted_content,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
256
311
|
if (item?.type === "function_call") {
|
|
257
312
|
const key = toolCallEventKey(event, item);
|
|
258
313
|
const entry = toolCalls.get(key);
|
package/dist/provider.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import OpenAI from "openai";
|
|
7
7
|
import { appendFileSync } from "node:fs";
|
|
8
8
|
import { createAnthropicMessagesProvider } from "./provider-anthropic.js";
|
|
9
|
-
import { createArkResponsesProvider } from "./provider-ark-responses.js";
|
|
9
|
+
import { createArkResponsesProvider, createOpenAIResponsesProvider } from "./provider-ark-responses.js";
|
|
10
10
|
import { createAiSdkProvider } from "./provider-ai-sdk.js";
|
|
11
11
|
import { createOpenAICodexProvider, isOpenAICodexBaseUrl } from "./provider-openai-codex.js";
|
|
12
12
|
import { buildGrokSubscriptionHeaders, createGrokSubscriptionFetch, isGrokSubscriptionBaseUrl, } from "./provider-grok.js";
|
|
@@ -89,6 +89,9 @@ export function createProviderInstance(options) {
|
|
|
89
89
|
if (protocol === "ark-responses") {
|
|
90
90
|
return createArkResponsesProvider(options);
|
|
91
91
|
}
|
|
92
|
+
if (protocol === "openai-responses") {
|
|
93
|
+
return createOpenAIResponsesProvider(options);
|
|
94
|
+
}
|
|
92
95
|
if (protocol === "ai-sdk") {
|
|
93
96
|
return createAiSdkProvider(options);
|
|
94
97
|
}
|
|
@@ -273,6 +276,9 @@ function resolveProviderProtocol(options) {
|
|
|
273
276
|
return options.protocol;
|
|
274
277
|
const providerId = (options.providerId || "").toLowerCase();
|
|
275
278
|
const baseURL = (options.baseURL || "").toLowerCase();
|
|
279
|
+
if (providerId === "opencode-zen") {
|
|
280
|
+
return "openai-responses";
|
|
281
|
+
}
|
|
276
282
|
if (providerId === "doubao"
|
|
277
283
|
&& baseURL.replace(/\/+$/, "") === "https://ark.cn-beijing.volces.com/api/v3") {
|
|
278
284
|
return "ark-responses";
|
|
@@ -299,20 +299,19 @@ export async function getImageFromClipboard() {
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
async function getClipboardImageDarwin() {
|
|
302
|
-
// Probe first — `as «class PNGf»` throws if clipboard has no image.
|
|
303
|
-
try {
|
|
304
|
-
await execFileAsync("osascript", ["-e", "the clipboard as «class PNGf»"], {
|
|
305
|
-
timeout: 5000,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
catch {
|
|
309
|
-
return null;
|
|
310
|
-
}
|
|
311
302
|
const tmp = path.join(os.tmpdir(), `bubble_clip_${Date.now()}_${process.pid}.png`);
|
|
312
303
|
const script = `set png_data to (the clipboard as «class PNGf»)\n` +
|
|
313
304
|
`set fp to open for access POSIX file "${tmp}" with write permission\n` +
|
|
305
|
+
`try\n` +
|
|
306
|
+
`set eof fp to 0\n` +
|
|
314
307
|
`write png_data to fp\n` +
|
|
315
|
-
`close access fp
|
|
308
|
+
`close access fp\n` +
|
|
309
|
+
`on error err_msg number err_num\n` +
|
|
310
|
+
`try\n` +
|
|
311
|
+
`close access fp\n` +
|
|
312
|
+
`end try\n` +
|
|
313
|
+
`error err_msg number err_num\n` +
|
|
314
|
+
`end try`;
|
|
316
315
|
try {
|
|
317
316
|
await execFileAsync("osascript", ["-e", script], { timeout: 5000 });
|
|
318
317
|
const buf = await fs.readFile(tmp);
|
package/dist/types.d.ts
CHANGED
|
@@ -18,12 +18,14 @@ export type ReasoningEffort = ThinkingLevel;
|
|
|
18
18
|
export type ProviderRawContentBlock = Record<string, unknown> & {
|
|
19
19
|
type: string;
|
|
20
20
|
};
|
|
21
|
-
export type ProviderMetadataProvider = "anthropic" | "google";
|
|
21
|
+
export type ProviderMetadataProvider = "anthropic" | "google" | "openai";
|
|
22
22
|
export interface ProviderContentBlockStore {
|
|
23
23
|
contentBlocks?: ProviderRawContentBlock[];
|
|
24
24
|
}
|
|
25
25
|
export interface AssistantProviderMetadata {
|
|
26
26
|
anthropic?: ProviderContentBlockStore;
|
|
27
|
+
/** OpenAI Responses reasoning items captured for stateless tool-loop replay. */
|
|
28
|
+
openai?: ProviderContentBlockStore;
|
|
27
29
|
/**
|
|
28
30
|
* Gemini raw parts captured for replay: reasoning/tool-call parts carrying
|
|
29
31
|
* thoughtSignature so multi-turn thinking round-trips through our own
|