lex-llm-bedrock 0.3.7 → 0.3.9
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 +18 -0
- data/lib/legion/extensions/llm/bedrock/provider.rb +164 -27
- data/lib/legion/extensions/llm/bedrock/version.rb +1 -1
- data/lib/legion/extensions/llm/bedrock.rb +43 -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: 1d0b15f1168f45e6f11211963cc8c0939085562bd92e720bcd74426367824318
|
|
4
|
+
data.tar.gz: e68d8865e321e62f0b7c54cf16a573286174626531229b325e53b0aecbc8c3ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac5ed2ff6e4d586891edc3d05a8935cd32b25b561862a882c04a17d2c42324f8cfde527338c8d464cc8373d06dfa4b758fc3a7ab4c1ac70072d22d9998799207
|
|
7
|
+
data.tar.gz: 1e6f4bd752aa5fe9eeb033426405718826135763e2f6974674a7238ffbf74134024ad8f164962d02dc908aa8276417451087c7092e623beda5e0df58977efbf4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.9 - 2026-05-18
|
|
4
|
+
|
|
5
|
+
- Fix streaming tool call parsing: `stream_converse` now handles content_block_start/delta/stop events for tool_use blocks, capturing tool ids, names, and accumulated input JSON. Previously only text deltas were captured and tool calls were silently dropped.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## 0.3.8 - 2026-05-13
|
|
9
|
+
|
|
10
|
+
- Auto-prefix `us.` on `inference_profile_id` for Anthropic, Meta, Mistral, Cohere, and AI21 models at API call time.
|
|
11
|
+
- Filter empty content blocks from messages to satisfy Bedrock validation.
|
|
12
|
+
- Wire Bearer token into AWS SDK via `Aws::StaticTokenProvider` to eliminate IMDS timeout on startup.
|
|
13
|
+
- Add `source` and `credential_fingerprint` fields to all discovered instances.
|
|
14
|
+
- Inject default capabilities into all discovered instances.
|
|
15
|
+
- Add static `CONTEXT_WINDOWS` map; `infer_limits` reads from `model_detail` cache instead of live API.
|
|
16
|
+
- Override `fetch_model_detail` to return static context window data without a network call.
|
|
17
|
+
- Cache live results in `discover_offerings`.
|
|
18
|
+
- Add `unresolved_credential?` filter — instances with `vault://` or `env://` credential refs are skipped during registration.
|
|
19
|
+
- Inject `default_model` into all discovered instances.
|
|
20
|
+
|
|
3
21
|
## 0.3.7 - 2026-05-12
|
|
4
22
|
|
|
5
23
|
- Use `Legion::Logging::Helper` explicitly across Bedrock provider, actor, and fleet runner logging surfaces.
|
|
@@ -26,6 +26,28 @@ module Legion
|
|
|
26
26
|
|
|
27
27
|
ALIASES = STATIC_MODELS.to_h { |entry| [entry.fetch(:alias), entry.fetch(:model)] }.freeze
|
|
28
28
|
|
|
29
|
+
CONTEXT_WINDOWS = {
|
|
30
|
+
'anthropic.claude-sonnet-4' => 200_000,
|
|
31
|
+
'anthropic.claude-haiku-4' => 200_000,
|
|
32
|
+
'anthropic.claude-opus-4' => 200_000,
|
|
33
|
+
'anthropic.claude-3-5-sonnet' => 200_000,
|
|
34
|
+
'anthropic.claude-3-5-haiku' => 200_000,
|
|
35
|
+
'anthropic.claude-3-haiku' => 200_000,
|
|
36
|
+
'anthropic.claude-3-opus' => 200_000,
|
|
37
|
+
'anthropic.claude-3-sonnet' => 200_000,
|
|
38
|
+
'meta.llama3' => 128_000,
|
|
39
|
+
'meta.llama3-1' => 128_000,
|
|
40
|
+
'meta.llama3-2' => 128_000,
|
|
41
|
+
'meta.llama3-3' => 128_000,
|
|
42
|
+
'mistral.mistral-large' => 128_000,
|
|
43
|
+
'mistral.mistral-small' => 128_000,
|
|
44
|
+
'amazon.titan-text-express' => 8_192,
|
|
45
|
+
'amazon.titan-text-premier' => 32_000,
|
|
46
|
+
'amazon.nova-pro' => 300_000,
|
|
47
|
+
'amazon.nova-lite' => 300_000,
|
|
48
|
+
'amazon.nova-micro' => 128_000
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
29
51
|
class << self
|
|
30
52
|
def slug = 'bedrock'
|
|
31
53
|
|
|
@@ -38,6 +60,7 @@ module Legion
|
|
|
38
60
|
bedrock_session_token
|
|
39
61
|
bedrock_profile
|
|
40
62
|
bedrock_stub_responses
|
|
63
|
+
bearer_token
|
|
41
64
|
]
|
|
42
65
|
end
|
|
43
66
|
|
|
@@ -51,6 +74,15 @@ module Legion
|
|
|
51
74
|
def resolve_model_id(model_id, config: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
52
75
|
ALIASES.fetch(model_id.to_s, model_id.to_s)
|
|
53
76
|
end
|
|
77
|
+
|
|
78
|
+
INFERENCE_PROFILE_PREFIXES = %w[anthropic. meta. mistral. cohere. ai21.].freeze
|
|
79
|
+
|
|
80
|
+
def inference_profile_id(model)
|
|
81
|
+
return model if model.start_with?('us.', 'eu.', 'ap.', 'arn:')
|
|
82
|
+
return model unless INFERENCE_PROFILE_PREFIXES.any? { |p| model.start_with?(p) }
|
|
83
|
+
|
|
84
|
+
"us.#{model}"
|
|
85
|
+
end
|
|
54
86
|
end
|
|
55
87
|
|
|
56
88
|
# Capability predicates inferred from Bedrock model IDs and API modalities.
|
|
@@ -86,15 +118,19 @@ module Legion
|
|
|
86
118
|
|
|
87
119
|
def discover_offerings(live: false, **filters)
|
|
88
120
|
unless live
|
|
121
|
+
return @cached_offerings if @cached_offerings&.any?
|
|
122
|
+
|
|
89
123
|
log.debug { 'bedrock.provider.discover_offerings: returning static catalog' }
|
|
90
124
|
return static_offerings(**filters)
|
|
91
125
|
end
|
|
92
126
|
|
|
93
127
|
log.info { "bedrock.provider.discover_offerings: listing foundation models (region=#{region})" }
|
|
94
128
|
response = bedrock_client.list_foundation_models(**filters)
|
|
95
|
-
Array(value(response, :model_summaries)).map
|
|
96
|
-
|
|
129
|
+
@cached_offerings = Array(value(response, :model_summaries)).map do |summary|
|
|
130
|
+
offering_from_summary(summary)
|
|
97
131
|
end
|
|
132
|
+
log.info { "bedrock.provider.discover_offerings: found #{@cached_offerings.size} models" }
|
|
133
|
+
@cached_offerings
|
|
98
134
|
end
|
|
99
135
|
|
|
100
136
|
def offering_for(model:, model_family: nil, instance_id: :default, **metadata)
|
|
@@ -194,7 +230,7 @@ module Legion
|
|
|
194
230
|
log.debug { "bedrock.provider.count_tokens: model=#{model_id(model)}" }
|
|
195
231
|
request = Utils.deep_merge(
|
|
196
232
|
{
|
|
197
|
-
model_id: model_id(model),
|
|
233
|
+
model_id: self.class.inference_profile_id(model_id(model)),
|
|
198
234
|
input: { converse: { messages: format_messages(messages), system: system_blocks(system) }.compact }
|
|
199
235
|
},
|
|
200
236
|
params
|
|
@@ -283,6 +319,7 @@ module Legion
|
|
|
283
319
|
|
|
284
320
|
def build_offering(model:, model_family:, usage_type:, instance_id: :default, alias_name: nil,
|
|
285
321
|
capabilities: nil, metadata: {})
|
|
322
|
+
limits = infer_limits(model)
|
|
286
323
|
Legion::Extensions::Llm::Routing::ModelOffering.new(
|
|
287
324
|
provider_family: :bedrock,
|
|
288
325
|
instance_id: instance_id,
|
|
@@ -291,10 +328,24 @@ module Legion
|
|
|
291
328
|
model: model,
|
|
292
329
|
usage_type: usage_type,
|
|
293
330
|
capabilities: capabilities || default_capabilities(model),
|
|
331
|
+
limits: limits,
|
|
294
332
|
metadata: metadata.merge(model_family: model_family, alias: alias_name).compact
|
|
295
333
|
)
|
|
296
334
|
end
|
|
297
335
|
|
|
336
|
+
def infer_limits(model)
|
|
337
|
+
detail = model_detail(model.to_s)
|
|
338
|
+
return detail if detail.is_a?(Hash) && detail[:context_window]
|
|
339
|
+
|
|
340
|
+
ctx = CONTEXT_WINDOWS.find { |prefix, _| model.to_s.start_with?(prefix) }&.last
|
|
341
|
+
ctx ? { context_window: ctx } : {}
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def fetch_model_detail(model_name)
|
|
345
|
+
ctx = CONTEXT_WINDOWS.find { |prefix, _| model_name.start_with?(prefix) }&.last
|
|
346
|
+
ctx ? { context_window: ctx } : nil
|
|
347
|
+
end
|
|
348
|
+
|
|
298
349
|
def configured_transport(default)
|
|
299
350
|
config.respond_to?(:transport) ? config.transport : default
|
|
300
351
|
end
|
|
@@ -305,7 +356,7 @@ module Legion
|
|
|
305
356
|
|
|
306
357
|
def converse_request(messages, model:, temperature:, max_tokens:, tools:, tool_prefs:)
|
|
307
358
|
{
|
|
308
|
-
model_id: model_id(model),
|
|
359
|
+
model_id: self.class.inference_profile_id(model_id(model)),
|
|
309
360
|
messages: format_messages(messages.reject { |message| message.role == :system }),
|
|
310
361
|
system: format_system(messages),
|
|
311
362
|
inference_config: { temperature: temperature, max_tokens: max_tokens || model_max_tokens(model) }.compact,
|
|
@@ -314,11 +365,11 @@ module Legion
|
|
|
314
365
|
end
|
|
315
366
|
|
|
316
367
|
def format_messages(messages)
|
|
317
|
-
messages.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
368
|
+
messages.filter_map do |message|
|
|
369
|
+
blocks = content_blocks(message.content)
|
|
370
|
+
next if blocks.empty?
|
|
371
|
+
|
|
372
|
+
{ role: bedrock_role(message.role), content: blocks }
|
|
322
373
|
end
|
|
323
374
|
end
|
|
324
375
|
|
|
@@ -339,7 +390,13 @@ module Legion
|
|
|
339
390
|
end
|
|
340
391
|
|
|
341
392
|
def content_blocks(content)
|
|
342
|
-
|
|
393
|
+
raw = raw_content(content)
|
|
394
|
+
return raw if raw
|
|
395
|
+
|
|
396
|
+
text = content_text(content)
|
|
397
|
+
return [] if text.strip.empty?
|
|
398
|
+
|
|
399
|
+
[{ text: text }]
|
|
343
400
|
end
|
|
344
401
|
|
|
345
402
|
def raw_content(content)
|
|
@@ -417,30 +474,99 @@ module Legion
|
|
|
417
474
|
end
|
|
418
475
|
|
|
419
476
|
def stream_converse(request, fallback_model)
|
|
420
|
-
|
|
421
|
-
final_usage = nil
|
|
477
|
+
state = { accumulated: +'', final_usage: nil, stop_reason: nil, tool_use_blocks: [], current_tool_use: nil }
|
|
422
478
|
|
|
423
479
|
runtime_client.converse_stream(**request) do |stream|
|
|
424
|
-
stream
|
|
425
|
-
|
|
426
|
-
|
|
480
|
+
wire_stream_handlers(stream, state, fallback_model) { |chunk| yield chunk if block_given? }
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
Legion::Extensions::Llm::Message.new(
|
|
484
|
+
role: :assistant,
|
|
485
|
+
content: state[:accumulated],
|
|
486
|
+
model_id: fallback_model,
|
|
487
|
+
tool_calls: build_stream_tool_calls(state[:tool_use_blocks]),
|
|
488
|
+
input_tokens: value(state[:final_usage], :input_tokens),
|
|
489
|
+
output_tokens: value(state[:final_usage], :output_tokens),
|
|
490
|
+
stop_reason: state[:stop_reason]
|
|
491
|
+
)
|
|
492
|
+
end
|
|
427
493
|
|
|
428
|
-
|
|
494
|
+
def wire_stream_handlers(stream, state, fallback_model, &)
|
|
495
|
+
wire_block_start(stream, state)
|
|
496
|
+
wire_block_delta(stream, state, fallback_model, &)
|
|
497
|
+
wire_block_stop(stream, state)
|
|
498
|
+
wire_message_stop(stream, state)
|
|
499
|
+
stream.on_metadata_event { |event| state[:final_usage] = value(event, :usage) }
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def wire_block_start(stream, state)
|
|
503
|
+
return unless stream.respond_to?(:on_content_block_start_event)
|
|
504
|
+
|
|
505
|
+
stream.on_content_block_start_event do |event|
|
|
506
|
+
start = value(event, :start)
|
|
507
|
+
tool_start = value(start, :tool_use) if start
|
|
508
|
+
next unless tool_start
|
|
509
|
+
|
|
510
|
+
state[:current_tool_use] = {
|
|
511
|
+
tool_use_id: value(tool_start, :tool_use_id),
|
|
512
|
+
name: value(tool_start, :name),
|
|
513
|
+
input_json: +''
|
|
514
|
+
}
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def wire_block_delta(stream, state, fallback_model)
|
|
519
|
+
stream.on_content_block_delta_event do |event|
|
|
520
|
+
delta = value(event, :delta)
|
|
521
|
+
text = value(delta, :text)
|
|
522
|
+
if text
|
|
523
|
+
state[:accumulated] << text
|
|
429
524
|
if block_given?
|
|
430
525
|
yield Legion::Extensions::Llm::Chunk.new(role: :assistant, content: text,
|
|
431
526
|
model_id: fallback_model)
|
|
432
527
|
end
|
|
433
528
|
end
|
|
434
|
-
|
|
529
|
+
|
|
530
|
+
tool_input = value(delta, :tool_use)
|
|
531
|
+
next unless tool_input && state[:current_tool_use]
|
|
532
|
+
|
|
533
|
+
input_chunk = value(tool_input, :input)
|
|
534
|
+
state[:current_tool_use][:input_json] << input_chunk.to_s if input_chunk
|
|
435
535
|
end
|
|
536
|
+
end
|
|
436
537
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
538
|
+
def wire_block_stop(stream, state)
|
|
539
|
+
return unless stream.respond_to?(:on_content_block_stop_event)
|
|
540
|
+
|
|
541
|
+
stream.on_content_block_stop_event do |_event|
|
|
542
|
+
next unless state[:current_tool_use]
|
|
543
|
+
|
|
544
|
+
state[:tool_use_blocks] << state[:current_tool_use]
|
|
545
|
+
state[:current_tool_use] = nil
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def wire_message_stop(stream, state)
|
|
550
|
+
return unless stream.respond_to?(:on_message_stop_event)
|
|
551
|
+
|
|
552
|
+
stream.on_message_stop_event do |event|
|
|
553
|
+
state[:stop_reason] = value(event, :stop_reason)
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def build_stream_tool_calls(tool_use_blocks)
|
|
558
|
+
return nil if tool_use_blocks.empty?
|
|
559
|
+
|
|
560
|
+
tool_use_blocks.to_h do |block|
|
|
561
|
+
input = begin
|
|
562
|
+
Legion::JSON.load(block[:input_json])
|
|
563
|
+
rescue StandardError
|
|
564
|
+
{}
|
|
565
|
+
end
|
|
566
|
+
name = block[:name]
|
|
567
|
+
id = block[:tool_use_id] || name
|
|
568
|
+
[id, Legion::Extensions::Llm::ToolCall.new(id: id, name: name, arguments: input)]
|
|
569
|
+
end
|
|
444
570
|
end
|
|
445
571
|
|
|
446
572
|
def parse_embedding_response(response, model:)
|
|
@@ -477,12 +603,23 @@ module Legion
|
|
|
477
603
|
end
|
|
478
604
|
|
|
479
605
|
def client_options
|
|
480
|
-
{
|
|
606
|
+
opts = {
|
|
481
607
|
region: region,
|
|
482
608
|
endpoint: config.bedrock_endpoint,
|
|
483
|
-
credentials: credentials,
|
|
484
609
|
stub_responses: config.bedrock_stub_responses
|
|
485
|
-
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if bearer_token_configured?
|
|
613
|
+
opts[:token_provider] = Aws::StaticTokenProvider.new(config.bearer_token)
|
|
614
|
+
else
|
|
615
|
+
opts[:credentials] = credentials
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
opts.compact
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def bearer_token_configured?
|
|
622
|
+
config.respond_to?(:bearer_token) && !config.bearer_token.to_s.empty?
|
|
486
623
|
end
|
|
487
624
|
|
|
488
625
|
def credentials
|
|
@@ -59,6 +59,8 @@ module Legion
|
|
|
59
59
|
@registry_publisher ||= Legion::Extensions::Llm::RegistryPublisher.new(provider_family: PROVIDER_FAMILY)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
DEFAULT_CAPABILITIES = %i[completion streaming embedding].freeze
|
|
63
|
+
|
|
62
64
|
def self.discover_instances
|
|
63
65
|
candidates = {}
|
|
64
66
|
discover_env_bearer(candidates)
|
|
@@ -66,7 +68,23 @@ module Legion
|
|
|
66
68
|
discover_env_sigv4(candidates)
|
|
67
69
|
discover_settings(candidates)
|
|
68
70
|
discover_broker(candidates)
|
|
69
|
-
CredentialSources.dedup_credentials(candidates)
|
|
71
|
+
CredentialSources.dedup_credentials(candidates)
|
|
72
|
+
.reject { |_, config| unresolved_credential?(config) }
|
|
73
|
+
.transform_values do |config|
|
|
74
|
+
sanitized = sanitize_instance_config(config)
|
|
75
|
+
sanitized[:capabilities] ||= DEFAULT_CAPABILITIES.dup
|
|
76
|
+
sanitized[:default_model] ||= 'us.anthropic.claude-sonnet-4-6'
|
|
77
|
+
sanitized
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.unresolved_credential?(config)
|
|
82
|
+
return false if config[:bedrock_profile]
|
|
83
|
+
|
|
84
|
+
cred = config[:bearer_token] || config[:bedrock_access_key_id] || config[:api_key]
|
|
85
|
+
return true if cred.nil?
|
|
86
|
+
|
|
87
|
+
cred.to_s.match?(%r{\A(vault|env)://})
|
|
70
88
|
end
|
|
71
89
|
|
|
72
90
|
def self.discover_env_bearer(candidates)
|
|
@@ -76,7 +94,9 @@ module Legion
|
|
|
76
94
|
candidates[:env_bearer] = {
|
|
77
95
|
bearer_token: bearer,
|
|
78
96
|
bedrock_region: CredentialSources.env('AWS_DEFAULT_REGION') || DEFAULT_REGION,
|
|
79
|
-
tier: :cloud
|
|
97
|
+
tier: :cloud,
|
|
98
|
+
source: CredentialSources.source_tag(:env, 'AWS_BEARER_TOKEN_BEDROCK'),
|
|
99
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(bearer)
|
|
80
100
|
}
|
|
81
101
|
end
|
|
82
102
|
|
|
@@ -88,7 +108,9 @@ module Legion
|
|
|
88
108
|
candidates[:claude] = {
|
|
89
109
|
bearer_token: claude_bearer,
|
|
90
110
|
bedrock_region: CredentialSources.claude_env_value('AWS_DEFAULT_REGION') || DEFAULT_REGION,
|
|
91
|
-
tier: :cloud
|
|
111
|
+
tier: :cloud,
|
|
112
|
+
source: CredentialSources.source_tag(:file, '~/.claude/settings.json', 'AWS_BEARER_TOKEN_BEDROCK'),
|
|
113
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(claude_bearer)
|
|
92
114
|
}
|
|
93
115
|
end
|
|
94
116
|
|
|
@@ -100,7 +122,10 @@ module Legion
|
|
|
100
122
|
candidates[:env_sigv4] = {
|
|
101
123
|
api_key: akid, bedrock_access_key_id: akid, bedrock_secret_access_key: skey,
|
|
102
124
|
bedrock_session_token: CredentialSources.env('AWS_SESSION_TOKEN'),
|
|
103
|
-
bedrock_region: CredentialSources.env('AWS_DEFAULT_REGION') || DEFAULT_REGION,
|
|
125
|
+
bedrock_region: CredentialSources.env('AWS_DEFAULT_REGION') || DEFAULT_REGION,
|
|
126
|
+
tier: :cloud,
|
|
127
|
+
source: CredentialSources.source_tag(:env, 'AWS_ACCESS_KEY_ID'),
|
|
128
|
+
credential_fingerprint: CredentialSources.credential_fingerprint(akid)
|
|
104
129
|
}.compact
|
|
105
130
|
end
|
|
106
131
|
|
|
@@ -109,12 +134,19 @@ module Legion
|
|
|
109
134
|
return unless settings.is_a?(Hash) && !settings.empty?
|
|
110
135
|
|
|
111
136
|
default_config = dedup_config(normalize_instance_config(settings))
|
|
112
|
-
|
|
137
|
+
unless default_config.empty?
|
|
138
|
+
default_config[:source] = CredentialSources.source_tag(:settings, 'extensions.llm.bedrock')
|
|
139
|
+
default_config[:credential_fingerprint] = CredentialSources.config_fingerprint(default_config)
|
|
140
|
+
candidates[:settings] = default_config.merge(tier: :cloud)
|
|
141
|
+
end
|
|
113
142
|
|
|
114
143
|
settings_instances(settings).each do |name, config|
|
|
115
144
|
next unless config.is_a?(Hash)
|
|
116
145
|
|
|
117
|
-
|
|
146
|
+
normalized = dedup_config(normalize_instance_config(config))
|
|
147
|
+
normalized[:source] = CredentialSources.source_tag(:settings, "extensions.llm.bedrock.instances.#{name}")
|
|
148
|
+
normalized[:credential_fingerprint] = CredentialSources.config_fingerprint(normalized)
|
|
149
|
+
candidates[name.to_sym] = normalized.merge(tier: :cloud)
|
|
118
150
|
end
|
|
119
151
|
end
|
|
120
152
|
|
|
@@ -122,7 +154,11 @@ module Legion
|
|
|
122
154
|
return unless defined?(Legion::Identity::Broker)
|
|
123
155
|
|
|
124
156
|
broker_creds = broker_aws_credentials
|
|
125
|
-
|
|
157
|
+
return unless broker_creds
|
|
158
|
+
|
|
159
|
+
broker_creds[:source] = CredentialSources.source_tag(:broker, 'identity', 'aws')
|
|
160
|
+
broker_creds[:credential_fingerprint] = CredentialSources.config_fingerprint(broker_creds)
|
|
161
|
+
candidates[:broker] = broker_creds.merge(tier: :cloud)
|
|
126
162
|
end
|
|
127
163
|
|
|
128
164
|
# Scan Claude config env hash for any key containing all of
|