xai-grpc-client 0.4.3

Feature-complete gRPC client for xAI's Grok API with streaming, tools, multimodal support
Documentation

xai-grpc-client

CI Crates.io Documentation License Rust

Unofficial Rust client for xAI's Grok API with full gRPC support.

Features

  • ๐Ÿš€ Async/await API - Built on tokio and tonic for high performance
  • ๐Ÿ”’ Type-safe - Strongly typed request builders with compile-time guarantees
  • ๐Ÿ“ก Streaming support - Real-time response streaming with tokio-stream
  • ๐Ÿ”ง Tool calling - Function calling with 7 tool types (function, web search, X search, MCP, etc.)
  • ๐Ÿ–ผ๏ธ Multimodal - Text and image inputs for vision capabilities
  • ๐Ÿง  Advanced features - Log probabilities, reasoning traces, deferred completions
  • ๐Ÿ“‹ Model discovery - List available models with pricing and capabilities
  • ๐Ÿ”ข Embeddings - Generate vector representations from text and images
  • ๐Ÿ”ค Tokenization - Count tokens for cost estimation and prompt optimization
  • ๐Ÿ”‘ API key management - Check API key status and permissions
  • ๐ŸŽจ Image generation - Create images from text prompts
  • ๐Ÿ“š Document search - RAG support with collection search
  • ๐Ÿ” Secure by default - Uses secrecy crate to protect API keys in memory
  • โœ… Complete - 100% coverage of Grok API (19/19 RPCs)
  • ๐Ÿงช Well-tested - 98 unit tests covering all core modules

Installation

Add this to your Cargo.toml:

[dependencies]
xai-grpc-client = "0.4"
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1"

TLS Configuration

The crate provides flexible TLS configuration through feature flags for root certificate selection:

Default (webpki-roots - recommended for containers):

[dependencies]
xai-grpc-client = "0.4"

Using native system roots (recommended for development):

[dependencies]
xai-grpc-client = { version = "0.4", features = ["tls-native-roots"], default-features = false }

Using both root stores (if unsure):

[dependencies]
xai-grpc-client = { version = "0.4", features = ["tls-roots"], default-features = false }

Available features:

  • tls-webpki-roots (default) - Uses Mozilla's root certificates (works in containers/distroless)
  • tls-native-roots - Uses the system's native certificate store
  • tls-roots - Enables both root stores simultaneously

Advanced: Custom TLS Configuration

For advanced use cases (custom CA certificates, proxies, custom timeouts), use the with_channel() constructor:

use xai_grpc_client::{GrokClient, Channel, ClientTlsConfig, Certificate};
use secrecy::SecretString;
use std::time::Duration;

// Load custom CA certificate
let ca_cert = std::fs::read("path/to/ca.pem")?;
let ca = Certificate::from_pem(ca_cert);

// Configure TLS with custom CA
let tls_config = ClientTlsConfig::new()
    .ca_certificate(ca)
    .domain_name("api.x.ai");

// Build custom channel
let channel = Channel::from_static("https://api.x.ai")
    .timeout(Duration::from_secs(120))
    .tls_config(tls_config)?
    .connect()
    .await?;

// Create client with custom channel
let api_key = SecretString::from("your-key".to_string());
let client = GrokClient::with_channel(channel, api_key);

See the custom_tls example for more details.

Quick Start

use xai_grpc_client::{GrokClient, ChatRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize client from GROK_API_KEY environment variable
    let mut client = GrokClient::from_env().await?;

    // Create a simple chat request
    let request = ChatRequest::new()
        .user_message("What is the meaning of life?")
        .with_model("grok-2-1212")
        .with_max_tokens(100);

    // Get response
    let response = client.complete_chat(request).await?;
    println!("{}", response.content);

    Ok(())
}

Set your API key:

export GROK_API_KEY="your-api-key-here"

Examples

Streaming Chat

Stream responses in real-time:

use xai_grpc_client::{GrokClient, ChatRequest};
use tokio_stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = ChatRequest::new()
        .user_message("Write a short poem about Rust");

    let mut stream = client.stream_chat(request).await?;

    while let Some(chunk) = stream.next().await {
        let chunk = chunk?;
        print!("{}", chunk.delta);
    }

    Ok(())
}

Tool Calling (Function Calling)

Enable the model to call functions:

use xai_grpc_client::{GrokClient, ChatRequest, FunctionTool, Tool, ToolChoice};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    // Define a function tool
    let get_weather = FunctionTool::new(
        "get_weather",
        "Get the current weather in a location"
    )
    .with_parameters(json!({
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "City name"
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"]
            }
        },
        "required": ["location"]
    }));

    let request = ChatRequest::new()
        .user_message("What's the weather in Tokyo?")
        .add_tool(Tool::Function(get_weather))
        .with_tool_choice(ToolChoice::Auto);

    let response = client.complete_chat(request).await?;

    // Check if model called the tool
    if !response.tool_calls.is_empty() {
        for tool_call in &response.tool_calls {
            println!("Function: {}", tool_call.function.name);
            println!("Arguments: {}", tool_call.function.arguments);
        }
    }

    Ok(())
}

