Developer Tools
plungeai (Rust)
The official Rust SDK for the One API — async client over reqwest, serde-typed resources, streaming, pagination, retries and errors, generated from the OpenAPI document.
The official Rust SDK for the PlungeAI One API (https://api.plungeai.com). Targets One API 2.4.0; crate version 1.0.0. The resource methods and their types are generated from the API's own OpenAPI document (GET /v1/openapi.json). Async over reqwest (rustls) on the tokio runtime.
Release status: cargo search plungeai; until it answers, depend on the path sdk/one-api-rust/ in the platform repository.
Installation
cargo add plungeai tokio --features tokio/fullUsage
use plungeai::{types::*, Client};
#[tokio::main]
async fn main() -> Result<(), plungeai::Error> {
let client = Client::new()?; // reads PLUNGEAI_API_KEY; or Client::builder().api_key("ozk_…").build()?
let me = client.whoami().await?;
let run = client
.agents()
.execute("calculator-agent", &AgentExecuteParams { prompt: Some("6*7".into()), ..Default::default() })
.await?;
let hits = client
.discovery()
.search(&DiscoverySearchParams { q: Some("web search".into()), kind: Some(DiscoverySearchParamsKind::Agents), ..Default::default() })
.await?;
Ok(())
}Resources are methods on the client (client.agents(), client.workflows().versions()); every operation takes its path parameters first, then a reference to its params struct (plungeai::types::<Resource><Method>Params, all Default). Params and responses are serde structs under plungeai::types; optional fields are Option<T>; response enums carry an Unknown variant, so a new server value never breaks a client.
Streaming responses
use futures_util::StreamExt;
let mut stream = client
.workflows()
.execute_inline_stream(&WorkflowExecuteInlineStreamParams { workflow: workflow.into(), ..Default::default() })
.await?;
while let Some(event) = stream.next().await {
let event = event?;
if event.event.as_deref() == Some("token") {
let token: serde_json::Value = event.json()?;
print!("{}", token["delta"]);
}
}event.data is the raw string of the server-sent event; event.json::<T>() parses it. Dropping the stream cancels the request. Methods that accept stream: true on the API have a …_stream twin (client.agents().execute_stream, client.chat().completions().create_stream) returning the same Stream.
Auto-pagination
let mut items = client.workflows().list(&WorkflowListParams { kind: Some(WorkflowListParamsKind::Bot), ..Default::default() }).await?.into_stream();
while let Some(w) = items.next().await {
println!("{:?}", w?);
}
let mut page = client.workflows().list(&WorkflowListParams { limit: Some(50), ..Default::default() }).await?;
loop {
for w in &page.data {
println!("{w:?}");
}
if !page.has_next_page() {
break;
}
page = page.next_page().await?;
}Handling errors
match client.agents().execute("calculator-agent", &AgentExecuteParams { prompt: Some("6*7".into()), ..Default::default() }).await {
Ok(run) => println!("{run:?}"),
Err(plungeai::Error::Api(err)) if err.status == 424 => println!("{:?}", err.connect.map(|c| c.link)),
Err(plungeai::Error::Api(err)) => println!("{} {:?} {:?}", err.status, err.code, err.request_id),
Err(err) => return Err(err),
}One enum, plungeai::Error: Api(Box<ApiError>) for a response with an error status (status, code — the envelope's error.code, message, request_id — the x-request-id header, and connect on 424 with provider, method, link), Connection for a request that never got a response, Timeout, and Decode for a body that does not match its type. Predicates err.status(), err.is_not_found(), err.is_rate_limit() and so on classify without matching.
Retries
Failed requests are retried 2 times by default with short exponential backoff (honouring Retry-After): connection errors, timeouts, 408, 429 and ≥ 500. 409 and 424 are never retried — on the One API they are semantic answers (approval_required, duplicate_execution_id, a missing connection), and a retried execute would start a second run. Set the count on the builder or per call:
let client = Client::builder().max_retries(0).build()?;
client.with_options(RequestOptions::new().max_retries(5)).agents().execute("calculator-agent", ¶ms).await?;RequestOptions::new().idempotency_key(key) is sent as x-trace-id, the API's idempotency key: it becomes the run's execution id, is re-sent on every retry of the call, and a replay answers 409 duplicate_execution_id.
Timeouts
Each attempt times out after 10 minutes by default and fails with Error::Timeout (retried as above). Set it on the builder or per call: Client::builder().timeout(Duration::from_secs(20)), RequestOptions::new().timeout(Duration::from_secs(5)).
Raw responses
let response = client.agents().execute_raw("calculator-agent", ¶ms).await?;
println!("{} {:?}", response.status, response.request_id);
let run: AgentExecuteResponse = response.parse()?;client.with_options(RequestOptions) (header, query, timeout, max_retries, idempotency_key) returns a client whose every call carries those options, and every JSON operation has a …_raw form returning the response with its status, headers, request_id and body bytes. client.get(path), client.post(path, &body) and friends reach routes the generated layer does not cover.
Models plane
client.chat().completions().create(...) (plus client.embeddings() and client.models()) covers the OpenAI-compatible routes here; any OpenAI client also works with base URL https://api.plungeai.com/v1 and the same key.
Requirements
Rust 1.85 or newer (edition 2021); tokio runtime. TLS through rustls — no system OpenSSL needed.