ferrin_core/output/
mod.rs1use std::fmt;
7use std::sync::Arc;
8
9use ferrin_schema::JsonSchema;
10use ferrin_schema::Schema;
11use ferrin_spec::FinishReason;
12use ferrin_spec::JsonValue;
13use ferrin_spec::ResponseFormat;
14use ferrin_spec::ResponseMetadata;
15use ferrin_spec::Usage;
16use serde::de::DeserializeOwned;
17
18use crate::error::Error;
19
20mod local_refs;
21mod strategies;
22
23pub use strategies::ArrayOutput;
24pub use strategies::ChoiceOutput;
25pub use strategies::JsonOutput;
26pub use strategies::ObjectOutput;
27pub use strategies::TextOutput;
28
29#[derive(Debug, Clone, PartialEq)]
32pub struct OutputContext {
33 pub response: ResponseMetadata,
35 pub usage: Usage,
37 pub finish_reason: FinishReason,
39}
40
41pub trait OutputHandler<O>: Send + Sync + 'static {
46 fn validate_configuration(&self) -> Result<(), Error> {
52 Ok(())
53 }
54
55 fn response_format(&self) -> Option<ResponseFormat>;
57
58 fn wants_output(&self) -> bool {
60 true
61 }
62
63 fn parse_complete(&self, text: &str, ctx: &OutputContext) -> Result<O, Error>;
70
71 fn parse_partial(&self, text: &str) -> Option<JsonValue>;
74
75 fn typed_partial(&self, _value: &JsonValue) -> Option<O> {
78 None
79 }
80
81 fn parse_elements(&self, _text: &str) -> Option<Vec<JsonValue>> {
84 None
85 }
86
87 fn parse_typed_elements(&self, text: &str) -> Option<O> {
89 self.parse_elements(text)
90 .and_then(|elements| self.typed_partial(&JsonValue::Array(elements)))
91 }
92
93 fn max_elements(&self) -> Option<usize> {
95 None
96 }
97}
98
99#[derive(Debug, Clone, Copy, Default)]
102pub struct NoOutput;
103
104impl OutputHandler<()> for NoOutput {
105 fn response_format(&self) -> Option<ResponseFormat> {
106 None
107 }
108
109 fn wants_output(&self) -> bool {
110 false
111 }
112
113 fn parse_complete(&self, _text: &str, _ctx: &OutputContext) -> Result<(), Error> {
114 Ok(())
115 }
116
117 fn parse_partial(&self, _text: &str) -> Option<JsonValue> {
118 None
119 }
120}
121
122pub struct Output<T> {
124 handler: Arc<dyn OutputHandler<T>>,
125}
126
127impl<T> Clone for Output<T> {
128 fn clone(&self) -> Self {
129 Self {
130 handler: Arc::clone(&self.handler),
131 }
132 }
133}
134
135impl<T> fmt::Debug for Output<T> {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str("Output(..)")
138 }
139}
140
141impl<T> Output<T> {
142 pub fn custom(handler: impl OutputHandler<T>) -> Self {
144 Self {
145 handler: Arc::new(handler),
146 }
147 }
148
149 #[must_use]
151 pub fn handler(&self) -> Arc<dyn OutputHandler<T>> {
152 Arc::clone(&self.handler)
153 }
154}
155
156impl Output<String> {
157 #[must_use]
159 pub fn text() -> Self {
160 Self::custom(TextOutput)
161 }
162
163 #[must_use]
165 pub fn choice(options: impl IntoIterator<Item = impl Into<String>>) -> Self {
166 Self::custom(ChoiceOutput::new(options))
167 }
168}
169
170impl<T: DeserializeOwned + JsonSchema + Send + Sync + 'static> Output<T> {
171 #[must_use]
173 pub fn object() -> Self {
174 Self::custom(ObjectOutput::new(Schema::<T>::derived()))
175 }
176}
177
178impl<T: DeserializeOwned + Send + Sync + 'static> Output<T> {
179 #[must_use]
181 pub fn object_with(schema: Schema<T>) -> Self {
182 Self::custom(ObjectOutput::new(schema))
183 }
184}
185
186impl<T: DeserializeOwned + JsonSchema + Send + Sync + 'static> Output<Vec<T>> {
187 #[must_use]
189 pub fn array() -> Self {
190 Self::custom(ArrayOutput::new(Schema::<T>::derived()))
191 }
192}
193
194impl<T: DeserializeOwned + Send + Sync + 'static> Output<Vec<T>> {
195 #[must_use]
197 pub fn array_with(element: Schema<T>) -> Self {
198 Self::custom(ArrayOutput::new(element))
199 }
200}
201
202impl Output<JsonValue> {
203 #[must_use]
205 pub fn json() -> Self {
206 Self::custom(JsonOutput::new(None))
207 }
208
209 #[must_use]
211 pub fn json_with_schema(schema: JsonValue) -> Self {
212 Self::custom(JsonOutput::new(Some(schema)))
213 }
214}
215
216pub trait ArrayElements {
218 type Element;
220}
221
222impl<T> ArrayElements for Vec<T> {
223 type Element = T;
224}
225
226#[derive(Debug, Clone, PartialEq)]
228pub struct PartialOutput<T> {
229 pub value: JsonValue,
231 pub typed: Option<T>,
233}