SDK quickstart: Rust
The async-openai crate, pointed at our endpoint. Tokio runtime, full type safety, streaming via Streams.
Cargo dependencies
# Cargo.toml
[dependencies]
async-openai = "0.27"
tokio = { version = "1", features = ["full"] }
futures-util = "0.3"
Hello world
use async_openai::{config::OpenAIConfig, Client};
use async_openai::types::{ChatCompletionRequestMessage, ChatCompletionRequestUserMessage,
CreateChatCompletionRequestArgs, Role};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = OpenAIConfig::new()
.with_api_base("https://api.greenjoules.cloud/v1")
.with_api_key(std::env::var("JC_API_KEY")?);
let client = Client::with_config(config);
let request = CreateChatCompletionRequestArgs::default()
.model("auto")
.messages([ChatCompletionRequestMessage::User(
ChatCompletionRequestUserMessage {
content: "hi".into(),
..Default::default()
},
)])
.build()?;
let response = client.chat().create(request).await?;
println!("{}", response.choices[0].message.content.as_deref().unwrap_or(""));
Ok(())
}
Streaming
use futures_util::StreamExt;
let mut stream = client.chat().create_stream(request).await?;
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
for choice in chunk.choices {
if let Some(content) = choice.delta.content {
print!("{content}");
}
}
}
Embeddings
use async_openai::types::{CreateEmbeddingRequestArgs, EmbeddingInput};
let req = CreateEmbeddingRequestArgs::default()
.model("text-embedding-3-small")
.input(EmbeddingInput::StringArray(vec!["one".into(), "two".into()]))
.build()?;
let r = client.embeddings().create(req).await?;
for d in r.data {
println!("dim={} first6={:?}", d.embedding.len(), &d.embedding[..6]);
}
Joule headers
async-openai consumes the headers internally; to surface them, drop down a layer to reqwest directly for that specific call, or open a feature-request issue. The headers are also on the receipt accessible via jc receipt get rcpt_….
Errors
use async_openai::error::OpenAIError;
match client.chat().create(request).await {
Ok(r) => println!("{}", r.choices[0].message.content.as_deref().unwrap_or("")),
Err(OpenAIError::ApiError(api_err)) if api_err.r#type == "insufficient_balance" => {
eprintln!("top up at portal.greenjoules.cloud");
}
Err(e) => Err(e)?,
}
Native CLI calls instead
If you're writing CLI tooling in Rust, the jc binary calls our API directly — you can shell out to it instead of pulling async-openai if you only need workloads + object store + receipts. See CLI reference.