squishling 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +34 -7
- data/docs/configuration.md +83 -26
- data/docs/failures.md +51 -14
- data/docs/routing.md +11 -5
- data/docs/schemas.md +29 -0
- data/lib/squishling/class_methods.rb +29 -10
- data/lib/squishling/configuration.rb +40 -10
- data/lib/squishling/definition.rb +36 -22
- data/lib/squishling/errors.rb +7 -4
- data/lib/squishling/invoker.rb +133 -39
- data/lib/squishling/model_path.rb +115 -0
- data/lib/squishling/params.rb +3 -2
- data/lib/squishling/router.rb +5 -5
- data/lib/squishling/schema.rb +45 -3
- data/lib/squishling/version.rb +1 -1
- data/lib/squishling.rb +6 -3
- metadata +2 -1
data/lib/squishling/schema.rb
CHANGED
|
@@ -7,6 +7,14 @@ module Squishling
|
|
|
7
7
|
CACHE = {}.compare_by_identity
|
|
8
8
|
CACHE_LOCK = Mutex.new
|
|
9
9
|
|
|
10
|
+
# Conditional keywords (Schematist's `given` and `dependent`) that providers' strict modes don't support.
|
|
11
|
+
# They're validated locally and kept out of the schema sent to the LLM.
|
|
12
|
+
LOCAL_ONLY_KEYWORDS = %w[if then else dependentRequired dependentSchemas].freeze
|
|
13
|
+
# Keywords whose value is a map of names to schemas: the names are data, never keywords to strip.
|
|
14
|
+
SCHEMA_MAPS = %w[properties patternProperties $defs definitions].freeze
|
|
15
|
+
# Keywords whose value is literal data, copied untouched.
|
|
16
|
+
DATA_KEYWORDS = %w[enum const default examples].freeze
|
|
17
|
+
|
|
10
18
|
class << self
|
|
11
19
|
def for(raw)
|
|
12
20
|
CACHE_LOCK.synchronize { CACHE[raw] ||= new(raw) }
|
|
@@ -18,9 +26,9 @@ module Squishling
|
|
|
18
26
|
end
|
|
19
27
|
end
|
|
20
28
|
|
|
21
|
-
# The payload handed to RubyLLM's `with_schema`: always strict.
|
|
29
|
+
# The payload handed to RubyLLM's `with_schema`: always strict, without LOCAL_ONLY_KEYWORDS.
|
|
22
30
|
attr_reader :llm_schema
|
|
23
|
-
# The
|
|
31
|
+
# The full JSON Schema used for validation and result typing.
|
|
24
32
|
attr_reader :json_schema
|
|
25
33
|
|
|
26
34
|
def initialize(raw)
|
|
@@ -36,7 +44,7 @@ module Squishling
|
|
|
36
44
|
@llm_schema = {
|
|
37
45
|
"name" => hash["name"] || body["title"],
|
|
38
46
|
"description" => hash["description"] || body["description"],
|
|
39
|
-
"schema" => @json_schema,
|
|
47
|
+
"schema" => provider_schema(@json_schema),
|
|
40
48
|
"strict" => true
|
|
41
49
|
}.compact
|
|
42
50
|
@validator = JSONSchemer.schema(@json_schema)
|
|
@@ -72,6 +80,40 @@ module Squishling
|
|
|
72
80
|
end
|
|
73
81
|
end
|
|
74
82
|
|
|
83
|
+
# A copy of the schema without the conditional keywords. An allOf made only of conditionals (how
|
|
84
|
+
# Schematist emits several `given` blocks) goes too.
|
|
85
|
+
def provider_schema(node)
|
|
86
|
+
case node
|
|
87
|
+
when Array then node.map { |item| provider_schema(item) }
|
|
88
|
+
when Hash
|
|
89
|
+
copy = node.each_with_object({}) do |(key, value), stripped|
|
|
90
|
+
next if LOCAL_ONLY_KEYWORDS.include?(key)
|
|
91
|
+
|
|
92
|
+
stripped[key] =
|
|
93
|
+
if DATA_KEYWORDS.include?(key)
|
|
94
|
+
value
|
|
95
|
+
elsif SCHEMA_MAPS.include?(key) && value.is_a?(Hash)
|
|
96
|
+
value.transform_values { |schema| provider_schema(schema) }
|
|
97
|
+
else
|
|
98
|
+
provider_schema(value)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
without_conditional_all_of(copy, node)
|
|
102
|
+
else node
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def without_conditional_all_of(copy, original)
|
|
107
|
+
return copy unless original["allOf"].is_a?(Array)
|
|
108
|
+
|
|
109
|
+
kept = copy["allOf"].zip(original["allOf"]).reject { |_, branch| conditional_only?(branch) }.map(&:first)
|
|
110
|
+
kept.empty? ? copy.except("allOf") : copy.merge("allOf" => kept)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def conditional_only?(branch)
|
|
114
|
+
branch.is_a?(Hash) && branch.any? && (branch.keys - LOCAL_ONLY_KEYWORDS - %w[description $comment]).empty?
|
|
115
|
+
end
|
|
116
|
+
|
|
75
117
|
# Some schema objects emit a { name:, description:, schema: {...} } wrapper instead of a bare schema.
|
|
76
118
|
def wrapped?(hash)
|
|
77
119
|
hash["schema"].is_a?(Hash) && !hash.key?("type") && !hash.key?("properties")
|
data/lib/squishling/version.rb
CHANGED
data/lib/squishling.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "squishling/version"
|
|
|
9
9
|
require_relative "squishling/errors"
|
|
10
10
|
require_relative "squishling/configuration"
|
|
11
11
|
require_relative "squishling/params"
|
|
12
|
+
require_relative "squishling/model_path"
|
|
12
13
|
require_relative "squishling/result"
|
|
13
14
|
require_relative "squishling/schema"
|
|
14
15
|
require_relative "squishling/source"
|
|
@@ -58,8 +59,10 @@ module Squishling
|
|
|
58
59
|
# handle this input. Returns the typed result (squished? true, or false when the declared fallback supplied
|
|
59
60
|
# it); return it from the method. The overrides apply to this call only: append_instructions adds to (or,
|
|
60
61
|
# with false, replaces) the declared sections, context is sent alongside the declared squish_context,
|
|
61
|
-
# model/provider/instructions replace the declared ones, and params merge key
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
# model/escalation (one or the other)/provider/instructions replace the declared ones, and params merge key
|
|
63
|
+
# by key over them.
|
|
64
|
+
def squish!(append_instructions: nil, context: nil, instructions: nil, model: nil, escalation: nil, provider: nil,
|
|
65
|
+
params: nil)
|
|
66
|
+
Router.hand_off(self, { append_instructions:, context:, instructions:, model:, escalation:, provider:, params: })
|
|
64
67
|
end
|
|
65
68
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: squishling
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Carroll
|
|
@@ -81,6 +81,7 @@ files:
|
|
|
81
81
|
- lib/squishling/definition.rb
|
|
82
82
|
- lib/squishling/errors.rb
|
|
83
83
|
- lib/squishling/invoker.rb
|
|
84
|
+
- lib/squishling/model_path.rb
|
|
84
85
|
- lib/squishling/params.rb
|
|
85
86
|
- lib/squishling/result.rb
|
|
86
87
|
- lib/squishling/router.rb
|