foliko 1.1.34 → 1.1.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.34",
3
+ "version": "1.1.35",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -22,6 +22,7 @@ const {
22
22
  const { prepareMessagesForAPI, cleanResponse } = require('../utils');
23
23
  const { ChatQueueManager } = require('../utils/chat-queue');
24
24
  const { countTokens } = require('@anthropic-ai/tokenizer');
25
+ const { isThinkingModel } = require('./provider');
25
26
  const fs = require('fs/promises');
26
27
  // 新模块
27
28
  const { ChatSession } = require('./chat-session');
@@ -46,7 +47,18 @@ class AgentChatHandler extends EventEmitter {
46
47
  this.baseURL = config.baseURL;
47
48
  this.providerOptions = config.providerOptions || {};
48
49
  this.providerOptions.maxOutputTokens = config.providerOptions?.maxOutputTokens || 8192;
49
- this.providerOptions.temperature = config.providerOptions?.temperature || 0.3;
50
+ // DeepSeek thinking mode 模型不支持 temperature 参数
51
+ if (isThinkingModel(this.model)) {
52
+ this.providerOptions.temperature = undefined;
53
+ // DeepSeek 需要通过 extra_body 启用 thinking mode
54
+ // AI SDK 会将 providerOptions.deepseek 映射到请求体的 extra_body
55
+ if (!this.providerOptions.deepseek) {
56
+ this.providerOptions.deepseek = {};
57
+ }
58
+ this.providerOptions.deepseek.thinking = { type: 'enabled' };
59
+ } else {
60
+ this.providerOptions.temperature = config.providerOptions?.temperature || 0.3;
61
+ }
50
62
 
51
63
  this._systemPrompt = config.systemPrompt || 'You are a helpful assistant.';
52
64
  this._maxSteps = config.maxSteps || 20;
@@ -452,6 +464,10 @@ class AgentChatHandler extends EventEmitter {
452
464
  logger.debug(`[AgentChat] SDK onError: ${error?.message}`);
453
465
  };
454
466
 
467
+ // DeepSeek thinking mode: 收集 reasoning_content
468
+ const isThinking = isThinkingModel(this.model);
469
+ let reasoningContent = '';
470
+
455
471
  const result = await framework.runInSession(sessionId, {}, async () => {
456
472
  return agent.stream({ messages, ...this.providerOptions, onError: handleSDKError });
457
473
  });
@@ -466,6 +482,7 @@ class AgentChatHandler extends EventEmitter {
466
482
  fullText += text;
467
483
  yield { type: 'text', text };
468
484
  } else if (part.type === 'reasoning') {
485
+ reasoningContent += part.text || '';
469
486
  yield { type: 'thinking', text: part.text };
470
487
  } else if (part.type === 'tool-call') {
471
488
  yield { type: 'tool-call', toolName: part.toolName, input: part.input };
@@ -478,7 +495,24 @@ class AgentChatHandler extends EventEmitter {
478
495
  }
479
496
  }
480
497
 
498
+ // DeepSeek thinking mode: 标记 reasoning_content 已处理
499
+ // 在 finishMessages 中标记,避免重复添加
481
500
  const finishMessages = (await result.response).messages;
501
+ if (isThinking && reasoningContent) {
502
+ // DeepSeek 要求:当有 tool_calls 时,必须将 reasoning_content 添加到消息中
503
+ // AI SDK 的 finishMessage 不会包含 reasoning_content,需要手动添加
504
+ for (const msg of finishMessages) {
505
+ if (msg.role === 'assistant') {
506
+ // 检查是否有 tool_calls
507
+ const hasToolCalls = msg.tool_calls?.length > 0 ||
508
+ (Array.isArray(msg.content) && msg.content.some(c => c.type === 'tool-call' || c.type === 'tool-use'));
509
+ if (hasToolCalls) {
510
+ // DeepSeek API 要求:带 tool_calls 时必须传递 reasoning_content
511
+ msg.reasoning_content = reasoningContent;
512
+ }
513
+ }
514
+ }
515
+ }
482
516
  messages.push(...finishMessages);
483
517
  const usage = await result.totalUsage;
484
518
  if (usage) {
@@ -542,6 +576,13 @@ class AgentChatHandler extends EventEmitter {
542
576
  }
543
577
  const systemPrompt = framework._mainAgent._buildSystemPrompt();
544
578
  const tools = this._getAITools(aiTool);
579
+
580
+ // DeepSeek thinking mode: 处理参数
581
+ const apiOptions = { ...this.providerOptions };
582
+ if (isThinkingModel(this.model)) {
583
+ delete apiOptions.temperature;
584
+ }
585
+
545
586
  const agent = new ToolLoopAgent({
546
587
  model: this._aiClient,
547
588
  instructions: systemPrompt,
@@ -551,13 +592,29 @@ class AgentChatHandler extends EventEmitter {
551
592
  });
552
593
 
553
594
  const result = await framework.runInSession(sessionId, {}, async () => {
554
- return agent.generate({ messages, ...this.providerOptions });
595
+ return agent.generate({ messages, ...apiOptions });
555
596
  });
556
597
 
557
598
  if (result.usage) {
558
599
  this._updateMessageStoreUsage(messageStore, result.usage);
559
600
  }
560
601
 
602
+ // DeepSeek thinking mode: 处理 reasoning_content
603
+ const isThinking = isThinkingModel(this.model);
604
+ if (isThinking && result.reasoningText) {
605
+ // 有 reasoning_text 需要添加到消息中(当有 tool_calls 时)
606
+ const finishMessages = result.response.messages;
607
+ for (const msg of finishMessages) {
608
+ if (msg.role === 'assistant') {
609
+ const hasToolCalls = msg.tool_calls?.length > 0 ||
610
+ (Array.isArray(msg.content) && msg.content.some(c => c.type === 'tool-call' || c.type === 'tool-use'));
611
+ if (hasToolCalls) {
612
+ msg.reasoning_content = result.reasoningText;
613
+ }
614
+ }
615
+ }
616
+ }
617
+
561
618
  messages.push(...result.response.messages);
562
619
  const userMsg = messages[messages.length - result.response.messages.length - 1];
563
620
  this.emit('message', { content: result.text, sessionId: sessionId, userMessage: userMsg });
@@ -15,6 +15,8 @@ const { Subagent } = require('./subagent');
15
15
  const MODEL_CONTEXT_LIMITS = {
16
16
  'deepseek-chat': 100000,
17
17
  'deepseek-coder': 100000,
18
+ 'deepseek-v4-pro': 800000,
19
+ 'deepseek-v4-flash': 800000,
18
20
  'deepseek-reasoner': 100000,
19
21
  'MiniMax-M2.7': 100000,
20
22
  'gpt-4': 100000,
@@ -37,6 +37,28 @@ const DEFAULT_PROVIDERS = {
37
37
  },
38
38
  };
39
39
 
40
+ /**
41
+ * 需要禁用 temperature 的模型(thinking mode 模型)
42
+ */
43
+ const THINKING_MODELS = [
44
+ 'deepseek-v4-pro',
45
+ 'deepseek-v4-flash',
46
+ 'deepseek-reasoner',
47
+ ];
48
+
49
+ /**
50
+ * 检查是否是 thinking mode 模型
51
+ * @param {string} model - 模型名称
52
+ * @returns {boolean}
53
+ */
54
+ function isThinkingModel(model) {
55
+ if (!model) return false;
56
+ const lowerModel = model.toLowerCase();
57
+ return THINKING_MODELS.some(
58
+ (tm) => lowerModel.includes(tm) || lowerModel === tm
59
+ );
60
+ }
61
+
40
62
  /**
41
63
  * 创建 AI 客户端
42
64
  * @param {Object} config - 配置
@@ -48,9 +70,22 @@ const DEFAULT_PROVIDERS = {
48
70
  function createAI(config) {
49
71
  const { provider, model, apiKey, baseURL } = config;
50
72
  const providerName = (provider || 'deepseek').toLowerCase();
73
+ const isThinking = isThinkingModel(model);
74
+
51
75
  // 检查是否是预定义提供商
52
76
  if (DEFAULT_PROVIDERS[providerName]) {
53
77
  const providerConfig = DEFAULT_PROVIDERS[providerName];
78
+
79
+ // 为 DeepSeek thinking mode 模型添加特殊配置
80
+ const streamOptions = {
81
+ includeUsage: true,
82
+ };
83
+
84
+ // DeepSeek 模型添加 reasoning_content 输出
85
+ if (providerName === 'deepseek') {
86
+ streamOptions.includeReasoningContent = true;
87
+ }
88
+
54
89
  return createOpenAICompatible({
55
90
  name: providerConfig.name,
56
91
  baseURL: baseURL || providerConfig.baseURL,
@@ -65,9 +100,7 @@ function createAI(config) {
65
100
  models: {
66
101
  default: {
67
102
  id: model || 'deepseek-chat',
68
- streamOptions: {
69
- includeUsage: true,
70
- },
103
+ streamOptions,
71
104
  },
72
105
  },
73
106
  });
@@ -111,4 +144,5 @@ module.exports = {
111
144
  createModel,
112
145
  getAvailableProviders,
113
146
  DEFAULT_PROVIDERS,
147
+ isThinkingModel,
114
148
  };