Skip to content

Commit

Permalink
remove options upon openai schema usage
Browse files Browse the repository at this point in the history
  • Loading branch information
JR-1991 committed Dec 13, 2024
1 parent ae4950b commit a18b373
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/bin/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ fn convert(args: ConvertArgs) -> Result<(), Box<dyn Error>> {

// Render the template.
let rendered = match args.template {
Templates::JsonSchema => model.json_schema(args.root)?,
Templates::JsonSchema => model.json_schema(args.root, false)?,
_ => render_jinja_template(&args.template, &mut model, None)?,
};

Expand Down Expand Up @@ -350,7 +350,7 @@ fn render_all_json_schemes(
fs::create_dir_all(outdir)?;

// Render the JSON Schema for each entity
model.json_schema_all(outdir.to_path_buf())?;
model.json_schema_all(outdir.to_path_buf(), false)?;

Ok(())
}
Expand Down
9 changes: 7 additions & 2 deletions src/bindings/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,19 @@ pub fn convert_to(markdown_content: &str, template: Templates) -> Result<String,
///
/// * `markdown_content` - A string slice that holds the markdown content to be converted.
/// * `root` - The root object to use for the schema.
/// * `openai` - Whether to remove options from the schema properties. OpenAI does not support options.
///
/// # Returns
///
/// A `Result` which is:
/// - `Ok(JsValue)` if the conversion is successful.
/// - `Err(JsValue)` if there is an error during parsing or conversion.
#[wasm_bindgen]
pub fn json_schema(markdown_content: &str, root: Option<String>) -> Result<String, JsValue> {
pub fn json_schema(
markdown_content: &str,
root: Option<String>,
openai: bool,
) -> Result<String, JsValue> {
let model = DataModel::from_markdown_string(markdown_content)
.map_err(|e| JsValue::from_str(&format!("Error parsing markdown content: {}", e)))?;

Expand All @@ -103,7 +108,7 @@ pub fn json_schema(markdown_content: &str, root: Option<String>) -> Result<Strin
.clone(),
};

let json_schema = to_json_schema(&model, &root)
let json_schema = to_json_schema(&model, &root, openai)
.map_err(|e| JsValue::from_str(&format!("Error serializing schema: {}", e)))?;

// Directly return the JSON schema object instead of converting it to a JsValue
Expand Down
16 changes: 12 additions & 4 deletions src/datamodel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ impl DataModel {
// # Returns
//
// A JSON schema string
pub fn json_schema(&self, obj_name: Option<String>) -> Result<String, Box<dyn Error>> {
pub fn json_schema(
&self,
obj_name: Option<String>,
openai: bool,
) -> Result<String, Box<dyn Error>> {
if self.objects.is_empty() {
panic!("No objects found in the markdown file");
}
Expand All @@ -112,11 +116,14 @@ impl DataModel {
if self.objects.iter().all(|o| o.name != name) {
panic!("Object '{}' not found in the markdown file", name);
}
Ok(serde_json::to_string_pretty(&to_json_schema(self, &name)?)?)
Ok(serde_json::to_string_pretty(&to_json_schema(
self, &name, openai,
)?)?)
}
None => Ok(serde_json::to_string_pretty(&to_json_schema(
self,
&self.objects[0].name,
openai,
)?)?),
}
}
Expand All @@ -125,6 +132,7 @@ impl DataModel {
// and write them to a file
//
// * `path` - Path to the directory where the JSON schema files will be written
// * `openai` - Whether to remove options from the schema properties. OpenAI does not support options.
//
// # Panics
//
Expand All @@ -137,7 +145,7 @@ impl DataModel {
// model.parse("path/to/file.md".to_string());
// model.json_schema_all("path/to/directory".to_string());
// ```
pub fn json_schema_all(&self, path: PathBuf) -> Result<(), Box<dyn Error>> {
pub fn json_schema_all(&self, path: PathBuf, openai: bool) -> Result<(), Box<dyn Error>> {
if self.objects.is_empty() {
panic!("No objects found in the markdown file");
}
Expand All @@ -149,7 +157,7 @@ impl DataModel {

let base_path = path.to_str().ok_or("Failed to convert path to string")?;
for object in &self.objects {
let schema = to_json_schema(self, &object.name)?;
let schema = to_json_schema(self, &object.name, openai)?;
let file_name = format!("{}/{}.json", base_path, object.name);
fs::write(file_name, serde_json::to_string_pretty(&schema)?)
.expect("Could not write file");
Expand Down
61 changes: 56 additions & 5 deletions src/json/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use std::{
use crate::{
attribute::Attribute,
datamodel::DataModel,
markdown::frontmatter::FrontMatter,
object::{Enumeration, Object},
validation::BASIC_TYPES,
};
Expand All @@ -47,7 +48,11 @@ const SCHEMA: &str = "https://json-schema.org/draft/2020-12/schema";
/// # Returns
///
/// A `Result` containing the `SchemaObject` or an error message.
pub fn to_json_schema(model: &DataModel, root: &str) -> Result<schema::SchemaObject, String> {
pub fn to_json_schema(
model: &DataModel,
root: &str,
openai: bool,
) -> Result<schema::SchemaObject, String> {
let root_object = retrieve_object(model, root)?;

let mut schema_object = schema::SchemaObject::try_from(root_object)?;
Expand All @@ -62,10 +67,7 @@ pub fn to_json_schema(model: &DataModel, root: &str) -> Result<schema::SchemaObj
schema_object.definitions = definitions;

if let Some(config) = model.config.clone() {
schema_object.id = Some(config.repo.clone());
if let Some(prefixes) = config.prefixes {
resolve_prefixes(&mut schema_object, &prefixes);
}
post_process_schema(&mut schema_object, &config, openai);
}

Ok(schema_object)
Expand Down Expand Up @@ -182,6 +184,12 @@ fn collect_definitions(
Ok(())
}

/// Resolves prefixes in the schema properties using the provided prefixes map.
///
/// # Arguments
///
/// * `schema` - A mutable reference to the `SchemaObject`.
/// * `prefixes` - A reference to a map containing prefix-to-URI mappings.
fn resolve_prefixes(schema: &mut schema::SchemaObject, prefixes: &HashMap<String, String>) {
for (_, property) in schema.properties.iter_mut() {
if let Some(reference) = property.term.clone() {
Expand All @@ -193,6 +201,49 @@ fn resolve_prefixes(schema: &mut schema::SchemaObject, prefixes: &HashMap<String
}
}

/// Removes options from the schema properties.
///
/// # Arguments
///
/// * `schema` - A mutable reference to the `SchemaObject`.
fn remove_options(schema: &mut schema::SchemaObject) {
for (_, property) in schema.properties.iter_mut() {
property.options = HashMap::new();
}
}

/// Post-processes the schema object by setting its ID, resolving prefixes, and optionally removing options.
///
/// # Arguments
///
/// * `schema_object` - A mutable reference to the `SchemaObject` to be post-processed.
/// * `config` - A reference to the `FrontMatter` configuration containing repository and prefix information.
/// * `no_options` - A boolean flag indicating whether to remove options from the schema properties.
fn post_process_schema(
schema_object: &mut schema::SchemaObject,
config: &FrontMatter,
openai: bool,
) {
schema_object.id = Some(config.repo.clone());
if let Some(prefixes) = &config.prefixes {
resolve_prefixes(schema_object, prefixes);

if openai {
remove_options(schema_object);
}

for (_, definition) in schema_object.definitions.iter_mut() {
if let schema::SchemaType::Object(schema_object) = definition {
resolve_prefixes(schema_object, prefixes);

if openai {
remove_options(schema_object);
}
}
}
}
}

impl TryFrom<&Enumeration> for schema::SchemaType {
type Error = String;

Expand Down
2 changes: 1 addition & 1 deletion src/llm/extraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn prepare_response_format(
root: &str,
multiple: bool,
) -> Result<Value, Box<dyn std::error::Error>> {
let schema = to_json_schema(model, root)?;
let schema = to_json_schema(model, root, true)?;

if multiple {
Ok(json!(
Expand Down
6 changes: 3 additions & 3 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ fn serialize_to_json_schema(

match root {
Some(root) => {
let schema = model.json_schema(Some(root))?;
let schema = model.json_schema(Some(root), false)?;
save_to_file(out, &schema)?;
print_render_msg(out, &Templates::JsonSchema);
Ok(())
Expand Down Expand Up @@ -376,15 +376,15 @@ fn serialize_all_json_schemes(
match merge_state {
MergeState::Merge => {
let model = build_models(specs)?;
model.json_schema_all(out.to_path_buf())?;
model.json_schema_all(out.to_path_buf(), false)?;
print_render_msg(out, &Templates::JsonSchemaAll);
Ok(())
}
MergeState::NoMerge => {
for spec in specs {
let model = DataModel::from_markdown(spec)?;
let path = out.join(get_file_name(spec));
model.json_schema_all(path.to_path_buf())?;
model.json_schema_all(path.to_path_buf(), false)?;
print_render_msg(&path, &Templates::JsonSchemaAll);
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions tests/data/expected_json_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@
"names": {
"title": "names",
"type": "array",
"$term": "schema:hello",
"$term": "http://schema.org/hello",
"items": {
"type": "string"
}
},
"number": {
"title": "number",
"type": "number",
"$term": "schema:one",
"$term": "http://schema.org/one",
"minimum": 0.0
}
},
Expand Down
97 changes: 97 additions & 0 deletions tests/data/expected_json_schema_openai.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.github.com/my/repo/",
"title": "Test",
"type": "object",
"properties": {
"array_valued": {
"title": "array_valued",
"type": "array",
"$term": "http://schema.org/something",
"items": {
"$ref": "#/$defs/Test2"
}
},
"multiple_types": {
"title": "multiple_types",
"oneOf": [
{
"type": "number"
},
{
"$ref": "#/$defs/Test2"
}
]
},
"multiple_types_array": {
"title": "multiple_types_array",
"type": "array",
"items": {
"oneOf": [
{
"type": "number"
},
{
"$ref": "#/$defs/Test2"
}
]
}
},
"name": {
"title": "name",
"type": "string",
"description": "A test description",
"$term": "http://schema.org/hello"
},
"number": {
"title": "number",
"type": "number",
"$term": "http://schema.org/one"
},
"ontology": {
"title": "ontology",
"$ref": "#/$defs/Ontology"
},
"single_valued": {
"title": "single_valued",
"type": "object",
"$ref": "#/$defs/Test2"
}
},
"$defs": {
"Ontology": {
"title": "Ontology",
"type": "string",
"enum": [
"https://www.evidenceontology.org/term/",
"https://amigo.geneontology.org/amigo/term/",
"http://semanticscience.org/resource/"
]
},
"Test2": {
"title": "Test2",
"type": "object",
"properties": {
"names": {
"title": "names",
"type": "array",
"$term": "http://schema.org/hello",
"items": {
"type": "string"
}
},
"number": {
"title": "number",
"type": "number",
"$term": "http://schema.org/one"
}
},
"required": [],
"additionalProperties": false
}
},
"required": [
"name"
],
"additionalProperties": false
}
Loading

0 comments on commit a18b373

Please sign in to comment.