lex-llm-anthropic 0.1.4 → 0.2.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 +11 -0
- data/lex-llm-anthropic.gemspec +1 -1
- data/lib/legion/extensions/llm/anthropic/provider.rb +13 -2
- data/lib/legion/extensions/llm/anthropic/registry_event_builder.rb +68 -0
- data/lib/legion/extensions/llm/anthropic/registry_publisher.rb +96 -0
- data/lib/legion/extensions/llm/anthropic/transport/exchanges/llm_registry.rb +24 -0
- data/lib/legion/extensions/llm/anthropic/transport/messages/registry_event.rb +42 -0
- data/lib/legion/extensions/llm/anthropic/version.rb +1 -1
- data/lib/legion/extensions/llm/anthropic.rb +59 -2
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a17bd5b25cc4b70aa861ad36f5c700dc96558efde25b11dc9a362ac7d545450e
|
|
4
|
+
data.tar.gz: 7f02f82b866d53792ca985c45cc36672d025722922fa853c8ac36948ed9514d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2b0860a1bb74cf8498f748aff5d709783591c8334c3f8ecfa52fd09fe6c29d84b0b74c2863fb64b76a3465520259ca26497b368e9db5cb6330a79f46a2a004c
|
|
7
|
+
data.tar.gz: 57800905a3e9e3faf9dd34e964b01c3b66a0eda08d86d7e2e4d3a7357d46a41164ab6740d5b5f532b505e7c150749cfe59fef411dffa2a4d0b3aeffb97644623
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 - 2026-05-01
|
|
4
|
+
|
|
5
|
+
- Add auto-discovery via CredentialSources and AutoRegistration from lex-llm 0.3.0
|
|
6
|
+
- Self-register discovered instances into Call::Registry at require-time
|
|
7
|
+
- Require lex-llm >= 0.3.0
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## 0.1.5 - 2026-04-28
|
|
11
|
+
|
|
12
|
+
- Publish best-effort `llm.registry` discovered-model availability events when transport is already loaded.
|
|
13
|
+
|
|
3
14
|
## 0.1.4 - 2026-04-28
|
|
4
15
|
|
|
5
16
|
- Require current shared Legion JSON, logging, settings, and LLM extension gems.
|
data/lex-llm-anthropic.gemspec
CHANGED
|
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
27
27
|
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
28
28
|
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
29
|
-
spec.add_dependency 'lex-llm', '>= 0.
|
|
29
|
+
spec.add_dependency 'lex-llm', '>= 0.3.0'
|
|
30
30
|
end
|
|
@@ -9,10 +9,16 @@ module Legion
|
|
|
9
9
|
# Anthropic Messages API provider implementation for the Legion::Extensions::Llm contract.
|
|
10
10
|
class Provider < Legion::Extensions::Llm::Provider # rubocop:disable Metrics/ClassLength
|
|
11
11
|
class << self
|
|
12
|
+
attr_writer :registry_publisher
|
|
13
|
+
|
|
12
14
|
def slug = 'anthropic'
|
|
13
15
|
def configuration_options = %i[anthropic_api_key anthropic_api_base anthropic_version]
|
|
14
16
|
def configuration_requirements = %i[anthropic_api_key]
|
|
15
17
|
def capabilities = Capabilities
|
|
18
|
+
|
|
19
|
+
def registry_publisher
|
|
20
|
+
@registry_publisher ||= RegistryPublisher.new
|
|
21
|
+
end
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
# Capability predicates for Anthropic chat model offerings.
|
|
@@ -45,6 +51,12 @@ module Legion
|
|
|
45
51
|
raise NotImplementedError, 'Anthropic does not expose embeddings through this provider'
|
|
46
52
|
end
|
|
47
53
|
|
|
54
|
+
def list_models
|
|
55
|
+
super.tap do |models|
|
|
56
|
+
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
private
|
|
49
61
|
|
|
50
62
|
def render_payload(messages, tools:, temperature:, model:, stream:, schema:, thinking:, tool_prefs:) # rubocop:disable Metrics/ParameterLists
|
|
@@ -339,8 +351,7 @@ module Legion
|
|
|
339
351
|
id: model_id,
|
|
340
352
|
name: model['display_name'] || model_id,
|
|
341
353
|
provider: provider,
|
|
342
|
-
|
|
343
|
-
metadata: model
|
|
354
|
+
metadata: model.merge('created_at' => model['created_at']).compact
|
|
344
355
|
)
|
|
345
356
|
end
|
|
346
357
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Anthropic
|
|
7
|
+
# Builds sanitized lex-llm registry envelopes for Anthropic provider state.
|
|
8
|
+
class RegistryEventBuilder
|
|
9
|
+
def model_available(model, readiness:)
|
|
10
|
+
registry_event_class.available(
|
|
11
|
+
model_offering(model),
|
|
12
|
+
runtime: runtime_metadata,
|
|
13
|
+
health: model_health(readiness),
|
|
14
|
+
metadata: model_metadata(model)
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def model_offering(model)
|
|
21
|
+
{
|
|
22
|
+
provider_family: :anthropic,
|
|
23
|
+
provider_instance: provider_instance,
|
|
24
|
+
transport: :http,
|
|
25
|
+
model: model.id,
|
|
26
|
+
usage_type: :inference,
|
|
27
|
+
capabilities: Array(model.capabilities).map(&:to_sym),
|
|
28
|
+
limits: model_limits(model),
|
|
29
|
+
metadata: { lex: :llm_anthropic, model_name: model.name }.compact
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def model_health(readiness)
|
|
34
|
+
ready = readiness.fetch(:ready, true) == true
|
|
35
|
+
{ ready:, status: ready ? :available : :degraded }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def model_metadata(model)
|
|
39
|
+
{ extension: :lex_llm_anthropic, provider: :anthropic, model_type: model.type }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def runtime_metadata
|
|
43
|
+
{ node: provider_instance }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def model_limits(model)
|
|
47
|
+
{
|
|
48
|
+
context_window: model.context_window,
|
|
49
|
+
max_output_tokens: model.max_output_tokens
|
|
50
|
+
}.compact
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def provider_instance
|
|
54
|
+
configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
|
|
55
|
+
value = configured_node.to_s.strip
|
|
56
|
+
value.empty? ? :anthropic : value.to_sym
|
|
57
|
+
rescue StandardError
|
|
58
|
+
:anthropic
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def registry_event_class
|
|
62
|
+
::Legion::Extensions::Llm::Routing::RegistryEvent
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Anthropic
|
|
7
|
+
# Best-effort publisher for Anthropic provider availability events.
|
|
8
|
+
class RegistryPublisher
|
|
9
|
+
APP_ID = 'lex-llm-anthropic'
|
|
10
|
+
|
|
11
|
+
def initialize(builder: RegistryEventBuilder.new)
|
|
12
|
+
@builder = builder
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def publish_models_async(models, readiness:)
|
|
16
|
+
schedule do
|
|
17
|
+
Array(models).each do |model|
|
|
18
|
+
publish_event(@builder.model_available(model, readiness:))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def schedule(&)
|
|
26
|
+
return false unless publishing_available?
|
|
27
|
+
|
|
28
|
+
Thread.new do
|
|
29
|
+
Thread.current.abort_on_exception = false
|
|
30
|
+
yield
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
log_publish_failure(e, level: :debug)
|
|
33
|
+
end
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
log_publish_failure(e, level: :debug)
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def publish_event(event)
|
|
40
|
+
return false unless publishing_available?
|
|
41
|
+
|
|
42
|
+
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
43
|
+
rescue StandardError => e
|
|
44
|
+
log_publish_failure(e)
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def publishing_available?
|
|
49
|
+
return false unless registry_event_available?
|
|
50
|
+
return false unless transport_message_available?
|
|
51
|
+
return true unless defined?(::Legion::Transport::Connection)
|
|
52
|
+
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
53
|
+
|
|
54
|
+
::Legion::Transport::Connection.session_open?
|
|
55
|
+
rescue StandardError
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def registry_event_available?
|
|
60
|
+
defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def transport_message_available?
|
|
64
|
+
return true if message_class_defined?
|
|
65
|
+
return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
|
|
66
|
+
|
|
67
|
+
require 'legion/extensions/llm/anthropic/transport/messages/registry_event'
|
|
68
|
+
message_class_defined?
|
|
69
|
+
rescue LoadError
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def message_class_defined?
|
|
74
|
+
defined?(::Legion::Extensions::Llm::Anthropic::Transport::Messages::RegistryEvent)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def message_class
|
|
78
|
+
::Legion::Extensions::Llm::Anthropic::Transport::Messages::RegistryEvent
|
|
79
|
+
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
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Anthropic
|
|
7
|
+
module Transport
|
|
8
|
+
module Exchanges
|
|
9
|
+
# Topic exchange for Anthropic provider availability events.
|
|
10
|
+
class LlmRegistry < ::Legion::Transport::Exchange
|
|
11
|
+
def exchange_name
|
|
12
|
+
'llm.registry'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def default_type
|
|
16
|
+
'topic'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/llm/anthropic/transport/exchanges/llm_registry'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Llm
|
|
8
|
+
module Anthropic
|
|
9
|
+
module Transport
|
|
10
|
+
module Messages
|
|
11
|
+
# Publishes lex-llm RegistryEvent envelopes to the llm.registry exchange.
|
|
12
|
+
class RegistryEvent < ::Legion::Transport::Message
|
|
13
|
+
def initialize(event:, **options)
|
|
14
|
+
super(**event.to_h.merge(options))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def exchange
|
|
18
|
+
Transport::Exchanges::LlmRegistry
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def routing_key
|
|
22
|
+
@options[:routing_key] || "llm.registry.#{@options.fetch(:event_type)}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def type
|
|
26
|
+
'llm.registry.event'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def app_id
|
|
30
|
+
@options[:app_id] || RegistryPublisher::APP_ID
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def persistent # rubocop:disable Naming/PredicateMethod
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
|
+
require 'legion/extensions/llm/anthropic/registry_event_builder'
|
|
5
|
+
require 'legion/extensions/llm/anthropic/registry_publisher'
|
|
4
6
|
require 'legion/extensions/llm/anthropic/provider'
|
|
5
7
|
require 'legion/extensions/llm/anthropic/version'
|
|
6
8
|
|
|
@@ -10,6 +12,7 @@ module Legion
|
|
|
10
12
|
# Anthropic provider extension namespace.
|
|
11
13
|
module Anthropic
|
|
12
14
|
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
15
|
+
extend Legion::Extensions::Llm::AutoRegistration
|
|
13
16
|
|
|
14
17
|
PROVIDER_FAMILY = :anthropic
|
|
15
18
|
|
|
@@ -30,10 +33,64 @@ module Legion
|
|
|
30
33
|
def self.provider_class
|
|
31
34
|
Provider
|
|
32
35
|
end
|
|
36
|
+
|
|
37
|
+
def self.discover_instances # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
38
|
+
candidates = {}
|
|
39
|
+
|
|
40
|
+
env_key = CredentialSources.env('ANTHROPIC_API_KEY')
|
|
41
|
+
if env_key
|
|
42
|
+
candidates[:env] = {
|
|
43
|
+
api_key: env_key,
|
|
44
|
+
anthropic_api_key: env_key,
|
|
45
|
+
tier: :frontier
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
claude_key = CredentialSources.claude_config_value(:anthropicApiKey)
|
|
50
|
+
if claude_key
|
|
51
|
+
candidates[:claude] = {
|
|
52
|
+
api_key: claude_key,
|
|
53
|
+
anthropic_api_key: claude_key,
|
|
54
|
+
tier: :frontier
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
settings_config = CredentialSources.setting(:extensions, :llm, :anthropic)
|
|
59
|
+
if settings_config.is_a?(Hash)
|
|
60
|
+
settings_key = settings_config[:api_key] || settings_config['api_key']
|
|
61
|
+
if settings_key
|
|
62
|
+
candidates[:settings] = settings_config.merge(
|
|
63
|
+
anthropic_api_key: settings_key,
|
|
64
|
+
tier: :frontier
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if defined?(Legion::Identity::Broker)
|
|
70
|
+
broker_cred = Legion::Identity::Broker.credential_for(:anthropic)
|
|
71
|
+
if broker_cred
|
|
72
|
+
candidates[:broker] = {
|
|
73
|
+
api_key: broker_cred,
|
|
74
|
+
anthropic_api_key: broker_cred,
|
|
75
|
+
tier: :frontier
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
CredentialSources.dedup_credentials(candidates)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.register_discovered_instances
|
|
84
|
+
super
|
|
85
|
+
return unless defined?(Legion::LLM::Call::Registry)
|
|
86
|
+
|
|
87
|
+
Legion::LLM::Call::Registry.instances_for(:anthropic).each do |instance_id, adapter|
|
|
88
|
+
Legion::LLM::Call::Registry.register(:claude, adapter, instance: instance_id)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
33
91
|
end
|
|
34
92
|
end
|
|
35
93
|
end
|
|
36
94
|
end
|
|
37
95
|
|
|
38
|
-
Legion::Extensions::Llm::
|
|
39
|
-
Legion::Extensions::Llm::Anthropic::Provider)
|
|
96
|
+
Legion::Extensions::Llm::Anthropic.register_discovered_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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0.
|
|
60
|
+
version: 0.3.0
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0.
|
|
67
|
+
version: 0.3.0
|
|
68
68
|
description: Anthropic provider integration for the LegionIO LLM routing framework.
|
|
69
69
|
email:
|
|
70
70
|
- matthewdiverson@gmail.com
|
|
@@ -84,6 +84,10 @@ files:
|
|
|
84
84
|
- lex-llm-anthropic.gemspec
|
|
85
85
|
- lib/legion/extensions/llm/anthropic.rb
|
|
86
86
|
- lib/legion/extensions/llm/anthropic/provider.rb
|
|
87
|
+
- lib/legion/extensions/llm/anthropic/registry_event_builder.rb
|
|
88
|
+
- lib/legion/extensions/llm/anthropic/registry_publisher.rb
|
|
89
|
+
- lib/legion/extensions/llm/anthropic/transport/exchanges/llm_registry.rb
|
|
90
|
+
- lib/legion/extensions/llm/anthropic/transport/messages/registry_event.rb
|
|
87
91
|
- lib/legion/extensions/llm/anthropic/version.rb
|
|
88
92
|
homepage: https://github.com/LegionIO/lex-llm-anthropic
|
|
89
93
|
licenses:
|