| Crates.io | ai-lib-rust |
| lib.rs | ai-lib-rust |
| version | 0.5.1 |
| created_at | 2026-01-16 16:39:58.224773+00 |
| updated_at | 2026-01-20 08:46:49.528405+00 |
| description | Protocol Runtime for AI-Protocol - A high-performance Rust reference implementation |
| homepage | |
| repository | https://github.com/hiddenpath/ai-lib-rust |
| max_upload_size | |
| id | 2048920 |
| size | 461,676 |
Protocol Runtime for AI-Protocol - A high-performance Rust reference implementation.
ai-lib-rust is the Rust runtime implementation for the AI-Protocol specification. It embodies the core design principle: 一切逻辑皆算子,一切配置皆协议 (All logic is operators, all configuration is protocol).
Unlike traditional adapter libraries that hardcode provider-specific logic, ai-lib-rust is a protocol-driven runtime that executes AI-Protocol specifications. This means:
The library is organized into three layers:
protocol/)pipeline/)client/, types/)standard_schemaai-lib-rust keeps the runtime core small, and exposes optional higher-level helpers behind feature flags.
For a deeper overview, see docs/ARCHITECTURE.md.
AiClient, AiClientBuilder, CancelHandle, CallStats, ChatBatchRequest, EndpointExtMessage, MessageRole, StreamingEvent, ToolCallResult<T>, Error, ErrorContextrouting_mvp: pure logic model management helpers (CustomModelManager, ModelArray, etc.)interceptors: application-layer call hooks (InterceptorPipeline, Interceptor, RequestContext)Enable with:
[dependencies]
ai-lib-rust = { version = "0.5.1", features = ["routing_mvp", "interceptors"] }
This is a structured view of what the crate provides, grouped by layers.
src/protocol/)ProtocolLoader: load provider manifests from local paths / env paths / GitHub raw URLsProtocolValidator: JSON Schema validation (supports offline via embedded schema)ProtocolManifest: typed representation of provider manifestsUnifiedRequest: provider-agnostic request payload used by the runtimesrc/transport/)HttpTransport: reqwest-based transport with proxy/timeout defaults and env knobs<PROVIDER_ID>_API_KEY envsrc/pipeline/)StreamingEventsrc/client/)AiClient: runtime entry point; model-driven ("provider/model")client.chat().messages(...).stream().execute_stream()chat_batch, chat_batch_smartcall_model_with_stats returns CallStatsexecute_stream_with_cancel() → CancelHandleEndpointExt for calling services declared in protocol manifestssrc/resilience/ + client/policy)src/types/)Message, MessageRole, MessageContent, ContentBlockToolDefinition, FunctionDefinition, ToolCallStreamingEventsrc/telemetry/)FeedbackSink / FeedbackEvent: opt-in feedback reportingsrc/utils/)routing_mvp (src/routing/): model selection + endpoint array load balancing (pure logic)interceptors (src/interceptors/): hooks around calls for logging/metrics/audituse ai_lib_rust::{AiClient, Message};
use ai_lib_rust::types::events::StreamingEvent;
use futures::StreamExt;
#[tokio::main]
async fn main() -> ai_lib_rust::Result<()> {
// Create client directly using provider/model string
// This is fully protocol-driven and supports any provider defined in ai-protocol manifests
let client = AiClient::new("anthropic/claude-3-5-sonnet").await?;
let messages = vec![Message::user("Hello!")];
// Streaming (unified events)
let mut stream = client
.chat()
.messages(messages)
.temperature(0.7)
.stream()
.execute_stream()
.await?;
while let Some(event) = stream.next().await {
match event? {
StreamingEvent::PartialContentDelta { content, .. } => print!("{content}"),
StreamingEvent::StreamEnd { .. } => break,
_ => {}
}
}
Ok(())
}
Multimodal inputs are represented as MessageContent::Blocks(Vec<ContentBlock>).
use ai_lib_rust::{Message, MessageRole};
use ai_lib_rust::types::message::{MessageContent, ContentBlock};
fn multimodal_message(image_path: &str) -> ai_lib_rust::Result<Message> {
let blocks = vec![
ContentBlock::text("Describe this image briefly."),
ContentBlock::image_from_file(image_path)?,
];
Ok(Message::with_content(
MessageRole::User,
MessageContent::blocks(blocks),
))
}
AI_PROTOCOL_DIR / AI_PROTOCOL_PATH: path to your local ai-protocol repo root (containing v1/)AI_LIB_ATTEMPT_TIMEOUT_MS: per-attempt timeout guard used by the unified policy engineAI_LIB_BATCH_CONCURRENCY: override concurrency limit for batch operationsuse ai_lib_rust::protocol::ProtocolLoader;
let loader = ProtocolLoader::new()
.with_base_path("./ai-protocol")
.with_hot_reload(true);
let manifest = loader.load_provider("openai").await?;
Add to your Cargo.toml:
[dependencies]
ai-lib-rust = "0.5.1"
tokio = { version = "1.0", features = ["full"] }
futures = "0.3"
The library automatically looks for protocol files in the following locations (in order):
ProtocolLoader::with_base_path()ai-protocol/ subdirectory (Git submodule)../ai-protocol/ (sibling directory)../../ai-protocol/ (parent's sibling)Protocol files should follow the AI-Protocol v1.5 specification structure. The runtime validates manifests against the official JSON Schema from the AI-Protocol repository.
Most providers require an API key. The runtime reads keys from (in order):
OS Keyring (optional, convenience feature)
ai-protocol, Username: provider idEnvironment Variable (recommended for production)
<PROVIDER_ID>_API_KEY (e.g. DEEPSEEK_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY)Example:
# Set API key via environment variable (recommended)
export DEEPSEEK_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
# Or use keyring (optional, for local development)
# Windows: Stored in Credential Manager
# macOS: Stored in Keychain
Provider-specific details vary, but ai-lib-rust normalizes them behind a unified client API.
AI_PROXY_URL (e.g. http://user:pass@host:port)AI_HTTP_TIMEOUT_SECS (fallback: AI_TIMEOUT_SECS)AI_LIB_MAX_INFLIGHT or use AiClientBuilder::max_inflight(n)AI_LIB_RPS (requests per second), orAI_LIB_RPM (requests per minute)AiClientBuilder::circuit_breaker_default() or env
AI_LIB_BREAKER_FAILURE_THRESHOLD (default 5)AI_LIB_BREAKER_COOLDOWN_SECS (default 30)If you need per-call stats (latency, retries, request ids, endpoint), use:
let (resp, stats) = client.call_model_with_stats(unified_req).await?;
println!("client_request_id={}", stats.client_request_id);
let (mut stream, cancel) = client.chat().messages(messages).stream().execute_stream_with_cancel().await?;
// cancel.cancel(); // emits StreamEnd{finish_reason:"cancelled"}, drops the underlying network stream, and releases inflight permit
Telemetry is opt-in. You can inject a FeedbackSink and report feedback explicitly:
use ai_lib_rust::telemetry::{FeedbackEvent, ChoiceSelectionFeedback};
client.report_feedback(FeedbackEvent::ChoiceSelection(ChoiceSelectionFeedback {
request_id: stats.client_request_id.clone(),
chosen_index: 0,
rejected_indices: None,
latency_to_select_ms: None,
ui_context: None,
candidate_hashes: None,
})).await?;
No match provider statements. All logic is derived from protocol configuration:
// The pipeline is built dynamically from protocol manifest
let pipeline = Pipeline::from_manifest(&manifest)?;
// Operators are configured via YAML, not hardcoded
// Adding a new provider requires zero code changes
Automatically handles multi-candidate scenarios through the FanOut operator:
streaming:
candidate:
candidate_id_path: "$.choices[*].index"
fan_out: true
Stateful accumulation of tool call arguments:
streaming:
accumulator:
stateful_tool_parsing: true
key_path: "$.delta.partial_json"
flush_on: "$.type == 'content_block_stop'"
Protocol configurations can be updated at runtime:
let loader = ProtocolLoader::new().with_hot_reload(true);
// Protocol changes are automatically picked up
See the examples/ directory:
basic_usage.rs: Simple non-streaming chat completiondeepseek_chat_stream.rs: Streaming chat exampledeepseek_tool_call_stream.rs: Tool calling with streamingcustom_protocol.rs: Loading custom protocol configurationslist_models.rs: Listing available models from providerservice_discovery.rs: Service discovery and custom service callstest_protocol_loading.rs: Protocol loading sanity checkcargo test
For batch execution (order-preserving), use:
use ai_lib_rust::{AiClient, ChatBatchRequest, Message};
let client = AiClient::new("deepseek/deepseek-chat").await?;
let reqs = vec![
ChatBatchRequest::new(vec![Message::user("Hello")]),
ChatBatchRequest::new(vec![Message::user("Explain SSE in one sentence")])
.temperature(0.2),
];
let results = client.chat_batch(reqs, Some(5)).await;
If you prefer a conservative default heuristic, use:
let results = client.chat_batch_smart(reqs).await;
Override concurrency with:
AI_LIB_BATCH_CONCURRENCYContributions are welcome! Please ensure that:
cargo clippyThis project is licensed under either of:
at your option.
ai-lib-rust - Where protocol meets performance. 🚀