The Chalk Rust client library provides a convenient way to query features from your Rust applications.


Installation

Add the Chalk Rust client to your Cargo.toml:

[dependencies]
chalk-client = "*"

Or install using cargo:

cargo add chalk-client

Basic Usage

Here’s a quick example of how to use the Chalk Rust client:

use chalk_client::ChalkClient;
use chalk_client::types::QueryOptions;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the client
    let client = ChalkClient::new()
        .client_id("your-client-id")
        .client_secret("your-client-secret")
        .environment("your-environment-id")
        .build()
        .await?;

    // Query features for a single entity
    let inputs = HashMap::from([("user.id".to_string(), serde_json::json!(1))]);
    let outputs = vec![
        "user.credit_score".to_string(),
        "user.account_age_days".to_string(),
    ];

    let response = client.query(inputs, outputs, QueryOptions::default()).await?;

    for feature in &response.data {
        println!("{}: {:?}", feature.field, feature.value);
    }
    Ok(())
}

For lower-latency, higher-throughput workloads, Chalk also offers a gRPC client (ChalkGrpcClient) that speaks Protocol Buffers over HTTP/2. Here’s a bulk online query, where inputs and results are exchanged as Arrow IPC (“feather”) data:

use chalk_client::ChalkGrpcClient;
use chalk_client::gen::chalk::common::v1::online_query_bulk_request::Inputs;
use chalk_client::gen::chalk::common::v1::output_expr::Expr;
use chalk_client::gen::chalk::common::v1::{OnlineQueryBulkRequest, OutputExpr};

use arrow::array::Int64Array;
use arrow::datatypes::{DataType, Field, Schema};
use arrow::ipc::reader::FileReader;
use arrow::ipc::writer::FileWriter;
use arrow::record_batch::RecordBatch;
use std::io::Cursor;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // gRPC enforces a 4 MiB limit when decoding responses by default. Bulk
    // queries can exceed that, so raise it to fit your largest result set.
    let client = ChalkGrpcClient::new()
        .client_id("your-client-id")
        .client_secret("your-client-secret")
        .environment("your-environment-id")
        .max_decoding_message_size(256 * 1024 * 1024) // 256 MiB
        .build()
        .await?;

    // Inputs are an Arrow record batch (one row per entity), serialized to
    // Arrow IPC ("feather") bytes.
    let schema = Arc::new(Schema::new(vec![Field::new("user.id", DataType::Int64, false)]));
    let batch = RecordBatch::try_new(schema, vec![Arc::new(Int64Array::from(vec![1, 2, 3]))])?;

    let mut feather = Vec::new();
    {
        let mut writer = FileWriter::try_new(&mut feather, &batch.schema())?;
        writer.write(&batch)?;
        writer.finish()?;
    }

    let response = client
        .query_bulk_proto(OnlineQueryBulkRequest {
            inputs: Some(Inputs::InputsFeather(feather)),
            outputs: vec![
                OutputExpr { expr: Some(Expr::FeatureFqn("user.name".to_string())) },
                OutputExpr { expr: Some(Expr::FeatureFqn("user.age".to_string())) },
            ],
            ..Default::default()
        })
        .await?;

    // Results come back as Arrow IPC bytes in `scalars_data`.
    if !response.scalars_data.is_empty() {
        let reader = FileReader::try_new(Cursor::new(&response.scalars_data), None)?;
        for batch in reader {
            let batch = batch?;
            println!("{} rows x {} cols", batch.num_rows(), batch.num_columns());
        }
    }
    for err in &response.errors {
        eprintln!("error: {}", err.message);
    }

    Ok(())
}

Next Steps