use anyhow::Result; use language_model::LanguageModelToolSchemaFormat; use schemars::{ JsonSchema, schema::{RootSchema, Schema, SchemaObject}, }; pub fn json_schema_for( format: LanguageModelToolSchemaFormat, ) -> Result { let schema = root_schema_for::(format); schema_to_json(&schema, format) } fn schema_to_json( schema: &RootSchema, format: LanguageModelToolSchemaFormat, ) -> Result { let mut value = serde_json::to_value(schema)?; assistant_tool::adapt_schema_to_format(&mut value, format)?; Ok(value) } fn root_schema_for(format: LanguageModelToolSchemaFormat) -> RootSchema { let mut generator = match format { LanguageModelToolSchemaFormat::JsonSchema => schemars::SchemaGenerator::default(), LanguageModelToolSchemaFormat::JsonSchemaSubset => { schemars::r#gen::SchemaSettings::default() .with(|settings| { settings.meta_schema = None; settings.inline_subschemas = true; settings .visitors .push(Box::new(TransformToJsonSchemaSubsetVisitor)); }) .into_generator() } }; generator.root_schema_for::() } #[derive(Debug, Clone)] struct TransformToJsonSchemaSubsetVisitor; impl schemars::visit::Visitor for TransformToJsonSchemaSubsetVisitor { fn visit_root_schema(&mut self, root: &mut RootSchema) { schemars::visit::visit_root_schema(self, root) } fn visit_schema(&mut self, schema: &mut Schema) { schemars::visit::visit_schema(self, schema) } fn visit_schema_object(&mut self, schema: &mut SchemaObject) { // Ensure that the type field is not an array, this happens when we use // Option, the type will be [T, "null"]. if let Some(instance_type) = schema.instance_type.take() { schema.instance_type = match instance_type { schemars::schema::SingleOrVec::Single(t) => { Some(schemars::schema::SingleOrVec::Single(t)) } schemars::schema::SingleOrVec::Vec(items) => items .into_iter() .next() .map(schemars::schema::SingleOrVec::from), }; } // One of is not supported, use anyOf instead. if let Some(subschema) = schema.subschemas.as_mut() { if let Some(one_of) = subschema.one_of.take() { subschema.any_of = Some(one_of); } } schemars::visit::visit_schema_object(self, schema) } }