-module(anthropic@api). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/anthropic/api.gleam"). -export([chat/2, create_message/2, event_text/1, stream_text/1, stream_complete/1, stream_has_error/1, stream_message_id/1, stream_model/1, chat_stream/2, chat_stream_with_callback/3]). -export_type([stream_result/0, stream_error/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( " API functions for Anthropic Messages API\n" "\n" " This module provides the core functions for interacting with Claude's\n" " Messages API, including message creation and response parsing.\n" "\n" " ## Quick Start\n" "\n" " For most use cases, use the unified chat functions:\n" "\n" " ```gleam\n" " import anthropic/api\n" " import anthropic/client\n" " import anthropic/types/request\n" " import anthropic/types/message.{user_message}\n" "\n" " // Initialize client\n" " let assert Ok(client) = client.init()\n" "\n" " // Create a request\n" " let req = request.new(\n" " \"claude-sonnet-4-20250514\",\n" " [user_message(\"Hello, Claude!\")],\n" " 1024,\n" " )\n" "\n" " // Non-streaming chat\n" " case api.chat(client, req) {\n" " Ok(response) -> io.println(request.response_text(response))\n" " Error(err) -> io.println(error.error_to_string(err))\n" " }\n" "\n" " // Streaming chat (batch mode - collects all events)\n" " case api.chat_stream(client, req) {\n" " Ok(result) -> io.println(api.stream_text(result))\n" " Error(err) -> io.println(error.error_to_string(err))\n" " }\n" " ```\n" ). -type stream_result() :: {stream_result, list(anthropic@streaming:stream_event())}. -type stream_error() :: {http_error, anthropic@error:anthropic_error()} | {sse_parse_error, binary()} | {event_decode_error, binary()} | {api_error, integer(), binary()}. -file("src/anthropic/api.gleam", 86). ?DOC(" Parse a response body into CreateMessageResponse\n"). -spec parse_response(binary()) -> {ok, anthropic@request:create_message_response()} | {error, anthropic@error:anthropic_error()}. parse_response(Body) -> anthropic@internal@decoder:parse_response_body(Body). -file("src/anthropic/api.gleam", 119). ?DOC( " Send a chat message and receive a response (non-streaming)\n" "\n" " This is the primary function for interacting with Claude. It sends a message\n" " and returns the complete response.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import anthropic/api\n" " import anthropic/client\n" " import anthropic/types/request\n" " import anthropic/types/message.{user_message}\n" "\n" " let assert Ok(client) = client.init()\n" " let req = request.new(\n" " \"claude-sonnet-4-20250514\",\n" " [user_message(\"What is the capital of France?\")],\n" " 1024,\n" " )\n" "\n" " case api.chat(client, req) {\n" " Ok(response) -> io.println(request.response_text(response))\n" " Error(err) -> handle_error(err)\n" " }\n" " ```\n" ). -spec chat( anthropic@client:client(), anthropic@request:create_message_request() ) -> {ok, anthropic@request:create_message_response()} | {error, anthropic@error:anthropic_error()}. chat(Client, Message_request) -> gleam@result:'try'( anthropic@internal@validation:validate_request_or_error(Message_request), fun(_) -> Body = anthropic@request:request_to_json_string(Message_request), gleam@result:'try'( anthropic@client:post_and_handle( Client, <<"/v1/messages"/utf8>>, Body ), fun(Response_body) -> parse_response(Response_body) end ) end ). -file("src/anthropic/api.gleam", 74). ?DOC( " Create a message using the Anthropic Messages API\n" "\n" " @deprecated Use `api.chat` instead for a more intuitive API\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Prefer using api.chat instead:\n" " case api.chat(client, request) {\n" " Ok(response) -> io.println(response_text(response))\n" " Error(err) -> io.println(error_to_string(err))\n" " }\n" " ```\n" ). -spec create_message( anthropic@client:client(), anthropic@request:create_message_request() ) -> {ok, anthropic@request:create_message_response()} | {error, anthropic@error:anthropic_error()}. create_message(Client, Message_request) -> chat(Client, Message_request). -file("src/anthropic/api.gleam", 283). ?DOC( " Extract text from a single streaming event\n" "\n" " ## Example\n" "\n" " ```gleam\n" " list.each(result.events, fn(event) {\n" " case api.event_text(event) {\n" " Ok(text) -> io.print(text)\n" " Error(_) -> Nil\n" " }\n" " })\n" " ```\n" ). -spec event_text(anthropic@streaming:stream_event()) -> {ok, binary()} | {error, nil}. event_text(Event) -> case Event of {content_block_delta_event_variant, Delta_event} -> case erlang:element(3, Delta_event) of {text_content_delta, Text_delta} -> {ok, erlang:element(2, Text_delta)}; _ -> {error, nil} end; _ -> {error, nil} end. -file("src/anthropic/api.gleam", 265). ?DOC( " Get the full text from a streaming result\n" "\n" " ## Example\n" "\n" " ```gleam\n" " case api.chat_stream(client, request) {\n" " Ok(result) -> io.println(api.stream_text(result))\n" " Error(_) -> io.println(\"Error\")\n" " }\n" " ```\n" ). -spec stream_text(stream_result()) -> binary(). stream_text(Result) -> _pipe = erlang:element(2, Result), _pipe@1 = gleam@list:filter_map(_pipe, fun event_text/1), gleam@string:join(_pipe@1, <<""/utf8>>). -file("src/anthropic/api.gleam", 296). ?DOC(" Check if a streaming result completed successfully\n"). -spec stream_complete(stream_result()) -> boolean(). stream_complete(Result) -> gleam@list:any(erlang:element(2, Result), fun(Event) -> case Event of message_stop_event -> true; _ -> false end end). -file("src/anthropic/api.gleam", 306). ?DOC(" Check if a streaming result contains an error\n"). -spec stream_has_error(stream_result()) -> boolean(). stream_has_error(Result) -> gleam@list:any(erlang:element(2, Result), fun(Event) -> case Event of {error_event, _} -> true; _ -> false end end). -file("src/anthropic/api.gleam", 316). ?DOC(" Get the message ID from a streaming result\n"). -spec stream_message_id(stream_result()) -> {ok, binary()} | {error, nil}. stream_message_id(Result) -> _pipe = erlang:element(2, Result), gleam@list:find_map(_pipe, fun(Event) -> case Event of {message_start_event, Msg} -> {ok, erlang:element(2, Msg)}; _ -> {error, nil} end end). -file("src/anthropic/api.gleam", 327). ?DOC(" Get the model from a streaming result\n"). -spec stream_model(stream_result()) -> {ok, binary()} | {error, nil}. stream_model(Result) -> _pipe = erlang:element(2, Result), gleam@list:find_map(_pipe, fun(Event) -> case Event of {message_start_event, Msg} -> {ok, erlang:element(5, Msg)}; _ -> {error, nil} end end). -file("src/anthropic/api.gleam", 342). ?DOC(" Parse SSE body text into streaming events\n"). -spec parse_sse_body(binary()) -> {ok, stream_result()} | {error, stream_error()}. parse_sse_body(Body) -> State = anthropic@internal@sse:new_parser_state(), Parse_result = anthropic@internal@sse:parse_chunk(State, Body), Events = begin _pipe = erlang:element(2, Parse_result), gleam@list:filter_map( _pipe, fun(Sse_event) -> case anthropic@streaming@decoder:decode_event(Sse_event) of {ok, Event} -> {ok, Event}; {error, _} -> {error, nil} end end ) end, Remaining_events = case anthropic@internal@sse:flush( erlang:element(3, Parse_result) ) of {ok, Sse_event@1} -> case anthropic@streaming@decoder:decode_event(Sse_event@1) of {ok, Event@1} -> [Event@1]; {error, _} -> [] end; {error, _} -> [] end, {ok, {stream_result, lists:append(Events, Remaining_events)}}. -file("src/anthropic/api.gleam", 422). ?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/api.gleam", 406). ?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/api.gleam", 370). ?DOC(" Make a streaming HTTP request using gleam_httpc\n"). -spec make_streaming_request(anthropic@client:client(), binary()) -> {ok, gleam@http@response:response(binary())} | {error, anthropic@error:anthropic_error()}. make_streaming_request(Api_client, Body) -> Base_url = erlang:element(3, erlang:element(2, Api_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, Api_client)) ) ), _pipe@5 = gleam@http@request:set_header( _pipe@4, <<"anthropic-version"/utf8>>, <<"2023-06-01"/utf8>> ), _pipe@6 = gleam@http@request:set_header( _pipe@5, <<"accept"/utf8>>, <<"text/event-stream"/utf8>> ), gleam@http@request:set_body(_pipe@6, Body) end, _pipe@7 = gleam@httpc:configure(), _pipe@8 = gleam@httpc:timeout( _pipe@7, erlang:element(5, erlang:element(2, Api_client)) ), _pipe@9 = gleam@httpc:dispatch(_pipe@8, Req@1), gleam@result:map_error( _pipe@9, fun(Err) -> http_error_to_anthropic_error(Err) end ) end ). -file("src/anthropic/api.gleam", 190). ?DOC( " Send a chat message with streaming response (batch mode)\n" "\n" " This function sends a message and returns all streaming events after\n" " the response completes. For true real-time streaming, use the sans-io\n" " functions in `anthropic/streaming/handler`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import anthropic/api\n" " import anthropic/client\n" " import anthropic/types/request\n" " import anthropic/types/message.{user_message}\n" "\n" " let assert Ok(client) = client.init()\n" " let req = request.new(\n" " \"claude-sonnet-4-20250514\",\n" " [user_message(\"Tell me a story\")],\n" " 2048,\n" " )\n" "\n" " case api.chat_stream(client, req) {\n" " Ok(result) -> io.println(api.stream_text(result))\n" " Error(err) -> handle_stream_error(err)\n" " }\n" " ```\n" ). -spec chat_stream( anthropic@client:client(), anthropic@request:create_message_request() ) -> {ok, stream_result()} | {error, stream_error()}. chat_stream(Client, Message_request) -> Streaming_request = anthropic@request:with_stream(Message_request, true), case anthropic@internal@validation:validate_request_or_error( Streaming_request ) of {error, Err} -> {error, {http_error, Err}}; {ok, _} -> Body = anthropic@request:request_to_json_string(Streaming_request), case make_streaming_request(Client, Body) of {error, Err@1} -> {error, {http_error, Err@1}}; {ok, Http_response} -> case erlang:element(2, Http_response) of 200 -> parse_sse_body(erlang:element(4, Http_response)); Status -> {error, {api_error, Status, erlang:element(4, Http_response)}} end end end. -file("src/anthropic/api.gleam", 238). ?DOC( " Send a streaming chat message with a callback for each event\n" "\n" " **Note**: Despite the callback, this function collects ALL events before\n" " calling the callbacks. It does NOT provide true real-time streaming.\n" " For real-time streaming, use the sans-io functions in `anthropic/streaming/handler`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " api.chat_stream_with_callback(client, request, fn(event) {\n" " case api.event_text(event) {\n" " Ok(text) -> io.print(text)\n" " Error(_) -> Nil\n" " }\n" " })\n" " ```\n" ). -spec chat_stream_with_callback( anthropic@client:client(), anthropic@request:create_message_request(), fun((anthropic@streaming:stream_event()) -> nil) ) -> {ok, stream_result()} | {error, stream_error()}. chat_stream_with_callback(Client, Message_request, Callback) -> gleam@result:'try'( chat_stream(Client, Message_request), fun(Stream_result) -> gleam@list:each(erlang:element(2, Stream_result), Callback), {ok, Stream_result} end ).