crimson_crab/lib.rs
1//! # crimson-crab
2//!
3//! The production-grade Rust SDK for Anthropic's Claude API.
4//!
5//! This crate mirrors the Claude wire API exactly (see the type modules under
6//! [`types`]) and is designed to be **forward compatible**: content blocks,
7//! stop reasons, and other type-tagged or string-valued enums all carry a
8//! catch-all variant, so values the SDK has never seen deserialize instead of
9//! erroring.
10//!
11//! > crimson-crab is an independent open-source project and is not affiliated
12//! > with Anthropic.
13//!
14//! ## Status
15//!
16//! The wire types, the HTTP [`Client`], the Messages, Models, and Batches
17//! endpoints, and SSE [`streaming`] (`client.messages().stream(&req)`) are all
18//! implemented.
19//!
20//! ## Quickstart
21//!
22//! ```no_run
23//! use crimson_crab::model_ids::CLAUDE_OPUS_4_8;
24//! use crimson_crab::prelude::*;
25//!
26//! # #[tokio::main]
27//! # async fn main() -> crimson_crab::Result<()> {
28//! // Reads the API key from the ANTHROPIC_API_KEY environment variable.
29//! let client = Client::from_env()?;
30//!
31//! let request = MessagesRequest::builder()
32//! .model(CLAUDE_OPUS_4_8)
33//! .max_tokens(1024)
34//! .messages(vec![MessageParam::user("Hello, Claude!")])
35//! .build()?;
36//!
37//! let message = client.messages().create(&request).await?;
38//! println!("{}", message.text());
39//! # Ok(())
40//! # }
41//! ```
42//!
43//! ## Building request values
44//!
45//! ```
46//! use crimson_crab::model_ids::CLAUDE_OPUS_4_8;
47//! use crimson_crab::prelude::*;
48//!
49//! // A conversation turn.
50//! let messages = vec![MessageParam::user("What is the weather in Paris?")];
51//!
52//! // A custom tool the model may call.
53//! let tool = Tool::new(
54//! "get_weather",
55//! "Get the current weather for a location",
56//! serde_json::json!({
57//! "type": "object",
58//! "properties": {"location": {"type": "string"}},
59//! "required": ["location"]
60//! }),
61//! );
62//!
63//! assert_eq!(CLAUDE_OPUS_4_8, "claude-opus-4-8");
64//! assert_eq!(messages[0].role, Role::User);
65//! assert_eq!(tool.name, "get_weather");
66//! ```
67//!
68//! ## Parsing a response
69//!
70//! ```
71//! use crimson_crab::prelude::*;
72//!
73//! let body = serde_json::json!({
74//! "id": "msg_01ABC",
75//! "type": "message",
76//! "role": "assistant",
77//! "model": "claude-opus-4-8",
78//! "content": [{"type": "text", "text": "It is sunny."}],
79//! "stop_reason": "end_turn",
80//! "stop_sequence": null,
81//! "usage": {"input_tokens": 12, "output_tokens": 4}
82//! });
83//! let msg: Message = serde_json::from_value(body).unwrap();
84//! assert_eq!(msg.text(), "It is sunny.");
85//! assert_eq!(msg.stop_reason, Some(StopReason::EndTurn));
86//! ```
87
88#![forbid(unsafe_code)]
89#![cfg_attr(
90 not(test),
91 deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::todo)
92)]
93#![deny(missing_docs)]
94
95// The HTTP client speaks HTTPS to `https://api.anthropic.com`, which requires a
96// TLS backend on native targets. Fail the build early (rather than at runtime
97// with an opaque connector error) if default features were disabled without
98// selecting one. `wasm32` is exempt: there the browser's `fetch` provides TLS.
99#[cfg(all(
100 not(target_arch = "wasm32"),
101 not(any(feature = "rustls-tls", feature = "native-tls"))
102))]
103compile_error!(
104 "crimson-crab requires a TLS backend on native targets: enable the \
105 `rustls-tls` (default) or `native-tls` feature."
106);
107
108pub mod api;
109pub mod client;
110pub mod error;
111mod http;
112pub mod model_ids;
113pub mod streaming;
114pub mod types;
115
116pub use client::{Client, ClientBuilder};
117pub use error::{ApiError, Error, Result};
118pub use streaming::{ContentDelta, MessageStream, StreamEvent};
119
120/// Commonly used types, re-exported for `use crimson_crab::prelude::*;`.
121///
122/// # Examples
123///
124/// ```
125/// use crimson_crab::prelude::*;
126///
127/// let _ = MessageParam::user("hi");
128/// let _ = ToolChoice::auto();
129/// let _ = ThinkingConfig::adaptive();
130/// let _ = MessagesRequest::builder().model("claude-opus-4-8");
131/// ```
132pub mod prelude {
133 pub use crate::api::{CountTokensRequest, CountTokensResponse, MessagesRequest};
134 pub use crate::client::{Client, ClientBuilder};
135 // `Error` and the `Result<T>` alias are deliberately NOT re-exported here:
136 // a glob import that shadows `std::result::Result` breaks common downstream
137 // patterns like `Result<Event, E>`. Reach them as `crimson_crab::Error` /
138 // `crimson_crab::Result` instead.
139 pub use crate::error::ApiError;
140 pub use crate::streaming::{ContentDelta, MessageStream, StreamEvent};
141 pub use crate::types::cache::{CacheControl, CacheTtl};
142 pub use crate::types::content::{ContentBlock, ContentBlockParam};
143 pub use crate::types::message::{
144 Message, MessageContent, MessageParam, Metadata, Role, StopDetails, StopReason,
145 SystemPrompt, Usage,
146 };
147 pub use crate::types::output::{Effort, OutputConfig, OutputFormat};
148 pub use crate::types::thinking::{ThinkingConfig, ThinkingDisplay};
149 pub use crate::types::tool::{Tool, ToolChoice, ToolResultContent, ToolUnion};
150 // Tool-loop types (the crate's most common use case) belong in the prelude.
151 pub use crate::types::content::{ToolResultBlockParam, ToolUseBlock};
152}
153
154/// Compiles the `README.md` code samples as part of `cargo test --doc` so the
155/// crate's most-read snippets (quickstart, streaming, tool loop) cannot silently
156/// drift from the public API. The item exists only during doctest builds.
157#[cfg(doctest)]
158#[doc = include_str!("../README.md")]
159pub struct ReadmeDoctests;