Class: RubynCode::LLM::Adapters::Anthropic
- Includes:
- JsonParsing, PromptCaching
- Defined in:
- lib/rubyn_code/llm/adapters/anthropic.rb
Overview
rubocop:disable Metrics/ClassLength -- auth helpers + streaming + finalize are all needed
Direct Known Subclasses
Constant Summary collapse
- API_URL =
'https://api.anthropic.com/v1/messages'- ANTHROPIC_VERSION =
'2023-06-01'- MAX_RETRIES =
3- RETRY_DELAYS =
[2, 5, 10].freeze
- FALLBACK_ELIGIBLE_MODELS =
Fable/Mythos/Opus 5 models opt into a server-side Opus 4.8 fallback by default: a declined request (safety-classifier refusal) is transparently re-served by the fallback model in the same call. No config toggle — hardcoded for these model families only.
/\Aclaude-(fable|mythos|opus-5)/- FALLBACK_BETA =
'server-side-fallback-2026-06-01'- TASK_BUDGET_BETA =
'task-budgets-2026-03-13'- TASK_BUDGET_MIN_TOKENS =
API minimum; below this, task_budget is omitted entirely
20_000- TASK_BUDGET_MODELS =
/\Aclaude-(fable|mythos|sonnet-5|opus-5|opus-4-[78])/- AVAILABLE_MODELS =
%w[ claude-fable-5 claude-opus-5 claude-opus-4-8 claude-opus-4-7 claude-opus-4-6 claude-sonnet-5 claude-sonnet-4-6 claude-haiku-4-5 ].freeze
- ADAPTIVE_THINKING_MODELS =
Models on the adaptive-thinking API surface (Claude 4.6+). budget_tokens is removed there and returns a 400; older models (Haiku 4.5, Sonnet/Opus 4.5 and earlier) still take enabled + budget.
/\Aclaude-(fable|mythos|opus-5|opus-4-[678]|sonnet-5|sonnet-4-6)/
Constants included from PromptCaching
PromptCaching::CACHE_EPHEMERAL, PromptCaching::OAUTH_GATE
Instance Method Summary collapse
-
#access_token ⇒ String?
The access token in use.
-
#api_key ⇒ String?
The API key (ANTHROPIC_API_KEY), or nil.
-
#chat(messages:, model:, max_tokens:, tools: nil, system: nil, on_text: nil, task_budget: nil, thinking: nil, effort: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists -- mirrors LLM adapter interface.
-
#ensure_valid_token! ⇒ Object
Validate the active auth path before sending the request.
- #models ⇒ Object
-
#oauth_token? ⇒ Boolean
True when using the OAuth token (vs API key).
- #provider_name ⇒ Object
- #raw_access_token ⇒ Object
-
#token_valid? ⇒ Boolean
True if the token store says the active credential is still valid.
Instance Method Details
#access_token ⇒ String?
Returns the access token in use. Accepts either a raw String token or a Hash like { access_token: "...", expires_at: ..., source: :keychain }. Memoized per-instance so that repeated calls during one request don't re-hit the token store.
102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 102 def access_token return @access_token if defined?(@access_token) token = raw_access_token @access_token = case token when nil then nil when String then token.empty? ? nil : token when Hash then token[:access_token] || token['access_token'] end end |
#api_key ⇒ String?
Returns the API key (ANTHROPIC_API_KEY), or nil.
115 116 117 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 115 def api_key ENV.fetch('ANTHROPIC_API_KEY', nil) end |
#chat(messages:, model:, max_tokens:, tools: nil, system: nil, on_text: nil, task_budget: nil, thinking: nil, effort: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists -- mirrors LLM adapter interface
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 55 def chat(messages:, model:, max_tokens:, tools: nil, system: nil, on_text: nil, # rubocop:disable Metrics/ParameterLists -- mirrors LLM adapter interface task_budget: nil, thinking: nil, effort: nil) ensure_valid_token! use_streaming = on_text && oauth_token? body = build_request_body( messages: , tools: tools, system: system, model: model, max_tokens: max_tokens, stream: use_streaming, task_budget: task_budget, thinking: thinking, effort: effort ) return stream_request(body, on_text) if use_streaming execute_with_retries(body, on_text) end |
#ensure_valid_token! ⇒ Object
Validate the active auth path before sending the request. Raises Client::AuthExpiredError with a clear message instead of letting the HTTP request fail with a 401.
77 78 79 80 81 82 83 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 77 def ensure_valid_token! return if token_valid? && access_token && !access_token.empty? return if !oauth_token? && api_key && !api_key.empty? raise Client::AuthExpiredError, 'No valid authentication configured' end |
#models ⇒ Object
51 52 53 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 51 def models AVAILABLE_MODELS end |
#oauth_token? ⇒ Boolean
Returns true when using the OAuth token (vs API key). A token from the keychain (or marked source: :keychain / :oauth) is treated as OAuth; a token from :env or :tokens_file is treated as a long-lived API key.
89 90 91 92 93 94 95 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 89 def oauth_token? token = raw_access_token return false unless token.is_a?(Hash) source = token[:source] || token['source'] [:keychain, 'keychain', :oauth].include?(source) end |
#provider_name ⇒ Object
47 48 49 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 47 def provider_name 'anthropic' end |
#raw_access_token ⇒ Object
128 129 130 131 132 133 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 128 def raw_access_token return @raw_access_token if defined?(@raw_access_token) @raw_access_token = (Auth::TokenStore.load if defined?(Auth::TokenStore) && Auth::TokenStore.respond_to?(:load)) end |
#token_valid? ⇒ Boolean
Returns true if the token store says the active credential is still valid.
121 122 123 124 125 126 |
# File 'lib/rubyn_code/llm/adapters/anthropic.rb', line 121 def token_valid? return true unless defined?(Auth::TokenStore) return true unless Auth::TokenStore.respond_to?(:valid?) Auth::TokenStore.valid? end |