llmshim 0.3.6 → 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 +4 -4
- data/README.md +9 -9
- data/lib/llmshim/client.rb +3 -0
- data/lib/llmshim/types.rb +47 -7
- data/lib/llmshim/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7daaabbea4814eb113a27e7cf8965d87f8bc29459f4a1b6caad395984388e2da
|
|
4
|
+
data.tar.gz: f629a9202933185c03908033c48d2ef8a6d30330acd5cabb7048309680eb4ca0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40f1298cb6bed88b1353236cb09b8085bd21e9dc6bb8d968c66ad85cddb9e711b7ce7ae3c2dc6ac9236aca12e776ee28dc43910c437a80995822065961b5c14b
|
|
7
|
+
data.tar.gz: 76a5d19612b5ae27ad8e602c5b37848e08998909e43b41ef2067ade819fffe8b6dc4efcf34ffa494f2b7594ac7aa30a56b6ee62a76c8dcf3d165dc43bf22253a
|
data/README.md
CHANGED
|
@@ -46,9 +46,9 @@ environment variables. The Ruby client never sees your keys — the proxy holds
|
|
|
46
46
|
| ---------- | --------------------------------------- | ------------------------------------- |
|
|
47
47
|
| OpenAI | `openai/gpt-5.6-sol` | `OPENAI_API_KEY` |
|
|
48
48
|
| Anthropic | `anthropic/claude-sonnet-5` | `ANTHROPIC_API_KEY` |
|
|
49
|
-
| Gemini | `gemini/gemini-3.
|
|
50
|
-
| xAI | `xai/grok-4.
|
|
51
|
-
| OpenRouter | `openrouter/anthropic/claude-sonnet-
|
|
49
|
+
| Gemini | `gemini/gemini-3.8-flash` | `GEMINI_API_KEY` |
|
|
50
|
+
| xAI | `xai/grok-4.6` | `XAI_API_KEY` |
|
|
51
|
+
| OpenRouter | `openrouter/anthropic/claude-sonnet-5` | `OPENROUTER_API_KEY` |
|
|
52
52
|
| vLLM | `vllm/<served-model>` | `VLLM_BASE_URL` (+ optional `VLLM_API_KEY`) |
|
|
53
53
|
| SGLang | `sglang/<served-model>` | `SGLANG_BASE_URL` (+ optional `SGLANG_API_KEY`) |
|
|
54
54
|
|
|
@@ -75,7 +75,7 @@ of message hashes:
|
|
|
75
75
|
|
|
76
76
|
```ruby
|
|
77
77
|
resp = client.chat(
|
|
78
|
-
model: "openai/gpt-5.
|
|
78
|
+
model: "openai/gpt-5.6-sol",
|
|
79
79
|
messages: [
|
|
80
80
|
{ role: "system", content: "You are a pirate." },
|
|
81
81
|
{ role: "user", content: "Hello!" }
|
|
@@ -99,7 +99,7 @@ A shared default client (base URL from `LLMSHIM_BASE_URL`, else `http://localhos
|
|
|
99
99
|
```ruby
|
|
100
100
|
require "llmshim"
|
|
101
101
|
|
|
102
|
-
resp = Llmshim.chat(model: "gpt-5.
|
|
102
|
+
resp = Llmshim.chat(model: "gpt-5.6-sol", messages: "Explain quicksort")
|
|
103
103
|
puts resp.content
|
|
104
104
|
```
|
|
105
105
|
|
|
@@ -127,7 +127,7 @@ Predicate helpers are available too: `event.content?`, `event.reasoning?`,
|
|
|
127
127
|
Called without a block, `stream` returns the collected array of events:
|
|
128
128
|
|
|
129
129
|
```ruby
|
|
130
|
-
events = client.stream(model: "gpt-5.
|
|
130
|
+
events = client.stream(model: "gpt-5.6-sol", messages: "Hi")
|
|
131
131
|
text = events.select(&:content?).map(&:text).join
|
|
132
132
|
```
|
|
133
133
|
|
|
@@ -145,9 +145,9 @@ resp = client.chat(
|
|
|
145
145
|
],
|
|
146
146
|
tool_choice: "auto",
|
|
147
147
|
# Provider-specific controls, namespaced under x-<provider> (see below):
|
|
148
|
-
provider_config: { "x-anthropic" => { thinking: { type: "
|
|
148
|
+
provider_config: { "x-anthropic" => { thinking: { type: "adaptive" }, output_config: { effort: "high" } } },
|
|
149
149
|
# Try these models if the primary fails with a retryable error:
|
|
150
|
-
fallback: ["openai/gpt-5.6-sol", "gemini/gemini-3.
|
|
150
|
+
fallback: ["openai/gpt-5.6-sol", "gemini/gemini-3.8-flash"]
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
resp.message.tool_calls.each do |tc|
|
|
@@ -177,7 +177,7 @@ level is ignored. Use the namespace matching the target provider:
|
|
|
177
177
|
|
|
178
178
|
```ruby
|
|
179
179
|
# Anthropic extended thinking:
|
|
180
|
-
provider_config: { "x-anthropic" => { thinking: { type: "
|
|
180
|
+
provider_config: { "x-anthropic" => { thinking: { type: "adaptive" }, output_config: { effort: "high" } } }
|
|
181
181
|
|
|
182
182
|
# OpenRouter provider routing (also accepts `models`, `transforms`):
|
|
183
183
|
provider_config: { "x-openrouter" => { provider: { sort: "throughput" } } }
|
data/lib/llmshim/client.rb
CHANGED
|
@@ -120,6 +120,9 @@ module Llmshim
|
|
|
120
120
|
provider_config["tool_choice"] = opts[:tool_choice] unless opts[:tool_choice].nil?
|
|
121
121
|
body["provider_config"] = provider_config unless provider_config.empty?
|
|
122
122
|
|
|
123
|
+
body["x-cache"] = stringify(opts[:cache]) unless opts[:cache].nil?
|
|
124
|
+
body["x-shim"] = stringify(opts[:shim]) unless opts[:shim].nil?
|
|
125
|
+
body["response_format"] = stringify(opts[:response_format]) unless opts[:response_format].nil?
|
|
123
126
|
body["fallback"] = opts[:fallback] unless opts[:fallback].nil?
|
|
124
127
|
body["stream"] = opts[:stream] unless opts[:stream].nil?
|
|
125
128
|
|
data/lib/llmshim/types.rb
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
3
5
|
module Llmshim
|
|
4
6
|
# Token accounting returned with a chat response or a usage stream event.
|
|
5
7
|
#
|
|
6
8
|
# Mirrors the +Usage+ schema in api/openapi.yaml.
|
|
7
9
|
Usage = Struct.new(
|
|
8
10
|
:input_tokens, :output_tokens, :reasoning_tokens, :total_tokens,
|
|
11
|
+
:cache_read_tokens, :cache_write_tokens,
|
|
9
12
|
keyword_init: true
|
|
10
13
|
) do
|
|
11
14
|
def self.from_hash(hash)
|
|
@@ -15,7 +18,9 @@ module Llmshim
|
|
|
15
18
|
input_tokens: hash["input_tokens"],
|
|
16
19
|
output_tokens: hash["output_tokens"],
|
|
17
20
|
reasoning_tokens: hash["reasoning_tokens"],
|
|
18
|
-
total_tokens: hash["total_tokens"]
|
|
21
|
+
total_tokens: hash["total_tokens"],
|
|
22
|
+
cache_read_tokens: hash.fetch("cache_read_tokens", 0),
|
|
23
|
+
cache_write_tokens: hash.fetch("cache_write_tokens", 0)
|
|
19
24
|
)
|
|
20
25
|
end
|
|
21
26
|
end
|
|
@@ -24,9 +29,13 @@ module Llmshim
|
|
|
24
29
|
#
|
|
25
30
|
# +function+ is a plain Hash with "name" and "arguments" (JSON-encoded string)
|
|
26
31
|
# to match the wire format exactly.
|
|
27
|
-
ToolCall = Struct.new(:id, :type, :function, keyword_init: true) do
|
|
32
|
+
ToolCall = Struct.new(:id, :type, :function, :thought_signature, :wire_ids, keyword_init: true) do
|
|
28
33
|
def self.from_hash(hash)
|
|
29
|
-
new(id: hash["id"], type: hash["type"], function: hash["function"])
|
|
34
|
+
new(id: hash["id"], type: hash["type"], function: hash["function"], thought_signature: hash["thought_signature"], wire_ids: hash["wire_ids"])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_json(*args)
|
|
38
|
+
to_h.reject { |_key, value| value.nil? }.to_json(*args)
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
# Convenience accessor for the tool name.
|
|
@@ -41,10 +50,14 @@ module Llmshim
|
|
|
41
50
|
end
|
|
42
51
|
|
|
43
52
|
# The assistant message inside a ChatResponse.
|
|
44
|
-
ResponseMessage = Struct.new(:role, :content, :tool_calls, keyword_init: true) do
|
|
53
|
+
ResponseMessage = Struct.new(:role, :content, :tool_calls, :reasoning, :refusal, keyword_init: true) do
|
|
45
54
|
def self.from_hash(hash)
|
|
46
55
|
calls = (hash["tool_calls"] || []).map { |c| ToolCall.from_hash(c) }
|
|
47
|
-
new(role: hash["role"], content: hash["content"], tool_calls: calls)
|
|
56
|
+
new(role: hash["role"], content: hash["content"], tool_calls: calls, reasoning: hash["reasoning"], refusal: hash["refusal"])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_json(*args)
|
|
60
|
+
to_h.reject { |key, value| value.nil? && !%i[role content].include?(key) }.to_json(*args)
|
|
48
61
|
end
|
|
49
62
|
end
|
|
50
63
|
|
|
@@ -52,7 +65,7 @@ module Llmshim
|
|
|
52
65
|
#
|
|
53
66
|
# +raw+ retains the original parsed Hash for forward compatibility.
|
|
54
67
|
ChatResponse = Struct.new(
|
|
55
|
-
:id, :model, :provider, :message, :reasoning, :usage, :latency_ms, :raw,
|
|
68
|
+
:id, :model, :provider, :message, :reasoning, :usage, :latency_ms, :raw, :served_model, :finish_reason,
|
|
56
69
|
keyword_init: true
|
|
57
70
|
) do
|
|
58
71
|
def self.from_hash(hash)
|
|
@@ -64,7 +77,9 @@ module Llmshim
|
|
|
64
77
|
reasoning: hash["reasoning"],
|
|
65
78
|
usage: Usage.from_hash(hash["usage"]),
|
|
66
79
|
latency_ms: hash["latency_ms"],
|
|
67
|
-
raw: hash
|
|
80
|
+
raw: hash,
|
|
81
|
+
served_model: hash["x-llmshim-served-model"],
|
|
82
|
+
finish_reason: hash["finish_reason"]
|
|
68
83
|
)
|
|
69
84
|
end
|
|
70
85
|
|
|
@@ -120,10 +135,22 @@ module Llmshim
|
|
|
120
135
|
type == "usage"
|
|
121
136
|
end
|
|
122
137
|
|
|
138
|
+
def finish_reason
|
|
139
|
+
raw["finish_reason"]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def served_model
|
|
143
|
+
raw["x-llmshim-served-model"]
|
|
144
|
+
end
|
|
145
|
+
|
|
123
146
|
def done?
|
|
124
147
|
type == "done"
|
|
125
148
|
end
|
|
126
149
|
|
|
150
|
+
def error_details
|
|
151
|
+
raw["error"]
|
|
152
|
+
end
|
|
153
|
+
|
|
127
154
|
def error?
|
|
128
155
|
type == "error"
|
|
129
156
|
end
|
|
@@ -133,6 +160,19 @@ module Llmshim
|
|
|
133
160
|
raw["text"]
|
|
134
161
|
end
|
|
135
162
|
|
|
163
|
+
# Reasoning delta blocks retain signatures, opaque data, and provenance.
|
|
164
|
+
def blocks
|
|
165
|
+
raw["blocks"]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def thought_signature
|
|
169
|
+
raw["thought_signature"]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def wire_ids
|
|
173
|
+
raw["wire_ids"]
|
|
174
|
+
end
|
|
175
|
+
|
|
136
176
|
# tool_call events
|
|
137
177
|
def id
|
|
138
178
|
raw["id"]
|
data/lib/llmshim/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llmshim
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sanjay Nadhavajhala
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|