rubyllm-semantic_router 0.1.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b01b92cdb4fa6f65278ec83440543f95642cf85026ea142cb2564c326c0512c
4
- data.tar.gz: c1670b2f6379c451e711950f8d50982ec01671a1070ade3805f4fab5b557108a
3
+ metadata.gz: 4aecffcf2d9ae00e08f10c2f606ca51ef8d3a7fd0fc7616b6e98c0826d85649c
4
+ data.tar.gz: 2b50357814fa44df4aad83c63de2418e0ac78581de10eb3ccd975a91b4e76c86
5
5
  SHA512:
6
- metadata.gz: f06a9a3b105253c45af254f3fdb98346ed28af13a73c36abd0f6eb57709de9b47cdec4a8dd24d214abac004da547124b4468e98b8e6b9d574c0e81583b978489
7
- data.tar.gz: 9e092a880b2c627850e50ce2f376fba33bd88ae7de824ab1bf50e32d88f416f74128bd9920a456bbbf394f26f6eba0ac74d2bdd8a43019d7bdd7060e41ad43a4
6
+ metadata.gz: d9dcf6225321e5dde48d54fec9df1c8ca64e111ec948ed35ede94434f1a27bbcaec39b73c22c2d8b078926ae1f1171d1538f0d83133aa5e0febef5db032784d1
7
+ data.tar.gz: 0d3620664177877adf2518ffd22766d6a831e14afde7501a7864c53cd219dc6b2b4a525a7d5939c3a7ff9a961eb0fe29cfd5f0db52d3b88619ec8b0a97dc4cd9
data/.gitignore CHANGED
@@ -19,3 +19,7 @@ vendor/
19
19
 
20
20
  # RSpec
21
21
  .rspec_status
