( taskState: string, newMessage: string, cfg: SummarizerConfig, log: Logger, )
| 268 | Sub-questions, tools, methods, details of current topic = S.`; |
| 269 | |
| 270 | export async function classifyTopicOpenAI( |
| 271 | taskState: string, |
| 272 | newMessage: string, |
| 273 | cfg: SummarizerConfig, |
| 274 | log: Logger, |
| 275 | ): Promise<TopicClassifyResult> { |
| 276 | const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions"); |
| 277 | const model = cfg.model ?? "gpt-4o-mini"; |
| 278 | const headers: Record<string, string> = { |
| 279 | "Content-Type": "application/json", |
| 280 | Authorization: `Bearer ${cfg.apiKey}`, |
| 281 | ...cfg.headers, |
| 282 | }; |
| 283 | |
| 284 | const userContent = `TASK:\n${taskState}\n\nMSG:\n${newMessage}`; |
| 285 | |
| 286 | const resp = await fetch(endpoint, { |
| 287 | method: "POST", |
| 288 | headers, |
| 289 | body: JSON.stringify(buildRequestBody(cfg, { |
| 290 | model, |
| 291 | temperature: 0, |
| 292 | max_tokens: 60, |
| 293 | messages: [ |
| 294 | { role: "system", content: TOPIC_CLASSIFIER_PROMPT }, |
| 295 | { role: "user", content: userContent }, |
| 296 | ], |
| 297 | })), |
| 298 | signal: AbortSignal.timeout(cfg.timeoutMs ?? 15_000), |
| 299 | }); |
| 300 | |
| 301 | if (!resp.ok) { |
| 302 | const body = await resp.text(); |
| 303 | throw new Error(`OpenAI topic-classifier failed (${resp.status}): ${body}`); |
| 304 | } |
| 305 | |
| 306 | const json = (await resp.json()) as { choices: Array<{ message: { content: string } }> }; |
| 307 | const raw = json.choices[0]?.message?.content?.trim() ?? ""; |
| 308 | log.debug(`Topic classifier raw: "${raw}"`); |
| 309 | |
| 310 | return parseTopicClassifyResult(raw, log); |
| 311 | } |
| 312 | |
| 313 | const TOPIC_ARBITRATION_PROMPT = `A classifier flagged this message as possibly new topic (low confidence). Is it truly UNRELATED, or a sub-question/follow-up? |
| 314 | Tools/methods/details of current task = SAME. Shared entity/theme = SAME. Entirely different domain = NEW. |
no test coverage detected