lex-llm-anthropic 0.2.6 → 0.2.8
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 +15 -0
- data/lib/legion/extensions/llm/anthropic/actors/fleet_worker.rb +10 -1
- data/lib/legion/extensions/llm/anthropic/provider.rb +35 -0
- data/lib/legion/extensions/llm/anthropic/registry_event_builder.rb +7 -1
- data/lib/legion/extensions/llm/anthropic/registry_publisher.rb +17 -17
- data/lib/legion/extensions/llm/anthropic/version.rb +1 -1
- data/lib/legion/extensions/llm/anthropic.rb +27 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23d189a1ea15ce64735d14d88766fcb6717136b9734c393599a7fbd3c7544b54
|
|
4
|
+
data.tar.gz: 5c3153468a9753fc406e5229ee41036113b5c396eb0b5761f933c2c8a78fba91
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 97685da785d9c89c5b63cb64406c3487f12b943aa28b289ef553de7ca32ab3e454ef51f8cff9ad0af4762fadd760453899e64f902a79579d652c2ee045ce0b8f
|
|
7
|
+
data.tar.gz: 796d40a66a89b96ac401beb3b74237a09c7bfb2fa26819e589c937adfc771cc40051338899dc19c80204006a351fd0a7886af1524f74d7b507bbb1d8e0e6836a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.8 - 2026-05-13
|
|
4
|
+
|
|
5
|
+
- Remove `:claude` provider alias (`provider_aliases` now returns `[]`).
|
|
6
|
+
- Attach `source` and `credential_fingerprint` to all discovered instances.
|
|
7
|
+
- Inject `default_model: 'claude-sonnet-4-6'` and `capabilities: [:completion, :streaming, :vision]` into every discovered instance.
|
|
8
|
+
- Add static `CONTEXT_WINDOWS` map for known Claude model families.
|
|
9
|
+
- Override `fetch_model_detail` to return context window from static map.
|
|
10
|
+
- Use `model_detail` in `parse_list_models_response` for cached `context_length` lookup.
|
|
11
|
+
- Add `infer_context_window` helper for prefix-based context window inference.
|
|
12
|
+
|
|
13
|
+
## 0.2.7 - 2026-05-13
|
|
14
|
+
|
|
15
|
+
- Use `Legion::Logging::Helper` for Anthropic provider and registry diagnostics.
|
|
16
|
+
- Route registry fallback errors through `handle_exception` with useful operation metadata.
|
|
17
|
+
|
|
3
18
|
## 0.2.6 - 2026-05-08
|
|
4
19
|
|
|
5
20
|
- Accept keyword arguments in `list_models` to match the base provider contract called by `discover_offerings`.
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
5
|
+
actor_load_logger = Object.new.extend(Legion::Logging::Helper)
|
|
6
|
+
actor_load_logger.define_singleton_method(:lex_filename) { 'llm_anthropic' }
|
|
7
|
+
|
|
3
8
|
begin
|
|
4
9
|
require 'legion/extensions/actors/subscription'
|
|
5
10
|
rescue LoadError => e
|
|
6
|
-
|
|
11
|
+
subscription_load_error = e
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
unless defined?(Legion::Extensions::Actors::Subscription)
|
|
15
|
+
if subscription_load_error
|
|
16
|
+
actor_load_logger.handle_exception(subscription_load_error, level: :warn, handled: true,
|
|
17
|
+
operation: 'anthropic.actor.subscription_load')
|
|
18
|
+
end
|
|
10
19
|
raise LoadError, 'LegionIO actor runtime is required for Anthropic fleet worker'
|
|
11
20
|
end
|
|
12
21
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
|
+
require 'legion/logging/helper'
|
|
4
5
|
|
|
5
6
|
module Legion
|
|
6
7
|
module Extensions
|
|
@@ -8,6 +9,8 @@ module Legion
|
|
|
8
9
|
module Anthropic
|
|
9
10
|
# Anthropic Messages API provider implementation for the Legion::Extensions::Llm contract.
|
|
10
11
|
class Provider < Legion::Extensions::Llm::Provider # rubocop:disable Metrics/ClassLength
|
|
12
|
+
include Legion::Logging::Helper
|
|
13
|
+
|
|
11
14
|
class << self
|
|
12
15
|
attr_writer :registry_publisher
|
|
13
16
|
|
|
@@ -52,14 +55,27 @@ module Legion
|
|
|
52
55
|
end
|
|
53
56
|
|
|
54
57
|
def list_models(**)
|
|
58
|
+
log.debug { 'listing available Anthropic models' }
|
|
55
59
|
super.tap do |models|
|
|
60
|
+
log.debug { "discovered #{Array(models).size} Anthropic model(s); publishing to registry" }
|
|
56
61
|
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
|
|
65
|
+
CONTEXT_WINDOWS = {
|
|
66
|
+
'claude-opus-4' => 200_000,
|
|
67
|
+
'claude-sonnet-4' => 200_000,
|
|
68
|
+
'claude-haiku-4' => 200_000,
|
|
69
|
+
'claude-3-5' => 200_000,
|
|
70
|
+
'claude-3-opus' => 200_000,
|
|
71
|
+
'claude-3-sonnet' => 200_000,
|
|
72
|
+
'claude-3-haiku' => 200_000
|
|
73
|
+
}.freeze
|
|
74
|
+
|
|
60
75
|
private
|
|
61
76
|
|
|
62
77
|
def render_payload(messages, tools:, temperature:, model:, stream:, schema:, thinking:, tool_prefs:) # rubocop:disable Metrics/ParameterLists
|
|
78
|
+
log_render_payload(messages:, tools:, model:, stream:, schema:)
|
|
63
79
|
system_messages, chat_messages = messages.partition { |message| message.role == :system }
|
|
64
80
|
|
|
65
81
|
{
|
|
@@ -76,6 +92,13 @@ module Legion
|
|
|
76
92
|
}.compact
|
|
77
93
|
end
|
|
78
94
|
|
|
95
|
+
def log_render_payload(messages:, tools:, model:, stream:, schema:)
|
|
96
|
+
log.debug do
|
|
97
|
+
"rendering Anthropic #{stream ? 'stream' : 'chat'} payload for #{model.id} " \
|
|
98
|
+
"with #{messages.size} message(s), #{tools.size} tool(s), schema=#{!schema.nil?}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
79
102
|
def system_content(messages)
|
|
80
103
|
content = messages.flat_map { |message| content_blocks(message.content) }
|
|
81
104
|
content.empty? ? nil : content
|
|
@@ -347,14 +370,26 @@ module Legion
|
|
|
347
370
|
def parse_list_models_response(response, provider, _capabilities)
|
|
348
371
|
Array(response.body['data']).map do |model|
|
|
349
372
|
model_id = model.fetch('id')
|
|
373
|
+
detail = model_detail(model_id)
|
|
374
|
+
ctx = detail&.dig(:context_window) || infer_context_window(model_id)
|
|
350
375
|
Legion::Extensions::Llm::Model::Info.new(
|
|
351
376
|
id: model_id,
|
|
352
377
|
name: model['display_name'] || model_id,
|
|
353
378
|
provider: provider,
|
|
379
|
+
context_length: ctx,
|
|
354
380
|
metadata: model.merge('created_at' => model['created_at']).compact
|
|
355
381
|
)
|
|
356
382
|
end
|
|
357
383
|
end
|
|
384
|
+
|
|
385
|
+
def infer_context_window(model_id)
|
|
386
|
+
CONTEXT_WINDOWS.find { |prefix, _| model_id.start_with?(prefix) }&.last
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def fetch_model_detail(model_name)
|
|
390
|
+
ctx = infer_context_window(model_name)
|
|
391
|
+
ctx ? { context_window: ctx } : nil
|
|
392
|
+
end
|
|
358
393
|
end
|
|
359
394
|
end
|
|
360
395
|
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
3
5
|
module Legion
|
|
4
6
|
module Extensions
|
|
5
7
|
module Llm
|
|
6
8
|
module Anthropic
|
|
7
9
|
# Builds sanitized lex-llm registry envelopes for Anthropic provider state.
|
|
8
10
|
class RegistryEventBuilder
|
|
11
|
+
include Legion::Logging::Helper
|
|
12
|
+
|
|
9
13
|
def model_available(model, readiness:)
|
|
10
14
|
registry_event_class.available(
|
|
11
15
|
model_offering(model),
|
|
@@ -54,7 +58,9 @@ module Legion
|
|
|
54
58
|
configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
|
|
55
59
|
value = configured_node.to_s.strip
|
|
56
60
|
value.empty? ? :anthropic : value.to_sym
|
|
57
|
-
rescue StandardError
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
handle_exception(e, level: :debug, handled: true,
|
|
63
|
+
operation: 'anthropic.registry.provider_instance')
|
|
58
64
|
:anthropic
|
|
59
65
|
end
|
|
60
66
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
3
5
|
module Legion
|
|
4
6
|
module Extensions
|
|
5
7
|
module Llm
|
|
6
8
|
module Anthropic
|
|
7
9
|
# Best-effort publisher for Anthropic provider availability events.
|
|
8
10
|
class RegistryPublisher
|
|
11
|
+
include Legion::Logging::Helper
|
|
12
|
+
|
|
9
13
|
APP_ID = 'lex-llm-anthropic'
|
|
10
14
|
|
|
11
15
|
def initialize(builder: RegistryEventBuilder.new)
|
|
@@ -13,6 +17,7 @@ module Legion
|
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
def publish_models_async(models, readiness:)
|
|
20
|
+
log.debug { "publishing #{Array(models).size} Anthropic model event(s) to llm.registry" }
|
|
16
21
|
schedule do
|
|
17
22
|
Array(models).each do |model|
|
|
18
23
|
publish_event(@builder.model_available(model, readiness:))
|
|
@@ -29,10 +34,12 @@ module Legion
|
|
|
29
34
|
Thread.current.abort_on_exception = false
|
|
30
35
|
yield
|
|
31
36
|
rescue StandardError => e
|
|
32
|
-
|
|
37
|
+
handle_exception(e, level: :debug, handled: true,
|
|
38
|
+
operation: 'anthropic.registry.schedule_thread')
|
|
33
39
|
end
|
|
34
40
|
rescue StandardError => e
|
|
35
|
-
|
|
41
|
+
handle_exception(e, level: :debug, handled: true,
|
|
42
|
+
operation: 'anthropic.registry.schedule')
|
|
36
43
|
false
|
|
37
44
|
end
|
|
38
45
|
|
|
@@ -41,7 +48,8 @@ module Legion
|
|
|
41
48
|
|
|
42
49
|
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
43
50
|
rescue StandardError => e
|
|
44
|
-
|
|
51
|
+
handle_exception(e, level: :warn, handled: true,
|
|
52
|
+
operation: 'anthropic.registry.publish_event')
|
|
45
53
|
false
|
|
46
54
|
end
|
|
47
55
|
|
|
@@ -52,7 +60,9 @@ module Legion
|
|
|
52
60
|
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
53
61
|
|
|
54
62
|
::Legion::Transport::Connection.session_open?
|
|
55
|
-
rescue StandardError
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
handle_exception(e, level: :debug, handled: true,
|
|
65
|
+
operation: 'anthropic.registry.publishing_available?')
|
|
56
66
|
false
|
|
57
67
|
end
|
|
58
68
|
|
|
@@ -66,7 +76,9 @@ module Legion
|
|
|
66
76
|
|
|
67
77
|
require 'legion/extensions/llm/anthropic/transport/messages/registry_event'
|
|
68
78
|
message_class_defined?
|
|
69
|
-
rescue LoadError
|
|
79
|
+
rescue LoadError => e
|
|
80
|
+
handle_exception(e, level: :debug, handled: true,
|
|
81
|
+
operation: 'anthropic.registry.transport_load')
|
|
70
82
|
false
|
|
71
83
|
end
|
|
72
84
|
|
|
@@ -77,18 +89,6 @@ module Legion
|
|
|
77
89
|
def message_class
|
|
78
90
|
::Legion::Extensions::Llm::Anthropic::Transport::Messages::RegistryEvent
|
|
79
91
|
end
|
|
80
|
-
|
|
81
|
-
def log_publish_failure(error, level: :warn)
|
|
82
|
-
message = "[lex-llm-anthropic] llm.registry publish failed: #{error.class}: #{error.message}"
|
|
83
|
-
logger = ::Legion::Extensions::Llm.logger if defined?(::Legion::Extensions::Llm)
|
|
84
|
-
if logger.respond_to?(level)
|
|
85
|
-
logger.public_send(level, message)
|
|
86
|
-
elsif logger.respond_to?(:debug)
|
|
87
|
-
logger.debug(message)
|
|
88
|
-
end
|
|
89
|
-
rescue StandardError
|
|
90
|
-
nil
|
|
91
|
-
end
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
|
+
require 'legion/logging/helper'
|
|
4
5
|
require 'legion/extensions/llm/anthropic/registry_event_builder'
|
|
5
6
|
require 'legion/extensions/llm/anthropic/registry_publisher'
|
|
6
7
|
require 'legion/extensions/llm/anthropic/provider'
|
|
@@ -10,8 +11,9 @@ module Legion
|
|
|
10
11
|
module Extensions
|
|
11
12
|
module Llm
|
|
12
13
|
# Anthropic provider extension namespace.
|
|
13
|
-
module Anthropic
|
|
14
|
+
module Anthropic # rubocop:disable Metrics/ModuleLength
|
|
14
15
|
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
16
|
+
extend Legion::Logging::Helper
|
|
15
17
|
extend Legion::Extensions::Llm::AutoRegistration
|
|
16
18
|
|
|
17
19
|
PROVIDER_FAMILY = :anthropic
|
|
@@ -20,6 +22,7 @@ module Legion
|
|
|
20
22
|
::Legion::Extensions::Llm.provider_settings(
|
|
21
23
|
family: PROVIDER_FAMILY,
|
|
22
24
|
instance: {
|
|
25
|
+
default_model: 'claude-sonnet-4-6',
|
|
23
26
|
endpoint: 'https://api.anthropic.com',
|
|
24
27
|
tier: :frontier,
|
|
25
28
|
transport: :http,
|
|
@@ -43,7 +46,7 @@ module Legion
|
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
def self.provider_aliases
|
|
46
|
-
[
|
|
49
|
+
[]
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
def self.discover_instances # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
@@ -54,7 +57,9 @@ module Legion
|
|
|
54
57
|
candidates[:env] = {
|
|
55
58
|
api_key: env_key,
|
|
56
59
|
anthropic_api_key: env_key,
|
|
57
|
-
tier: :frontier
|
|
60
|
+
tier: :frontier,
|
|
61
|
+
source: CredentialSources.source_tag(:env, 'ANTHROPIC_API_KEY'),
|
|
62
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(env_key)
|
|
58
63
|
}
|
|
59
64
|
end
|
|
60
65
|
|
|
@@ -63,7 +68,9 @@ module Legion
|
|
|
63
68
|
candidates[:claude] = {
|
|
64
69
|
api_key: claude_key,
|
|
65
70
|
anthropic_api_key: claude_key,
|
|
66
|
-
tier: :frontier
|
|
71
|
+
tier: :frontier,
|
|
72
|
+
source: CredentialSources.source_tag(:file, '~/.claude/settings.json', 'anthropicApiKey'),
|
|
73
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(claude_key)
|
|
67
74
|
}
|
|
68
75
|
end
|
|
69
76
|
|
|
@@ -74,7 +81,9 @@ module Legion
|
|
|
74
81
|
candidates[:settings] = normalize_instance_config(settings_config).merge(
|
|
75
82
|
api_key: settings_key,
|
|
76
83
|
anthropic_api_key: settings_key,
|
|
77
|
-
tier: :frontier
|
|
84
|
+
tier: :frontier,
|
|
85
|
+
source: CredentialSources.source_tag(:settings, 'extensions.llm.anthropic'),
|
|
86
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(settings_key)
|
|
78
87
|
)
|
|
79
88
|
end
|
|
80
89
|
|
|
@@ -85,6 +94,10 @@ module Legion
|
|
|
85
94
|
next unless normalized[:anthropic_api_key]
|
|
86
95
|
|
|
87
96
|
normalized[:api_key] = normalized[:anthropic_api_key]
|
|
97
|
+
normalized[:source] =
|
|
98
|
+
CredentialSources.source_tag(:settings, "extensions.llm.anthropic.instances.#{name}")
|
|
99
|
+
normalized[:credential_fingerprint] =
|
|
100
|
+
CredentialSources.credential_fingerprint(normalized[:anthropic_api_key])
|
|
88
101
|
candidates[name.to_sym] = normalized.merge(tier: :frontier)
|
|
89
102
|
end
|
|
90
103
|
end
|
|
@@ -95,12 +108,19 @@ module Legion
|
|
|
95
108
|
candidates[:broker] = {
|
|
96
109
|
api_key: broker_cred,
|
|
97
110
|
anthropic_api_key: broker_cred,
|
|
98
|
-
tier: :frontier
|
|
111
|
+
tier: :frontier,
|
|
112
|
+
source: CredentialSources.source_tag(:broker, 'identity', 'anthropic'),
|
|
113
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(broker_cred)
|
|
99
114
|
}
|
|
100
115
|
end
|
|
101
116
|
end
|
|
102
117
|
|
|
103
|
-
CredentialSources.dedup_credentials(candidates).transform_values
|
|
118
|
+
CredentialSources.dedup_credentials(candidates).transform_values do |config|
|
|
119
|
+
sanitized = sanitize_instance_config(config)
|
|
120
|
+
sanitized[:capabilities] ||= %i[completion streaming vision].freeze
|
|
121
|
+
sanitized[:default_model] ||= 'claude-sonnet-4-6'
|
|
122
|
+
sanitized
|
|
123
|
+
end
|
|
104
124
|
end
|
|
105
125
|
|
|
106
126
|
def self.settings_instances(config)
|