22
+ *.gem
23
+
24
+ # Test Rails app
25
+ /test_app/
data/ARCHITECTURE.md ADDED
@@ -0,0 +1,329 @@
1
+ # Architecture
2
+
3
+ This document describes the architecture and design decisions of RubyLLM Semantic Router.
4
+
5
+ ## Overview
6
+
7
+ ```
8
+ ┌─────────────────────────────────────────────────────────────┐
9
+ │ Router │
10
+ │ - Manages agents and conversation state │
11
+ │ - Delegates routing to strategy │
12
+ │ - Handles agent switching and chat │
13
+ │ - Provides ask, ask_batch, match, debug_routing APIs │
14
+ └─────────────────────────────────────────────────────────────┘
15
+ │ │ │
16
+ │ │ │
17
+ ▼ ▼ ▼
18
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
19
+ │ EmbeddingCache │ │ Logger │ │ Retry Logic │
20
+ │ (optional TTL) │ │ (optional) │ │ (exp. backoff) │
21
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
22
+
23
+
24
+ ┌─────────────────────────────────────────────────────────────┐
25
+ │ Strategies::Semantic │
26
+ │ - Generates embeddings via RubyLLM │
27
+ │ - Finds nearest neighbors (kNN) │
28
+ │ - Returns RoutingDecision │
29
+ └─────────────────────────────────────────────────────────────┘
30
+
31
+
32
+ ┌─────────────────────────────────────────────────────────────┐
33
+ │ Example Storage │
34
+ │ - In-memory arrays │
35
+ │ - ActiveRecord with neighbor gem │
36
+ │ - Custom vector databases via find_examples │
37
+ └─────────────────────────────────────────────────────────────┘
38
+ ```
39
+
40
+ ## Core Components
41
+
42
+ ### Router (`lib/rubyllm/semantic_router/router.rb`)
43
+
44
+ The main entry point that orchestrates routing and agent management.
45
+
46
+ **Responsibilities:**
47
+ - Accept and normalize agent configurations
48
+ - Store and manage routing examples
49
+ - Delegate routing decisions to the strategy
50
+ - Maintain conversation state and agent switching
51
+ - Provide the `ask`, `ask_batch`, `match`, and `debug_routing` APIs
52
+ - Manage embedding cache (if configured)
53
+ - Handle retry logic for transient failures
54
+ - Emit debug logs (if logger configured)
55
+
56
+ **Key Design Decisions:**
57
+ - Accepts RubyLLM chat objects directly for ergonomic API
58
+ - Extracts configuration from chat objects (instructions, tools, model)
59
+ - Maintains single chat instance, switching agent config on route changes
60
+ - Preserves full conversation history across agent switches
61
+
62
+ ### Strategy (`lib/rubyllm/semantic_router/strategies/`)
63
+
64
+ Pluggable routing strategies following the Strategy pattern.
65
+
66
+ **Base Class:** `Strategies::Base`
67
+ - Defines the `#route` interface
68
+ - Provides shared `apply_fallback` logic
69
+
70
+ **Semantic Strategy:** `Strategies::Semantic`
71
+ - Generates embeddings using RubyLLM.embed
72
+ - Performs kNN search against examples
73
+ - Supports multiple storage backends via duck typing
74
+ - Returns `RoutingDecision` with agent, confidence, and reason
75
+
76
+ ### RoutingDecision (`lib/rubyllm/semantic_router/routing_decision.rb`)
77
+
78
+ Value object representing a routing decision.
79
+
80
+ **Attributes:**
81
+ - `agent` - Target agent name (symbol)
82
+ - `confidence` - Match confidence (0.0-1.0)
83
+ - `matched_example` - The example text that matched
84
+ - `reason` - Why this decision was made (`:semantic_match`, `:fallback`, etc.)
85
+ - `inject_instruction` - Optional instruction for clarification flow
86
+
87
+ ### Configuration (`lib/rubyllm/semantic_router/configuration.rb`)
88
+
89
+ Global configuration with validation. All setters validate input and raise `ConfigurationError` for invalid values.
90
+
91
+ **Routing Options:**
92
+ - `default_embedding_model` - Model for generating embeddings
93
+ - `default_similarity_threshold` - Minimum confidence for routing (0.0-1.0)
94
+ - `default_k_neighbors` - Number of neighbors for kNN
95
+ - `default_fallback` - Behavior when no match found
96
+ - `default_max_words` - Message truncation limit
97
+
98
+ **Reliability Options:**
99
+ - `logger` - Logger instance for debug output (default: nil)
100
+ - `cache_ttl` - Embedding cache TTL in seconds (default: nil = no caching)
101
+ - `max_retries` - Maximum retry attempts for embedding failures (default: 3)
102
+ - `retry_base_delay` - Base delay for exponential backoff in seconds (default: 0.5)
103
+
104
+ ### Utils (`lib/rubyllm/semantic_router/utils.rb`)
105
+
106
+ Shared utility functions.
107
+
108
+ - `cosine_distance(a, b)` - Calculate cosine distance between vectors
109
+ - `cosine_similarity(a, b)` - Calculate cosine similarity
110
+ - `truncate_to_max_words(text, max_words)` - Truncate text by word count
111
+
112
+ ### EmbeddingCache (`lib/rubyllm/semantic_router/embedding_cache.rb`)
113
+
114
+ Thread-safe in-memory cache for embeddings with TTL support.
115
+
116
+ **Purpose:** Reduce API calls and latency when the same text is embedded multiple times (e.g., adding the same example after clearing, or routing identical messages).
117
+
118
+ **Features:**
119
+ - TTL-based expiration
120
+ - Thread-safe with Mutex
121
+ - Simple get/set/fetch interface
122
+ - Automatic cleanup of expired entries
123
+
124
+ ```ruby
125
+ # Internal structure
126
+ CacheEntry = Struct.new(:embedding, :expires_at)
127
+
128
+ # Usage (internal to Router)
129
+ @embedding_cache.fetch(text) { generate_embedding_api_call(text) }
130
+ ```
131
+
132
+ **Note:** The cache is per-router instance. Each router maintains its own cache.
133
+
134
+ ### Errors (`lib/rubyllm/semantic_router/errors.rb`)
135
+
136
+ Custom exception hierarchy for clear error handling.
137
+
138
+ ```
139
+ Error (base)
140
+ ├── AgentNotFoundError
141
+ ├── NoDefaultAgentError
142
+ ├── NoAgentsError
143
+ ├── NoRoutingExamplesError
144
+ ├── EmbeddingError
145
+ ├── InvalidFallbackError
146
+ ├── InvalidAgentError
147
+ └── ConfigurationError
148
+ ```
149
+
150
+ ## Storage Backends
151
+
152
+ The router supports multiple storage backends through duck typing:
153
+
154
+ ### In-Memory
155
+
156
+ Default storage using simple arrays of `InMemoryExample` structs.
157
+
158
+ ```ruby
159
+ # Internal structure
160
+ InMemoryExample = Struct.new(:agent_name, :example_text, :embedding)
161
+ ```
162
+
163
+ ### ActiveRecord with neighbor gem
164
+
165
+ Expects model with `has_neighbors :embedding` and `agent_name`, `example_text` columns.
166
+
167
+ ```ruby
168
+ # Detection
169
+ examples.respond_to?(:nearest_neighbors) # Use neighbor gem's kNN
170
+ ```
171
+
172
+ ### Custom Vector Database
173
+
174
+ Accepts a `find_examples` callable for custom search:
175
+
176
+ ```ruby
177
+ find_examples: ->(embedding, limit:) {
178
+ # Return array of hashes or objects with:
179
+ # - agent_name (required)
180
+ # - example_text (optional)
181
+ # - distance OR score (optional, defaults to 0)
182
+ }
183
+ ```
184
+
185
+ ## Routing Flow
186
+
187
+ 1. **Message received** via `router.ask(message)`
188
+ 2. **Log** routing attempt (if logger configured)
189
+ 3. **Check cache** for existing embedding (if cache enabled)
190
+ 4. **Generate embedding** for the message using configured model
191
+ - On failure: retry with exponential backoff (up to `max_retries`)
192
+ - Cache result (if cache enabled)
193
+ 5. **Find nearest neighbors** from examples (k = `k_neighbors`)
194
+ 6. **Calculate confidence** from cosine distance of best match
195
+ 7. **Apply threshold** - if below `similarity_threshold`, use fallback
196
+ 8. **Log** routing decision (if logger configured)
197
+ 9. **Return decision** with target agent and metadata
198
+ 10. **Switch agent** if target differs from current
199
+ 11. **Send message** to current agent's chat and return response
200
+
201
+ ## Batch Routing Flow
202
+
203
+ For `router.ask_batch(messages)`:
204
+
205
+ 1. **Generate embeddings** for all messages in single API call
206
+ 2. **Route each message** using its pre-computed embedding
207
+ 3. **Return array** of `RoutingDecision` objects
208
+ 4. **Note:** Does not send messages or switch agents - only returns decisions
209
+
210
+ ## Reliability Features
211
+
212
+ ### Logging
213
+
214
+ When a logger is configured, the router emits debug information:
215
+
216
+ ```
217
+ [SemanticRouter] Router initialized with agents: product, support
218
+ [SemanticRouter] Routing message: Show me laptops...
219
+ [SemanticRouter] Cache hit for embedding
220
+ [SemanticRouter] Routed to :product (confidence: 0.892, reason: semantic_match)
221
+ [SemanticRouter] Switching from :support to :product
222
+ ```
223
+
224
+ Log levels used:
225
+ - `debug` - Detailed operation info (routing attempts, cache hits, agent switches)
226
+ - `info` - Routing decisions
227
+ - `warn` - Retry attempts
228
+ - `error` - Final failures after retries exhausted
229
+
230
+ ### Retry with Exponential Backoff
231
+
232
+ Embedding API calls are retried on failure:
233
+
234
+ ```
235
+ Attempt 1: fail → wait 0.5s
236
+ Attempt 2: fail → wait 1.0s
237
+ Attempt 3: fail → wait 2.0s
238
+ Attempt 4: fail → raise EmbeddingError
239
+ ```
240
+
241
+ Formula: `delay = retry_base_delay * (2 ** attempt_number)`
242
+
243
+ ### Embedding Cache
244
+
245
+ Reduces API calls by caching embeddings:
246
+
247
+ ```ruby
248
+ # First call - generates embedding, stores in cache
249
+ router.add_example("Show products", agent: :product)
250
+
251
+ # Later - same text uses cached embedding
252
+ router.clear_examples!
253
+ router.add_example("Show products", agent: :product) # Cache hit
254
+ ```
255
+
256
+ Cache is keyed by the truncated text (after `max_words` applied).
257
+
258
+ ## Design Principles
259
+
260
+ ### 1. Duck Typing for Flexibility
261
+
262
+ The router uses duck typing to support multiple storage backends without requiring specific interfaces:
263
+
264
+ ```ruby
265
+ # ActiveRecord detection
266
+ if examples.respond_to?(:nearest_neighbors)
267
+ # Use neighbor gem
268
+ elsif examples.respond_to?(:where)
269
+ # Use ActiveRecord scoping
270
+ else
271
+ # Use array operations
272
+ end
273
+ ```
274
+
275
+ ### 2. Sensible Defaults
276
+
277
+ All configuration has reasonable defaults that work out of the box:
278
+ - Embedding model: `text-embedding-3-small` (fast, cheap)
279
+ - Threshold: `0.7` (balanced precision/recall)
280
+ - Fallback: `:default_agent` (predictable behavior)
281
+
282
+ ### 3. Validation at Boundaries
283
+
284
+ Input validation occurs at configuration and router initialization, failing fast with clear error messages rather than producing undefined behavior during routing.
285
+
286
+ ### 4. Strategy Pattern for Routing
287
+
288
+ Using the strategy pattern allows for future alternative routing implementations (e.g., keyword-based, LLM-based) without changing the Router class.
289
+
290
+ ### 5. Value Objects for Decisions
291
+
292
+ `RoutingDecision` is immutable with clear semantics, making it easy to inspect, log, and test routing behavior.
293
+
294
+ ## Extension Points
295
+
296
+ ### Custom Strategies
297
+
298
+ Create new routing strategies by extending `Strategies::Base`:
299
+
300
+ ```ruby
301
+ class MyStrategy < RubyLLM::SemanticRouter::Strategies::Base
302
+ def route(message, agents:, examples:, current_agent:, config:, find_examples: nil)
303
+ # Custom routing logic
304
+ RoutingDecision.new(agent: :some_agent, confidence: 0.9, reason: :custom)
305
+ end
306
+ end
307
+
308
+ router = Router.new(agents: {...}, strategy: MyStrategy.new, ...)
309
+ ```
310
+
311
+ ### Callbacks
312
+
313
+ Register callbacks for routing events:
314
+
315
+ ```ruby
316
+ router.on(:on_route) do |decision|
317
+ Rails.logger.info("Routed to #{decision.agent} with confidence #{decision.confidence}")
318
+ end
319
+ ```
320
+
321
+ ## Performance Considerations
322
+
323
+ - **Embedding generation** is the main latency source (~50-200ms per call)
324
+ - **In-memory kNN** is O(n) - fine for hundreds of examples
325
+ - **neighbor gem** uses database indexes for O(log n) performance
326
+ - **Batch imports** (`import_examples`) reduce embedding API calls
327
+ - **Batch routing** (`ask_batch`) generates all embeddings in one API call
328
+ - **Embedding cache** eliminates redundant API calls for repeated text
329
+ - **max_words** truncation reduces embedding costs for long messages
data/CHANGELOG.md ADDED
@@ -0,0 +1,98 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.0] - 2026-05-29
9
+
10
+ ### Changed
11
+ - Verified compatibility with RubyLLM 1.15.0 (tested with real OpenAI API)
12
+ - Lowered default `similarity_threshold` from 0.7 to 0.3 to match real-world embedding similarity ranges
13
+
14
+ ### Added
15
+ - Integration test suite (`spec/integration/`) for testing against real OpenAI API
16
+
17
+ ## [0.3.0] - 2025-01-24
18
+
19
+ ### Added
20
+ - Shared `Utils` module with `cosine_distance`, `cosine_similarity`, and `truncate_to_max_words` methods
21
+ - Configuration validation for `similarity_threshold` (must be 0.0-1.0), `k_neighbors` (must be positive integer), `max_words` (must be nil or positive integer), and `fallback` (must be valid symbol)
22
+ - `ConfigurationError` exception for invalid configuration values
23
+ - Debug logging support with configurable `logger` option
24
+ - Embedding cache with TTL support via `cache_ttl` option
25
+ - `ask_batch` method for routing multiple messages efficiently
26
+ - Retry logic with exponential backoff via `max_retries` and `retry_base_delay` options
27
+ - Comprehensive test suite for Utils, Configuration, and edge cases
28
+ - CHANGELOG.md, CONTRIBUTING.md, and ARCHITECTURE.md documentation
29
+
30
+ ### Changed
31
+ - Extracted duplicate `cosine_distance` and `truncate_to_max_words` implementations into shared Utils module
32
+ - Improved error messages for configuration validation
33
+
34
+ ### Breaking Changes
35
+ - Configuration now validates values and raises `ConfigurationError` for invalid settings:
36
+ - `similarity_threshold` must be between 0.0 and 1.0
37
+ - `k_neighbors` must be a positive integer
38
+ - `max_words` must be nil or a positive integer
39
+ - `fallback` must be one of `:default_agent`, `:keep_current`, `:ask_clarification`
40
+ - Previously, invalid values were silently accepted but could cause unexpected behavior. If your code was using invalid configuration values, you will now receive clear error messages indicating what needs to be fixed.
41
+
42
+ ## [0.2.0] - 2025-01-21
43
+
44
+ ### Added
45
+ - `max_words` option to truncate messages before embedding generation
46
+ - Global `default_max_words` configuration option
47
+ - Message truncation applied consistently to both example import and routing
48
+
49
+ ### Changed
50
+ - Updated README to be database-agnostic
51
+
52
+ ## [0.1.3] - 2025-01-20
53
+
54
+ ### Added
55
+ - Custom vector search support via `find_examples` callback
56
+ - Support for both `distance` (lower=better) and `score` (higher=better) in custom search results
57
+ - Documented custom vector database integration (Pinecone, Qdrant, OpenSearch, etc.)
58
+
59
+ ### Fixed
60
+ - Dependency versions and file permissions
61
+
62
+ ## [0.1.2] - 2025-01-19
63
+
64
+ ### Changed
65
+ - Simplified API to accept `RubyLLM.chat` objects directly as agents
66
+ - Agents now extract configuration from chat objects automatically
67
+
68
+ ## [0.1.1] - 2025-01-18
69
+
70
+ ### Added
71
+ - ActiveRecord + pgvector example to README
72
+ - Installation instructions
73
+
74
+ ### Changed
75
+ - Simplified README structure
76
+
77
+ ## [0.1.0] - 2025-01-17
78
+
79
+ ### Added
80
+ - Initial implementation of semantic routing for RubyLLM
81
+ - Core `Router` class for managing multiple agents
82
+ - `Semantic` routing strategy using embeddings and kNN search
83
+ - Support for multiple fallback behaviors: `:default_agent`, `:keep_current`, `:ask_clarification`
84
+ - In-memory example storage with `add_example` and `import_examples`
85
+ - External example sources via `with_examples` (ActiveRecord compatible)
86
+ - Scoped examples for multi-tenant applications
87
+ - Routing callbacks via `on(:on_route)`
88
+ - Debug routing with `match` and `debug_routing` methods
89
+ - Global configuration via `RubyLLM::SemanticRouter.configure`
90
+ - Comprehensive test suite
91
+
92
+ [0.4.0]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.3.0...v0.4.0
93
+ [0.3.0]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.2.0...v0.3.0
94
+ [0.2.0]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.1.3...v0.2.0
95
+ [0.1.3]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.1.2...v0.1.3
96
+ [0.1.2]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.1.1...v0.1.2
97
+ [0.1.1]: https://github.com/khasinski/ruby_llm-semantic_router/compare/v0.1.0...v0.1.1
98
+ [0.1.0]: https://github.com/khasinski/ruby_llm-semantic_router/releases/tag/v0.1.0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,103 @@
1
+ # Contributing to RubyLLM Semantic Router
2
+
3
+ Thank you for your interest in contributing! This document provides guidelines for contributing to the project.
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repository
8
+ 2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/rubyllm-semantic_router.git`
9
+ 3. Install dependencies: `bundle install`
10
+ 4. Run tests: `bundle exec rspec`
11
+
12
+ ## Development Setup
13
+
14
+ ### Requirements
15
+
16
+ - Ruby 3.1+
17
+ - Bundler 2.0+
18
+
19
+ ### Running Tests
20
+
21
+ ```bash
22
+ # Run all tests
23
+ bundle exec rspec
24
+
25
+ # Run specific test file
26
+ bundle exec rspec spec/rubyllm/semantic_router/router_spec.rb
27
+
28
+ # Run with verbose output
29
+ bundle exec rspec --format documentation
30
+ ```
31
+
32
+ ## Making Changes
33
+
34
+ ### Code Style
35
+
36
+ - Use frozen string literals (`# frozen_string_literal: true`)
37
+ - Follow existing code patterns and naming conventions
38
+ - Keep methods focused and under 20 lines when possible
39
+ - Add YARD documentation for public methods
40
+
41
+ ### Testing
42
+
43
+ - Write tests for all new functionality
44
+ - Maintain or improve existing test coverage
45
+ - Tests should be fast and not require external API calls
46
+ - Use the mock RubyLLM classes in `spec/spec_helper.rb`
47
+
48
+ ### Commit Messages
49
+
50
+ - Use clear, descriptive commit messages
51
+ - Start with a verb (Add, Fix, Update, Remove, Refactor)
52
+ - Keep the first line under 72 characters
53
+ - Reference issues when applicable: `Fix #123`
54
+
55
+ Examples:
56
+ ```
57
+ Add batch routing support with ask_batch method
58
+ Fix configuration validation for edge cases
59
+ Update README with multi-tenant scoping docs
60
+ ```
61
+
62
+ ## Pull Request Process
63
+
64
+ 1. Create a feature branch: `git checkout -b feature/my-feature`
65
+ 2. Make your changes with tests
66
+ 3. Run the test suite: `bundle exec rspec`
67
+ 4. Update CHANGELOG.md with your changes under `[Unreleased]`
68
+ 5. Push to your fork and create a Pull Request
69
+
70
+ ### PR Checklist
71
+
72
+ - [ ] Tests pass locally
73
+ - [ ] New functionality has tests
74
+ - [ ] CHANGELOG.md updated
75
+ - [ ] Documentation updated (if applicable)
76
+ - [ ] Code follows existing style
77
+
78
+ ## Reporting Issues
79
+
80
+ When reporting issues, please include:
81
+
82
+ - Ruby version (`ruby -v`)
83
+ - Gem version
84
+ - Minimal reproduction steps
85
+ - Expected vs actual behavior
86
+ - Error messages with backtraces
87
+
88
+ ## Feature Requests
89
+
90
+ Feature requests are welcome! Please:
91
+
92
+ - Check existing issues first
93
+ - Describe the use case
94
+ - Explain why existing functionality doesn't solve it
95
+ - Consider if it fits the gem's scope
96
+
97
+ ## Architecture
98
+
99
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for an overview of the codebase structure and design decisions.
100
+
101
+ ## License
102
+
103
+ By contributing, you agree that your contributions will be licensed under the MIT License.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubyllm-semantic_router (0.1.0)
4
+ rubyllm-semantic_router (0.4.0)
5
5
  ruby_llm (~> 1.0)
