anthropic

    Thin Anthropic Messages API client for MoonBit built on gaato/http and gaato/mttp.

    anthropic
    llm
    sdk
    messages
    Download zip
    Author
    Version
    0.15.0
    License
    Apache-2.0
    Last updated
    9 hours ago
    Downloads
    148

    #gaato/anthropic

    An unofficial Anthropic Messages API client for MoonBit: messages, streaming, tool use, token counting and Models. Compatible servers are selected with base_url. It uses gaato/mttp, with a caller-supplied Backend and Clock. The facade itself requires no async runtime.

    Status: unofficial and experimental. Minor releases may contain breaking changes; the changelog lists each one with a migration note.

    #Install

    moon add gaato/anthropic moon add gaato/http moon add gaato/mttp moon add gaato/http-async

    #Usage

    With gaato/http-async/backend imported as @backend and gaato/http-async/runtime as @runtime, gaato/http-async/codings as @codings and gaato/http/h1client as @h1client, create the adapters. Opt in to the exact experimental packages moonbitlang/core/async and gaato/http/h1client in the calling package's moon.pkg:

    ///|
    let backend = @codings.Decompress::new(
    @backend.AsyncBackend::new(config={
    ..@h1client.ClientConfig::default(),
    connect_timeout_ms: Some(5000),
    }),
    )

    ///|
    let clock = @runtime.AsyncClock::new()

    ///|
    let timer = @runtime.ClockTimer::new()

    Then pass them to the client and send a message:

    ///|
    /// Asks `model` one question and returns its answer.
    pub async fn ask(
    backend : &@http.Backend,
    clock : &@rt.Clock,
    timer : &@rt.Timer,
    api_key~ : String,
    model~ : String,
    ) -> Result[String, @anthropic.AnthropicFailure] {
    let client = @anthropic.Anthropic::new(
    api_key~,
    backend,
    clock,
    timer~,
    total_timeout_ms=900_000,
    )
    let reply = client.create_message(
    @anthropic.MessageRequest::new(model~, max_tokens=1024, messages=[
    @anthropic.Message::user("Hello"),
    ]),
    )
    match reply {
    Ok(reply) => Ok(reply.text())
    Err(error) => Err(error)
    }
    }

    Buffered responses are limited to 64 MiB by default. To change the limit, construct an mttp Client with max_body_bytes and pass it to from_client. Streaming reads incrementally, with a 16 MiB per-event limit. Limit failures raise TransportError(TooLarge, ...). HTTP and decode failures return Err(Response(ResponseFailure)), preserving the received head and bounded diagnostic body. Input checks return Err(Validation(message)). api_error extracts a provider error envelope from an AnthropicFailure value. Transport failures and unknown callback exceptions remain raised errors; cancellation remains a signal.

    #Packages

    PackageWhat it is
    gaato/anthropicThe Anthropic client, MessageRequest, MessageResponse, MessageEvent, and tool helpers
    gaato/anthropic/genRequest and response types and operations generated from the official Python SDK's OpenAPI document

    #Operations

    The client has create_message, stream_message, count_tokens, list_models and retrieve_model. Beta APIs, batches, files and other endpoints are not included.

    • MessageRequest takes typed input blocks, tool choice (the tool_choice_* helpers build the common ones), thinking, output configuration and metadata; its extra map adds fields it does not type.
    • create_message_params and stream_message_params take the generated CreateMessageParams, for the whole request vocabulary.
    • A MessageResponse keeps both the generated message and the original raw JSON. Message::from_response turns it into the assistant turn of the next request, and tool_uses() lists the tool calls it asks for.
    • client.paginate_models(limit=100).collect(max=1000) collects up to 1000 models across pages.

    #Streaming

    stream_message calls a handler for each MessageEvent as it arrives:

    ///|
    /// Streams a message, passing each piece of text to `on_text` as it arrives.
    pub async fn stream_text(
    client : @anthropic.Anthropic,
    model~ : String,
    on_text : (String) -> Unit,
    ) -> Result[Unit, @anthropic.AnthropicFailure] {
    client.stream_message(
    @anthropic.MessageRequest::new(model~, max_tokens=1024, messages=[
    @anthropic.Message::user("Write a haiku about rivers."),
    ]),
    event => if event is TextDelta(text~, ..) { on_text(text) },
    )
    }

    ToolUseAccumulator reassembles streamed tool arguments. It grows with what you feed it, so cap a stream by its events or bytes, and set max_tokens.

    #Retries

    Retries follow the official Python SDK: two retries of 408, 409, 429 and every 5xx (529 overloaded included), and of a timeout or network failure, for every method. That includes POST, so a message can be generated, and billed, twice; pass retry=@client.RetryPolicy::conservative() to retry a POST only when a failure proves nothing was sent, unless replay safety is explicit. The server's x-should-retry is obeyed, waits back off from 0.5 s to 8 s, and a Retry-After is waited out however long it is, so bound the call with total_timeout_ms as above: a wait that would outlast it is not begun.

    #Timeouts

    Each attempt defaults to 600 s. A configured total_timeout_ms requires a timer and covers admission, retries, waits and streaming callbacks. Expiry initiates cancellation; necessary cleanup may finish afterwards. Backend phase deadlines can finish a call earlier. The example sets a 5-second connect deadline; the SDK passes the attempt budget in SendOptions. Streaming callbacks run after the attempt has been selected and are not retried after delivery starts. Close the backend when its owner shuts down.

    #License

    Apache-2.0

    CreateMessageParams

    The full Messages request, for create_message_params and stream_message_params.

    InputContentBlock

    A content block of an input message: text, image, document, tool use or result, and others.

    ListResponseModelInfo

    One page of list_models.

    MessageDelta

    The top-level changes in a message_delta stream event, such as the stop reason.

    MessageDeltaUsage

    The cumulative token counts in a message_delta stream event.

    MessageStreamEvent

    A raw Messages stream event as the server sends it.

    Metadata

    Request metadata: an opaque id of the end user.

    ModelInfo

    A model as list_models and retrieve_model return it.

    ModelsListLifecycleItem

    A model lifecycle stage that list_models can filter by.

    OutputConfig

    Output settings of a request: the effort level and a structured output format.

    StopReason

    Why the model stopped generating, as in the stop_reason of a MessageResponse.

    ThinkingConfigParam

    Whether and how the model thinks before answering.

    ToolChoice

    How the model may use the request's tools; the tool_choice_* helpers build the common ones.

    Usage

    Token counts billed for a message.

    Anthropic

    pub struct Anthropic {
    // private fields
    }

    An Anthropic API client backed by the transport-independent SDK runtime.

    Request and response shapes come from the generated gen package, which is produced from the vendored first-party OpenAPI document; this facade adds a small stable vocabulary on top of it.
    impl Debug for Anthropic

    Anthropic::count_tokens

    async fn Anthropic::count_tokens(self : Anthropic, request : MessageRequest) -> Result[Int, AnthropicFailure]

    Counts input tokens without creating a message.

    Anthropic::create_message

    async fn Anthropic::create_message(self : Anthropic, request : MessageRequest) -> Result[MessageResponse, AnthropicFailure]

    Creates and buffers one message.

    Anthropic::create_message_params

    Creates a buffered message from generated parameters. stream is ignored.

    Anthropic::from_client

    fn Anthropic::from_client(client :
    Client
    , version? : String) -> Anthropic

    Wraps a preconfigured runtime client for compatible APIs and tests.

    Anthropic::list_models

    Lists a page of models. Follow has_more with after_id=last_id.

    Anthropic::new

    fn Anthropic::new(api_key~ : String, backend : &
    Backend
    , clock : &
    Clock
    , base_url? : String, version? : String, auth_header? : AuthHeader, retry? :
    RetryPolicy
    , timer? : &
    Timer
    , timeout_ms? : Int, max_body_bytes? : Int, total_timeout_ms? : Int) -> Anthropic

    Creates an Anthropic client with the selected authentication header.

    base_url is the server root: generated paths already start with /v1, so an Anthropic-compatible gateway is addressed by its root as well (for example https://openrouter.ai/api).

    retry defaults to the official SDK's behaviour (anthropic-sdk-python, _base_client.py): an SDK-local policy with two retries of 408, 409, 429 and every 5xx (529 overloaded included), and of a timeout or network failure, for every method including POST, so a message can be generated twice; x-should-retry is obeyed, and any Retry-After is waited out however long, so set total_timeout_ms to bound a call. Pass retry=@client.RetryPolicy::conservative() to retry a POST only on failures that prove nothing was sent, unless explicitly declared replay-safe.

    With a timer (such as gaato/http-async/runtime's ClockTimer), each attempt is limited to timeout_ms (600 s, the official default) and, when total_timeout_ms is given, the whole call, retries and their waits included, to that. A total deadline requires Timer and also covers admission and stream callbacks. Backend phase deadlines can finish attempts earlier.

    Anthropic::paginate_models

    Iterates model pages following has_more and last_id.

    Anthropic::retrieve_model

    async fn Anthropic::retrieve_model(self : Anthropic, model_id : String) -> Result[
    ModelInfo
    , AnthropicFailure]

    Retrieves one model by identifier or alias.

    Anthropic::stream_message

    async fn Anthropic::stream_message(self : Anthropic, request : MessageRequest, f : async (MessageEvent) -> Unit) -> Result[Unit, AnthropicFailure]

    Dispatches message events in order, reading through EOF and closing on failure.

    Anthropic::stream_message_params

    Streams generated events. stream is forced on; future event kinds remain Unknown.

    Anthropic::to_repr

    Redacted debug representation of an Anthropic client.

    AnthropicFailure

    pub(all) enum AnthropicFailure {
    Response(
    ResponseFailure
    )
    Validation(String)
    } derive(
    Debug
    )

    An expected SDK failure. TransportError and unknown user errors remain raised.

    AnthropicFailure::to_repr

    Formats an expected failure with its original response metadata.

    ApiErrorBody

    pub(all) struct ApiErrorBody {
    type_ : String
    message : String
    } derive(Eq,
    Debug
    )

    The error body returned by the Anthropic API.

    test {
    let error : @anthropic.ApiErrorBody = {
    type_: "invalid_request_error",
    message: "invalid request",
    }
    assert_eq(error.message, "invalid request")
    }

    ApiErrorBody::equal

    Compares API error bodies field by field.

    ApiErrorBody::not_equal

    fn ApiErrorBody::not_equal(x : ApiErrorBody, y : ApiErrorBody) -> Bool

    Compares API error bodies field by field.

    ApiErrorBody::to_repr

    Debug representation of an API error body.

    AuthHeader

    pub(all) enum AuthHeader {
    XApiKey
    Bearer
    } derive(Eq,
    Debug
    )

    Selects the credential header used by the API.

    AuthHeader::equal

    fn AuthHeader::equal(AuthHeader, AuthHeader) -> Bool

    Compares authentication-header choices.

    AuthHeader::not_equal

    fn AuthHeader::not_equal(x : AuthHeader, y : AuthHeader) -> Bool

    Compares authentication-header choices.

    AuthHeader::to_repr

    Debug representation of an authentication-header choice.

    ContentBlock

    pub(all) enum ContentBlock {
    Text(String)
    Thinking(thinking~ : String, signature~ : String)
    RedactedThinking(String)
    ToolUse(id~ : String, name~ : String, input~ :
    Json
    )
    Other(String,
    Json
    )
    } derive(Eq,
    Debug
    )

    One output content block. Blocks beyond text, thinking and tool use keep their JSON; the typed form is on MessageResponse::message.

    ContentBlock::equal

    Compares output content blocks.

    ContentBlock::not_equal

    fn ContentBlock::not_equal(x : ContentBlock, y : ContentBlock) -> Bool

    Compares output content blocks.

    ContentBlock::to_repr

    Debug representation of an output content block.

    InputContent

    pub(all) enum InputContent {
    Text(String)
    Blocks(Array[
    InputContentBlock
    ])
    } derive(Eq,
    Debug
    )

    Input content as plain text or typed content blocks. A block the generated vocabulary does not know is carried losslessly as @gen.InputContentBlock::Unknown.

    InputContent::equal

    Compares input content, including typed blocks.

    InputContent::not_equal

    fn InputContent::not_equal(x : InputContent, y : InputContent) -> Bool

    Compares input content, including typed blocks.

    InputContent::to_repr

    Debug representation of input content.

    Message

    pub(all) struct Message {
    role : Role
    content : InputContent
    } derive(Eq,
    Debug
    )

    One user, assistant or system turn sent to the Messages API.

    Message::assistant

    fn Message::assistant(text : String) -> Message

    Creates an assistant message containing plain text.

    test {
    assert_eq(@anthropic.Message::assistant("hello"), {
    role: @anthropic.Assistant,
    content: @anthropic.Text("hello"),
    })
    }

    Message::assistant_blocks

    Creates an assistant message from typed content blocks.

    Message::equal

    fn Message::equal(Message, Message) -> Bool

    Compares messages field by field.

    Message::from_response

    fn Message::from_response(response : MessageResponse) -> Message

    Turns a response into the assistant turn to send back, block for block: text, thinking (with its signature), tool use and every other block the API returned are replayed as input blocks. A block the request vocabulary cannot represent is passed through as raw JSON.

    Message::not_equal

    fn Message::not_equal(x : Message, y : Message) -> Bool

    Compares messages field by field.

    Message::system

    fn Message::system(text : String) -> Message

    Creates a mid-conversation system message containing plain text. Place it right after a user turn; see Role.

    test {
    assert_eq(@anthropic.Message::system("answer in French"), {
    role: @anthropic.System,
    content: @anthropic.Text("answer in French"),
    })
    }

    Message::to_repr

    Debug representation of a message.

    Message::tool_result

    fn Message::tool_result(tool_use_id~ : String, content~ : String, is_error? : Bool) -> Message

    Creates a user message containing one tool result block.

    test {
    let message = @anthropic.Message::tool_result(
    tool_use_id="toolu_1",
    content="done",
    )
    assert_eq(message.role, @anthropic.User)
    }

    Message::user

    fn Message::user(text : String) -> Message

    Creates a user message containing plain text.

    test {
    assert_eq(@anthropic.Message::user("hello"), {
    role: @anthropic.User,
    content: @anthropic.Text("hello"),
    })
    }

    Message::user_blocks

    Creates a user message from typed content blocks (images, documents, tool results, ...).

    MessageEvent

    pub(all) enum MessageEvent {
    MessageStart(MessageResponse)
    ContentBlockStart(index~ : Int, block~ : ContentBlock)
    TextDelta(index~ : Int, text~ : String)
    ThinkingDelta(index~ : Int, thinking~ : String)
    SignatureDelta(index~ : Int, signature~ : String)
    InputJsonDelta(index~ : Int, partial_json~ : String)
    ContentBlockStop(index~ : Int)
    MessageDelta(delta~ :
    MessageDelta
    , usage~ :
    MessageDeltaUsage
    )
    MessageStop
    Ping
    Error(ApiErrorBody)
    Other(String,
    Json
    )
    } derive(Eq,
    Debug
    )

    One event from a streaming message. Event and delta kinds outside the generated vocabulary retain their raw JSON.

    MessageEvent::equal

    Compares message events, including retained unknown JSON.

    MessageEvent::not_equal

    fn MessageEvent::not_equal(x : MessageEvent, y : MessageEvent) -> Bool

    Compares message events, including retained unknown JSON.

    MessageEvent::to_repr

    Debug representation of a message event.

    MessageRequest

    pub(all) struct MessageRequest {
    model : String
    max_tokens : Int
    messages : Array[Message]
    system : String?
    stop_sequences : Array[String]?
    tools : Array[ToolDef]?
    tool_choice :
    ToolChoice
    ?
    thinking :
    ThinkingConfigParam
    ?
    output_config :
    OutputConfig
    ?
    metadata :
    Metadata
    ?
    extra : Map[String,
    Json
    ]
    } derive(Eq,
    Debug
    )

    Input accepted by buffered and streaming message creation and by token counting.

    The typed fields cover the common surface; extra is merged into the JSON body after the typed fields and validated against the generated schema, so any other documented parameter (service_tier, inference_geo, ...) can be passed without a facade change. For full control build a @gen.CreateMessageParams and use Anthropic::create_message_params.

    MessageRequest::equal

    Compares message requests field by field.

    MessageRequest::new

    fn MessageRequest::new(model~ : String, max_tokens~ : Int, messages~ : Array[Message], system? : String, stop_sequences? : Array[String], tools? : Array[ToolDef], tool_choice? :
    ToolChoice
    , thinking? :
    ThinkingConfigParam
    , output_config? :
    OutputConfig
    , metadata? :
    Metadata
    , extra? : Map[String,
    Json
    ]) -> MessageRequest

    Creates a message request and snapshots its mutable inputs.

    test {
    let request = @anthropic.MessageRequest::new(
    model="claude-opus-5",
    max_tokens=64,
    messages=[@anthropic.Message::user("hello")],
    )
    assert_eq(request.messages.length(), 1)
    }

    MessageRequest::not_equal

    fn MessageRequest::not_equal(x : MessageRequest, y : MessageRequest) -> Bool

    Compares message requests field by field.

    MessageRequest::to_count_tokens_params

    The generated token-counting request for this facade request, without extra.

    MessageRequest::to_params

    The generated request for this facade request, without extra. stream selects server-sent events; the facade sets it for stream_message.

    MessageRequest::to_repr

    Debug representation of a message request.

    MessageResponse

    pub(all) struct MessageResponse {
    id : String
    model : String
    role : Role
    content : Array[ContentBlock]
    stop_reason :
    StopReason
    ?
    usage :
    Usage

    message :
    Message

    raw :
    Json

    } derive(Eq,
    Debug
    )

    A decoded message: the facade view (content), the generated view (message) and the original JSON (raw).

    MessageResponse::equal

    Compares decoded messages field by field.

    MessageResponse::not_equal

    fn MessageResponse::not_equal(x : MessageResponse, y : MessageResponse) -> Bool

    Compares decoded messages field by field.

    MessageResponse::text

    fn MessageResponse::text(self : MessageResponse) -> String

    Concatenates text blocks in response order and ignores other block kinds.

    MessageResponse::to_repr

    Debug representation of a decoded message.

    MessageResponse::tool_uses

    fn MessageResponse::tool_uses(self : MessageResponse) -> Array[(String, String,
    Json
    )]

    The tool uses requested by the response, in content order.

    Role

    pub(all) enum Role {
    User
    Assistant
    System
    } derive(Eq,
    Debug
    )

    A participant in a Messages API conversation. System is a mid-conversation system message, which only some models accept and which must directly follow a user turn; a system prompt for the whole conversation goes in MessageRequest.system. A response is always Assistant.

    Role::equal

    fn Role::equal(Role, Role) -> Bool

    Compares message roles.

    Role::not_equal

    fn Role::not_equal(x : Role, y : Role) -> Bool

    Compares message roles.

    Role::to_repr

    Debug representation of a message role.

    ToolDef

    pub(all) struct ToolDef {
    name : String
    description : String?
    input_schema :
    Json

    strict : Bool?
    } derive(Eq,
    Debug
    )

    A client tool definition for the Messages API.

    test {
    let tool = @anthropic.ToolDef::new(name="lookup", input_schema={
    "type": "object",
    })
    assert_eq(tool.name, "lookup")
    }

    ToolDef::equal

    fn ToolDef::equal(ToolDef, ToolDef) -> Bool

    Compares tool definitions field by field.

    ToolDef::new

    fn ToolDef::new(name~ : String, input_schema~ :
    Json
    , description? : String, strict? : Bool) -> ToolDef

    Creates a client tool definition with a JSON Schema input object.

    ToolDef::not_equal

    fn ToolDef::not_equal(x : ToolDef, y : ToolDef) -> Bool

    Compares tool definitions field by field.

    ToolDef::to_repr

    Debug representation of a tool definition.

    ToolUseAccumulator

    pub struct ToolUseAccumulator {
    // private fields
    }

    Reconstructs streamed tool-use inputs by content block index.

    test {
    let tools = @anthropic.ToolUseAccumulator::new()
    tools.feed(
    @anthropic.ContentBlockStart(
    index=0,
    block=@anthropic.ToolUse(id="toolu_1", name="lookup", input={}),
    ),
    )
    tools.feed(@anthropic.InputJsonDelta(index=0, partial_json="{}"))
    tools.feed(@anthropic.ContentBlockStop(index=0))
    assert_eq(tools.finish()[0].2, {})
    }

    ToolUseAccumulator::feed

    fn ToolUseAccumulator::feed(self : ToolUseAccumulator, event : MessageEvent) -> Unit

    Consumes one message event. Invalid partial JSON is retained without raising.

    ToolUseAccumulator::finish

    Returns completed tool uses ordered by block index. Invalid accumulated JSON raises @json.ParseError here rather than from feed.

    ToolUseAccumulator::new

    Creates an empty tool-use accumulator.

    api_error

    fn api_error(error : AnthropicFailure) -> ApiErrorBody?

    Extracts an Anthropic error envelope from an HTTP status failure. Returns None for transport, decode, configuration, and malformed error bodies.

    tool_choice_any

    fn tool_choice_any(disable_parallel_tool_use? : Bool) ->
    ToolChoice

    Requires the model to call some tool.

    tool_choice_auto

    fn tool_choice_auto(disable_parallel_tool_use? : Bool) ->
    ToolChoice

    Lets the model decide whether to call a tool.

    tool_choice_none

    Forbids tool calls.

    tool_choice_tool

    fn tool_choice_tool(name : String, disable_parallel_tool_use? : Bool) ->
    ToolChoice

    Requires the model to call the named tool.