foliko 1.1.63 → 1.1.64

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.
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Foliko Framework 统一常量
3
+ *
4
+ * 集中管理所有 magic numbers / strings,消除散落各处的重复值。
5
+ * 所有模块应引用此文件而非硬编码。
6
+ */
7
+
8
+ const path = require('path');
9
+
10
+ // ============================================================================
11
+ // AI 模型与提供者
12
+ // ============================================================================
13
+
14
+ /** 默认 AI 提供者 */
15
+ const DEFAULT_PROVIDER = 'deepseek';
16
+
17
+ /** 默认 AI 模型 */
18
+ const DEFAULT_MODEL = 'deepseek-chat';
19
+
20
+ /** 默认 Max Output Tokens */
21
+ const DEFAULT_MAX_OUTPUT_TOKENS = 8192;
22
+
23
+ /** 默认 Temperature */
24
+ const DEFAULT_TEMPERATURE = 0.3;
25
+
26
+ /** 默认 Max Steps(工具调用最大轮数) */
27
+ const DEFAULT_MAX_STEPS = 20;
28
+
29
+ /** 需要禁用 temperature 的 thinking mode 模型列表 */
30
+ const THINKING_MODELS = [
31
+ 'deepseek-v4-pro',
32
+ 'deepseek-v4-flash',
33
+ 'deepseek-reasoner',
34
+ ];
35
+
36
+ // ============================================================================
37
+ // Session 上下文
38
+ // ============================================================================
39
+
40
+ /** Session TTL:默认 30 分钟 */
41
+ const DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000;
42
+
43
+ /** Session 清理检查间隔 */
44
+ const DEFAULT_SESSION_CLEANUP_INTERVAL_MS = 60 * 1000;
45
+
46
+ /** 每个 Session 最大消息数 */
47
+ const DEFAULT_MAX_MESSAGES_PER_SESSION = 1000;
48
+
49
+ // ============================================================================
50
+ // 上下文压缩
51
+ // ============================================================================
52
+
53
+ /** 保留最近的 N 条消息 */
54
+ const KEEP_RECENT_MESSAGES = 20;
55
+
56
+ /** 智能压缩启用 */
57
+ const ENABLE_SMART_COMPRESS = true;
58
+
59
+ /** 压缩超时(毫秒) */
60
+ const COMPRESSION_TIMEOUT_MS = 1_200_000;
61
+
62
+ /** 工具结果最大字符数 */
63
+ const MAX_TOOL_RESULT_SIZE = 4000;
64
+
65
+ // ============================================================================
66
+ // 消息队列
67
+ // ============================================================================
68
+
69
+ /** 默认最大并发数 */
70
+ const DEFAULT_MAX_CONCURRENT = 1;
71
+
72
+ /** 默认重试次数 */
73
+ const DEFAULT_RETRY_ATTEMPTS = 3;
74
+
75
+ /** 默认重试延迟(毫秒) */
76
+ const DEFAULT_RETRY_DELAY_MS = 2000;
77
+
78
+ // ============================================================================
79
+ // System Prompt 优先级
80
+ // ============================================================================
81
+
82
+ const PROMPT_PRIORITY = {
83
+ DATETIME: 50,
84
+ ORIGINAL_PROMPT: 100,
85
+ SHARED_PROMPT: 200,
86
+ METADATA: 300,
87
+ TOOLS: 400,
88
+ SKILLS: 500,
89
+ SUB_AGENTS: 600,
90
+ CAPABILITIES: 700,
91
+ MCP_TOOLS: 750,
92
+ EXTENSION_TOOLS: 800,
93
+ TOOL_CORE_RULES: 95,
94
+ };
95
+
96
+ // ============================================================================
97
+ // 熔断器
98
+ // ============================================================================
99
+
100
+ /** 熔断器默认失败阈值 */
101
+ const DEFAULT_CIRCUIT_FAILURE_THRESHOLD = 3;
102
+
103
+ /** 熔断器默认成功阈值 */
104
+ const DEFAULT_CIRCUIT_SUCCESS_THRESHOLD = 2;
105
+
106
+ /** 熔断器默认超时(毫秒) */
107
+ const DEFAULT_CIRCUIT_TIMEOUT_MS = 60_000;
108
+
109
+ // ============================================================================
110
+ // 插件
111
+ // ============================================================================
112
+
113
+ /** 默认插件优先级 */
114
+ const DEFAULT_PLUGIN_PRIORITY = 100;
115
+
116
+ /** 系统插件名称集合 */
117
+ const SYSTEM_PLUGINS = new Set([
118
+ 'ai', 'defaults', 'tools', 'skill-manager', 'session',
119
+ 'storage', 'audit', 'rules', 'file-system',
120
+ ]);
121
+
122
+ /** Agent 配置目录名 */
123
+ const AGENT_DIR = '.agent';
124
+
125
+ // ============================================================================
126
+ // Session 存储
127
+ // ============================================================================
128
+
129
+ /** 默认 Session 存储类型 */
130
+ const DEFAULT_SESSION_STORAGE_TYPE = 'file';
131
+
132
+ /** 默认 Session 存储目录 */
133
+ const DEFAULT_SESSION_STORAGE_DIR = path.join(AGENT_DIR, 'sessions');
134
+
135
+ // ============================================================================
136
+ // Token 计数(估算系数)
137
+ // ============================================================================
138
+
139
+ /** 每字符对应的 token 数(中文系数更大) */
140
+ const TOKENS_PER_CHAR = 0.25;
141
+
142
+ /** 每工具对应的 token 数 */
143
+ const TOKENS_PER_TOOL = 500;
144
+
145
+ /** 每消息固定开销 token */
146
+ const TOKENS_PER_MESSAGE_OVERHEAD = 4;
147
+
148
+ // ============================================================================
149
+ // 导出
150
+ // ============================================================================
151
+
152
+ module.exports = {
153
+ // AI
154
+ DEFAULT_PROVIDER,
155
+ DEFAULT_MODEL,
156
+ DEFAULT_MAX_OUTPUT_TOKENS,
157
+ DEFAULT_TEMPERATURE,
158
+ DEFAULT_MAX_STEPS,
159
+ THINKING_MODELS,
160
+
161
+ // Session
162
+ DEFAULT_SESSION_TTL_MS,
163
+ DEFAULT_SESSION_CLEANUP_INTERVAL_MS,
164
+ DEFAULT_MAX_MESSAGES_PER_SESSION,
165
+
166
+ // Compression
167
+ KEEP_RECENT_MESSAGES,
168
+ ENABLE_SMART_COMPRESS,
169
+ COMPRESSION_TIMEOUT_MS,
170
+ MAX_TOOL_RESULT_SIZE,
171
+
172
+ // Queue
173
+ DEFAULT_MAX_CONCURRENT,
174
+ DEFAULT_RETRY_ATTEMPTS,
175
+ DEFAULT_RETRY_DELAY_MS,
176
+
177
+ // Priority
178
+ PROMPT_PRIORITY,
179
+
180
+ // Circuit Breaker
181
+ DEFAULT_CIRCUIT_FAILURE_THRESHOLD,
182
+ DEFAULT_CIRCUIT_SUCCESS_THRESHOLD,
183
+ DEFAULT_CIRCUIT_TIMEOUT_MS,
184
+
185
+ // Plugin
186
+ DEFAULT_PLUGIN_PRIORITY,
187
+ SYSTEM_PLUGINS,
188
+ AGENT_DIR,
189
+
190
+ // Storage
191
+ DEFAULT_SESSION_STORAGE_TYPE,
192
+ DEFAULT_SESSION_STORAGE_DIR,
193
+
194
+ // Token
195
+ TOKENS_PER_CHAR,
196
+ TOKENS_PER_TOOL,
197
+ TOKENS_PER_MESSAGE_OVERHEAD,
198
+ };
@@ -9,7 +9,7 @@
9
9
 
