Skip to main content

poe2_agent/
query_agent.rs

1//! Single-turn query agent: takes parsed build data + user question,
2//! streams an LLM response with the build as context.
3
4use futures_core::Stream;
5
6use crate::llm::{input_message, ChatGptClient, LlmError, ResponseStreamEvent};
7
8const SYSTEM_PROMPT: &str = "\
9You are a Path of Exile 2 build analysis assistant. The user has uploaded \
10their Path of Building export and you have access to their parsed build stats.\n\
11\n\
12Answer the user's questions about their build. Be specific and reference \
13actual numbers from the build data when relevant. If the data doesn't contain \
14enough information to answer, say so.\n\
15\n\
16Here is the parsed build data:\n";
17
18/// Answers questions about a PoB build using an LLM with injected context.
19pub struct QueryAgent {
20    llm: ChatGptClient,
21}
22
23impl QueryAgent {
24    pub fn new(llm: ChatGptClient) -> Self {
25        Self { llm }
26    }
27
28    /// Stream a response to a user question about the given build.
29    ///
30    /// `build_json` is the JSON output from `PobParser::parse`.
31    pub fn respond(
32        &self,
33        build_json: &[u8],
34        message: &str,
35    ) -> impl Stream<Item = Result<String, LlmError>> + Send {
36        let instructions = format!(
37            "{SYSTEM_PROMPT}```json\n{}\n```",
38            String::from_utf8_lossy(build_json),
39        );
40
41        let input = vec![input_message("user", message)];
42        let stream = self
43            .llm
44            .create_response_stream(&input, Some(&instructions), None, None);
45
46        async_stream::stream! {
47            tokio::pin!(stream);
48            while let Some(event) = futures_lite::StreamExt::next(&mut stream).await {
49                match event {
50                    Ok(ResponseStreamEvent::TextDelta(t)) => yield Ok(t),
51                    Err(e) => { yield Err(e); return; }
52                    _ => {} // ignore function calls, usage
53                }
54            }
55        }
56    }
57}