Packages

A well-typed, idiomatic Gleam client for Anthropic's Claude API with streaming support and tool use

Current section

Files

Jump to
anthropic_gleam src anthropic streaming.gleam
Raw

src/anthropic/streaming.gleam

//// Streaming event types for the Anthropic Messages API
////
//// This module defines types for Server-Sent Events (SSE) from the streaming API,
//// including message_start, content_block_start, content_block_delta,
//// content_block_stop, message_delta, message_stop, ping, and error events.
import anthropic/message.{type ContentBlock, type Role}
import anthropic/request.{type StopReason, type Usage}
import gleam/json.{type Json}
import gleam/option.{type Option, None, Some}
// =============================================================================
// Delta Types
// =============================================================================
/// Delta type for text content streaming
pub type TextDelta {
TextDelta(
/// The text fragment being streamed
text: String,
)
}
/// Delta type for tool input JSON streaming
pub type InputJsonDelta {
InputJsonDelta(
/// Partial JSON string for tool input
partial_json: String,
)
}
/// Union type for content block deltas
pub type ContentBlockDelta {
/// Text content delta
TextContentDelta(delta: TextDelta)
/// Tool input JSON delta
InputJsonContentDelta(delta: InputJsonDelta)
}
// =============================================================================
// Message Start Event
// =============================================================================
/// Initial message information sent at stream start
pub type MessageStart {
MessageStart(
/// Unique identifier for this message
id: String,
/// Object type, always "message"
message_type: String,
/// Role of the response, always "assistant"
role: Role,
/// Model that is generating the response
model: String,
/// Initial usage information (input tokens)
usage: Usage,
)
}
// =============================================================================
// Content Block Start Event
// =============================================================================
/// Information about a content block starting
pub type ContentBlockStart {
ContentBlockStart(
/// Index of this content block in the message
index: Int,
/// The initial content block (with empty/partial content)
content_block: ContentBlock,
)
}
// =============================================================================
// Content Block Delta Event
// =============================================================================
/// Delta update for a content block
pub type ContentBlockDeltaEvent {
ContentBlockDeltaEvent(
/// Index of the content block being updated
index: Int,
/// The delta update
delta: ContentBlockDelta,
)
}
// =============================================================================
// Content Block Stop Event
// =============================================================================
/// Marks the end of a content block
pub type ContentBlockStop {
ContentBlockStop(
/// Index of the content block that ended
index: Int,
)
}
// =============================================================================
// Message Delta Event
// =============================================================================
/// Delta update for message-level information
pub type MessageDelta {
MessageDelta(
/// The reason generation stopped (if applicable)
stop_reason: Option(StopReason),
/// The stop sequence that triggered stopping (if applicable)
stop_sequence: Option(String),
)
}
/// Usage information in message delta
pub type MessageDeltaUsage {
MessageDeltaUsage(
/// Number of output tokens generated
output_tokens: Int,
)
}
/// Full message delta event with usage
pub type MessageDeltaEvent {
MessageDeltaEvent(
/// The delta update
delta: MessageDelta,
/// Updated usage information
usage: MessageDeltaUsage,
)
}
// =============================================================================
// Error Event
// =============================================================================
/// Error information from a streaming error event
pub type StreamError {
StreamError(
/// Error type string
error_type: String,
/// Error message
message: String,
)
}
// =============================================================================
// Stream Event (Union Type)
// =============================================================================
/// All possible streaming events from the API
pub type StreamEvent {
/// Initial message information
MessageStartEvent(message: MessageStart)
/// Start of a content block
ContentBlockStartEvent(content_block_start: ContentBlockStart)
/// Delta update for a content block
ContentBlockDeltaEventVariant(content_block_delta: ContentBlockDeltaEvent)
/// End of a content block
ContentBlockStopEvent(content_block_stop: ContentBlockStop)
/// Delta update for message-level information
MessageDeltaEventVariant(message_delta: MessageDeltaEvent)
/// End of the message
MessageStopEvent
/// Keepalive ping
PingEvent
/// Error during streaming
ErrorEvent(error: StreamError)
}
// =============================================================================
// Event Type String Conversion
// =============================================================================
/// Get the event type string for a StreamEvent
pub fn event_type_string(event: StreamEvent) -> String {
case event {
MessageStartEvent(_) -> "message_start"
ContentBlockStartEvent(_) -> "content_block_start"
ContentBlockDeltaEventVariant(_) -> "content_block_delta"
ContentBlockStopEvent(_) -> "content_block_stop"
MessageDeltaEventVariant(_) -> "message_delta"
MessageStopEvent -> "message_stop"
PingEvent -> "ping"
ErrorEvent(_) -> "error"
}
}
/// Parse an event type string
pub fn event_type_from_string(str: String) -> Result(String, String) {
case str {
"message_start"
| "content_block_start"
| "content_block_delta"
| "content_block_stop"
| "message_delta"
| "message_stop"
| "ping"
| "error" -> Ok(str)
_ -> Error("Unknown event type: " <> str)
}
}
// =============================================================================
// JSON Encoding
// =============================================================================
/// Encode a TextDelta to JSON
pub fn text_delta_to_json(delta: TextDelta) -> Json {
json.object([
#("type", json.string("text_delta")),
#("text", json.string(delta.text)),
])
}
/// Encode an InputJsonDelta to JSON
pub fn input_json_delta_to_json(delta: InputJsonDelta) -> Json {
json.object([
#("type", json.string("input_json_delta")),
#("partial_json", json.string(delta.partial_json)),
])
}
/// Encode a ContentBlockDelta to JSON
pub fn content_block_delta_to_json(delta: ContentBlockDelta) -> Json {
case delta {
TextContentDelta(d) -> text_delta_to_json(d)
InputJsonContentDelta(d) -> input_json_delta_to_json(d)
}
}
/// Encode a StreamError to JSON
pub fn stream_error_to_json(error: StreamError) -> Json {
json.object([
#("type", json.string(error.error_type)),
#("message", json.string(error.message)),
])
}
// =============================================================================
// Constructors
// =============================================================================
/// Create a TextDelta
pub fn text_delta(text: String) -> TextDelta {
TextDelta(text: text)
}
/// Create an InputJsonDelta
pub fn input_json_delta(partial_json: String) -> InputJsonDelta {
InputJsonDelta(partial_json: partial_json)
}
/// Create a MessageStart
pub fn message_start(
id: String,
role: Role,
model: String,
input_tokens: Int,
) -> MessageStart {
MessageStart(
id: id,
message_type: "message",
role: role,
model: model,
usage: request.Usage(input_tokens: input_tokens, output_tokens: 0),
)
}
/// Create a ContentBlockStart for a text block
pub fn text_block_start(index: Int) -> ContentBlockStart {
ContentBlockStart(index: index, content_block: message.TextBlock(text: ""))
}
/// Create a ContentBlockStart for a tool use block
pub fn tool_use_block_start(
index: Int,
id: String,
name: String,
) -> ContentBlockStart {
ContentBlockStart(
index: index,
content_block: message.ToolUseBlock(id: id, name: name, input: ""),
)
}
/// Create a ContentBlockDeltaEvent for text
pub fn text_delta_event(index: Int, text: String) -> ContentBlockDeltaEvent {
ContentBlockDeltaEvent(
index: index,
delta: TextContentDelta(TextDelta(text: text)),
)
}
/// Create a ContentBlockDeltaEvent for tool input JSON
pub fn input_json_delta_event(
index: Int,
partial_json: String,
) -> ContentBlockDeltaEvent {
ContentBlockDeltaEvent(
index: index,
delta: InputJsonContentDelta(InputJsonDelta(partial_json: partial_json)),
)
}
/// Create a ContentBlockStop
pub fn content_block_stop(index: Int) -> ContentBlockStop {
ContentBlockStop(index: index)
}
/// Create a MessageDeltaEvent
pub fn message_delta_event(
stop_reason: Option(StopReason),
stop_sequence: Option(String),
output_tokens: Int,
) -> MessageDeltaEvent {
MessageDeltaEvent(
delta: MessageDelta(stop_reason: stop_reason, stop_sequence: stop_sequence),
usage: MessageDeltaUsage(output_tokens: output_tokens),
)
}
/// Create a StreamError
pub fn stream_error(error_type: String, message: String) -> StreamError {
StreamError(error_type: error_type, message: message)
}
// =============================================================================
// Utility Functions
// =============================================================================
/// Check if an event is a terminal event (message_stop or error)
pub fn is_terminal_event(event: StreamEvent) -> Bool {
case event {
MessageStopEvent -> True
ErrorEvent(_) -> True
_ -> False
}
}
/// Get the text from a content block delta, if it's a text delta
pub fn get_delta_text(delta: ContentBlockDelta) -> Option(String) {
case delta {
TextContentDelta(d) -> Some(d.text)
InputJsonContentDelta(_) -> None
}
}
/// Get the partial JSON from a content block delta, if it's an input JSON delta
pub fn get_delta_json(delta: ContentBlockDelta) -> Option(String) {
case delta {
TextContentDelta(_) -> None
InputJsonContentDelta(d) -> Some(d.partial_json)
}
}