ruby_llm-providers-lms 0.1.0 → 0.1.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/.github/workflows/ci.yml +2 -5
- data/.rubocop.yml +1 -1
- data/README.md +135 -8
- data/lib/ruby_llm/providers/lms/connection_guard.rb +27 -0
- data/lib/ruby_llm/providers/lms.rb +6 -0
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_can_handle_a_multi_turn_conversation.yml +2 -2
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_can_have_a_basic_conversation.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_can_use_tools.yml +2 -2
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_returns_the_raw_response.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_supports_streaming_responses.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_qwen3_0_6b_bible_assistant_can_have_a_basic_conversation.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_qwen3_0_6b_bible_assistant_returns_structured_output.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_chat_lms_qwen3_0_6b_bible_assistant_returns_the_raw_response.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_embedding_lms_text_embedding_nomic_embed_text_v1_5_embeds_one_text.yml +1 -1
- data/spec/fixtures/vcr_cassettes/rubyllm_embedding_lms_text_embedding_nomic_embed_text_v1_5_embeds_several_texts.yml +1 -1
- data/spec/ruby_llm/providers/lms/connection_guard_spec.rb +47 -0
- data/spec/ruby_llm/providers/lms_spec.rb +4 -0
- data/spec/support/vcr_configuration.rb +9 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdaf33f5245f3b78bcfd33a13c7f2a0838149de0998b09c7f8e4256e447cda69
|
|
4
|
+
data.tar.gz: 285c1c5ddff4227b90b435755b8cc29b1a28bb695fa33c7f1c6510ce3a5853cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 548a88a50a88795f9b5b7cd11c64ee1c43e76406c7730c34c42cf90bf2e748eded77029f38f9994f5ab8820a05d7e0479118e8a43f3dbb165a3bc11e0297a892
|
|
7
|
+
data.tar.gz: d01419d61951513cc00dcd72e546f7a56e2023fa59ff1bcadc99e0b117f1bdfab6b06c541078b0a984dede41f025e9668fd00921086b3163c5590654d1d99bc2
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -16,7 +16,8 @@ jobs:
|
|
|
16
16
|
strategy:
|
|
17
17
|
fail-fast: false
|
|
18
18
|
matrix:
|
|
19
|
-
|
|
19
|
+
# Non-EOL Rubies only; must match the gemspec's required_ruby_version.
|
|
20
|
+
ruby-version: ["3.3", "3.4", "4.0"]
|
|
20
21
|
steps:
|
|
21
22
|
- uses: actions/checkout@v6
|
|
22
23
|
|
|
@@ -26,7 +27,3 @@ jobs:
|
|
|
26
27
|
bundler-cache: true
|
|
27
28
|
|
|
28
29
|
- run: bundle exec rake
|
|
29
|
-
if: matrix.ruby-version != '3.1'
|
|
30
|
-
|
|
31
|
-
- run: bundle exec rake rubocop flay spec
|
|
32
|
-
if: matrix.ruby-version == '3.1'
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
|
@@ -40,15 +40,50 @@ response = chat.ask('Hello')
|
|
|
40
40
|
puts response.content
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
If the server isn't running, requests raise a `RubyLLM::Error` telling you to
|
|
44
|
+
start it (`lms server start`) instead of a raw connection failure.
|
|
45
|
+
|
|
43
46
|
Because LM Studio is a local provider, RubyLLM assumes any model id you pass
|
|
44
47
|
exists — LM Studio will just-in-time load the model if it isn't loaded yet.
|
|
45
|
-
To browse what the server offers:
|
|
48
|
+
To browse what the server offers right now, ask the provider directly:
|
|
46
49
|
|
|
47
50
|
```ruby
|
|
48
|
-
RubyLLM.
|
|
49
|
-
|
|
51
|
+
provider = RubyLLM::Provider.resolve!(:lms).new(RubyLLM.config)
|
|
52
|
+
provider.list_models.each { |model| puts model.id }
|
|
50
53
|
```
|
|
51
54
|
|
|
55
|
+
## The model catalog (models.json)
|
|
56
|
+
|
|
57
|
+
The gem ships a `models.json` at its root. **This file is only a sample**,
|
|
58
|
+
recorded from the author's machine: LM Studio serves whatever models *you*
|
|
59
|
+
have downloaded, so your catalog will be different. RubyLLM loads the shipped
|
|
60
|
+
file as the provider's model registry so `RubyLLM.models` has something to
|
|
61
|
+
show.
|
|
62
|
+
|
|
63
|
+
Note that `RubyLLM.models.refresh` does **not** query your LM Studio server:
|
|
64
|
+
provider gems that ship a registry file are excluded from the live fetch, and
|
|
65
|
+
refresh just re-reads the registered `models.json`. Your live server is still
|
|
66
|
+
the source of truth, reached differently depending on what you need:
|
|
67
|
+
|
|
68
|
+
- **Chatting** needs no catalog at all — any model id LM Studio knows will
|
|
69
|
+
work (see above).
|
|
70
|
+
- **Browsing what your server offers** is `provider.list_models` (see the
|
|
71
|
+
snippet above), which hits the live endpoints every time.
|
|
72
|
+
- **Making `RubyLLM.models` reflect your machine** means registering your own
|
|
73
|
+
catalog file in place of the shipped sample:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
require 'ruby_llm/providers/lms'
|
|
77
|
+
|
|
78
|
+
provider = RubyLLM::Provider.resolve!(:lms).new(RubyLLM.config)
|
|
79
|
+
RubyLLM::Models.new(provider.list_models).save_to_json('my_models.json')
|
|
80
|
+
RubyLLM::Provider.register :lms, RubyLLM::Providers::LMS, models: 'my_models.json'
|
|
81
|
+
RubyLLM.models.refresh
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Don't edit the `models.json` inside the installed gem — it is overwritten on
|
|
85
|
+
every gem update; re-register with your own file instead.
|
|
86
|
+
|
|
52
87
|
Embeddings work the same way:
|
|
53
88
|
|
|
54
89
|
```ruby
|
|
@@ -61,6 +96,99 @@ To use the Responses dialect instead of Chat Completions:
|
|
|
61
96
|
RubyLLM.configure { |config| config.lms_protocol = :responses }
|
|
62
97
|
```
|
|
63
98
|
|
|
99
|
+
## LM Studio server options
|
|
100
|
+
|
|
101
|
+
LM Studio serves three API surfaces on one port (`http://localhost:1234` by
|
|
102
|
+
default). This gem talks to the first two:
|
|
103
|
+
|
|
104
|
+
| Surface | Endpoints | Used by this gem |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| OpenAI-compatible | `GET /v1/models`, `POST /v1/chat/completions`, `POST /v1/completions`, `POST /v1/embeddings`, `POST /v1/responses` | chat, tools, structured output, embeddings, Responses protocol |
|
|
107
|
+
| Native REST v0 | `GET /api/v0/models`, `GET /api/v0/models/{model}`, `POST /api/v0/chat/completions`, `POST /api/v0/completions`, `POST /api/v0/embeddings` | model-listing enrichment |
|
|
108
|
+
| Native REST v1 | `POST /api/v1/chat`, `GET /api/v1/list`, `POST /api/v1/load`, `POST /api/v1/unload`, `POST /api/v1/download`, `GET /api/v1/download-status` | not yet (see below) |
|
|
109
|
+
|
|
110
|
+
### Standard request parameters
|
|
111
|
+
|
|
112
|
+
On its OpenAI-compatible chat endpoints LM Studio honors the usual OpenAI
|
|
113
|
+
vocabulary: `model`, `messages`, `temperature`, `top_p`, `max_tokens`,
|
|
114
|
+
`stream`, `stop`, `presence_penalty`, `frequency_penalty`, `logit_bias`,
|
|
115
|
+
`seed`, `tools` / `tool_choice`, and `response_format` with a `json_schema`
|
|
116
|
+
(strict structured output). Vision models accept image content parts. You
|
|
117
|
+
reach all of these through RubyLLM's normal API — `with_temperature`,
|
|
118
|
+
`with_max_output_tokens`, `with_tools`, `with_schema`, `chat.ask(with: 'image.png')`,
|
|
119
|
+
and so on; nothing LM Studio-specific is required.
|
|
120
|
+
|
|
121
|
+
### LM Studio extras
|
|
122
|
+
|
|
123
|
+
LM Studio also accepts request fields that are not part of OpenAI's
|
|
124
|
+
vocabulary. RubyLLM merges `with_provider_options` into the request payload
|
|
125
|
+
as-is, so all of these work today:
|
|
126
|
+
|
|
127
|
+
| Option | What it does |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `ttl` | Idle time-to-live in seconds for the model handling this request. A JIT-loaded model unloads itself after sitting idle that long (LM Studio's default is 60 minutes; each request resets the timer). |
|
|
130
|
+
| `draft_model` | Enables speculative decoding: a small draft model proposes tokens the main model verifies, often speeding up generation substantially. Both models must use the same tokenizer family. |
|
|
131
|
+
| `top_k` | Sample only from the k most likely tokens. |
|
|
132
|
+
| `min_p` | Discard tokens below this base probability. |
|
|
133
|
+
| `repeat_penalty` | Penalize recently generated tokens to discourage repetition. |
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
chat = RubyLLM.chat(model: 'qwen/qwen3-4b', provider: :lms)
|
|
137
|
+
chat.with_provider_options(
|
|
138
|
+
ttl: 300, # unload after 5 idle minutes
|
|
139
|
+
draft_model: 'qwen/qwen3-0.6b', # speculative decoding
|
|
140
|
+
top_k: 40,
|
|
141
|
+
min_p: 0.05,
|
|
142
|
+
repeat_penalty: 1.1
|
|
143
|
+
)
|
|
144
|
+
chat.ask('Hello')
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Model lifecycle: JIT loading, TTL, auto-evict
|
|
148
|
+
|
|
149
|
+
Because LM Studio only serves models you have downloaded, the server manages
|
|
150
|
+
memory rather than a fleet:
|
|
151
|
+
|
|
152
|
+
- **JIT loading** — with just-in-time loading enabled (the server default),
|
|
153
|
+
`/v1/models` lists every downloaded model and an inference request for an
|
|
154
|
+
unloaded model loads it on demand. Expect a long time-to-first-token on
|
|
155
|
+
that first request. With JIT off, only already-loaded models are served.
|
|
156
|
+
- **TTL** — JIT-loaded models default to a 60-minute idle TTL; override it
|
|
157
|
+
per request with the `ttl` option above, or at load time with
|
|
158
|
+
`lms load <model> --ttl 3600`. Models loaded explicitly via `lms load`
|
|
159
|
+
have no TTL and stay resident until unloaded.
|
|
160
|
+
- **Auto-evict** — by default LM Studio keeps at most one JIT-loaded model
|
|
161
|
+
in memory, evicting the previous one before loading the next. Turn
|
|
162
|
+
auto-evict off in the server settings to keep several models resident at
|
|
163
|
+
once (each still subject to its own TTL).
|
|
164
|
+
|
|
165
|
+
The `state` field in this gem's model metadata (`loaded` / `not-loaded`)
|
|
166
|
+
comes from the native v0 API and tells you which models are resident right
|
|
167
|
+
now.
|
|
168
|
+
|
|
169
|
+
### Native REST APIs (not yet wired up)
|
|
170
|
+
|
|
171
|
+
The native APIs offer capabilities the OpenAI-compatible surface cannot
|
|
172
|
+
express. This gem does not use them yet, beyond the v0 model-listing
|
|
173
|
+
enrichment:
|
|
174
|
+
|
|
175
|
+
- **Performance stats** — native v0/v1 chat responses include a `stats`
|
|
176
|
+
object (tokens per second, time to first token, generation time, and
|
|
177
|
+
draft-token acceptance counts when speculative decoding is active) plus
|
|
178
|
+
`model_info` and `runtime` blocks.
|
|
179
|
+
- **Stateful chats** — `/api/v1/chat` stores conversations server-side
|
|
180
|
+
(`store`, default true) and continues them via `previous_response_id`.
|
|
181
|
+
- **Reasoning control** — `/api/v1/chat` takes a `reasoning` effort option
|
|
182
|
+
(`off` / `low` / `medium` / `high` / `on`) and returns reasoning output as
|
|
183
|
+
separate items.
|
|
184
|
+
- **Server-side MCP tools** — `/api/v1/chat` can run tools itself through
|
|
185
|
+
`integrations`: pre-configured MCP plugins from `mcp.json` or ephemeral
|
|
186
|
+
MCP servers declared in the request, with `allowed_tools` filtering.
|
|
187
|
+
- **Per-request context length** — `/api/v1/chat` accepts a
|
|
188
|
+
`context_length` override.
|
|
189
|
+
- **Model management** — `/api/v1/load`, `/api/v1/unload`, `/api/v1/download`,
|
|
190
|
+
and `/api/v1/download-status` control what is in memory and on disk.
|
|
191
|
+
|
|
64
192
|
## Development
|
|
65
193
|
|
|
66
194
|
Start the LM Studio server (`lms server start`), then:
|
|
@@ -70,11 +198,10 @@ bundle exec rake models # refresh models.json from your local server
|
|
|
70
198
|
bundle exec rake # rubocop, flay, archspec, specs
|
|
71
199
|
```
|
|
72
200
|
|
|
73
|
-
`rake models` calls the local server's model-listing endpoints and
|
|
74
|
-
`models.json` at the gem root
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
sample — live listings via `RubyLLM.models.refresh!` reflect your machine.
|
|
201
|
+
`rake models` calls the local server's model-listing endpoints and rewrites
|
|
202
|
+
the sample `models.json` at the gem root (see "The model catalog" above) —
|
|
203
|
+
it is a maintainer task for refreshing the shipped sample, not something gem
|
|
204
|
+
users run.
|
|
78
205
|
|
|
79
206
|
The suite always runs the provider integration specs. The first local run
|
|
80
207
|
calls the API and records VCR cassettes; CI only replays committed cassettes.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'delegate'
|
|
4
|
+
|
|
5
|
+
module RubyLLM
|
|
6
|
+
module Providers
|
|
7
|
+
class LMS < Provider
|
|
8
|
+
# Wraps the transport connection so a request against a server that
|
|
9
|
+
# is not running surfaces as a RubyLLM::Error explaining how to start
|
|
10
|
+
# LM Studio, instead of a raw Faraday::ConnectionFailed.
|
|
11
|
+
class ConnectionGuard < SimpleDelegator
|
|
12
|
+
%i[get post patch delete].each do |verb|
|
|
13
|
+
define_method(verb) do |*args, **kwargs, &block|
|
|
14
|
+
__getobj__.public_send(verb, *args, **kwargs, &block)
|
|
15
|
+
rescue Faraday::ConnectionFailed
|
|
16
|
+
raise Error, unreachable_message
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def unreachable_message
|
|
21
|
+
"Cannot connect to the LM Studio server at #{provider.api_base}. " \
|
|
22
|
+
'Start it with `lms server start`.'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'ruby_llm'
|
|
4
|
+
require 'ruby_llm/providers/lms/connection_guard'
|
|
4
5
|
require 'ruby_llm/providers/lms/models'
|
|
5
6
|
|
|
6
7
|
module RubyLLM
|
|
@@ -20,6 +21,11 @@ module RubyLLM
|
|
|
20
21
|
protocol :chat_completions, ChatCompletions
|
|
21
22
|
protocol :responses, Protocols::Responses
|
|
22
23
|
|
|
24
|
+
def initialize(config)
|
|
25
|
+
super
|
|
26
|
+
@connection = ConnectionGuard.new(@connection)
|
|
27
|
+
end
|
|
28
|
+
|
|
23
29
|
def api_base
|
|
24
30
|
@config.lms_api_base || DEFAULT_API_BASE
|
|
25
31
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
http_interactions:
|
|
3
3
|
- request:
|
|
4
4
|
method: post
|
|
5
|
-
uri: "
|
|
5
|
+
uri: "http://localhost:1234/v1/chat/completions"
|
|
6
6
|
body:
|
|
7
7
|
encoding: UTF-8
|
|
8
8
|
string: '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"Who
|
|
@@ -44,7 +44,7 @@ http_interactions:
|
|
|
44
44
|
recorded_at: Fri, 11 Sep 2026 17:27:55 GMT
|
|
45
45
|
- request:
|
|
46
46
|
method: post
|
|
47
|
-
uri: "
|
|
47
|
+
uri: "http://localhost:1234/v1/chat/completions"
|
|
48
48
|
body:
|
|
49
49
|
encoding: UTF-8
|
|
50
50
|
string: '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"Who
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
http_interactions:
|
|
3
3
|
- request:
|
|
4
4
|
method: post
|
|
5
|
-
uri: "
|
|
5
|
+
uri: "http://localhost:1234/v1/chat/completions"
|
|
6
6
|
body:
|
|
7
7
|
encoding: UTF-8
|
|
8
8
|
string: '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"What''s
|
|
@@ -82,7 +82,7 @@ http_interactions:
|
|
|
82
82
|
recorded_at: Fri, 11 Sep 2026 17:28:04 GMT
|
|
83
83
|
- request:
|
|
84
84
|
method: post
|
|
85
|
-
uri: "
|
|
85
|
+
uri: "http://localhost:1234/v1/chat/completions"
|
|
86
86
|
body:
|
|
87
87
|
encoding: UTF-8
|
|
88
88
|
string: '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"What''s
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe RubyLLM::Providers::LMS::ConnectionGuard do
|
|
6
|
+
subject(:guard) { described_class.new(connection) }
|
|
7
|
+
|
|
8
|
+
let(:provider) { instance_double(RubyLLM::Providers::LMS, api_base: 'http://localhost:1234/v1') }
|
|
9
|
+
let(:connection) { instance_double(RubyLLM::Transport::Connection, provider: provider) }
|
|
10
|
+
|
|
11
|
+
{ get: ['models'], post: ['chat/completions', {}], patch: ['models', {}], delete: ['models'] }.each do |verb, args|
|
|
12
|
+
it "wraps a connection failure on ##{verb} in a RubyLLM::Error" do
|
|
13
|
+
allow(connection).to receive(verb).and_raise(Faraday::ConnectionFailed, 'Connection refused')
|
|
14
|
+
|
|
15
|
+
expect { guard.public_send(verb, *args) }.to raise_error(RubyLLM::Error) do |error|
|
|
16
|
+
expect(error.message).to include('http://localhost:1234/v1')
|
|
17
|
+
expect(error.message).to include('lms server start')
|
|
18
|
+
expect(error.cause).to be_a(Faraday::ConnectionFailed)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'passes successful calls through with arguments and block' do
|
|
24
|
+
response = instance_double(Faraday::Response)
|
|
25
|
+
request = Object.new
|
|
26
|
+
allow(connection).to receive(:post).with('chat/completions', { model: 'x' }, usage: nil)
|
|
27
|
+
.and_yield(request).and_return(response)
|
|
28
|
+
|
|
29
|
+
yielded = nil
|
|
30
|
+
result = guard.post('chat/completions', { model: 'x' }, usage: nil) { |req| yielded = req }
|
|
31
|
+
|
|
32
|
+
expect(result).to be(response)
|
|
33
|
+
expect(yielded).to be(request)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'lets other errors propagate untouched' do
|
|
37
|
+
allow(connection).to receive(:get).and_raise(RubyLLM::UnauthorizedError.new('nope'))
|
|
38
|
+
|
|
39
|
+
expect { guard.get('models') }.to raise_error(RubyLLM::UnauthorizedError, 'nope')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'names the server address and the start command in its message' do
|
|
43
|
+
expect(guard.unreachable_message)
|
|
44
|
+
.to eq('Cannot connect to the LM Studio server at http://localhost:1234/v1. ' \
|
|
45
|
+
'Start it with `lms server start`.')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -50,6 +50,10 @@ RSpec.describe RubyLLM::Providers::LMS do
|
|
|
50
50
|
expect(provider.headers).to eq('Authorization' => 'Bearer test-key')
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
it 'guards its connection against a server that is not running' do
|
|
54
|
+
expect(provider.connection).to be_a(described_class::ConnectionGuard)
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
describe 'model listing' do
|
|
54
58
|
subject(:protocol) do
|
|
55
59
|
described_class::ChatCompletions.new(described_class.new(config))
|
|
@@ -6,7 +6,15 @@ VCR.configure do |config|
|
|
|
6
6
|
config.default_cassette_options = { record: ENV['CI'] ? :none : :once }
|
|
7
7
|
config.allow_http_connections_when_no_cassette = true
|
|
8
8
|
config.filter_sensitive_data('<LMS_API_KEY>') { ENV.fetch('LMS_API_KEY', nil) }
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
# Normalize the recorder's server address to the default so cassettes
|
|
11
|
+
# replay on any machine (CI included) without LMS_API_BASE set. The
|
|
12
|
+
# placeholder must be a parseable URI, not a <TOKEN>: VCR parses every
|
|
13
|
+
# cassette URI on replay and raises URI::InvalidURIError otherwise.
|
|
14
|
+
config.filter_sensitive_data(RubyLLM::Providers::LMS::DEFAULT_API_BASE) do
|
|
15
|
+
base = ENV.fetch('LMS_API_BASE', nil)
|
|
16
|
+
base unless base.nil? || base == RubyLLM::Providers::LMS::DEFAULT_API_BASE
|
|
17
|
+
end
|
|
10
18
|
|
|
11
19
|
config.before_record do |interaction|
|
|
12
20
|
next unless interaction.request.headers['Authorization']
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-providers-lms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- madbomber
|
|
@@ -42,6 +42,7 @@ files:
|
|
|
42
42
|
- LICENSE
|
|
43
43
|
- README.md
|
|
44
44
|
- lib/ruby_llm/providers/lms.rb
|
|
45
|
+
- lib/ruby_llm/providers/lms/connection_guard.rb
|
|
45
46
|
- lib/ruby_llm/providers/lms/models.rb
|
|
46
47
|
- models.json
|
|
47
48
|
- spec/fixtures/vcr_cassettes/rubyllm_chat_lms_openai_gpt_oss_20b_can_handle_a_multi_turn_conversation.yml
|
|
@@ -62,6 +63,7 @@ files:
|
|
|
62
63
|
- spec/ruby_llm/image_spec.rb
|
|
63
64
|
- spec/ruby_llm/models_spec.rb
|
|
64
65
|
- spec/ruby_llm/moderation_spec.rb
|
|
66
|
+
- spec/ruby_llm/providers/lms/connection_guard_spec.rb
|
|
65
67
|
- spec/ruby_llm/providers/lms_spec.rb
|
|
66
68
|
- spec/ruby_llm/rerank_spec.rb
|
|
67
69
|
- spec/ruby_llm/speech_spec.rb
|
|
@@ -86,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
86
88
|
requirements:
|
|
87
89
|
- - ">="
|
|
88
90
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '3.
|
|
91
|
+
version: '3.3'
|
|
90
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
93
|
requirements:
|
|
92
94
|
- - ">="
|