-module(anthropic@client). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/anthropic/client.gleam"). -export([new/1, init/0, init_with_key/1, handle_response/1, post_json/3, post_and_handle/3]). -export_type([client/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " HTTP client wrapper for Anthropic API calls\n" "\n" " This module provides a client for making requests to the Anthropic Messages API,\n" " handling headers, timeouts, and response parsing.\n" "\n" " ## Quick Start\n" "\n" " For most use cases, use the simple `init` functions:\n" "\n" " ```gleam\n" " import anthropic/client\n" " import anthropic/api\n" " import anthropic/types/request\n" " import anthropic/types/message.{user_message}\n" "\n" " // Read API key from ANTHROPIC_API_KEY environment variable\n" " let assert Ok(client) = client.init()\n" "\n" " // Or provide the key explicitly\n" " let assert Ok(client) = client.init_with_key(\"sk-ant-...\")\n" "\n" " // Create a request and chat\n" " let req = request.new(\"claude-sonnet-4-20250514\", [user_message(\"Hello!\")], 1024)\n" " api.chat(client, req)\n" " ```\n" "\n" " ## Advanced Configuration\n" "\n" " For custom base URLs, timeouts, or retry settings, use the config builders:\n" "\n" " ```gleam\n" " import anthropic/config\n" " import anthropic/client\n" "\n" " let assert Ok(cfg) = config.config_options()\n" " |> config.with_api_key(\"sk-ant-...\")\n" " |> config.with_base_url(\"https://custom-endpoint.example.com\")\n" " |> config.with_timeout_ms(120_000)\n" " |> config.load_config()\n" "\n" " let client = client.new(cfg)\n" " ```\n" ). -type client() :: {client, anthropic@config:config()}. -file("src/anthropic/client.gleam", 84). ?DOC( " Create a new client from configuration\n" "\n" " For advanced use cases where you need custom configuration.\n" " For simple use cases, prefer `init()` or `init_with_key()`.\n" ). -spec new(anthropic@config:config()) -> client(). new(Config) -> {client, Config}. -file("src/anthropic/client.gleam", 100). ?DOC( " Initialize a client using the ANTHROPIC_API_KEY environment variable\n" "\n" " This is the simplest way to create a client when you have your API key\n" " set in the environment.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Ensure ANTHROPIC_API_KEY is set in your environment\n" " let assert Ok(client) = client.init()\n" " api.create_message(client, request)\n" " ```\n" ). -spec init() -> {ok, client()} | {error, anthropic@error:anthropic_error()}. init() -> _pipe = anthropic@config:config_options(), _pipe@1 = anthropic@config:load_config(_pipe), gleam@result:map(_pipe@1, fun new/1). -file("src/anthropic/client.gleam", 117). ?DOC( " Initialize a client with an explicit API key\n" "\n" " Use this when you want to provide the API key directly rather than\n" " reading from environment variables.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let assert Ok(client) = client.init_with_key(\"sk-ant-api03-...\")\n" " api.create_message(client, request)\n" " ```\n" ). -spec init_with_key(binary()) -> {ok, client()} | {error, anthropic@error:anthropic_error()}. init_with_key(Api_key) -> _pipe = anthropic@config:config_options(), _pipe@1 = anthropic@config:with_api_key(_pipe, Api_key), _pipe@2 = anthropic@config:load_config(_pipe@1), gleam@result:map(_pipe@2, fun new/1). -file("src/anthropic/client.gleam", 177). ?DOC(" Convert ConnectError to string\n"). -spec connect_error_to_string(gleam@httpc:connect_error()) -> binary(). connect_error_to_string(Err) -> case Err of {posix, Code} -> <<"POSIX error: "/utf8, Code/binary>>; {tls_alert, Code@1, Detail} -> <<<<<<"TLS alert "/utf8, Code@1/binary>>/binary, ": "/utf8>>/binary, Detail/binary>> end. -file("src/anthropic/client.gleam", 161). ?DOC(" Convert httpc error to AnthropicError\n"). -spec http_error_to_anthropic_error(gleam@httpc:http_error()) -> anthropic@error:anthropic_error(). http_error_to_anthropic_error(Err) -> case Err of invalid_utf8_response -> anthropic@error:http_error(<<"Invalid UTF-8 in response"/utf8>>); {failed_to_connect, Ip4, Ip6} -> anthropic@error:network_error( <<<<<<<<"Failed to connect to server (IPv4: "/utf8, (connect_error_to_string(Ip4))/binary>>/binary, ", IPv6: "/utf8>>/binary, (connect_error_to_string(Ip6))/binary>>/binary, ")"/utf8>> ); response_timeout -> anthropic@error:timeout_error(0) end. -file("src/anthropic/client.gleam", 189). ?DOC(" Handle an API response, parsing success or error\n"). -spec handle_response(gleam@http@response:response(binary())) -> {ok, binary()} | {error, anthropic@error:anthropic_error()}. handle_response(Response) -> Status = erlang:element(2, Response), case Status of 200 -> {ok, erlang:element(4, Response)}; 400 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), invalid_request_error )}; 401 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), authentication_error )}; 403 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), permission_error )}; 404 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), not_found_error )}; 429 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), rate_limit_error )}; 500 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), internal_api_error )}; 529 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), overloaded_error )}; _ when (Status >= 400) andalso (Status < 500) -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), invalid_request_error )}; _ when Status >= 500 -> {error, anthropic@internal@decoder:parse_api_error( Status, erlang:element(4, Response), internal_api_error )}; _ -> {error, anthropic@error:http_error( <<"Unexpected status code: "/utf8, (gleam@string:inspect(Status))/binary>> )} end. -file("src/anthropic/client.gleam", 129). ?DOC(" Make a POST request with JSON body to the specified path\n"). -spec post_json(client(), binary(), binary()) -> {ok, gleam@http@response:response(binary())} | {error, anthropic@error:anthropic_error()}. post_json(Client, Path, Body) -> Base_url = erlang:element(3, erlang:element(2, Client)), Full_url = <>, gleam@result:'try'( begin _pipe = gleam@http@request:to(Full_url), gleam@result:map_error( _pipe, fun(_) -> anthropic@error:config_error( <<"Invalid URL: "/utf8, Full_url/binary>> ) end ) end, fun(Req) -> Req@1 = begin _pipe@1 = Req, _pipe@2 = gleam@http@request:set_method(_pipe@1, post), _pipe@3 = gleam@http@request:set_header( _pipe@2, <<"content-type"/utf8>>, <<"application/json"/utf8>> ), _pipe@4 = gleam@http@request:set_header( _pipe@3, <<"x-api-key"/utf8>>, anthropic@config:api_key_to_string( erlang:element(2, erlang:element(2, Client)) ) ), _pipe@5 = gleam@http@request:set_header( _pipe@4, <<"anthropic-version"/utf8>>, <<"2023-06-01"/utf8>> ), gleam@http@request:set_body(_pipe@5, Body) end, _pipe@6 = gleam@httpc:send(Req@1), gleam@result:map_error( _pipe@6, fun(Err) -> http_error_to_anthropic_error(Err) end ) end ). -file("src/anthropic/client.gleam", 235). ?DOC(" Make a POST request and handle the response\n"). -spec post_and_handle(client(), binary(), binary()) -> {ok, binary()} | {error, anthropic@error:anthropic_error()}. post_and_handle(Client, Path, Body) -> gleam@result:'try'( post_json(Client, Path, Body), fun(Response) -> handle_response(Response) end ).