Skip to content

Latest commit

 

History

History
221 lines (174 loc) · 9.46 KB

File metadata and controls

221 lines (174 loc) · 9.46 KB

SDK Cookbook and Migration Guide

This guide is for teams embedding Pi as a Rust library. The Rust SDK provides idiomatic Rust APIs that deliver equivalent functionality to the TypeScript SDK, using Rust-native patterns like Result types, structured concurrency, and zero-copy operations.

Note: This SDK is an idiomatic Rust companion to the pi-mono TypeScript SDK, not a drop-in equivalent. The APIs provide functional equivalence while following Rust ecosystem conventions.

Install

[dependencies]
pi = { path = "." }
futures = "0.3"

SemVer Surface

The supported library surface is the crate root aliases pi::Error, pi::PiResult, and the pi::sdk module. Other root modules are implementation details for the CLI, examples, and in-repository tests; they are hidden from the published API documentation and may change without SemVer guarantees.

The semver GitHub Actions workflow runs cargo-semver-checks on PRs and main pushes that touch the SDK/API surface. It compares the current public API to the PR target branch or previous push baseline. Removing or changing a stable item below requires a major version bump; additive changes remain minor-version compatible.

Stability Annotations

Item Stability Notes
pi::Error Stable Crate-root error type alias target.
pi::PiResult Stable Crate-root result alias for pi::Error.
pi::sdk::{Error, Result} Stable SDK error/result exports.
pi::sdk::{AbortHandle, AbortSignal} Stable Prompt cancellation handles.
pi::sdk::{Agent, AgentConfig, AgentEvent, AgentSession, QueueMode} Stable In-process agent/session integration exports.
pi::sdk::{AssistantMessage, ContentBlock, Cost, CustomMessage, ImageContent, Message, StopReason, StreamEvent, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserContent, UserMessage} Stable Message, content, streaming, and accounting model types.
pi::sdk::{Config, ExtensionManager, ExtensionPolicy, ExtensionRegion, Session, ThinkingLevel} Stable Configuration, extension, session, and thinking-control exports.
pi::sdk::{InputType, Model, ModelCost, Provider, ProviderContext, ProviderThinkingBudgets, StreamOptions, ToolDef} Stable Provider integration exports.
pi::sdk::{ModelEntry, ModelRegistry} Stable Model registry exports.
pi::sdk::{Tool, ToolDefinition, ToolOutput, ToolRegistry, ToolUpdate} Stable Tool integration exports.
pi::sdk::BUILTIN_TOOL_NAMES Stable Canonical built-in tool-name inventory.
pi::sdk::{create_read_tool, create_bash_tool, create_edit_tool, create_write_tool, create_grep_tool, create_find_tool, create_ls_tool, create_hashline_edit_tool, create_all_tools} Stable Built-in tool constructors.
pi::sdk::{tool_to_definition, all_tool_definitions} Stable Tool schema helpers.
pi::sdk::{SubscriptionId, EventListeners, EventSubscriber, OnStreamEvent, OnToolEnd, OnToolStart} Stable Event subscription and hook types.
pi::sdk::{SessionOptions, ToolFactory, default_tool_registry} Stable In-process session construction and custom tool registry extension points.
pi::sdk::{AgentSessionHandle, AgentSessionState, create_agent_session} Stable Primary in-process SDK entry point and state handle.
pi::sdk::{SessionPromptResult, SessionTransport, SessionTransportEvent, SessionTransportState} Stable Unified in-process/RPC transport adapter.
pi::sdk::{RpcTransportClient, RpcTransportOptions} Stable Subprocess RPC transport client.
pi::sdk::{RpcBashResult, RpcCancelledResult, RpcCommandInfo, RpcCompactionResult, RpcCycleModelResult, RpcExportHtmlResult, RpcExtensionUiResponse, RpcForkMessage, RpcForkResult, RpcLastAssistantText, RpcModelInfo, RpcSessionState, RpcSessionStats, RpcThinkingLevelResult, RpcTokenStats} Stable RPC request/response payloads.

Migration Map (TypeScript -> Rust)

TypeScript surface Rust SDK surface
createAgentSession(options) pi::sdk::create_agent_session(SessionOptions)
session.prompt(text, onEvent) AgentSessionHandle::prompt(text, on_event)
session.subscribe(listener) AgentSessionHandle::subscribe(listener)
unsubscribe() AgentSessionHandle::unsubscribe(subscription_id)
session.setModel(provider, model) AgentSessionHandle::set_model(provider, model)
session.setThinkingLevel(level) AgentSessionHandle::set_thinking_level(level)
session.compact() AgentSessionHandle::compact(on_event)
session.abort() AgentSessionHandle::new_abort_handle() + prompt_with_abort(...)
session.steer(...), session.followUp(...) RpcTransportClient::steer(...), RpcTransportClient::follow_up(...)
RPC bridge client RpcTransportClient / SessionTransport::RpcSubprocess

Recipe 1: Create In-Process Session and Prompt

