mpc logging
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user