@weisiren000/oiiai 0.1.3 → 0.2.0

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/index.js CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,103 +20,788 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
23
+ // src/providers/__types__.ts
24
+ var EFFORT_TOKEN_MAP;
25
+ var init_types = __esm({
26
+ "src/providers/__types__.ts"() {
27
+ "use strict";
28
+ EFFORT_TOKEN_MAP = {
29
+ off: 0,
30
+ low: 1024,
31
+ medium: 4096,
32
+ high: 16384
33
+ };
34
+ }
35
+ });
36
+
37
+ // src/utils/request-builder.ts
38
+ var RequestBuilder;
39
+ var init_request_builder = __esm({
40
+ "src/utils/request-builder.ts"() {
41
+ "use strict";
42
+ init_types();
43
+ RequestBuilder = class {
44
+ /**
45
+ * 构建聊天请求的基础参数
46
+ * 生成标准化的 OpenAI 兼容格式请求体
47
+ *
48
+ * @param options - 聊天选项
49
+ * @param stream - 是否为流式请求
50
+ * @returns 请求体对象
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const body = RequestBuilder.buildChatBody({
55
+ * model: 'gpt-4',
56
+ * messages: [{ role: 'user', content: 'Hello' }],
57
+ * temperature: 0.7
58
+ * });
59
+ * ```
60
+ */
61
+ static buildChatBody(options, stream = false) {
62
+ const { model, messages, temperature = 0.7, maxTokens } = options;
63
+ const body = {
64
+ model,
65
+ messages,
66
+ temperature,
67
+ stream
68
+ };
69
+ if (maxTokens !== void 0) {
70
+ body.max_tokens = maxTokens;
71
+ }
72
+ return body;
73
+ }
74
+ /**
75
+ * 构建 OpenRouter 格式的 reasoning 参数
76
+ *
77
+ * @param config - 推理配置
78
+ * @returns OpenRouter 格式的 reasoning 参数,或 undefined
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const reasoning = RequestBuilder.buildOpenRouterReasoning({ effort: 'high' });
83
+ * // => { effort: 'high', max_tokens: 16384 }
84
+ * ```
85
+ */
86
+ static buildOpenRouterReasoning(config) {
87
+ if (!config) return void 0;
88
+ if (config.effort === "off") return void 0;
89
+ const param = {};
90
+ if (config.effort) {
91
+ param.effort = config.effort;
92
+ }
93
+ if (config.budgetTokens !== void 0) {
94
+ param.max_tokens = config.budgetTokens;
95
+ } else if (config.effort && EFFORT_TOKEN_MAP[config.effort]) {
96
+ param.max_tokens = EFFORT_TOKEN_MAP[config.effort];
97
+ }
98
+ if (config.exclude !== void 0) {
99
+ param.exclude = config.exclude;
100
+ }
101
+ return Object.keys(param).length > 0 ? param : void 0;
102
+ }
103
+ /**
104
+ * 构建 Gemini 格式的 reasoning 参数
105
+ * Gemini 2.5+ 模型使用 reasoning_effort 控制思考
106
+ *
107
+ * @param config - 推理配置
108
+ * @returns Gemini 格式的参数对象
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const params = RequestBuilder.buildGeminiReasoning({ effort: 'high' });
113
+ * // => { reasoning_effort: 'high' }
114
+ * ```
115
+ */
116
+ static buildGeminiReasoning(config) {
117
+ if (!config || !config.effort || config.effort === "off") {
118
+ return {};
119
+ }
120
+ return {
121
+ reasoning_effort: config.effort
122
+ };
123
+ }
124
+ /**
125
+ * 构建 Groq 格式的 reasoning 参数
126
+ * Groq 使用 reasoning_format 参数控制推理输出
127
+ *
128
+ * @param config - 推理配置
129
+ * @returns Groq 格式的参数对象
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const params = RequestBuilder.buildGroqReasoning({ effort: 'high' });
134
+ * // => { reasoning_format: 'parsed' }
135
+ * ```
136
+ */
137
+ static buildGroqReasoning(config) {
138
+ if (!config) {
139
+ return {};
140
+ }
141
+ if (config.effort === "off") {
142
+ return { include_reasoning: false };
143
+ }
144
+ if (config.effort) {
145
+ return { reasoning_format: "parsed" };
146
+ }
147
+ return {};
148
+ }
149
+ /**
150
+ * 构建 DeepSeek 格式的 reasoning 参数
151
+ * DeepSeek 使用 thinking 参数启用思考模式
152
+ *
153
+ * @param config - 推理配置
154
+ * @returns DeepSeek 格式的参数对象
155
+ */
156
+ static buildDeepSeekReasoning(config) {
157
+ if (!config || !config.effort || config.effort === "off") {
158
+ return {};
159
+ }
160
+ return {
161
+ thinking: { type: "enabled" }
162
+ };
163
+ }
164
+ /**
165
+ * 构建 Nova 格式的 reasoning 参数
166
+ * Nova 使用 reasoningConfig 控制 extended thinking
167
+ *
168
+ * @param config - 推理配置
169
+ * @returns Nova 格式的参数对象
170
+ */
171
+ static buildNovaReasoning(config) {
172
+ if (!config || !config.effort || config.effort === "off") {
173
+ return {};
174
+ }
175
+ return {
176
+ reasoningConfig: {
177
+ type: "enabled",
178
+ maxReasoningEffort: config.effort
179
+ }
180
+ };
181
+ }
182
+ /**
183
+ * 构建 HTTP 请求头
184
+ *
185
+ * @param apiKey - API 密钥
186
+ * @param additionalHeaders - 额外的请求头
187
+ * @returns 请求头对象
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * const headers = RequestBuilder.buildHeaders('sk-xxx', {
192
+ * 'X-Custom-Header': 'value'
193
+ * });
194
+ * ```
195
+ */
196
+ static buildHeaders(apiKey, additionalHeaders) {
197
+ return {
198
+ "Content-Type": "application/json",
199
+ Authorization: `Bearer ${apiKey}`,
200
+ ...additionalHeaders
201
+ };
202
+ }
203
+ };
204
+ }
205
+ });
206
+
207
+ // src/client/types.ts
208
+ var ProviderError, APIError, NetworkError, TimeoutError;
209
+ var init_types2 = __esm({
210
+ "src/client/types.ts"() {
211
+ "use strict";
212
+ ProviderError = class extends Error {
213
+ constructor(message, code, provider, cause) {
214
+ super(message);
215
+ this.code = code;
216
+ this.provider = provider;
217
+ this.cause = cause;
218
+ this.name = "ProviderError";
219
+ }
220
+ };
221
+ APIError = class extends ProviderError {
222
+ constructor(message, provider, statusCode, responseBody) {
223
+ super(message, "API_ERROR", provider);
224
+ this.statusCode = statusCode;
225
+ this.responseBody = responseBody;
226
+ this.name = "APIError";
227
+ }
228
+ };
229
+ NetworkError = class extends ProviderError {
230
+ constructor(message, provider, cause) {
231
+ super(message, "NETWORK_ERROR", provider, cause);
232
+ this.name = "NetworkError";
233
+ }
234
+ };
235
+ TimeoutError = class extends ProviderError {
236
+ constructor(message, provider, timeoutMs) {
237
+ super(message, "TIMEOUT_ERROR", provider);
238
+ this.timeoutMs = timeoutMs;
239
+ this.name = "TimeoutError";
240
+ }
241
+ };
242
+ }
243
+ });
244
+
245
+ // src/client/http-provider-client.ts
246
+ var http_provider_client_exports = {};
247
+ __export(http_provider_client_exports, {
248
+ HttpProviderClient: () => HttpProviderClient
249
+ });
250
+ var DEFAULT_TIMEOUT, HttpProviderClient;
251
+ var init_http_provider_client = __esm({
252
+ "src/client/http-provider-client.ts"() {
253
+ "use strict";
254
+ init_request_builder();
255
+ init_types2();
256
+ DEFAULT_TIMEOUT = 3e4;
257
+ HttpProviderClient = class {
258
+ config;
259
+ /**
260
+ * 创建 HTTP Provider 客户端实例
261
+ *
262
+ * @param config - 客户端配置
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * const client = new HttpProviderClient({
267
+ * apiKey: 'sk-xxx',
268
+ * baseUrl: 'https://api.openai.com/v1',
269
+ * timeout: 60000
270
+ * });
271
+ * ```
272
+ */
273
+ constructor(config) {
274
+ this.config = {
275
+ ...config,
276
+ timeout: config.timeout ?? DEFAULT_TIMEOUT
277
+ };
278
+ }
279
+ /**
280
+ * 获取 Provider 名称(从 baseUrl 推断)
281
+ */
282
+ getProviderName() {
283
+ try {
284
+ const url = new URL(this.config.baseUrl);
285
+ return url.hostname;
286
+ } catch {
287
+ return "unknown";
288
+ }
289
+ }
290
+ /**
291
+ * 构建完整的请求 URL
292
+ */
293
+ buildUrl(endpoint) {
294
+ const baseUrl = this.config.baseUrl.replace(/\/$/, "");
295
+ const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
296
+ return `${baseUrl}${path}`;
297
+ }
298
+ /**
299
+ * 构建请求头
300
+ */
301
+ buildHeaders() {
302
+ return RequestBuilder.buildHeaders(this.config.apiKey, this.config.headers);
303
+ }
304
+ /**
305
+ * 创建带超时的 AbortController
306
+ */
307
+ createAbortController() {
308
+ const controller = new AbortController();
309
+ const timeoutId = setTimeout(() => {
310
+ controller.abort();
311
+ }, this.config.timeout ?? DEFAULT_TIMEOUT);
312
+ return { controller, timeoutId };
313
+ }
314
+ /**
315
+ * 处理 HTTP 错误响应
316
+ */
317
+ async handleErrorResponse(response) {
318
+ const provider = this.getProviderName();
319
+ let responseBody;
320
+ try {
321
+ responseBody = await response.text();
322
+ } catch {
323
+ }
324
+ let message;
325
+ switch (response.status) {
326
+ case 400:
327
+ message = "\u8BF7\u6C42\u53C2\u6570\u9519\u8BEF";
328
+ break;
329
+ case 401:
330
+ message = "API \u5BC6\u94A5\u65E0\u6548\u6216\u5DF2\u8FC7\u671F";
331
+ break;
332
+ case 403:
333
+ message = "\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u6B64\u8D44\u6E90";
334
+ break;
335
+ case 404:
336
+ message = "\u8BF7\u6C42\u7684\u8D44\u6E90\u4E0D\u5B58\u5728";
337
+ break;
338
+ case 429:
339
+ message = "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
340
+ break;
341
+ case 500:
342
+ message = "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF";
343
+ break;
344
+ case 502:
345
+ message = "\u7F51\u5173\u9519\u8BEF";
346
+ break;
347
+ case 503:
348
+ message = "\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528";
349
+ break;
350
+ default:
351
+ message = `HTTP \u9519\u8BEF: ${response.status} ${response.statusText}`;
352
+ }
353
+ throw new APIError(message, provider, response.status, responseBody);
354
+ }
355
+ /**
356
+ * 发送聊天请求(非流式)
357
+ *
358
+ * @param endpoint - API 端点路径
359
+ * @param body - 请求体
360
+ * @returns 响应数据
361
+ */
362
+ async chat(endpoint, body) {
363
+ const url = this.buildUrl(endpoint);
364
+ const headers = this.buildHeaders();
365
+ const { controller, timeoutId } = this.createAbortController();
366
+ const provider = this.getProviderName();
367
+ try {
368
+ const response = await fetch(url, {
369
+ method: "POST",
370
+ headers,
371
+ body: JSON.stringify(body),
372
+ signal: controller.signal
373
+ });
374
+ clearTimeout(timeoutId);
375
+ if (!response.ok) {
376
+ await this.handleErrorResponse(response);
377
+ }
378
+ const data = await response.json();
379
+ return data;
380
+ } catch (error) {
381
+ clearTimeout(timeoutId);
382
+ if (error instanceof APIError) {
383
+ throw error;
384
+ }
385
+ if (error instanceof Error && error.name === "AbortError") {
386
+ throw new TimeoutError(
387
+ `\u8BF7\u6C42\u8D85\u65F6\uFF08${this.config.timeout}ms\uFF09`,
388
+ provider,
389
+ this.config.timeout ?? DEFAULT_TIMEOUT
390
+ );
391
+ }
392
+ if (error instanceof TypeError) {
393
+ throw new NetworkError("\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8BBE\u7F6E", provider, error);
394
+ }
395
+ throw new NetworkError(
396
+ error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF",
397
+ provider,
398
+ error instanceof Error ? error : void 0
399
+ );
400
+ }
401
+ }
402
+ /**
403
+ * 发送流式聊天请求
404
+ *
405
+ * @param endpoint - API 端点路径
406
+ * @param body - 请求体
407
+ * @returns fetch Response 对象
408
+ */
409
+ async chatStream(endpoint, body) {
410
+ const url = this.buildUrl(endpoint);
411
+ const headers = this.buildHeaders();
412
+ const { controller, timeoutId } = this.createAbortController();
413
+ const provider = this.getProviderName();
414
+ try {
415
+ const response = await fetch(url, {
416
+ method: "POST",
417
+ headers,
418
+ body: JSON.stringify(body),
419
+ signal: controller.signal
420
+ });
421
+ clearTimeout(timeoutId);
422
+ if (!response.ok) {
423
+ await this.handleErrorResponse(response);
424
+ }
425
+ return response;
426
+ } catch (error) {
427
+ clearTimeout(timeoutId);
428
+ if (error instanceof APIError) {
429
+ throw error;
430
+ }
431
+ if (error instanceof Error && error.name === "AbortError") {
432
+ throw new TimeoutError(
433
+ `\u8BF7\u6C42\u8D85\u65F6\uFF08${this.config.timeout}ms\uFF09`,
434
+ provider,
435
+ this.config.timeout ?? DEFAULT_TIMEOUT
436
+ );
437
+ }
438
+ if (error instanceof TypeError) {
439
+ throw new NetworkError("\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8BBE\u7F6E", provider, error);
440
+ }
441
+ throw new NetworkError(
442
+ error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF",
443
+ provider,
444
+ error instanceof Error ? error : void 0
445
+ );
446
+ }
447
+ }
448
+ };
449
+ }
450
+ });
451
+
452
+ // src/utils/stream-processor.ts
453
+ var stream_processor_exports = {};
454
+ __export(stream_processor_exports, {
455
+ StreamProcessor: () => StreamProcessor
456
+ });
457
+ var StreamProcessor;
458
+ var init_stream_processor = __esm({
459
+ "src/utils/stream-processor.ts"() {
460
+ "use strict";
461
+ StreamProcessor = class _StreamProcessor {
462
+ /**
463
+ * 从响应内容中提取文本
464
+ * 支持字符串和数组格式的 content
465
+ *
466
+ * @param content - 响应内容,可以是字符串、数组或其他类型
467
+ * @returns 提取的文本内容
468
+ *
469
+ * @example
470
+ * ```ts
471
+ * // 字符串格式
472
+ * StreamProcessor.extractTextContent('Hello') // => 'Hello'
473
+ *
474
+ * // 数组格式
475
+ * StreamProcessor.extractTextContent([
476
+ * { type: 'text', text: 'Hello' },
477
+ * { type: 'text', text: ' World' }
478
+ * ]) // => 'Hello World'
479
+ * ```
480
+ */
481
+ static extractTextContent(content) {
482
+ if (typeof content === "string") {
483
+ return content;
484
+ }
485
+ if (Array.isArray(content)) {
486
+ return content.filter(
487
+ (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
488
+ ).map((item) => item.text).join("");
489
+ }
490
+ return "";
491
+ }
492
+ /**
493
+ * 解析 SSE 数据行
494
+ *
495
+ * @param line - SSE 数据行(如 "data: {...}")
496
+ * @returns 解析后的 JSON 对象,或 null(如果是 [DONE] 或无效数据)
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * StreamProcessor.parseSSELine('data: {"content": "Hello"}')
501
+ * // => { content: 'Hello' }
502
+ *
503
+ * StreamProcessor.parseSSELine('data: [DONE]')
504
+ * // => null
505
+ * ```
506
+ */
507
+ static parseSSELine(line) {
508
+ const trimmed = line.trim();
509
+ if (!trimmed || trimmed === "data: [DONE]") {
510
+ return null;
511
+ }
512
+ if (!trimmed.startsWith("data: ")) {
513
+ return null;
514
+ }
515
+ try {
516
+ const jsonStr = trimmed.slice(6);
517
+ return JSON.parse(jsonStr);
518
+ } catch {
519
+ return null;
520
+ }
521
+ }
522
+ /**
523
+ * 创建流式响应处理器
524
+ * 处理 SSE 格式的流式响应,提取并生成 StreamChunk
525
+ *
526
+ * @param response - fetch Response 对象
527
+ * @param deltaExtractor - 从 delta 中提取 StreamChunk 的函数
528
+ * @returns AsyncGenerator<StreamChunk>
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * const response = await fetch(url, { ... });
533
+ * const extractor = (delta) => {
534
+ * if (delta.content) {
535
+ * return { type: 'content', text: delta.content };
536
+ * }
537
+ * return null;
538
+ * };
539
+ *
540
+ * for await (const chunk of StreamProcessor.processStream(response, extractor)) {
541
+ * console.log(chunk.type, chunk.text);
542
+ * }
543
+ * ```
544
+ */
545
+ static async *processStream(response, deltaExtractor) {
546
+ const reader = response.body?.getReader();
547
+ if (!reader) {
548
+ throw new Error("No response body");
549
+ }
550
+ const decoder = new TextDecoder();
551
+ let buffer = "";
552
+ try {
553
+ while (true) {
554
+ const { done, value } = await reader.read();
555
+ if (done) break;
556
+ buffer += decoder.decode(value, { stream: true });
557
+ const lines = buffer.split("\n");
558
+ buffer = lines.pop() ?? "";
559
+ for (const line of lines) {
560
+ const data = _StreamProcessor.parseSSELine(line);
561
+ if (!data) continue;
562
+ const choices = data.choices;
563
+ const delta = choices?.[0]?.delta;
564
+ if (!delta) continue;
565
+ const chunk = deltaExtractor(delta);
566
+ if (chunk) {
567
+ yield chunk;
568
+ }
569
+ }
570
+ }
571
+ } finally {
572
+ reader.releaseLock();
573
+ }
574
+ }
575
+ /**
576
+ * 创建默认的 delta 提取器
577
+ * 支持 reasoning_content、reasoning、thoughts 和 content 字段
578
+ *
579
+ * @returns DeltaExtractor 函数
580
+ */
581
+ static createDefaultExtractor() {
582
+ return (delta) => {
583
+ const reasoningContent = delta.reasoning_content ?? delta.reasoning ?? delta.thoughts;
584
+ if (reasoningContent) {
585
+ return {
586
+ type: "reasoning",
587
+ text: _StreamProcessor.extractTextContent(reasoningContent)
588
+ };
589
+ }
590
+ if (delta.content) {
591
+ return {
592
+ type: "content",
593
+ text: _StreamProcessor.extractTextContent(delta.content)
594
+ };
595
+ }
596
+ return null;
597
+ };
598
+ }
599
+ };
600
+ }
601
+ });
602
+
603
+ // src/fluent/chat-session.ts
604
+ var chat_session_exports = {};
605
+ __export(chat_session_exports, {
606
+ ChatSessionImpl: () => ChatSessionImpl
607
+ });
608
+ var ChatSessionImpl;
609
+ var init_chat_session = __esm({
610
+ "src/fluent/chat-session.ts"() {
611
+ "use strict";
612
+ ChatSessionImpl = class {
613
+ /** 预设实例引用 */
614
+ preset;
615
+ /** 模型 ID */
616
+ model;
617
+ /** 会话配置 */
618
+ options;
619
+ /** 对话历史 */
620
+ history = [];
621
+ /**
622
+ * 创建对话会话
623
+ * @param preset - 预设实例
624
+ * @param model - 模型 ID
625
+ * @param options - 会话配置
626
+ */
627
+ constructor(preset, model, options) {
628
+ this.preset = preset;
629
+ this.model = model;
630
+ this.options = options ?? {};
631
+ if (this.options.system) {
632
+ this.history.push({
633
+ role: "system",
634
+ content: this.options.system
635
+ });
636
+ }
637
+ }
638
+ /**
639
+ * 发送消息并获取响应(非流式)
640
+ * @param message - 用户消息
641
+ * @returns 助手响应内容
642
+ */
643
+ async send(message) {
644
+ this.history.push({
645
+ role: "user",
646
+ content: message
647
+ });
648
+ try {
649
+ const response = await this.preset.ask(this.model, message, {
650
+ system: this.buildSystemContext(),
651
+ temperature: this.options.temperature,
652
+ maxTokens: this.options.maxTokens,
653
+ reasoning: this.options.reasoning
654
+ });
655
+ this.history.push({
656
+ role: "assistant",
657
+ content: response
658
+ });
659
+ return response;
660
+ } catch (error) {
661
+ this.history.pop();
662
+ throw error;
663
+ }
664
+ }
665
+ /**
666
+ * 发送消息并获取流式响应
667
+ * @param message - 用户消息
668
+ * @returns 流式数据块生成器
669
+ */
670
+ async *sendStream(message) {
671
+ this.history.push({
672
+ role: "user",
673
+ content: message
674
+ });
675
+ let responseContent = "";
676
+ try {
677
+ const stream = this.preset.stream(this.model, message, {
678
+ system: this.buildSystemContext(),
679
+ temperature: this.options.temperature,
680
+ maxTokens: this.options.maxTokens,
681
+ reasoning: this.options.reasoning
682
+ });
683
+ for await (const chunk of stream) {
684
+ if (chunk.type === "content") {
685
+ responseContent += chunk.text;
686
+ }
687
+ yield chunk;
688
+ }
689
+ this.history.push({
690
+ role: "assistant",
691
+ content: responseContent
692
+ });
693
+ } catch (error) {
694
+ this.history.pop();
695
+ throw error;
696
+ }
697
+ }
698
+ /**
699
+ * 获取对话历史
700
+ * @returns 按发送顺序排列的消息列表
701
+ */
702
+ getHistory() {
703
+ return [...this.history];
704
+ }
705
+ /**
706
+ * 清空对话历史
707
+ */
708
+ clearHistory() {
709
+ this.history = [];
710
+ if (this.options.system) {
711
+ this.history.push({
712
+ role: "system",
713
+ content: this.options.system
714
+ });
715
+ }
716
+ }
717
+ /**
718
+ * 构建系统上下文
719
+ * 将对话历史转换为系统提示词的一部分
720
+ * @returns 系统上下文字符串
721
+ */
722
+ buildSystemContext() {
723
+ const conversationHistory = this.history.filter(
724
+ (msg, index) => msg.role !== "system" && index < this.history.length - 1
725
+ );
726
+ if (conversationHistory.length === 0) {
727
+ return this.options.system;
728
+ }
729
+ const historyContext = conversationHistory.map((msg) => `${msg.role === "user" ? "\u7528\u6237" : "\u52A9\u624B"}: ${msg.content}`).join("\n");
730
+ const baseSystem = this.options.system ?? "";
731
+ return `${baseSystem}
732
+
733
+ \u4EE5\u4E0B\u662F\u4E4B\u524D\u7684\u5BF9\u8BDD\u5386\u53F2\uFF1A
734
+ ${historyContext}`.trim();
735
+ }
736
+ };
737
+ }
738
+ });
739
+
20
740
  // src/index.ts
21
741
  var index_exports = {};
22
742
  __export(index_exports, {
743
+ APIError: () => APIError,
744
+ BaseAdapter: () => BaseAdapter,
23
745
  BaseProvider: () => BaseProvider,
746
+ CONFIG_DEFAULTS: () => CONFIG_DEFAULTS,
747
+ ConfigManager: () => ConfigManager,
748
+ ConfigValidator: () => ConfigValidator,
749
+ ConfigurationError: () => ConfigurationError,
750
+ DeepSeekAdapter: () => DeepSeekAdapter,
24
751
  EFFORT_TOKEN_MAP: () => EFFORT_TOKEN_MAP,
752
+ FluentValidationError: () => ValidationError,
753
+ GeminiAdapter: () => GeminiAdapter,
25
754
  GeminiProvider: () => GeminiProvider,
755
+ GroqAdapter: () => GroqAdapter,
26
756
  GroqProvider: () => GroqProvider,
757
+ HttpProviderClient: () => HttpProviderClient,
758
+ HuggingFaceAdapter: () => HuggingFaceAdapter,
27
759
  HuggingFaceProvider: () => HuggingFaceProvider,
760
+ ModelScopeAdapter: () => ModelScopeAdapter,
28
761
  ModelScopeProvider: () => ModelScopeProvider,
762
+ NetworkError: () => NetworkError,
763
+ NovaAdapter: () => NovaAdapter,
764
+ OpenRouterAdapter: () => OpenRouterAdapter,
29
765
  OpenRouterProvider: () => OpenRouterProvider,
766
+ PoeAdapter: () => PoeAdapter,
767
+ ProviderError: () => ProviderError,
768
+ ProviderRegistry: () => ProviderRegistry,
769
+ RegistryError: () => RegistryError,
770
+ RequestBuilder: () => RequestBuilder,
771
+ StreamProcessor: () => StreamProcessor,
772
+ TimeoutError: () => TimeoutError,
773
+ VALID_PROVIDERS: () => VALID_PROVIDERS,
30
774
  ai: () => ai,
31
- createProvider: () => createProvider
775
+ createBuilder: () => createBuilder,
776
+ createBuiltInAdapters: () => createBuiltInAdapters,
777
+ createProvider: () => createProvider,
778
+ deepseek: () => deepseek,
779
+ gemini: () => gemini,
780
+ groq: () => groq,
781
+ huggingface: () => huggingface,
782
+ modelscope: () => modelscope,
783
+ nova: () => nova,
784
+ oiiai: () => oiiai,
785
+ openrouter: () => openrouter,
786
+ poe: () => poe
32
787
  });
33
788
  module.exports = __toCommonJS(index_exports);
34
789
 
35
- // src/providers/openrouter.ts
36
- var import_sdk = require("@openrouter/sdk");
37
-
38
- // src/providers/__base__.ts
39
- var BaseProvider = class {
40
- /**
41
- * 简单对话:单轮问答(默认实现)
42
- * 对于思考模型,如果 content 为空则返回 reasoning
43
- */
44
- async ask(model, question, options) {
45
- const result = await this.chat({
46
- model,
47
- messages: [{ role: "user", content: question }],
48
- ...options
49
- });
50
- return result.content || result.reasoning || "";
51
- }
790
+ // src/adapters/types.ts
791
+ var BaseAdapter = class {
52
792
  /**
53
- * 带系统提示的对话(默认实现)
54
- * 对于思考模型,如果 content 为空则返回 reasoning
793
+ * 创建 Provider 客户端
794
+ * 默认实现:需要在运行时导入 HttpProviderClient 以避免循环依赖
55
795
  */
56
- async askWithSystem(model, systemPrompt, userMessage, options) {
57
- const result = await this.chat({
58
- model,
59
- messages: [
60
- { role: "system", content: systemPrompt },
61
- { role: "user", content: userMessage }
62
- ],
63
- ...options
64
- });
65
- return result.content || result.reasoning || "";
66
- }
67
- };
68
-
69
- // src/providers/__types__.ts
70
- var EFFORT_TOKEN_MAP = {
71
- off: 0,
72
- low: 1024,
73
- medium: 4096,
74
- high: 16384
75
- };
76
-
77
- // src/providers/openrouter.ts
78
- function extractTextContent(content) {
79
- if (typeof content === "string") {
80
- return content;
81
- }
82
- if (Array.isArray(content)) {
83
- return content.filter(
84
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
85
- ).map((item) => item.text).join("");
86
- }
87
- return "";
88
- }
89
- function buildReasoningParam(config) {
90
- if (!config) return void 0;
91
- if (config.effort === "off") return void 0;
92
- const param = {};
93
- if (config.effort) {
94
- param.effort = config.effort;
95
- }
96
- if (config.budgetTokens !== void 0) {
97
- param.max_tokens = config.budgetTokens;
98
- } else if (config.effort && EFFORT_TOKEN_MAP[config.effort]) {
99
- param.max_tokens = EFFORT_TOKEN_MAP[config.effort];
100
- }
101
- if (config.exclude !== void 0) {
102
- param.exclude = config.exclude;
103
- }
104
- return Object.keys(param).length > 0 ? param : void 0;
105
- }
106
- var OpenRouterProvider = class extends BaseProvider {
107
- name = "openrouter";
108
- client;
109
- constructor(apiKey) {
110
- super();
111
- this.client = new import_sdk.OpenRouter({ apiKey });
796
+ createClient(config) {
797
+ const { HttpProviderClient: HttpProviderClient2 } = (init_http_provider_client(), __toCommonJS(http_provider_client_exports));
798
+ return new HttpProviderClient2(config);
112
799
  }
113
800
  /**
114
- * 发送聊天请求(非流式)
801
+ * 构建聊天请求体
802
+ * 默认实现:构建 OpenAI 兼容格式的请求体
115
803
  */
116
- async chat(options) {
804
+ buildChatRequest(options, stream = false) {
117
805
  const {
118
806
  model,
119
807
  messages,
@@ -121,40 +809,93 @@ var OpenRouterProvider = class extends BaseProvider {
121
809
  maxTokens,
122
810
  reasoning
123
811
  } = options;
124
- const reasoningParam = buildReasoningParam(reasoning);
125
- const requestParams = {
812
+ const body = {
126
813
  model,
127
814
  messages,
128
815
  temperature,
129
- maxTokens,
130
- stream: false
816
+ stream
131
817
  };
132
- if (reasoningParam) {
133
- requestParams.reasoning = reasoningParam;
818
+ if (maxTokens !== void 0) {
819
+ body.max_tokens = maxTokens;
134
820
  }
135
- const result = await this.client.chat.send(requestParams);
136
- const choice = result.choices[0];
821
+ const reasoningParams = this.buildReasoningParams(reasoning);
822
+ Object.assign(body, reasoningParams);
823
+ return body;
824
+ }
825
+ /**
826
+ * 构建 reasoning 参数
827
+ * 默认实现:返回空对象,子类应覆盖此方法
828
+ */
829
+ buildReasoningParams(_config) {
830
+ return {};
831
+ }
832
+ /**
833
+ * 解析聊天响应
834
+ * 默认实现:解析 OpenAI 兼容格式的响应
835
+ */
836
+ parseChatResponse(response, model) {
837
+ const choices = response.choices;
838
+ const choice = choices?.[0];
137
839
  if (!choice) {
138
840
  throw new Error("No response from model");
139
841
  }
140
842
  const msg = choice.message;
141
- const reasoningContent = msg.reasoning_content ?? msg.reasoning ?? null;
843
+ const reasoningContent = msg?.reasoning_content ?? msg?.reasoning ?? null;
844
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
845
+ const usage = response.usage;
142
846
  return {
143
- content: extractTextContent(msg.content),
144
- reasoning: reasoningContent ? extractTextContent(reasoningContent) : null,
145
- model: result.model,
847
+ content: StreamProcessor2.extractTextContent(msg?.content),
848
+ reasoning: reasoningContent ? StreamProcessor2.extractTextContent(reasoningContent) : null,
849
+ model: response.model ?? model,
146
850
  usage: {
147
- promptTokens: result.usage?.promptTokens ?? 0,
148
- completionTokens: result.usage?.completionTokens ?? 0,
149
- totalTokens: result.usage?.totalTokens ?? 0
851
+ promptTokens: usage?.prompt_tokens ?? usage?.promptTokens ?? 0,
852
+ completionTokens: usage?.completion_tokens ?? usage?.completionTokens ?? 0,
853
+ totalTokens: usage?.total_tokens ?? usage?.totalTokens ?? 0
150
854
  },
151
- finishReason: choice.finishReason
855
+ finishReason: choice.finish_reason ?? choice.finishReason ?? null
152
856
  };
153
857
  }
154
858
  /**
155
- * 发送流式聊天请求
859
+ * 从 delta 中提取 StreamChunk
860
+ * 默认实现:支持 reasoning_content、reasoning、thoughts 和 content 字段
156
861
  */
157
- async *chatStream(options) {
862
+ extractStreamChunk(delta) {
863
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
864
+ const reasoningContent = delta.reasoning_content ?? delta.reasoning ?? delta.thoughts;
865
+ if (reasoningContent) {
866
+ return {
867
+ type: "reasoning",
868
+ text: StreamProcessor2.extractTextContent(reasoningContent)
869
+ };
870
+ }
871
+ if (delta.content) {
872
+ return {
873
+ type: "content",
874
+ text: StreamProcessor2.extractTextContent(delta.content)
875
+ };
876
+ }
877
+ return null;
878
+ }
879
+ /**
880
+ * 获取 API 端点 URL
881
+ * 默认实现:返回 /chat/completions 端点
882
+ */
883
+ getEndpointUrl(baseUrl) {
884
+ return `${baseUrl}/chat/completions`;
885
+ }
886
+ };
887
+
888
+ // src/adapters/openrouter-adapter.ts
889
+ init_request_builder();
890
+ var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
891
+ var OpenRouterAdapter = class extends BaseAdapter {
892
+ name = "openrouter";
893
+ defaultBaseUrl = DEFAULT_BASE_URL;
894
+ /**
895
+ * 构建聊天请求体
896
+ * OpenRouter 使用 OpenAI 兼容格式,但有特殊的 reasoning 参数
897
+ */
898
+ buildChatRequest(options, stream = false) {
158
899
  const {
159
900
  model,
160
901
  messages,
@@ -162,93 +903,54 @@ var OpenRouterProvider = class extends BaseProvider {
162
903
  maxTokens,
163
904
  reasoning
164
905
  } = options;
165
- const reasoningParam = buildReasoningParam(reasoning);
166
- const requestParams = {
906
+ const body = {
167
907
  model,
168
908
  messages,
169
909
  temperature,
170
- maxTokens,
171
- stream: true
910
+ stream
172
911
  };
173
- if (reasoningParam) {
174
- requestParams.reasoning = reasoningParam;
912
+ if (maxTokens !== void 0) {
913
+ body.max_tokens = maxTokens;
175
914
  }
176
- const stream = await this.client.chat.send(
177
- requestParams
178
- );
179
- for await (const chunk of stream) {
180
- const delta = chunk.choices?.[0]?.delta;
181
- if (!delta) continue;
182
- const reasoningContent = delta.reasoning_content ?? delta.reasoning;
183
- if (reasoningContent) {
184
- yield { type: "reasoning", text: extractTextContent(reasoningContent) };
185
- }
186
- if (delta.content) {
187
- yield { type: "content", text: extractTextContent(delta.content) };
188
- }
915
+ const reasoningParam = this.buildReasoningParams(reasoning);
916
+ if (reasoningParam && Object.keys(reasoningParam).length > 0) {
917
+ body.reasoning = reasoningParam;
189
918
  }
919
+ return body;
190
920
  }
191
921
  /**
192
- * 获取可用模型列表
922
+ * 构建 OpenRouter 格式的 reasoning 参数
923
+ *
924
+ * OpenRouter reasoning 参数格式:
925
+ * {
926
+ * effort: 'low' | 'medium' | 'high',
927
+ * max_tokens: number,
928
+ * exclude: boolean
929
+ * }
193
930
  */
194
- async listModels() {
195
- const result = await this.client.models.list();
196
- return (result.data ?? []).map((m) => ({
197
- id: m.id,
198
- canonicalSlug: m.canonical_slug ?? m.id,
199
- name: m.name,
200
- description: m.description ?? "",
201
- created: m.created ?? 0,
202
- pricing: {
203
- prompt: m.pricing?.prompt ?? "0",
204
- completion: m.pricing?.completion ?? "0",
205
- request: m.pricing?.request ?? "0",
206
- image: m.pricing?.image ?? "0"
207
- },
208
- contextLength: m.context_length ?? 0,
209
- architecture: {
210
- modality: m.architecture?.modality ?? "",
211
- inputModalities: m.architecture?.input_modalities ?? [],
212
- outputModalities: m.architecture?.output_modalities ?? [],
213
- tokenizer: m.architecture?.tokenizer ?? "",
214
- instructType: m.architecture?.instruct_type ?? ""
215
- },
216
- supportedParameters: m.supported_parameters ?? []
217
- }));
931
+ buildReasoningParams(config) {
932
+ return RequestBuilder.buildOpenRouterReasoning(config) ?? {};
933
+ }
934
+ /**
935
+ * 获取 API 端点 URL
936
+ * OpenRouter 使用标准的 /chat/completions 端点
937
+ */
938
+ getEndpointUrl(baseUrl) {
939
+ return `${baseUrl}/chat/completions`;
218
940
  }
219
941
  };
220
942
 
221
- // src/providers/gemini.ts
222
- var BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai";
223
- function extractTextContent2(content) {
224
- if (typeof content === "string") {
225
- return content;
226
- }
227
- if (Array.isArray(content)) {
228
- return content.filter(
229
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
230
- ).map((item) => item.text).join("");
231
- }
232
- return "";
233
- }
234
- var GeminiProvider = class extends BaseProvider {
943
+ // src/adapters/gemini-adapter.ts
944
+ init_request_builder();
945
+ var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com/v1beta/openai";
946
+ var GeminiAdapter = class extends BaseAdapter {
235
947
  name = "gemini";
236
- apiKey;
237
- baseUrl;
238
- constructor(config) {
239
- super();
240
- if (typeof config === "string") {
241
- this.apiKey = config;
242
- this.baseUrl = BASE_URL;
243
- } else {
244
- this.apiKey = config.apiKey;
245
- this.baseUrl = config.baseUrl ?? BASE_URL;
246
- }
247
- }
948
+ defaultBaseUrl = DEFAULT_BASE_URL2;
248
949
  /**
249
- * 发送聊天请求(非流式)
950
+ * 构建聊天请求体
951
+ * Gemini 使用 OpenAI 兼容格式,reasoning_effort 直接放在请求体中
250
952
  */
251
- async chat(options) {
953
+ buildChatRequest(options, stream = false) {
252
954
  const {
253
955
  model,
254
956
  messages,
@@ -260,450 +962,201 @@ var GeminiProvider = class extends BaseProvider {
260
962
  model,
261
963
  messages,
262
964
  temperature,
263
- stream: false
965
+ stream
264
966
  };
265
- if (maxTokens) {
967
+ if (maxTokens !== void 0) {
266
968
  body.max_tokens = maxTokens;
267
969
  }
268
- if (reasoning?.effort && reasoning.effort !== "off") {
269
- body.reasoning_effort = reasoning.effort;
270
- }
271
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
272
- method: "POST",
273
- headers: {
274
- "Content-Type": "application/json",
275
- Authorization: `Bearer ${this.apiKey}`
276
- },
277
- body: JSON.stringify(body)
278
- });
279
- if (!response.ok) {
280
- const error = await response.text();
281
- throw new Error(`Gemini API error: ${response.status} ${error}`);
970
+ const reasoningParams = this.buildReasoningParams(reasoning);
971
+ Object.assign(body, reasoningParams);
972
+ return body;
973
+ }
974
+ /**
975
+ * 构建 Gemini 格式的 reasoning 参数
976
+ *
977
+ * Gemini 2.5+ 模型使用 reasoning_effort 参数:
978
+ * - 'low': 快速思考
979
+ * - 'medium': 平衡模式
980
+ * - 'high': 深度思考
981
+ */
982
+ buildReasoningParams(config) {
983
+ return RequestBuilder.buildGeminiReasoning(config);
984
+ }
985
+ /**
986
+ * 从 delta 中提取 StreamChunk
987
+ * Gemini 可能使用 reasoning_content 或 thoughts 字段
988
+ */
989
+ extractStreamChunk(delta) {
990
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
991
+ const reasoningContent = delta.reasoning_content ?? delta.thoughts;
992
+ if (reasoningContent) {
993
+ return {
994
+ type: "reasoning",
995
+ text: StreamProcessor2.extractTextContent(reasoningContent)
996
+ };
282
997
  }
283
- const result = await response.json();
284
- const choice = result.choices?.[0];
285
- if (!choice) {
286
- throw new Error("No response from model");
998
+ if (delta.content) {
999
+ return {
1000
+ type: "content",
1001
+ text: StreamProcessor2.extractTextContent(delta.content)
1002
+ };
287
1003
  }
288
- const msg = choice.message;
289
- const reasoningContent = msg?.reasoning_content ?? null;
290
- return {
291
- content: extractTextContent2(msg?.content),
292
- reasoning: reasoningContent ? extractTextContent2(reasoningContent) : null,
293
- model: result.model ?? model,
294
- usage: {
295
- promptTokens: result.usage?.prompt_tokens ?? 0,
296
- completionTokens: result.usage?.completion_tokens ?? 0,
297
- totalTokens: result.usage?.total_tokens ?? 0
298
- },
299
- finishReason: choice.finish_reason ?? null
300
- };
1004
+ return null;
301
1005
  }
302
1006
  /**
303
- * 发送流式聊天请求
1007
+ * 获取 API 端点 URL
304
1008
  */
305
- async *chatStream(options) {
306
- const {
307
- model,
308
- messages,
309
- temperature = 0.7,
310
- maxTokens,
311
- reasoning
312
- } = options;
1009
+ getEndpointUrl(baseUrl) {
1010
+ return `${baseUrl}/chat/completions`;
1011
+ }
1012
+ };
1013
+
1014
+ // src/adapters/groq-adapter.ts
1015
+ init_request_builder();
1016
+ var DEFAULT_BASE_URL3 = "https://api.groq.com/openai/v1";
1017
+ var GroqAdapter = class extends BaseAdapter {
1018
+ name = "groq";
1019
+ defaultBaseUrl = DEFAULT_BASE_URL3;
1020
+ /**
1021
+ * 构建聊天请求体
1022
+ * Groq 使用 OpenAI 兼容格式,但有一些特殊参数
1023
+ */
1024
+ buildChatRequest(options, stream = false) {
1025
+ const { model, messages, temperature = 1, maxTokens, reasoning } = options;
313
1026
  const body = {
314
1027
  model,
315
1028
  messages,
316
1029
  temperature,
317
- stream: true
1030
+ stream,
1031
+ top_p: 1
318
1032
  };
319
- if (maxTokens) {
320
- body.max_tokens = maxTokens;
321
- }
322
- if (reasoning?.effort && reasoning.effort !== "off") {
323
- body.reasoning_effort = reasoning.effort;
1033
+ if (maxTokens !== void 0) {
1034
+ body.max_completion_tokens = maxTokens;
324
1035
  }
325
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
326
- method: "POST",
327
- headers: {
328
- "Content-Type": "application/json",
329
- Authorization: `Bearer ${this.apiKey}`
330
- },
331
- body: JSON.stringify(body)
332
- });
333
- if (!response.ok) {
334
- const error = await response.text();
335
- throw new Error(`Gemini API error: ${response.status} ${error}`);
336
- }
337
- const reader = response.body?.getReader();
338
- if (!reader) {
339
- throw new Error("No response body");
340
- }
341
- const decoder = new TextDecoder();
342
- let buffer = "";
343
- try {
344
- while (true) {
345
- const { done, value } = await reader.read();
346
- if (done) break;
347
- buffer += decoder.decode(value, { stream: true });
348
- const lines = buffer.split("\n");
349
- buffer = lines.pop() ?? "";
350
- for (const line of lines) {
351
- const trimmed = line.trim();
352
- if (!trimmed || trimmed === "data: [DONE]") continue;
353
- if (!trimmed.startsWith("data: ")) continue;
354
- try {
355
- const data = JSON.parse(trimmed.slice(6));
356
- const delta = data.choices?.[0]?.delta;
357
- if (!delta) continue;
358
- const thought = delta.reasoning_content ?? delta.thoughts;
359
- if (thought) {
360
- yield {
361
- type: "reasoning",
362
- text: extractTextContent2(thought)
363
- };
364
- }
365
- if (delta.content) {
366
- yield {
367
- type: "content",
368
- text: extractTextContent2(delta.content)
369
- };
370
- }
371
- } catch {
372
- }
373
- }
374
- }
375
- } finally {
376
- reader.releaseLock();
377
- }
378
- }
379
- };
380
-
381
- // src/providers/groq.ts
382
- var BASE_URL2 = "https://api.groq.com/openai/v1";
383
- function extractTextContent3(content) {
384
- if (typeof content === "string") {
385
- return content;
1036
+ const reasoningParams = this.buildReasoningParams(reasoning);
1037
+ Object.assign(body, reasoningParams);
1038
+ return body;
386
1039
  }
387
- if (Array.isArray(content)) {
388
- return content.filter(
389
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
390
- ).map((item) => item.text).join("");
391
- }
392
- return "";
393
- }
394
- var GroqProvider = class extends BaseProvider {
395
- name = "groq";
396
- apiKey;
397
- baseUrl;
398
- constructor(config) {
399
- super();
400
- if (typeof config === "string") {
401
- this.apiKey = config;
402
- this.baseUrl = BASE_URL2;
403
- } else {
404
- this.apiKey = config.apiKey;
405
- this.baseUrl = config.baseUrl ?? BASE_URL2;
406
- }
1040
+ /**
1041
+ * 构建 Groq 格式的 reasoning 参数
1042
+ *
1043
+ * Groq 使用 reasoning_format 参数:
1044
+ * - 'raw': 原始格式
1045
+ * - 'parsed': 解析格式(推荐)
1046
+ *
1047
+ * 注意:不能同时使用 include_reasoning reasoning_format
1048
+ */
1049
+ buildReasoningParams(config) {
1050
+ return RequestBuilder.buildGroqReasoning(config);
407
1051
  }
408
1052
  /**
409
- * 发送聊天请求(非流式)
1053
+ * 从 delta 中提取 StreamChunk
1054
+ * Groq 使用 reasoning_content 或 reasoning 字段
410
1055
  */
411
- async chat(options) {
412
- const { model, messages, temperature = 1, maxTokens, reasoning } = options;
413
- const body = {
414
- model,
415
- messages,
416
- temperature,
417
- stream: false,
418
- top_p: 1
419
- };
420
- if (maxTokens) {
421
- body.max_completion_tokens = maxTokens;
422
- }
423
- if (reasoning?.effort && reasoning.effort !== "off") {
424
- body.reasoning_format = "parsed";
425
- } else if (reasoning?.effort === "off") {
426
- body.include_reasoning = false;
1056
+ extractStreamChunk(delta) {
1057
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1058
+ const reasoningContent = delta.reasoning_content ?? delta.reasoning;
1059
+ if (reasoningContent) {
1060
+ return {
1061
+ type: "reasoning",
1062
+ text: StreamProcessor2.extractTextContent(reasoningContent)
1063
+ };
427
1064
  }
428
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
429
- method: "POST",
430
- headers: {
431
- "Content-Type": "application/json",
432
- Authorization: `Bearer ${this.apiKey}`
433
- },
434
- body: JSON.stringify(body)
435
- });
436
- if (!response.ok) {
437
- const error = await response.text();
438
- throw new Error(`Groq API error: ${response.status} ${error}`);
439
- }
440
- const result = await response.json();
441
- const choice = result.choices?.[0];
442
- if (!choice) {
443
- throw new Error("No response from model");
1065
+ if (delta.content) {
1066
+ return {
1067
+ type: "content",
1068
+ text: StreamProcessor2.extractTextContent(delta.content)
1069
+ };
444
1070
  }
445
- const msg = choice.message;
446
- const reasoningContent = msg?.reasoning_content ?? msg?.reasoning ?? null;
447
- return {
448
- content: extractTextContent3(msg?.content),
449
- reasoning: reasoningContent ? extractTextContent3(reasoningContent) : null,
450
- model: result.model ?? model,
451
- usage: {
452
- promptTokens: result.usage?.prompt_tokens ?? 0,
453
- completionTokens: result.usage?.completion_tokens ?? 0,
454
- totalTokens: result.usage?.total_tokens ?? 0
455
- },
456
- finishReason: choice.finish_reason ?? null
457
- };
1071
+ return null;
458
1072
  }
459
1073
  /**
460
- * 发送流式聊天请求
1074
+ * 获取 API 端点 URL
461
1075
  */
462
- async *chatStream(options) {
463
- const { model, messages, temperature = 1, maxTokens, reasoning } = options;
464
- const body = {
465
- model,
466
- messages,
467
- temperature,
468
- stream: true,
469
- top_p: 1
470
- };
471
- if (maxTokens) {
472
- body.max_completion_tokens = maxTokens;
473
- }
474
- if (reasoning?.effort && reasoning.effort !== "off") {
475
- body.reasoning_format = "parsed";
476
- } else if (reasoning?.effort === "off") {
477
- body.include_reasoning = false;
478
- }
479
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
480
- method: "POST",
481
- headers: {
482
- "Content-Type": "application/json",
483
- Authorization: `Bearer ${this.apiKey}`
484
- },
485
- body: JSON.stringify(body)
486
- });
487
- if (!response.ok) {
488
- const error = await response.text();
489
- throw new Error(`Groq API error: ${response.status} ${error}`);
490
- }
491
- const reader = response.body?.getReader();
492
- if (!reader) {
493
- throw new Error("No response body");
494
- }
495
- const decoder = new TextDecoder();
496
- let buffer = "";
497
- try {
498
- while (true) {
499
- const { done, value } = await reader.read();
500
- if (done) break;
501
- buffer += decoder.decode(value, { stream: true });
502
- const lines = buffer.split("\n");
503
- buffer = lines.pop() ?? "";
504
- for (const line of lines) {
505
- const trimmed = line.trim();
506
- if (!trimmed || trimmed === "data: [DONE]") continue;
507
- if (!trimmed.startsWith("data: ")) continue;
508
- try {
509
- const data = JSON.parse(trimmed.slice(6));
510
- const delta = data.choices?.[0]?.delta;
511
- if (!delta) continue;
512
- const reasoningContent = delta.reasoning_content ?? delta.reasoning;
513
- if (reasoningContent) {
514
- yield {
515
- type: "reasoning",
516
- text: extractTextContent3(reasoningContent)
517
- };
518
- }
519
- if (delta.content) {
520
- yield {
521
- type: "content",
522
- text: extractTextContent3(delta.content)
523
- };
524
- }
525
- } catch {
526
- }
527
- }
528
- }
529
- } finally {
530
- reader.releaseLock();
531
- }
1076
+ getEndpointUrl(baseUrl) {
1077
+ return `${baseUrl}/chat/completions`;
532
1078
  }
533
1079
  };
534
1080
 
535
- // src/providers/huggingface.ts
536
- var BASE_URL3 = "https://router.huggingface.co/v1";
537
- function extractTextContent4(content) {
538
- if (typeof content === "string") {
539
- return content;
540
- }
541
- if (Array.isArray(content)) {
542
- return content.filter(
543
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
544
- ).map((item) => item.text).join("");
545
- }
546
- return "";
547
- }
548
- var HuggingFaceProvider = class extends BaseProvider {
1081
+ // src/adapters/huggingface-adapter.ts
1082
+ var DEFAULT_BASE_URL4 = "https://router.huggingface.co/v1";
1083
+ var HuggingFaceAdapter = class extends BaseAdapter {
549
1084
  name = "huggingface";
550
- apiKey;
551
- baseUrl;
552
- constructor(config) {
553
- super();
554
- if (typeof config === "string") {
555
- this.apiKey = config;
556
- this.baseUrl = BASE_URL3;
557
- } else {
558
- this.apiKey = config.apiKey;
559
- this.baseUrl = config.baseUrl ?? BASE_URL3;
560
- }
561
- }
1085
+ defaultBaseUrl = DEFAULT_BASE_URL4;
562
1086
  /**
563
- * 发送聊天请求(非流式)
1087
+ * 构建聊天请求体
1088
+ * HuggingFace 使用标准 OpenAI 兼容格式
564
1089
  */
565
- async chat(options) {
566
- const { model, messages, temperature = 0.7, maxTokens } = options;
1090
+ buildChatRequest(options, stream = false) {
1091
+ const {
1092
+ model,
1093
+ messages,
1094
+ temperature = 0.7,
1095
+ maxTokens,
1096
+ reasoning
1097
+ } = options;
567
1098
  const body = {
568
1099
  model,
569
1100
  messages,
570
1101
  temperature,
571
- stream: false
1102
+ stream
572
1103
  };
573
- if (maxTokens) {
1104
+ if (maxTokens !== void 0) {
574
1105
  body.max_tokens = maxTokens;
575
1106
  }
576
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
577
- method: "POST",
578
- headers: {
579
- "Content-Type": "application/json",
580
- Authorization: `Bearer ${this.apiKey}`
581
- },
582
- body: JSON.stringify(body)
583
- });
584
- if (!response.ok) {
585
- const error = await response.text();
586
- throw new Error(`HuggingFace API error: ${response.status} ${error}`);
587
- }
588
- const result = await response.json();
589
- const choice = result.choices?.[0];
590
- if (!choice) {
591
- throw new Error("No response from model");
1107
+ const reasoningParams = this.buildReasoningParams(reasoning);
1108
+ Object.assign(body, reasoningParams);
1109
+ return body;
1110
+ }
1111
+ /**
1112
+ * 构建 HuggingFace 格式的 reasoning 参数
1113
+ * HuggingFace 使用 reasoning_effort 参数(取决于具体模型是否支持)
1114
+ */
1115
+ buildReasoningParams(config) {
1116
+ if (!config || !config.effort || config.effort === "off") {
1117
+ return {};
592
1118
  }
593
- const msg = choice.message;
594
- const reasoningContent = msg?.reasoning_content ?? null;
595
1119
  return {
596
- content: extractTextContent4(msg?.content),
597
- reasoning: reasoningContent ? extractTextContent4(reasoningContent) : null,
598
- model: result.model ?? model,
599
- usage: {
600
- promptTokens: result.usage?.prompt_tokens ?? 0,
601
- completionTokens: result.usage?.completion_tokens ?? 0,
602
- totalTokens: result.usage?.total_tokens ?? 0
603
- },
604
- finishReason: choice.finish_reason ?? null
1120
+ reasoning_effort: config.effort
605
1121
  };
606
1122
  }
607
1123
  /**
608
- * 发送流式聊天请求
1124
+ * 从 delta 中提取 StreamChunk
609
1125
  */
610
- async *chatStream(options) {
611
- const { model, messages, temperature = 0.7, maxTokens } = options;
612
- const body = {
613
- model,
614
- messages,
615
- temperature,
616
- stream: true
617
- };
618
- if (maxTokens) {
619
- body.max_tokens = maxTokens;
620
- }
621
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
622
- method: "POST",
623
- headers: {
624
- "Content-Type": "application/json",
625
- Authorization: `Bearer ${this.apiKey}`
626
- },
627
- body: JSON.stringify(body)
628
- });
629
- if (!response.ok) {
630
- const error = await response.text();
631
- throw new Error(`HuggingFace API error: ${response.status} ${error}`);
1126
+ extractStreamChunk(delta) {
1127
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1128
+ if (delta.reasoning_content) {
1129
+ return {
1130
+ type: "reasoning",
1131
+ text: StreamProcessor2.extractTextContent(delta.reasoning_content)
1132
+ };
632
1133
  }
633
- const reader = response.body?.getReader();
634
- if (!reader) {
635
- throw new Error("No response body");
636
- }
637
- const decoder = new TextDecoder();
638
- let buffer = "";
639
- try {
640
- while (true) {
641
- const { done, value } = await reader.read();
642
- if (done) break;
643
- buffer += decoder.decode(value, { stream: true });
644
- const lines = buffer.split("\n");
645
- buffer = lines.pop() ?? "";
646
- for (const line of lines) {
647
- const trimmed = line.trim();
648
- if (!trimmed || trimmed === "data: [DONE]") continue;
649
- if (!trimmed.startsWith("data: ")) continue;
650
- try {
651
- const data = JSON.parse(trimmed.slice(6));
652
- const delta = data.choices?.[0]?.delta;
653
- if (!delta) continue;
654
- if (delta.reasoning_content) {
655
- yield {
656
- type: "reasoning",
657
- text: extractTextContent4(delta.reasoning_content)
658
- };
659
- }
660
- if (delta.content) {
661
- yield {
662
- type: "content",
663
- text: extractTextContent4(delta.content)
664
- };
665
- }
666
- } catch {
667
- }
668
- }
669
- }
670
- } finally {
671
- reader.releaseLock();
1134
+ if (delta.content) {
1135
+ return {
1136
+ type: "content",
1137
+ text: StreamProcessor2.extractTextContent(delta.content)
1138
+ };
672
1139
  }
1140
+ return null;
1141
+ }
1142
+ /**
1143
+ * 获取 API 端点 URL
1144
+ */
1145
+ getEndpointUrl(baseUrl) {
1146
+ return `${baseUrl}/chat/completions`;
673
1147
  }
674
1148
  };
675
1149
 
676
- // src/providers/modelscope.ts
677
- var BASE_URL4 = "https://api-inference.modelscope.cn/v1";
678
- function extractTextContent5(content) {
679
- if (typeof content === "string") {
680
- return content;
681
- }
682
- if (Array.isArray(content)) {
683
- return content.filter(
684
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
685
- ).map((item) => item.text).join("");
686
- }
687
- return "";
688
- }
689
- var ModelScopeProvider = class extends BaseProvider {
1150
+ // src/adapters/modelscope-adapter.ts
1151
+ var DEFAULT_BASE_URL5 = "https://api-inference.modelscope.cn/v1";
1152
+ var ModelScopeAdapter = class extends BaseAdapter {
690
1153
  name = "modelscope";
691
- apiKey;
692
- baseUrl;
693
- constructor(config) {
694
- super();
695
- if (typeof config === "string") {
696
- this.apiKey = config;
697
- this.baseUrl = BASE_URL4;
698
- } else {
699
- this.apiKey = config.apiKey;
700
- this.baseUrl = config.baseUrl ?? BASE_URL4;
701
- }
702
- }
1154
+ defaultBaseUrl = DEFAULT_BASE_URL5;
703
1155
  /**
704
- * 发送聊天请求(非流式)
1156
+ * 构建聊天请求体
1157
+ * ModelScope 使用 OpenAI 兼容格式
705
1158
  */
706
- async chat(options) {
1159
+ buildChatRequest(options, stream = false) {
707
1160
  const {
708
1161
  model,
709
1162
  messages,
@@ -715,53 +1168,66 @@ var ModelScopeProvider = class extends BaseProvider {
715
1168
  model,
716
1169
  messages,
717
1170
  temperature,
718
- stream: false
1171
+ stream
719
1172
  };
720
- if (maxTokens) {
1173
+ if (maxTokens !== void 0) {
721
1174
  body.max_tokens = maxTokens;
722
1175
  }
723
- if (reasoning?.effort) {
724
- if (reasoning.effort === "off") {
725
- body.enable_thinking = false;
726
- } else {
727
- body.enable_thinking = true;
728
- }
1176
+ const reasoningParams = this.buildReasoningParams(reasoning);
1177
+ Object.assign(body, reasoningParams);
1178
+ return body;
1179
+ }
1180
+ /**
1181
+ * 构建 ModelScope 格式的 reasoning 参数
1182
+ * ModelScope 使用 enable_thinking 参数控制思考模式
1183
+ */
1184
+ buildReasoningParams(config) {
1185
+ if (!config || !config.effort) {
1186
+ return {};
729
1187
  }
730
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
731
- method: "POST",
732
- headers: {
733
- "Content-Type": "application/json",
734
- Authorization: `Bearer ${this.apiKey}`
735
- },
736
- body: JSON.stringify(body)
737
- });
738
- if (!response.ok) {
739
- const error = await response.text();
740
- throw new Error(`ModelScope API error: ${response.status} ${error}`);
1188
+ if (config.effort === "off") {
1189
+ return { enable_thinking: false };
741
1190
  }
742
- const result = await response.json();
743
- const choice = result.choices?.[0];
744
- if (!choice) {
745
- throw new Error("No response from model");
1191
+ return { enable_thinking: true };
1192
+ }
1193
+ /**
1194
+ * delta 中提取 StreamChunk
1195
+ */
1196
+ extractStreamChunk(delta) {
1197
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1198
+ if (delta.reasoning_content) {
1199
+ return {
1200
+ type: "reasoning",
1201
+ text: StreamProcessor2.extractTextContent(delta.reasoning_content)
1202
+ };
746
1203
  }
747
- const msg = choice.message;
748
- const reasoningContent = msg?.reasoning_content ?? null;
749
- return {
750
- content: extractTextContent5(msg?.content),
751
- reasoning: reasoningContent ? extractTextContent5(reasoningContent) : null,
752
- model: result.model ?? model,
753
- usage: {
754
- promptTokens: result.usage?.prompt_tokens ?? 0,
755
- completionTokens: result.usage?.completion_tokens ?? 0,
756
- totalTokens: result.usage?.total_tokens ?? 0
757
- },
758
- finishReason: choice.finish_reason ?? null
759
- };
1204
+ if (delta.content) {
1205
+ return {
1206
+ type: "content",
1207
+ text: StreamProcessor2.extractTextContent(delta.content)
1208
+ };
1209
+ }
1210
+ return null;
760
1211
  }
761
1212
  /**
762
- * 发送流式聊天请求
1213
+ * 获取 API 端点 URL
763
1214
  */
764
- async *chatStream(options) {
1215
+ getEndpointUrl(baseUrl) {
1216
+ return `${baseUrl}/chat/completions`;
1217
+ }
1218
+ };
1219
+
1220
+ // src/adapters/deepseek-adapter.ts
1221
+ init_request_builder();
1222
+ var DEFAULT_BASE_URL6 = "https://api.deepseek.com";
1223
+ var DeepSeekAdapter = class extends BaseAdapter {
1224
+ name = "deepseek";
1225
+ defaultBaseUrl = DEFAULT_BASE_URL6;
1226
+ /**
1227
+ * 构建聊天请求体
1228
+ * DeepSeek 使用 OpenAI 兼容格式
1229
+ */
1230
+ buildChatRequest(options, stream = false) {
765
1231
  const {
766
1232
  model,
767
1233
  messages,
@@ -773,116 +1239,78 @@ var ModelScopeProvider = class extends BaseProvider {
773
1239
  model,
774
1240
  messages,
775
1241
  temperature,
776
- stream: true
1242
+ stream
777
1243
  };
778
- if (maxTokens) {
1244
+ if (maxTokens !== void 0) {
779
1245
  body.max_tokens = maxTokens;
780
1246
  }
781
- if (reasoning?.effort) {
782
- if (reasoning.effort === "off") {
783
- body.enable_thinking = false;
784
- } else {
785
- body.enable_thinking = true;
786
- }
787
- }
788
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
789
- method: "POST",
790
- headers: {
791
- "Content-Type": "application/json",
792
- Authorization: `Bearer ${this.apiKey}`
793
- },
794
- body: JSON.stringify(body)
795
- });
796
- if (!response.ok) {
797
- const error = await response.text();
798
- throw new Error(`ModelScope API error: ${response.status} ${error}`);
799
- }
800
- const reader = response.body?.getReader();
801
- if (!reader) {
802
- throw new Error("No response body");
1247
+ const reasoningParams = this.buildReasoningParams(reasoning);
1248
+ Object.assign(body, reasoningParams);
1249
+ return body;
1250
+ }
1251
+ /**
1252
+ * 构建 DeepSeek 格式的 reasoning 参数
1253
+ * DeepSeek 使用 thinking 参数启用思考模式
1254
+ */
1255
+ buildReasoningParams(config) {
1256
+ return RequestBuilder.buildDeepSeekReasoning(config);
1257
+ }
1258
+ /**
1259
+ * 从 delta 中提取 StreamChunk
1260
+ * DeepSeek R1 使用 reasoning_content 返回思考过程
1261
+ */
1262
+ extractStreamChunk(delta) {
1263
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1264
+ if (delta.reasoning_content) {
1265
+ return {
1266
+ type: "reasoning",
1267
+ text: StreamProcessor2.extractTextContent(delta.reasoning_content)
1268
+ };
803
1269
  }
804
- const decoder = new TextDecoder();
805
- let buffer = "";
806
- try {
807
- while (true) {
808
- const { done, value } = await reader.read();
809
- if (done) break;
810
- buffer += decoder.decode(value, { stream: true });
811
- const lines = buffer.split("\n");
812
- buffer = lines.pop() ?? "";
813
- for (const line of lines) {
814
- const trimmed = line.trim();
815
- if (!trimmed || trimmed === "data: [DONE]") continue;
816
- if (!trimmed.startsWith("data: ")) continue;
817
- try {
818
- const data = JSON.parse(trimmed.slice(6));
819
- const delta = data.choices?.[0]?.delta;
820
- if (!delta) continue;
821
- if (delta.reasoning_content) {
822
- yield {
823
- type: "reasoning",
824
- text: extractTextContent5(delta.reasoning_content)
825
- };
826
- }
827
- if (delta.content) {
828
- yield {
829
- type: "content",
830
- text: extractTextContent5(delta.content)
831
- };
832
- }
833
- } catch {
834
- }
835
- }
836
- }
837
- } finally {
838
- reader.releaseLock();
1270
+ if (delta.content) {
1271
+ return {
1272
+ type: "content",
1273
+ text: StreamProcessor2.extractTextContent(delta.content)
1274
+ };
839
1275
  }
1276
+ return null;
1277
+ }
1278
+ /**
1279
+ * 获取 API 端点 URL
1280
+ */
1281
+ getEndpointUrl(baseUrl) {
1282
+ return `${baseUrl}/chat/completions`;
840
1283
  }
841
1284
  };
842
1285
 
843
- // src/providers/deepseek.ts
844
- var BASE_URL5 = "https://api.deepseek.com";
845
- function getReasoningMaxTokens(reasoning, userMaxTokens) {
846
- if (!reasoning || reasoning.effort === "off") {
847
- return userMaxTokens;
848
- }
849
- if (reasoning.budgetTokens !== void 0) {
850
- return reasoning.budgetTokens;
851
- }
852
- if (reasoning.effort) {
853
- return EFFORT_TOKEN_MAP[reasoning.effort];
1286
+ // src/adapters/poe-adapter.ts
1287
+ init_types();
1288
+ var DEFAULT_BASE_URL7 = "https://api.poe.com/v1";
1289
+ function extractThinkingFromContent(content) {
1290
+ const thinkMatch = content.match(/<think>([\s\S]*?)<\/think>/);
1291
+ if (thinkMatch) {
1292
+ const thinking = thinkMatch[1].trim();
1293
+ const cleanContent = content.replace(/<think>[\s\S]*?<\/think>/, "").trim();
1294
+ return { thinking, content: cleanContent };
854
1295
  }
855
- return userMaxTokens;
856
- }
857
- function extractTextContent6(content) {
858
- if (typeof content === "string") {
859
- return content;
860
- }
861
- if (Array.isArray(content)) {
862
- return content.filter(
863
- (item) => typeof item === "object" && item !== null && item.type === "text" && typeof item.text === "string"
864
- ).map((item) => item.text).join("");
1296
+ const thinkingMatch = content.match(
1297
+ /^\*Thinking\.{0,3}\*\s*\n((?:>.*(?:\n|$))+)/
1298
+ );
1299
+ if (thinkingMatch) {
1300
+ const thinking = thinkingMatch[1].split("\n").map((line) => line.replace(/^>\s?/, "")).join("\n").trim();
1301
+ const cleanContent = content.replace(thinkingMatch[0], "").trim();
1302
+ return { thinking, content: cleanContent };
865
1303
  }
866
- return "";
1304
+ return { thinking: "", content };
867
1305
  }
868
- var DeepSeekProvider = class extends BaseProvider {
869
- name = "deepseek";
870
- apiKey;
871
- baseUrl;
872
- constructor(config) {
873
- super();
874
- if (typeof config === "string") {
875
- this.apiKey = config;
876
- this.baseUrl = BASE_URL5;
877
- } else {
878
- this.apiKey = config.apiKey;
879
- this.baseUrl = config.baseUrl ?? BASE_URL5;
880
- }
881
- }
1306
+ var PoeAdapter = class extends BaseAdapter {
1307
+ name = "poe";
1308
+ defaultBaseUrl = DEFAULT_BASE_URL7;
882
1309
  /**
883
- * 发送聊天请求(非流式)
1310
+ * 构建聊天请求体
1311
+ * Poe 使用 OpenAI 兼容格式,通过 extra_body 传递自定义参数
884
1312
  */
885
- async chat(options) {
1313
+ buildChatRequest(options, stream = false) {
886
1314
  const {
887
1315
  model,
888
1316
  messages,
@@ -890,54 +1318,111 @@ var DeepSeekProvider = class extends BaseProvider {
890
1318
  maxTokens,
891
1319
  reasoning
892
1320
  } = options;
893
- const effectiveMaxTokens = getReasoningMaxTokens(reasoning, maxTokens);
894
1321
  const body = {
895
1322
  model,
896
1323
  messages,
897
1324
  temperature,
898
- stream: false
1325
+ stream
899
1326
  };
900
- if (effectiveMaxTokens) {
901
- body.max_tokens = effectiveMaxTokens;
1327
+ if (maxTokens !== void 0) {
1328
+ body.max_tokens = maxTokens;
1329
+ }
1330
+ const reasoningParams = this.buildReasoningParams(reasoning);
1331
+ Object.assign(body, reasoningParams);
1332
+ return body;
1333
+ }
1334
+ /**
1335
+ * 构建 Poe 格式的 reasoning 参数
1336
+ * Poe 通过 extra_body 传递 reasoning_effort 和 thinking_budget
1337
+ */
1338
+ buildReasoningParams(config) {
1339
+ if (!config || config.effort === "off") {
1340
+ return {};
902
1341
  }
903
- if (reasoning?.effort && reasoning.effort !== "off") {
904
- body.thinking = { type: "enabled" };
1342
+ const params = {};
1343
+ if (config.effort) {
1344
+ params.reasoning_effort = config.effort;
905
1345
  }
906
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
907
- method: "POST",
908
- headers: {
909
- "Content-Type": "application/json",
910
- Authorization: `Bearer ${this.apiKey}`
911
- },
912
- body: JSON.stringify(body)
913
- });
914
- if (!response.ok) {
915
- const error = await response.text();
916
- throw new Error(`DeepSeek API error: ${response.status} ${error}`);
1346
+ if (config.budgetTokens !== void 0) {
1347
+ params.thinking_budget = config.budgetTokens;
1348
+ } else if (config.effort && EFFORT_TOKEN_MAP[config.effort]) {
1349
+ params.thinking_budget = EFFORT_TOKEN_MAP[config.effort];
917
1350
  }
918
- const result = await response.json();
919
- const choice = result.choices?.[0];
1351
+ return params;
1352
+ }
1353
+ /**
1354
+ * 解析聊天响应
1355
+ * Poe 可能返回 reasoning_content,或者需要从 <think> 标签提取
1356
+ */
1357
+ parseChatResponse(response, model) {
1358
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1359
+ const choices = response.choices;
1360
+ const choice = choices?.[0];
920
1361
  if (!choice) {
921
1362
  throw new Error("No response from model");
922
1363
  }
923
1364
  const msg = choice.message;
924
- const reasoningContent = msg?.reasoning_content ?? null;
1365
+ let reasoningContent = msg?.reasoning_content ?? null;
1366
+ let contentText = StreamProcessor2.extractTextContent(msg?.content);
1367
+ if (!reasoningContent && contentText) {
1368
+ const extracted = extractThinkingFromContent(contentText);
1369
+ if (extracted.thinking) {
1370
+ reasoningContent = extracted.thinking;
1371
+ contentText = extracted.content;
1372
+ }
1373
+ }
1374
+ const usage = response.usage;
925
1375
  return {
926
- content: extractTextContent6(msg?.content),
927
- reasoning: reasoningContent ? extractTextContent6(reasoningContent) : null,
928
- model: result.model ?? model,
1376
+ content: contentText,
1377
+ reasoning: reasoningContent ? StreamProcessor2.extractTextContent(reasoningContent) : null,
1378
+ model: response.model ?? model,
929
1379
  usage: {
930
- promptTokens: result.usage?.prompt_tokens ?? 0,
931
- completionTokens: result.usage?.completion_tokens ?? 0,
932
- totalTokens: result.usage?.total_tokens ?? 0
1380
+ promptTokens: usage?.prompt_tokens ?? usage?.promptTokens ?? 0,
1381
+ completionTokens: usage?.completion_tokens ?? usage?.completionTokens ?? 0,
1382
+ totalTokens: usage?.total_tokens ?? usage?.totalTokens ?? 0
933
1383
  },
934
- finishReason: choice.finish_reason ?? null
1384
+ finishReason: choice.finish_reason ?? choice.finishReason ?? null
935
1385
  };
936
1386
  }
937
1387
  /**
938
- * 发送流式聊天请求
1388
+ * 从 delta 中提取 StreamChunk
1389
+ * Poe 的流式响应处理比较复杂,需要处理多种思考格式
939
1390
  */
940
- async *chatStream(options) {
1391
+ extractStreamChunk(delta) {
1392
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1393
+ if (delta.reasoning_content) {
1394
+ return {
1395
+ type: "reasoning",
1396
+ text: StreamProcessor2.extractTextContent(delta.reasoning_content)
1397
+ };
1398
+ }
1399
+ if (delta.content) {
1400
+ return {
1401
+ type: "content",
1402
+ text: StreamProcessor2.extractTextContent(delta.content)
1403
+ };
1404
+ }
1405
+ return null;
1406
+ }
1407
+ /**
1408
+ * 获取 API 端点 URL
1409
+ */
1410
+ getEndpointUrl(baseUrl) {
1411
+ return `${baseUrl}/chat/completions`;
1412
+ }
1413
+ };
1414
+
1415
+ // src/adapters/nova-adapter.ts
1416
+ init_request_builder();
1417
+ var DEFAULT_BASE_URL8 = "https://api.nova.amazon.com/v1";
1418
+ var NovaAdapter = class extends BaseAdapter {
1419
+ name = "nova";
1420
+ defaultBaseUrl = DEFAULT_BASE_URL8;
1421
+ /**
1422
+ * 构建聊天请求体
1423
+ * Nova 使用 OpenAI 兼容格式
1424
+ */
1425
+ buildChatRequest(options, stream = false) {
941
1426
  const {
942
1427
  model,
943
1428
  messages,
@@ -945,93 +1430,817 @@ var DeepSeekProvider = class extends BaseProvider {
945
1430
  maxTokens,
946
1431
  reasoning
947
1432
  } = options;
948
- const effectiveMaxTokens = getReasoningMaxTokens(reasoning, maxTokens);
949
1433
  const body = {
950
1434
  model,
951
1435
  messages,
952
1436
  temperature,
953
- stream: true
1437
+ stream
954
1438
  };
955
- if (effectiveMaxTokens) {
956
- body.max_tokens = effectiveMaxTokens;
1439
+ if (maxTokens !== void 0) {
1440
+ body.max_tokens = maxTokens;
957
1441
  }
958
- if (reasoning?.effort && reasoning.effort !== "off") {
959
- body.thinking = { type: "enabled" };
1442
+ const reasoningParams = this.buildReasoningParams(reasoning);
1443
+ Object.assign(body, reasoningParams);
1444
+ return body;
1445
+ }
1446
+ /**
1447
+ * 构建 Nova 格式的 reasoning 参数
1448
+ * Nova 使用 reasoningConfig 控制 extended thinking
1449
+ */
1450
+ buildReasoningParams(config) {
1451
+ return RequestBuilder.buildNovaReasoning(config);
1452
+ }
1453
+ /**
1454
+ * 从 delta 中提取 StreamChunk
1455
+ * Nova 返回 reasoning_content 作为思考过程
1456
+ */
1457
+ extractStreamChunk(delta) {
1458
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
1459
+ if (delta.reasoning_content) {
1460
+ return {
1461
+ type: "reasoning",
1462
+ text: StreamProcessor2.extractTextContent(delta.reasoning_content)
1463
+ };
960
1464
  }
961
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
962
- method: "POST",
963
- headers: {
964
- "Content-Type": "application/json",
965
- Authorization: `Bearer ${this.apiKey}`
966
- },
967
- body: JSON.stringify(body)
968
- });
969
- if (!response.ok) {
970
- const error = await response.text();
971
- throw new Error(`DeepSeek API error: ${response.status} ${error}`);
1465
+ if (delta.content) {
1466
+ return {
1467
+ type: "content",
1468
+ text: StreamProcessor2.extractTextContent(delta.content)
1469
+ };
972
1470
  }
973
- const reader = response.body?.getReader();
974
- if (!reader) {
975
- throw new Error("No response body");
1471
+ return null;
1472
+ }
1473
+ /**
1474
+ * 获取 API 端点 URL
1475
+ */
1476
+ getEndpointUrl(baseUrl) {
1477
+ return `${baseUrl}/chat/completions`;
1478
+ }
1479
+ };
1480
+
1481
+ // src/adapters/index.ts
1482
+ function createBuiltInAdapters() {
1483
+ const adapters = /* @__PURE__ */ new Map();
1484
+ adapters.set("openrouter", new OpenRouterAdapter());
1485
+ adapters.set("gemini", new GeminiAdapter());
1486
+ adapters.set("groq", new GroqAdapter());
1487
+ adapters.set("huggingface", new HuggingFaceAdapter());
1488
+ adapters.set("modelscope", new ModelScopeAdapter());
1489
+ adapters.set("deepseek", new DeepSeekAdapter());
1490
+ adapters.set("poe", new PoeAdapter());
1491
+ adapters.set("nova", new NovaAdapter());
1492
+ return adapters;
1493
+ }
1494
+
1495
+ // src/registry/provider-registry.ts
1496
+ var RegistryError = class extends Error {
1497
+ constructor(message, provider, code = "REGISTRY_ERROR") {
1498
+ super(message);
1499
+ this.provider = provider;
1500
+ this.code = code;
1501
+ this.name = "RegistryError";
1502
+ }
1503
+ };
1504
+ var ProviderRegistry = class {
1505
+ /** 适配器映射表 */
1506
+ static adapters = /* @__PURE__ */ new Map();
1507
+ /** 是否已初始化内置适配器 */
1508
+ static initialized = false;
1509
+ /**
1510
+ * 注册 Provider 适配器
1511
+ *
1512
+ * @param adapter - 要注册的适配器实例
1513
+ * @throws RegistryError 如果适配器无效
1514
+ *
1515
+ * @example
1516
+ * ```typescript
1517
+ * const myAdapter = new MyCustomAdapter();
1518
+ * ProviderRegistry.register(myAdapter);
1519
+ * ```
1520
+ */
1521
+ static register(adapter) {
1522
+ if (!adapter) {
1523
+ throw new RegistryError("\u9002\u914D\u5668\u4E0D\u80FD\u4E3A\u7A7A", void 0, "INVALID_ADAPTER");
976
1524
  }
977
- const decoder = new TextDecoder();
978
- let buffer = "";
979
- try {
980
- while (true) {
981
- const { done, value } = await reader.read();
982
- if (done) break;
983
- buffer += decoder.decode(value, { stream: true });
984
- const lines = buffer.split("\n");
985
- buffer = lines.pop() ?? "";
986
- for (const line of lines) {
987
- const trimmed = line.trim();
988
- if (!trimmed || trimmed === "data: [DONE]") continue;
989
- if (!trimmed.startsWith("data: ")) continue;
990
- try {
991
- const data = JSON.parse(trimmed.slice(6));
992
- const delta = data.choices?.[0]?.delta;
993
- if (!delta) continue;
994
- if (delta.reasoning_content) {
995
- yield {
996
- type: "reasoning",
997
- text: extractTextContent6(delta.reasoning_content)
998
- };
999
- }
1000
- if (delta.content) {
1001
- yield {
1002
- type: "content",
1003
- text: extractTextContent6(delta.content)
1004
- };
1005
- }
1006
- } catch {
1525
+ if (!adapter.name) {
1526
+ throw new RegistryError(
1527
+ "\u9002\u914D\u5668\u5FC5\u987B\u6709 name \u5C5E\u6027",
1528
+ void 0,
1529
+ "INVALID_ADAPTER"
1530
+ );
1531
+ }
1532
+ this.adapters.set(adapter.name, adapter);
1533
+ }
1534
+ /**
1535
+ * 获取 Provider 适配器
1536
+ *
1537
+ * @param type - Provider 类型
1538
+ * @returns 对应的适配器实例
1539
+ * @throws RegistryError 如果 Provider 未注册
1540
+ *
1541
+ * @example
1542
+ * ```typescript
1543
+ * const adapter = ProviderRegistry.getAdapter('openrouter');
1544
+ * const client = adapter.createClient(config);
1545
+ * ```
1546
+ */
1547
+ static getAdapter(type) {
1548
+ this.initializeBuiltIn();
1549
+ const adapter = this.adapters.get(type);
1550
+ if (!adapter) {
1551
+ const supported = this.listSupported();
1552
+ throw new RegistryError(
1553
+ `Provider "${type}" \u672A\u6CE8\u518C\u3002\u53EF\u7528\u7684 Provider: ${supported.join(", ")}`,
1554
+ type,
1555
+ "PROVIDER_NOT_FOUND"
1556
+ );
1557
+ }
1558
+ return adapter;
1559
+ }
1560
+ /**
1561
+ * 检查 Provider 是否已注册
1562
+ *
1563
+ * @param type - Provider 类型
1564
+ * @returns 是否已注册
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * if (ProviderRegistry.hasAdapter('gemini')) {
1569
+ * console.log('Gemini 已注册');
1570
+ * }
1571
+ * ```
1572
+ */
1573
+ static hasAdapter(type) {
1574
+ this.initializeBuiltIn();
1575
+ return this.adapters.has(type);
1576
+ }
1577
+ /**
1578
+ * 获取所有已注册的 Provider 类型
1579
+ *
1580
+ * @returns Provider 类型数组
1581
+ *
1582
+ * @example
1583
+ * ```typescript
1584
+ * const providers = ProviderRegistry.listSupported();
1585
+ * console.log('支持的 Provider:', providers);
1586
+ * ```
1587
+ */
1588
+ static listSupported() {
1589
+ this.initializeBuiltIn();
1590
+ return Array.from(this.adapters.keys());
1591
+ }
1592
+ /**
1593
+ * 从配置文件加载并注册 Provider
1594
+ *
1595
+ * @param config - 注册表配置
1596
+ * @throws RegistryError 如果配置无效或加载失败
1597
+ *
1598
+ * @example
1599
+ * ```typescript
1600
+ * const config: RegistryConfig = {
1601
+ * providers: {
1602
+ * 'custom-provider': {
1603
+ * adapter: './my-adapter',
1604
+ * config: {
1605
+ * apiKey: 'xxx',
1606
+ * baseUrl: 'https://api.example.com'
1607
+ * }
1608
+ * }
1609
+ * }
1610
+ * };
1611
+ * ProviderRegistry.loadFromConfig(config);
1612
+ * ```
1613
+ */
1614
+ static loadFromConfig(config) {
1615
+ if (!config || !config.providers) {
1616
+ throw new RegistryError(
1617
+ "\u914D\u7F6E\u65E0\u6548\uFF1A\u7F3A\u5C11 providers \u5B57\u6BB5",
1618
+ void 0,
1619
+ "INVALID_CONFIG"
1620
+ );
1621
+ }
1622
+ this.initializeBuiltIn();
1623
+ for (const [providerName, providerConfig] of Object.entries(
1624
+ config.providers
1625
+ )) {
1626
+ if (providerConfig.adapter) {
1627
+ try {
1628
+ const CustomAdapter = require(providerConfig.adapter);
1629
+ const AdapterClass = CustomAdapter.default || CustomAdapter;
1630
+ const adapter = new AdapterClass();
1631
+ if (typeof adapter.name !== "string" || typeof adapter.createClient !== "function") {
1632
+ throw new RegistryError(
1633
+ `\u81EA\u5B9A\u4E49\u9002\u914D\u5668 "${providerConfig.adapter}" \u672A\u6B63\u786E\u5B9E\u73B0 ProviderAdapter \u63A5\u53E3`,
1634
+ providerName,
1635
+ "INVALID_ADAPTER"
1636
+ );
1637
+ }
1638
+ this.adapters.set(providerName, adapter);
1639
+ } catch (error) {
1640
+ if (error instanceof RegistryError) {
1641
+ throw error;
1642
+ }
1643
+ throw new RegistryError(
1644
+ `\u52A0\u8F7D\u81EA\u5B9A\u4E49\u9002\u914D\u5668\u5931\u8D25: ${providerConfig.adapter}`,
1645
+ providerName,
1646
+ "ADAPTER_LOAD_ERROR"
1647
+ );
1648
+ }
1649
+ } else if (!this.adapters.has(providerName)) {
1650
+ throw new RegistryError(
1651
+ `Provider "${providerName}" \u672A\u6CE8\u518C\u4E14\u672A\u6307\u5B9A\u81EA\u5B9A\u4E49\u9002\u914D\u5668`,
1652
+ providerName,
1653
+ "PROVIDER_NOT_FOUND"
1654
+ );
1655
+ }
1656
+ }
1657
+ }
1658
+ /**
1659
+ * 初始化内置 Provider
1660
+ * 在首次使用时自动调用
1661
+ *
1662
+ * @example
1663
+ * ```typescript
1664
+ * // 通常不需要手动调用,会在首次使用时自动初始化
1665
+ * ProviderRegistry.initializeBuiltIn();
1666
+ * ```
1667
+ */
1668
+ static initializeBuiltIn() {
1669
+ if (this.initialized) {
1670
+ return;
1671
+ }
1672
+ const builtInAdapters = createBuiltInAdapters();
1673
+ for (const [type, adapter] of builtInAdapters) {
1674
+ if (!this.adapters.has(type)) {
1675
+ this.adapters.set(type, adapter);
1676
+ }
1677
+ }
1678
+ this.initialized = true;
1679
+ }
1680
+ /**
1681
+ * 重置注册表(主要用于测试)
1682
+ * 清除所有已注册的适配器并重置初始化状态
1683
+ */
1684
+ static reset() {
1685
+ this.adapters.clear();
1686
+ this.initialized = false;
1687
+ }
1688
+ /**
1689
+ * 获取适配器数量(主要用于测试)
1690
+ *
1691
+ * @returns 已注册的适配器数量
1692
+ */
1693
+ static get size() {
1694
+ this.initializeBuiltIn();
1695
+ return this.adapters.size;
1696
+ }
1697
+ };
1698
+
1699
+ // src/config/types.ts
1700
+ var CONFIG_DEFAULTS = {
1701
+ /** 默认超时时间(毫秒) */
1702
+ timeout: 3e4,
1703
+ /** 默认重试次数 */
1704
+ retries: 3,
1705
+ /** 默认功能开关 */
1706
+ features: {
1707
+ streaming: true,
1708
+ reasoning: false
1709
+ }
1710
+ };
1711
+ var VALID_PROVIDERS = [
1712
+ "openrouter",
1713
+ "gemini",
1714
+ "groq",
1715
+ "huggingface",
1716
+ "modelscope",
1717
+ "deepseek",
1718
+ "poe",
1719
+ "nova"
1720
+ ];
1721
+
1722
+ // src/utils/config-validator.ts
1723
+ var VALID_PROVIDERS2 = [
1724
+ "openrouter",
1725
+ "gemini",
1726
+ "groq",
1727
+ "huggingface",
1728
+ "modelscope",
1729
+ "deepseek",
1730
+ "poe",
1731
+ "nova"
1732
+ ];
1733
+ var ConfigValidator = class _ConfigValidator {
1734
+ /**
1735
+ * 验证 Provider 配置
1736
+ *
1737
+ * @param config - 要验证的配置对象
1738
+ * @returns 验证结果
1739
+ *
1740
+ * @example
1741
+ * ```ts
1742
+ * const result = ConfigValidator.validate({
1743
+ * provider: 'openrouter',
1744
+ * credentials: { apiKey: 'sk-xxx' }
1745
+ * });
1746
+ *
1747
+ * if (!result.valid) {
1748
+ * console.error(result.errors);
1749
+ * }
1750
+ * ```
1751
+ */
1752
+ static validate(config) {
1753
+ const errors = [];
1754
+ if (!config || typeof config !== "object") {
1755
+ return {
1756
+ valid: false,
1757
+ errors: [
1758
+ {
1759
+ field: "",
1760
+ message: "\u914D\u7F6E\u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61",
1761
+ code: "INVALID_CONFIG_TYPE"
1762
+ }
1763
+ ]
1764
+ };
1765
+ }
1766
+ const cfg = config;
1767
+ if (!cfg.provider) {
1768
+ errors.push({
1769
+ field: "provider",
1770
+ message: "provider \u5B57\u6BB5\u662F\u5FC5\u586B\u7684",
1771
+ code: "MISSING_PROVIDER"
1772
+ });
1773
+ } else if (typeof cfg.provider !== "string") {
1774
+ errors.push({
1775
+ field: "provider",
1776
+ message: "provider \u5FC5\u987B\u662F\u5B57\u7B26\u4E32",
1777
+ code: "INVALID_PROVIDER_TYPE"
1778
+ });
1779
+ } else if (!VALID_PROVIDERS2.includes(cfg.provider)) {
1780
+ errors.push({
1781
+ field: "provider",
1782
+ message: `\u65E0\u6548\u7684 provider: ${cfg.provider}\uFF0C\u6709\u6548\u503C\u4E3A: ${VALID_PROVIDERS2.join(", ")}`,
1783
+ code: "INVALID_PROVIDER"
1784
+ });
1785
+ }
1786
+ if (!cfg.credentials) {
1787
+ errors.push({
1788
+ field: "credentials",
1789
+ message: "credentials \u5B57\u6BB5\u662F\u5FC5\u586B\u7684",
1790
+ code: "MISSING_CREDENTIALS"
1791
+ });
1792
+ } else if (typeof cfg.credentials !== "object") {
1793
+ errors.push({
1794
+ field: "credentials",
1795
+ message: "credentials \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61",
1796
+ code: "INVALID_CREDENTIALS_TYPE"
1797
+ });
1798
+ } else {
1799
+ const creds = cfg.credentials;
1800
+ if (!creds.apiKey) {
1801
+ errors.push({
1802
+ field: "credentials.apiKey",
1803
+ message: "apiKey \u5B57\u6BB5\u662F\u5FC5\u586B\u7684",
1804
+ code: "MISSING_API_KEY"
1805
+ });
1806
+ } else if (typeof creds.apiKey !== "string") {
1807
+ errors.push({
1808
+ field: "credentials.apiKey",
1809
+ message: "apiKey \u5FC5\u987B\u662F\u5B57\u7B26\u4E32",
1810
+ code: "INVALID_API_KEY_TYPE"
1811
+ });
1812
+ } else if (creds.apiKey.trim() === "") {
1813
+ errors.push({
1814
+ field: "credentials.apiKey",
1815
+ message: "apiKey \u4E0D\u80FD\u4E3A\u7A7A",
1816
+ code: "EMPTY_API_KEY"
1817
+ });
1818
+ }
1819
+ if (creds.baseUrl !== void 0) {
1820
+ const urlResult = _ConfigValidator.validateUrl(creds.baseUrl);
1821
+ if (!urlResult.valid) {
1822
+ errors.push(
1823
+ ...urlResult.errors.map((e) => ({
1824
+ ...e,
1825
+ field: "credentials.baseUrl"
1826
+ }))
1827
+ );
1828
+ }
1829
+ }
1830
+ }
1831
+ if (cfg.options !== void 0) {
1832
+ if (typeof cfg.options !== "object") {
1833
+ errors.push({
1834
+ field: "options",
1835
+ message: "options \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61",
1836
+ code: "INVALID_OPTIONS_TYPE"
1837
+ });
1838
+ } else {
1839
+ const opts = cfg.options;
1840
+ if (opts.timeout !== void 0) {
1841
+ if (typeof opts.timeout !== "number" || opts.timeout <= 0) {
1842
+ errors.push({
1843
+ field: "options.timeout",
1844
+ message: "timeout \u5FC5\u987B\u662F\u6B63\u6570",
1845
+ code: "INVALID_TIMEOUT"
1846
+ });
1847
+ }
1848
+ }
1849
+ if (opts.retries !== void 0) {
1850
+ if (typeof opts.retries !== "number" || opts.retries < 0 || !Number.isInteger(opts.retries)) {
1851
+ errors.push({
1852
+ field: "options.retries",
1853
+ message: "retries \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570",
1854
+ code: "INVALID_RETRIES"
1855
+ });
1007
1856
  }
1008
1857
  }
1009
1858
  }
1010
- } finally {
1011
- reader.releaseLock();
1012
1859
  }
1860
+ return {
1861
+ valid: errors.length === 0,
1862
+ errors
1863
+ };
1864
+ }
1865
+ /**
1866
+ * 验证 API Key 格式
1867
+ * 不同 Provider 可能有不同的 API Key 格式要求
1868
+ *
1869
+ * @param apiKey - API 密钥
1870
+ * @param provider - Provider 类型
1871
+ * @returns 验证结果
1872
+ */
1873
+ static validateApiKey(apiKey, provider) {
1874
+ const errors = [];
1875
+ if (!apiKey || typeof apiKey !== "string") {
1876
+ errors.push({
1877
+ field: "apiKey",
1878
+ message: "apiKey \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32",
1879
+ code: "INVALID_API_KEY"
1880
+ });
1881
+ return { valid: false, errors };
1882
+ }
1883
+ const trimmed = apiKey.trim();
1884
+ if (trimmed === "") {
1885
+ errors.push({
1886
+ field: "apiKey",
1887
+ message: "apiKey \u4E0D\u80FD\u4E3A\u7A7A",
1888
+ code: "EMPTY_API_KEY"
1889
+ });
1890
+ return { valid: false, errors };
1891
+ }
1892
+ switch (provider) {
1893
+ case "openrouter":
1894
+ if (!trimmed.startsWith("sk-")) {
1895
+ errors.push({
1896
+ field: "apiKey",
1897
+ message: "OpenRouter API Key \u5E94\u4EE5 sk- \u5F00\u5934",
1898
+ code: "INVALID_API_KEY_FORMAT"
1899
+ });
1900
+ }
1901
+ break;
1902
+ case "gemini":
1903
+ if (!trimmed.startsWith("AI")) {
1904
+ errors.push({
1905
+ field: "apiKey",
1906
+ message: "Gemini API Key \u683C\u5F0F\u53EF\u80FD\u4E0D\u6B63\u786E",
1907
+ code: "INVALID_API_KEY_FORMAT"
1908
+ });
1909
+ }
1910
+ break;
1911
+ // 其他 Provider 暂不做特定格式验证
1912
+ default:
1913
+ break;
1914
+ }
1915
+ return {
1916
+ valid: errors.length === 0,
1917
+ errors
1918
+ };
1919
+ }
1920
+ /**
1921
+ * 验证 URL 格式
1922
+ *
1923
+ * @param url - 要验证的 URL
1924
+ * @returns 验证结果
1925
+ */
1926
+ static validateUrl(url) {
1927
+ const errors = [];
1928
+ if (typeof url !== "string") {
1929
+ errors.push({
1930
+ field: "url",
1931
+ message: "URL \u5FC5\u987B\u662F\u5B57\u7B26\u4E32",
1932
+ code: "INVALID_URL_TYPE"
1933
+ });
1934
+ return { valid: false, errors };
1935
+ }
1936
+ const trimmed = url.trim();
1937
+ if (trimmed === "") {
1938
+ errors.push({
1939
+ field: "url",
1940
+ message: "URL \u4E0D\u80FD\u4E3A\u7A7A",
1941
+ code: "EMPTY_URL"
1942
+ });
1943
+ return { valid: false, errors };
1944
+ }
1945
+ try {
1946
+ const parsed = new URL(trimmed);
1947
+ if (!["http:", "https:"].includes(parsed.protocol)) {
1948
+ errors.push({
1949
+ field: "url",
1950
+ message: "URL \u5FC5\u987B\u4F7F\u7528 http \u6216 https \u534F\u8BAE",
1951
+ code: "INVALID_URL_PROTOCOL"
1952
+ });
1953
+ }
1954
+ } catch {
1955
+ errors.push({
1956
+ field: "url",
1957
+ message: "URL \u683C\u5F0F\u65E0\u6548",
1958
+ code: "INVALID_URL_FORMAT"
1959
+ });
1960
+ }
1961
+ return {
1962
+ valid: errors.length === 0,
1963
+ errors
1964
+ };
1965
+ }
1966
+ };
1967
+
1968
+ // src/config/config-manager.ts
1969
+ var ConfigManager = class _ConfigManager {
1970
+ /**
1971
+ * 验证配置
1972
+ * 检查配置是否符合 UnifiedProviderConfig 格式要求
1973
+ *
1974
+ * @param config - 要验证的配置对象
1975
+ * @returns 验证结果
1976
+ *
1977
+ * @example
1978
+ * ```ts
1979
+ * const result = ConfigManager.validate({
1980
+ * provider: 'openrouter',
1981
+ * credentials: { apiKey: 'sk-xxx' }
1982
+ * });
1983
+ *
1984
+ * if (!result.valid) {
1985
+ * console.error(result.errors);
1986
+ * }
1987
+ * ```
1988
+ */
1989
+ static validate(config) {
1990
+ return ConfigValidator.validate(config);
1991
+ }
1992
+ /**
1993
+ * 应用默认值
1994
+ * 为缺失的可选字段填充默认值
1995
+ *
1996
+ * @param config - 部分配置对象
1997
+ * @returns 填充默认值后的完整配置
1998
+ *
1999
+ * @example
2000
+ * ```ts
2001
+ * const fullConfig = ConfigManager.applyDefaults({
2002
+ * provider: 'openrouter',
2003
+ * credentials: { apiKey: 'sk-xxx' }
2004
+ * });
2005
+ * // fullConfig.options.timeout === 30000
2006
+ * // fullConfig.options.retries === 3
2007
+ * ```
2008
+ */
2009
+ static applyDefaults(config) {
2010
+ if (!config.provider || !config.credentials?.apiKey) {
2011
+ throw new Error("\u914D\u7F6E\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: provider \u548C credentials.apiKey");
2012
+ }
2013
+ return {
2014
+ provider: config.provider,
2015
+ adapter: config.adapter,
2016
+ credentials: {
2017
+ apiKey: config.credentials.apiKey,
2018
+ baseUrl: config.credentials.baseUrl
2019
+ },
2020
+ options: {
2021
+ timeout: config.options?.timeout ?? CONFIG_DEFAULTS.timeout,
2022
+ retries: config.options?.retries ?? CONFIG_DEFAULTS.retries,
2023
+ headers: config.options?.headers ?? {}
2024
+ },
2025
+ features: {
2026
+ streaming: config.features?.streaming ?? CONFIG_DEFAULTS.features.streaming,
2027
+ reasoning: config.features?.reasoning ?? CONFIG_DEFAULTS.features.reasoning
2028
+ }
2029
+ };
2030
+ }
2031
+ /**
2032
+ * 合并环境变量
2033
+ * 将 ${ENV_VAR} 格式的占位符替换为实际环境变量值
2034
+ *
2035
+ * @param config - 包含环境变量占位符的配置
2036
+ * @returns 替换后的配置
2037
+ *
2038
+ * @example
2039
+ * ```ts
2040
+ * // 假设 process.env.OPENROUTER_API_KEY = 'sk-xxx'
2041
+ * const config = ConfigManager.mergeWithEnv({
2042
+ * provider: 'openrouter',
2043
+ * credentials: { apiKey: '${OPENROUTER_API_KEY}' }
2044
+ * });
2045
+ * // config.credentials.apiKey === 'sk-xxx'
2046
+ * ```
2047
+ */
2048
+ static mergeWithEnv(config) {
2049
+ const result = JSON.parse(JSON.stringify(config));
2050
+ const replaceEnvVars = (obj) => {
2051
+ for (const key of Object.keys(obj)) {
2052
+ const value = obj[key];
2053
+ if (typeof value === "string") {
2054
+ obj[key] = _ConfigManager.replaceEnvPlaceholders(value);
2055
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2056
+ replaceEnvVars(value);
2057
+ }
2058
+ }
2059
+ };
2060
+ replaceEnvVars(result);
2061
+ return result;
2062
+ }
2063
+ /**
2064
+ * 替换字符串中的环境变量占位符
2065
+ * 支持 ${ENV_VAR} 格式
2066
+ *
2067
+ * @param str - 包含占位符的字符串
2068
+ * @returns 替换后的字符串
2069
+ */
2070
+ static replaceEnvPlaceholders(str) {
2071
+ const envVarPattern = /\$\{([^}]+)\}/g;
2072
+ return str.replace(envVarPattern, (match, envVarName) => {
2073
+ const envValue = process.env[envVarName];
2074
+ return envValue !== void 0 ? envValue : match;
2075
+ });
2076
+ }
2077
+ /**
2078
+ * 从旧格式配置转换为新格式
2079
+ * 保持向后兼容性
2080
+ *
2081
+ * @param config - 旧格式的 Provider 配置
2082
+ * @returns 新格式的统一配置
2083
+ *
2084
+ * @example
2085
+ * ```ts
2086
+ * const newConfig = ConfigManager.fromLegacyConfig({
2087
+ * provider: 'openrouter',
2088
+ * apiKey: 'sk-xxx',
2089
+ * baseUrl: 'https://api.example.com'
2090
+ * });
2091
+ * // newConfig.credentials.apiKey === 'sk-xxx'
2092
+ * // newConfig.credentials.baseUrl === 'https://api.example.com'
2093
+ * ```
2094
+ */
2095
+ static fromLegacyConfig(config) {
2096
+ if (!config.provider) {
2097
+ throw new Error("\u65E7\u683C\u5F0F\u914D\u7F6E\u7F3A\u5C11 provider \u5B57\u6BB5");
2098
+ }
2099
+ if (!config.apiKey) {
2100
+ throw new Error("\u65E7\u683C\u5F0F\u914D\u7F6E\u7F3A\u5C11 apiKey \u5B57\u6BB5");
2101
+ }
2102
+ if (!VALID_PROVIDERS.includes(config.provider)) {
2103
+ throw new Error(
2104
+ `\u65E0\u6548\u7684 provider: ${config.provider}\uFF0C\u6709\u6548\u503C\u4E3A: ${VALID_PROVIDERS.join(", ")}`
2105
+ );
2106
+ }
2107
+ return _ConfigManager.applyDefaults({
2108
+ provider: config.provider,
2109
+ credentials: {
2110
+ apiKey: config.apiKey,
2111
+ baseUrl: config.baseUrl
2112
+ }
2113
+ });
2114
+ }
2115
+ /**
2116
+ * 检查配置是否为旧格式
2117
+ *
2118
+ * @param config - 要检查的配置对象
2119
+ * @returns 是否为旧格式
2120
+ */
2121
+ static isLegacyConfig(config) {
2122
+ if (!config || typeof config !== "object") {
2123
+ return false;
2124
+ }
2125
+ const cfg = config;
2126
+ return typeof cfg.provider === "string" && typeof cfg.apiKey === "string" && cfg.credentials === void 0;
2127
+ }
2128
+ /**
2129
+ * 智能转换配置
2130
+ * 自动检测配置格式并转换为统一格式
2131
+ *
2132
+ * @param config - 任意格式的配置
2133
+ * @returns 统一格式的配置
2134
+ */
2135
+ static normalize(config) {
2136
+ if (_ConfigManager.isLegacyConfig(config)) {
2137
+ return _ConfigManager.fromLegacyConfig(config);
2138
+ }
2139
+ return _ConfigManager.applyDefaults(
2140
+ config
2141
+ );
2142
+ }
2143
+ /**
2144
+ * 获取指定 Provider 的默认基础 URL
2145
+ *
2146
+ * @param provider - Provider 类型
2147
+ * @returns 默认基础 URL
2148
+ */
2149
+ static getDefaultBaseUrl(provider) {
2150
+ const defaultUrls = {
2151
+ openrouter: "https://openrouter.ai/api/v1",
2152
+ gemini: "https://generativelanguage.googleapis.com/v1beta",
2153
+ groq: "https://api.groq.com/openai/v1",
2154
+ huggingface: "https://api-inference.huggingface.co",
2155
+ modelscope: "https://dashscope.aliyuncs.com/compatible-mode/v1",
2156
+ deepseek: "https://api.deepseek.com/v1",
2157
+ poe: "https://api.poe.com/bot",
2158
+ nova: "https://bedrock-runtime.us-east-1.amazonaws.com"
2159
+ };
2160
+ return defaultUrls[provider];
1013
2161
  }
1014
2162
  };
1015
2163
 
1016
2164
  // src/providers/__factory__.ts
1017
- function createProvider(config) {
1018
- const { provider, apiKey, baseUrl } = config;
1019
- switch (provider) {
1020
- case "openrouter":
1021
- return new OpenRouterProvider(apiKey);
1022
- case "gemini":
1023
- return new GeminiProvider(baseUrl ? { apiKey, baseUrl } : apiKey);
1024
- case "groq":
1025
- return new GroqProvider(baseUrl ? { apiKey, baseUrl } : apiKey);
1026
- case "huggingface":
1027
- return new HuggingFaceProvider(baseUrl ? { apiKey, baseUrl } : apiKey);
1028
- case "modelscope":
1029
- return new ModelScopeProvider(baseUrl ? { apiKey, baseUrl } : apiKey);
1030
- case "deepseek":
1031
- return new DeepSeekProvider(baseUrl ? { apiKey, baseUrl } : apiKey);
1032
- default:
1033
- throw new Error(`Unknown provider: ${provider}`);
2165
+ var AdapterBasedProvider = class {
2166
+ constructor(adapter, apiKey, baseUrl) {
2167
+ this.adapter = adapter;
2168
+ this.apiKey = apiKey;
2169
+ this.baseUrl = baseUrl;
2170
+ this.name = adapter.name;
2171
+ }
2172
+ name;
2173
+ /**
2174
+ * 获取客户端实例
2175
+ */
2176
+ getClient() {
2177
+ return this.adapter.createClient({
2178
+ apiKey: this.apiKey,
2179
+ baseUrl: this.baseUrl ?? this.adapter.defaultBaseUrl
2180
+ });
2181
+ }
2182
+ /**
2183
+ * 发送聊天请求(非流式)
2184
+ */
2185
+ async chat(options) {
2186
+ const client = this.getClient();
2187
+ const baseUrl = this.baseUrl ?? this.adapter.defaultBaseUrl;
2188
+ const endpoint = this.adapter.getEndpointUrl(baseUrl);
2189
+ const endpointPath = endpoint.replace(baseUrl, "");
2190
+ const body = this.adapter.buildChatRequest(options, false);
2191
+ const response = await client.chat(endpointPath, body);
2192
+ return this.adapter.parseChatResponse(response, options.model);
2193
+ }
2194
+ /**
2195
+ * 发送流式聊天请求
2196
+ */
2197
+ async *chatStream(options) {
2198
+ const client = this.getClient();
2199
+ const baseUrl = this.baseUrl ?? this.adapter.defaultBaseUrl;
2200
+ const endpoint = this.adapter.getEndpointUrl(baseUrl);
2201
+ const endpointPath = endpoint.replace(baseUrl, "");
2202
+ const body = this.adapter.buildChatRequest(options, true);
2203
+ const response = await client.chatStream(endpointPath, body);
2204
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
2205
+ yield* StreamProcessor2.processStream(
2206
+ response,
2207
+ (delta) => this.adapter.extractStreamChunk(delta)
2208
+ );
2209
+ }
2210
+ /**
2211
+ * 简单对话:单轮问答
2212
+ */
2213
+ async ask(model, question, options) {
2214
+ const result = await this.chat({
2215
+ model,
2216
+ messages: [{ role: "user", content: question }],
2217
+ ...options
2218
+ });
2219
+ return result.content;
2220
+ }
2221
+ /**
2222
+ * 带系统提示的对话
2223
+ */
2224
+ async askWithSystem(model, systemPrompt, userMessage, options) {
2225
+ const result = await this.chat({
2226
+ model,
2227
+ messages: [
2228
+ { role: "system", content: systemPrompt },
2229
+ { role: "user", content: userMessage }
2230
+ ],
2231
+ ...options
2232
+ });
2233
+ return result.content;
1034
2234
  }
2235
+ };
2236
+ function createProvider(config) {
2237
+ const unifiedConfig = ConfigManager.fromLegacyConfig(config);
2238
+ const adapter = ProviderRegistry.getAdapter(unifiedConfig.provider);
2239
+ return new AdapterBasedProvider(
2240
+ adapter,
2241
+ unifiedConfig.credentials.apiKey,
2242
+ unifiedConfig.credentials.baseUrl
2243
+ );
1035
2244
  }
1036
2245
  var ai = {
1037
2246
  openrouter: (apiKey, baseUrl) => createProvider({ provider: "openrouter", apiKey, baseUrl }),
@@ -1039,18 +2248,1166 @@ var ai = {
1039
2248
  groq: (apiKey, baseUrl) => createProvider({ provider: "groq", apiKey, baseUrl }),
1040
2249
  huggingface: (apiKey, baseUrl) => createProvider({ provider: "huggingface", apiKey, baseUrl }),
1041
2250
  modelscope: (apiKey, baseUrl) => createProvider({ provider: "modelscope", apiKey, baseUrl }),
1042
- deepseek: (apiKey, baseUrl) => createProvider({ provider: "deepseek", apiKey, baseUrl })
2251
+ deepseek: (apiKey, baseUrl) => createProvider({ provider: "deepseek", apiKey, baseUrl }),
2252
+ poe: (apiKey, baseUrl) => createProvider({ provider: "poe", apiKey, baseUrl }),
2253
+ nova: (apiKey, baseUrl) => createProvider({ provider: "nova", apiKey, baseUrl })
2254
+ };
2255
+
2256
+ // src/providers/__model-detection__.ts
2257
+ var THINKING_MODEL_PATTERNS = [
2258
+ // 明确的思考/推理标识
2259
+ /[-_]think(?:ing)?(?:[-_:]|$)/i,
2260
+ // *-think, *-thinking
2261
+ /[-_]reason(?:ing)?(?:[-_:]|$)/i,
2262
+ // *-reason, *-reasoning
2263
+ /[-_]cot(?:[-_:]|$)/i,
2264
+ // chain-of-thought
2265
+ /[-_]reflect(?:ion)?(?:[-_:]|$)/i,
2266
+ // reflection models
2267
+ // 知名推理模型系列
2268
+ /\bo1[-_]?/i,
2269
+ // OpenAI o1 系列
2270
+ /\bo3[-_]?/i,
2271
+ // OpenAI o3 系列
2272
+ /\br1[-_]?/i,
2273
+ // DeepSeek R1 等
2274
+ /\bqwq\b/i,
2275
+ // Qwen QwQ
2276
+ /\bn1[-_]?/i,
2277
+ // nex-n1 等
2278
+ // 通用思考关键词(较低优先级)
2279
+ /think/i
2280
+ // 包含 think
2281
+ ];
2282
+ var DIRECT_ANSWER_PATTERNS = [
2283
+ /[-_]chat$/i,
2284
+ // *-chat (结尾)
2285
+ /[-_]instruct/i,
2286
+ // *-instruct
2287
+ /[-_]turbo/i,
2288
+ // *-turbo
2289
+ /[-_]flash/i,
2290
+ // gemini-flash 等快速模型
2291
+ /[-_]lite[-_v]/i,
2292
+ // lite 版本(但不匹配 lite 结尾,避免误判)
2293
+ /[-_]fast/i
2294
+ // fast 模型
2295
+ ];
2296
+ var PROBLEMATIC_MODEL_PATTERNS = [
2297
+ /nova[-_]?\d*[-_]lite/i
2298
+ // Amazon Nova Lite 系列
2299
+ ];
2300
+ function isProblematicModel(modelId) {
2301
+ return PROBLEMATIC_MODEL_PATTERNS.some((pattern) => pattern.test(modelId));
2302
+ }
2303
+ function detectByModelName(modelId) {
2304
+ const normalizedId = modelId.toLowerCase();
2305
+ const isThinkingPattern = THINKING_MODEL_PATTERNS.some(
2306
+ (pattern) => pattern.test(normalizedId)
2307
+ );
2308
+ const isDirectPattern = DIRECT_ANSWER_PATTERNS.some(
2309
+ (pattern) => pattern.test(normalizedId)
2310
+ );
2311
+ const isProblematic = isProblematicModel(normalizedId);
2312
+ if (isThinkingPattern) {
2313
+ return {
2314
+ behavior: "thinking-first",
2315
+ supportsReasoningConfig: true,
2316
+ recommendedMinTokens: 500,
2317
+ confidence: 0.7,
2318
+ detectedBy: "pattern"
2319
+ };
2320
+ }
2321
+ if (isDirectPattern) {
2322
+ return {
2323
+ behavior: "direct-answer",
2324
+ supportsReasoningConfig: false,
2325
+ // 问题模型需要更多 token
2326
+ recommendedMinTokens: isProblematic ? 300 : 100,
2327
+ confidence: 0.6,
2328
+ detectedBy: "pattern"
2329
+ };
2330
+ }
2331
+ return {
2332
+ behavior: "unknown",
2333
+ supportsReasoningConfig: false,
2334
+ recommendedMinTokens: isProblematic ? 300 : 200,
2335
+ confidence: 0.3,
2336
+ detectedBy: "pattern"
2337
+ };
2338
+ }
2339
+ var modelCharacteristicsCache = /* @__PURE__ */ new Map();
2340
+ function detectByResponse(modelId, result) {
2341
+ const hasReasoning = !!result.reasoning && result.reasoning.length > 0;
2342
+ const hasContent = !!result.content && result.content.trim().length > 0;
2343
+ const reasoningLength = result.reasoning?.length ?? 0;
2344
+ const contentLength = result.content?.length ?? 0;
2345
+ let behavior;
2346
+ let supportsReasoningConfig = false;
2347
+ let recommendedMinTokens = 200;
2348
+ if (hasReasoning && !hasContent) {
2349
+ behavior = "thinking-first";
2350
+ supportsReasoningConfig = true;
2351
+ recommendedMinTokens = Math.max(500, reasoningLength + 200);
2352
+ } else if (hasReasoning && hasContent) {
2353
+ if (reasoningLength > contentLength * 2) {
2354
+ behavior = "thinking-first";
2355
+ supportsReasoningConfig = true;
2356
+ recommendedMinTokens = 500;
2357
+ } else {
2358
+ behavior = "hybrid";
2359
+ supportsReasoningConfig = true;
2360
+ recommendedMinTokens = 300;
2361
+ }
2362
+ } else if (hasContent && !hasReasoning) {
2363
+ behavior = "direct-answer";
2364
+ supportsReasoningConfig = false;
2365
+ recommendedMinTokens = 100;
2366
+ } else {
2367
+ behavior = "unknown";
2368
+ recommendedMinTokens = 500;
2369
+ }
2370
+ const characteristics = {
2371
+ behavior,
2372
+ supportsReasoningConfig,
2373
+ recommendedMinTokens,
2374
+ confidence: 0.9,
2375
+ detectedBy: "runtime"
2376
+ };
2377
+ modelCharacteristicsCache.set(modelId, characteristics);
2378
+ return characteristics;
2379
+ }
2380
+ function getModelCharacteristics(modelId) {
2381
+ const cached = modelCharacteristicsCache.get(modelId);
2382
+ if (cached) {
2383
+ return { ...cached, detectedBy: "cache" };
2384
+ }
2385
+ return detectByModelName(modelId);
2386
+ }
2387
+ var DEFAULT_FALLBACK_CONFIG = {
2388
+ enabled: true,
2389
+ returnReasoningAsContent: true,
2390
+ extractConclusionFromReasoning: true,
2391
+ autoRetryWithMoreTokens: false,
2392
+ // 默认关闭自动重试,避免额外消耗
2393
+ retryTokenIncrement: 300,
2394
+ maxRetries: 2
2395
+ };
2396
+ function extractConclusionFromReasoning(reasoning) {
2397
+ if (!reasoning) return null;
2398
+ const conclusionPatterns = [
2399
+ /(?:therefore|thus|so|hence|finally|in conclusion|the answer is|result is)[:\s]*(.+?)(?:\n|$)/i,
2400
+ /(?:答案是|结论是|因此|所以|最终)[::\s]*(.+?)(?:\n|$)/,
2401
+ /(?:\*\*answer\*\*|\*\*result\*\*)[:\s]*(.+?)(?:\n|$)/i,
2402
+ /=\s*(.+?)(?:\n|$)/
2403
+ // 数学等式结果
2404
+ ];
2405
+ for (const pattern of conclusionPatterns) {
2406
+ const match = reasoning.match(pattern);
2407
+ if (match && match[1]) {
2408
+ return match[1].trim();
2409
+ }
2410
+ }
2411
+ const paragraphs = reasoning.split(/\n\n+/).filter((p) => p.trim());
2412
+ if (paragraphs.length > 0) {
2413
+ const lastParagraph = paragraphs[paragraphs.length - 1].trim();
2414
+ if (lastParagraph.length < 500) {
2415
+ return lastParagraph;
2416
+ }
2417
+ }
2418
+ return null;
2419
+ }
2420
+ function applyFallbackStrategy(result, config = {}) {
2421
+ const finalConfig = { ...DEFAULT_FALLBACK_CONFIG, ...config };
2422
+ if (result.content && result.content.trim().length > 0) {
2423
+ return {
2424
+ content: result.content,
2425
+ didFallback: false,
2426
+ originalReasoning: result.reasoning
2427
+ };
2428
+ }
2429
+ if (!finalConfig.enabled) {
2430
+ return {
2431
+ content: "",
2432
+ didFallback: false,
2433
+ originalReasoning: result.reasoning
2434
+ };
2435
+ }
2436
+ if (finalConfig.extractConclusionFromReasoning && result.reasoning) {
2437
+ const conclusion = extractConclusionFromReasoning(result.reasoning);
2438
+ if (conclusion) {
2439
+ return {
2440
+ content: conclusion,
2441
+ didFallback: true,
2442
+ fallbackReason: "extracted_conclusion_from_reasoning",
2443
+ originalReasoning: result.reasoning
2444
+ };
2445
+ }
2446
+ }
2447
+ if (finalConfig.returnReasoningAsContent && result.reasoning) {
2448
+ return {
2449
+ content: result.reasoning,
2450
+ didFallback: true,
2451
+ fallbackReason: "returned_reasoning_as_content",
2452
+ originalReasoning: result.reasoning
2453
+ };
2454
+ }
2455
+ return {
2456
+ content: "",
2457
+ didFallback: false,
2458
+ fallbackReason: "no_fallback_available",
2459
+ originalReasoning: result.reasoning
2460
+ };
2461
+ }
2462
+ function adjustOptionsForModel(modelId, options) {
2463
+ const characteristics = getModelCharacteristics(modelId);
2464
+ if (characteristics.behavior === "thinking-first" && (!options.maxTokens || options.maxTokens < characteristics.recommendedMinTokens)) {
2465
+ return {
2466
+ ...options,
2467
+ maxTokens: Math.max(
2468
+ options.maxTokens ?? 0,
2469
+ characteristics.recommendedMinTokens
2470
+ )
2471
+ };
2472
+ }
2473
+ return options;
2474
+ }
2475
+ function getRecommendedConfig(modelId, scenario = "simple") {
2476
+ const characteristics = getModelCharacteristics(modelId);
2477
+ if (characteristics.behavior !== "thinking-first") {
2478
+ return {};
2479
+ }
2480
+ const configs = {
2481
+ simple: {
2482
+ maxTokens: 300,
2483
+ reasoning: { effort: "low" }
2484
+ },
2485
+ math: {
2486
+ maxTokens: 600,
2487
+ reasoning: { effort: "high" }
2488
+ },
2489
+ reasoning: {
2490
+ maxTokens: 800,
2491
+ reasoning: { effort: "medium" }
2492
+ },
2493
+ fast: {
2494
+ maxTokens: 200,
2495
+ reasoning: { effort: "off" }
2496
+ }
2497
+ };
2498
+ return configs[scenario] ?? configs.simple;
2499
+ }
2500
+ var ModelDetection = {
2501
+ detectByModelName,
2502
+ detectByResponse,
2503
+ getModelCharacteristics,
2504
+ applyFallbackStrategy,
2505
+ adjustOptionsForModel,
2506
+ getRecommendedConfig,
2507
+ extractConclusionFromReasoning,
2508
+ isProblematicModel,
2509
+ clearCache: () => modelCharacteristicsCache.clear()
1043
2510
  };
2511
+
2512
+ // src/providers/__base__.ts
2513
+ var BaseProvider = class {
2514
+ /** 降级策略配置 */
2515
+ fallbackConfig = DEFAULT_FALLBACK_CONFIG;
2516
+ /** 是否启用自动参数调整 */
2517
+ autoAdjustEnabled = true;
2518
+ /**
2519
+ * 配置降级策略
2520
+ */
2521
+ configureFallback(config) {
2522
+ this.fallbackConfig = { ...this.fallbackConfig, ...config };
2523
+ return this;
2524
+ }
2525
+ /**
2526
+ * 启用/禁用自动参数调整
2527
+ */
2528
+ setAutoAdjust(enabled) {
2529
+ this.autoAdjustEnabled = enabled;
2530
+ return this;
2531
+ }
2532
+ /**
2533
+ * 获取模型特性信息
2534
+ */
2535
+ getModelCharacteristics(modelId) {
2536
+ return ModelDetection.getModelCharacteristics(modelId);
2537
+ }
2538
+ /**
2539
+ * 智能聊天:自动检测模型特性并应用降级策略
2540
+ */
2541
+ async chatSmart(options) {
2542
+ const adjustedOptions = this.autoAdjustEnabled ? ModelDetection.adjustOptionsForModel(options.model, options) : options;
2543
+ const result = await this.chat(adjustedOptions);
2544
+ ModelDetection.detectByResponse(options.model, result);
2545
+ return result;
2546
+ }
2547
+ /**
2548
+ * 简单对话:单轮问答(默认实现)
2549
+ *
2550
+ * 智能处理思考模型:
2551
+ * 1. 自动检测模型类型
2552
+ * 2. 为思考模型自动调整 maxTokens
2553
+ * 3. 如果 content 为空,智能降级(提取结论或返回 reasoning)
2554
+ */
2555
+ async ask(model, question, options) {
2556
+ const {
2557
+ fallback,
2558
+ autoAdjust = this.autoAdjustEnabled,
2559
+ ...chatOptions
2560
+ } = options ?? {};
2561
+ let finalOptions = {
2562
+ model,
2563
+ messages: [{ role: "user", content: question }],
2564
+ ...chatOptions
2565
+ };
2566
+ if (autoAdjust) {
2567
+ finalOptions = ModelDetection.adjustOptionsForModel(model, finalOptions);
2568
+ }
2569
+ const result = await this.chat(finalOptions);
2570
+ ModelDetection.detectByResponse(model, result);
2571
+ const fallbackResult = ModelDetection.applyFallbackStrategy(result, {
2572
+ ...this.fallbackConfig,
2573
+ ...fallback
2574
+ });
2575
+ return fallbackResult.content;
2576
+ }
2577
+ /**
2578
+ * 带系统提示的对话(默认实现)
2579
+ *
2580
+ * 智能处理思考模型:
2581
+ * 1. 自动检测模型类型
2582
+ * 2. 为思考模型自动调整 maxTokens
2583
+ * 3. 如果 content 为空,智能降级(提取结论或返回 reasoning)
2584
+ */
2585
+ async askWithSystem(model, systemPrompt, userMessage, options) {
2586
+ const {
2587
+ fallback,
2588
+ autoAdjust = this.autoAdjustEnabled,
2589
+ ...chatOptions
2590
+ } = options ?? {};
2591
+ let finalOptions = {
2592
+ model,
2593
+ messages: [
2594
+ { role: "system", content: systemPrompt },
2595
+ { role: "user", content: userMessage }
2596
+ ],
2597
+ ...chatOptions
2598
+ };
2599
+ if (autoAdjust) {
2600
+ finalOptions = ModelDetection.adjustOptionsForModel(model, finalOptions);
2601
+ }
2602
+ const result = await this.chat(finalOptions);
2603
+ ModelDetection.detectByResponse(model, result);
2604
+ const fallbackResult = ModelDetection.applyFallbackStrategy(result, {
2605
+ ...this.fallbackConfig,
2606
+ ...fallback
2607
+ });
2608
+ return fallbackResult.content;
2609
+ }
2610
+ /**
2611
+ * 场景化问答:根据场景自动配置参数
2612
+ *
2613
+ * @param model 模型 ID
2614
+ * @param question 问题
2615
+ * @param scenario 场景类型
2616
+ * - 'simple': 简单问答(默认)
2617
+ * - 'math': 数学计算
2618
+ * - 'reasoning': 逻辑推理
2619
+ * - 'fast': 快速回答(关闭思考)
2620
+ */
2621
+ async askWithScenario(model, question, scenario = "simple", options) {
2622
+ const recommendedConfig = ModelDetection.getRecommendedConfig(
2623
+ model,
2624
+ scenario
2625
+ );
2626
+ return this.ask(model, question, {
2627
+ ...recommendedConfig,
2628
+ ...options
2629
+ });
2630
+ }
2631
+ };
2632
+
2633
+ // src/providers/__index__.ts
2634
+ init_types();
2635
+
2636
+ // src/providers/openrouter.ts
2637
+ init_http_provider_client();
2638
+ init_stream_processor();
2639
+ var OpenRouterProvider = class extends BaseProvider {
2640
+ name = "openrouter";
2641
+ adapter;
2642
+ client;
2643
+ baseUrl;
2644
+ apiKey;
2645
+ constructor(apiKey, baseUrl) {
2646
+ super();
2647
+ this.apiKey = apiKey;
2648
+ this.adapter = new OpenRouterAdapter();
2649
+ this.baseUrl = baseUrl ?? this.adapter.defaultBaseUrl;
2650
+ this.client = new HttpProviderClient({
2651
+ apiKey,
2652
+ baseUrl: this.baseUrl
2653
+ });
2654
+ }
2655
+ /**
2656
+ * 发送聊天请求(非流式)
2657
+ */
2658
+ async chat(options) {
2659
+ const body = this.adapter.buildChatRequest(options, false);
2660
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2661
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2662
+ const response = await this.client.chat(endpointPath, body);
2663
+ return this.adapter.parseChatResponse(response, options.model);
2664
+ }
2665
+ /**
2666
+ * 发送流式聊天请求
2667
+ */
2668
+ async *chatStream(options) {
2669
+ const body = this.adapter.buildChatRequest(options, true);
2670
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2671
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2672
+ const response = await this.client.chatStream(endpointPath, body);
2673
+ yield* StreamProcessor.processStream(
2674
+ response,
2675
+ (delta) => this.adapter.extractStreamChunk(delta)
2676
+ );
2677
+ }
2678
+ /**
2679
+ * 获取可用模型列表
2680
+ * 注意:此方法直接调用 OpenRouter API,不使用适配器
2681
+ */
2682
+ async listModels() {
2683
+ const response = await fetch(`${this.baseUrl}/models`, {
2684
+ headers: {
2685
+ Authorization: `Bearer ${this.apiKey}`,
2686
+ "Content-Type": "application/json"
2687
+ }
2688
+ });
2689
+ if (!response.ok) {
2690
+ throw new Error(
2691
+ `Failed to fetch models: ${response.status} ${response.statusText}`
2692
+ );
2693
+ }
2694
+ const result = await response.json();
2695
+ return (result.data ?? []).map((m) => ({
2696
+ id: m.id,
2697
+ canonicalSlug: m.canonical_slug ?? m.id,
2698
+ name: m.name,
2699
+ description: m.description ?? "",
2700
+ created: m.created ?? 0,
2701
+ pricing: {
2702
+ prompt: m.pricing?.prompt ?? "0",
2703
+ completion: m.pricing?.completion ?? "0",
2704
+ request: m.pricing?.request ?? "0",
2705
+ image: m.pricing?.image ?? "0"
2706
+ },
2707
+ contextLength: m.context_length ?? 0,
2708
+ architecture: {
2709
+ modality: m.architecture?.modality ?? "",
2710
+ inputModalities: m.architecture?.input_modalities ?? [],
2711
+ outputModalities: m.architecture?.output_modalities ?? [],
2712
+ tokenizer: m.architecture?.tokenizer ?? "",
2713
+ instructType: m.architecture?.instruct_type ?? ""
2714
+ },
2715
+ supportedParameters: m.supported_parameters ?? []
2716
+ }));
2717
+ }
2718
+ };
2719
+
2720
+ // src/providers/modelscope.ts
2721
+ init_http_provider_client();
2722
+ init_stream_processor();
2723
+ var ModelScopeProvider = class extends BaseProvider {
2724
+ name = "modelscope";
2725
+ adapter;
2726
+ client;
2727
+ baseUrl;
2728
+ constructor(config) {
2729
+ super();
2730
+ this.adapter = new ModelScopeAdapter();
2731
+ if (typeof config === "string") {
2732
+ this.baseUrl = this.adapter.defaultBaseUrl;
2733
+ this.client = new HttpProviderClient({
2734
+ apiKey: config,
2735
+ baseUrl: this.baseUrl
2736
+ });
2737
+ } else {
2738
+ this.baseUrl = config.baseUrl ?? this.adapter.defaultBaseUrl;
2739
+ this.client = new HttpProviderClient({
2740
+ apiKey: config.apiKey,
2741
+ baseUrl: this.baseUrl
2742
+ });
2743
+ }
2744
+ }
2745
+ /**
2746
+ * 发送聊天请求(非流式)
2747
+ */
2748
+ async chat(options) {
2749
+ const body = this.adapter.buildChatRequest(options, false);
2750
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2751
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2752
+ const response = await this.client.chat(endpointPath, body);
2753
+ return this.adapter.parseChatResponse(response, options.model);
2754
+ }
2755
+ /**
2756
+ * 发送流式聊天请求
2757
+ */
2758
+ async *chatStream(options) {
2759
+ const body = this.adapter.buildChatRequest(options, true);
2760
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2761
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2762
+ const response = await this.client.chatStream(endpointPath, body);
2763
+ yield* StreamProcessor.processStream(
2764
+ response,
2765
+ (delta) => this.adapter.extractStreamChunk(delta)
2766
+ );
2767
+ }
2768
+ };
2769
+
2770
+ // src/providers/huggingface.ts
2771
+ init_http_provider_client();
2772
+ init_stream_processor();
2773
+ var HuggingFaceProvider = class extends BaseProvider {
2774
+ name = "huggingface";
2775
+ adapter;
2776
+ client;
2777
+ baseUrl;
2778
+ constructor(config) {
2779
+ super();
2780
+ this.adapter = new HuggingFaceAdapter();
2781
+ if (typeof config === "string") {
2782
+ this.baseUrl = this.adapter.defaultBaseUrl;
2783
+ this.client = new HttpProviderClient({
2784
+ apiKey: config,
2785
+ baseUrl: this.baseUrl
2786
+ });
2787
+ } else {
2788
+ this.baseUrl = config.baseUrl ?? this.adapter.defaultBaseUrl;
2789
+ this.client = new HttpProviderClient({
2790
+ apiKey: config.apiKey,
2791
+ baseUrl: this.baseUrl
2792
+ });
2793
+ }
2794
+ }
2795
+ /**
2796
+ * 发送聊天请求(非流式)
2797
+ *
2798
+ * reasoning 参数说明:
2799
+ * - HuggingFace 是模型聚合平台,thinking 支持取决于具体模型
2800
+ * - 如果模型支持,会返回 reasoning_content
2801
+ */
2802
+ async chat(options) {
2803
+ const body = this.adapter.buildChatRequest(options, false);
2804
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2805
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2806
+ const response = await this.client.chat(endpointPath, body);
2807
+ return this.adapter.parseChatResponse(response, options.model);
2808
+ }
2809
+ /**
2810
+ * 发送流式聊天请求
2811
+ */
2812
+ async *chatStream(options) {
2813
+ const body = this.adapter.buildChatRequest(options, true);
2814
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2815
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2816
+ const response = await this.client.chatStream(endpointPath, body);
2817
+ yield* StreamProcessor.processStream(
2818
+ response,
2819
+ (delta) => this.adapter.extractStreamChunk(delta)
2820
+ );
2821
+ }
2822
+ };
2823
+
2824
+ // src/providers/groq.ts
2825
+ init_http_provider_client();
2826
+ init_stream_processor();
2827
+ var GroqProvider = class extends BaseProvider {
2828
+ name = "groq";
2829
+ adapter;
2830
+ client;
2831
+ baseUrl;
2832
+ constructor(config) {
2833
+ super();
2834
+ this.adapter = new GroqAdapter();
2835
+ if (typeof config === "string") {
2836
+ this.baseUrl = this.adapter.defaultBaseUrl;
2837
+ this.client = new HttpProviderClient({
2838
+ apiKey: config,
2839
+ baseUrl: this.baseUrl
2840
+ });
2841
+ } else {
2842
+ this.baseUrl = config.baseUrl ?? this.adapter.defaultBaseUrl;
2843
+ this.client = new HttpProviderClient({
2844
+ apiKey: config.apiKey,
2845
+ baseUrl: this.baseUrl
2846
+ });
2847
+ }
2848
+ }
2849
+ /**
2850
+ * 发送聊天请求(非流式)
2851
+ */
2852
+ async chat(options) {
2853
+ const body = this.adapter.buildChatRequest(options, false);
2854
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2855
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2856
+ const response = await this.client.chat(endpointPath, body);
2857
+ return this.adapter.parseChatResponse(response, options.model);
2858
+ }
2859
+ /**
2860
+ * 发送流式聊天请求
2861
+ */
2862
+ async *chatStream(options) {
2863
+ const body = this.adapter.buildChatRequest(options, true);
2864
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2865
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2866
+ const response = await this.client.chatStream(endpointPath, body);
2867
+ yield* StreamProcessor.processStream(
2868
+ response,
2869
+ (delta) => this.adapter.extractStreamChunk(delta)
2870
+ );
2871
+ }
2872
+ };
2873
+
2874
+ // src/providers/gemini.ts
2875
+ init_http_provider_client();
2876
+ init_stream_processor();
2877
+ var GeminiProvider = class extends BaseProvider {
2878
+ name = "gemini";
2879
+ adapter;
2880
+ client;
2881
+ baseUrl;
2882
+ constructor(config) {
2883
+ super();
2884
+ this.adapter = new GeminiAdapter();
2885
+ if (typeof config === "string") {
2886
+ this.baseUrl = this.adapter.defaultBaseUrl;
2887
+ this.client = new HttpProviderClient({
2888
+ apiKey: config,
2889
+ baseUrl: this.baseUrl
2890
+ });
2891
+ } else {
2892
+ this.baseUrl = config.baseUrl ?? this.adapter.defaultBaseUrl;
2893
+ this.client = new HttpProviderClient({
2894
+ apiKey: config.apiKey,
2895
+ baseUrl: this.baseUrl
2896
+ });
2897
+ }
2898
+ }
2899
+ /**
2900
+ * 发送聊天请求(非流式)
2901
+ */
2902
+ async chat(options) {
2903
+ const body = this.adapter.buildChatRequest(options, false);
2904
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2905
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2906
+ const response = await this.client.chat(endpointPath, body);
2907
+ return this.adapter.parseChatResponse(response, options.model);
2908
+ }
2909
+ /**
2910
+ * 发送流式聊天请求
2911
+ */
2912
+ async *chatStream(options) {
2913
+ const body = this.adapter.buildChatRequest(options, true);
2914
+ const endpoint = this.adapter.getEndpointUrl(this.baseUrl);
2915
+ const endpointPath = endpoint.replace(this.baseUrl, "");
2916
+ const response = await this.client.chatStream(endpointPath, body);
2917
+ yield* StreamProcessor.processStream(
2918
+ response,
2919
+ (delta) => this.adapter.extractStreamChunk(delta)
2920
+ );
2921
+ }
2922
+ };
2923
+
2924
+ // src/providers/deepseek.ts
2925
+ init_http_provider_client();
2926
+ init_stream_processor();
2927
+
2928
+ // src/providers/poe.ts
2929
+ init_http_provider_client();
2930
+ init_stream_processor();
2931
+
2932
+ // src/providers/nova.ts
2933
+ init_http_provider_client();
2934
+ init_stream_processor();
2935
+
2936
+ // src/utils/index.ts
2937
+ init_stream_processor();
2938
+ init_request_builder();
2939
+
2940
+ // src/client/index.ts
2941
+ init_types2();
2942
+ init_http_provider_client();
2943
+
2944
+ // src/fluent/errors.ts
2945
+ var ConfigurationError = class _ConfigurationError extends Error {
2946
+ constructor(message) {
2947
+ super(message);
2948
+ this.name = "ConfigurationError";
2949
+ Object.setPrototypeOf(this, _ConfigurationError.prototype);
2950
+ }
2951
+ };
2952
+ var ValidationError = class _ValidationError extends Error {
2953
+ constructor(message) {
2954
+ super(message);
2955
+ this.name = "ValidationError";
2956
+ Object.setPrototypeOf(this, _ValidationError.prototype);
2957
+ }
2958
+ };
2959
+
2960
+ // src/fluent/builder.ts
2961
+ var OiiaiBuilderImpl = class _OiiaiBuilderImpl {
2962
+ /** 内部配置状态 */
2963
+ config = {};
2964
+ /**
2965
+ * 创建构建器实例
2966
+ * @param initialConfig - 可选的初始配置
2967
+ */
2968
+ constructor(initialConfig) {
2969
+ if (initialConfig) {
2970
+ this.config = { ...initialConfig };
2971
+ }
2972
+ }
2973
+ /**
2974
+ * 选择服务提供商
2975
+ * @param provider - Provider 类型
2976
+ * @returns this 支持链式调用
2977
+ */
2978
+ use(provider) {
2979
+ if (!ProviderRegistry.hasAdapter(provider)) {
2980
+ const supported = ProviderRegistry.listSupported();
2981
+ throw new ValidationError(
2982
+ `\u4E0D\u652F\u6301\u7684 Provider: ${provider}\uFF0C\u652F\u6301\u7684 Provider: ${supported.join(", ")}`
2983
+ );
2984
+ }
2985
+ this.config.provider = provider;
2986
+ return this;
2987
+ }
2988
+ /**
2989
+ * 指定模型
2990
+ * @param modelId - 模型 ID
2991
+ * @returns this 支持链式调用
2992
+ */
2993
+ model(modelId) {
2994
+ this.config.model = modelId;
2995
+ return this;
2996
+ }
2997
+ /**
2998
+ * 设置系统提示词
2999
+ * @param prompt - 系统提示词
3000
+ * @returns this 支持链式调用
3001
+ */
3002
+ system(prompt) {
3003
+ this.config.system = prompt;
3004
+ return this;
3005
+ }
3006
+ /**
3007
+ * 设置温度参数
3008
+ * @param value - 温度值 (0-2)
3009
+ * @returns this 支持链式调用
3010
+ */
3011
+ temperature(value) {
3012
+ if (value < 0 || value > 2) {
3013
+ throw new ValidationError("temperature \u5FC5\u987B\u5728 0-2 \u4E4B\u95F4");
3014
+ }
3015
+ this.config.temperature = value;
3016
+ return this;
3017
+ }
3018
+ /**
3019
+ * 设置最大输出 token 数
3020
+ * @param value - token 数量
3021
+ * @returns this 支持链式调用
3022
+ */
3023
+ maxTokens(value) {
3024
+ this.config.maxTokens = value;
3025
+ return this;
3026
+ }
3027
+ /**
3028
+ * 配置思考/推理模式
3029
+ * @param config - 推理配置
3030
+ * @returns this 支持链式调用
3031
+ */
3032
+ reasoning(config) {
3033
+ this.config.reasoning = config;
3034
+ return this;
3035
+ }
3036
+ /**
3037
+ * 设置 API Key
3038
+ * @param apiKey - API Key
3039
+ * @returns this 支持链式调用
3040
+ */
3041
+ key(apiKey) {
3042
+ this.config.apiKey = apiKey;
3043
+ return this;
3044
+ }
3045
+ /**
3046
+ * 设置基础 URL
3047
+ * @param url - 基础 URL
3048
+ * @returns this 支持链式调用
3049
+ */
3050
+ baseUrl(url) {
3051
+ this.config.baseUrl = url;
3052
+ return this;
3053
+ }
3054
+ /**
3055
+ * 标记为流式请求
3056
+ * 调用后 ask() 将返回 AsyncGenerator
3057
+ * @returns StreamBuilder 类型
3058
+ */
3059
+ stream() {
3060
+ this.config.isStream = true;
3061
+ return this;
3062
+ }
3063
+ ask(question) {
3064
+ this.validateConfig();
3065
+ const adapter = ProviderRegistry.getAdapter(this.config.provider);
3066
+ const client = adapter.createClient({
3067
+ apiKey: this.config.apiKey,
3068
+ baseUrl: this.config.baseUrl ?? adapter.defaultBaseUrl
3069
+ });
3070
+ const messages = [];
3071
+ if (this.config.system) {
3072
+ messages.push({ role: "system", content: this.config.system });
3073
+ }
3074
+ messages.push({ role: "user", content: question });
3075
+ const chatOptions = {
3076
+ model: this.config.model,
3077
+ messages,
3078
+ temperature: this.config.temperature,
3079
+ maxTokens: this.config.maxTokens,
3080
+ reasoning: this.config.reasoning
3081
+ };
3082
+ if (this.config.isStream) {
3083
+ return this.executeStreamRequest(adapter, client, chatOptions);
3084
+ }
3085
+ return this.executeNonStreamRequest(adapter, client, chatOptions);
3086
+ }
3087
+ /**
3088
+ * 执行非流式请求
3089
+ * @param adapter - Provider 适配器
3090
+ * @param client - Provider 客户端
3091
+ * @param chatOptions - 聊天选项
3092
+ * @returns 响应内容
3093
+ */
3094
+ async executeNonStreamRequest(adapter, client, chatOptions) {
3095
+ const baseUrl = this.config.baseUrl ?? adapter.defaultBaseUrl;
3096
+ const endpoint = adapter.getEndpointUrl(baseUrl);
3097
+ const endpointPath = endpoint.replace(baseUrl, "");
3098
+ const body = adapter.buildChatRequest(chatOptions, false);
3099
+ const response = await client.chat(endpointPath, body);
3100
+ const result = adapter.parseChatResponse(response, this.config.model);
3101
+ return result.content;
3102
+ }
3103
+ /**
3104
+ * 执行流式请求
3105
+ * @param adapter - Provider 适配器
3106
+ * @param client - Provider 客户端
3107
+ * @param chatOptions - 聊天选项
3108
+ * @returns 流式数据块生成器
3109
+ */
3110
+ async *executeStreamRequest(adapter, client, chatOptions) {
3111
+ const baseUrl = this.config.baseUrl ?? adapter.defaultBaseUrl;
3112
+ const endpoint = adapter.getEndpointUrl(baseUrl);
3113
+ const endpointPath = endpoint.replace(baseUrl, "");
3114
+ const body = adapter.buildChatRequest(chatOptions, true);
3115
+ const response = await client.chatStream(endpointPath, body);
3116
+ const { StreamProcessor: StreamProcessor2 } = (init_stream_processor(), __toCommonJS(stream_processor_exports));
3117
+ yield* StreamProcessor2.processStream(
3118
+ response,
3119
+ (delta) => adapter.extractStreamChunk(delta)
3120
+ );
3121
+ }
3122
+ /**
3123
+ * 验证配置是否完整
3124
+ * @throws ConfigurationError 如果缺少必需参数
3125
+ */
3126
+ validateConfig() {
3127
+ if (!this.config.provider) {
3128
+ throw new ConfigurationError("\u8BF7\u5148\u8C03\u7528 use(provider) \u9009\u62E9\u670D\u52A1\u63D0\u4F9B\u5546");
3129
+ }
3130
+ if (!this.config.model) {
3131
+ throw new ConfigurationError("\u8BF7\u5148\u8C03\u7528 model(modelId) \u6307\u5B9A\u6A21\u578B");
3132
+ }
3133
+ if (!this.config.apiKey) {
3134
+ throw new ConfigurationError(
3135
+ "\u8BF7\u5148\u914D\u7F6E API Key\uFF1A\u8C03\u7528 key(apiKey) \u6216\u901A\u8FC7\u9884\u8BBE\u5B9E\u4F8B\u914D\u7F6E"
3136
+ );
3137
+ }
3138
+ }
3139
+ /**
3140
+ * 获取当前配置(用于测试)
3141
+ * @returns 当前配置的副本
3142
+ */
3143
+ getConfig() {
3144
+ return { ...this.config };
3145
+ }
3146
+ /**
3147
+ * 克隆构建器(用于创建新实例)
3148
+ * @returns 新的构建器实例
3149
+ */
3150
+ clone() {
3151
+ return new _OiiaiBuilderImpl({ ...this.config });
3152
+ }
3153
+ };
3154
+ function createBuilder(initialConfig) {
3155
+ return new OiiaiBuilderImpl(initialConfig);
3156
+ }
3157
+ var oiiai = new Proxy({}, {
3158
+ get(_target, prop) {
3159
+ const builder = new OiiaiBuilderImpl();
3160
+ const value = builder[prop];
3161
+ if (typeof value === "function") {
3162
+ return value.bind(builder);
3163
+ }
3164
+ return value;
3165
+ }
3166
+ });
3167
+
3168
+ // src/fluent/preset-provider.ts
3169
+ var PresetProviderImpl = class _PresetProviderImpl {
3170
+ /** Provider 名称 */
3171
+ name;
3172
+ /** 内部配置状态 */
3173
+ config = {};
3174
+ /** 环境变量名称映射 */
3175
+ static ENV_KEY_MAP = {
3176
+ deepseek: "DEEPSEEK_API_KEY",
3177
+ openrouter: "OPENROUTER_API_KEY",
3178
+ gemini: "GEMINI_API_KEY",
3179
+ groq: "GROQ_API_KEY",
3180
+ huggingface: "HUGGINGFACE_API_KEY",
3181
+ modelscope: "MODELSCOPE_API_KEY",
3182
+ poe: "POE_API_KEY",
3183
+ nova: "NOVA_API_KEY"
3184
+ };
3185
+ /**
3186
+ * 创建预设实例
3187
+ * @param providerType - Provider 类型
3188
+ */
3189
+ constructor(providerType) {
3190
+ if (!ProviderRegistry.hasAdapter(providerType)) {
3191
+ const supported = ProviderRegistry.listSupported();
3192
+ throw new ValidationError(
3193
+ `\u4E0D\u652F\u6301\u7684 Provider: ${providerType}\uFF0C\u652F\u6301\u7684 Provider: ${supported.join(", ")}`
3194
+ );
3195
+ }
3196
+ this.name = providerType;
3197
+ }
3198
+ /**
3199
+ * 配置 API Key 和其他选项
3200
+ * @param options - 配置选项
3201
+ * @returns this 支持链式调用
3202
+ */
3203
+ configure(options) {
3204
+ this.config.apiKey = options.apiKey;
3205
+ if (options.baseUrl) {
3206
+ this.config.baseUrl = options.baseUrl;
3207
+ }
3208
+ return this;
3209
+ }
3210
+ /**
3211
+ * 从环境变量读取配置
3212
+ * 环境变量名格式: {PROVIDER}_API_KEY (如 DEEPSEEK_API_KEY)
3213
+ * @returns this 支持链式调用
3214
+ */
3215
+ fromEnv() {
3216
+ const envKey = _PresetProviderImpl.ENV_KEY_MAP[this.name];
3217
+ const apiKey = process.env[envKey];
3218
+ if (!apiKey) {
3219
+ throw new ConfigurationError(`\u73AF\u5883\u53D8\u91CF ${envKey} \u672A\u8BBE\u7F6E`);
3220
+ }
3221
+ this.config.apiKey = apiKey;
3222
+ return this;
3223
+ }
3224
+ /**
3225
+ * 简单问答(非流式)
3226
+ * @param model - 模型 ID
3227
+ * @param question - 问题
3228
+ * @param options - 可选配置
3229
+ * @returns 响应内容
3230
+ */
3231
+ async ask(model, question, options) {
3232
+ this.validateApiKey();
3233
+ const builder = this.createConfiguredBuilder(model, options);
3234
+ const result = builder.ask(question);
3235
+ return result;
3236
+ }
3237
+ /**
3238
+ * 流式问答
3239
+ * @param model - 模型 ID
3240
+ * @param question - 问题
3241
+ * @param options - 可选配置
3242
+ * @returns 流式数据块生成器
3243
+ */
3244
+ async *stream(model, question, options) {
3245
+ this.validateApiKey();
3246
+ const builder = this.createConfiguredBuilder(model, options);
3247
+ const streamBuilder = builder.stream();
3248
+ yield* streamBuilder.ask(question);
3249
+ }
3250
+ /**
3251
+ * 带回调的流式问答
3252
+ * @param model - 模型 ID
3253
+ * @param question - 问题
3254
+ * @param callbacks - 回调函数
3255
+ * @returns Promise,完成时 resolve
3256
+ */
3257
+ async streamWithCallbacks(model, question, callbacks) {
3258
+ this.validateApiKey();
3259
+ let reasoningContent = "";
3260
+ let contentText = "";
3261
+ const builder = this.createConfiguredBuilder(model);
3262
+ const streamBuilder = builder.stream();
3263
+ for await (const chunk of streamBuilder.ask(question)) {
3264
+ if (chunk.type === "reasoning") {
3265
+ reasoningContent += chunk.text;
3266
+ callbacks.onReasoning?.(chunk.text);
3267
+ } else if (chunk.type === "content") {
3268
+ contentText += chunk.text;
3269
+ callbacks.onContent?.(chunk.text);
3270
+ }
3271
+ }
3272
+ callbacks.onDone?.({
3273
+ reasoning: reasoningContent,
3274
+ content: contentText
3275
+ });
3276
+ }
3277
+ /**
3278
+ * 获取构建器(预配置 provider 和 model)
3279
+ * @param model - 模型 ID
3280
+ * @returns 预配置的构建器
3281
+ */
3282
+ builder(model) {
3283
+ this.validateApiKey();
3284
+ const adapter = ProviderRegistry.getAdapter(this.name);
3285
+ const builder = new OiiaiBuilderImpl({
3286
+ provider: this.name,
3287
+ model,
3288
+ apiKey: this.config.apiKey,
3289
+ baseUrl: this.config.baseUrl ?? adapter.defaultBaseUrl
3290
+ });
3291
+ return builder;
3292
+ }
3293
+ /**
3294
+ * 创建多轮对话会话
3295
+ * @param model - 模型 ID
3296
+ * @param options - 会话配置
3297
+ * @returns 对话会话实例
3298
+ */
3299
+ chat(model, options) {
3300
+ this.validateApiKey();
3301
+ const { ChatSessionImpl: ChatSessionImpl2 } = (init_chat_session(), __toCommonJS(chat_session_exports));
3302
+ return new ChatSessionImpl2(this, model, options);
3303
+ }
3304
+ /**
3305
+ * 验证 API Key 是否已配置
3306
+ * @throws ConfigurationError 如果未配置 API Key
3307
+ */
3308
+ validateApiKey() {
3309
+ if (!this.config.apiKey) {
3310
+ throw new ConfigurationError(
3311
+ `\u8BF7\u5148\u914D\u7F6E API Key\uFF1A\u8C03\u7528 configure({ apiKey: 'xxx' }) \u6216 fromEnv()`
3312
+ );
3313
+ }
3314
+ }
3315
+ /**
3316
+ * 创建已配置的构建器
3317
+ * @param model - 模型 ID
3318
+ * @param options - 可选配置
3319
+ * @returns 配置好的构建器
3320
+ */
3321
+ createConfiguredBuilder(model, options) {
3322
+ const adapter = ProviderRegistry.getAdapter(this.name);
3323
+ const builder = new OiiaiBuilderImpl({
3324
+ provider: this.name,
3325
+ model,
3326
+ apiKey: this.config.apiKey,
3327
+ baseUrl: this.config.baseUrl ?? adapter.defaultBaseUrl
3328
+ });
3329
+ if (options?.system) {
3330
+ builder.system(options.system);
3331
+ }
3332
+ if (options?.temperature !== void 0) {
3333
+ builder.temperature(options.temperature);
3334
+ }
3335
+ if (options?.maxTokens !== void 0) {
3336
+ builder.maxTokens(options.maxTokens);
3337
+ }
3338
+ if (options?.reasoning) {
3339
+ builder.reasoning(options.reasoning);
3340
+ }
3341
+ return builder;
3342
+ }
3343
+ /**
3344
+ * 获取当前配置(用于测试)
3345
+ * @returns 当前配置的副本
3346
+ */
3347
+ getConfig() {
3348
+ return { ...this.config };
3349
+ }
3350
+ };
3351
+
3352
+ // src/fluent/index.ts
3353
+ init_chat_session();
3354
+
3355
+ // src/fluent/preset-instances.ts
3356
+ var deepseek = new PresetProviderImpl("deepseek");
3357
+ var openrouter = new PresetProviderImpl("openrouter");
3358
+ var gemini = new PresetProviderImpl("gemini");
3359
+ var groq = new PresetProviderImpl("groq");
3360
+ var huggingface = new PresetProviderImpl(
3361
+ "huggingface"
3362
+ );
3363
+ var modelscope = new PresetProviderImpl("modelscope");
3364
+ var poe = new PresetProviderImpl("poe");
3365
+ var nova = new PresetProviderImpl("nova");
1044
3366
  // Annotate the CommonJS export names for ESM import in node:
1045
3367
  0 && (module.exports = {
3368
+ APIError,
3369
+ BaseAdapter,
1046
3370
  BaseProvider,
3371
+ CONFIG_DEFAULTS,
3372
+ ConfigManager,
3373
+ ConfigValidator,
3374
+ ConfigurationError,
3375
+ DeepSeekAdapter,
1047
3376
  EFFORT_TOKEN_MAP,
3377
+ FluentValidationError,
3378
+ GeminiAdapter,
1048
3379
  GeminiProvider,
3380
+ GroqAdapter,
1049
3381
  GroqProvider,
3382
+ HttpProviderClient,
3383
+ HuggingFaceAdapter,
1050
3384
  HuggingFaceProvider,
3385
+ ModelScopeAdapter,
1051
3386
  ModelScopeProvider,
3387
+ NetworkError,
3388
+ NovaAdapter,
3389
+ OpenRouterAdapter,
1052
3390
  OpenRouterProvider,
3391
+ PoeAdapter,
3392
+ ProviderError,
3393
+ ProviderRegistry,
3394
+ RegistryError,
3395
+ RequestBuilder,
3396
+ StreamProcessor,
3397
+ TimeoutError,
3398
+ VALID_PROVIDERS,
1053
3399
  ai,
1054
- createProvider
3400
+ createBuilder,
3401
+ createBuiltInAdapters,
3402
+ createProvider,
3403
+ deepseek,
3404
+ gemini,
3405
+ groq,
3406
+ huggingface,
3407
+ modelscope,
3408
+ nova,
3409
+ oiiai,
3410
+ openrouter,
3411
+ poe
1055
3412
  });
1056
3413
  //# sourceMappingURL=index.js.map