@librechat/agents 3.1.28 → 3.1.30

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,283 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { config } from 'dotenv';
4
+ config();
5
+ import { Calculator } from '@/tools/Calculator';
6
+ import {
7
+ HumanMessage,
8
+ BaseMessage,
9
+ UsageMetadata,
10
+ } from '@langchain/core/messages';
11
+ import type * as t from '@/types';
12
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
13
+ import { ContentTypes, GraphEvents, Providers } from '@/common';
14
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
15
+ import { capitalizeFirstLetter } from './spec.utils';
16
+ import { getLLMConfig } from '@/utils/llmConfig';
17
+ import { Run } from '@/run';
18
+
19
+ const provider = Providers.DEEPSEEK;
20
+ const llmConfig = getLLMConfig(provider);
21
+
22
+ const skipTests = process.env.DEEPSEEK_API_KEY == null;
23
+
24
+ (skipTests ? describe.skip : describe)(
25
+ `${capitalizeFirstLetter(provider)} Streaming Tests`,
26
+ () => {
27
+ jest.setTimeout(120000);
28
+ let run: Run<t.IState>;
29
+ let collectedUsage: UsageMetadata[];
30
+ let conversationHistory: BaseMessage[];
31
+ let aggregateContent: t.ContentAggregator;
32
+ let _contentParts: t.MessageContentComplex[];
33
+
34
+ const testConfig = {
35
+ configurable: {
36
+ thread_id: 'deepseek-test-1',
37
+ },
38
+ streamMode: 'values',
39
+ version: 'v2' as const,
40
+ };
41
+
42
+ beforeEach(async () => {
43
+ conversationHistory = [];
44
+ collectedUsage = [];
45
+ const { contentParts: cp, aggregateContent: ac } =
46
+ createContentAggregator();
47
+ _contentParts = cp as t.MessageContentComplex[];
48
+ aggregateContent = ac;
49
+ });
50
+
51
+ const onMessageDeltaSpy = jest.fn();
52
+ const onReasoningDeltaSpy = jest.fn();
53
+ const onRunStepSpy = jest.fn();
54
+
55
+ afterAll(() => {
56
+ onMessageDeltaSpy.mockReset();
57
+ onReasoningDeltaSpy.mockReset();
58
+ onRunStepSpy.mockReset();
59
+ });
60
+
61
+ const setupCustomHandlers = (): Record<
62
+ string | GraphEvents,
63
+ t.EventHandler
64
+ > => ({
65
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
66
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
67
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
68
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
69
+ handle: (
70
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
71
+ data: t.StreamEventData
72
+ ): void => {
73
+ aggregateContent({
74
+ event,
75
+ data: data as unknown as { result: t.ToolEndEvent },
76
+ });
77
+ },
78
+ },
79
+ [GraphEvents.ON_RUN_STEP]: {
80
+ handle: (
81
+ event: GraphEvents.ON_RUN_STEP,
82
+ data: t.StreamEventData,
83
+ metadata,
84
+ graph
85
+ ): void => {
86
+ onRunStepSpy(event, data, metadata, graph);
87
+ aggregateContent({ event, data: data as t.RunStep });
88
+ },
89
+ },
90
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
91
+ handle: (
92
+ event: GraphEvents.ON_RUN_STEP_DELTA,
93
+ data: t.StreamEventData
94
+ ): void => {
95
+ aggregateContent({ event, data: data as t.RunStepDeltaEvent });
96
+ },
97
+ },
98
+ [GraphEvents.ON_MESSAGE_DELTA]: {
99
+ handle: (
100
+ event: GraphEvents.ON_MESSAGE_DELTA,
101
+ data: t.StreamEventData,
102
+ metadata,
103
+ graph
104
+ ): void => {
105
+ onMessageDeltaSpy(event, data, metadata, graph);
106
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
107
+ },
108
+ },
109
+ [GraphEvents.ON_REASONING_DELTA]: {
110
+ handle: (
111
+ event: GraphEvents.ON_REASONING_DELTA,
112
+ data: t.StreamEventData
113
+ ): void => {
114
+ onReasoningDeltaSpy(event, data);
115
+ },
116
+ },
117
+ [GraphEvents.TOOL_START]: {
118
+ handle: (
119
+ _event: string,
120
+ _data: t.StreamEventData,
121
+ _metadata?: Record<string, unknown>
122
+ ): void => {
123
+ // Handle tool start
124
+ },
125
+ },
126
+ });
127
+
128
+ test(`${capitalizeFirstLetter(provider)}: should handle tool calls with reasoning_content preservation (streaming)`, async () => {
129
+ const customHandlers = setupCustomHandlers();
130
+
131
+ run = await Run.create<t.IState>({
132
+ runId: 'deepseek-tool-test',
133
+ graphConfig: {
134
+ type: 'standard',
135
+ llmConfig,
136
+ tools: [new Calculator()],
137
+ instructions:
138
+ 'You are a helpful math assistant. Use the calculator tool to solve math problems.',
139
+ },
140
+ returnContent: true,
141
+ customHandlers,
142
+ });
143
+
144
+ const userMessage = 'What is 127 * 453?';
145
+ conversationHistory.push(new HumanMessage(userMessage));
146
+
147
+ const inputs = {
148
+ messages: conversationHistory,
149
+ };
150
+
151
+ console.log('Starting DeepSeek streaming tool call test...');
152
+ const finalContentParts = await run.processStream(inputs, testConfig);
153
+
154
+ expect(finalContentParts).toBeDefined();
155
+ console.log('Final content parts:', finalContentParts);
156
+
157
+ const finalMessages = run.getRunMessages();
158
+ expect(finalMessages).toBeDefined();
159
+ expect(finalMessages?.length).toBeGreaterThan(0);
160
+
161
+ const hasToolCall = finalMessages?.some(
162
+ (msg) =>
163
+ msg.getType() === 'ai' &&
164
+ Array.isArray((msg as any).tool_calls) &&
165
+ (msg as any).tool_calls.length > 0
166
+ );
167
+ expect(hasToolCall).toBe(true);
168
+
169
+ const hasToolResult = finalMessages?.some(
170
+ (msg) => msg.getType() === 'tool'
171
+ );
172
+ expect(hasToolResult).toBe(true);
173
+
174
+ console.log(
175
+ 'Streaming tool call test passed - reasoning_content was preserved'
176
+ );
177
+ console.log(
178
+ 'Final response:',
179
+ finalMessages?.[finalMessages.length - 1]?.content
180
+ );
181
+ });
182
+
183
+ test(`${capitalizeFirstLetter(provider)}: should handle tool calls with disableStreaming`, async () => {
184
+ const customHandlers = setupCustomHandlers();
185
+
186
+ const nonStreamingLlmConfig: t.LLMConfig = {
187
+ ...llmConfig,
188
+ disableStreaming: true,
189
+ };
190
+
191
+ run = await Run.create<t.IState>({
192
+ runId: 'deepseek-non-streaming-tool-test',
193
+ graphConfig: {
194
+ type: 'standard',
195
+ llmConfig: nonStreamingLlmConfig,
196
+ tools: [new Calculator()],
197
+ instructions:
198
+ 'You are a helpful math assistant. Use the calculator tool to solve math problems.',
199
+ },
200
+ returnContent: true,
201
+ customHandlers,
202
+ });
203
+
204
+ const userMessage = 'What is 99 * 77?';
205
+ conversationHistory.push(new HumanMessage(userMessage));
206
+
207
+ const inputs = {
208
+ messages: conversationHistory,
209
+ };
210
+
211
+ console.log('Starting DeepSeek non-streaming tool call test...');
212
+ const finalContentParts = await run.processStream(inputs, testConfig);
213
+
214
+ expect(finalContentParts).toBeDefined();
215
+ console.log('Final content parts (non-streaming):', finalContentParts);
216
+
217
+ const finalMessages = run.getRunMessages();
218
+ expect(finalMessages).toBeDefined();
219
+ expect(finalMessages?.length).toBeGreaterThan(0);
220
+
221
+ const hasToolCall = finalMessages?.some(
222
+ (msg) =>
223
+ msg.getType() === 'ai' &&
224
+ Array.isArray((msg as any).tool_calls) &&
225
+ (msg as any).tool_calls.length > 0
226
+ );
227
+ expect(hasToolCall).toBe(true);
228
+
229
+ const hasToolResult = finalMessages?.some(
230
+ (msg) => msg.getType() === 'tool'
231
+ );
232
+ expect(hasToolResult).toBe(true);
233
+
234
+ console.log('Non-streaming tool call test passed');
235
+ console.log(
236
+ 'Final response:',
237
+ finalMessages?.[finalMessages.length - 1]?.content
238
+ );
239
+ });
240
+
241
+ test(`${capitalizeFirstLetter(provider)}: should process simple message without tools`, async () => {
242
+ const customHandlers = setupCustomHandlers();
243
+
244
+ run = await Run.create<t.IState>({
245
+ runId: 'deepseek-simple-test',
246
+ graphConfig: {
247
+ type: 'standard',
248
+ llmConfig,
249
+ tools: [],
250
+ instructions: 'You are a friendly AI assistant.',
251
+ },
252
+ returnContent: true,
253
+ customHandlers,
254
+ });
255
+
256
+ const userMessage = 'Hello! How are you today?';
257
+ conversationHistory.push(new HumanMessage(userMessage));
258
+
259
+ const inputs = {
260
+ messages: conversationHistory,
261
+ };
262
+
263
+ const finalContentParts = await run.processStream(inputs, testConfig);
264
+ expect(finalContentParts).toBeDefined();
265
+
266
+ const allTextParts = finalContentParts?.every(
267
+ (part) => part.type === ContentTypes.TEXT
268
+ );
269
+ expect(allTextParts).toBe(true);
270
+
271
+ expect(collectedUsage.length).toBeGreaterThan(0);
272
+ expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
273
+ expect(collectedUsage[0].output_tokens).toBeGreaterThan(0);
274
+
275
+ const finalMessages = run.getRunMessages();
276
+ expect(finalMessages).toBeDefined();
277
+ console.log(
278
+ `${capitalizeFirstLetter(provider)} response:`,
279
+ finalMessages?.[finalMessages.length - 1]?.content
280
+ );
281
+ });
282
+ }
283
+ );
@@ -0,0 +1,358 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { config } from 'dotenv';
4
+ config();
5
+ import { Calculator } from '@/tools/Calculator';
6
+ import {
7
+ HumanMessage,
8
+ BaseMessage,
9
+ UsageMetadata,
10
+ } from '@langchain/core/messages';
11
+ import type * as t from '@/types';
12
+ import { ToolEndHandler, ModelEndHandler } from '@/events';
13
+ import { ContentTypes, GraphEvents, Providers } from '@/common';
14
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
15
+ import { capitalizeFirstLetter } from './spec.utils';
16
+ import { Run } from '@/run';
17
+
18
+ const provider = Providers.MOONSHOT;
19
+
20
+ const llmConfig: t.LLMConfig = {
21
+ provider,
22
+ model: 'kimi-k2.5',
23
+ configuration: {
24
+ apiKey: process.env.MOONSHOT_API_KEY,
25
+ baseURL: 'https://api.moonshot.ai/v1',
26
+ },
27
+ };
28
+
29
+ const skipTests = process.env.MOONSHOT_API_KEY == null;
30
+
31
+ (skipTests ? describe.skip : describe)(
32
+ `${capitalizeFirstLetter(provider)} Streaming Tests`,
33
+ () => {
34
+ jest.setTimeout(120000);
35
+ let run: Run<t.IState>;
36
+ let collectedUsage: UsageMetadata[];
37
+ let conversationHistory: BaseMessage[];
38
+ let aggregateContent: t.ContentAggregator;
39
+ let _contentParts: t.MessageContentComplex[];
40
+
41
+ const testConfig = {
42
+ configurable: {
43
+ thread_id: 'moonshot-test-1',
44
+ },
45
+ streamMode: 'values',
46
+ version: 'v2' as const,
47
+ };
48
+
49
+ beforeEach(async () => {
50
+ conversationHistory = [];
51
+ collectedUsage = [];
52
+ const { contentParts: cp, aggregateContent: ac } =
53
+ createContentAggregator();
54
+ _contentParts = cp as t.MessageContentComplex[];
55
+ aggregateContent = ac;
56
+ });
57
+
58
+ const onMessageDeltaSpy = jest.fn();
59
+ const onReasoningDeltaSpy = jest.fn();
60
+ const onRunStepSpy = jest.fn();
61
+
62
+ afterAll(() => {
63
+ onMessageDeltaSpy.mockReset();
64
+ onReasoningDeltaSpy.mockReset();
65
+ onRunStepSpy.mockReset();
66
+ });
67
+
68
+ const setupCustomHandlers = (): Record<
69
+ string | GraphEvents,
70
+ t.EventHandler
71
+ > => ({
72
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
73
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
74
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
75
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
76
+ handle: (
77
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
78
+ data: t.StreamEventData
79
+ ): void => {
80
+ aggregateContent({
81
+ event,
82
+ data: data as unknown as { result: t.ToolEndEvent },
83
+ });
84
+ },
85
+ },
86
+ [GraphEvents.ON_RUN_STEP]: {
87
+ handle: (
88
+ event: GraphEvents.ON_RUN_STEP,
89
+ data: t.StreamEventData,
90
+ metadata,
91
+ graph
92
+ ): void => {
93
+ onRunStepSpy(event, data, metadata, graph);
94
+ aggregateContent({ event, data: data as t.RunStep });
95
+ },
96
+ },
97
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
98
+ handle: (
99
+ event: GraphEvents.ON_RUN_STEP_DELTA,
100
+ data: t.StreamEventData
101
+ ): void => {
102
+ aggregateContent({ event, data: data as t.RunStepDeltaEvent });
103
+ },
104
+ },
105
+ [GraphEvents.ON_MESSAGE_DELTA]: {
106
+ handle: (
107
+ event: GraphEvents.ON_MESSAGE_DELTA,
108
+ data: t.StreamEventData,
109
+ metadata,
110
+ graph
111
+ ): void => {
112
+ onMessageDeltaSpy(event, data, metadata, graph);
113
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
114
+ },
115
+ },
116
+ [GraphEvents.ON_REASONING_DELTA]: {
117
+ handle: (
118
+ event: GraphEvents.ON_REASONING_DELTA,
119
+ data: t.StreamEventData
120
+ ): void => {
121
+ onReasoningDeltaSpy(event, data);
122
+ },
123
+ },
124
+ [GraphEvents.TOOL_START]: {
125
+ handle: (
126
+ _event: string,
127
+ _data: t.StreamEventData,
128
+ _metadata?: Record<string, unknown>
129
+ ): void => {
130
+ // Handle tool start
131
+ },
132
+ },
133
+ });
134
+
135
+ test(`${capitalizeFirstLetter(provider)}: should handle tool calls with reasoning_content preservation`, async () => {
136
+ const customHandlers = setupCustomHandlers();
137
+
138
+ run = await Run.create<t.IState>({
139
+ runId: 'moonshot-tool-test',
140
+ graphConfig: {
141
+ type: 'standard',
142
+ llmConfig,
143
+ tools: [new Calculator()],
144
+ instructions:
145
+ 'You are a helpful math assistant. Use the calculator tool to solve math problems.',
146
+ },
147
+ returnContent: true,
148
+ customHandlers,
149
+ });
150
+
151
+ const userMessage = 'What is 127 * 453?';
152
+ conversationHistory.push(new HumanMessage(userMessage));
153
+
154
+ const inputs = {
155
+ messages: conversationHistory,
156
+ };
157
+
158
+ console.log('Starting Moonshot tool call test...');
159
+ const finalContentParts = await run.processStream(inputs, testConfig);
160
+
161
+ expect(finalContentParts).toBeDefined();
162
+ console.log('Final content parts:', finalContentParts);
163
+
164
+ const finalMessages = run.getRunMessages();
165
+ expect(finalMessages).toBeDefined();
166
+ expect(finalMessages?.length).toBeGreaterThan(0);
167
+
168
+ const hasToolCall = finalMessages?.some(
169
+ (msg) =>
170
+ msg.getType() === 'ai' &&
171
+ Array.isArray((msg as any).tool_calls) &&
172
+ (msg as any).tool_calls.length > 0
173
+ );
174
+ expect(hasToolCall).toBe(true);
175
+
176
+ const hasToolResult = finalMessages?.some(
177
+ (msg) => msg.getType() === 'tool'
178
+ );
179
+ expect(hasToolResult).toBe(true);
180
+
181
+ console.log('Tool call test passed - reasoning_content was preserved');
182
+ console.log(
183
+ 'Final response:',
184
+ finalMessages?.[finalMessages.length - 1]?.content
185
+ );
186
+ });
187
+
188
+ test(`${capitalizeFirstLetter(provider)}: should handle multi-turn conversation with tools`, async () => {
189
+ const customHandlers = setupCustomHandlers();
190
+
191
+ run = await Run.create<t.IState>({
192
+ runId: 'moonshot-multi-turn-test',
193
+ graphConfig: {
194
+ type: 'standard',
195
+ llmConfig,
196
+ tools: [new Calculator()],
197
+ instructions:
198
+ 'You are a helpful math assistant. Use the calculator tool when needed.',
199
+ },
200
+ returnContent: true,
201
+ customHandlers,
202
+ });
203
+
204
+ conversationHistory.push(new HumanMessage('What is 15 + 27?'));
205
+
206
+ let finalContentParts = await run.processStream(
207
+ { messages: conversationHistory },
208
+ testConfig
209
+ );
210
+ expect(finalContentParts).toBeDefined();
211
+
212
+ let runMessages = run.getRunMessages();
213
+ conversationHistory.push(...(runMessages ?? []));
214
+
215
+ console.log(
216
+ 'Turn 1 completed, conversation length:',
217
+ conversationHistory.length
218
+ );
219
+
220
+ conversationHistory.push(
221
+ new HumanMessage('Now multiply that result by 3')
222
+ );
223
+
224
+ run = await Run.create<t.IState>({
225
+ runId: 'moonshot-multi-turn-test-2',
226
+ graphConfig: {
227
+ type: 'standard',
228
+ llmConfig,
229
+ tools: [new Calculator()],
230
+ instructions:
231
+ 'You are a helpful math assistant. Use the calculator tool when needed.',
232
+ },
233
+ returnContent: true,
234
+ customHandlers,
235
+ });
236
+
237
+ finalContentParts = await run.processStream(
238
+ { messages: conversationHistory },
239
+ { ...testConfig, configurable: { thread_id: 'moonshot-test-2' } }
240
+ );
241
+ expect(finalContentParts).toBeDefined();
242
+
243
+ runMessages = run.getRunMessages();
244
+ expect(runMessages).toBeDefined();
245
+
246
+ console.log('Turn 2 completed');
247
+ console.log(
248
+ 'Final response:',
249
+ runMessages?.[runMessages.length - 1]?.content
250
+ );
251
+
252
+ const textParts = finalContentParts?.filter(
253
+ (part) => part.type === ContentTypes.TEXT
254
+ );
255
+ expect(textParts?.length).toBeGreaterThan(0);
256
+ });
257
+
258
+ test(`${capitalizeFirstLetter(provider)}: should process simple message without tools`, async () => {
259
+ const customHandlers = setupCustomHandlers();
260
+
261
+ run = await Run.create<t.IState>({
262
+ runId: 'moonshot-simple-test',
263
+ graphConfig: {
264
+ type: 'standard',
265
+ llmConfig,
266
+ tools: [],
267
+ instructions: 'You are a friendly AI assistant.',
268
+ },
269
+ returnContent: true,
270
+ customHandlers,
271
+ });
272
+
273
+ const userMessage = 'Hello! How are you today?';
274
+ conversationHistory.push(new HumanMessage(userMessage));
275
+
276
+ const inputs = {
277
+ messages: conversationHistory,
278
+ };
279
+
280
+ const finalContentParts = await run.processStream(inputs, testConfig);
281
+ expect(finalContentParts).toBeDefined();
282
+
283
+ const allTextParts = finalContentParts?.every(
284
+ (part) => part.type === ContentTypes.TEXT
285
+ );
286
+ expect(allTextParts).toBe(true);
287
+
288
+ expect(collectedUsage.length).toBeGreaterThan(0);
289
+ expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
290
+ expect(collectedUsage[0].output_tokens).toBeGreaterThan(0);
291
+
292
+ const finalMessages = run.getRunMessages();
293
+ expect(finalMessages).toBeDefined();
294
+ console.log(
295
+ `${capitalizeFirstLetter(provider)} response:`,
296
+ finalMessages?.[finalMessages.length - 1]?.content
297
+ );
298
+ });
299
+
300
+ test(`${capitalizeFirstLetter(provider)}: should handle tool calls with disableStreaming`, async () => {
301
+ const customHandlers = setupCustomHandlers();
302
+
303
+ const nonStreamingLlmConfig: t.LLMConfig = {
304
+ ...llmConfig,
305
+ disableStreaming: true,
306
+ };
307
+
308
+ run = await Run.create<t.IState>({
309
+ runId: 'moonshot-non-streaming-tool-test',
310
+ graphConfig: {
311
+ type: 'standard',
312
+ llmConfig: nonStreamingLlmConfig,
313
+ tools: [new Calculator()],
314
+ instructions:
315
+ 'You are a helpful math assistant. Use the calculator tool to solve math problems.',
316
+ },
317
+ returnContent: true,
318
+ customHandlers,
319
+ });
320
+
321
+ const userMessage = 'What is 99 * 77?';
322
+ conversationHistory.push(new HumanMessage(userMessage));
323
+
324
+ const inputs = {
325
+ messages: conversationHistory,
326
+ };
327
+
328
+ console.log('Starting Moonshot non-streaming tool call test...');
329
+ const finalContentParts = await run.processStream(inputs, testConfig);
330
+
331
+ expect(finalContentParts).toBeDefined();
332
+ console.log('Final content parts (non-streaming):', finalContentParts);
333
+
334
+ const finalMessages = run.getRunMessages();
335
+ expect(finalMessages).toBeDefined();
336
+ expect(finalMessages?.length).toBeGreaterThan(0);
337
+
338
+ const hasToolCall = finalMessages?.some(
339
+ (msg) =>
340
+ msg.getType() === 'ai' &&
341
+ Array.isArray((msg as any).tool_calls) &&
342
+ (msg as any).tool_calls.length > 0
343
+ );
344
+ expect(hasToolCall).toBe(true);
345
+
346
+ const hasToolResult = finalMessages?.some(
347
+ (msg) => msg.getType() === 'tool'
348
+ );
349
+ expect(hasToolResult).toBe(true);
350
+
351
+ console.log('Non-streaming tool call test passed');
352
+ console.log(
353
+ 'Final response:',
354
+ finalMessages?.[finalMessages.length - 1]?.content
355
+ );
356
+ });
357
+ }
358
+ );
package/src/types/llm.ts CHANGED
@@ -26,6 +26,7 @@ import type { ChatXAIInput } from '@langchain/xai';
26
26
  import {
27
27
  AzureChatOpenAI,
28
28
  ChatDeepSeek,
29
+ ChatMoonshot,
29
30
  ChatOpenAI,
30
31
  ChatXAI,
31
32
  } from '@/llm/openai';
