092_UI_core / src /modules /agent /agent.service.ts
anhkhoiphan's picture
Revert "Merge branch 'prod'"
a6d0e2a
import {
Injectable,
Logger,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
interface AgentApiRequest {
conversation_id: string;
query: string;
sender_id: string;
}
interface AgentApiResponse {
reply?: string;
message?: string;
content?: string;
response?: string;
text?: string;
answer?: string;
}
@Injectable()
export class AgentService {
private readonly logger = new Logger(AgentService.name);
constructor(
private readonly configService: ConfigService,
) {}
// ==================== Mention Bot Logic ====================
/**
* Check if message contains a mention of the configured bot username
*/
containsMention(content: string): boolean {
const botUsername = this.configService.get<string>('AGENT_BOT_USERNAME') || this.configService.get<string>('agent.botUsername') || 'studybot';
const pattern = new RegExp(`@${botUsername}\\b`, 'i');
return pattern.test(content);
}
/**
* Extract the prompt from a message by removing the bot mention
*/
extractPrompt(content: string): string {
const botUsername = this.configService.get<string>('AGENT_BOT_USERNAME') || this.configService.get<string>('agent.botUsername') || 'studybot';
const pattern = new RegExp(`@${botUsername}\\b`, 'gi');
return content.replace(pattern, '').trim();
}
/**
* Get bot user ID from config
*/
getBotUserId(): string | undefined {
const botUserId = this.configService.get<string>('AGENT_BOT_USER_ID');
if (botUserId) return botUserId;
return this.configService.get<string>('agent.botUserId') || undefined;
}
/**
* Call the external agent API and return the reply text
* Returns null if agent is not configured or returns empty
*/
async callAgent(
roomId: string,
userId: string,
prompt: string,
username?: string,
): Promise<string | null> {
const baseUrl = this.configService.get<string>('AGENT_API_URL') || this.configService.get<string>('agent.apiUrl');
if (!baseUrl) {
this.logger.warn('AGENT_API_URL not configured, skipping agent call');
return 'Xin lỗi, tôi đang gặp sự cố kết nối. Vui lòng thử lại sau!';
}
if (!prompt) {
this.logger.debug('Empty prompt, skipping agent call');
return null;
}
try {
this.logger.log(`Calling agent API for room ${roomId}, user ${userId}`);
const reply = await this.callAgentApi({
conversation_id: roomId,
query: prompt,
sender_id: username || userId,
});
if (!reply || reply.trim().length === 0) {
this.logger.warn('Agent returned empty reply');
return 'Xin lỗi, tôi không hiểu câu hỏi của bạn. Vui lòng thử lại!';
}
this.logger.log(`Agent replied for room ${roomId}`);
return reply;
} catch (error) {
this.logger.error(`Failed to call agent: ${error.message}`);
return 'Xin lỗi, tôi đang gặp sự cố kỹ thuật. Vui lòng thử lại sau nhé!';
}
}
/**
* Call the external agent API at POST /api/v1/chat
*/
private async callAgentApi(payload: AgentApiRequest): Promise<string> {
const baseUrl = this.configService.get<string>('AGENT_API_URL') || this.configService.get<string>('agent.apiUrl');
const apiKey = this.configService.get<string>('AGENT_API_KEY') || this.configService.get<string>('agent.apiKey');
const url = `${baseUrl}/api/v1/chat`;
this.logger.debug(`Agent API URL: ${url}`);
this.logger.debug(`Agent API payload: ${JSON.stringify(payload)}`);
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`;
}
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Agent API returned ${response.status}: ${await response.text()}`);
}
const data = (await response.json()) as AgentApiResponse;
// Support multiple response formats
return (
data.reply ||
data.message ||
data.content ||
data.response ||
data.text ||
data.answer ||
''
);
}
}