-
Notifications
You must be signed in to change notification settings - Fork 15.8k
feat: add MiniMax provider support #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import { describe, expect, test, beforeEach, afterEach, mock } from 'bun:test' | ||
|
|
||
| // Defensive: mock proxy module before importing | ||
| mock.module('src/utils/proxy.js', () => ({ | ||
| getProxyFetchOptions: () => ({} as any), | ||
| })) | ||
|
|
||
| import { getMiniMaxClient, clearMiniMaxClientCache } from '../client.js' | ||
|
|
||
| describe('getMiniMaxClient', () => { | ||
| const originalEnv = { ...process.env } | ||
|
|
||
| beforeEach(() => { | ||
| clearMiniMaxClientCache() | ||
| process.env.MINIMAX_API_KEY = 'test-minimax-key' | ||
| delete process.env.MINIMAX_BASE_URL | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| clearMiniMaxClientCache() | ||
| process.env = { ...originalEnv } | ||
| }) | ||
|
|
||
| test('creates client with default base URL', () => { | ||
| const client = getMiniMaxClient() | ||
| expect(client).toBeDefined() | ||
| expect(client.baseURL).toBe('https://api.minimax.io/anthropic') | ||
| }) | ||
|
|
||
| test('uses MINIMAX_BASE_URL when set', () => { | ||
| process.env.MINIMAX_BASE_URL = 'https://custom.minimax.api/anthropic' | ||
| clearMiniMaxClientCache() | ||
| const client = getMiniMaxClient() | ||
| expect(client.baseURL).toBe('https://custom.minimax.api/anthropic') | ||
| }) | ||
|
|
||
| test('default base URL does not use api.minimax.chat', () => { | ||
| const client = getMiniMaxClient() | ||
| expect(client.baseURL).not.toContain('api.minimax.chat') | ||
| expect(client.baseURL).toContain('api.minimax.io') | ||
| }) | ||
|
|
||
| test('returns cached client on second call', () => { | ||
| const client1 = getMiniMaxClient() | ||
| const client2 = getMiniMaxClient() | ||
| expect(client1).toBe(client2) | ||
| }) | ||
|
|
||
| test('clearMiniMaxClientCache resets cache', () => { | ||
| const client1 = getMiniMaxClient() | ||
| clearMiniMaxClientCache() | ||
| process.env.MINIMAX_BASE_URL = 'https://other.minimax.api/anthropic' | ||
| const client2 = getMiniMaxClient() | ||
| expect(client1).not.toBe(client2) | ||
| }) | ||
| }) | ||
|
|
||
| describe('MiniMax API constraints', () => { | ||
| test('default base URL uses overseas api.minimax.io (not api.minimax.chat)', () => { | ||
| const defaultBaseUrl = 'https://api.minimax.io/anthropic' | ||
| expect(defaultBaseUrl).toContain('api.minimax.io') | ||
| expect(defaultBaseUrl).not.toContain('api.minimax.chat') | ||
| }) | ||
|
|
||
| test('validates temperature range (0.0, 1.0] — 0 is invalid for MiniMax', () => { | ||
| const isValidTemperature = (t: number) => t > 0 && t <= 1.0 | ||
| expect(isValidTemperature(1.0)).toBe(true) | ||
| expect(isValidTemperature(0.5)).toBe(true) | ||
| expect(isValidTemperature(0.0)).toBe(false) | ||
| expect(isValidTemperature(1.1)).toBe(false) | ||
| }) | ||
|
|
||
| test('filters unsupported parameters', () => { | ||
| const UNSUPPORTED_PARAMS = new Set(['top_k', 'stop_sequences', 'service_tier']) | ||
| const input: Record<string, unknown> = { | ||
| model: 'MiniMax-M2.7', | ||
| messages: [{ role: 'user', content: 'hi' }], | ||
| top_k: 40, | ||
| stop_sequences: ['END'], | ||
| temperature: 1.0, | ||
| } | ||
| const filtered = Object.fromEntries( | ||
| Object.entries(input).filter(([k]) => !UNSUPPORTED_PARAMS.has(k)), | ||
| ) | ||
| expect('top_k' in filtered).toBe(false) | ||
| expect('stop_sequences' in filtered).toBe(false) | ||
| expect('temperature' in filtered).toBe(true) | ||
| expect('model' in filtered).toBe(true) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import Anthropic from '@anthropic-ai/sdk' | ||
| import { getProxyFetchOptions } from 'src/utils/proxy.js' | ||
|
|
||
| /** | ||
| * Environment variables: | ||
| * | ||
| * MINIMAX_API_KEY: Required. API key for the MiniMax Anthropic-compatible endpoint. | ||
| * MINIMAX_BASE_URL: Optional. Defaults to https://api.minimax.io/anthropic. | ||
| */ | ||
|
|
||
| const DEFAULT_BASE_URL = 'https://api.minimax.io/anthropic' | ||
|
|
||
| let cachedClient: Anthropic | null = null | ||
|
|
||
| export function getMiniMaxClient(options?: { | ||
| maxRetries?: number | ||
| fetchOverride?: typeof fetch | ||
| }): Anthropic { | ||
| if (cachedClient) return cachedClient | ||
|
|
||
| const apiKey = process.env.MINIMAX_API_KEY || '' | ||
| const baseURL = process.env.MINIMAX_BASE_URL || DEFAULT_BASE_URL | ||
|
|
||
| const client = new Anthropic({ | ||
| apiKey, | ||
| baseURL, | ||
| maxRetries: options?.maxRetries ?? 0, | ||
| timeout: parseInt(process.env.API_TIMEOUT_MS || String(600 * 1000), 10), | ||
| dangerouslyAllowBrowser: true, | ||
| fetchOptions: getProxyFetchOptions({ forAnthropicAPI: false }), | ||
| ...(options?.fetchOverride && { fetch: options.fetchOverride }), | ||
| }) | ||
|
|
||
| if (!options?.fetchOverride) { | ||
| cachedClient = client | ||
| } | ||
|
|
||
| return client | ||
| } | ||
|
|
||
| export function clearMiniMaxClientCache(): void { | ||
| cachedClient = null | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,220 @@ | ||||||||||
| import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs' | ||||||||||
| import type { SystemPrompt } from '../../../utils/systemPromptType.js' | ||||||||||
| import type { Message, StreamEvent, SystemAPIErrorMessage, AssistantMessage } from '../../../types/message.js' | ||||||||||
| import type { Tools } from '../../../Tool.js' | ||||||||||
| import { getMiniMaxClient } from './client.js' | ||||||||||
| import { normalizeMessagesForAPI } from '../../../utils/messages.js' | ||||||||||
| import type { SDKAssistantMessageError } from '../../../entrypoints/agentSdkTypes.js' | ||||||||||
| import { toolToAPISchema } from '../../../utils/api.js' | ||||||||||
| import { logForDebugging } from '../../../utils/debug.js' | ||||||||||
| import { addToTotalSessionCost } from '../../../cost-tracker.js' | ||||||||||
| import { calculateUSDCost } from '../../../utils/modelCost.js' | ||||||||||
| import { recordLLMObservation } from '../../../services/langfuse/tracing.js' | ||||||||||
| import { convertMessagesToLangfuse, convertOutputToLangfuse, convertToolsToLangfuse } from '../../../services/langfuse/convert.js' | ||||||||||
| import type { Options } from '../claude.js' | ||||||||||
| import { randomUUID } from 'crypto' | ||||||||||
| import { | ||||||||||
| createAssistantAPIErrorMessage, | ||||||||||
| normalizeContentFromAPI, | ||||||||||
| } from '../../../utils/messages.js' | ||||||||||
|
|
||||||||||
| // Parameters not supported by MiniMax's Anthropic-compatible API | ||||||||||
| const UNSUPPORTED_PARAMS = new Set([ | ||||||||||
| 'top_k', | ||||||||||
| 'stop_sequences', | ||||||||||
| 'service_tier', | ||||||||||
| 'mcp_servers', | ||||||||||
| 'context_management', | ||||||||||
| 'container', | ||||||||||
| ]) | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * MiniMax query path. MiniMax uses an Anthropic-compatible API, so we use | ||||||||||
| * the Anthropic SDK directly with a MiniMax base URL. | ||||||||||
| * | ||||||||||
| * Key constraints: | ||||||||||
| * - temperature must be in (0.0, 1.0] — 0.0 is invalid | ||||||||||
| * - Some Anthropic-specific parameters are unsupported | ||||||||||
| * - System message is accepted as a string | ||||||||||
| */ | ||||||||||
| export async function* queryModelMiniMax( | ||||||||||
| messages: Message[], | ||||||||||
| systemPrompt: SystemPrompt, | ||||||||||
| tools: Tools, | ||||||||||
| signal: AbortSignal, | ||||||||||
| options: Options, | ||||||||||
| ): AsyncGenerator< | ||||||||||
| StreamEvent | AssistantMessage | SystemAPIErrorMessage, | ||||||||||
| void | ||||||||||
| > { | ||||||||||
| try { | ||||||||||
| const messagesForAPI = normalizeMessagesForAPI(messages, tools) | ||||||||||
|
|
||||||||||
| const toolSchemas = await Promise.all( | ||||||||||
| tools.map(tool => | ||||||||||
| toolToAPISchema(tool, { | ||||||||||
| getToolPermissionContext: options.getToolPermissionContext, | ||||||||||
| tools, | ||||||||||
| agents: options.agents, | ||||||||||
| allowedAgentTypes: options.allowedAgentTypes, | ||||||||||
| model: options.model, | ||||||||||
| }), | ||||||||||
| ), | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| // Filter out unsupported tool types (computer use, etc.) | ||||||||||
| const standardTools = toolSchemas.filter( | ||||||||||
| (t): t is BetaToolUnion & { type: string } => { | ||||||||||
| const anyT = t as unknown as Record<string, unknown> | ||||||||||
| return anyT.type !== 'advisor_20260301' && anyT.type !== 'computer_20250124' | ||||||||||
| }, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| // Join system prompt blocks into a single string | ||||||||||
| const systemText = systemPrompt.filter(Boolean).join('\n\n') | ||||||||||
|
|
||||||||||
| // MiniMax temperature constraint: must be in (0.0, 1.0], default 1.0 | ||||||||||
| const temperature = | ||||||||||
| options.temperatureOverride !== undefined && options.temperatureOverride > 0 | ||||||||||
| ? options.temperatureOverride | ||||||||||
| : 1.0 | ||||||||||
|
|
||||||||||
| const client = getMiniMaxClient({ | ||||||||||
| maxRetries: 0, | ||||||||||
| fetchOverride: options.fetchOverride as typeof fetch | undefined, | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| logForDebugging( | ||||||||||
| `[MiniMax] Calling model=${options.model}, messages=${messagesForAPI.length}, tools=${standardTools.length}`, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| const stream = client.messages.stream( | ||||||||||
| { | ||||||||||
| model: options.model, | ||||||||||
| messages: messagesForAPI as Parameters<typeof client.messages.stream>[0]['messages'], | ||||||||||
| ...(systemText && { system: systemText }), | ||||||||||
| ...(standardTools.length > 0 && { | ||||||||||
| tools: standardTools as Parameters<typeof client.messages.stream>[0]['tools'], | ||||||||||
| }), | ||||||||||
| max_tokens: options.maxTokens ?? 16000, | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm Options type fields and how peer adapters resolve max_tokens.
rg -nP --type=ts -C2 '\bmaxTokens\b|\bmaxOutputTokensOverride\b' src/services/api
ast-grep --pattern $'export type Options = {
$$$
}'Repository: claude-code-best/claude-code Length of output: 10657 🏁 Script executed: cat -n src/services/api/minimax/index.tsRepository: claude-code-best/claude-code Length of output: 9413 🏁 Script executed: rg -n 'getMaxOutputTokensForModel' src/services/api/claude.tsRepository: claude-code-best/claude-code Length of output: 197 Bug: The 🐛 Suggested fix- max_tokens: options.maxTokens ?? 16000,
+ max_tokens:
+ options.maxOutputTokensOverride ??
+ getMaxOutputTokensForModel(options.model),Add the import: import { getMaxOutputTokensForModel } from '../claude.js'📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| temperature, | ||||||||||
| stream: true, | ||||||||||
| }, | ||||||||||
| { signal }, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| const contentBlocks: Record<number, any> = {} | ||||||||||
| const collectedMessages: AssistantMessage[] = [] | ||||||||||
| let partialMessage: any = undefined | ||||||||||
| let usage = { | ||||||||||
| input_tokens: 0, | ||||||||||
| output_tokens: 0, | ||||||||||
| cache_creation_input_tokens: 0, | ||||||||||
| cache_read_input_tokens: 0, | ||||||||||
| } | ||||||||||
| let ttftMs = 0 | ||||||||||
| const start = Date.now() | ||||||||||
|
|
||||||||||
| for await (const event of stream) { | ||||||||||
| switch (event.type) { | ||||||||||
| case 'message_start': { | ||||||||||
| partialMessage = (event as any).message | ||||||||||
| ttftMs = Date.now() - start | ||||||||||
| if ((event as any).message?.usage) { | ||||||||||
| usage = { ...usage, ...((event as any).message.usage) } | ||||||||||
| } | ||||||||||
| break | ||||||||||
| } | ||||||||||
| case 'content_block_start': { | ||||||||||
| const idx = (event as any).index | ||||||||||
| const cb = (event as any).content_block | ||||||||||
| if (cb.type === 'tool_use') { | ||||||||||
| contentBlocks[idx] = { ...cb, input: '' } | ||||||||||
| } else if (cb.type === 'text') { | ||||||||||
| contentBlocks[idx] = { ...cb, text: '' } | ||||||||||
| } else { | ||||||||||
| contentBlocks[idx] = { ...cb } | ||||||||||
| } | ||||||||||
| break | ||||||||||
| } | ||||||||||
| case 'content_block_delta': { | ||||||||||
| const idx = (event as any).index | ||||||||||
| const delta = (event as any).delta | ||||||||||
| const block = contentBlocks[idx] | ||||||||||
| if (!block) break | ||||||||||
| if (delta.type === 'text_delta') { | ||||||||||
| block.text = (block.text || '') + delta.text | ||||||||||
| } else if (delta.type === 'input_json_delta') { | ||||||||||
| block.input = (block.input || '') + delta.partial_json | ||||||||||
| } | ||||||||||
| break | ||||||||||
| } | ||||||||||
| case 'content_block_stop': { | ||||||||||
| const idx = (event as any).index | ||||||||||
| const block = contentBlocks[idx] | ||||||||||
| if (!block || !partialMessage) break | ||||||||||
|
|
||||||||||
| const m: AssistantMessage = { | ||||||||||
| message: { | ||||||||||
| ...partialMessage, | ||||||||||
| content: normalizeContentFromAPI([block], tools, options.agentId), | ||||||||||
| }, | ||||||||||
| requestId: undefined, | ||||||||||
| type: 'assistant', | ||||||||||
| uuid: randomUUID(), | ||||||||||
| timestamp: new Date().toISOString(), | ||||||||||
| } | ||||||||||
| collectedMessages.push(m) | ||||||||||
| yield m | ||||||||||
| break | ||||||||||
| } | ||||||||||
| case 'message_delta': { | ||||||||||
| const deltaUsage = (event as any).usage | ||||||||||
| if (deltaUsage) { | ||||||||||
| usage = { ...usage, ...deltaUsage } | ||||||||||
| } | ||||||||||
| break | ||||||||||
| } | ||||||||||
| case 'message_stop': | ||||||||||
| break | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (event.type === 'message_stop' && usage.input_tokens + usage.output_tokens > 0) { | ||||||||||
| const costUSD = calculateUSDCost(options.model, usage as any) | ||||||||||
| addToTotalSessionCost(costUSD, usage as any, options.model) | ||||||||||
| } | ||||||||||
|
Comment on lines
+182
to
+185
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect calculateUSDCost for unknown-model handling and see if MiniMax pricing exists.
ast-grep --pattern $'export function calculateUSDCost($_, $_) { $$$ }'
rg -nP --type=ts -C3 '\bMiniMax-M2\.7\b|minimax' src/utils/modelCost.ts src/cost-tracker.tsRepository: claude-code-best/claude-code Length of output: 54 🏁 Script executed: # Find where calculateUSDCost is defined
rg -l "calculateUSDCost" --type=tsRepository: claude-code-best/claude-code Length of output: 335 🏁 Script executed: # Search for cost-related files
fd -e ts -e tsx | grep -i costRepository: claude-code-best/claude-code Length of output: 450 🏁 Script executed: # Search for MiniMax references in the codebase
rg -n "MiniMax|minimax" --type=tsRepository: claude-code-best/claude-code Length of output: 7894 🏁 Script executed: # Read the main modelCost.ts file to see calculateUSDCost implementation
cat -n src/utils/modelCost.tsRepository: claude-code-best/claude-code Length of output: 9208 🏁 Script executed: # Check if there's a cost table with MiniMax pricing
rg -n "MiniMax|calculateUSDCost" src/utils/modelCost.ts -A 5 -B 5Repository: claude-code-best/claude-code Length of output: 932 🏁 Script executed: # Check getDefaultMainLoopModelSetting to understand the fallback
rg -n "getDefaultMainLoopModelSetting" src/utils/model/model.ts -A 10Repository: claude-code-best/claude-code Length of output: 1598 🏁 Script executed: # Check if MiniMax is imported into modelCost.ts or if there's a config for it
rg -n "MINIMAX|minimax" src/utils/modelCost.tsRepository: claude-code-best/claude-code Length of output: 54 🏁 Script executed: # Look at the actual call site in minimax/index.ts to confirm the context
sed -n '180,190p' src/services/api/minimax/index.tsRepository: claude-code-best/claude-code Length of output: 461 MiniMax models use fallback pricing tier instead of dedicated cost entries. MiniMax-M2.7 and MiniMax-M2.7-highspeed are not in the 🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| yield { | ||||||||||
| type: 'stream_event', | ||||||||||
| event: event as any, | ||||||||||
| ...(event.type === 'message_start' ? { ttftMs } : undefined), | ||||||||||
| } as StreamEvent | ||||||||||
| } | ||||||||||
|
Comment on lines
+66
to
+192
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, BetaRawMessageStreamEvent in the Anthropic TypeScript SDK is a discriminated union type based on the type field. It unions several specific event interfaces for handling streaming responses from the beta Messages API. The type is defined as: export type BetaRawMessageStreamEvent = | BetaRawMessageStartEvent | BetaRawMessageDeltaEvent | BetaRawMessageStopEvent | BetaRawContentBlockStartEvent | BetaRawContentBlockDeltaEvent | BetaRawContentBlockStopEvent; Each variant has a literal type discriminator: - BetaRawMessageStartEvent: { type: 'message_start'; message: BetaMessage } - BetaRawMessageStopEvent: { type: 'message_stop' } - BetaRawMessageDeltaEvent: { type: 'message_delta'; delta: { ... } } - BetaRawContentBlockStartEvent: { type: 'content_block_start'; content_block: BetaContentBlock } - BetaRawContentBlockDeltaEvent: { type: 'content_block_delta'; index: number; delta: BetaRawContentBlockDelta } - BetaRawContentBlockStopEvent: { type: 'content_block_stop'; index: number } This union is imported and aliased as BetaMessageStreamEvent in BetaMessageStream, used in event handlers like streamEvent: (event: BetaMessageStreamEvent, snapshot: BetaMessage) => void. It enables type-safe pattern matching/switching on event.type in streaming code, as shown in SDK examples and source. The SDK uses this for beta features (e.g., client.beta.messages.stream), supporting advanced streaming like tool use, thinking blocks, and content deltas. Citations:
🏁 Script executed: #!/bin/bash
# Check imports and client type in minimax file
head -30 src/services/api/minimax/index.ts
# Also check what getMiniMaxClient is
rg "getMiniMaxClient" -A 5 src/services/api/minimax/Repository: claude-code-best/claude-code Length of output: 6063 🏁 Script executed: #!/bin/bash
# Check full client.ts implementation
cat src/services/api/minimax/client.ts
# Check claude.ts streaming pattern (mentioned as reference)
rg "for await.*stream" src/services/api/claude.ts -A 80 | head -150Repository: claude-code-best/claude-code Length of output: 4620 🏁 Script executed: #!/bin/bash
# Check what types are imported in minimax and if BetaRawMessageStreamEvent exists
rg "import.*BetaRaw|import.*stream|MessageStreamEvent" src/services/api/minimax/
# Check full streaming event handling in minimax (lines 100-150)
sed -n '100,150p' src/services/api/minimax/index.ts
# Also check what type annotations exist
rg "contentBlocks|partialMessage|const.*=" src/services/api/minimax/index.ts -B 1 -A 1 | head -60Repository: claude-code-best/claude-code Length of output: 3601 🏁 Script executed: #!/bin/bash
# Check all imports in minimax/index.ts
head -25 src/services/api/minimax/index.ts
# Check what types are available from Anthropic SDK
rg "BetaRawMessage|BetaMessage|BetaContent" src/services/api/minimax/ src/services/api/claude.ts
# Check claude.ts imports to see what types it uses
head -40 src/services/api/claude.ts | grep -E "^import"Repository: claude-code-best/claude-code Length of output: 4915 Import and use proper Anthropic types instead of The streaming loop uses
Add the missing imports and replace the type annotations: Required imports and fixes import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
+ import type { BetaRawMessageStreamEvent, BetaContentBlock, BetaMessage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'- const contentBlocks: Record<number, any> = {}
+ const contentBlocks: Record<number, BetaContentBlock> = {}
const collectedMessages: AssistantMessage[] = []
- let partialMessage: any = undefined
+ let partialMessage: BetaMessage | undefined = undefinedThen narrow event fields via the discriminated union in each 🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| // Record LLM observation in Langfuse (no-op if not configured) | ||||||||||
| recordLLMObservation(options.langfuseTrace ?? null, { | ||||||||||
| model: options.model, | ||||||||||
| provider: 'minimax', | ||||||||||
| input: convertMessagesToLangfuse(messagesForAPI, systemPrompt), | ||||||||||
| output: convertOutputToLangfuse(collectedMessages), | ||||||||||
| usage: { | ||||||||||
| input_tokens: usage.input_tokens, | ||||||||||
| output_tokens: usage.output_tokens, | ||||||||||
| cache_creation_input_tokens: usage.cache_creation_input_tokens, | ||||||||||
| cache_read_input_tokens: usage.cache_read_input_tokens, | ||||||||||
| }, | ||||||||||
| startTime: new Date(start), | ||||||||||
| endTime: new Date(), | ||||||||||
| completionStartTime: ttftMs > 0 ? new Date(start + ttftMs) : undefined, | ||||||||||
| tools: convertToolsToLangfuse(toolSchemas as unknown[]), | ||||||||||
| }) | ||||||||||
| } catch (error) { | ||||||||||
| const errorMessage = error instanceof Error ? error.message : String(error) | ||||||||||
| logForDebugging(`[MiniMax] Error: ${errorMessage}`, { level: 'error' }) | ||||||||||
| yield createAssistantAPIErrorMessage({ | ||||||||||
| content: `API Error: ${errorMessage}`, | ||||||||||
| apiError: 'api_error', | ||||||||||
| error: (error instanceof Error ? error : new Error(String(error))) as unknown as SDKAssistantMessageError, | ||||||||||
| }) | ||||||||||
| } | ||||||||||
| } | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use
src/*path alias instead of deep relative imports.All relative imports here (
../../../utils/systemPromptType.js,../../../types/message.js,../../../Tool.js, etc.) violate the repository convention.As per coding guidelines: "Import from
src/*path alias instead of relative paths (e.g.,import { ... } from 'src/utils/...')".♻️ Example rewrite
Apply the same rewrite to the remaining relative imports on lines 7, 9–13, and 17–19.
🤖 Prompt for AI Agents