10
10
  const { logger } = require('../utils/logger');
11
11
  const { TokenCounter } = require('./token-counter');
12
- const { Subagent } = require('./subagent');
12
+ const { validateMessagesPairing, filterPairedMessages } = require('../utils/message-validator');
13
13
 
14
14
  // 模型上下文限制表
15
15
  const MODEL_CONTEXT_LIMITS = {
@@ -30,7 +30,7 @@ const MODEL_CONTEXT_LIMITS = {
30
30
  };
31
31
 
32
32
  // 压缩超时
33
- const COMPRESSION_TIMEOUT = 1200000;
33
+ const COMPRESSION_TIMEOUT_MS = 1200000;
34
34
 
35
35
  class ContextCompressor {
36
36
  /**
@@ -128,8 +128,8 @@ class ContextCompressor {
128
128
  return new Promise((_, reject) => {
129
129
  this._compressionTimeoutId = setTimeout(() => {
130
130
  this._compressionTimeoutId = null;
131
- reject(new Error(`Compression timeout (${COMPRESSION_TIMEOUT}ms)`));
132
- }, COMPRESSION_TIMEOUT);
131
+ reject(new Error(`Compression timeout (${COMPRESSION_TIMEOUT_MS}ms)`));
132
+ }, COMPRESSION_TIMEOUT_MS);
133
133
  });
134
134
  }
135
135
 
@@ -242,122 +242,7 @@ class ContextCompressor {
242
242
  * @private
243
243
  */
244
244
  _filterPairedMessages(messages) {
245
- // 工具类型检查
246
- const isToolCall = (item) =>
247
- item && (item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId;
248
- const isToolResult = (item) =>
249
- item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId;
250
-
251
- // 第一遍:找出所有 tool call 和它们的 results
252
- const assistantToolCalls = new Map(); // toolCallId -> assistant message index
253
- const toolResults = new Map(); // toolCallId -> tool result indices
254
-
255
- for (let i = 0; i < messages.length; i++) {
256
- const msg = messages[i];
257
- if (msg.role === 'assistant') {
258
- // 格式1: msg.content 中的 tool-call 块
259
- if (msg.content) {
260
- const content = Array.isArray(msg.content) ? msg.content : [msg.content];
261
- for (const item of content) {
262
- if (isToolCall(item)) {
263
- assistantToolCalls.set(item.toolCallId, i);
264
- }
265
- }
266
- }
267
- // 格式2: msg.tool_calls 数组 (OpenAI 格式)
268
- if (Array.isArray(msg.tool_calls)) {
269
- for (const tc of msg.tool_calls) {
270
- if (tc.id) {
271
- assistantToolCalls.set(tc.id, i);
272
- }
273
- }
274
- }
275
- }
276
- if (msg.role === 'tool' && Array.isArray(msg.content)) {
277
- for (const item of msg.content) {
278
- if (isToolResult(item)) {
279
- if (!toolResults.has(item.toolCallId)) {
280
- toolResults.set(item.toolCallId, []);
281
- }
282
- toolResults.get(item.toolCallId).push(i);
283
- }
284
- }
285
- }
286
- }
287
-
288
- // 第二遍:找出哪些 assistant 消息需要保留
289
- const assistantIndicesToKeep = new Set();
290
- for (let i = 0; i < messages.length; i++) {
291
- const msg = messages[i];
292
- if (msg.role === 'assistant') {
293
- let hasToolCall = false;
294
- // 检查 msg.content 格式
295
- if (msg.content) {
296
- const content = Array.isArray(msg.content) ? msg.content : [msg.content];
297
- for (const item of content) {
298
- if (isToolCall(item)) {
299
- hasToolCall = true;
300
- break;
301
- }
302
- }
303
- }
304
- // 检查 msg.tool_calls 格式 (OpenAI)
305
- if (!hasToolCall && Array.isArray(msg.tool_calls)) {
306
- for (const tc of msg.tool_calls) {
307
- if (tc.id) {
308
- hasToolCall = true;
309
- break;
310
- }
311
- }
312
- }
313
- if (hasToolCall) {
314
- assistantIndicesToKeep.add(i);
315
- } else if (i >= messages.length - 3) {
316
- // 保留最近几条 assistant 消息
317
- assistantIndicesToKeep.add(i);
318
- }
319
- }
320
- }
321
-
322
- // 如果有孤儿的 tool call(没有 result),保留该 assistant 消息
323
- for (const [toolCallId, assistantIdx] of assistantToolCalls) {
324
- if (!toolResults.has(toolCallId)) {
325
- assistantIndicesToKeep.add(assistantIdx);
326
- }
327
- }
328
-
329
- // 第三遍:确定哪些 tool result 要保留
330
- const indicesToKeep = new Set();
331
- for (let i = 0; i < messages.length; i++) {
332
- const msg = messages[i];
333
- if (msg.role === 'tool') {
334
- let hasPairedAssistant = false;
335
- const content = Array.isArray(msg.content) ? msg.content : [msg.content];
336
- for (const item of content) {
337
- if (isToolResult(item)) {
338
- const assistantIdx = assistantToolCalls.get(item.toolCallId);
339
- if (assistantIdx !== undefined && assistantIndicesToKeep.has(assistantIdx)) {
340
- hasPairedAssistant = true;
341
- break;
342
- }
343
- }
344
- }
345
- if (hasPairedAssistant) {
346
- indicesToKeep.add(i);
347
- }
348
- } else if (msg.role === 'assistant') {
349
- if (assistantIndicesToKeep.has(i)) {
350
- indicesToKeep.add(i);
351
- }
352
- } else {
353
- // user, system 等角色默认保留
354
- indicesToKeep.add(i);
355
- }
356
- }
357
-
358
- // 按索引排序并返回
359
- const sortedIndices = Array.from(indicesToKeep).sort((a, b) => a - b);
360
- return sortedIndices.map((i) => messages[i]);
245
+ return filterPairedMessages(messages);
361
246
  }
362
247
 
363
248
  /**
@@ -366,67 +251,7 @@ class ContextCompressor {
366
251
  * @returns {Array} 验证后的消息
367
252
  */
368
253
  validateMessagesPairing(messages) {
369
- // 收集所有 assistant 的 tool-call IDs(兼容多种类型名)
370
- const assistantToolCallIds = new Set();
371
- for (const msg of messages) {
372
- if (msg.role === 'assistant') {
373
- // 格式1: msg.content 中的 tool-call 块
374
- if (Array.isArray(msg.content)) {
375
- for (const item of msg.content) {
376
- if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) {
377
- assistantToolCallIds.add(item.toolCallId);
378
- }
379
- }
380
- }
381
- // 格式2: msg.tool_calls 数组 (OpenAI 格式)
382
- if (Array.isArray(msg.tool_calls)) {
383
- for (const tc of msg.tool_calls) {
384
- if (tc.id) {
385
- assistantToolCallIds.add(tc.id);
386
- }
387
- }
388
- }
389
- }
390
- }
391
-
392
- // 检查并删除没有配对的 tool result
393
- let removedCount = 0;
394
- for (const msg of messages) {
395
- if (msg.role === 'tool' && Array.isArray(msg.content)) {
396
- const originalLength = msg.content.length;
397
- msg.content = msg.content.filter((item) => {
398
- // 兼容 tool-result 和 tool_result 两种类型
399
- if (
400
- item &&
401
- (item.type === 'tool-result' || item.type === 'tool_result') &&
402
- item.toolCallId
403
- ) {
404
- if (!assistantToolCallIds.has(item.toolCallId)) {
405
- removedCount++;
406
- return false; // 删除没有配对的 tool result
407
- }
408
- }
409
- return true;
410
- });
411
-
412
- // 如果所有 content 都被删除了,标记整个消息待删除
413
- if (msg.content.length === 0 && originalLength > 0) {
414
- msg._orphaned = true;
415
- }
416
- }
417
- }
418
-
419
- // 移除被标记为 orphaned 的 tool 消息
420
- const originalLength = messages.length;
421
- const filtered = messages.filter((msg) => !(msg.role === 'tool' && msg._orphaned));
422
-
423
- if (removedCount > 0 || filtered.length !== originalLength) {
424
- logger.debug(
425
- `Removed ${removedCount} orphaned tool-results, ${originalLength - filtered.length} orphaned tool messages`
426
- );
427
- }
428
-
429
- return filtered;
254
+ return validateMessagesPairing(messages);
430
255
  }
431
256
 
432
257
  /**
@@ -324,6 +324,55 @@ class Framework extends EventEmitter {
324
324
  return this.toolRegistry.getAll();
325
325
  }
326
326
 
327
+ // ============================================================================
328
+ // 公开 API — 减少外部直接访问私有属性
329
+ // ============================================================================
330
+
331
+ /**
332
+ * 获取主 Agent
333
+ * @returns {Agent|null}
334
+ */
335
+ getMainAgent() {
336
+ return this._mainAgent;
337
+ }
338
+
339
+ /**
340
+ * 获取主 Agent 的 System Prompt
341
+ * @returns {string}
342
+ */
343
+ getSystemPrompt() {
344
+ if (this._mainAgent) {
345
+ return this._mainAgent._buildSystemPrompt();
346
+ }
347
+ return '';
348
+ }
349
+
350
+ /**
351
+ * 获取插件实例(安全访问)
352
+ * @param {string} name - 插件名称
353
+ * @returns {Object|null}
354
+ */
355
+ getPluginInstance(name) {
356
+ const entry = this.pluginManager.get(name);
357
+ return entry || null;
358
+ }
359
+
360
+ /**
361
+ * 获取所有 Agent 列表
362
+ * @returns {Array<Agent>}
363
+ */
364
+ getAgents() {
365
+ return [...this._agents];
366
+ }
367
+
368
+ /**
369
+ * 获取所有 SessionContext 的 ID 列表
370
+ * @returns {string[]}
371
+ */
372
+ listSessionContexts() {
373
+ return Array.from(this._sessionContexts.keys());
374
+ }
375
+
327
376
  // ============================================================================
328
377
  // 事件描述注册(供 Ambient Agent 使用)
329
378
  // ============================================================================
@@ -551,6 +600,68 @@ class Framework extends EventEmitter {
551
600
  }
552
601
  }
553
602
 
603
+ // Session TTL 清理配置
604
+ _sessionTTL = 30 * 60 * 1000; // 默认 30 分钟
605
+ _sessionCleanupInterval = null;
606
+
607
+ /**
608
+ * 配置 Session TTL
609
+ * @param {number} ttlMs - TTL 毫秒数
610
+ */
611
+ setSessionTTL(ttlMs) {
612
+ this._sessionTTL = ttlMs;
613
+ return this;
614
+ }
615
+
616
+ /**
617
+ * 启动 Session 自动清理
618
+ * @param {number} [intervalMs=60000] - 检查间隔
619
+ */
620
+ startSessionCleanup(intervalMs = 60000) {
621
+ if (this._sessionCleanupInterval) {
622
+ clearInterval(this._sessionCleanupInterval);
623
+ }
624
+ this._sessionCleanupInterval = setInterval(() => {
625
+ this._cleanupStaleSessions();
626
+ }, intervalMs);
627
+ // 允许进程退出时不阻止
628
+ if (this._sessionCleanupInterval.unref) {
629
+ this._sessionCleanupInterval.unref();
630
+ }
631
+ return this;
632
+ }
633
+
634
+ /**
635
+ * 停止 Session 自动清理
636
+ */
637
+ stopSessionCleanup() {
638
+ if (this._sessionCleanupInterval) {
639
+ clearInterval(this._sessionCleanupInterval);
640
+ this._sessionCleanupInterval = null;
641
+ }
642
+ return this;
643
+ }
644
+
645
+ /**
646
+ * 清理过期的 Session
647
+ * @private
648
+ */
649
+ _cleanupStaleSessions() {
650
+ const now = Date.now();
651
+ const staleIds = [];
652
+ for (const [sessionId, ctx] of this._sessionContexts) {
653
+ if (ctx.metadata && (now - ctx.metadata.lastActive) > this._sessionTTL) {
654
+ staleIds.push(sessionId);
655
+ }
656
+ }
657
+ for (const id of staleIds) {
658
+ this.destroySessionContext(id);
659
+ }
660
+ if (staleIds.length > 0) {
661
+ this.logger.debug(`Cleaned up ${staleIds.length} stale sessions`);
662
+ }
663
+ }
664
+
554
665
  /**
555
666
  * 列出所有活跃的 Session ID
556
667
  * @returns {string[]}
@@ -737,9 +848,9 @@ class Framework extends EventEmitter {
737
848
 
738
849
  /**
739
850
  * 同步主Agent的提示词部分到所有子Agent
740
- * @private
851
+ * @public — 插件可能需要调用
741
852
  */
742
- _syncPromptPartsToSubagents() {
853
+ syncPromptPartsToSubagents() {
743
854
  if (!this._mainAgent || !this._mainAgent._systemPromptBuilder) return;
744
855
  const mainParts = this._mainAgent._systemPromptBuilder._parts;
745
856
  if (!mainParts) return;
@@ -784,8 +895,9 @@ class Framework extends EventEmitter {
784
895
  lines.push('');
785
896
 
786
897
  // 2. 主Agent的系统提示词(基础部分)
787
- if (this._mainAgent) {
788
- const mainPrompt = this._mainAgent._originalPrompt;
898
+ const mainAgent = this.getMainAgent();
899
+ if (mainAgent) {
900
+ const mainPrompt = mainAgent.getOriginalPrompt();
789
901
  if (mainPrompt) {
790
902
  lines.push('## 主Agent的系统提示词');
791
903
  lines.push(mainPrompt);
@@ -988,10 +1100,17 @@ class Framework extends EventEmitter {
988
1100
  this._toolRegistryListeners = [];
989
1101
  }
990
1102
 
991
- // 卸载所有插件
1103
+ // 停止 Session 清理
1104
+ this.stopSessionCleanup();
1105
+
1106
+ // 卸载所有插件(逐个保护,防止一个失败阻断后续)
992
1107
  const plugins = this.pluginManager.getAll();
993
1108
  for (const { name } of plugins) {
994
- await this.pluginManager.unload(name);
1109
+ try {
1110
+ await this.pluginManager.unload(name);
1111
+ } catch (err) {
1112
+ this.logger.warn(`Failed to unload plugin '${name}': ${err.message}`);
1113
+ }
995
1114
  }
996
1115
 
997
1116
  // 清空工具
@@ -286,10 +286,11 @@ class Plugin {
286
286
  this._promptParts.push({ name, priority, provider });
287
287
 
288
288
  // 如果主 agent 已存在,直接注册
289
- if (this._framework._mainAgent) {
290
- this._framework._mainAgent.registerPromptPart(name, priority, provider);
289
+ const mainAgent = this._framework.getMainAgent();
290
+ if (mainAgent) {
291
+ mainAgent.registerPromptPart(name, priority, provider);
291
292
  // 同步到所有子Agent
292
- this._framework._syncPromptPartsToSubagents();
293
+ this._framework.syncPromptPartsToSubagents();
293
294
  return;
294
295
  }
295
296
 
@@ -307,14 +308,15 @@ class Plugin {
307
308
  if (this._deferredPromptParts) {
308
309
  for (const part of this._deferredPromptParts) {
309
310
  try {
310
- this._framework._mainAgent?.registerPromptPart(part.name, part.priority, part.provider);
311
+ const mainAgent = this._framework.getMainAgent();
312
+ mainAgent?.registerPromptPart(part.name, part.priority, part.provider);
311
313
  } catch (err) {
312
314
  log.error(` Failed to register prompt part ${part.name}:`, err.message);
313
315
  }
314
316
  }
315
317
  this._deferredPromptParts = null;
316
318
  // 同步到所有子Agent
317
- this._framework._syncPromptPartsToSubagents();
319
+ this._framework.syncPromptPartsToSubagents();
318
320
  }
319
321
  }, 100);
320
322
  });
@@ -14,26 +14,32 @@ const DEFAULT_PROVIDERS = {
14
14
  openai: {
15
15
  name: 'OpenAI',
16
16
  baseURL: 'https://api.openai.com/v1',
17
+ defaultModel: 'gpt-4o',
17
18
  },
18
19
  ollama: {
19
20
  name: 'Ollama',
20
21
  baseURL: 'http://localhost:11434/v1',
22
+ defaultModel: 'llama3',
21
23
  },
22
24
  lmstudio: {
23
25
  name: 'LM Studio',
24
26
  baseURL: 'http://localhost:1234/v1',
27
+ defaultModel: 'local-model',
25
28
  },
26
29
  deepseek: {
27
30
  name: 'DeepSeek',
28
31
  baseURL: 'https://api.deepseek.com/v1',
32
+ defaultModel: 'deepseek-chat',
29
33
  },
30
34
  anthropic: {
31
35
  name: 'Anthropic',
32
36
  baseURL: 'https://api.anthropic.com/v1',
37
+ defaultModel: 'claude-sonnet-4-20250514',
33
38
  },
34
39
  minimax: {
35
40
  name: 'MiniMax',
36
41
  baseURL: 'https://api.minimaxi.com/v1',
42
+ defaultModel: 'MiniMax-M2.7',
37
43
  },
38
44
  };
39
45