lex-llm-anthropic 0.2.20 → 0.2.26
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 +28 -0
- data/lex-llm-anthropic.gemspec +1 -1
- data/lib/legion/extensions/llm/anthropic/actors/discovery_refresh.rb +123 -9
- data/lib/legion/extensions/llm/anthropic/provider.rb +36 -18
- data/lib/legion/extensions/llm/anthropic/registry_event_builder.rb +1 -1
- data/lib/legion/extensions/llm/anthropic/translator.rb +27 -1
- data/lib/legion/extensions/llm/anthropic/version.rb +1 -1
- data/lib/legion/extensions/llm/anthropic.rb +16 -2
- metadata +3 -4
- data/lib/legion/extensions/llm/anthropic/registry_publisher.rb +0 -96
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a43ec4b5821302a3e7a3144ee23d22bb4954530af9cebd7c5b51b6767915b575
|
|
4
|
+
data.tar.gz: 3a470f8f0a6b69a96411eb1c13276eee6428e90e176d93a42e64fb442ce4da02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c80ed046d1c0b7d9ad03be2ad3f06284732d5cb53419cc45bdf6c8ccbc3f53162c6e5831c6e42cc322435285dcd24693c4abc955c626995fbccb329b57e92f6
|
|
7
|
+
data.tar.gz: 975f2fde3051df569513aed373a8485559b240f7c0a548759c4fd6ec9d9b363c74f9c1788a43e33a102b6f7a73e60fb0f6d7f728d5d267ae4096a9aef72c493d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.26] - 2026-06-20
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Stub shared registry publishing through `RegistryPublisher#schedule` in specs so async availability-event coverage stays stable after the shared publisher moved off raw `Thread.new`.
|
|
7
|
+
|
|
8
|
+
## [0.2.25] - 2026-06-20
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Stop bulk-publishing Anthropic model availability from `list_models`; discovery now emits one registry event per seen model from the shared `lex-llm` policy-filter path so blocked models stay observable without duplicate publishes.
|
|
12
|
+
|
|
13
|
+
## [0.2.24] - 2026-06-20
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Route Anthropic capability overrides through the shared `lex-llm` provider contract so provider, instance, and model settings all resolve through the same canonical capability vocabulary.
|
|
17
|
+
|
|
18
|
+
## [0.2.23] - 2026-06-19
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Adopt `Legion::Extensions::Llm::Inventory::ScopedRefresher` mixin (lex-llm 0.6.0). Discovery
|
|
22
|
+
refresh actors now write directly to the live `Inventory` catalog via `Inventory.write_lane`.
|
|
23
|
+
- Pin `lex-llm >= 0.6.0` and `legion-llm >= 0.14.0` in gemspec.
|
|
24
|
+
- Standard `weight: 100` default added to provider instance settings schema.
|
|
25
|
+
|
|
26
|
+
## 0.2.21 - 2026-06-17
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **Policy-aware default model** — `default_model` is no longer a hardcoded literal forced via `||=`. The `claude-sonnet-4-6` fallback is now a named `DEFAULT_MODEL` constant applied through `Provider.policy_safe_default_model`, so a configured `model_whitelist`/`model_blacklist` is never overridden: if neither the configured default nor the fallback is permitted, `default_model` is left unset and routing resolves an allowed discovered model instead. (Fixes the case where a haiku-only whitelist still surfaced a sonnet default.) Requires lex-llm >= 0.5.4.
|
|
30
|
+
|
|
3
31
|
## 0.2.20 - 2026-06-16
|
|
4
32
|
|
|
5
33
|
- dependency updates, code quality improvements
|
data/lex-llm-anthropic.gemspec
CHANGED
|
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
28
28
|
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
29
29
|
spec.add_dependency 'legion-transport', '>= 1.4.14'
|
|
30
|
-
spec.add_dependency 'lex-llm', '>= 0.
|
|
30
|
+
spec.add_dependency 'lex-llm', '>= 0.6.0'
|
|
31
31
|
end
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'digest'
|
|
4
|
+
|
|
3
5
|
begin
|
|
4
6
|
require 'legion/extensions/actors/every'
|
|
5
7
|
rescue LoadError => e
|
|
6
8
|
warn(e.message) if $VERBOSE
|
|
7
9
|
end
|
|
8
10
|
|
|
11
|
+
begin
|
|
12
|
+
require 'legion/extensions/llm/inventory/scoped_refresher'
|
|
13
|
+
rescue LoadError => e
|
|
14
|
+
warn(e.message) if $VERBOSE
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
return unless defined?(Legion::Extensions::Actors::Every)
|
|
10
18
|
|
|
11
19
|
module Legion
|
|
@@ -15,8 +23,11 @@ module Legion
|
|
|
15
23
|
module Actor
|
|
16
24
|
class DiscoveryRefresh < Legion::Extensions::Actors::Every
|
|
17
25
|
include Legion::Logging::Helper
|
|
26
|
+
include Legion::Extensions::Llm::Inventory::ScopedRefresher if defined?(Legion::Extensions::Llm::Inventory::ScopedRefresher)
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
EMBED_TYPES = %i[embed embedding].freeze
|
|
29
|
+
|
|
30
|
+
def self.every_seconds = 3600
|
|
20
31
|
|
|
21
32
|
def runner_class = self.class
|
|
22
33
|
def runner_function = 'manual'
|
|
@@ -26,22 +37,125 @@ module Legion
|
|
|
26
37
|
def generate_task? = false
|
|
27
38
|
|
|
28
39
|
def time
|
|
29
|
-
return
|
|
40
|
+
return self.class.every_seconds unless defined?(Legion::Settings)
|
|
30
41
|
|
|
31
|
-
Legion::Settings.dig(:extensions, :llm, :anthropic, :discovery_interval) ||
|
|
42
|
+
Legion::Settings.dig(:extensions, :llm, :anthropic, :discovery_interval) || self.class.every_seconds
|
|
32
43
|
end
|
|
33
44
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
def scope_key
|
|
46
|
+
{ provider: :anthropic }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def compute_lanes_for_scope
|
|
50
|
+
return [] unless defined?(Legion::LLM::Call::Registry)
|
|
51
|
+
|
|
52
|
+
instances = Legion::LLM::Call::Registry.all_instances.select do |e|
|
|
53
|
+
(e[:provider] || '').to_sym == :anthropic
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
lanes = []
|
|
57
|
+
|
|
58
|
+
instances.each do |instance|
|
|
59
|
+
adapter = instance[:adapter]
|
|
60
|
+
source =
|
|
61
|
+
if adapter.respond_to?(:discover_offerings) then adapter.discover_offerings(live: true)
|
|
62
|
+
elsif adapter.respond_to?(:offerings) then adapter.offerings(live: false)
|
|
63
|
+
else next
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Array(source).filter_map do |raw|
|
|
67
|
+
offering = offering_to_hash(raw)
|
|
68
|
+
next unless offering
|
|
69
|
+
|
|
70
|
+
instance_id = instance[:instance] || instance[:instance_id] || instance[:id] || 'default'
|
|
71
|
+
model = offering[:model] || offering[:id]
|
|
72
|
+
next unless model
|
|
73
|
+
|
|
74
|
+
offering_type = if EMBED_TYPES.include?((offering[:type] || '').to_sym)
|
|
75
|
+
:embedding
|
|
76
|
+
else
|
|
77
|
+
:inference
|
|
78
|
+
end
|
|
37
79
|
|
|
38
|
-
|
|
80
|
+
tier = offering[:tier]&.to_sym || :frontier
|
|
39
81
|
|
|
40
|
-
|
|
41
|
-
|
|
82
|
+
capabilities = if defined?(Legion::Extensions::Llm::Inventory::Capabilities)
|
|
83
|
+
Legion::Extensions::Llm::Inventory::Capabilities.normalize(offering[:capabilities])
|
|
84
|
+
else
|
|
85
|
+
Array(offering[:capabilities])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
lane_id = Legion::Extensions::Llm::Inventory::ScopedRefresher.compose_id(
|
|
89
|
+
tier: tier,
|
|
90
|
+
provider_family: :anthropic,
|
|
91
|
+
instance_id: instance_id,
|
|
92
|
+
type: offering_type,
|
|
93
|
+
model: model
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
lane = {
|
|
97
|
+
id: lane_id,
|
|
98
|
+
tier: tier,
|
|
99
|
+
provider_family: :anthropic,
|
|
100
|
+
instance_id: instance_id,
|
|
101
|
+
model: model,
|
|
102
|
+
canonical_model_alias: offering[:canonical_model_alias],
|
|
103
|
+
type: offering_type,
|
|
104
|
+
capabilities: capabilities,
|
|
105
|
+
limits: offering[:limits] || {},
|
|
106
|
+
enabled: offering.fetch(:enabled, true),
|
|
107
|
+
cost: offering[:cost] || {}
|
|
108
|
+
}.compact
|
|
109
|
+
|
|
110
|
+
lanes << lane
|
|
111
|
+
|
|
112
|
+
# G29: also emit a fleet lane for each inference lane when fleet dispatch is configured
|
|
113
|
+
if offering_type == :inference
|
|
114
|
+
settings = Legion::Settings.dig(:extensions, :llm, :anthropic) || {}
|
|
115
|
+
if settings[:fleet]&.dig(:dispatch, :enabled)
|
|
116
|
+
fleet_id = Legion::Extensions::Llm::Inventory::ScopedRefresher.compose_id(
|
|
117
|
+
tier: :fleet,
|
|
118
|
+
provider_family: :anthropic,
|
|
119
|
+
instance_id: instance_id,
|
|
120
|
+
type: :inference,
|
|
121
|
+
model: model
|
|
122
|
+
)
|
|
123
|
+
lanes << lane.merge(id: fleet_id, tier: :fleet)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
lanes
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
handle_exception(e, level: :warn, handled: true, operation: 'anthropic.actor.discovery_refresh.compute_lanes')
|
|
132
|
+
[]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def credential_hash
|
|
136
|
+
settings = Legion::Settings.dig(:extensions, :llm, :anthropic) || {}
|
|
137
|
+
Digest::SHA256.hexdigest(settings[:api_key].to_s + settings[:instances].to_s)[0, 16]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def manual
|
|
141
|
+
tick if respond_to?(:tick)
|
|
42
142
|
rescue StandardError => e
|
|
43
143
|
handle_exception(e, level: :warn, handled: true, operation: 'anthropic.actor.discovery_refresh')
|
|
44
144
|
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
# ModelOffering objects do not implement `[]`; normalize to a Hash so the
|
|
149
|
+
# writer stays Hash-shaped. Hash inputs pass through untouched.
|
|
150
|
+
def offering_to_hash(offering)
|
|
151
|
+
return nil if offering.nil?
|
|
152
|
+
return offering if offering.is_a?(Hash)
|
|
153
|
+
|
|
154
|
+
hash = offering.to_h
|
|
155
|
+
hash[:type] ||= hash[:usage_type]
|
|
156
|
+
hash[:enabled] = offering.respond_to?(:enabled?) ? offering.enabled? : true
|
|
157
|
+
hash
|
|
158
|
+
end
|
|
45
159
|
end
|
|
46
160
|
end
|
|
47
161
|
end
|
|
@@ -65,11 +65,32 @@ module Legion
|
|
|
65
65
|
def list_models(**)
|
|
66
66
|
log.debug { 'listing available Anthropic models' }
|
|
67
67
|
super.tap do |models|
|
|
68
|
-
log.debug { "discovered #{Array(models).size} Anthropic model(s)
|
|
69
|
-
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
68
|
+
log.debug { "discovered #{Array(models).size} Anthropic model(s)" }
|
|
70
69
|
end
|
|
71
70
|
end
|
|
72
71
|
|
|
72
|
+
def discover_offerings(live: false, raise_on_unreachable: false, **filters)
|
|
73
|
+
return filter_cached_offerings(Array(@cached_offerings), filters) unless live
|
|
74
|
+
|
|
75
|
+
provider_health = health(live:)
|
|
76
|
+
readiness = discovery_registry_readiness(provider_health, live:)
|
|
77
|
+
@cached_offerings = Array(list_models(live:, **filters)).filter_map do |model|
|
|
78
|
+
self.class.registry_publisher.publish_models_async([model], readiness:)
|
|
79
|
+
next unless model_matches_filters?(model, filters)
|
|
80
|
+
next unless model_allowed?(model.id)
|
|
81
|
+
|
|
82
|
+
log.debug("[#{slug}] instance=#{provider_instance_id} action=model_discovered model=#{model.id} family=#{model.family}")
|
|
83
|
+
offering_from_model(model, health: provider_health)
|
|
84
|
+
end
|
|
85
|
+
log.info("[#{slug}] instance=#{provider_instance_id} action=discover_complete model_count=#{Array(@cached_offerings).size}")
|
|
86
|
+
@cached_offerings
|
|
87
|
+
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
|
88
|
+
log.warn("[#{slug}] instance=#{provider_instance_id} unreachable: #{e.message}")
|
|
89
|
+
raise if raise_on_unreachable
|
|
90
|
+
|
|
91
|
+
[]
|
|
92
|
+
end
|
|
93
|
+
|
|
73
94
|
CONTEXT_WINDOWS = {
|
|
74
95
|
'claude-opus-4' => 200_000,
|
|
75
96
|
'claude-sonnet-4' => 200_000,
|
|
@@ -84,6 +105,16 @@ module Legion
|
|
|
84
105
|
|
|
85
106
|
private
|
|
86
107
|
|
|
108
|
+
def discovery_registry_readiness(provider_health, live:)
|
|
109
|
+
{
|
|
110
|
+
provider: slug.to_sym,
|
|
111
|
+
configured: configured?,
|
|
112
|
+
ready: provider_health[:ready] == true,
|
|
113
|
+
live: live,
|
|
114
|
+
health: provider_health
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
87
118
|
def render_payload(messages, tools:, temperature:, model:, stream:, schema:, thinking:, tool_prefs:)
|
|
88
119
|
log_render_payload(messages:, tools:, model:, stream:, schema:)
|
|
89
120
|
system_messages, chat_messages = messages.partition { |message| message.role == :system }
|
|
@@ -420,30 +451,17 @@ module Legion
|
|
|
420
451
|
end
|
|
421
452
|
|
|
422
453
|
def resolve_model_capabilities(model_id)
|
|
423
|
-
provider_settings = CredentialSources.setting(:extensions, :llm, :anthropic)
|
|
424
|
-
provider_cfg = provider_settings.is_a?(Hash) ? provider_settings.except(:instances) : {}
|
|
425
|
-
model_cfg = model_config_for(model_id, provider_settings)
|
|
426
|
-
|
|
427
454
|
Legion::Extensions::Llm::CapabilityPolicy.resolve(
|
|
428
455
|
real: {},
|
|
429
456
|
provider_catalog: {},
|
|
430
457
|
probe: {},
|
|
431
458
|
provider_envelope: { streaming: true, tools: true },
|
|
432
|
-
provider_config:
|
|
433
|
-
instance_config:
|
|
434
|
-
model_config:
|
|
459
|
+
provider_config: provider_capability_config,
|
|
460
|
+
instance_config: instance_capability_config,
|
|
461
|
+
model_config: model_capability_config(model_id)
|
|
435
462
|
)
|
|
436
463
|
end
|
|
437
464
|
|
|
438
|
-
def model_config_for(model_id, provider_settings)
|
|
439
|
-
return {} unless provider_settings.is_a?(Hash)
|
|
440
|
-
|
|
441
|
-
models = provider_settings[:models] || provider_settings['models']
|
|
442
|
-
return {} unless models.is_a?(Hash)
|
|
443
|
-
|
|
444
|
-
models[model_id.to_sym] || models[model_id] || {}
|
|
445
|
-
end
|
|
446
|
-
|
|
447
465
|
def infer_context_window(model_id)
|
|
448
466
|
CONTEXT_WINDOWS.find { |prefix, _| model_id.start_with?(prefix) }&.last
|
|
449
467
|
end
|
|
@@ -59,7 +59,7 @@ module Legion
|
|
|
59
59
|
value = configured_node.to_s.strip
|
|
60
60
|
value.empty? ? :anthropic : value.to_sym
|
|
61
61
|
rescue StandardError => e
|
|
62
|
-
handle_exception(e, level: :
|
|
62
|
+
handle_exception(e, level: :warn, handled: true,
|
|
63
63
|
operation: 'anthropic.registry.provider_instance')
|
|
64
64
|
:anthropic
|
|
65
65
|
end
|
|
@@ -48,6 +48,7 @@ module Legion
|
|
|
48
48
|
else
|
|
49
49
|
render_system_content(system_messages)
|
|
50
50
|
end
|
|
51
|
+
inject_prompt_cache_control!(system_parts, canonical_request.system)
|
|
51
52
|
message_parts = render_messages(chat_messages, thinking: thinking_enabled?(canonical_request))
|
|
52
53
|
tools = render_tools(canonical_request.tools)
|
|
53
54
|
tool_choice = render_tool_choice(canonical_request.tool_choice)
|
|
@@ -172,7 +173,7 @@ module Legion
|
|
|
172
173
|
nil
|
|
173
174
|
end
|
|
174
175
|
rescue StandardError => e
|
|
175
|
-
handle_exception(e, level: :
|
|
176
|
+
handle_exception(e, level: :warn, handled: true, operation: 'anthropic.translator.parse_chunk')
|
|
176
177
|
Canonical::Chunk.error_chunk(
|
|
177
178
|
error: "#{e.class}: #{e.message}",
|
|
178
179
|
request_id: raw[:request_id] || ''
|
|
@@ -639,6 +640,31 @@ module Legion
|
|
|
639
640
|
:end_turn
|
|
640
641
|
end
|
|
641
642
|
|
|
643
|
+
# --- prompt caching ---
|
|
644
|
+
|
|
645
|
+
def inject_prompt_cache_control!(system_parts, raw_system)
|
|
646
|
+
return unless system_parts.is_a?(Array) && !system_parts.empty?
|
|
647
|
+
return unless raw_system.is_a?(String)
|
|
648
|
+
|
|
649
|
+
caching = prompt_caching_settings
|
|
650
|
+
return unless caching[:enabled] != false
|
|
651
|
+
|
|
652
|
+
min_tokens = caching[:min_tokens] || 1024
|
|
653
|
+
return unless raw_system.length > min_tokens
|
|
654
|
+
|
|
655
|
+
last = system_parts.last
|
|
656
|
+
return unless last.is_a?(Hash) && last[:type] == 'text'
|
|
657
|
+
|
|
658
|
+
last[:cache_control] = { type: 'ephemeral' }
|
|
659
|
+
log.debug "[llm][anthropic][translator] action=inject_cache_control length=#{raw_system.length}"
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def prompt_caching_settings
|
|
663
|
+
return @config[:prompt_caching] if @config.key?(:prompt_caching)
|
|
664
|
+
|
|
665
|
+
Legion::Settings.dig(:extensions, :llm, :anthropic, :prompt_caching) || {}
|
|
666
|
+
end
|
|
667
|
+
|
|
642
668
|
# --- settings helpers ---
|
|
643
669
|
|
|
644
670
|
def settings_default_max_tokens
|
|
@@ -17,12 +17,16 @@ module Legion
|
|
|
17
17
|
extend Legion::Extensions::Llm::AutoRegistration
|
|
18
18
|
|
|
19
19
|
PROVIDER_FAMILY = :anthropic
|
|
20
|
+
# Provider's preferred default when the operator configures none. Used only
|
|
21
|
+
# as a fallback and only when the configured model policy permits it
|
|
22
|
+
# (see resolve_default_model) — a whitelist/blacklist is never overridden.
|
|
23
|
+
DEFAULT_MODEL = 'claude-sonnet-4-6'
|
|
20
24
|
|
|
21
25
|
def self.default_settings
|
|
22
26
|
::Legion::Extensions::Llm.provider_settings(
|
|
23
27
|
family: PROVIDER_FAMILY,
|
|
24
28
|
instance: {
|
|
25
|
-
default_model:
|
|
29
|
+
default_model: DEFAULT_MODEL,
|
|
26
30
|
endpoint: 'https://api.anthropic.com',
|
|
27
31
|
api_version: '2023-10-16',
|
|
28
32
|
default_max_tokens: 4096,
|
|
@@ -31,6 +35,7 @@ module Legion
|
|
|
31
35
|
credentials: { api_key: 'env://ANTHROPIC_API_KEY' },
|
|
32
36
|
usage: { inference: true, embedding: false, image: false },
|
|
33
37
|
limits: { concurrency: 4 },
|
|
38
|
+
prompt_caching: {},
|
|
34
39
|
fleet: {
|
|
35
40
|
enabled: false,
|
|
36
41
|
respond_to_requests: false,
|
|
@@ -117,11 +122,20 @@ module Legion
|
|
|
117
122
|
CredentialSources.dedup_credentials(candidates).transform_values do |config|
|
|
118
123
|
sanitized = sanitize_instance_config(config)
|
|
119
124
|
sanitized[:capabilities] ||= %i[completion streaming vision tools].freeze
|
|
120
|
-
sanitized[:default_model]
|
|
125
|
+
sanitized[:default_model] = resolve_default_model(sanitized)
|
|
121
126
|
sanitized
|
|
122
127
|
end
|
|
123
128
|
end
|
|
124
129
|
|
|
130
|
+
# Resolve a default_model that never violates the configured model policy
|
|
131
|
+
# (whitelist/blacklist stays authoritative over the DEFAULT_MODEL fallback).
|
|
132
|
+
def self.resolve_default_model(config)
|
|
133
|
+
provider_class.policy_safe_default_model(
|
|
134
|
+
configured: config[:default_model], fallback: DEFAULT_MODEL,
|
|
135
|
+
**provider_class.model_policy(config, PROVIDER_FAMILY)
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
125
139
|
def self.settings_instances(config)
|
|
126
140
|
instances = config[:instances] || config['instances']
|
|
127
141
|
instances.is_a?(Hash) ? instances : {}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-llm-anthropic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -71,14 +71,14 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.
|
|
74
|
+
version: 0.6.0
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.
|
|
81
|
+
version: 0.6.0
|
|
82
82
|
description: Anthropic provider integration for the LegionIO LLM routing framework.
|
|
83
83
|
email:
|
|
84
84
|
- matthewdiverson@gmail.com
|
|
@@ -101,7 +101,6 @@ files:
|
|
|
101
101
|
- lib/legion/extensions/llm/anthropic/actors/fleet_worker.rb
|
|
102
102
|
- lib/legion/extensions/llm/anthropic/provider.rb
|
|
103
103
|
- lib/legion/extensions/llm/anthropic/registry_event_builder.rb
|
|
104
|
-
- lib/legion/extensions/llm/anthropic/registry_publisher.rb
|
|
105
104
|
- lib/legion/extensions/llm/anthropic/runners/fleet_worker.rb
|
|
106
105
|
- lib/legion/extensions/llm/anthropic/translator.rb
|
|
107
106
|
- lib/legion/extensions/llm/anthropic/transport/exchanges/llm_registry.rb
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'legion/logging/helper'
|
|
4
|
-
|
|
5
|
-
module Legion
|
|
6
|
-
module Extensions
|
|
7
|
-
module Llm
|
|
8
|
-
module Anthropic
|
|
9
|
-
# Best-effort publisher for Anthropic provider availability events.
|
|
10
|
-
class RegistryPublisher
|
|
11
|
-
include Legion::Logging::Helper
|
|
12
|
-
|
|
13
|
-
APP_ID = 'lex-llm-anthropic'
|
|
14
|
-
|
|
15
|
-
def initialize(builder: RegistryEventBuilder.new)
|
|
16
|
-
@builder = builder
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def publish_models_async(models, readiness:)
|
|
20
|
-
log.debug { "publishing #{Array(models).size} Anthropic model event(s) to llm.registry" }
|
|
21
|
-
schedule do
|
|
22
|
-
Array(models).each do |model|
|
|
23
|
-
publish_event(@builder.model_available(model, readiness:))
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def schedule(&)
|
|
31
|
-
return false unless publishing_available?
|
|
32
|
-
|
|
33
|
-
Thread.new do
|
|
34
|
-
Thread.current.abort_on_exception = false
|
|
35
|
-
yield
|
|
36
|
-
rescue StandardError => e
|
|
37
|
-
handle_exception(e, level: :debug, handled: true,
|
|
38
|
-
operation: 'anthropic.registry.schedule_thread')
|
|
39
|
-
end
|
|
40
|
-
rescue StandardError => e
|
|
41
|
-
handle_exception(e, level: :debug, handled: true,
|
|
42
|
-
operation: 'anthropic.registry.schedule')
|
|
43
|
-
false
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def publish_event(event)
|
|
47
|
-
return false unless publishing_available?
|
|
48
|
-
|
|
49
|
-
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
50
|
-
rescue StandardError => e
|
|
51
|
-
handle_exception(e, level: :warn, handled: true,
|
|
52
|
-
operation: 'anthropic.registry.publish_event')
|
|
53
|
-
false
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def publishing_available?
|
|
57
|
-
return false unless registry_event_available?
|
|
58
|
-
return false unless transport_message_available?
|
|
59
|
-
return true unless defined?(::Legion::Transport::Connection)
|
|
60
|
-
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
61
|
-
|
|
62
|
-
::Legion::Transport::Connection.session_open?
|
|
63
|
-
rescue StandardError => e
|
|
64
|
-
handle_exception(e, level: :debug, handled: true,
|
|
65
|
-
operation: 'anthropic.registry.publishing_available?')
|
|
66
|
-
false
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def registry_event_available?
|
|
70
|
-
defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def transport_message_available?
|
|
74
|
-
return true if message_class_defined?
|
|
75
|
-
return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
|
|
76
|
-
|
|
77
|
-
require 'legion/extensions/llm/anthropic/transport/messages/registry_event'
|
|
78
|
-
message_class_defined?
|
|
79
|
-
rescue LoadError => e
|
|
80
|
-
handle_exception(e, level: :debug, handled: true,
|
|
81
|
-
operation: 'anthropic.registry.transport_load')
|
|
82
|
-
false
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def message_class_defined?
|
|
86
|
-
defined?(::Legion::Extensions::Llm::Anthropic::Transport::Messages::RegistryEvent)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def message_class
|
|
90
|
-
::Legion::Extensions::Llm::Anthropic::Transport::Messages::RegistryEvent
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|