lex-llm-bedrock 0.1.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 +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/dependabot.yml +10 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +32 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +20 -0
- data/LICENSE +21 -0
- data/README.md +79 -0
- data/lex-llm-bedrock.gemspec +32 -0
- data/lib/legion/extensions/llm/bedrock/provider.rb +508 -0
- data/lib/legion/extensions/llm/bedrock/version.rb +11 -0
- data/lib/legion/extensions/llm/bedrock.rb +47 -0
- metadata +143 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 59c2c3fda768fc58435dbe327fd8314e4b9dbfdbdf978e9220e3ad4999e42bc1
|
|
4
|
+
data.tar.gz: 8b448d4dbb18e1c8d2d3dde0c817cc49591142d5bc88c6057cf8f8f28d3cab8d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 47d5d1f35bd64d93db2e068b988a4ff9bb3d8622726f23cfe897b282f8cd1c4f454a44b82c170c04831c108226f373869121ac9a61f574bf023a13a8d9c92c8b
|
|
7
|
+
data.tar.gz: 2c9679fabbce22abde6a5f7e58da92c34e113afe5b6f166e3956ee6c91034f7178737e95897a93ba348750c85358822b3dc464841dfcbbf37d74ceb66469ef03
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @LegionIO/maintainers
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-performance
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
- rubocop-rspec
|
|
5
|
+
|
|
6
|
+
AllCops:
|
|
7
|
+
NewCops: enable
|
|
8
|
+
TargetRubyVersion: 3.4
|
|
9
|
+
SuggestExtensions: false
|
|
10
|
+
|
|
11
|
+
Metrics/BlockLength:
|
|
12
|
+
Exclude:
|
|
13
|
+
- "*.gemspec"
|
|
14
|
+
- spec/**/*
|
|
15
|
+
Metrics/MethodLength:
|
|
16
|
+
Enabled: false
|
|
17
|
+
Metrics/ParameterLists:
|
|
18
|
+
Enabled: false
|
|
19
|
+
Metrics/AbcSize:
|
|
20
|
+
Enabled: false
|
|
21
|
+
Metrics/CyclomaticComplexity:
|
|
22
|
+
Enabled: false
|
|
23
|
+
Metrics/PerceivedComplexity:
|
|
24
|
+
Enabled: false
|
|
25
|
+
RSpec/MultipleExpectations:
|
|
26
|
+
Enabled: false
|
|
27
|
+
RSpec/ExampleLength:
|
|
28
|
+
Enabled: false
|
|
29
|
+
RSpec/LeakyConstantDeclaration:
|
|
30
|
+
Enabled: false
|
|
31
|
+
RSpec/InstanceVariable:
|
|
32
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-04-28
|
|
4
|
+
|
|
5
|
+
- Initial Legion::Extensions::Llm Bedrock provider extension scaffold.
|
|
6
|
+
- Add offline provider defaults, model offering mapping, AWS SDK client construction, chat, streaming, embeddings, token counting, health, and live discovery entrypoints.
|
|
7
|
+
- Add README, gemspec, CI, and stubbed unit specs for Bedrock routing behavior.
|
data/Gemfile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
group :test do
|
|
6
|
+
llm_base_path = ENV.fetch('LEX_LLM_PATH', File.expand_path('../lex-llm', __dir__))
|
|
7
|
+
gem 'lex-llm', path: llm_base_path if File.directory?(llm_base_path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
gemspec
|
|
11
|
+
|
|
12
|
+
group :development do
|
|
13
|
+
gem 'bundler', '>= 2.0'
|
|
14
|
+
gem 'rake', '>= 13.0'
|
|
15
|
+
gem 'rspec', '~> 3.12'
|
|
16
|
+
gem 'rubocop', '>= 1.0'
|
|
17
|
+
gem 'rubocop-performance'
|
|
18
|
+
gem 'rubocop-rake', '>= 0.6'
|
|
19
|
+
gem 'rubocop-rspec'
|
|
20
|
+
end
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LegionIO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# lex-llm-bedrock
|
|
2
|
+
|
|
3
|
+
Amazon Bedrock provider extension for `Legion::Extensions::Llm`.
|
|
4
|
+
|
|
5
|
+
This gem adds a hosted Bedrock provider surface for Legion LLM routing without depending on the old `legion-llm` gem. It uses the official AWS SDK for Ruby and keeps discovery offline by default, so loading the extension or running tests does not require live AWS credentials.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'lex-llm-bedrock'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
The provider registers the `:bedrock` provider family with `Legion::Extensions::Llm::Provider`.
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require 'legion/extensions/llm/bedrock'
|
|
19
|
+
|
|
20
|
+
Legion::Extensions::Llm.configure do |config|
|
|
21
|
+
config.bedrock_region = ENV.fetch('AWS_REGION', 'us-east-1')
|
|
22
|
+
config.bedrock_access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
|
23
|
+
config.bedrock_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
|
24
|
+
config.bedrock_session_token = ENV['AWS_SESSION_TOKEN']
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If explicit keys are not configured, the AWS SDK default credential provider chain is used. Default settings expose `env://` credential references and mark live discovery disabled:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
Legion::Extensions::Llm::Bedrock.default_settings
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Provider Surface
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
provider = Legion::Extensions::Llm::Bedrock::Provider.new(Legion::Extensions::Llm.config)
|
|
38
|
+
|
|
39
|
+
provider.discover_offerings(live: false)
|
|
40
|
+
provider.offering_for(model: 'anthropic.claude-3-haiku-20240307-v1:0')
|
|
41
|
+
provider.health(live: false)
|
|
42
|
+
provider.chat(messages, model: model)
|
|
43
|
+
provider.stream(messages, model: model) { |chunk| chunk.content }
|
|
44
|
+
provider.embed('hello', model: 'amazon.titan-embed-text-v2:0')
|
|
45
|
+
provider.count_tokens(messages, model: model)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`discover_offerings(live: false)` returns a small static catalog that is useful for routing defaults and unit tests. `discover_offerings(live: true)` calls Bedrock `ListFoundationModels` and maps the returned model summaries into `Legion::Extensions::Llm::Routing::ModelOffering` records.
|
|
49
|
+
|
|
50
|
+
## Model Offerings
|
|
51
|
+
|
|
52
|
+
Every offering uses:
|
|
53
|
+
|
|
54
|
+
- `provider_family: :bedrock`
|
|
55
|
+
- `transport: :aws_sdk`
|
|
56
|
+
- the Bedrock model ID as `model`
|
|
57
|
+
- `metadata[:model_family]` inferred from the provider prefix or accepted from the caller
|
|
58
|
+
|
|
59
|
+
Known aliases are intentionally small and conservative. For example, `claude-3-haiku` resolves to `anthropic.claude-3-haiku-20240307-v1:0`, while the preserved Bedrock model ID remains the routing model.
|
|
60
|
+
|
|
61
|
+
## API Contract
|
|
62
|
+
|
|
63
|
+
The implementation is intentionally limited to Bedrock operations documented by AWS:
|
|
64
|
+
|
|
65
|
+
- `ListFoundationModels` for live model discovery
|
|
66
|
+
- `Converse` for chat-style inference
|
|
67
|
+
- `ConverseStream` for streaming chat responses
|
|
68
|
+
- `CountTokens` for token estimates
|
|
69
|
+
- `InvokeModel` only for the Titan text embedding request shape implemented here
|
|
70
|
+
|
|
71
|
+
Provider-specific request bodies are not guessed. Non-Titan embedding models raise until their documented body shape is added explicitly.
|
|
72
|
+
|
|
73
|
+
AWS references:
|
|
74
|
+
|
|
75
|
+
- [Converse](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
|
|
76
|
+
- [ConverseStream](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html)
|
|
77
|
+
- [CountTokens](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_CountTokens.html)
|
|
78
|
+
- [ListFoundationModels](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_ListFoundationModels.html)
|
|
79
|
+
- [Foundation model information](https://docs.aws.amazon.com/bedrock/latest/userguide/foundation-models-reference.html)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/llm/bedrock/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-llm-bedrock'
|
|
7
|
+
spec.version = Legion::Extensions::Llm::Bedrock::VERSION
|
|
8
|
+
spec.authors = ['LegionIO']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
spec.summary = 'LegionIO LLM Amazon Bedrock provider extension'
|
|
11
|
+
spec.description = 'Amazon Bedrock provider integration for the LegionIO LLM routing framework.'
|
|
12
|
+
spec.homepage = 'https://github.com/LegionIO/lex-llm-bedrock'
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
spec.required_ruby_version = '>= 3.4'
|
|
15
|
+
|
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
17
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['documentation_uri'] = spec.homepage
|
|
19
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
21
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
22
|
+
|
|
23
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |file| file.match(%r{^(spec|test|features|tmp|coverage)/}) }
|
|
24
|
+
spec.require_paths = ['lib']
|
|
25
|
+
|
|
26
|
+
spec.add_dependency 'aws-sdk-bedrock'
|
|
27
|
+
spec.add_dependency 'aws-sdk-bedrockruntime'
|
|
28
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
29
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
30
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
31
|
+
spec.add_dependency 'lex-llm', '>= 0.1.3'
|
|
32
|
+
end
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aws-sdk-bedrock'
|
|
4
|
+
require 'aws-sdk-bedrockruntime'
|
|
5
|
+
require 'legion/json'
|
|
6
|
+
require 'legion/logging'
|
|
7
|
+
require 'legion/settings'
|
|
8
|
+
require 'legion/extensions/llm'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Llm
|
|
13
|
+
module Bedrock
|
|
14
|
+
# Amazon Bedrock provider implementation for the Legion::Extensions::Llm contract.
|
|
15
|
+
class Provider < Legion::Extensions::Llm::Provider # rubocop:disable Metrics/ClassLength
|
|
16
|
+
DEFAULT_REGION = 'us-east-1'
|
|
17
|
+
|
|
18
|
+
STATIC_MODELS = [
|
|
19
|
+
{ model: 'anthropic.claude-3-haiku-20240307-v1:0', alias: 'claude-3-haiku' },
|
|
20
|
+
{ model: 'amazon.titan-text-express-v1', alias: 'titan-text-express' },
|
|
21
|
+
{ model: 'amazon.titan-embed-text-v2:0', alias: 'titan-embed-text-v2', usage_type: :embedding },
|
|
22
|
+
{ model: 'meta.llama3-2-11b-instruct-v1:0', alias: 'llama-3.2-11b-instruct' },
|
|
23
|
+
{ model: 'mistral.mistral-large-3-675b-instruct', alias: 'mistral-large-3' }
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
ALIASES = STATIC_MODELS.to_h { |entry| [entry.fetch(:alias), entry.fetch(:model)] }.freeze
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
def slug = 'bedrock'
|
|
30
|
+
|
|
31
|
+
def configuration_options
|
|
32
|
+
%i[
|
|
33
|
+
bedrock_region
|
|
34
|
+
bedrock_endpoint
|
|
35
|
+
bedrock_access_key_id
|
|
36
|
+
bedrock_secret_access_key
|
|
37
|
+
bedrock_session_token
|
|
38
|
+
bedrock_profile
|
|
39
|
+
bedrock_stub_responses
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def configuration_requirements = []
|
|
44
|
+
def capabilities = Capabilities
|
|
45
|
+
|
|
46
|
+
def resolve_model_id(model_id, config: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
47
|
+
ALIASES.fetch(model_id.to_s, model_id.to_s)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Capability predicates inferred from Bedrock model IDs and API modalities.
|
|
52
|
+
module Capabilities
|
|
53
|
+
module_function
|
|
54
|
+
|
|
55
|
+
def chat?(model) = !embeddings?(model)
|
|
56
|
+
def streaming?(model) = chat?(model)
|
|
57
|
+
def vision?(model) = model_id(model).match?(/(claude-3|llama3-2-(11|90)b)/)
|
|
58
|
+
def functions?(model) = chat?(model)
|
|
59
|
+
def embeddings?(model) = model_id(model).match?(/embed|embedding/)
|
|
60
|
+
|
|
61
|
+
def model_id(model)
|
|
62
|
+
return model.fetch('model', model.fetch('id', '')) if model.is_a?(Hash)
|
|
63
|
+
|
|
64
|
+
model.respond_to?(:id) ? model.id.to_s : model.to_s
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def api_base
|
|
69
|
+
config.bedrock_endpoint || "https://bedrock-runtime.#{region}.amazonaws.com"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def completion_url = 'Converse'
|
|
73
|
+
def stream_url = 'ConverseStream'
|
|
74
|
+
def models_url = 'ListFoundationModels'
|
|
75
|
+
def embedding_url(**) = 'InvokeModel'
|
|
76
|
+
def count_tokens_url = 'CountTokens'
|
|
77
|
+
|
|
78
|
+
def region
|
|
79
|
+
config.bedrock_region || DEFAULT_REGION
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def discover_offerings(live: false, **filters)
|
|
83
|
+
return static_offerings(**filters) unless live
|
|
84
|
+
|
|
85
|
+
response = bedrock_client.list_foundation_models(**filters)
|
|
86
|
+
Array(value(response, :model_summaries)).map { |summary| offering_from_summary(summary) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def offering_for(model:, model_family: nil, instance_id: :default, **metadata)
|
|
90
|
+
model_id = self.class.resolve_model_id(model)
|
|
91
|
+
build_offering(
|
|
92
|
+
model: model_id,
|
|
93
|
+
alias_name: alias_for(model_id),
|
|
94
|
+
model_family: model_family || model_family_for(model_id),
|
|
95
|
+
instance_id: instance_id,
|
|
96
|
+
usage_type: metadata.delete(:usage_type) || usage_type_for(model_id),
|
|
97
|
+
metadata: metadata
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def health(live: false)
|
|
102
|
+
baseline = {
|
|
103
|
+
provider: :bedrock,
|
|
104
|
+
region: region,
|
|
105
|
+
configured: true,
|
|
106
|
+
ready: true,
|
|
107
|
+
live: live,
|
|
108
|
+
credentials: credential_source
|
|
109
|
+
}
|
|
110
|
+
return baseline.merge(checked: false) unless live
|
|
111
|
+
|
|
112
|
+
bedrock_client.list_foundation_models
|
|
113
|
+
baseline.merge(checked: true)
|
|
114
|
+
rescue StandardError => e
|
|
115
|
+
baseline.merge(checked: true, ready: false, error: e.class.name, message: e.message)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def readiness(live: false)
|
|
119
|
+
health(live: live).merge(local: false, remote: true, api_base: api_base, endpoints: endpoint_manifest)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def list_models
|
|
123
|
+
discover_offerings(live: true).map do |offering|
|
|
124
|
+
Legion::Extensions::Llm::Model::Info.new(
|
|
125
|
+
id: offering.model,
|
|
126
|
+
name: offering.metadata[:alias] || offering.model,
|
|
127
|
+
provider: :bedrock,
|
|
128
|
+
family: offering.metadata[:model_family],
|
|
129
|
+
capabilities: offering.capabilities.map(&:to_s),
|
|
130
|
+
metadata: offering.to_h
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def chat(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {})
|
|
136
|
+
request = Utils.deep_merge(
|
|
137
|
+
converse_request(messages, model:, temperature:, max_tokens:, tools:, tool_prefs:),
|
|
138
|
+
params
|
|
139
|
+
)
|
|
140
|
+
parse_converse_response(runtime_client.converse(**request), model_id(model))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def stream(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {},
|
|
144
|
+
&)
|
|
145
|
+
request = Utils.deep_merge(
|
|
146
|
+
converse_request(messages, model:, temperature:, max_tokens:, tools:, tool_prefs:),
|
|
147
|
+
params
|
|
148
|
+
)
|
|
149
|
+
stream_converse(request, model_id(model), &)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def count_tokens(messages, model:, system: nil, params: {})
|
|
153
|
+
request = Utils.deep_merge(
|
|
154
|
+
{
|
|
155
|
+
model_id: model_id(model),
|
|
156
|
+
input: { converse: { messages: format_messages(messages), system: system_blocks(system) }.compact }
|
|
157
|
+
},
|
|
158
|
+
params
|
|
159
|
+
)
|
|
160
|
+
response = runtime_client.count_tokens(**request)
|
|
161
|
+
{ input_tokens: value(response, :input_tokens), raw: normalize_response(response) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def embed(text, model:, dimensions: nil)
|
|
165
|
+
model_id = model_id(model)
|
|
166
|
+
unless titan_embed?(model_id)
|
|
167
|
+
raise NotImplementedError,
|
|
168
|
+
"Bedrock embedding payload for #{model_id} is not standardized"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
body = { inputText: text, dimensions: dimensions }.compact
|
|
172
|
+
response = runtime_client.invoke_model(
|
|
173
|
+
model_id: model_id,
|
|
174
|
+
content_type: 'application/json',
|
|
175
|
+
accept: 'application/json',
|
|
176
|
+
body: Legion::JSON.generate(body)
|
|
177
|
+
)
|
|
178
|
+
parse_embedding_response(response, model: model_id)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def complete(messages, tools:, temperature:, model:, params: {}, schema: nil, thinking: nil, tool_prefs: nil,
|
|
182
|
+
&)
|
|
183
|
+
payload = params.dup
|
|
184
|
+
payload[:additional_model_request_fields] ||= {}
|
|
185
|
+
payload[:additional_model_request_fields][:thinking] = thinking if thinking
|
|
186
|
+
payload[:additional_model_request_fields][:response_format] = schema if schema
|
|
187
|
+
|
|
188
|
+
if block_given?
|
|
189
|
+
stream(messages, model:, temperature:, tools:, tool_prefs:, params: payload, &)
|
|
190
|
+
else
|
|
191
|
+
chat(messages, model:, temperature:, tools:, tool_prefs:, params: payload)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
|
|
197
|
+
def static_offerings(**filters)
|
|
198
|
+
STATIC_MODELS.filter_map do |entry|
|
|
199
|
+
provider_filter = normalize_provider(filters[:by_provider])
|
|
200
|
+
next if provider_filter && model_family_for(entry.fetch(:model)) != provider_filter
|
|
201
|
+
|
|
202
|
+
offering_for(**entry.slice(:model, :usage_type))
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def offering_from_summary(summary)
|
|
207
|
+
model = value(summary, :model_id)
|
|
208
|
+
build_offering(
|
|
209
|
+
model: model,
|
|
210
|
+
alias_name: alias_for(model),
|
|
211
|
+
model_family: normalize_provider(value(summary, :provider_name)) || model_family_for(model),
|
|
212
|
+
usage_type: usage_type_from_modalities(value(summary, :output_modalities)),
|
|
213
|
+
capabilities: capabilities_from_summary(summary),
|
|
214
|
+
metadata: normalize_response(summary)
|
|
215
|
+
)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def build_offering(model:, model_family:, usage_type:, instance_id: :default, alias_name: nil,
|
|
219
|
+
capabilities: nil, metadata: {})
|
|
220
|
+
Legion::Extensions::Llm::Routing::ModelOffering.new(
|
|
221
|
+
provider_family: :bedrock,
|
|
222
|
+
instance_id: instance_id,
|
|
223
|
+
transport: :aws_sdk,
|
|
224
|
+
tier: :frontier,
|
|
225
|
+
model: model,
|
|
226
|
+
usage_type: usage_type,
|
|
227
|
+
capabilities: capabilities || default_capabilities(model),
|
|
228
|
+
metadata: metadata.merge(model_family: model_family, alias: alias_name).compact
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def converse_request(messages, model:, temperature:, max_tokens:, tools:, tool_prefs:)
|
|
233
|
+
{
|
|
234
|
+
model_id: model_id(model),
|
|
235
|
+
messages: format_messages(messages.reject { |message| message.role == :system }),
|
|
236
|
+
system: format_system(messages),
|
|
237
|
+
inference_config: { temperature: temperature, max_tokens: max_tokens || model_max_tokens(model) }.compact,
|
|
238
|
+
tool_config: format_tool_config(tools, tool_prefs)
|
|
239
|
+
}.compact
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def format_messages(messages)
|
|
243
|
+
messages.map do |message|
|
|
244
|
+
{
|
|
245
|
+
role: bedrock_role(message.role),
|
|
246
|
+
content: content_blocks(message.content)
|
|
247
|
+
}
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def format_system(messages)
|
|
252
|
+
system_messages = messages.select { |message| message.role == :system }
|
|
253
|
+
system_text = system_messages.map { |message| content_text(message.content) }
|
|
254
|
+
system_blocks(system_text.join("\n"))
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def system_blocks(system)
|
|
258
|
+
return nil if system.to_s.empty?
|
|
259
|
+
|
|
260
|
+
[{ text: system }]
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def bedrock_role(role)
|
|
264
|
+
role == :assistant ? 'assistant' : 'user'
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def content_blocks(content)
|
|
268
|
+
raw_content(content) || [{ text: content_text(content) }]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def raw_content(content)
|
|
272
|
+
return nil unless content.is_a?(Legion::Extensions::Llm::Content::Raw)
|
|
273
|
+
|
|
274
|
+
Array(content.format)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def content_text(content)
|
|
278
|
+
return content.text.to_s if content.respond_to?(:text)
|
|
279
|
+
|
|
280
|
+
content.to_s
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def format_tool_config(tools, tool_prefs)
|
|
284
|
+
return nil if tools.empty?
|
|
285
|
+
|
|
286
|
+
{ tools: tools.values.map { |tool| tool_definition(tool) }, tool_choice: tool_choice(tool_prefs) }.compact
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def tool_definition(tool)
|
|
290
|
+
{
|
|
291
|
+
tool_spec: {
|
|
292
|
+
name: tool.name,
|
|
293
|
+
description: tool.description,
|
|
294
|
+
input_schema: { json: tool_schema(tool) }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def tool_schema(tool)
|
|
300
|
+
return tool.params_schema if tool.respond_to?(:params_schema) && tool.params_schema
|
|
301
|
+
|
|
302
|
+
{ type: 'object', properties: {} }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def tool_choice(tool_prefs)
|
|
306
|
+
return nil unless tool_prefs
|
|
307
|
+
|
|
308
|
+
choice = tool_prefs[:choice] || tool_prefs['choice']
|
|
309
|
+
case choice
|
|
310
|
+
when :auto, 'auto'
|
|
311
|
+
{ auto: {} }
|
|
312
|
+
when :required, 'required'
|
|
313
|
+
{ any: {} }
|
|
314
|
+
else
|
|
315
|
+
{ tool: { name: choice.to_s } }
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def parse_converse_response(response, fallback_model)
|
|
320
|
+
output = value(response, :output)
|
|
321
|
+
message = value(output, :message)
|
|
322
|
+
usage = value(response, :usage) || {}
|
|
323
|
+
|
|
324
|
+
Legion::Extensions::Llm::Message.new(
|
|
325
|
+
role: :assistant,
|
|
326
|
+
content: text_from(value(message, :content)),
|
|
327
|
+
model_id: fallback_model,
|
|
328
|
+
tool_calls: parse_tool_calls(value(message, :content)),
|
|
329
|
+
input_tokens: value(usage, :input_tokens),
|
|
330
|
+
output_tokens: value(usage, :output_tokens),
|
|
331
|
+
raw: normalize_response(response)
|
|
332
|
+
)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def stream_converse(request, fallback_model)
|
|
336
|
+
accumulated = +''
|
|
337
|
+
final_usage = nil
|
|
338
|
+
|
|
339
|
+
runtime_client.converse_stream(**request) do |stream|
|
|
340
|
+
stream.on_content_block_delta_event do |event|
|
|
341
|
+
text = value(value(event, :delta), :text)
|
|
342
|
+
next if text.nil?
|
|
343
|
+
|
|
344
|
+
accumulated << text
|
|
345
|
+
if block_given?
|
|
346
|
+
yield Legion::Extensions::Llm::Chunk.new(role: :assistant, content: text,
|
|
347
|
+
model_id: fallback_model)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
stream.on_metadata_event { |event| final_usage = value(event, :usage) }
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
Legion::Extensions::Llm::Message.new(
|
|
354
|
+
role: :assistant,
|
|
355
|
+
content: accumulated,
|
|
356
|
+
model_id: fallback_model,
|
|
357
|
+
input_tokens: value(final_usage, :input_tokens),
|
|
358
|
+
output_tokens: value(final_usage, :output_tokens)
|
|
359
|
+
)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def parse_embedding_response(response, model:)
|
|
363
|
+
body = parse_body(value(response, :body))
|
|
364
|
+
vectors = body['embedding'] || body['embeddings'] || body.dig('data', 0, 'embedding')
|
|
365
|
+
Legion::Extensions::Llm::Embedding.new(vectors: vectors, model: model,
|
|
366
|
+
input_tokens: body['inputTextTokenCount'])
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def text_from(content)
|
|
370
|
+
Array(content).filter_map { |block| value(block, :text) }.join
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def parse_tool_calls(content)
|
|
374
|
+
calls = Array(content).filter_map { |block| value(block, :tool_use) }
|
|
375
|
+
return nil if calls.empty?
|
|
376
|
+
|
|
377
|
+
calls.to_h do |call|
|
|
378
|
+
name = value(call, :name)
|
|
379
|
+
[
|
|
380
|
+
value(call, :tool_use_id) || name,
|
|
381
|
+
Legion::Extensions::Llm::ToolCall.new(id: value(call, :tool_use_id) || name, name: name,
|
|
382
|
+
arguments: value(call, :input) || {})
|
|
383
|
+
]
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def bedrock_client
|
|
388
|
+
Aws::Bedrock::Client.new(client_options)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def runtime_client
|
|
392
|
+
Aws::BedrockRuntime::Client.new(client_options)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def client_options
|
|
396
|
+
{
|
|
397
|
+
region: region,
|
|
398
|
+
endpoint: config.bedrock_endpoint,
|
|
399
|
+
credentials: credentials,
|
|
400
|
+
stub_responses: config.bedrock_stub_responses
|
|
401
|
+
}.compact
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def credentials
|
|
405
|
+
return Aws::SharedCredentials.new(profile_name: config.bedrock_profile) if config.bedrock_profile
|
|
406
|
+
return nil unless config.bedrock_access_key_id
|
|
407
|
+
|
|
408
|
+
Aws::Credentials.new(config.bedrock_access_key_id, config.bedrock_secret_access_key,
|
|
409
|
+
config.bedrock_session_token)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def credential_source
|
|
413
|
+
return :static if config.bedrock_access_key_id
|
|
414
|
+
return :profile if config.bedrock_profile
|
|
415
|
+
|
|
416
|
+
:aws_sdk_default_chain
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def model_id(model)
|
|
420
|
+
id = model.respond_to?(:id) ? model.id : model
|
|
421
|
+
self.class.resolve_model_id(id)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def model_max_tokens(model)
|
|
425
|
+
model.respond_to?(:max_tokens) ? model.max_tokens : nil
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def usage_type_for(model)
|
|
429
|
+
titan_embed?(model) ? :embedding : :inference
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def usage_type_from_modalities(output_modalities)
|
|
433
|
+
Array(output_modalities).map(&:to_s).include?('EMBEDDING') ? :embedding : :inference
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def default_capabilities(model)
|
|
437
|
+
return %i[embedding] if titan_embed?(model)
|
|
438
|
+
|
|
439
|
+
capabilities = %i[chat streaming]
|
|
440
|
+
capabilities << :vision if Capabilities.vision?(model)
|
|
441
|
+
capabilities << :functions if Capabilities.functions?(model)
|
|
442
|
+
capabilities
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def capabilities_from_summary(summary)
|
|
446
|
+
capabilities = []
|
|
447
|
+
capabilities << :embedding if usage_type_from_modalities(value(summary, :output_modalities)) == :embedding
|
|
448
|
+
capabilities << :chat if capabilities.empty?
|
|
449
|
+
capabilities << :streaming if value(summary, :response_streaming_supported)
|
|
450
|
+
capabilities << :vision if Array(value(summary, :input_modalities)).map(&:to_s).include?('IMAGE')
|
|
451
|
+
capabilities
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def model_family_for(model)
|
|
455
|
+
normalize_provider(model.to_s.split('.').first)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def normalize_provider(provider)
|
|
459
|
+
value = provider.to_s.downcase.tr(' ', '_').tr('-', '_')
|
|
460
|
+
return nil if value.empty?
|
|
461
|
+
|
|
462
|
+
case value
|
|
463
|
+
when 'mistral_ai'
|
|
464
|
+
:mistral
|
|
465
|
+
else
|
|
466
|
+
value.to_sym
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def titan_embed?(model)
|
|
471
|
+
model.to_s.include?('titan-embed')
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def alias_for(model)
|
|
475
|
+
ALIASES.key(model)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def parse_body(body)
|
|
479
|
+
body = body.read if body.respond_to?(:read)
|
|
480
|
+
body = body.string if body.respond_to?(:string)
|
|
481
|
+
body.is_a?(String) ? Legion::JSON.parse(body, symbolize_names: false) : body.to_h
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def normalize_response(response)
|
|
485
|
+
response.respond_to?(:to_h) ? response.to_h : {}
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def value(object, key)
|
|
489
|
+
return nil if object.nil?
|
|
490
|
+
|
|
491
|
+
string_key = key.to_s
|
|
492
|
+
return object[key] if object.respond_to?(:key?) && object.key?(key)
|
|
493
|
+
return object[string_key] if object.respond_to?(:key?) && object.key?(string_key)
|
|
494
|
+
return object.public_send(key) if object.respond_to?(key)
|
|
495
|
+
|
|
496
|
+
if object.respond_to?(:to_h)
|
|
497
|
+
hash = object.to_h
|
|
498
|
+
return hash[key] if hash.key?(key)
|
|
499
|
+
return hash[string_key] if hash.key?(string_key)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
nil
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/llm'
|
|
4
|
+
require 'legion/extensions/llm/bedrock/provider'
|
|
5
|
+
require 'legion/extensions/llm/bedrock/version'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module Llm
|
|
10
|
+
# Amazon Bedrock provider extension namespace.
|
|
11
|
+
module Bedrock
|
|
12
|
+
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
13
|
+
|
|
14
|
+
PROVIDER_FAMILY = :bedrock
|
|
15
|
+
|
|
16
|
+
def self.default_settings
|
|
17
|
+
::Legion::Extensions::Llm.provider_settings(
|
|
18
|
+
family: PROVIDER_FAMILY,
|
|
19
|
+
discovery: { enabled: false, live: false, regions: %w[us-east-1 us-west-2] },
|
|
20
|
+
instance: {
|
|
21
|
+
endpoint: 'https://bedrock-runtime.us-east-1.amazonaws.com',
|
|
22
|
+
region: 'us-east-1',
|
|
23
|
+
tier: :frontier,
|
|
24
|
+
transport: :aws_sdk,
|
|
25
|
+
credentials: {
|
|
26
|
+
provider: 'aws-sdk-default-chain',
|
|
27
|
+
access_key_id: 'env://AWS_ACCESS_KEY_ID',
|
|
28
|
+
secret_access_key: 'env://AWS_SECRET_ACCESS_KEY',
|
|
29
|
+
session_token: 'env://AWS_SESSION_TOKEN',
|
|
30
|
+
profile: 'env://AWS_PROFILE'
|
|
31
|
+
},
|
|
32
|
+
usage: { inference: true, embedding: true, token_counting: true },
|
|
33
|
+
limits: { concurrency: 4 }
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.provider_class
|
|
39
|
+
Provider
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::Bedrock::PROVIDER_FAMILY,
|
|
47
|
+
Legion::Extensions::Llm::Bedrock::Provider)
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-llm-bedrock
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- LegionIO
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: aws-sdk-bedrock
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: aws-sdk-bedrockruntime
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: legion-json
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.2.1
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.2.1
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: legion-logging
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.3.2
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 1.3.2
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: legion-settings
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.3.14
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.3.14
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: lex-llm
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 0.1.3
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 0.1.3
|
|
96
|
+
description: Amazon Bedrock provider integration for the LegionIO LLM routing framework.
|
|
97
|
+
email:
|
|
98
|
+
- matthewdiverson@gmail.com
|
|
99
|
+
executables: []
|
|
100
|
+
extensions: []
|
|
101
|
+
extra_rdoc_files: []
|
|
102
|
+
files:
|
|
103
|
+
- ".github/CODEOWNERS"
|
|
104
|
+
- ".github/dependabot.yml"
|
|
105
|
+
- ".github/workflows/ci.yml"
|
|
106
|
+
- ".gitignore"
|
|
107
|
+
- ".rubocop.yml"
|
|
108
|
+
- CHANGELOG.md
|
|
109
|
+
- Gemfile
|
|
110
|
+
- LICENSE
|
|
111
|
+
- README.md
|
|
112
|
+
- lex-llm-bedrock.gemspec
|
|
113
|
+
- lib/legion/extensions/llm/bedrock.rb
|
|
114
|
+
- lib/legion/extensions/llm/bedrock/provider.rb
|
|
115
|
+
- lib/legion/extensions/llm/bedrock/version.rb
|
|
116
|
+
homepage: https://github.com/LegionIO/lex-llm-bedrock
|
|
117
|
+
licenses:
|
|
118
|
+
- MIT
|
|
119
|
+
metadata:
|
|
120
|
+
homepage_uri: https://github.com/LegionIO/lex-llm-bedrock
|
|
121
|
+
source_code_uri: https://github.com/LegionIO/lex-llm-bedrock
|
|
122
|
+
documentation_uri: https://github.com/LegionIO/lex-llm-bedrock
|
|
123
|
+
changelog_uri: https://github.com/LegionIO/lex-llm-bedrock/blob/main/CHANGELOG.md
|
|
124
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-llm-bedrock/issues
|
|
125
|
+
rubygems_mfa_required: 'true'
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '3.4'
|
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
requirements: []
|
|
140
|
+
rubygems_version: 3.6.9
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: LegionIO LLM Amazon Bedrock provider extension
|
|
143
|
+
test_files: []
|