Skip to content

Commit 1a4518a

Browse files
ms-jpqstainless-app[bot]
authored andcommitted
chore: extract reused JSON schema references even in unions (#761)
1 parent e67e54c commit 1a4518a

File tree

2 files changed

+23
-25
lines changed

2 files changed

+23
-25
lines changed

lib/openai/helpers/structured_output/json_schema_converter.rb

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ module JsonSchemaConverter
1212
end
1313
end.freeze
1414
# @api private
15-
COUNTER = Object.new.tap do
16-
_1.define_singleton_method(:inspect) do
17-
"#<#{OpenAI::Helpers::StructuredOutput::JsonSchemaConverter}::COUNTER>"
18-
end
19-
end.freeze
20-
# @api private
2115
NO_REF = Object.new.tap do
2216
_1.define_singleton_method(:inspect) do
2317
"#<#{OpenAI::Helpers::StructuredOutput::JsonSchemaConverter}::NO_REF>"
@@ -81,14 +75,13 @@ def to_nilable(schema)
8175
def cache_def!(state, type:, &blk)
8276
defs, path = state.fetch_values(:defs, :path)
8377
if (stored = defs[type])
84-
stored[OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::COUNTER] += 1
85-
stored.fetch(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER)
78+
pointers = stored.fetch(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER)
79+
pointers.first.except(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::NO_REF).tap { pointers << _1 }
8680
else
8781
ref_path = String.new
8882
ref = {"$ref": ref_path}
8983
stored = {
90-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER => ref,
91-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::COUNTER => 1
84+
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER => [ref]
9285
}
9386
defs.store(type, stored)
9487
schema = blk.call
@@ -112,17 +105,21 @@ def to_json_schema(type)
112105
)
113106
reused_defs = {}
114107
defs.each_value do |acc|
115-
ref = acc.fetch(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER)
116-
if (no_ref = ref.delete(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::NO_REF))
117-
acc[OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::COUNTER] -= 1
108+
sch = acc.except(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER)
109+
pointers = acc.fetch(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER)
110+
111+
no_refs, refs = pointers.partition do
112+
_1.delete(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::NO_REF)
118113
end
119-
cnt = acc.fetch(OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::COUNTER)
120114

121-
sch = acc.except(
122-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::POINTER,
123-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter::COUNTER
124-
)
125-
cnt > 1 && !no_ref ? reused_defs.store(ref.fetch(:$ref), sch) : ref.replace(sch)
115+
case refs
116+
in [_, ref, *]
117+
reused_defs.store(ref.fetch(:$ref), sch)
118+
in [ref]
119+
ref.replace(sch)
120+
else
121+
end
122+
no_refs.each { _1.replace(sch) }
126123
end
127124

128125
xformed = reused_defs.transform_keys { _1.delete_prefix("#/$defs/") }

test/openai/helpers/structured_output_test.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,19 +311,20 @@ def test_definition_reusing
311311
]
312312
},
313313
M11 => {
314-
type: "object",
315-
properties: {
314+
:$defs => {".a/?.0/[]" => {type: "array", items: {type: "string"}}},
315+
:type => "object",
316+
:properties => {
316317
a: {
317318
anyOf: [
318319
{type: "array", items: {type: "string"}},
319320
{type: "array", items: {type: "string"}}
320321
]
321322
},
322-
b: {type: "array", items: {type: "string"}},
323-
c: {type: "array", items: {type: "string"}}
323+
b: {:$ref => "#/$defs/.a/?.0/[]"},
324+
c: {:$ref => "#/$defs/.a/?.0/[]"}
324325
},
325-
required: %w[a b c],
326-
additionalProperties: false
326+
:required => %w[a b c],
327+
:additionalProperties => false
327328
}
328329
}
329330

0 commit comments

Comments
 (0)