@@ -110,6 +111,7 @@ export type ProviderOptionsMap = {
110
111
  [Providers.OPENROUTER]: ChatOpenRouterCallOptions;
111
112
  [Providers.BEDROCK]: BedrockConverseClientOptions;
112
113
  [Providers.XAI]: XAIClientOptions;
114
+ [Providers.MOONSHOT]: OpenAIClientOptions;
113
115
  };
114
116
 
115
117
  export type ChatModelMap = {
@@ -124,6 +126,7 @@ export type ChatModelMap = {
124
126
  [Providers.OPENROUTER]: ChatOpenRouter;
125
127
  [Providers.BEDROCK]: CustomChatBedrockConverse;
126
128
  [Providers.GOOGLE]: CustomChatGoogleGenerativeAI;
129
+ [Providers.MOONSHOT]: ChatMoonshot;
127
130
  };
128
131
 
129
132
  export type ChatModelConstructorMap = {
@@ -120,6 +120,16 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
120
120
  streaming: true,
121
121
  streamUsage: true,
122
122
  },
123
+ [Providers.MOONSHOT]: {
124
+ provider: Providers.MOONSHOT,
125
+ model: 'kimi-k2.5',
126
+ streaming: true,
127
+ streamUsage: true,
128
+ configuration: {
129
+ apiKey: process.env.MOONSHOT_API_KEY,
130
+ baseURL: 'https://api.moonshot.ai/v1',
131
+ },
132
+ },
123
133
  [Providers.ANTHROPIC]: {
124
134
  provider: Providers.ANTHROPIC,
125
135
  model: 'claude-sonnet-4-5',