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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e95d99f4330626d49048eb320631d65206496fd54c14bfb83fb2fa5cae638735
4
- data.tar.gz: b7127571b56508f8c5bf3b1f68220368b8a674fa0caff76094d9ef932b6b51a7
3
+ metadata.gz: a17bd5b25cc4b70aa861ad36f5c700dc96558efde25b11dc9a362ac7d545450e
4
+ data.tar.gz: 7f02f82b866d53792ca985c45cc36672d025722922fa853c8ac36948ed9514d1
5
5
  SHA512:
6
- metadata.gz: f39817dc0618493345d1ca7935f84388d97ae87a152994f895ceb8729eb8cde2e4a45ac0c34070fe62dc7243b8b51f1910f0a44ce5bf3b67fdae0ae3c1fa993a
7
- data.tar.gz: 30b3e0705c65e392f5c5df2881ea9c17e3abea10e96001a7c95f2a9eb4b76cd8cde5cd7845313a85f6c30ff0dc69ce9ac73fca664277ec668116a07510c31a4a
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.
@@ -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.1.5'
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
- created_at: model['created_at'],
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
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Anthropic
7
- VERSION = '0.1.4'
7
+ VERSION = '0.2.0'
8
8
  end
9
9
  end
10
10
  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::Provider.register(Legion::Extensions::Llm::Anthropic::PROVIDER_FAMILY,
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.1.4
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.1.5
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.1.5
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: