@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.
@@ -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)
@@ -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.baseUrl;
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
- .map(d => d.url)
66
- .filter((u) => !!u);
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaozhiclaw/provider-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Shared QLogic LLM provider adaptation layer",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",