6
6
 
7
7
  GEM
@@ -10,19 +10,19 @@ GEM
10
10
  base64 (0.3.0)
11
11
  diff-lcs (1.6.2)
12
12
  event_stream_parser (1.0.0)
13
- faraday (2.14.0)
13
+ faraday (2.14.2)
14
14
  faraday-net_http (>= 2.0, < 3.5)
15
15
  json
16
16
  logger
17
17
  faraday-multipart (1.2.0)
18
18
  multipart-post (~> 2.0)
19
- faraday-net_http (3.4.2)
19
+ faraday-net_http (3.4.3)
20
20
  net-http (~> 0.5)
21
21
  faraday-retry (2.4.0)
22
22
  faraday (~> 2.0)
23
- json (2.18.0)
23
+ json (2.19.7)
24
24
  logger (1.7.0)
25
- marcel (1.1.0)
25
+ marcel (1.2.1)
26
26
  multipart-post (2.4.1)
27
27
  net-http (0.9.1)
28
28
  uri (>= 0.11.1)
@@ -40,19 +40,19 @@ GEM
40
40
  diff-lcs (>= 1.2.0, < 2.0)
41
41
  rspec-support (~> 3.13.0)
42
42
  rspec-support (3.13.6)
43
- ruby_llm (1.9.1)
43
+ ruby_llm (1.15.0)
44
44
  base64
45
45
  event_stream_parser (~> 1)
46
46
  faraday (>= 1.10.0)
47
47
  faraday-multipart (>= 1)
48
48
  faraday-net_http (>= 1)
49
49
  faraday-retry (>= 1)
50
- marcel (~> 1.0)
51
- ruby_llm-schema (~> 0.2.1)
50
+ marcel (~> 1)
51
+ ruby_llm-schema (~> 0)
52
52
  zeitwerk (~> 2)
53
- ruby_llm-schema (0.2.5)
53
+ ruby_llm-schema (0.4.0)
54
54
  uri (1.1.1)
55
- zeitwerk (2.7.4)
55
+ zeitwerk (2.8.2)
56
56
 
57
57
  PLATFORMS
58
58
  arm64-darwin-25