Skip to main content

objectiveai_api/vector/completions/
response_key.rs

1//! Response key schema generation for structured LLM voting.
2//!
3//! Provides JSON schema and tool definitions that constrain LLM output to
4//! select one of the available response keys.
5
6/// Parsed response key from LLM structured output.
7#[derive(Debug, serde::Deserialize)]
8pub struct ResponseKey {
9    /// Optional synthetic reasoning from the LLM.
10    pub _think: Option<String>,
11    /// The selected response key.
12    #[allow(dead_code)]
13    response_key: String,
14}
15
16impl ResponseKey {
17    /// Creates a JSON schema for response key selection.
18    fn schema(
19        vector_response_keys: Vec<String>,
20        think: bool,
21    ) -> serde_json::Map<String, serde_json::Value> {
22        let mut map = serde_json::Map::with_capacity(4);
23        map.insert(
24            "type".to_string(),
25            serde_json::Value::String("object".to_string()),
26        );
27        map.insert(
28            "properties".to_string(),
29            serde_json::Value::Object({
30                let mut properties = serde_json::Map::with_capacity(if think { 2 } else { 1 });
31                if think {
32                    properties.insert(
33                        "_think".to_string(),
34                        serde_json::json!({
35                            "type": "string",
36                            "description": "The assistant's internal reasoning.",
37                        }),
38                    );
39                }
40                properties.insert(
41                    "response_key".to_string(),
42                    serde_json::json!({
43                        "type": "string",
44                        "enum": vector_response_keys
45                    }),
46                );
47                properties
48            }),
49        );
50        map.insert(
51            "required".to_string(),
52            serde_json::Value::Array({
53                let mut required = Vec::with_capacity(if think { 2 } else { 1 });
54                if think {
55                    required.push(serde_json::Value::String("_think".to_string()));
56                }
57                required.push(serde_json::Value::String("response_key".to_string()));
58                required
59            }),
60        );
61        map.insert(
62            "additionalProperties".to_string(),
63            serde_json::Value::Bool(false),
64        );
65        map
66    }
67
68    /// Creates a response format for JSON schema output mode.
69    ///
70    /// Constrains the LLM to output a JSON object with the selected response key.
71    pub fn response_format(
72        vector_response_keys: Vec<String>,
73        think: bool,
74    ) -> objectiveai::chat::completions::request::ResponseFormat {
75        objectiveai::chat::completions::request::ResponseFormat::JsonSchema {
76            json_schema: objectiveai::chat::completions::request::JsonSchema {
77                name: "response_key".to_string(),
78                description: None,
79                strict: Some(true),
80                schema: Some(serde_json::Value::Object(Self::schema(
81                    vector_response_keys,
82                    think,
83                ))),
84            },
85        }
86    }
87
88    /// Creates a tool definition for tool call output mode.
89    ///
90    /// The LLM calls this tool with the selected response key as an argument.
91    pub fn tool(
92        vector_response_keys: Vec<String>,
93        think: bool,
94    ) -> objectiveai::chat::completions::request::Tool {
95        objectiveai::chat::completions::request::Tool::Function {
96            function: objectiveai::chat::completions::request::FunctionTool {
97                name: "response_key".to_string(),
98                description: None,
99                strict: Some(true),
100                parameters: Some(Self::schema(vector_response_keys, think)),
101            },
102        }
103    }
104
105    /// Creates a tool choice that forces the LLM to call the response_key tool.
106    pub fn tool_choice() -> objectiveai::chat::completions::request::ToolChoice {
107        objectiveai::chat::completions::request::ToolChoice::Function(
108            objectiveai::chat::completions::request::ToolChoiceFunction::Function {
109                function: objectiveai::chat::completions::request::ToolChoiceFunctionFunction {
110                    name: "response_key".to_string(),
111                },
112            },
113        )
114    }
115}