-module(anthropic@http). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/anthropic/http.gleam"). -export([method_to_string/1, check_status/1, parse_response_body/1, parse_messages_response/1, validate_request/1, build_messages_request/3, build_streaming_request/3]). -export_type([method/0, http_request/0, http_response/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 types and request/response builders for sans-io pattern\n" "\n" " This module provides HTTP-library-agnostic types for building requests\n" " and parsing responses. Users can use any HTTP client by:\n" "\n" " 1. Building a request with `build_messages_request`\n" " 2. Sending it with their preferred HTTP client\n" " 3. Parsing the response with `parse_messages_response`\n" "\n" " ## Example with custom HTTP client\n" "\n" " ```gleam\n" " import anthropic/http\n" " import anthropic/types/request\n" " import anthropic/types/message.{user_message}\n" "\n" " // Build the request\n" " let api_request = request.new(\n" " \"claude-sonnet-4-20250514\",\n" " [user_message(\"Hello!\")],\n" " 1024,\n" " )\n" " let http_request = http.build_messages_request(config, api_request)\n" "\n" " // Send with your HTTP client (e.g., hackney, httpc, fetch on JS)\n" " let http_response = my_http_client.send(http_request)\n" "\n" " // Parse the response\n" " case http.parse_messages_response(http_response) {\n" " Ok(response) -> io.println(request.response_text(response))\n" " Error(err) -> io.println(error.error_to_string(err))\n" " }\n" " ```\n" ). -type method() :: get | post | put | delete | patch. -type http_request() :: {http_request, method(), binary(), list({binary(), binary()}), binary()}. -type http_response() :: {http_response, integer(), list({binary(), binary()}), binary()}. -file("src/anthropic/http.gleam", 170). ?DOC(" Convert Method to string for HTTP libraries that need it\n"). -spec method_to_string(method()) -> binary(). method_to_string(Method) -> case Method of get -> <<"GET"/utf8>>; post -> <<"POST"/utf8>>; put -> <<"PUT"/utf8>>; delete -> <<"DELETE"/utf8>>; patch -> <<"PATCH"/utf8>> end. -file("src/anthropic/http.gleam", 210). ?DOC(" Check HTTP status and extract body or error\n"). -spec check_status(http_response()) -> {ok, binary()} | {error, anthropic@error:anthropic_error()}. check_status(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/http.gleam", 250). ?DOC(" Parse successful response body into CreateMessageResponse\n"). -spec parse_response_body(binary()) -> {ok, anthropic@request:create_message_response()} | {error, anthropic@error:anthropic_error()}. parse_response_body(Body) -> anthropic@internal@decoder:parse_response_body(Body). -file("src/anthropic/http.gleam", 200). ?DOC( " Parse an HTTP response into a CreateMessageResponse\n" "\n" " This function handles:\n" " - Status code checking (success vs error)\n" " - Error response parsing with proper error types\n" " - Success response JSON decoding\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let http_response = HttpResponse(status: 200, headers: [], body: json_body)\n" " case parse_messages_response(http_response) {\n" " Ok(response) -> handle_success(response)\n" " Error(err) -> handle_error(err)\n" " }\n" " ```\n" ). -spec parse_messages_response(http_response()) -> {ok, anthropic@request:create_message_response()} | {error, anthropic@error:anthropic_error()}. parse_messages_response(Response) -> case check_status(Response) of {ok, Body} -> parse_response_body(Body); {error, Err} -> {error, Err} end. -file("src/anthropic/http.gleam", 264). ?DOC( " Validate a CreateMessageRequest before sending\n" "\n" " This performs client-side validation to catch errors early.\n" " Delegates to the shared validation module for consistent validation rules.\n" ). -spec validate_request(anthropic@request:create_message_request()) -> {ok, nil} | {error, anthropic@error:anthropic_error()}. validate_request(Req) -> anthropic@internal@validation:validate_request_or_error(Req). -file("src/anthropic/http.gleam", 156). ?DOC(" Build standard Anthropic API headers\n"). -spec build_headers(binary(), boolean()) -> list({binary(), binary()}). build_headers(Api_key, Streaming) -> Base_headers = [{<<"content-type"/utf8>>, <<"application/json"/utf8>>}, {<<"x-api-key"/utf8>>, Api_key}, {<<"anthropic-version"/utf8>>, <<"2023-06-01"/utf8>>}], case Streaming of true -> lists:append( Base_headers, [{<<"accept"/utf8>>, <<"text/event-stream"/utf8>>}] ); false -> Base_headers end. -file("src/anthropic/http.gleam", 118). ?DOC( " Build an HTTP request for the Messages API\n" "\n" " This function creates an HTTP-library-agnostic request that can be\n" " sent using any HTTP client. It handles:\n" " - URL construction\n" " - Authentication headers\n" " - Content-Type and API version headers\n" " - JSON body encoding\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let req = request.new(\"claude-sonnet-4-20250514\", messages, 1024)\n" " let http_req = build_messages_request(\"sk-ant-...\", default_base_url, req)\n" " // Send http_req with your HTTP client\n" " ```\n" ). -spec build_messages_request( binary(), binary(), anthropic@request:create_message_request() ) -> http_request(). build_messages_request(Api_key, Base_url, Message_request) -> Url = <>, Body = anthropic@request:request_to_json_string(Message_request), {http_request, post, Url, build_headers(Api_key, false), Body}. -file("src/anthropic/http.gleam", 138). ?DOC( " Build an HTTP request for streaming Messages API\n" "\n" " Similar to `build_messages_request` but adds the Accept header for SSE\n" " and ensures the stream flag is set on the request.\n" ). -spec build_streaming_request( binary(), binary(), anthropic@request:create_message_request() ) -> http_request(). build_streaming_request(Api_key, Base_url, Message_request) -> Streaming_request = anthropic@request:with_stream(Message_request, true), Url = <>, Body = anthropic@request:request_to_json_string(Streaming_request), {http_request, post, Url, build_headers(Api_key, true), Body}.