reflex/semantic/
schema.rs

1//! Schema definitions for LLM responses
2
3use serde::{Deserialize, Serialize};
4
5/// LLM response containing rfx query commands
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct QueryResponse {
8    /// Array of rfx commands to execute. Most queries should have 1 command.
9    /// Only use multiple commands when absolutely necessary (e.g., cross-language search).
10    pub queries: Vec<QueryCommand>,
11}
12
13/// Enhanced response for agentic mode containing both queries and results
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct AgenticQueryResponse {
16    /// Generated query commands
17    pub queries: Vec<QueryCommand>,
18
19    /// Executed search results (file-grouped)
20    #[serde(skip_serializing_if = "Vec::is_empty")]
21    pub results: Vec<crate::models::FileGroupedResult>,
22
23    /// Total count of matches across all results
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub total_count: Option<usize>,
26
27    /// Context gathered from tools (documentation, codebase structure, etc.)
28    /// Used for answer generation when query results are insufficient
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub gathered_context: Option<String>,
31
32    /// Tools that were executed during context gathering (for UI display)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub tools_executed: Option<Vec<String>>,
35
36    /// Conversational answer synthesized from results (only when --answer is used)
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub answer: Option<String>,
39}
40
41/// A single rfx query command with execution metadata
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct QueryCommand {
44    /// The rfx query command WITHOUT the 'rfx' prefix
45    ///
46    /// Examples:
47    /// - `query "TODO"`
48    /// - `query "async" --symbols --kind function --lang typescript`
49    pub command: String,
50
51    /// Execution order (1-based). Commands execute sequentially by order.
52    pub order: i32,
53
54    /// Whether to include this result in final output
55    /// - `false`: context-only (used to inform subsequent queries)
56    /// - `true`: include in merged results shown to user
57    pub merge: bool,
58}
59
60/// JSON schema for LLM prompt (OpenAI structured output)
61pub const RESPONSE_SCHEMA: &str = r#"{
62  "type": "object",
63  "properties": {
64    "queries": {
65      "type": "array",
66      "description": "Array of rfx commands to execute. Most queries should have 1 command.",
67      "items": {
68        "type": "object",
69        "properties": {
70          "command": {
71            "type": "string",
72            "description": "The rfx query command WITHOUT the 'rfx' prefix"
73          },
74          "order": {
75            "type": "integer",
76            "description": "Execution order (1-based, sequential)"
77          },
78          "merge": {
79            "type": "boolean",
80            "description": "Whether to include in final results (false = context-only)"
81          }
82        },
83        "required": ["command", "order", "merge"]
84      }
85    }
86  },
87  "required": ["queries"]
88}"#;
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_deserialize_single_query() {
96        let json = r#"{
97            "queries": [{
98                "command": "query \"TODO\"",
99                "order": 1,
100                "merge": true
101            }]
102        }"#;
103
104        let response: QueryResponse = serde_json::from_str(json).unwrap();
105        assert_eq!(response.queries.len(), 1);
106        assert_eq!(response.queries[0].command, "query \"TODO\"");
107        assert_eq!(response.queries[0].order, 1);
108        assert_eq!(response.queries[0].merge, true);
109    }
110
111    #[test]
112    fn test_deserialize_multiple_queries() {
113        let json = r#"{
114            "queries": [
115                {
116                    "command": "query \"User\" --symbols --kind struct --lang rust",
117                    "order": 1,
118                    "merge": false
119                },
120                {
121                    "command": "query \"User\" --lang rust",
122                    "order": 2,
123                    "merge": true
124                }
125            ]
126        }"#;
127
128        let response: QueryResponse = serde_json::from_str(json).unwrap();
129        assert_eq!(response.queries.len(), 2);
130        assert_eq!(response.queries[0].order, 1);
131        assert_eq!(response.queries[0].merge, false);
132        assert_eq!(response.queries[1].order, 2);
133        assert_eq!(response.queries[1].merge, true);
134    }
135}