Multimodal (Vision)

Send images with your prompts:

use xai_grpc_client::{GrokClient, ChatRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = ChatRequest::new()
        .user_with_image(
            "What's in this image?",
            "https://example.com/image.jpg"
        )
        .with_model("grok-2-vision-1212");

    let response = client.complete_chat(request).await?;
    println!("{}", response.content);

    Ok(())
}

Web Search

Enable web search for up-to-date information:

use xai_grpc_client::{GrokClient, ChatRequest, Tool, WebSearchTool};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = ChatRequest::new()
        .user_message("What are the latest developments in AI?")
        .add_tool(Tool::WebSearch(WebSearchTool::new()));

    let response = client.complete_chat(request).await?;

    println!("Response: {}", response.content);

    if !response.citations.is_empty() {
        println!("\nSources:");
        for citation in &response.citations {
            println!("  - {}", citation);
        }
    }

    Ok(())
}

Model Listing

List available models and get pricing information:

use xai_grpc_client::GrokClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    // List all available models
    let models = client.list_models().await?;
    for model in models {
        println!("{}: {} (max {} tokens)",
            model.name, model.version, model.max_prompt_length);

        // Calculate cost for a request
        let cost = model.calculate_cost(10000, 1000, 0);
        println!("  Example cost: ${:.4}", cost);
    }

    // Get specific model details
    let model = client.get_model("grok-2-1212").await?;
    println!("Model: {} v{}", model.name, model.version);

    Ok(())
}

Embeddings

Generate vector embeddings from text or images:

use xai_grpc_client::{GrokClient, EmbedRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = EmbedRequest::new("embed-large-v1")
        .add_text("Hello, world!")
        .add_text("How are you?");

    let response = client.embed(request).await?;

    for embedding in response.embeddings {
        println!("Embedding {} has {} dimensions",
            embedding.index, embedding.vector.len());
    }

    Ok(())
}

Tokenization

Count tokens before making requests for cost estimation:

use xai_grpc_client::{GrokClient, TokenizeRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = TokenizeRequest::new("grok-2-1212")
        .with_text("Hello, world! How are you today?");

    let response = client.tokenize(request).await?;

    println!("Token count: {}", response.token_count());
    println!("Tokens:");
    for token in &response.tokens {
        println!("  '{}' (ID: {})", token.string_token, token.token_id);
    }

    // Calculate cost
    let model = client.get_model("grok-2-1212").await?;
    let cost = model.calculate_cost(response.token_count() as u32, 1000, 0);
    println!("Estimated cost: ${:.4}", cost);

    Ok(())
}

API Key Information

Check your API key status and permissions:

use xai_grpc_client::GrokClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;
    let info = client.get_api_key_info().await?;

    println!("API Key: {}", info.redacted_api_key);
    println!("Team ID: {}", info.team_id);
    println!("Status: {}", info.status_string());

    if !info.is_active() {
        println!("โš ๏ธ  Warning: API key is not active!");
    }

    println!("\nPermissions:");
    for acl in &info.acls {
        println!("  - {}", acl);
    }

    Ok(())
}

Advanced: Deferred Completions

For long-running tasks, start a deferred completion and poll for results:

use xai_grpc_client::{GrokClient, ChatRequest};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = ChatRequest::new()
        .user_message("Write a detailed analysis of quantum computing")
        .with_reasoning_effort(ReasoningEffort::High);

    // Start deferred completion
    let request_id = client.start_deferred(request).await?;
    println!("Started deferred completion: {}", request_id);

    // Wait for completion with polling
    let response = client.wait_for_deferred(
        request_id,
        Duration::from_secs(2),  // poll interval
        Duration::from_secs(300) // timeout
    ).await?;

    println!("{}", response.content);

    Ok(())
}

CompletionOptions (for trait abstraction)

Use CompletionOptions to create reusable configurations:

use xai_grpc_client::{GrokClient, ChatRequest, CompletionOptions, Message, MessageContent};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    // Define reusable options
    let options = CompletionOptions::new()
        .with_model("grok-2-1212")
        .with_temperature(0.7)
        .with_max_tokens(500);

    // Use with different message sets
    let messages = vec![
        Message::System("You are a helpful coding assistant".to_string()),
        Message::User(MessageContent::Text("Explain Rust ownership".into()))
    ];

    let request = ChatRequest::from_messages_with_options(messages, options);
    let response = client.complete_chat(request).await?;

    println!("{}", response.content);

    Ok(())
}

API Coverage

This library implements 100% (19/19) of the xAI Grok API services! ๐ŸŽ‰

โœ… Fully Implemented Services

Chat Service (6/6 RPCs)

  • โœ… GetCompletion - Blocking chat completions
  • โœ… GetCompletionChunk - Streaming chat completions
  • โœ… StartDeferredCompletion - Async completion handling
  • โœ… GetDeferredCompletion - Poll deferred completions
  • โœ… GetStoredCompletion - Retrieve stored completions
  • โœ… DeleteStoredCompletion - Delete stored completions

