feat(ai-moderation): implement error handling and robust streaming for llmClient

Wrap the LLM completion logic in a try-catch block to provide detailed
error logging, including status codes and raw response data, when
API requests fail.

- Add comprehensive error logging for failed LLM API calls.
- Ensure streaming responses are correctly aggregated and returned
  even when wrapped in error handling logic.
This commit is contained in:
MythEclipse
2026-06-05 18:47:30 +07:00
parent c0a067322b
commit 049582504e
@@ -100,46 +100,59 @@ export async function llmChat(
return retryWithBackoff( return retryWithBackoff(
async () => { async () => {
return withLlmConcurrency(async () => { return withLlmConcurrency(async () => {
const response = await client.chat.completions.create(params); try {
if (stream) { const response = await client.chat.completions.create(params);
let content = ""; if (stream) {
let finishReason = "stop"; let content = "";
for await (const chunk of response as any) { let finishReason = "stop";
const choice = chunk?.choices?.[0]; for await (const chunk of response as any) {
const choice = chunk?.choices?.[0];
// Dynamic parsing to support multiple providers (OpenAI, Ollama, Groq, Anthropic via proxy, etc.)
const textChunk =
choice?.delta?.content ||
choice?.message?.content ||
choice?.text ||
chunk?.message?.content ||
chunk?.response ||
chunk?.content ||
"";
content += textChunk; // Dynamic parsing to support multiple providers (OpenAI, Ollama, Groq, Anthropic via proxy, etc.)
const textChunk =
const fr = choice?.finish_reason || chunk?.finish_reason; choice?.delta?.content ||
if (fr) { choice?.message?.content ||
finishReason = fr; choice?.text ||
chunk?.message?.content ||
chunk?.response ||
chunk?.content ||
"";
content += textChunk;
const fr = choice?.finish_reason || chunk?.finish_reason;
if (fr) {
finishReason = fr;
}
} }
return {
id: 'stream-aggregated',
choices: [
{
message: { role: 'assistant', content, refusal: null },
finish_reason: finishReason,
index: 0,
logprobs: null,
},
],
created: Math.floor(Date.now() / 1000),
model: model,
object: 'chat.completion',
} as OpenAI.Chat.Completions.ChatCompletion;
} }
return { return response as OpenAI.Chat.Completions.ChatCompletion;
id: 'stream-aggregated', } catch (error: any) {
choices: [ log.error(
{ {
message: { role: 'assistant', content, refusal: null }, error: error.message,
finish_reason: finishReason, status: error.status,
index: 0, rawResponse: error.error || error.body || error.response?.data || "N/A",
logprobs: null, model
}, },
], "LLM API request failed"
created: Math.floor(Date.now() / 1000), );
model: model, throw error;
object: 'chat.completion',
} as OpenAI.Chat.Completions.ChatCompletion;
} }
return response as OpenAI.Chat.Completions.ChatCompletion;
}); });
}, },
{ {