mpc logging

This commit is contained in:
Bastian Wagner
2026-06-12 14:02:33 +02:00
parent 907e18fecb
commit 26e0ba5caf
6 changed files with 235 additions and 27 deletions

View File

@@ -3,6 +3,10 @@ import {
Injectable,
ServiceUnavailableException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { randomUUID } from 'crypto';
import { Repository } from 'typeorm';
import { AssistantChatLogEntity } from './assistant-chat-log.entity';
import {
AssistantChatMessage,
AssistantChatRequest,
@@ -21,12 +25,17 @@ interface MistralAgentCompletionResponse {
export class AssistantService {
private readonly endpoint = 'https://api.mistral.ai/v1/agents/completions';
constructor(
@InjectRepository(AssistantChatLogEntity)
private readonly chatLogsRepository: Repository<AssistantChatLogEntity>,
) {}
async chat(
_userId: string,
userId: string,
request: AssistantChatRequest,
): Promise<AssistantChatResponse> {
const messages = this.normalizeMessages(request.messages);
const response = await this.callMistralAgent(messages);
const response = await this.callMistralAgent(userId, messages);
const content = response.choices?.[0]?.message?.content?.trim();
if (!content) {
@@ -43,6 +52,7 @@ export class AssistantService {
}
private async callMistralAgent(
userId: string,
messages: AssistantChatMessage[],
): Promise<MistralAgentCompletionResponse> {
const apiKey = process.env.MISTRAL_API_KEY;
@@ -56,36 +66,117 @@ export class AssistantService {
throw new ServiceUnavailableException('Mistral agent id is not configured.');
}
const requestPayload = {
agent_id: agentId,
messages: [
...messages,
{ "role": "system", "content": "benutze immer den listify connector"}
],
tools: [
{
"type": "connector",
"connector_id": "listify"
}
],
stream: false,
response_format: {
type: 'text',
},
};
const startedAt = Date.now();
let statusCode: number | null = null;
let responsePayload: unknown = null;
let assistantContent: string | null = null;
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent_id: agentId,
messages: [
...messages,
{ "role": "system", "content": "benutze immer den listify connector"}
],
tools: [
{
"type": "connector",
"connector_id": "listify"
}
],
stream: false,
response_format: {
type: 'text',
},
}),
body: JSON.stringify(requestPayload),
});
statusCode = response.status;
responsePayload = await this.readResponsePayload(response);
if (!response.ok) {
await this.recordChatLog({
userId,
agentId,
requestPayload,
responsePayload,
statusCode,
durationMs: Date.now() - startedAt,
assistantContent,
errorMessage: 'Mistral agent request failed.',
});
throw new ServiceUnavailableException('Mistral agent request failed.');
}
return (await response.json()) as MistralAgentCompletionResponse;
assistantContent = this.extractAssistantContent(responsePayload);
await this.recordChatLog({
userId,
agentId,
requestPayload,
responsePayload,
statusCode,
durationMs: Date.now() - startedAt,
assistantContent,
errorMessage: null,
});
return responsePayload as MistralAgentCompletionResponse;
}
private async readResponsePayload(response: Response): Promise<unknown> {
const rawBody = await response.text();
if (!rawBody) {
return null;
}
try {
return JSON.parse(rawBody) as unknown;
} catch {
return { rawBody };
}
}
private extractAssistantContent(responsePayload: unknown): string | null {
if (!responsePayload || typeof responsePayload !== 'object') {
return null;
}
const response = responsePayload as MistralAgentCompletionResponse;
return response.choices?.[0]?.message?.content?.trim() ?? null;
}
private async recordChatLog(input: {
userId: string;
agentId: string;
requestPayload: Record<string, unknown>;
responsePayload: unknown;
statusCode: number | null;
durationMs: number;
assistantContent: string | null;
errorMessage: string | null;
}): Promise<void> {
await this.chatLogsRepository.save(
this.chatLogsRepository.create({
id: randomUUID(),
userId: input.userId,
provider: 'mistral',
endpoint: this.endpoint,
agentId: input.agentId,
statusCode: input.statusCode,
durationMs: input.durationMs,
requestPayload: input.requestPayload,
responsePayload: input.responsePayload,
assistantContent: input.assistantContent,
errorMessage: input.errorMessage,
}),
);
}
private normalizeMessages(