patient_llm 0.2.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +43 -8
- data/VERSION +1 -1
- data/lib/patient_llm/callback.rb +2 -1
- data/lib/patient_llm/configuration.rb +14 -4
- data/lib/patient_llm.rb +28 -13
- data/patient_llm.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87bcd18afe8c5a4c46e191946f51201e077b355d223ac9d1817bd4041041143f
|
|
4
|
+
data.tar.gz: 51a67045f05fc5f55ec1aee9391006eecf60170c3d3e47f3248d2169046ffa0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 594c2deb985655a5396c587e6bce19f603f2f2ae9af3c6df1d8f268f5ac81d9a2542bedeed966f84700ed6f971cc1e1916bf0be3f217397fd07840a794a1f005
|
|
7
|
+
data.tar.gz: 2e4d20a58e2ce23bb882c1035a98aca401644b85171661b039aa1bc0b7ac5a55a207095ca5facf54723e6ff6baa06e39e752948411dbf3100183bcb041c082f5
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.4.0
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added `preprocessors:` option to provider configuration and `PatientLLM.ask` for applying named PatientHttp request preprocessors (e.g., AWS SigV4 request signing) at dispatch time. A per-request value replaces the provider default; pass an empty array to clear it for a single request.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Requires patient_http version 1.2 or higher.
|
|
16
|
+
|
|
17
|
+
## 0.3.0
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Renamed the `completion_path` argument to `path` on both `PatientLLM.ask` and provider configuration. The `completion_path` name is still accepted as a deprecated alias and now emits a deprecation warning.
|
|
22
|
+
- The path is now joined with the base URL using `URI.join` to ensure proper handling of relative paths in the `path` argument. If the path starts with a slash, it will be treated as an absolute path and will replace the base URL's path. If it does not start with a slash, it will be treated as a relative path and will be appended to the base URL's path.
|
|
23
|
+
|
|
7
24
|
## 0.2.0
|
|
8
25
|
|
|
9
26
|
### Added
|
data/README.md
CHANGED
|
@@ -52,6 +52,38 @@ end
|
|
|
52
52
|
> [!NOTE]
|
|
53
53
|
> You can also set up encryption for your job payloads to ensure the entire serialized payload is always encrypted in the job queue. See the documentation for [patient_http-sidekiq](https://github.com/bdurand/patient_http-sidekiq#sensitive-data-handling) or [patient_http-solid_queue](https://github.com/bdurand/patient_http-solid_queue#sensitive-data-handling) for details.
|
|
54
54
|
|
|
55
|
+
### Request signing (preprocessors)
|
|
56
|
+
|
|
57
|
+
Some providers require request signing rather than a static authentication header — for example, AWS Bedrock with SigV4, where a signature is computed over the final outgoing request. For these, register a [request preprocessor](https://github.com/bdurand/patient_http#request-preprocessors) on the PatientHttp configuration and reference it by name from the provider. Like secrets, only the preprocessor name is serialized into the job queue; the signing logic and credentials stay on the processor side.
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
PatientLLM.configure do |config|
|
|
61
|
+
config.provider :bedrock,
|
|
62
|
+
url: "https://bedrock-runtime.us-east-1.amazonaws.com",
|
|
63
|
+
serializer: :converse,
|
|
64
|
+
preprocessors: :aws_sigv4
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
PatientHttp::Sidekiq.configure do |config|
|
|
68
|
+
config.register_preprocessor(:aws_sigv4) do |request|
|
|
69
|
+
signer = Aws::Sigv4::Signer.new(
|
|
70
|
+
service: "bedrock",
|
|
71
|
+
region: "us-east-1",
|
|
72
|
+
credentials_provider: Aws::CredentialProviderChain.new.resolve
|
|
73
|
+
)
|
|
74
|
+
signature = signer.sign_request(
|
|
75
|
+
http_method: request.http_method.to_s.upcase,
|
|
76
|
+
url: request.url,
|
|
77
|
+
headers: request.headers.to_h,
|
|
78
|
+
body: request.body.to_s
|
|
79
|
+
)
|
|
80
|
+
signature.headers.each { |name, value| request.headers[name] = value }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Multiple preprocessors can be given as an array; they run in order at dispatch time.
|
|
86
|
+
|
|
55
87
|
### Creating a Callback Class
|
|
56
88
|
|
|
57
89
|
Create a callback class with `on_complete` and `on_error` methods. Callbacks receive
|
|
@@ -185,34 +217,37 @@ session.max_output_tokens = 1000
|
|
|
185
217
|
PatientLLM.ask(session,
|
|
186
218
|
provider: :openai,
|
|
187
219
|
callback: LLMCallback,
|
|
188
|
-
url: "http://localhost:1234",
|
|
220
|
+
url: "http://localhost:1234", # Override the provider's base URL
|
|
189
221
|
serializer: :messages, # Override the API format
|
|
190
|
-
|
|
222
|
+
path: "/chat/completions", # Override the endpoint path
|
|
191
223
|
headers: {"X-Custom" => "value"}, # Additional HTTP headers
|
|
192
|
-
params: {max_completion_tokens: 1000}
|
|
224
|
+
params: {max_completion_tokens: 1000}, # Additional request parameters
|
|
225
|
+
preprocessors: :aws_sigv4 # Replace the provider's request preprocessors
|
|
193
226
|
)
|
|
194
227
|
```
|
|
195
228
|
|
|
229
|
+
Note that `headers` and `params` are merged on top of the provider's configured values, while `preprocessors` (like `url`, `serializer`, and `path`) replaces the provider default. Pass `preprocessors: []` to clear a provider-level preprocessor for a single request.
|
|
230
|
+
|
|
196
231
|
### URL composition
|
|
197
232
|
|
|
198
|
-
The full request URL is built by concatenating the base URL (from the provider registry or the `url:` option) with the `
|
|
233
|
+
The full request URL is built by concatenating the base URL (from the provider registry or the `url:` option) with the `path`. When you don't set `path`, it defaults to the path for the active serializer (`/v1/chat/completions` for `:chat_completion`, `/v1/responses` for `:open_responses`, `/v1/messages` for `:messages`, `/converse` for `:converse`, `/v1beta/models/{model}:generateContent` for `:gemini`). A `{model}` placeholder in the path is replaced with the session's model at dispatch time, which is how the Gemini default targets Google's `/v1beta/models/{model}:generateContent` endpoint. Trailing slashes on the base and leading slashes on the path are normalized, so:
|
|
199
234
|
|
|
200
235
|
```
|
|
201
|
-
url = "https://api.openai.com"
|
|
236
|
+
url = "https://api.openai.com" path = "/v1/chat/completions"
|
|
202
237
|
-> https://api.openai.com/v1/chat/completions
|
|
203
238
|
|
|
204
|
-
url = "http://localhost:1234"
|
|
239
|
+
url = "http://localhost:1234" path = "/v1/chat/completions"
|
|
205
240
|
-> http://localhost:1234/v1/chat/completions
|
|
206
241
|
```
|
|
207
242
|
|
|
208
|
-
If your base URL already includes a `/v1` prefix, override the
|
|
243
|
+
If your base URL already includes a `/v1` prefix, override the path to avoid duplication:
|
|
209
244
|
|
|
210
245
|
```ruby
|
|
211
246
|
PatientLLM.ask(session,
|
|
212
247
|
provider: :openai,
|
|
213
248
|
callback: LLMCallback,
|
|
214
249
|
url: "https://my-gateway.internal/openai/v1",
|
|
215
|
-
|
|
250
|
+
path: "chat/completions"
|
|
216
251
|
)
|
|
217
252
|
```
|
|
218
253
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.4.0
|
data/lib/patient_llm/callback.rb
CHANGED
|
@@ -213,9 +213,10 @@ module PatientLLM
|
|
|
213
213
|
# Restore per-request overrides
|
|
214
214
|
ask_kwargs[:url] = request_options["url"] if request_options["url"]
|
|
215
215
|
ask_kwargs[:serializer] = request_options["serializer"].to_sym if request_options["serializer"]
|
|
216
|
-
ask_kwargs[:
|
|
216
|
+
ask_kwargs[:path] = request_options["path"] if request_options["path"]
|
|
217
217
|
ask_kwargs[:headers] = request_options["headers"] if request_options["headers"]
|
|
218
218
|
ask_kwargs[:params] = request_options["params"] if request_options["params"]
|
|
219
|
+
ask_kwargs[:preprocessors] = request_options["preprocessors"] if request_options["preprocessors"]
|
|
219
220
|
|
|
220
221
|
PatientLLM.ask(session, **ask_kwargs)
|
|
221
222
|
end
|
|
@@ -26,10 +26,19 @@ module PatientLLM
|
|
|
26
26
|
# @param url [String] Base URL for the provider API
|
|
27
27
|
# @param headers [Hash] Default headers for requests
|
|
28
28
|
# @param serializer [Symbol] API format (:chat_completion, :open_responses, :messages, :converse, :gemini)
|
|
29
|
-
# @param
|
|
29
|
+
# @param path [String, nil] Override the default endpoint path
|
|
30
|
+
# @param completion_path [String, nil] Deprecated alias for +path+
|
|
30
31
|
# @param params [Hash] Additional parameters to merge into every request payload
|
|
32
|
+
# @param preprocessors [String, Symbol, Array<String, Symbol>, nil] Names of request
|
|
33
|
+
# preprocessors to apply to every request (e.g. for request signing). Names must be
|
|
34
|
+
# registered on the PatientHttp configuration with `register_preprocessor`.
|
|
31
35
|
# @return [void]
|
|
32
|
-
def provider(name, url:, headers: {}, serializer: :chat_completion, completion_path: nil, params: {})
|
|
36
|
+
def provider(name, url:, headers: {}, serializer: :chat_completion, path: nil, completion_path: nil, params: {}, preprocessors: nil)
|
|
37
|
+
if completion_path
|
|
38
|
+
warn "PatientLLM::Configuration#provider: the `completion_path:` argument is deprecated; use `path:` instead", uplevel: 1
|
|
39
|
+
path ||= completion_path
|
|
40
|
+
end
|
|
41
|
+
|
|
33
42
|
sym = serializer.to_sym
|
|
34
43
|
unless PatientLLM::VALID_SERIALIZERS.include?(sym)
|
|
35
44
|
raise ArgumentError, "Unknown serializer: #{sym.inspect}. Valid options: #{PatientLLM::VALID_SERIALIZERS.map(&:inspect).join(", ")}"
|
|
@@ -41,8 +50,9 @@ module PatientLLM
|
|
|
41
50
|
url: url,
|
|
42
51
|
headers: headers,
|
|
43
52
|
serializer: sym,
|
|
44
|
-
|
|
45
|
-
params: params
|
|
53
|
+
path: path,
|
|
54
|
+
params: params,
|
|
55
|
+
preprocessors: preprocessors
|
|
46
56
|
}
|
|
47
57
|
end
|
|
48
58
|
|
data/lib/patient_llm.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "patient_http"
|
|
4
4
|
require "prompt_builder"
|
|
5
|
+
require "uri"
|
|
5
6
|
|
|
6
7
|
module PatientLLM
|
|
7
8
|
VERSION = File.read(File.join(__dir__, "../VERSION")).strip
|
|
@@ -16,11 +17,11 @@ module PatientLLM
|
|
|
16
17
|
# dispatch time, matching Google's `/v1beta/models/{model}:generateContent`
|
|
17
18
|
# endpoint.
|
|
18
19
|
SERIALIZER_PATHS = {
|
|
19
|
-
chat_completion: "
|
|
20
|
-
open_responses: "
|
|
21
|
-
messages: "
|
|
22
|
-
converse: "
|
|
23
|
-
gemini: "
|
|
20
|
+
chat_completion: "v1/chat/completions",
|
|
21
|
+
open_responses: "v1/responses",
|
|
22
|
+
messages: "v1/messages",
|
|
23
|
+
converse: "converse",
|
|
24
|
+
gemini: "v1beta/models/{model}:generateContent"
|
|
24
25
|
}.freeze
|
|
25
26
|
|
|
26
27
|
# Required version header for the Anthropic Messages API.
|
|
@@ -62,11 +63,20 @@ module PatientLLM
|
|
|
62
63
|
# @param callback_args [Hash] Custom arguments passed through to the callback
|
|
63
64
|
# @param url [String, nil] Override the provider's base URL for this request
|
|
64
65
|
# @param serializer [Symbol, nil] Override the provider's serializer for this request
|
|
65
|
-
# @param
|
|
66
|
+
# @param path [String, nil] Override the endpoint path for this request
|
|
67
|
+
# @param completion_path [String, nil] Deprecated alias for +path+
|
|
66
68
|
# @param headers [Hash, nil] Additional headers merged on top of provider headers
|
|
67
69
|
# @param params [Hash, nil] Additional params merged into the request payload
|
|
70
|
+
# @param preprocessors [String, Symbol, Array<String, Symbol>, nil] Names of request
|
|
71
|
+
# preprocessors to apply to this request. Replaces the provider's configured
|
|
72
|
+
# preprocessors; pass an empty array to clear them for this request.
|
|
68
73
|
# @return [Object] Handler-specific identifier for the enqueued request
|
|
69
|
-
def ask(session, provider:, callback:, callback_args: {}, url: nil, serializer: nil, completion_path: nil, headers: nil, params: nil, tool_iteration: 0, original_request_id: nil) # :nodoc: tool_iteration and original_request_id are internal
|
|
74
|
+
def ask(session, provider:, callback:, callback_args: {}, url: nil, serializer: nil, path: nil, completion_path: nil, headers: nil, params: nil, preprocessors: nil, tool_iteration: 0, original_request_id: nil) # :nodoc: tool_iteration and original_request_id are internal
|
|
75
|
+
if completion_path
|
|
76
|
+
warn "PatientLLM.ask: the `completion_path:` argument is deprecated; use `path:` instead", uplevel: 1
|
|
77
|
+
path ||= completion_path
|
|
78
|
+
end
|
|
79
|
+
|
|
70
80
|
provider_config = self.provider(provider) || {}
|
|
71
81
|
provider_name = provider.to_s
|
|
72
82
|
|
|
@@ -79,32 +89,35 @@ module PatientLLM
|
|
|
79
89
|
|
|
80
90
|
resolved_serializer = (serializer || provider_config[:serializer] || :chat_completion).to_sym
|
|
81
91
|
validate_serializer!(resolved_serializer)
|
|
82
|
-
|
|
83
|
-
if
|
|
84
|
-
|
|
92
|
+
resolved_path = path || provider_config[:path] || SERIALIZER_PATHS[resolved_serializer] || "/v1/chat/completions"
|
|
93
|
+
if resolved_path.include?("{model}")
|
|
94
|
+
resolved_path = resolved_path.gsub("{model}", session.model.to_s)
|
|
85
95
|
end
|
|
86
96
|
resolved_headers = (provider_config[:headers] || {}).merge(headers || {})
|
|
87
97
|
if resolved_serializer == :messages && !resolved_headers.key?("anthropic-version")
|
|
88
98
|
resolved_headers = {"anthropic-version" => ANTHROPIC_VERSION}.merge(resolved_headers)
|
|
89
99
|
end
|
|
90
100
|
resolved_params = (provider_config[:params] || {}).merge(params || {})
|
|
101
|
+
resolved_preprocessors = preprocessors || provider_config[:preprocessors]
|
|
91
102
|
|
|
92
103
|
payload = session.request_payload(resolved_serializer)
|
|
93
104
|
payload = deep_merge(payload, deep_stringify_keys(resolved_params)) unless resolved_params.empty?
|
|
94
105
|
|
|
95
|
-
request_url = join_url(resolved_url,
|
|
106
|
+
request_url = join_url(resolved_url, resolved_path)
|
|
96
107
|
|
|
97
108
|
request_options = {}
|
|
98
109
|
request_options["url"] = url if url
|
|
99
110
|
request_options["serializer"] = serializer.to_s if serializer
|
|
100
|
-
request_options["
|
|
111
|
+
request_options["path"] = path if path
|
|
101
112
|
request_options["headers"] = headers if headers && !headers.empty?
|
|
102
113
|
request_options["params"] = params if params && !params.empty?
|
|
114
|
+
request_options["preprocessors"] = preprocessors if preprocessors
|
|
103
115
|
|
|
104
116
|
PatientHttp.post(
|
|
105
117
|
request_url,
|
|
106
118
|
json: payload,
|
|
107
119
|
headers: resolved_headers,
|
|
120
|
+
preprocessors: resolved_preprocessors,
|
|
108
121
|
raise_error_responses: true,
|
|
109
122
|
callback: PatientLLM::Callback,
|
|
110
123
|
callback_args: {
|
|
@@ -128,7 +141,9 @@ module PatientLLM
|
|
|
128
141
|
end
|
|
129
142
|
|
|
130
143
|
def join_url(base, path)
|
|
131
|
-
|
|
144
|
+
base_uri = URI.parse(base)
|
|
145
|
+
base_uri.path = "#{base_uri.path}/" unless base_uri.path&.end_with?("/")
|
|
146
|
+
URI.join(base_uri, path).to_s
|
|
132
147
|
end
|
|
133
148
|
|
|
134
149
|
def deep_merge(hash1, hash2)
|
data/patient_llm.gemspec
CHANGED
|
@@ -37,7 +37,7 @@ Gem::Specification.new do |spec|
|
|
|
37
37
|
|
|
38
38
|
spec.required_ruby_version = ">= 3.0"
|
|
39
39
|
|
|
40
|
-
spec.add_dependency "patient_http", "~> 1.
|
|
40
|
+
spec.add_dependency "patient_http", "~> 1.2"
|
|
41
41
|
spec.add_dependency "prompt_builder"
|
|
42
42
|
|
|
43
43
|
spec.add_development_dependency "bundler"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: patient_llm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '1.
|
|
18
|
+
version: '1.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '1.
|
|
25
|
+
version: '1.2'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: prompt_builder
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|