From d75c0ecc681a170bcd8d43da6bbf53b5bbeeef5d Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Fri, 12 Jun 2026 14:23:55 +0200 Subject: [PATCH] mcp --- .../src/assistant/assistant.service.spec.ts | 52 +++++++++++++++++++ .../src/assistant/assistant.service.ts | 29 ++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/listify-api/src/assistant/assistant.service.spec.ts b/listify-api/src/assistant/assistant.service.spec.ts index 8a87731..dbc9d5e 100644 --- a/listify-api/src/assistant/assistant.service.spec.ts +++ b/listify-api/src/assistant/assistant.service.spec.ts @@ -128,6 +128,58 @@ describe('AssistantService', () => { ); }); + it('extracts the final assistant message from multi-completion responses', async () => { + const providerResponse = { + id: 'chatcmpl-test', + object: 'chat.multi_completion', + choices: [ + { + messages: [ + { + role: 'assistant', + index: 0, + content: '', + tool_calls: [ + { + id: 'call-1', + type: 'function', + function: { + name: 'listify_list_existing_lists', + arguments: '{"includeItems": false}', + }, + }, + ], + }, + { + role: 'tool', + index: 1, + content: [{ type: 'text', text: '{"lists":[]}' }], + }, + { + role: 'assistant', + index: 2, + content: 'Hier sind deine bestehenden Listen.', + }, + ], + finish_reason: 'stop', + }, + ], + }; + mockMistralResponse(providerResponse); + + const result = await service.chat('user-1', { + messages: [{ role: 'user', content: 'Welche Listen habe ich?' }], + }); + + expect(result.message.content).toBe('Hier sind deine bestehenden Listen.'); + expect(chatLogsRepository.save).toHaveBeenCalledWith( + expect.objectContaining({ + responsePayload: providerResponse, + assistantContent: 'Hier sind deine bestehenden Listen.', + }), + ); + }); + it('fails clearly when the api key is missing', async () => { delete process.env.MISTRAL_API_KEY; diff --git a/listify-api/src/assistant/assistant.service.ts b/listify-api/src/assistant/assistant.service.ts index 1bcbce3..415b015 100644 --- a/listify-api/src/assistant/assistant.service.ts +++ b/listify-api/src/assistant/assistant.service.ts @@ -18,6 +18,10 @@ interface MistralAgentCompletionResponse { message?: { content?: string | null; }; + messages?: Array<{ + role?: string; + content?: string | null | unknown[]; + }>; }>; } @@ -36,7 +40,7 @@ export class AssistantService { ): Promise { const messages = this.normalizeMessages(request.messages); const response = await this.callMistralAgent(userId, messages); - const content = response.choices?.[0]?.message?.content?.trim(); + const content = this.extractAssistantContent(response); if (!content) { throw new ServiceUnavailableException('Mistral response was empty.'); @@ -149,7 +153,28 @@ export class AssistantService { } const response = responsePayload as MistralAgentCompletionResponse; - return response.choices?.[0]?.message?.content?.trim() ?? null; + const directContent = response.choices?.[0]?.message?.content?.trim(); + + if (directContent) { + return directContent; + } + + for (const choice of response.choices ?? []) { + const assistantMessages = (choice.messages ?? []) + .filter((message) => message.role === 'assistant') + .reverse(); + + for (const message of assistantMessages) { + const content = + typeof message.content === 'string' ? message.content.trim() : ''; + + if (content) { + return content; + } + } + } + + return null; } private async recordChatLog(input: {