diff --git a/app/src/lib/audit/runner.ts b/app/src/lib/audit/runner.ts index 4f86074..72b5a10 100644 --- a/app/src/lib/audit/runner.ts +++ b/app/src/lib/audit/runner.ts @@ -130,6 +130,7 @@ async function callMinimax( { role: 'user', content: userPrompt }, ], temperature: 0, + stream: true, response_format: { type: 'json_object' }, }; const response = await fetchWithTimeout(url, { @@ -144,11 +145,33 @@ async function callMinimax( const errText = await response.text(); throw new Error(`MiniMax HTTP ${response.status}: ${errText.substring(0, 100)}...`); } - const data = await response.json() as { choices?: { message?: { content?: string } }[]; error?: { message?: string } }; - if (data.error) throw new Error(`MiniMax error: ${data.error.message}`); - const content = data.choices?.[0]?.message?.content; - if (!content) throw new Error('MiniMax returned empty response'); - return content; + + if (!response.body) throw new Error('MiniMax response body is null'); + + const reader = response.body.getReader(); + const decoder = new TextDecoder('utf-8'); + let fullText = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const chunk = decoder.decode(value, { stream: true }); + const lines = chunk.split('\n').filter(l => l.trim().startsWith('data: ')); + for (const line of lines) { + const jsonStr = line.replace(/^data: /, '').trim(); + if (jsonStr === '[DONE]') continue; + try { + const parsed = JSON.parse(jsonStr); + const content = parsed.choices?.[0]?.delta?.content || ''; + fullText += content; + } catch (e) { + // ignore incomplete JSON + } + } + } + + if (!fullText) throw new Error('MiniMax returned empty streaming response'); + return fullText; } async function callOpenRouter(