squishling 0.4.0 → 0.5.1
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 +149 -0
- data/README.md +26 -10
- data/docs/adapters.md +257 -0
- data/docs/configuration.md +54 -19
- data/docs/failures.md +170 -22
- data/docs/harnesses.md +39 -15
- data/docs/measuring-tokens.md +12 -6
- data/docs/observers.md +3 -3
- data/docs/routing.md +14 -3
- data/docs/schemas.md +24 -2
- data/docs/testing.md +187 -0
- data/docs/verifying.md +86 -0
- data/lib/squishling/adapter/error_body.rb +54 -0
- data/lib/squishling/adapter.rb +328 -0
- data/lib/squishling/adapters/openai_compatible.rb +311 -0
- data/lib/squishling/adapters/openai_options.rb +114 -0
- data/lib/squishling/adapters/openai_transport.rb +123 -0
- data/lib/squishling/adapters/ruby_llm.rb +229 -0
- data/lib/squishling/budget.rb +71 -0
- data/lib/squishling/class_methods.rb +43 -7
- data/lib/squishling/class_registry.rb +63 -0
- data/lib/squishling/configuration.rb +58 -6
- data/lib/squishling/definition.rb +90 -16
- data/lib/squishling/errors.rb +94 -7
- data/lib/squishling/harness/judged_squishsum.rb +2 -2
- data/lib/squishling/harness/option_types.rb +2 -1
- data/lib/squishling/harness/registry.rb +8 -36
- data/lib/squishling/invoker.rb +44 -14
- data/lib/squishling/judge.rb +16 -3
- data/lib/squishling/llm_client.rb +66 -132
- data/lib/squishling/model_path.rb +60 -13
- data/lib/squishling/named_registry.rb +64 -0
- data/lib/squishling/observer.rb +13 -4
- data/lib/squishling/output_check.rb +13 -9
- data/lib/squishling/params.rb +7 -24
- data/lib/squishling/railtie.rb +10 -0
- data/lib/squishling/result.rb +50 -16
- data/lib/squishling/rotation.rb +5 -0
- data/lib/squishling/router.rb +3 -2
- data/lib/squishling/rspec.rb +18 -0
- data/lib/squishling/run/messages.rb +40 -3
- data/lib/squishling/run/rejections.rb +43 -0
- data/lib/squishling/run/reports.rb +63 -0
- data/lib/squishling/run.rb +60 -53
- data/lib/squishling/schema.rb +50 -9
- data/lib/squishling/secret.rb +45 -0
- data/lib/squishling/squawk.rb +37 -4
- data/lib/squishling/tasks/squishling.rake +18 -0
- data/lib/squishling/testing/adapter.rb +194 -0
- data/lib/squishling/testing/checks.rb +39 -0
- data/lib/squishling/testing/direct.rb +41 -0
- data/lib/squishling/testing/errors.rb +18 -0
- data/lib/squishling/testing/fixtures.rb +206 -0
- data/lib/squishling/testing/helpers.rb +71 -0
- data/lib/squishling/testing/minitest.rb +32 -0
- data/lib/squishling/testing/observer.rb +31 -0
- data/lib/squishling/testing/reply.rb +60 -0
- data/lib/squishling/testing/request.rb +75 -0
- data/lib/squishling/testing/rspec.rb +30 -0
- data/lib/squishling/testing/script.rb +189 -0
- data/lib/squishling/testing/state.rb +74 -0
- data/lib/squishling/testing/stubs.rb +112 -0
- data/lib/squishling/testing.rb +126 -0
- data/lib/squishling/verification.rb +39 -0
- data/lib/squishling/verifier.rb +179 -0
- data/lib/squishling/version.rb +1 -1
- data/lib/squishling/wrapper.rb +19 -1
- data/lib/squishling.rb +60 -4
- metadata +43 -21
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Squishling
|
|
4
|
+
# Checks squished classes without making a request: what can only otherwise fail on a method's first call (its
|
|
5
|
+
# output schema and typed result, its callables' keywords) and the settings the layers resolve to. Collects every
|
|
6
|
+
# problem rather than stopping at the first. See Squishling.verify.
|
|
7
|
+
class Verifier
|
|
8
|
+
# Checks one squished method's Definition. Each check returns messages, or raises what the call would.
|
|
9
|
+
class MethodCheck
|
|
10
|
+
CHECKS = %i[schema declarations harness callables].freeze
|
|
11
|
+
|
|
12
|
+
def initialize(definition)
|
|
13
|
+
@definition = definition
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def problems
|
|
17
|
+
CHECKS.flat_map { |check| run(check) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def run(check)
|
|
23
|
+
Array(send(check)).map { |message| problem(message) }
|
|
24
|
+
rescue ConfigurationError, ArgumentError => e
|
|
25
|
+
[problem(e.message)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def problem(message)
|
|
29
|
+
Verification::Problem.new(label: @definition.label, message:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The output schema, its typed result (the Data class a call returns), and the JSON Schema itself.
|
|
33
|
+
def schema
|
|
34
|
+
schema = @definition.schema
|
|
35
|
+
messages = []
|
|
36
|
+
messages << "has no output_schema" unless schema
|
|
37
|
+
messages << "has no purpose" unless @definition.purpose_declared?
|
|
38
|
+
return messages unless schema
|
|
39
|
+
|
|
40
|
+
schema.result_class
|
|
41
|
+
messages + JSONSchemer.schema(schema.json_schema).validate_schema.map { |error| error["error"] }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The escalation, generation params, and squawk this method resolves to across method, class, and config.
|
|
45
|
+
def declarations
|
|
46
|
+
steps = @definition.declared_path.first.reject { |step| step[:model].nil? }
|
|
47
|
+
ModelPath.from_escalation(steps, "#{@definition.label} escalation") if steps.any?
|
|
48
|
+
Params.normalize(@definition.params, "#{@definition.label} params")
|
|
49
|
+
Squawk.validate(@definition.squawk, @definition.label)
|
|
50
|
+
[]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def harness
|
|
54
|
+
harness = @definition.harness
|
|
55
|
+
messages = []
|
|
56
|
+
unless Harness::Registry.fetch(harness.type).equal?(harness.klass)
|
|
57
|
+
messages << "harness #{harness.type.inspect} was unregistered or replaced after it was declared"
|
|
58
|
+
end
|
|
59
|
+
messages << "the judge needs an adapter that supports judgments" if unsupported_judgment?(harness)
|
|
60
|
+
messages
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def unsupported_judgment?(harness)
|
|
64
|
+
judge = harness.options[:judge]
|
|
65
|
+
judge.is_a?(Hash) && judge[:type] == :judgment && !Squishling.config.resolved_adapter.judgments?
|
|
66
|
+
rescue ConfigurationError
|
|
67
|
+
false # no usable adapter: reported once, as the adapter's own problem
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Callables the method's inputs are passed to as keywords: include_when: of each step, and squish_when.
|
|
71
|
+
def callables
|
|
72
|
+
names, open = @definition.input_names
|
|
73
|
+
gates = @definition.declared_path.first.filter_map do |step|
|
|
74
|
+
[step[:include_when], "escalation step #{step[:model].inspect} include_when:"] if step[:include_when]
|
|
75
|
+
end
|
|
76
|
+
gates << [@definition.predicate, "squish_when"] if @definition.predicate
|
|
77
|
+
gates.flat_map { |callable, what| KeywordCheck.messages(callable, names, open).map { "#{what} #{_1}" } }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Whether a Proc called as instance_exec(**inputs, &proc) accepts the inputs.
|
|
82
|
+
module KeywordCheck
|
|
83
|
+
module_function
|
|
84
|
+
|
|
85
|
+
# Messages for how the proc can't be called with the inputs. open: any keyword may be an input (the method
|
|
86
|
+
# takes **), so none can be known.
|
|
87
|
+
def messages(callable, inputs, open)
|
|
88
|
+
return ["must be a Proc, got #{callable.inspect}"] unless callable.is_a?(Proc)
|
|
89
|
+
return [] if open
|
|
90
|
+
|
|
91
|
+
parameters = callable.parameters
|
|
92
|
+
[missing_keywords(parameters, inputs), unaccepted_keywords(callable, parameters, inputs),
|
|
93
|
+
missing_positionals(callable, parameters, inputs)].compact
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def missing_keywords(parameters, inputs)
|
|
97
|
+
missing = parameters.filter_map { |type, name| name if type == :keyreq } - inputs
|
|
98
|
+
return if missing.empty?
|
|
99
|
+
|
|
100
|
+
"requires #{keywords(missing)}, which #{missing.one? ? 'is not an input' : 'are not inputs'} of the method"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# A lambda with no positional parameters, or a proc that names keywords, raises on a keyword it doesn't take.
|
|
104
|
+
# Other procs and positional lambdas receive the keywords as a Hash, or ignore them.
|
|
105
|
+
def unaccepted_keywords(callable, parameters, inputs)
|
|
106
|
+
types = parameters.map(&:first)
|
|
107
|
+
return if types.include?(:keyrest) || !strict?(callable, types)
|
|
108
|
+
|
|
109
|
+
unaccepted = inputs - parameters.filter_map { |type, name| name if Squawk::KEYWORD_TYPES.include?(type) }
|
|
110
|
+
return if unaccepted.empty?
|
|
111
|
+
|
|
112
|
+
"doesn't accept #{keywords(unaccepted)}, which the method takes as input when a caller passes it (add **)"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# A lambda's required positional parameters get the keywords as one Hash, or nothing when there are none or
|
|
116
|
+
# the lambda names keywords itself.
|
|
117
|
+
def missing_positionals(callable, parameters, inputs)
|
|
118
|
+
return unless callable.lambda?
|
|
119
|
+
|
|
120
|
+
types = parameters.map(&:first)
|
|
121
|
+
required = types.count(:req)
|
|
122
|
+
given = types.intersect?(%i[key keyreq keyrest]) || inputs.empty? ? 0 : 1
|
|
123
|
+
return unless required > given
|
|
124
|
+
|
|
125
|
+
"takes #{required} positional argument(s), but is called with keywords only"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def strict?(callable, types)
|
|
129
|
+
keywords = types.intersect?(Squawk::KEYWORD_TYPES)
|
|
130
|
+
callable.lambda? ? keywords || !types.intersect?(%i[req opt rest]) : keywords
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def keywords(names)
|
|
134
|
+
names.map { |name| "#{name}:" }.join(", ")
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def initialize(classes)
|
|
139
|
+
classes.each { |klass| check_squished!(klass) }
|
|
140
|
+
@classes = classes
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def call
|
|
144
|
+
Verification::Report.new(adapter_problems + @classes.flat_map { |klass| class_problems(klass) })
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def check_squished!(klass)
|
|
150
|
+
return if klass.is_a?(Class) && klass.include?(Squishling)
|
|
151
|
+
|
|
152
|
+
raise ConfigurationError, "#{klass.inspect} doesn't include Squishling"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The configured adapter, or RubyLLM's, must be usable.
|
|
156
|
+
def adapter_problems
|
|
157
|
+
Squishling.config.resolved_adapter
|
|
158
|
+
[]
|
|
159
|
+
rescue ConfigurationError => e
|
|
160
|
+
[Verification::Problem.new(label: "adapter", message: e.message)]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def class_problems(klass)
|
|
164
|
+
klass.squished_methods.keys.flat_map do |name|
|
|
165
|
+
definition = klass.squishling_definition(name)
|
|
166
|
+
next [] if unused_default?(definition)
|
|
167
|
+
|
|
168
|
+
MethodCheck.new(definition).problems
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Every class has the default method (call) squished, whether or not it uses it.
|
|
173
|
+
def unused_default?(definition)
|
|
174
|
+
definition.name == ClassMethods::DEFAULT_METHOD && !definition.implemented? && definition.schema.nil?
|
|
175
|
+
rescue ConfigurationError, ArgumentError
|
|
176
|
+
false
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/squishling/version.rb
CHANGED
data/lib/squishling/wrapper.rb
CHANGED
|
@@ -4,6 +4,24 @@ module Squishling
|
|
|
4
4
|
# Module prepended to a squishling class. It intercepts squished methods regardless of
|
|
5
5
|
# whether they're defined before or after the `squish` declaration (or at all).
|
|
6
6
|
class Wrapper < Module
|
|
7
|
+
# Backtrace locations report real paths, so resolve this one too (a symlinked install keeps __FILE__ as given).
|
|
8
|
+
FILE = File.realpath(__FILE__)
|
|
9
|
+
private_constant :FILE
|
|
10
|
+
|
|
11
|
+
# Raised by the wrapper when a squished method has no implementation at all.
|
|
12
|
+
class NoImplementation < NotImplementedError; end
|
|
13
|
+
private_constant :NoImplementation
|
|
14
|
+
|
|
15
|
+
# Whether a NotImplementedError is the "no implementation" signal rather than an error from called code: the
|
|
16
|
+
# wrapper's own, or one raised directly in the method's own frame (`def x = raise NotImplementedError`). The
|
|
17
|
+
# implementation lambda below sits one frame above the method, so anything raised deeper (a helper, a block,
|
|
18
|
+
# another object, a `super` into an unsquished parent) has more frames in between. A missing backtrace fails
|
|
19
|
+
# closed; an exception re-raised or reused keeps the backtrace of its first raise, so a stub that escapes
|
|
20
|
+
# another squished call (after its `squish!`) still reads as a stub to the caller.
|
|
21
|
+
def self.stub?(error)
|
|
22
|
+
error.is_a?(NoImplementation) || error.backtrace_locations&.at(1)&.absolute_path == FILE
|
|
23
|
+
end
|
|
24
|
+
|
|
7
25
|
# The method beneath any Squishling wrappers (instance_method resolves to the prepended wrapper first),
|
|
8
26
|
# or nil when the method has no implementation.
|
|
9
27
|
def self.implementation(method)
|
|
@@ -20,7 +38,7 @@ module Squishling
|
|
|
20
38
|
if defined?(super)
|
|
21
39
|
-> { super(*args, **kwargs, &block) }
|
|
22
40
|
else
|
|
23
|
-
-> { raise
|
|
41
|
+
-> { raise NoImplementation, "#{self.class}##{name} has no implementation" }
|
|
24
42
|
end
|
|
25
43
|
|
|
26
44
|
Router.dispatch(self, name, args, kwargs, impl, wrapper:)
|
data/lib/squishling.rb
CHANGED
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "json_schemer"
|
|
5
|
-
require "ruby_llm"
|
|
6
5
|
require "schematist"
|
|
7
6
|
|
|
8
7
|
require_relative "squishling/version"
|
|
9
8
|
require_relative "squishling/errors"
|
|
10
9
|
require_relative "squishling/configuration"
|
|
11
10
|
require_relative "squishling/params"
|
|
11
|
+
require_relative "squishling/named_registry"
|
|
12
|
+
require_relative "squishling/secret"
|
|
13
|
+
require_relative "squishling/adapter/error_body"
|
|
14
|
+
require_relative "squishling/adapter"
|
|
15
|
+
require_relative "squishling/adapters/ruby_llm"
|
|
16
|
+
require_relative "squishling/adapters/openai_options"
|
|
17
|
+
require_relative "squishling/adapters/openai_transport"
|
|
18
|
+
require_relative "squishling/adapters/openai_compatible"
|
|
12
19
|
require_relative "squishling/squawk"
|
|
13
20
|
require_relative "squishling/observer"
|
|
14
21
|
require_relative "squishling/observers/coolhand"
|
|
@@ -26,9 +33,12 @@ require_relative "squishling/schema"
|
|
|
26
33
|
require_relative "squishling/source"
|
|
27
34
|
require_relative "squishling/appendices"
|
|
28
35
|
require_relative "squishling/definition"
|
|
36
|
+
require_relative "squishling/budget"
|
|
29
37
|
require_relative "squishling/llm_client"
|
|
30
38
|
require_relative "squishling/output_check"
|
|
31
39
|
require_relative "squishling/run/messages"
|
|
40
|
+
require_relative "squishling/run/rejections"
|
|
41
|
+
require_relative "squishling/run/reports"
|
|
32
42
|
require_relative "squishling/run"
|
|
33
43
|
require_relative "squishling/invoker"
|
|
34
44
|
require_relative "squishling/judge"
|
|
@@ -36,6 +46,10 @@ require_relative "squishling/router"
|
|
|
36
46
|
require_relative "squishling/wrapper"
|
|
37
47
|
require_relative "squishling/class_methods"
|
|
38
48
|
require_relative "squishling/collisions"
|
|
49
|
+
require_relative "squishling/class_registry"
|
|
50
|
+
require_relative "squishling/verification"
|
|
51
|
+
require_relative "squishling/verifier"
|
|
52
|
+
require_relative "squishling/railtie" if defined?(Rails::Railtie)
|
|
39
53
|
|
|
40
54
|
# Include Squishling in a class to make it elastic: its squished methods either run their
|
|
41
55
|
# Ruby implementation or send their inputs through an LLM and return schema-validated results.
|
|
@@ -74,6 +88,47 @@ module Squishling
|
|
|
74
88
|
Harness.types
|
|
75
89
|
end
|
|
76
90
|
|
|
91
|
+
# Registers a custom adapter class (see Adapter::Base) under a name, usable as config.adapter = :name, or
|
|
92
|
+
# config.adapter = { type: :name, option: value } for an adapter whose initialize takes options. To use an adapter
|
|
93
|
+
# without registering it, assign an instance: config.adapter = MyAdapter.new. Raises ConfigurationError for a name
|
|
94
|
+
# that is invalid or already taken, including the built-in :ruby_llm and :openai_compatible.
|
|
95
|
+
def register_adapter(name, klass)
|
|
96
|
+
Adapter::Registry.register(name, klass)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The names of the adapters that can be configured: the built-ins, then those registered.
|
|
100
|
+
def adapters
|
|
101
|
+
Adapter::Registry.names
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Forgets the registered adapters (the built-ins stay), e.g. between specs.
|
|
105
|
+
def reset_adapters!
|
|
106
|
+
Adapter::Registry.reset!
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Every class that includes Squishling, and each subclass of one, that is loaded and hasn't been replaced by a
|
|
110
|
+
# reload. Classes are held weakly, so listing them doesn't keep reloaded ones alive. In Rails, eager load first
|
|
111
|
+
# (Rails.application.eager_load!) to include classes not yet autoloaded.
|
|
112
|
+
def squished_classes
|
|
113
|
+
ClassRegistry.classes
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Checks squished classes without making a request: each method's output schema and typed result, its resolved
|
|
117
|
+
# escalation, params, squawk and harness, and the keywords its include_when: and squish_when procs take. Checks
|
|
118
|
+
# the given classes, or every squished_classes when none are given. Returns a Verification::Report listing every
|
|
119
|
+
# problem found (ok? when none).
|
|
120
|
+
def verify(*classes)
|
|
121
|
+
Verifier.new(classes.empty? ? squished_classes : classes.flatten).call
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Like verify, but raises a VerificationError (a ConfigurationError) naming every problem. For CI and boot checks.
|
|
125
|
+
def verify!(*classes)
|
|
126
|
+
report = verify(*classes)
|
|
127
|
+
raise VerificationError, report unless report.ok?
|
|
128
|
+
|
|
129
|
+
report
|
|
130
|
+
end
|
|
131
|
+
|
|
77
132
|
def included(base)
|
|
78
133
|
raise ConfigurationError, "Squishling can only be included in a class" unless base.is_a?(Class)
|
|
79
134
|
|
|
@@ -108,10 +163,11 @@ module Squishling
|
|
|
108
163
|
# it); return it from the method. The overrides apply to this call only: append_to_purpose adds to (or,
|
|
109
164
|
# with false, replaces) the declared sections, context is sent alongside the declared squish_context,
|
|
110
165
|
# model/escalation (one or the other)/provider/purpose/harness replace the declared ones, and params merge
|
|
111
|
-
# key by key over them.
|
|
166
|
+
# key by key over them. max_total_output_tokens/max_total_input_tokens cap what the call may spend across all its
|
|
167
|
+
# requests (false lifts a declared cap).
|
|
112
168
|
def squish!(append_to_purpose: nil, context: nil, purpose: nil, model: nil, escalation: nil, provider: nil,
|
|
113
|
-
params: nil, harness: nil)
|
|
169
|
+
params: nil, harness: nil, max_total_output_tokens: nil, max_total_input_tokens: nil)
|
|
114
170
|
Router.hand_off(self, { append_to_purpose:, context:, purpose:, model:, escalation:, provider:, params:,
|
|
115
|
-
harness: })
|
|
171
|
+
harness:, max_total_output_tokens:, max_total_input_tokens: })
|
|
116
172
|
end
|
|
117
173
|
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.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Carroll
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '2.0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: ruby_llm
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '2.1'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '2.1'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: schematist
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,12 +39,13 @@ dependencies:
|
|
|
53
39
|
version: '1.1'
|
|
54
40
|
description: 'Squishling makes Ruby classes elastic. Add a squishling to a class and
|
|
55
41
|
it can send inputs straight to an LLM (OpenAI, Anthropic Claude, Google Gemini,
|
|
56
|
-
or any provider RubyLLM supports
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
the
|
|
61
|
-
|
|
42
|
+
or any provider RubyLLM supports, or any OpenAI-compatible endpoint, or your own
|
|
43
|
+
adapter) and return strict-schema-validated, typed results: the same type your Ruby
|
|
44
|
+
code returns. Use it to gracefully maintain fickle integrations, build interfaces
|
|
45
|
+
for unknown data formats, and rescue production errors by handing the failing call
|
|
46
|
+
to the LLM with the source class as context. Write Ruby only for the paths where
|
|
47
|
+
token costs justify maintaining it. Per-input routing, validated retries, and fallbacks
|
|
48
|
+
for your LLM calls included.'
|
|
62
49
|
email:
|
|
63
50
|
- mc@coolhandlabs.com
|
|
64
51
|
executables: []
|
|
@@ -70,6 +57,7 @@ files:
|
|
|
70
57
|
- README.md
|
|
71
58
|
- Rakefile
|
|
72
59
|
- SECURITY.md
|
|
60
|
+
- docs/adapters.md
|
|
73
61
|
- docs/configuration.md
|
|
74
62
|
- docs/coolhand.md
|
|
75
63
|
- docs/failures.md
|
|
@@ -79,9 +67,19 @@ files:
|
|
|
79
67
|
- docs/observers.md
|
|
80
68
|
- docs/routing.md
|
|
81
69
|
- docs/schemas.md
|
|
70
|
+
- docs/testing.md
|
|
71
|
+
- docs/verifying.md
|
|
82
72
|
- lib/squishling.rb
|
|
73
|
+
- lib/squishling/adapter.rb
|
|
74
|
+
- lib/squishling/adapter/error_body.rb
|
|
75
|
+
- lib/squishling/adapters/openai_compatible.rb
|
|
76
|
+
- lib/squishling/adapters/openai_options.rb
|
|
77
|
+
- lib/squishling/adapters/openai_transport.rb
|
|
78
|
+
- lib/squishling/adapters/ruby_llm.rb
|
|
83
79
|
- lib/squishling/appendices.rb
|
|
80
|
+
- lib/squishling/budget.rb
|
|
84
81
|
- lib/squishling/class_methods.rb
|
|
82
|
+
- lib/squishling/class_registry.rb
|
|
85
83
|
- lib/squishling/collisions.rb
|
|
86
84
|
- lib/squishling/configuration.rb
|
|
87
85
|
- lib/squishling/definition.rb
|
|
@@ -97,18 +95,42 @@ files:
|
|
|
97
95
|
- lib/squishling/judge.rb
|
|
98
96
|
- lib/squishling/llm_client.rb
|
|
99
97
|
- lib/squishling/model_path.rb
|
|
98
|
+
- lib/squishling/named_registry.rb
|
|
100
99
|
- lib/squishling/observer.rb
|
|
101
100
|
- lib/squishling/observers/coolhand.rb
|
|
102
101
|
- lib/squishling/output_check.rb
|
|
103
102
|
- lib/squishling/params.rb
|
|
103
|
+
- lib/squishling/railtie.rb
|
|
104
104
|
- lib/squishling/result.rb
|
|
105
105
|
- lib/squishling/rotation.rb
|
|
106
106
|
- lib/squishling/router.rb
|
|
107
|
+
- lib/squishling/rspec.rb
|
|
107
108
|
- lib/squishling/run.rb
|
|
108
109
|
- lib/squishling/run/messages.rb
|
|
110
|
+
- lib/squishling/run/rejections.rb
|
|
111
|
+
- lib/squishling/run/reports.rb
|
|
109
112
|
- lib/squishling/schema.rb
|
|
113
|
+
- lib/squishling/secret.rb
|
|
110
114
|
- lib/squishling/source.rb
|
|
111
115
|
- lib/squishling/squawk.rb
|
|
116
|
+
- lib/squishling/tasks/squishling.rake
|
|
117
|
+
- lib/squishling/testing.rb
|
|
118
|
+
- lib/squishling/testing/adapter.rb
|
|
119
|
+
- lib/squishling/testing/checks.rb
|
|
120
|
+
- lib/squishling/testing/direct.rb
|
|
121
|
+
- lib/squishling/testing/errors.rb
|
|
122
|
+
- lib/squishling/testing/fixtures.rb
|
|
123
|
+
- lib/squishling/testing/helpers.rb
|
|
124
|
+
- lib/squishling/testing/minitest.rb
|
|
125
|
+
- lib/squishling/testing/observer.rb
|
|
126
|
+
- lib/squishling/testing/reply.rb
|
|
127
|
+
- lib/squishling/testing/request.rb
|
|
128
|
+
- lib/squishling/testing/rspec.rb
|
|
129
|
+
- lib/squishling/testing/script.rb
|
|
130
|
+
- lib/squishling/testing/state.rb
|
|
131
|
+
- lib/squishling/testing/stubs.rb
|
|
132
|
+
- lib/squishling/verification.rb
|
|
133
|
+
- lib/squishling/verifier.rb
|
|
112
134
|
- lib/squishling/version.rb
|
|
113
135
|
- lib/squishling/wrapper.rb
|
|
114
136
|
homepage: https://github.com/Coolhand-Labs/squishling
|