axn-ruby_llm 0.2.0 → 0.3.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 +179 -0
- data/README.md +67 -35
- data/lib/axn/ruby_llm/ask.rb +130 -112
- data/lib/axn/ruby_llm/rspec.rb +23 -43
- data/lib/axn/ruby_llm/tool_adapter.rb +100 -75
- data/lib/axn/ruby_llm/version.rb +1 -1
- data/lib/axn/ruby_llm.rb +8 -40
- metadata +22 -8
|
@@ -3,17 +3,22 @@
|
|
|
3
3
|
module Axn
|
|
4
4
|
module RubyLLM
|
|
5
5
|
# Namespaced per-class config (axn's `Axn::Configurable`, PRO-2880): any Axn — with no
|
|
6
|
-
# adapter-specific mixin required — can declare `configure(:ruby_llm) { |c| c.
|
|
6
|
+
# adapter-specific mixin required — can declare `configure(:ruby_llm) { |c| c.present_as = :message }`
|
|
7
7
|
# to set these per-class, alongside e.g. `configure(:mcp) { ... }` for a different adapter on the
|
|
8
8
|
# same class, without the two colliding. `wrap` resolves them via `resolve_override_for`, which
|
|
9
9
|
# falls back to this module's own global `config` (`Axn::RubyLLM.configure { |c| ... }`) and then
|
|
10
10
|
# to each setting's default — the same class-override-then-global-then-default chain a flat
|
|
11
11
|
# `overridable: true` accessor would give a single-adapter consumer.
|
|
12
12
|
config_namespace :ruby_llm
|
|
13
|
-
setting :
|
|
14
|
-
setting :provider_params, default: {}, overridable: true
|
|
13
|
+
setting :provider_options, default: {}, overridable: true
|
|
15
14
|
setting :present_as, default: :structured, one_of: %i[structured message], overridable: true
|
|
16
|
-
|
|
15
|
+
# `Axn::Tools::AdapterSerialization` (extended onto Axn::RubyLLM in ruby_llm.rb, which is required
|
|
16
|
+
# before this file reopens the module) owns this setting's declaration so the three adapters can't
|
|
17
|
+
# drift on it. `default:` is a required kwarg with no core-picked value on purpose: an LLM-facing
|
|
18
|
+
# adapter is better off shipping an ugly-but-honest rendering than failing the whole tool call, so
|
|
19
|
+
# ruby_llm (like axn-mcp) declares `false`, where axn-openapi's published output contract declares
|
|
20
|
+
# `true`. Must follow `config_namespace` above -- it's an `overridable:` setting.
|
|
21
|
+
declare_reject_opaque_exposed_values! default: false
|
|
17
22
|
|
|
18
23
|
# Wraps any Axn as a ::RubyLLM::Tool: schema, name, and description are read straight off the
|
|
19
24
|
# Axn's own declared contract (`input_schema` / `resolved_axn_name` / `description`, from axn's
|
|
@@ -29,15 +34,14 @@ module Axn
|
|
|
29
34
|
ADAPTER_FAILURE_MESSAGE = "The tool could not produce a valid response"
|
|
30
35
|
|
|
31
36
|
class << self
|
|
32
|
-
def wrap(axn_class,
|
|
37
|
+
def wrap(axn_class, provider_options: nil, present_as: nil, render_as: NOT_SET, provider_params: NOT_SET, ambient_context: NOT_SET)
|
|
33
38
|
validate_present_as_kwargs!(present_as, render_as)
|
|
39
|
+
validate_provider_options_kwargs!(provider_params)
|
|
34
40
|
|
|
35
41
|
tool_class = build_tool_class(
|
|
36
42
|
axn_class,
|
|
37
|
-
|
|
38
|
-
provider_params: provider_params.nil? ? Axn::RubyLLM.resolve_override_for(axn_class, :provider_params) : provider_params,
|
|
43
|
+
provider_options: provider_options.nil? ? Axn::RubyLLM.resolve_override_for(axn_class, :provider_options) : provider_options,
|
|
39
44
|
present_as: present_as.nil? ? Axn::RubyLLM.resolve_override_for(axn_class, :present_as) : present_as,
|
|
40
|
-
reject_opaque: Axn::RubyLLM.resolve_override_for(axn_class, :reject_opaque_exposed_values),
|
|
41
45
|
ambient_context:,
|
|
42
46
|
)
|
|
43
47
|
|
|
@@ -64,7 +68,50 @@ module Axn
|
|
|
64
68
|
raise ArgumentError, "present_as must be one of :structured, :message; got #{present_as.inspect}#{hint}"
|
|
65
69
|
end
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
# `provider_params:` was renamed to `provider_options:` (PRO-3467) to match RubyLLM 2.0's own
|
|
72
|
+
# `Tool.provider_options`, which replaced `with_params` for tool-level provider metadata.
|
|
73
|
+
# Same hard-error treatment as `render_as:` above: pre-1.0, never silently shimmed.
|
|
74
|
+
def validate_provider_options_kwargs!(provider_params)
|
|
75
|
+
return if provider_params.equal?(NOT_SET)
|
|
76
|
+
|
|
77
|
+
raise ArgumentError,
|
|
78
|
+
"`provider_params:` was renamed to `provider_options:` " \
|
|
79
|
+
"(e.g. `Axn::RubyLLM.wrap(..., provider_options: { ... })`)."
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# `guard_tool_response`'s `on_error`: the transport-native error response, plus the operator's
|
|
83
|
+
# only pointer to WHY (the tool-facing text stays generic -- see ADAPTER_FAILURE_MESSAGE).
|
|
84
|
+
# Mirrors axn-openapi's dispatcher hint / axn-mcp's Invocation guard: the config pointer lives
|
|
85
|
+
# HERE rather than in core's exception message, since core raises the same error for adapters
|
|
86
|
+
# with no such setting. Named as BOTH config levels, never just the gem-wide setter -- the
|
|
87
|
+
# value is resolved per-tool, so a `configure(:ruby_llm)` override beats `config`, and core
|
|
88
|
+
# exposes no way to ask which level supplied a resolved value. Non-committal ("if this is")
|
|
89
|
+
# because reject_opaque_exposed_values being on doesn't mean THIS failure is an opaque
|
|
90
|
+
# rejection -- it could equally be a colliding key, a non-finite Float, or a gem bug.
|
|
91
|
+
#
|
|
92
|
+
# The whole hint is built and logged INSIDE a best_effort: `axn_class` is caller code, and
|
|
93
|
+
# interpolating it (a hostile/buggy #to_s) must not raise out of `on_error` -- `guard_tool_response`
|
|
94
|
+
# reports and re-raises an on_error failure rather than substituting a response, so a raise
|
|
95
|
+
# here would cost the tool its error response entirely. Deliberately a SEPARATE best_effort
|
|
96
|
+
# from the guard's own on_exception report: a broken configured logger must not suppress that
|
|
97
|
+
# report, and a broken reporter must not suppress this diagnostic line -- each is the guard's
|
|
98
|
+
# only surviving signal when the OTHER one is what's broken.
|
|
99
|
+
def serialization_failure_response(axn_class, error)
|
|
100
|
+
Axn::Extensions.best_effort("logging a tool serialization failure hint") do
|
|
101
|
+
hint = if Axn::RubyLLM.resolve_override_for(axn_class, :reject_opaque_exposed_values)
|
|
102
|
+
" (if this is an opaque-value rejection: reject_opaque_exposed_values resolved true for " \
|
|
103
|
+
"#{axn_class} — unset it on the action via `configure(:ruby_llm)`, or gem-wide via " \
|
|
104
|
+
"`Axn::RubyLLM.config.reject_opaque_exposed_values = false`, whichever is set)"
|
|
105
|
+
else
|
|
106
|
+
""
|
|
107
|
+
end
|
|
108
|
+
Axn.config.logger.error { "[axn-ruby_llm] failed to serialize successful result: #{error.class}: #{error.message}#{hint}" }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
{ error: ADAPTER_FAILURE_MESSAGE }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def build_tool_class(axn_class, provider_options:, present_as:, ambient_context:)
|
|
68
115
|
# Core's canonical, provider-safe tool_name (PRO-2921): strips configured leading prefixes,
|
|
69
116
|
# snake_cases with single underscores, restricts to [a-z0-9_], and is never blank (anonymous
|
|
70
117
|
# -> "tool"). Pass the `:ruby_llm` adapter key so a per-adapter `tool ruby_llm: { name: }`
|
|
@@ -74,15 +121,25 @@ module Axn
|
|
|
74
121
|
# different name, so provider tool calls / forced choices on the declared name wouldn't
|
|
75
122
|
# match. Absent an override it's identical to the zero-arg name (Axn::MCP.wrap passes `:mcp`
|
|
76
123
|
# the same way -- the author-once point).
|
|
124
|
+
#
|
|
125
|
+
# Passed through unmodified (PRO-3467): RubyLLM 2.0's Gemini protocol reads a tool's schema
|
|
126
|
+
# via `parametersJsonSchema` -- the wire form verbatim, with no whitelist converter in the
|
|
127
|
+
# way -- so the array-valued-`type` / additionalProperties / min-maxProperties workarounds
|
|
128
|
+
# 1.x needed here are gone along with the fixed-property Gemini schema converter they patched
|
|
129
|
+
# around.
|
|
77
130
|
tool_name = axn_class.tool_name(:ruby_llm)
|
|
78
|
-
input_schema =
|
|
131
|
+
input_schema = axn_class.input_schema
|
|
132
|
+
# Built HERE, not inside `define_method(:execute)`: `self` in the executed block is the
|
|
133
|
+
# ::RubyLLM::Tool instance, which has no access to this module's private helpers. Closing
|
|
134
|
+
# over the lambda from build_tool_class's scope binds it to ToolAdapter instead.
|
|
135
|
+
on_serialization_failure = ->(e) { serialization_failure_response(axn_class, e) }
|
|
79
136
|
|
|
80
137
|
Class.new(::RubyLLM::Tool) do
|
|
81
138
|
description(axn_class.description) if axn_class.description
|
|
82
|
-
|
|
83
|
-
|
|
139
|
+
parameters(input_schema)
|
|
140
|
+
provider_options(provider_options) if provider_options.any?
|
|
84
141
|
|
|
85
|
-
|
|
142
|
+
define_singleton_method(:tool_name) { tool_name }
|
|
86
143
|
|
|
87
144
|
define_method(:execute) do |**args|
|
|
88
145
|
# Run the Axn through axn core's tool Invoker (PRO-2943): input types are coerced from the
|
|
@@ -90,8 +147,10 @@ module Axn
|
|
|
90
147
|
# (the injection guard) while the wrap's own trusted context is injected in its place.
|
|
91
148
|
# Contract violations settle user-facing, so `input_invalid?` lets us hand the model a
|
|
92
149
|
# clean, correctable "Invalid tool arguments" error instead of leaking a dev-facing bug
|
|
93
|
-
# (which also keeps a bad tool call from paging on_exception).
|
|
94
|
-
|
|
150
|
+
# (which also keeps a bad tool call from paging on_exception). `adapter: :ruby_llm`
|
|
151
|
+
# (PRO-3332) stamps the invoked_via dimension around the call, so a Datadog dashboard can
|
|
152
|
+
# separate tool-driven traffic from ordinary direct `.call`s with no per-call work here.
|
|
153
|
+
invoker = ::Axn::Tools::Invoker.new(adapter: :ruby_llm, user_facing_input_errors: true, reject_undeclared_inputs: true)
|
|
95
154
|
result = if ambient_context.equal?(NOT_SET)
|
|
96
155
|
invoker.call(axn_class, args)
|
|
97
156
|
else
|
|
@@ -110,71 +169,37 @@ module Axn
|
|
|
110
169
|
# step that runs AFTER it (exposed-value serialization + JSON encoding) can raise
|
|
111
170
|
# outside core's executor: a value core can't render (two Hash keys colliding on one
|
|
112
171
|
# JSON property, a non-finite Float, non-UTF-8 bytes, an opaque value under
|
|
113
|
-
#
|
|
114
|
-
# RubyLLM has no rescue around a tool's #execute, so any of these would escape
|
|
115
|
-
# break the whole chat.
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
# (and reraises in dev per best_effort_raises_in_dev), same as core.
|
|
140
|
-
Axn::Extensions.best_effort("reporting a tool serialization failure via on_exception") do
|
|
141
|
-
Axn.config.on_exception(e, action: axn_class, context: { source: "Axn::RubyLLM" })
|
|
172
|
+
# reject_opaque_exposed_values), a structure past the JSON encoder's max_nesting, or a
|
|
173
|
+
# gem bug. RubyLLM has no rescue around a tool's #execute, so any of these would escape
|
|
174
|
+
# and break the whole chat. `guard_tool_response` (PRO-2996, from
|
|
175
|
+
# Axn::Tools::AdapterSerialization) is core's shared version of exactly that guard --
|
|
176
|
+
# report through the global on_exception inside a best_effort, re-raise when
|
|
177
|
+
# raises_in_dev? so a real bug surfaces loudly, else hand `on_error` the exception so
|
|
178
|
+
# this adapter builds its own transport-native error response. It is scoped to JUST the
|
|
179
|
+
# mapping step (NOT the Invoker call, which already handles + reports its own
|
|
180
|
+
# exceptions -- double-guarding would double-report on_exception), and the block's
|
|
181
|
+
# return value is #execute's.
|
|
182
|
+
Axn::RubyLLM.guard_tool_response(axn_class, on_error: on_serialization_failure) do
|
|
183
|
+
# RubyLLM::Tool.split_result (called from Chat#add_tool_result_message) sends a String
|
|
184
|
+
# through as-is but `#to_json`'s a returned Hash/Array only via its OWN #to_json
|
|
185
|
+
# dispatch, not necessarily matching how axn would serialize it (Symbol keys/values,
|
|
186
|
+
# BigDecimal, Time, opaque-value rejection). Serialize structured payloads ourselves so
|
|
187
|
+
# the wire form always reflects axn's own serialization contract, not Ruby's default.
|
|
188
|
+
#
|
|
189
|
+
# `serialize_exposed` (not `Serialization.render` directly) resolves
|
|
190
|
+
# reject_opaque_exposed_values PER CALL off the result's own action class, so a
|
|
191
|
+
# per-tool `configure(:ruby_llm)` override is honored and a config change reaches
|
|
192
|
+
# already-wrapped tools. `present_as` stays a wrap-time kwarg: it's adapter-owned, not
|
|
193
|
+
# part of the shared mixin, and `wrap` accepts it as an explicit override.
|
|
194
|
+
if present_as == :message
|
|
195
|
+
result.message
|
|
196
|
+
else
|
|
197
|
+
Axn::RubyLLM.serialize_exposed(result).to_json
|
|
142
198
|
end
|
|
143
|
-
raise if Axn::Extensions.raises_in_dev?
|
|
144
|
-
|
|
145
|
-
{ error: ADAPTER_FAILURE_MESSAGE }
|
|
146
199
|
end
|
|
147
200
|
end
|
|
148
201
|
end
|
|
149
202
|
end
|
|
150
|
-
|
|
151
|
-
# axn reflects a nullable/optional field as a JSON Schema array-valued `type`
|
|
152
|
-
# (e.g. `["integer", "null"]`). That's valid JSON Schema and OpenAI/Anthropic consume it
|
|
153
|
-
# fine, but RubyLLM's Gemini converter only recognizes anyOf-form nullability: it does
|
|
154
|
-
# `param_type_for_gemini(type)` with `type.to_s.downcase`, so an array `type` matches no
|
|
155
|
-
# case and falls through to STRING -- silently dropping both the declared type and the
|
|
156
|
-
# nullability. Rewrite every array-valued `type` into the equivalent `anyOf: [{type: ...}]`,
|
|
157
|
-
# which Gemini's `normalize_any_of_schema` collapses back to the real type + nullable, and
|
|
158
|
-
# which the other providers accept unchanged. Purely a wire-shape change: the admitted value
|
|
159
|
-
# set is identical, and the adapter's own validator (json_types_for) already reads anyOf.
|
|
160
|
-
#
|
|
161
|
-
# Builds new Hashes/Arrays throughout rather than mutating -- axn may hand back a memoized
|
|
162
|
-
# input_schema, and mutating it would corrupt every other reader.
|
|
163
|
-
def normalize_nullable_types(node)
|
|
164
|
-
case node
|
|
165
|
-
when Hash
|
|
166
|
-
rebuilt = node.to_h { |key, value| [key, normalize_nullable_types(value)] }
|
|
167
|
-
if rebuilt[:type].is_a?(Array)
|
|
168
|
-
types = rebuilt.delete(:type)
|
|
169
|
-
rebuilt[:anyOf] = types.map { |type| { type: } }
|
|
170
|
-
end
|
|
171
|
-
rebuilt
|
|
172
|
-
when Array
|
|
173
|
-
node.map { |value| normalize_nullable_types(value) }
|
|
174
|
-
else
|
|
175
|
-
node
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
203
|
end
|
|
179
204
|
end
|
|
180
205
|
|
data/lib/axn/ruby_llm/version.rb
CHANGED
data/lib/axn/ruby_llm.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "delegate"
|
|
4
3
|
require "ruby_llm"
|
|
5
4
|
require "axn"
|
|
6
5
|
|
|
@@ -12,17 +11,20 @@ module Axn
|
|
|
12
11
|
include Axn::Mountable
|
|
13
12
|
extend Axn::Configurable
|
|
14
13
|
extend Axn::Tools::AdapterRoots
|
|
14
|
+
extend Axn::Tools::AdapterSerialization
|
|
15
15
|
|
|
16
16
|
setting :default_model, default: "gpt-4o-mini"
|
|
17
17
|
setting :enabled, default: true
|
|
18
18
|
setting :error_headline, default: "LLM request failed"
|
|
19
19
|
|
|
20
|
-
# `Axn::Tools::AdapterRoots` (extended above) declares `tool_roots` with
|
|
21
|
-
#
|
|
20
|
+
# `Axn::Tools::AdapterRoots` (extended above) declares `tool_roots` with core's conservative
|
|
21
|
+
# `default: []`; `tool_roots_default` re-declares it to ship the shared agent-tools dir, so any Axn
|
|
22
22
|
# living under `app/agent_tools` is exposed as a `:ruby_llm` tool out of the box. It's the same dir
|
|
23
|
-
# axn-mcp defaults to, so one Axn there is authored once and surfaces on both.
|
|
24
|
-
# keeps AdapterRoots' broad-path
|
|
25
|
-
|
|
23
|
+
# axn-mcp defaults to, so one Axn there is authored once and surfaces on both. Going through
|
|
24
|
+
# `tool_roots_default` rather than a hand-written `setting` keeps AdapterRoots' broad-path
|
|
25
|
+
# validation (no widening a root to `app/`/`actions`/`.`/`..`) without hand-copying its lambda, and
|
|
26
|
+
# validates the default EAGERLY at gem load instead of at the registry's first read.
|
|
27
|
+
tool_roots_default %w[agent_tools]
|
|
26
28
|
|
|
27
29
|
# Register this module as the `:ruby_llm` adapter AND its config source (PRO-2948): the registry
|
|
28
30
|
# reads `Axn::RubyLLM.config.tool_roots` off the source to grant directory-based membership.
|
|
@@ -30,16 +32,6 @@ module Axn
|
|
|
30
32
|
|
|
31
33
|
mount_axn :ask, Ask
|
|
32
34
|
|
|
33
|
-
# Backward-compatible view of `config` returned by the deprecated `configuration` alias. The
|
|
34
|
-
# pre-DSL `Configuration#enabled?` invoked a callable gate (`enabled = -> { ... }`); the
|
|
35
|
-
# DSL-generated `config.enabled?` returns an assigned Proc as-is (always truthy). Delegate
|
|
36
|
-
# everything to `config`, but restore the callable-resolving `enabled?` (via the module-level
|
|
37
|
-
# `enabled?`) so a compatibility caller's production gate still resolves correctly during the
|
|
38
|
-
# deprecation window instead of silently reading as enabled. Removed with the alias in 0.3.0.
|
|
39
|
-
class DeprecatedConfigProxy < SimpleDelegator
|
|
40
|
-
def enabled? = Axn::RubyLLM.enabled?
|
|
41
|
-
end
|
|
42
|
-
|
|
43
35
|
class << self
|
|
44
36
|
# `enabled` accepts a Boolean OR a callable — the documented production-gating idiom is
|
|
45
37
|
# `c.enabled = -> { Rails.env.production? }`. axn's Configurable used to invoke an assigned
|
|
@@ -51,30 +43,6 @@ module Axn
|
|
|
51
43
|
value = config.enabled
|
|
52
44
|
value.respond_to?(:call) ? !!value.call : !!value
|
|
53
45
|
end
|
|
54
|
-
|
|
55
|
-
# DEPRECATED backward-compatible aliases for the pre-DSL API. The
|
|
56
|
-
# Axn::Configurable DSL standardizes on `.config` / `reset_config!`.
|
|
57
|
-
# These keep older callers working but emit a deprecation warning and
|
|
58
|
-
# are scheduled for removal in the next minor version (see DEPRECATIONS.md).
|
|
59
|
-
def configuration
|
|
60
|
-
_warn_deprecated_alias("Axn::RubyLLM.configuration", "Axn::RubyLLM.config")
|
|
61
|
-
DeprecatedConfigProxy.new(config)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def reset_configuration!
|
|
65
|
-
_warn_deprecated_alias("Axn::RubyLLM.reset_configuration!", "Axn::RubyLLM.reset_config!")
|
|
66
|
-
reset_config!
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
def _warn_deprecated_alias(old, new)
|
|
72
|
-
warn(
|
|
73
|
-
"[axn-ruby_llm] DEPRECATION: #{old} is deprecated and will be removed in the next minor version; use #{new} instead.",
|
|
74
|
-
category: :deprecated,
|
|
75
|
-
uplevel: 2,
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
46
|
end
|
|
79
47
|
end
|
|
80
48
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: axn-ruby_llm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kali Donovan
|
|
@@ -15,7 +15,7 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.1.0.pre.alpha.
|
|
18
|
+
version: 0.1.0.pre.alpha.6.1
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: 0.2.0
|
|
@@ -25,7 +25,7 @@ dependencies:
|
|
|
25
25
|
requirements:
|
|
26
26
|
- - ">="
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: 0.1.0.pre.alpha.
|
|
28
|
+
version: 0.1.0.pre.alpha.6.1
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: 0.2.0
|
|
@@ -35,22 +35,36 @@ dependencies:
|
|
|
35
35
|
requirements:
|
|
36
36
|
- - ">="
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: '
|
|
38
|
+
version: '2.0'
|
|
39
39
|
- - "<"
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '
|
|
41
|
+
version: '3.0'
|
|
42
42
|
type: :runtime
|
|
43
43
|
prerelease: false
|
|
44
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
46
|
- - ">="
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: '
|
|
48
|
+
version: '2.0'
|
|
49
49
|
- - "<"
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
|
-
version: '
|
|
51
|
+
version: '3.0'
|
|
52
|
+
- !ruby/object:Gem::Dependency
|
|
53
|
+
name: faraday
|
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: 1.10.0
|
|
59
|
+
type: :runtime
|
|
60
|
+
prerelease: false
|
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: 1.10.0
|
|
52
66
|
description: Call LLMs from Axn actions using RubyLLM, with structured error handling,
|
|
53
|
-
|
|
67
|
+
schema-based structured output, and cost/token tracking.
|
|
54
68
|
email:
|
|
55
69
|
- kali@teamshares.com
|
|
56
70
|
executables: []
|