Models Service (6/6 RPCs)

  • โœ… ListLanguageModels - List all language models
  • โœ… GetLanguageModel - Get language model details
  • โœ… ListEmbeddingModels - List all embedding models
  • โœ… GetEmbeddingModel - Get embedding model details
  • โœ… ListImageGenerationModels - List image generation models
  • โœ… GetImageGenerationModel - Get image model details

Embeddings Service (1/1 RPCs)

  • โœ… Embed - Generate embeddings from text/images

Tokenize Service (1/1 RPCs)

  • โœ… TokenizeText - Count tokens for cost estimation

Auth Service (1/1 RPCs)

  • โœ… GetApiKeyInfo - Get API key status and permissions

Sample Service (2/2 RPCs)

  • โœ… SampleText - Simpler text completion API
  • โœ… SampleTextStreaming - Streaming text sampling

Image Service (1/1 RPCs)

  • โœ… GenerateImage - Generate images from text prompts

Documents Service (1/1 RPCs)

  • โœ… Search - Search documents/collections for RAG

Feature Summary

  • โœ… Chat & Completions: Complete - All chat methods including streaming, deferred, and stored
  • โœ… Embeddings: Complete - Text and image embedding generation
  • โœ… Models: Complete - Full model discovery for language, embedding, and image models
  • โœ… Tokenization: Complete - Token counting for all models
  • โœ… Auth: Complete - API key information and validation
  • โœ… Image Generation: Complete - Text-to-image and image-to-image generation
  • โœ… Document Search: Complete - RAG with collection-based search
  • โœ… Sample API: Complete - Alternative simple text completion interface
  • โœ… Tool Calling: 7 tool types (function, web search, X search, MCP, collections, documents, code execution)
  • โœ… Multimodal: Text and image inputs for vision models
  • โœ… Advanced Features: Log probabilities, reasoning effort, JSON output, stop sequences, seed

Error Handling

The library provides comprehensive error handling with retry logic:

use xai_grpc_client::{GrokClient, ChatRequest, GrokError};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = GrokClient::from_env().await?;

    let request = ChatRequest::new()
        .user_message("Hello!");

    match client.complete_chat(request).await {
        Ok(response) => println!("{}", response.content),
        Err(GrokError::RateLimit { retry_after_secs }) => {
            println!("Rate limited. Retry after {} seconds", retry_after_secs);
        }
        Err(GrokError::Auth(msg)) => {
            println!("Authentication error: {}", msg);
        }
        Err(e) if e.is_retryable() => {
            println!("Retryable error: {}", e);
        }
        Err(e) => {
            println!("Error: {}", e);
        }
    }

    Ok(())
}

Configuration

From Environment Variable

let client = GrokClient::from_env().await?;

Requires GROK_API_KEY environment variable.

Manual Configuration

use xai_grpc_client::{GrokClient, GrokConfig};
use secrecy::SecretString;
use std::time::Duration;

let config = GrokConfig {
    endpoint: "https://api.x.ai".to_string(),
    api_key: SecretString::from("your-api-key".to_string()),
    default_model: "grok-2-1212".to_string(),
    timeout: Duration::from_secs(120),
};

let client = GrokClient::new(config).await?;

Available Models

  • grok-2-1212 - Latest Grok 2 (December 2024)
  • grok-2-vision-1212 - Grok 2 with vision capabilities
  • grok-beta - Beta model with experimental features

Check xAI's documentation for the latest model list.

Testing

Run the test suite:

# Unit tests (no API key required)
cargo test --lib

# Integration tests (requires GROK_API_KEY)
cargo test --test integration_test

# Run specific test
cargo test test_chat_request_builder

The library includes 77 comprehensive unit tests covering:

  • Request building and validation
  • Response parsing
  • Error handling and retry logic
  • Tool configuration
  • Multimodal messages
  • Model information and pricing
  • Embedding generation
  • Tokenization
  • API key management

Examples

See the examples/ directory for more complete examples:

# Simple chat
cargo run --example simple_chat

# Streaming
cargo run --example streaming_chat

# Tool calling
cargo run --example tool_calling

# Multimodal
cargo run --example multimodal

# Model listing
cargo run --example list_models

# Embeddings
cargo run --example embeddings

# Tokenization
cargo run --example tokenize

# Custom TLS configuration
cargo run --example custom_tls

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repository with submodules
git clone --recursive https://github.com/fpinsight/xai-grpc-client
cd xai-grpc-client

# Or if you already cloned without --recursive:
git submodule update --init --recursive

cargo build
cargo test

Note: This project uses a Git submodule for proto definitions. The xai-proto submodule must be initialized before building.

License

Licensed under either of:

at your option.

Disclaimer

This is an unofficial client library and is not affiliated with or endorsed by xAI. Use at your own risk.

Links

Changelog

See CHANGELOG.md for release history.