use futures::executor::block_on;
use pi::sdk::{AgentEvent, SessionOptions, create_agent_session};

fn main() -> pi::sdk::Result<()> {
    let mut session = block_on(create_agent_session(SessionOptions {
        provider: Some("openai".to_string()),
        model: Some("gpt-4o".to_string()),
        api_key: Some(std::env::var("OPENAI_API_KEY").unwrap_or_default()),
        no_session: true,
        ..SessionOptions::default()
    }))?;

    let message = block_on(session.prompt("Summarize src/sdk.rs", |event: AgentEvent| {
        eprintln!("{event:?}");
    }))?;

    println!("{message:#?}");
    Ok(())
}

Recipe 2: Session-Level Subscribers and Typed Hooks

use futures::executor::block_on;
use pi::sdk::{SessionOptions, create_agent_session};
use std::sync::Arc;

fn main() -> pi::sdk::Result<()> {
    let options = SessionOptions {
        on_tool_start: Some(Arc::new(|tool, args| eprintln!("tool start: {tool} {args}"))),
        on_tool_end: Some(Arc::new(|tool, output, is_error| {
            eprintln!("tool end: {tool}, error={is_error}, output={output:?}");
        })),
        on_stream_event: Some(Arc::new(|ev| eprintln!("stream: {ev:?}"))),
        ..SessionOptions::default()
    };

    let mut session = block_on(create_agent_session(options))?;
    let sub_id = session.subscribe(|event| eprintln!("session event: {event:?}"));

    let _ = block_on(session.prompt("read Cargo.toml", |_| {}))?;
    let _removed = session.unsubscribe(sub_id);
    Ok(())
}

Recipe 3: Prompt Cancellation

use futures::executor::block_on;
use pi::sdk::{AgentSessionHandle, SessionOptions, create_agent_session};

fn main() -> pi::sdk::Result<()> {
    let mut session = block_on(create_agent_session(SessionOptions::default()))?;

    let (abort_handle, abort_signal) = AgentSessionHandle::new_abort_handle();
    let fut = session.prompt_with_abort("long running prompt", abort_signal, |_| {});
    abort_handle.abort();
    let _ = block_on(fut);
    Ok(())
}

Recipe 4: Model and Thinking Controls

use futures::executor::block_on;
use pi::sdk::{SessionOptions, ThinkingLevel, create_agent_session};

fn main() -> pi::sdk::Result<()> {
    let mut session = block_on(create_agent_session(SessionOptions::default()))?;
    block_on(session.set_model("openai", "gpt-4o"))?;
    block_on(session.set_thinking_level(ThinkingLevel::Low))?;

    let state = block_on(session.state())?;
    println!("provider={} model={}", state.provider, state.model_id);
    Ok(())
}

Recipe 5: Load Extensions in SDK Sessions

use futures::executor::block_on;
use pi::sdk::{SessionOptions, create_agent_session};
use std::path::PathBuf;

fn main() -> pi::sdk::Result<()> {
    let session = block_on(create_agent_session(SessionOptions {
        extension_paths: vec![PathBuf::from("extensions/my_extension.js")],
        extension_policy: Some("safe".to_string()),
        repair_policy: Some("ask".to_string()),
        ..SessionOptions::default()
    }))?;

    if session.has_extensions() {
        eprintln!("extensions loaded");
    }
    Ok(())
}

Recipe 6: Use RPC Transport Client

use futures::executor::block_on;
use pi::sdk::{RpcTransportClient, RpcTransportOptions};

fn main() -> pi::sdk::Result<()> {
    let mut rpc = RpcTransportClient::connect(RpcTransportOptions::default())?;

    let state = block_on(rpc.get_state())?;
    println!("rpc session id: {}", state.session_id);

    let events = block_on(rpc.prompt("Hello from RPC"))?;
    println!("received {} rpc events", events.len());

    rpc.shutdown()?;
    Ok(())
}

Recipe 7: Unified Transport Adapter (In-Process or RPC)

use futures::executor::block_on;
use pi::sdk::{SessionOptions, SessionTransport};

fn main() -> pi::sdk::Result<()> {
    let mut transport = block_on(SessionTransport::in_process(SessionOptions::default()))?;

    let _result = block_on(transport.prompt("Status?", |_event| {}))?;
    let _state = block_on(transport.state())?;
    transport.shutdown()?;
    Ok(())
}

Compatibility Notes for Migrating Integrators

  • SessionOptions::default().no_session is true (ephemeral by default).
  • In-process AgentSessionHandle currently exposes prompt/state/model/thinking/compaction flows; queue controls like steer/follow_up are on RpcTransportClient.
  • SessionTransport::prompt returns SessionPromptResult, which is InProcess(AssistantMessage) or RpcEvents(Vec<Value>) depending on backend.
  • Extension loading is opt-in via extension_paths, with extension_policy/repair_policy controls.

Verified Reference Surfaces

  • src/sdk.rs
  • tests/sdk_api.rs
  • tests/sdk_unit.rs
  • tests/sdk_integration.rs