Skip to content
9 changes: 9 additions & 0 deletions lib/ruby_llm/aliases.json
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,15 @@
"openai": "gpt-5.5-pro",
"openrouter": "openai/gpt-5.5-pro"
},
"gpt-5.6-luna": {
"bedrock": "openai.gpt-5.6-luna"
},
"gpt-5.6-sol": {
"bedrock": "openai.gpt-5.6-sol"
},
"gpt-5.6-terra": {
"bedrock": "openai.gpt-5.6-terra"
},
"gpt-audio": {
"openai": "gpt-audio",
"openrouter": "openai/gpt-audio"
Expand Down
4 changes: 2 additions & 2 deletions lib/ruby_llm/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ def self.basic(&)
end
end

def initialize(provider, config)
def initialize(provider, config, base_url: provider.api_base)
@provider = provider
@config = config

@connection = Faraday.new(provider.api_base) do |faraday|
@connection = Faraday.new(base_url) do |faraday|
setup_timeout(faraday)
setup_logging(faraday)
setup_retry(faraday)
Expand Down
198 changes: 198 additions & 0 deletions lib/ruby_llm/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -16784,6 +16784,204 @@
"knowledge": "2025-12-01"
}
},
{
"id": "openai.gpt-5.6-luna",
"name": "GPT-5.6 Luna",
"provider": "bedrock",
"family": "gpt",
"created_at": "2026-07-13 00:00:00 UTC",
"context_window": 272000,
"max_output_tokens": 128000,
"knowledge_cutoff": null,
"modalities": {
"input": [
"text",
"image",
"pdf"
],
"output": [
"text"
]
},
"capabilities": [
"function_calling",
"structured_output",
"reasoning",
"vision"
],
"pricing": {
"text_tokens": {
"standard": {
"input_per_million": 1.0,
"output_per_million": 6.0,
"cache_read_input_per_million": 0.1
}
}
},
"metadata": {
"source": "manual",
"provider_id": "amazon-bedrock",
"open_weights": false,
"attachment": true,
"temperature": false,
"last_updated": "2026-07-13",
"reasoning_options": [
{
"type": "effort",
"values": [
"none",
"low",
"medium",
"high",
"xhigh",
"max"
]
}
],
"cost": {
"input": 1.0,
"output": 6.0,
"cache_read": 0.1
},
"limit": {
"context": 272000,
"output": 128000
},
"knowledge": null
}
},
{
"id": "openai.gpt-5.6-sol",
"name": "GPT-5.6 Sol",
"provider": "bedrock",
"family": "gpt",
"created_at": "2026-07-13 00:00:00 UTC",
"context_window": 272000,
"max_output_tokens": 128000,
"knowledge_cutoff": null,
"modalities": {
"input": [
"text",
"image",
"pdf"
],
"output": [
"text"
]
},
"capabilities": [
"function_calling",
"structured_output",
"reasoning",
"vision"
],
"pricing": {
"text_tokens": {
"standard": {
"input_per_million": 5.0,
"output_per_million": 30.0,
"cache_read_input_per_million": 0.5
}
}
},
"metadata": {
"source": "manual",
"provider_id": "amazon-bedrock",
"open_weights": false,
"attachment": true,
"temperature": false,
"last_updated": "2026-07-13",
"reasoning_options": [
{
"type": "effort",
"values": [
"none",
"low",
"medium",
"high",
"xhigh",
"max"
]
}
],
"cost": {
"input": 5.0,
"output": 30.0,
"cache_read": 0.5
},
"limit": {
"context": 272000,
"output": 128000
},
"knowledge": null
}
},
{
"id": "openai.gpt-5.6-terra",
"name": "GPT-5.6 Terra",
"provider": "bedrock",
"family": "gpt",
"created_at": "2026-07-13 00:00:00 UTC",
"context_window": 272000,
"max_output_tokens": 128000,
"knowledge_cutoff": null,
"modalities": {
"input": [
"text",
"image",
"pdf"
],
"output": [
"text"
]
},
"capabilities": [
"function_calling",
"structured_output",
"reasoning",
"vision"
],
"pricing": {
"text_tokens": {
"standard": {
"input_per_million": 2.5,
"output_per_million": 15.0,
"cache_read_input_per_million": 0.25
}
}
},
"metadata": {
"source": "manual",
"provider_id": "amazon-bedrock",
"open_weights": false,
"attachment": true,
"temperature": false,
"last_updated": "2026-07-13",
"reasoning_options": [
{
"type": "effort",
"values": [
"none",
"low",
"medium",
"high",
"xhigh",
"max"
]
}
],
"cost": {
"input": 2.5,
"output": 15.0,
"cache_read": 0.25
},
"limit": {
"context": 272000,
"output": 128000
},
"knowledge": null
}
},
{
"id": "openai.gpt-oss-120b",
"name": "gpt-oss-120b",
Expand Down
55 changes: 55 additions & 0 deletions lib/ruby_llm/protocols/mantle_responses.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module RubyLLM
module Protocols
# AWS Bedrock's bedrock-mantle endpoint, speaking the OpenAI Responses API.
# Reachable only via bedrock-mantle (not bedrock-runtime), and only for models
# that physically cannot serve Converse or InvokeModel (the GPT-5.x frontier
# family). Talks to the provider's mantle connection instead of the default
# bedrock-runtime connection, and SigV4-signs every request against the
# "bedrock-mantle" service namespace instead of "bedrock".
class MantleResponses < Responses
# Frontier openai.gpt-5.x models are served at /openai/v1/responses; every other
# mantle model (e.g. openai.gpt-oss-*) is served at /v1/responses. See the
# AWS Bedrock model cards for GPT-5.6.
#
# Kept in sync with Providers::Bedrock::MANTLE_ONLY_MODEL_PATTERN — that pattern picks
# mantle vs Converse/InvokeModel, this one picks the /openai/v1 vs /v1 mantle path. They
# coincide today but are distinct concepts; update both when a new frontier family lands.
FRONTIER_GPT5_PATTERN = /\Aopenai\.gpt-5/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: FRONTIER_GPT5_PATTERN here duplicates Bedrock::MANTLE_ONLY_MODEL_PATTERN (same regex, same intent). Since MantleResponses only ever handles models protocol_for already routed via mantle_only_model?, consider referencing Providers::Bedrock::MANTLE_ONLY_MODEL_PATTERN here instead of maintaining two copies that could silently diverge.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Do it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Queued — will dedupe by reusing Bedrock::MANTLE_ONLY_MODEL_PATTERN instead of the separate FRONTIER_GPT5_PATTERN constant.


🔍 View trace


def initialize(provider, model = nil)
super
@connection = provider.mantle_connection
end

# The '/v1/responses' branch is forward-looking: today Providers::Bedrock#protocol_for only
# ever routes /\Aopenai\.gpt-5/ ids here, so this branch is unreachable in production (e.g.
# openai.gpt-oss-* still routes to Converse). It exists so this protocol is ready if a
# future mantle-only, non-frontier model needs it, without another round of plumbing.
def completion_url
FRONTIER_GPT5_PATTERN.match?(@model.id) ? '/openai/v1/responses' : '/v1/responses'
end

private

def sync_response(payload, additional_headers = {})
super(payload, additional_headers.merge(mantle_signature_headers(payload)))
end

def stream_response(payload, additional_headers = {}, &)
super(payload, additional_headers.merge(mantle_signature_headers(payload)), &)
end

def mantle_signature_headers(payload)
body = JSON.generate(payload)
@provider.sign_headers(
'POST', completion_url, body,
base_url: @provider.mantle_api_base,
service: Providers::Bedrock::MANTLE_SIGNING_SERVICE,
region: @provider.mantle_region
)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/ruby_llm/protocols/responses/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def parse_output_citations(output, content)
end

def reasoning_model?(model_id)
model_id.match?(/^o\d|^gpt-5/)
model_id.match?(/\A(?:openai\.)?(?:o\d|gpt-5)/)
end

def parse_usage(usage)
Expand Down
Loading