File size: 4,270 Bytes
ffda0ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6d0e2a
 
ffda0ec
 
 
 
 
 
 
 
 
 
 
 
 
 
a6d0e2a
 
ffda0ec
 
 
 
a6d0e2a
 
 
 
 
 
ffda0ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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 ||
      ''
    );
  }
}