use std::fmt;
use std::sync::Arc;
use ferrin_schema::JsonSchema;
use ferrin_schema::Schema;
use ferrin_spec::FinishReason;
use ferrin_spec::JsonValue;
use ferrin_spec::ResponseFormat;
use ferrin_spec::ResponseMetadata;
use ferrin_spec::Usage;
use serde::de::DeserializeOwned;
use crate::error::Error;
mod local_refs;
mod strategies;
pub use strategies::ArrayOutput;
pub use strategies::ChoiceOutput;
pub use strategies::JsonOutput;
pub use strategies::ObjectOutput;
pub use strategies::TextOutput;
#[derive(Debug, Clone, PartialEq)]
pub struct OutputContext {
pub response: ResponseMetadata,
pub usage: Usage,
pub finish_reason: FinishReason,
}
pub trait OutputHandler<O>: Send + Sync + 'static {
fn validate_configuration(&self) -> Result<(), Error> {
Ok(())
}
fn response_format(&self) -> Option<ResponseFormat>;
fn wants_output(&self) -> bool {
true
}
fn parse_complete(&self, text: &str, ctx: &OutputContext) -> Result<O, Error>;
fn parse_partial(&self, text: &str) -> Option<JsonValue>;
fn typed_partial(&self, _value: &JsonValue) -> Option<O> {
None
}
fn parse_elements(&self, _text: &str) -> Option<Vec<JsonValue>> {
None
}
fn parse_typed_elements(&self, text: &str) -> Option<O> {
self.parse_elements(text)
.and_then(|elements| self.typed_partial(&JsonValue::Array(elements)))
}
fn max_elements(&self) -> Option<usize> {
None
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoOutput;
impl OutputHandler<()> for NoOutput {
fn response_format(&self) -> Option<ResponseFormat> {
None
}
fn wants_output(&self) -> bool {
false
}
fn parse_complete(&self, _text: &str, _ctx: &OutputContext) -> Result<(), Error> {
Ok(())
}
fn parse_partial(&self, _text: &str) -> Option<JsonValue> {
None
}
}
pub struct Output<T> {
handler: Arc<dyn OutputHandler<T>>,
}
impl<T> Clone for Output<T> {
fn clone(&self) -> Self {
Self {
handler: Arc::clone(&self.handler),
}
}
}
impl<T> fmt::Debug for Output<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Output(..)")
}
}
impl<T> Output<T> {
pub fn custom(handler: impl OutputHandler<T>) -> Self {
Self {
handler: Arc::new(handler),
}
}
#[must_use]
pub fn handler(&self) -> Arc<dyn OutputHandler<T>> {
Arc::clone(&self.handler)
}
}
impl Output<String> {
#[must_use]
pub fn text() -> Self {
Self::custom(TextOutput)
}
#[must_use]
pub fn choice(options: impl IntoIterator<Item = impl Into<String>>) -> Self {
Self::custom(ChoiceOutput::new(options))
}
}
impl<T: DeserializeOwned + JsonSchema + Send + Sync + 'static> Output<T> {
#[must_use]
pub fn object() -> Self {
Self::custom(ObjectOutput::new(Schema::<T>::derived()))
}
}
impl<T: DeserializeOwned + Send + Sync + 'static> Output<T> {
#[must_use]
pub fn object_with(schema: Schema<T>) -> Self {
Self::custom(ObjectOutput::new(schema))
}
}
impl<T: DeserializeOwned + JsonSchema + Send + Sync + 'static> Output<Vec<T>> {
#[must_use]
pub fn array() -> Self {
Self::custom(ArrayOutput::new(Schema::<T>::derived()))
}
}
impl<T: DeserializeOwned + Send + Sync + 'static> Output<Vec<T>> {
#[must_use]
pub fn array_with(element: Schema<T>) -> Self {
Self::custom(ArrayOutput::new(element))
}
}
impl Output<JsonValue> {
#[must_use]
pub fn json() -> Self {
Self::custom(JsonOutput::new(None))
}
#[must_use]
pub fn json_with_schema(schema: JsonValue) -> Self {
Self::custom(JsonOutput::new(Some(schema)))
}
}
pub trait ArrayElements {
type Element;
}
impl<T> ArrayElements for Vec<T> {
type Element = T;
}
#[derive(Debug, Clone, PartialEq)]
pub struct PartialOutput<T> {
pub value: JsonValue,
pub typed: Option<T>,
}