@xiaozhiclaw/provider-core 0.1.0 → 0.1.2
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/builtin-providers.js +164 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/media-client.js +7 -1
- package/dist/media-transport.d.ts +3 -1
- package/dist/transports/openai-compatible-embedding-media.d.ts +12 -0
- package/dist/transports/openai-compatible-embedding-media.js +51 -0
- package/dist/transports/openai-media.js +7 -3
- package/dist/transports/volcengine-media.js +38 -3
- package/package.json +1 -1
|
@@ -5,6 +5,34 @@
|
|
|
5
5
|
* model catalog API. External broad catalogs are intentionally not merged in.
|
|
6
6
|
*/
|
|
7
7
|
export const BUILTIN_PROVIDERS = [
|
|
8
|
+
{
|
|
9
|
+
id: "tei-bge-m3",
|
|
10
|
+
name: "BAAI bge-m3 (TEI)",
|
|
11
|
+
transport: "openai-chat",
|
|
12
|
+
baseUrl: "http://127.0.0.1:8080",
|
|
13
|
+
apiKeyEnvVars: [],
|
|
14
|
+
authType: "none",
|
|
15
|
+
isAggregator: false,
|
|
16
|
+
defaultModel: "BAAI/bge-m3",
|
|
17
|
+
models: [
|
|
18
|
+
{
|
|
19
|
+
id: "BAAI/bge-m3",
|
|
20
|
+
aliases: ["bge-m3"],
|
|
21
|
+
name: "BAAI bge-m3",
|
|
22
|
+
contextWindow: 8192,
|
|
23
|
+
maxOutput: 0,
|
|
24
|
+
toolCall: false,
|
|
25
|
+
reasoning: false,
|
|
26
|
+
vision: false,
|
|
27
|
+
mediaType: "embedding",
|
|
28
|
+
mediaCapabilities: {
|
|
29
|
+
type: "embedding",
|
|
30
|
+
dimensions: 1024,
|
|
31
|
+
maxTokens: 8192,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
8
36
|
// 鈹€鈹€ Tier 1: Major Chinese providers 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
9
37
|
// DeepSeek via Anthropic Messages API 鈥?CC-parity transport for tool_use / thinking blocks.
|
|
10
38
|
// See https://api-docs.deepseek.com/guides/anthropic_api
|
|
@@ -55,6 +83,9 @@ export const BUILTIN_PROVIDERS = [
|
|
|
55
83
|
{
|
|
56
84
|
id: "qwen",
|
|
57
85
|
name: "Alibaba Qwen (DashScope)",
|
|
86
|
+
group: "qwen",
|
|
87
|
+
variantKind: "standard",
|
|
88
|
+
billingChannelKind: "paygo",
|
|
58
89
|
transport: "anthropic-messages",
|
|
59
90
|
baseUrl: "https://dashscope.aliyuncs.com/apps/anthropic",
|
|
60
91
|
apiKeyEnvVars: ["DASHSCOPE_API_KEY", "QWEN_API_KEY"],
|
|
@@ -1444,9 +1475,13 @@ export const BUILTIN_PROVIDERS = [
|
|
|
1444
1475
|
{
|
|
1445
1476
|
id: "volcengine",
|
|
1446
1477
|
name: "Doubao / Volcengine",
|
|
1478
|
+
group: "volcengine",
|
|
1479
|
+
variantKind: "standard",
|
|
1480
|
+
billingChannelKind: "paygo",
|
|
1481
|
+
capabilities: ["thinking", "vision", "media"],
|
|
1447
1482
|
transport: "volcengine-responses",
|
|
1448
1483
|
baseUrl: "https://ark.cn-beijing.volces.com/api",
|
|
1449
|
-
apiKeyEnvVars: ["ARK_API_KEY", "DOUBAO_API_KEY"],
|
|
1484
|
+
apiKeyEnvVars: ["VOLCANO_ENGINE_API_KEY", "ARK_API_KEY", "DOUBAO_API_KEY"],
|
|
1450
1485
|
authType: "bearer",
|
|
1451
1486
|
isAggregator: false,
|
|
1452
1487
|
defaultModel: "doubao-seed-2-0-lite-260428",
|
|
@@ -1585,9 +1620,9 @@ export const BUILTIN_PROVIDERS = [
|
|
|
1585
1620
|
// Supports: text-to-image, image-to-image (single/multi-ref), group generation
|
|
1586
1621
|
// Docs: https://www.volcengine.com/docs/82379/1824121
|
|
1587
1622
|
{
|
|
1588
|
-
id: "doubao-seedream-5-0-260128",
|
|
1589
|
-
aliases: ["doubao-seedream-5-0"],
|
|
1590
|
-
name: "Doubao Seedream 5.0",
|
|
1623
|
+
id: "doubao-seedream-5-0-lite-260128",
|
|
1624
|
+
aliases: ["doubao-seedream-5-0-lite"],
|
|
1625
|
+
name: "Doubao Seedream 5.0 Lite",
|
|
1591
1626
|
contextWindow: 4096,
|
|
1592
1627
|
maxOutput: 1,
|
|
1593
1628
|
toolCall: false,
|
|
@@ -1681,6 +1716,58 @@ export const BUILTIN_PROVIDERS = [
|
|
|
1681
1716
|
},
|
|
1682
1717
|
],
|
|
1683
1718
|
},
|
|
1719
|
+
// Doubao subscription / Plan channel. It is a first-class provider variant so
|
|
1720
|
+
// a Plan key never gets silently mixed with the pay-as-you-go Ark API key.
|
|
1721
|
+
{
|
|
1722
|
+
id: "volcengine-plan",
|
|
1723
|
+
name: "Doubao / Volcengine Plan",
|
|
1724
|
+
group: "volcengine",
|
|
1725
|
+
variantKind: "coding-plan",
|
|
1726
|
+
billingChannelKind: "plan",
|
|
1727
|
+
capabilities: ["thinking", "coding"],
|
|
1728
|
+
transport: "openai-chat",
|
|
1729
|
+
baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3",
|
|
1730
|
+
apiKeyEnvVars: ["VOLCANO_ENGINE_API_KEY", "ARK_API_KEY", "DOUBAO_API_KEY"],
|
|
1731
|
+
authType: "bearer",
|
|
1732
|
+
isAggregator: false,
|
|
1733
|
+
defaultModel: "ark-code-latest",
|
|
1734
|
+
models: [
|
|
1735
|
+
{
|
|
1736
|
+
id: "ark-code-latest",
|
|
1737
|
+
name: "Ark Code Latest (Plan)",
|
|
1738
|
+
contextWindow: 262_144,
|
|
1739
|
+
maxOutput: 65_536,
|
|
1740
|
+
toolCall: true,
|
|
1741
|
+
reasoning: true,
|
|
1742
|
+
vision: false,
|
|
1743
|
+
costInput: 0,
|
|
1744
|
+
costOutput: 0,
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
id: "doubao-seed-code",
|
|
1748
|
+
name: "Doubao Seed Code (Plan)",
|
|
1749
|
+
contextWindow: 262_144,
|
|
1750
|
+
maxOutput: 65_536,
|
|
1751
|
+
toolCall: true,
|
|
1752
|
+
reasoning: true,
|
|
1753
|
+
vision: false,
|
|
1754
|
+
costInput: 0,
|
|
1755
|
+
costOutput: 0,
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
id: "doubao-seed-code-preview-251028",
|
|
1759
|
+
aliases: ["doubao-seed-code-preview"],
|
|
1760
|
+
name: "Doubao Seed Code Preview (Plan)",
|
|
1761
|
+
contextWindow: 262_144,
|
|
1762
|
+
maxOutput: 65_536,
|
|
1763
|
+
toolCall: true,
|
|
1764
|
+
reasoning: true,
|
|
1765
|
+
vision: false,
|
|
1766
|
+
costInput: 0,
|
|
1767
|
+
costOutput: 0,
|
|
1768
|
+
},
|
|
1769
|
+
],
|
|
1770
|
+
},
|
|
1684
1771
|
// 鈹€鈹€ Tier 2: Major International providers 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
1685
1772
|
{
|
|
1686
1773
|
id: "openai",
|
|
@@ -2153,7 +2240,7 @@ export const BUILTIN_PROVIDERS = [
|
|
|
2153
2240
|
id: "openrouter",
|
|
2154
2241
|
name: "OpenRouter",
|
|
2155
2242
|
transport: "openai-chat",
|
|
2156
|
-
baseUrl: "https://openrouter.ai/api",
|
|
2243
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
2157
2244
|
apiKeyEnvVars: ["OPENROUTER_API_KEY"],
|
|
2158
2245
|
authType: "bearer",
|
|
2159
2246
|
isAggregator: true,
|
|
@@ -2187,6 +2274,53 @@ export const BUILTIN_PROVIDERS = [
|
|
|
2187
2274
|
costOutput: 25.0,
|
|
2188
2275
|
costCacheRead: 0.5,
|
|
2189
2276
|
},
|
|
2277
|
+
{
|
|
2278
|
+
id: "openai/gpt-5.5",
|
|
2279
|
+
name: "GPT-5.5 (via OpenRouter)",
|
|
2280
|
+
contextWindow: 1050000,
|
|
2281
|
+
maxOutput: 128000,
|
|
2282
|
+
toolCall: true,
|
|
2283
|
+
reasoning: true,
|
|
2284
|
+
vision: true,
|
|
2285
|
+
costInput: 5.0,
|
|
2286
|
+
costOutput: 30.0,
|
|
2287
|
+
costCacheRead: 0.5,
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
id: "openai/gpt-5.4",
|
|
2291
|
+
name: "GPT-5.4 (via OpenRouter)",
|
|
2292
|
+
contextWindow: 1050000,
|
|
2293
|
+
maxOutput: 128000,
|
|
2294
|
+
toolCall: true,
|
|
2295
|
+
reasoning: true,
|
|
2296
|
+
vision: true,
|
|
2297
|
+
costInput: 2.5,
|
|
2298
|
+
costOutput: 15.0,
|
|
2299
|
+
costCacheRead: 0.25,
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
id: "google/gemini-3.1-pro-preview",
|
|
2303
|
+
name: "Gemini 3.1 Pro (via OpenRouter)",
|
|
2304
|
+
contextWindow: 1048576,
|
|
2305
|
+
maxOutput: 65536,
|
|
2306
|
+
toolCall: true,
|
|
2307
|
+
reasoning: true,
|
|
2308
|
+
reasoningRequired: true,
|
|
2309
|
+
vision: true,
|
|
2310
|
+
costInput: 2.0,
|
|
2311
|
+
costOutput: 12.0,
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
id: "google/gemini-3-flash-preview",
|
|
2315
|
+
name: "Gemini 3 Flash (via OpenRouter)",
|
|
2316
|
+
contextWindow: 1048576,
|
|
2317
|
+
maxOutput: 65536,
|
|
2318
|
+
toolCall: true,
|
|
2319
|
+
reasoning: true,
|
|
2320
|
+
vision: true,
|
|
2321
|
+
costInput: 0.50,
|
|
2322
|
+
costOutput: 3.0,
|
|
2323
|
+
},
|
|
2190
2324
|
{
|
|
2191
2325
|
id: "deepseek/deepseek-v4-flash",
|
|
2192
2326
|
name: "DeepSeek V4 Flash (via OpenRouter)",
|
|
@@ -2207,6 +2341,9 @@ export const BUILTIN_PROVIDERS = [
|
|
|
2207
2341
|
{
|
|
2208
2342
|
id: "qwen-coding",
|
|
2209
2343
|
name: "Alibaba Qwen (Coding Plan)",
|
|
2344
|
+
group: "qwen",
|
|
2345
|
+
variantKind: "coding-plan",
|
|
2346
|
+
billingChannelKind: "plan",
|
|
2210
2347
|
transport: "anthropic-messages",
|
|
2211
2348
|
baseUrl: "https://coding.dashscope.aliyuncs.com/apps/anthropic",
|
|
2212
2349
|
apiKeyEnvVars: ["DASHSCOPE_API_KEY", "QWEN_API_KEY"],
|
|
@@ -2234,4 +2371,26 @@ export const BUILTIN_PROVIDERS = [
|
|
|
2234
2371
|
},
|
|
2235
2372
|
],
|
|
2236
2373
|
},
|
|
2374
|
+
{
|
|
2375
|
+
id: "llmrouter",
|
|
2376
|
+
name: "LLMRouter",
|
|
2377
|
+
transport: "openai-responses",
|
|
2378
|
+
baseUrl: "https://www.qlogicagent.com",
|
|
2379
|
+
apiKeyEnvVars: ["QLOGIC_LLMROUTER_API_KEY", "LLMROUTER_API_KEY"],
|
|
2380
|
+
authType: "bearer",
|
|
2381
|
+
isAggregator: true,
|
|
2382
|
+
defaultModel: "llmrouter-default",
|
|
2383
|
+
capabilities: ["thinking", "tool_stream", "vision", "media"],
|
|
2384
|
+
models: [
|
|
2385
|
+
{
|
|
2386
|
+
id: "llmrouter-default",
|
|
2387
|
+
name: "LLMRouter Default",
|
|
2388
|
+
contextWindow: 1000000,
|
|
2389
|
+
maxOutput: 128000,
|
|
2390
|
+
toolCall: true,
|
|
2391
|
+
reasoning: true,
|
|
2392
|
+
vision: true,
|
|
2393
|
+
},
|
|
2394
|
+
],
|
|
2395
|
+
},
|
|
2237
2396
|
];
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export { VolcengineMediaTransport } from "./transports/volcengine-media.js";
|
|
|
26
26
|
export { OpenAIMediaTransport } from "./transports/openai-media.js";
|
|
27
27
|
export { MiniMaxMediaTransport } from "./transports/minimax-media.js";
|
|
28
28
|
export { GeminiMediaTransport, type GeminiMediaConfig } from "./transports/gemini-media.js";
|
|
29
|
+
export { OpenAICompatibleEmbeddingMediaTransport, type OpenAICompatibleEmbeddingMediaConfig } from "./transports/openai-compatible-embedding-media.js";
|
|
29
30
|
export { GeminiLyriaRealtimeSession, generateRealtimeMusic } from "./transports/gemini-lyria-realtime.js";
|
|
30
31
|
export type { WeightedPrompt, MusicGenerationConfig, MusicScale, MusicGenerationMode, LyriaRealtimeConfig, LyriaRealtimeSessionOptions, AudioChunk } from "./transports/gemini-lyria-realtime.js";
|
|
31
32
|
export { RealtimeTransport } from "./transports/realtime-transport.js";
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export { VolcengineMediaTransport } from "./transports/volcengine-media.js";
|
|
|
22
22
|
export { OpenAIMediaTransport } from "./transports/openai-media.js";
|
|
23
23
|
export { MiniMaxMediaTransport } from "./transports/minimax-media.js";
|
|
24
24
|
export { GeminiMediaTransport } from "./transports/gemini-media.js";
|
|
25
|
+
export { OpenAICompatibleEmbeddingMediaTransport } from "./transports/openai-compatible-embedding-media.js";
|
|
25
26
|
// Gemini Lyria RealTime (WebSocket streaming music)
|
|
26
27
|
export { GeminiLyriaRealtimeSession, generateRealtimeMusic } from "./transports/gemini-lyria-realtime.js";
|
|
27
28
|
// OpenAI Realtime (WebSocket bidirectional audio/voice)
|
package/dist/media-client.js
CHANGED
|
@@ -14,6 +14,7 @@ import { MiniMaxMediaTransport } from "./transports/minimax-media.js";
|
|
|
14
14
|
import { GeminiMediaTransport } from "./transports/gemini-media.js";
|
|
15
15
|
import { QwenMediaTransport } from "./transports/qwen-media.js";
|
|
16
16
|
import { ZhipuMediaTransport } from "./transports/zhipu-media.js";
|
|
17
|
+
import { OpenAICompatibleEmbeddingMediaTransport } from "./transports/openai-compatible-embedding-media.js";
|
|
17
18
|
// 鈹€鈹€ MediaClient 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
18
19
|
export class MediaClient {
|
|
19
20
|
registry;
|
|
@@ -142,11 +143,14 @@ function modelSupportsOperation(caps, requiredOp) {
|
|
|
142
143
|
*/
|
|
143
144
|
function createMediaTransport(providerDef) {
|
|
144
145
|
// Remove trailing /anthropic path for providers that use it for chat
|
|
145
|
-
const rawUrl = providerDef.
|
|
146
|
+
const rawUrl = providerDef.id === "tei-bge-m3"
|
|
147
|
+
? (process.env.TEI_BGE_M3_BASE_URL || process.env.BGE_M3_BASE_URL || providerDef.baseUrl)
|
|
148
|
+
: providerDef.baseUrl;
|
|
146
149
|
switch (providerDef.id) {
|
|
147
150
|
case "volcengine":
|
|
148
151
|
return new VolcengineMediaTransport({ baseUrl: rawUrl });
|
|
149
152
|
case "openai":
|
|
153
|
+
case "llmrouter":
|
|
150
154
|
return new OpenAIMediaTransport({ baseUrl: rawUrl });
|
|
151
155
|
case "minimax":
|
|
152
156
|
return new MiniMaxMediaTransport({ baseUrl: rawUrl });
|
|
@@ -167,6 +171,8 @@ function createMediaTransport(providerDef) {
|
|
|
167
171
|
return new ZhipuMediaTransport({
|
|
168
172
|
baseUrl: rawUrl.replace(/\/api\/coding\/paas\/v4\/?$/, "/api/paas/v4"),
|
|
169
173
|
});
|
|
174
|
+
case "tei-bge-m3":
|
|
175
|
+
return new OpenAICompatibleEmbeddingMediaTransport({ baseUrl: rawUrl });
|
|
170
176
|
default:
|
|
171
177
|
// Provider doesn't support media generation
|
|
172
178
|
return undefined;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export type MediaType = "image" | "video" | "music" | "music_realtime" | "tts" | "3d" | "stt" | "embedding" | "video_understanding" | "image_understanding" | "voice_clone" | "rerank" | "document_parsing" | "realtime_audio" | "realtime_video";
|
|
13
13
|
export interface MediaRequest {
|
|
14
|
-
/** Generation model id, e.g. "doubao-seedream-5-0-260128", "gpt-image-2" */
|
|
14
|
+
/** Generation model id, e.g. "doubao-seedream-5-0-lite-260128", "gpt-image-2" */
|
|
15
15
|
model: string;
|
|
16
16
|
/** What kind of media to generate */
|
|
17
17
|
mediaType: MediaType;
|
|
@@ -117,6 +117,8 @@ export interface MediaRequest {
|
|
|
117
117
|
export interface MediaResult {
|
|
118
118
|
/** URLs of generated media files */
|
|
119
119
|
mediaUrls: string[];
|
|
120
|
+
/** Provider actually used */
|
|
121
|
+
provider?: string;
|
|
120
122
|
/** Model actually used */
|
|
121
123
|
model?: string;
|
|
122
124
|
/** Output dimensions / format info */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MediaRequest, MediaResult, MediaTransport, MediaType } from "../media-transport.js";
|
|
2
|
+
export interface OpenAICompatibleEmbeddingMediaConfig {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class OpenAICompatibleEmbeddingMediaTransport implements MediaTransport {
|
|
7
|
+
readonly supportedTypes: readonly MediaType[];
|
|
8
|
+
private readonly baseUrl;
|
|
9
|
+
private readonly timeoutMs;
|
|
10
|
+
constructor(config: OpenAICompatibleEmbeddingMediaConfig);
|
|
11
|
+
generate(request: MediaRequest, apiKey: string, signal?: AbortSignal): Promise<MediaResult>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export class OpenAICompatibleEmbeddingMediaTransport {
|
|
2
|
+
supportedTypes = ["embedding"];
|
|
3
|
+
baseUrl;
|
|
4
|
+
timeoutMs;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
7
|
+
this.timeoutMs = config.timeoutMs ?? 30_000;
|
|
8
|
+
}
|
|
9
|
+
async generate(request, apiKey, signal) {
|
|
10
|
+
const start = Date.now();
|
|
11
|
+
const text = request.metadata?.input ?? request.text ?? request.prompt;
|
|
12
|
+
if (!text)
|
|
13
|
+
throw new Error("OpenAICompatibleEmbeddingMediaTransport: text or prompt is required for embedding");
|
|
14
|
+
const body = {
|
|
15
|
+
model: request.model,
|
|
16
|
+
input: text,
|
|
17
|
+
};
|
|
18
|
+
if (request.metadata?.dimensions)
|
|
19
|
+
body.dimensions = request.metadata.dimensions;
|
|
20
|
+
const headers = { "Content-Type": "application/json" };
|
|
21
|
+
if (apiKey)
|
|
22
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
23
|
+
const res = await fetch(`${this.baseUrl}/v1/embeddings`, {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers,
|
|
26
|
+
body: JSON.stringify(body),
|
|
27
|
+
signal: signal ?? AbortSignal.timeout(this.timeoutMs),
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const textBody = await res.text().catch(() => "");
|
|
31
|
+
throw new Error(`OpenAI-compatible embedding error ${res.status}: ${textBody}`);
|
|
32
|
+
}
|
|
33
|
+
const data = await res.json();
|
|
34
|
+
const embeddings = (data.data ?? [])
|
|
35
|
+
.map(item => item.embedding)
|
|
36
|
+
.filter((item) => Array.isArray(item));
|
|
37
|
+
const totalTokens = data.usage?.total_tokens ?? data.usage?.prompt_tokens;
|
|
38
|
+
return {
|
|
39
|
+
mediaUrls: [],
|
|
40
|
+
model: data.model ?? request.model,
|
|
41
|
+
durationMs: Date.now() - start,
|
|
42
|
+
billingUnit: totalTokens !== undefined ? "per_token" : undefined,
|
|
43
|
+
billingQuantity: totalTokens,
|
|
44
|
+
metadata: {
|
|
45
|
+
embeddings,
|
|
46
|
+
dimensions: embeddings[0]?.length ?? 0,
|
|
47
|
+
usage: data.usage,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -61,9 +61,13 @@ export class OpenAIMediaTransport {
|
|
|
61
61
|
throw new Error(`OpenAI images API error ${res.status}: ${text}`);
|
|
62
62
|
}
|
|
63
63
|
const data = await res.json();
|
|
64
|
-
const mediaUrls = (data.data ?? [])
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
const mediaUrls = (data.data ?? []).flatMap(d => {
|
|
65
|
+
if (d.url)
|
|
66
|
+
return [d.url];
|
|
67
|
+
if (d.b64_json)
|
|
68
|
+
return [`data:image/webp;base64,${d.b64_json}`];
|
|
69
|
+
return [];
|
|
70
|
+
});
|
|
67
71
|
return {
|
|
68
72
|
mediaUrls,
|
|
69
73
|
model: request.model,
|
|
@@ -140,9 +140,7 @@ export class VolcengineMediaTransport {
|
|
|
140
140
|
return this.parseStreamingImage(res.body, request, start);
|
|
141
141
|
}
|
|
142
142
|
const data = await res.json();
|
|
143
|
-
const mediaUrls = (data.data ??
|
|
144
|
-
.map(d => d.url)
|
|
145
|
-
.filter((u) => !!u);
|
|
143
|
+
const mediaUrls = extractVolcengineImageUrls(data.data ?? data);
|
|
146
144
|
return {
|
|
147
145
|
mediaUrls,
|
|
148
146
|
model: request.model,
|
|
@@ -770,6 +768,43 @@ export class VolcengineMediaTransport {
|
|
|
770
768
|
throw new Error("Volcengine task timed out after polling");
|
|
771
769
|
}
|
|
772
770
|
}
|
|
771
|
+
function extractVolcengineImageUrls(value) {
|
|
772
|
+
const urls = [];
|
|
773
|
+
const visit = (node) => {
|
|
774
|
+
if (!node)
|
|
775
|
+
return;
|
|
776
|
+
if (Array.isArray(node)) {
|
|
777
|
+
for (const item of node)
|
|
778
|
+
visit(item);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (typeof node !== "object")
|
|
782
|
+
return;
|
|
783
|
+
const record = node;
|
|
784
|
+
for (const key of ["url", "image_url", "output_url"]) {
|
|
785
|
+
const url = record[key];
|
|
786
|
+
if (typeof url === "string" && url.trim())
|
|
787
|
+
urls.push(url);
|
|
788
|
+
}
|
|
789
|
+
const b64 = record.b64_json;
|
|
790
|
+
if (typeof b64 === "string" && b64.trim())
|
|
791
|
+
urls.push(`data:image/webp;base64,${b64}`);
|
|
792
|
+
for (const key of ["image_urls", "urls", "output_urls"]) {
|
|
793
|
+
const value = record[key];
|
|
794
|
+
if (Array.isArray(value)) {
|
|
795
|
+
for (const item of value) {
|
|
796
|
+
if (typeof item === "string" && item.trim())
|
|
797
|
+
urls.push(item);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
for (const child of Object.values(record)) {
|
|
802
|
+
visit(child);
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
visit(value);
|
|
806
|
+
return [...new Set(urls)];
|
|
807
|
+
}
|
|
773
808
|
function extractGeneratedTokenUsage(result) {
|
|
774
809
|
const usage = (result.usage ?? result.data?.usage);
|
|
775
810
|
const completionTokens = numericUsage(usage?.completion_tokens)
|