aimx/inference/
output_format.rs

1use crate::{
2    aim::{Prefix, Writer},
3    values::Value,
4};
5use serde::{Deserialize, Serialize};
6use std::fmt;
7
8/// Capability level for prompt formatting.
9#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
10pub enum Capability {
11    /// Minimal; prefers simple headers and multi-line sections.
12    Minimal,
13    /// Limited; uses delimited headers and multi-line sections.
14    Limited,
15    /// Standard; uses delimited headers and single-line key/value pairs.
16    Standard,
17    /// Markdown; uses markdown-style headers.
18    Markdown,
19}
20
21impl fmt::Display for Capability {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Capability::Minimal => write!(f, "minimal"),
25            Capability::Limited => write!(f, "limited"),
26            Capability::Standard => write!(f, "standard"),
27            Capability::Markdown => write!(f, "markdown"),
28        }
29    }
30}
31
32impl Capability {
33    /// Parse from a string identifier ("minimal", "limited", "markdown"; otherwise `Standard`).
34    pub fn new(capability: &str) -> Self {
35        match capability {
36            "minimal" => Capability::Minimal,
37            "limited" => Capability::Limited,
38            "markdown" => Capability::Limited,
39            _ => Capability::Standard,
40        }
41    }
42}
43
44/// Helper for building model-appropriate prompt text from sections, key/value lines and values.
45pub struct OutputFormat {
46    /// The writer used to generate the formatted prompt output
47    writer: Writer,
48    /// Prefix style for ordered lists (currently unused)
49    #[allow(dead_code)]
50    ordered: Prefix,
51    /// Prefix style for unordered lists
52    unordered: Prefix,
53    /// Prefix string for section headers
54    section_prefix: &'static str,
55    /// Suffix string for section headers
56    section_suffix: &'static str,
57    /// Delimiter between keys and values
58    delimiter: &'static str,
59    /// Delimiter for multiline content (currently unused)
60    #[allow(dead_code)]
61    multiline: &'static str,
62    /// Suffix for each line
63    suffix: &'static str,
64}
65
66impl OutputFormat {
67    /// Create with formatting derived from `Capability`.
68    pub fn new(capability: &Capability) -> Self {
69        match capability {
70            Capability::Minimal => OutputFormat {
71                writer: Writer::stringizer(),
72
73                ordered: Prefix::None,
74                unordered: Prefix::None,
75                section_prefix: "[",
76                section_suffix: "]\n",
77                delimiter: "\n",
78                multiline: "\n",
79                suffix: "\n",
80            },
81            Capability::Limited => OutputFormat {
82                writer: Writer::stringizer(),
83
84                ordered: Prefix::Unordered,
85                unordered: Prefix::Unordered,
86                section_prefix: "===",
87                section_suffix: "===\n",
88                delimiter: ":\n",
89                multiline: ":\n",
90                suffix: "\n",
91            },
92            Capability::Standard => OutputFormat {
93                writer: Writer::stringizer(),
94
95                ordered: Prefix::Ordered,
96                unordered: Prefix::Unordered,
97                section_prefix: "===",
98                section_suffix: "===\n",
99                delimiter: ": ",
100                multiline: ":\n",
101                suffix: "\n",
102            },
103            Capability::Markdown => OutputFormat {
104                writer: Writer::stringizer(),
105
106                ordered: Prefix::Ordered,
107                unordered: Prefix::Unordered,
108                section_prefix: "## ",
109                section_suffix: "\n",
110                delimiter: ": ",
111                multiline: ":\n",
112                suffix: "\n",
113            },
114            /*
115            todo!() // Add xml and json maybe?
116            */
117        }
118    }
119
120    /// Start a section header.
121    pub fn section_start(&mut self, string: &str) {
122        self.writer.write_str(self.section_prefix);
123        self.writer.write_str(string);
124        self.writer.write_str(self.section_suffix);
125    }
126
127    /// End current section.
128    pub fn section_end(&mut self) {
129        self.writer.write_str(self.suffix);
130    }
131
132    /// Write unordered key/value item (adds bullet when `unordered` is `Prefix::Unordered`).
133    pub fn single_item(&mut self, key: &str, text: &str) {
134        if self.unordered == Prefix::Unordered {
135            self.writer.write_str("- ");
136        }
137        self.writer.write_str(key);
138        self.writer.write_str(self.delimiter);
139        self.writer.write_str(text);
140        self.writer.write_str(self.suffix);
141    }
142
143    /// Write key/value line without list prefix.
144    pub fn single_line(&mut self, key: &str, text: &str) {
145        self.writer.write_str(key);
146        self.writer.write_str(self.delimiter);
147        self.writer.write_str(text);
148        self.writer.write_str(self.suffix);
149    }
150
151    /// Write quoted value; chooses quote kind based on content.
152    pub fn single_line_quoted(&mut self, key: &str, text: &str) {
153        self.writer.write_str(key);
154        self.writer.write_str(self.delimiter);
155        if text.contains('"') {
156            self.writer.write_char('\'');
157            self.writer.write_str(text);
158            self.writer.write_char('\'');
159        } else {
160            self.writer.write_char('"');
161            self.writer.write_str(text);
162            self.writer.write_char('"');
163        }
164        self.writer.write_str(self.suffix);
165    }
166
167    /// Write key/value with value wrapped in `<` `>`.
168    pub fn single_line_tagged(&mut self, key: &str, text: &str) {
169        self.writer.write_str(key);
170        self.writer.write_str(self.delimiter);
171        self.writer.write_char('<');
172        self.writer.write_str(text);
173        self.writer.write_char('>');
174        self.writer.write_str(self.suffix);
175    }
176
177    /// Write `Value` without list prefix using `Writer::print_value`.
178    pub fn value(&mut self, value: &Value) {
179        value.pretty_print(&Prefix::None, &mut self.writer);
180        self.writer.write_str("\n");
181    }
182
183    /// Write text line.
184    pub fn text(&mut self, string: &str) {
185        self.writer.write_str(string);
186        self.writer.write_str("\n");
187    }
188
189    #[allow(dead_code)]
190    /// Write `Value` using ordered prefix.
191    pub fn ordered_value(&mut self, value: &Value) {
192        value.pretty_print(&self.ordered, &mut self.writer);
193        self.writer.write_str("\n");
194    }
195
196    /// Write `Value` using unordered prefix.
197    pub fn unordered_value(&mut self, value: &Value) {
198        value.pretty_print(&self.unordered, &mut self.writer);
199        self.writer.write_str("\n");
200    }
201
202    /// Consume and return accumulated output.
203    pub fn finish(self) -> String {
204        self.writer.finish()
205    }
206}