Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions lib/llm_gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
require_relative "llm_gateway/clients/claude"
require_relative "llm_gateway/clients/claude_code"
require_relative "llm_gateway/clients/open_ai"
require_relative "llm_gateway/clients/openai_codex"
require_relative "llm_gateway/clients/openai_codex/oauth_flow"
require_relative "llm_gateway/clients/openai_codex/token_manager"
require_relative "llm_gateway/clients/groq"

# Load adapters
Expand All @@ -22,9 +23,8 @@

require_relative "llm_gateway/adapters/claude/input_mapper"
require_relative "llm_gateway/adapters/claude/output_mapper"
require_relative "llm_gateway/adapters/claude_code/input_mapper"
require_relative "llm_gateway/adapters/claude_code/output_mapper"
require_relative "llm_gateway/adapters/open_ai/file_output_mapper"
require_relative "llm_gateway/adapters/open_ai/prompt_cache_option_mapper"
require_relative "llm_gateway/adapters/open_ai/chat_completions/input_mapper"
require_relative "llm_gateway/adapters/open_ai/chat_completions/output_mapper"
require_relative "llm_gateway/adapters/open_ai/chat_completions/option_mapper"
Expand All @@ -38,7 +38,6 @@
# Load adapter classes
require_relative "llm_gateway/adapters/adapter"
require_relative "llm_gateway/adapters/claude/messages_adapter"
require_relative "llm_gateway/adapters/claude_code/messages_adapter"
require_relative "llm_gateway/adapters/open_ai/chat_completions_adapter"
require_relative "llm_gateway/adapters/open_ai/responses_adapter"
require_relative "llm_gateway/adapters/openai_codex/responses_adapter"
Expand Down Expand Up @@ -69,7 +68,7 @@ module OpenAi
end

module OpenAiCodex
Client = LlmGateway::Clients::OpenAiCodex
Client = LlmGateway::Clients::OpenAi
end

module Groq
Expand Down Expand Up @@ -118,10 +117,6 @@ def self.reset_configuration!
client: Clients::Claude,
adapter: Adapters::Claude::MessagesAdapter)

ProviderRegistry.register("anthropic_oauth_messages",
client: Clients::ClaudeCode,
adapter: Adapters::ClaudeCode::MessagesAdapter)

ProviderRegistry.register("openai_apikey_completions",
client: Clients::OpenAi,
adapter: Adapters::OpenAi::ChatCompletionsAdapter)
Expand All @@ -135,6 +130,6 @@ def self.reset_configuration!
adapter: Adapters::Groq::ChatCompletionsAdapter)

ProviderRegistry.register("openai_oauth_codex",
client: Clients::OpenAiCodex,
client: Clients::OpenAi,
adapter: Adapters::OpenAiCodex::ResponsesAdapter)
end
3 changes: 1 addition & 2 deletions lib/llm_gateway/adapters/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ def stream_client_method

def stream_api_name
case self
when LlmGateway::Adapters::Claude::MessagesAdapter,
LlmGateway::Adapters::ClaudeCode::MessagesAdapter
when LlmGateway::Adapters::Claude::MessagesAdapter
"messages"
when LlmGateway::Adapters::OpenAi::ChatCompletionsAdapter,
LlmGateway::Adapters::Groq::ChatCompletionsAdapter
Expand Down
22 changes: 20 additions & 2 deletions lib/llm_gateway/adapters/anthropic_option_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,33 @@ module AnthropicOptionMapper
module_function

def map(options)
mapped_options = options.reject { |key, _| %i[reasoning max_completion_tokens].include?(key) }
mapped_options[:max_tokens] = options[:max_completion_tokens] || 20480
mapped_options = options.reject { |key, _| %i[reasoning max_completion_tokens response_format prompt_cache_retention cache_key prompt_cache_key].include?(key) }
mapped_options[:max_tokens] = options[:max_completion_tokens] || DEFAULT_MAX_TOKENS

retention = options[:cache_retention]
mapped_options[:cache_retention] = retention unless retention.nil?

response_format = options[:response_format]
mapped_options[:output_config] = normalize_output_config(response_format) unless response_format.nil?

reasoning = options[:reasoning]
return mapped_options if reasoning.nil? || reasoning.to_s == "none"

mapped_options[:thinking] = normalize_reasoning(reasoning)
mapped_options
end

def normalize_output_config(response_format)
format_type = response_format.is_a?(Hash) ? response_format[:type] || response_format["type"] : response_format

case format_type.to_s
when "json_object", "json_schema"
{ format: "json_schema" }
else
{ format: "text" }
end
end

