# frozen_string_literal: true require "httpx" module Vercon class Claude BASE_URL = "https://api.anthropic.com" def initialize config = Vercon::Config.new @api_token = config.claude_token @claude_model = config.claude_model end def submit(model: nil, system: nil, max_tokens: 4096, temperature: 0.2, stop_sequences: nil, user: nil, # rubocop:disable Metrics/ParameterLists messages: nil, tools: nil) body = { model: model || @claude_model, system: system, max_tokens: max_tokens, temperature: temperature, stop_sequences: stop_sequences, messages: messages || [{role: "user", content: user}], tools: tools }.reject { |_, v| v.nil? || ["", [], {}].include?(v) } client.post("/v1/messages", body: body.to_json).then { |res| prepare_response(res.json) } end private def extra_headers {"x-api-key" => @api_token, "anthropic-version" => "2023-06-01"} end def client @client ||= HTTPX .plugin(:retries) .with(headers: {"Content-Type" => "application/json", "Cache-Control" => "no-cache", "anthropic-version" => "2023-06-01", "anthropic-beta" => "tools-2024-04-04"}) .with(headers: extra_headers) .with(origin: BASE_URL) .with(ssl: {alpn_protocols: %w[http/1.1]}) .with( timeout: { connect_timeout: 10, read_timeout: 400, keep_alive_timeout: 480, request_timeout: 400, operation_timeout: 400 } ) end def prepare_response(response) return {error: response.dig("error", "message")} if response["type"] == "error" texts = [] tools = [] response["content"].each do |msg| case msg["type"] when "text" texts << msg["text"].gsub(%r{\n?</?thinking>\n?}, "") when "tool_use" tools << msg.slice("name", "id", "input").transform_keys(&:to_sym) end end {text: texts.join("\n"), tools: tools} end end end