def normalize_reasoning(reasoning)
budget_tokens = REASONING_EFFORT_BUDGET_TOKENS[reasoning.to_s] ||
raise(ArgumentError,
Expand Down
4 changes: 4 additions & 0 deletions lib/llm_gateway/adapters/claude/stream_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ def map(chunk)
message = error[:message] || "Stream error"
code = error[:type]

if LlmGateway::Errors.context_overflow_message?(message)
raise LlmGateway::Errors::PromptTooLong.new(message, code)
end

if code == "overloaded_error"
raise LlmGateway::Errors::OverloadError.new(message, code)
end
Expand Down
14 changes: 0 additions & 14 deletions lib/llm_gateway/adapters/claude_code/input_mapper.rb

This file was deleted.

28 changes: 0 additions & 28 deletions lib/llm_gateway/adapters/claude_code/messages_adapter.rb

This file was deleted.

22 changes: 0 additions & 22 deletions lib/llm_gateway/adapters/claude_code/option_mapper.rb

This file was deleted.

12 changes: 0 additions & 12 deletions lib/llm_gateway/adapters/claude_code/output_mapper.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Adapters
module OpenAi
module ChatCompletions
module OptionMapper
include LlmGateway::Adapters::OpenAi::PromptCacheOptionMapper

VALID_REASONING_LEVELS = %w[low medium high xhigh].freeze

module_function
Expand All @@ -13,6 +15,9 @@ def map(options)
mapped_options = options.dup
mapped_options[:max_completion_tokens] ||= 20_480

map_cache_key!(mapped_options)
map_prompt_cache_retention!(mapped_options)

return mapped_options unless mapped_options.key?(:reasoning)

reasoning = mapped_options.delete(:reasoning)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ def raise_stream_error!(data)
error = data[:error].is_a?(Hash) ? data[:error] : data
message = error[:message] || "Stream error"
code = error[:code] || error[:type]

if LlmGateway::Errors.context_overflow_message?(message)
raise LlmGateway::Errors::PromptTooLong.new(message, code)
end

raise LlmGateway::Errors::APIStatusError.new(message, code)
end
end
Expand Down
39 changes: 39 additions & 0 deletions lib/llm_gateway/adapters/open_ai/prompt_cache_option_mapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module LlmGateway
module Adapters
module OpenAi
module PromptCacheOptionMapper
def self.included(base)
base.extend(self)
end

def map_cache_key!(mapped_options)
cache_key = mapped_options.delete(:cache_key)
mapped_options.delete(:prompt_cache_key)
mapped_options[:prompt_cache_key] = cache_key unless cache_key.nil?
end

def map_prompt_cache_retention!(mapped_options)
retention = mapped_options.delete(:cache_retention)
mapped_options.delete(:prompt_cache_retention)
retention ||= "short" if mapped_options.key?(:prompt_cache_key)

case retention&.to_s
when nil
nil
when "short"
mapped_options[:prompt_cache_retention] = "in_memory"
when "long"
mapped_options[:prompt_cache_retention] = "24h"
when "none"
mapped_options.delete(:prompt_cache_key)
else
raise ArgumentError,
"Invalid cache_retention '#{retention}'. Use 'short', 'long', or 'none'."
end
end
end
end
end
end
5 changes: 5 additions & 0 deletions lib/llm_gateway/adapters/open_ai/responses/option_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Adapters
module OpenAi
module Responses
module OptionMapper
include LlmGateway::Adapters::OpenAi::PromptCacheOptionMapper

VALID_REASONING_LEVELS = %w[low medium high xhigh].freeze

module_function
Expand All @@ -15,6 +17,9 @@ def map(options)
max_completion_tokens = mapped_options.delete(:max_completion_tokens)
mapped_options[:max_output_tokens] = max_completion_tokens || mapped_options[:max_output_tokens] || 20_480

map_cache_key!(mapped_options)
map_prompt_cache_retention!(mapped_options)

return mapped_options unless mapped_options.key?(:reasoning)

reasoning = mapped_options.delete(:reasoning)
Expand Down
5 changes: 5 additions & 0 deletions lib/llm_gateway/adapters/open_ai/responses/stream_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@ def raise_stream_error!(data)
error = data[:error].is_a?(Hash) ? data[:error] : data
message = error[:message] || "Stream error"
code = error[:code] || error[:type]

if LlmGateway::Errors.context_overflow_message?(message)
raise LlmGateway::Errors::PromptTooLong.new(message, code)
end

raise LlmGateway::Errors::APIStatusError.new(message, code)
end
end
Expand Down
11 changes: 10 additions & 1 deletion lib/llm_gateway/adapters/openai_codex/option_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ module OptionMapper

def map(options)
mapped_options = OpenAi::Responses::OptionMapper.map(options)
mapped_options[:max_completion_tokens] ||= 20480

# Codex endpoint currently rejects token limit parameters.
mapped_options.delete(:max_output_tokens)
mapped_options.delete(:max_completion_tokens)

# Codex transport does not use retention flags in the request body.
mapped_options.delete(:prompt_cache_retention)
mapped_options.delete(:cacheRetention)
mapped_options.delete(:cache_retention)

mapped_options
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/llm_gateway/adapters/openai_codex/responses_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ def initialize(client)
output_mapper: OpenAi::Responses::OutputMapper,
file_output_mapper: OpenAi::FileOutputMapper,
option_mapper: OptionMapper,
client_method: :chat,
client_method: :chat_codex,
stream_mapper: OpenAi::Responses::StreamMapper
)
end

private

def stream_client_method
:stream
:stream_codex
end

def stream_api_name
Expand Down
11 changes: 1 addition & 10 deletions lib/llm_gateway/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def self.download_file(provider, **kwargs)
end

def self.provider_from_model(model)
return "anthropic" if model.start_with?("claude_code/")
return "anthropic" if model.start_with?("claude")
return "groq" if model.start_with?("llama")
return "openai" if model.start_with?("gpt") ||
Expand Down Expand Up @@ -72,15 +71,7 @@ def self.provider_id_from_client(client)
def self.build_adapter_from_model(model, api_key: nil, refresh_token: nil, expires_at: nil, api: nil)
provider = provider_from_model(model)

if model.start_with?("claude_code/")
LlmGateway.build_provider(
provider: "anthropic_oauth_messages",
model_key: model,
access_token: api_key,
refresh_token: refresh_token,
expires_at: expires_at
)
elsif api == "responses"
if api == "responses"
config = {
provider: "#{provider}_apikey_responses",
model_key: model
Expand Down
Loading
Loading