From f0f3fee06567aaac013407f99d1997d03eb3db99 Mon Sep 17 00:00:00 2001 From: "Yifeng \"Evan\" Wang" <7312949+doodlewind@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:11:58 +0800 Subject: [PATCH] chore(pocket-pi): add a hello acceptance example (full pi says hello on gpt-5.6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A self-contained ~8.9 MB binary carrying the whole embedded pi: new() → boot on OPENAI_API_KEY → prompt → streamed reply. Doubles as the one-command acceptance check. Co-Authored-By: Claude Opus 4.8 --- README.md | 3 ++ crates/pocket-pi/examples/hello.rs | 56 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 crates/pocket-pi/examples/hello.rs diff --git a/README.md b/README.md index 04ecdd0..718a1c4 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,9 @@ extensions), TLS `rustls`+`ring` (~0.6 MB), `regex` (~0.5 MB), and QuickJS (~0.5 ## Build & test ```sh +# Acceptance: a self-contained ~8.9 MB binary carrying the whole pi says hello. +OPENAI_API_KEY=… cargo run --release --example hello # → "Hello! Nice to meet you." + # The full pi bundle is committed + embedded, so the whole suite runs with only # Rust — no Node, no build step. This includes the offline pi tests: it loads, # an extension binds to a session, sessions persist to disk. diff --git a/crates/pocket-pi/examples/hello.rs b/crates/pocket-pi/examples/hello.rs new file mode 100644 index 0000000..8e45543 --- /dev/null +++ b/crates/pocket-pi/examples/hello.rs @@ -0,0 +1,56 @@ +//! Dead-simple acceptance: boot the full, embedded pi on a real model and say +//! hello. This binary carries the WHOLE unmodified pi-coding-agent inside it. +//! +//! OPENAI_API_KEY=… cargo run --release --example hello +//! OPENAI_API_KEY=… OPENAI_MODEL=gpt-5.6 cargo run --release --example hello +use pocket_pi::{HostEvent, PiRuntime}; +use std::cell::RefCell; +use std::io::Write; +use std::rc::Rc; + +fn main() { + let key = std::env::var("OPENAI_API_KEY").expect("set OPENAI_API_KEY"); + let model = std::env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-5.6".into()); + + // The whole pi is embedded — new() stands it up with no external files. + let mut rt = PiRuntime::new().expect("runtime"); + + let reply = Rc::new(RefCell::new(String::new())); + let r = reply.clone(); + rt.on_event(move |ev: &HostEvent| match ev.kind.as_str() { + "text" => { + if let Some(d) = ev.value.get("delta").and_then(|v| v.as_str()) { + r.borrow_mut().push_str(d); + print!("{d}"); + std::io::stdout().flush().ok(); + } + } + "assistant_text" if r.borrow().is_empty() => { + if let Some(t) = ev.value.get("text").and_then(|v| v.as_str()) { + *r.borrow_mut() = t.to_string(); + print!("{t}"); + } + } + "error" => eprintln!("\n[error] {}", ev.raw), + _ => {} + }); + + let cfg = serde_json::json!({ + "provider": "openai", "model": model, "apiKey": key, + "maxTokens": 2048, + "systemPrompt": "You are a friendly assistant. Be very brief." + }); + eprintln!("booting full pi on {model} …"); + rt.boot(&cfg.to_string()).expect("boot"); + eprint!("pi > "); + rt.prompt("Say hello in a few words.").expect("prompt"); + while !rt.is_idle() { + rt.pump().expect("pump"); + std::thread::sleep(std::time::Duration::from_millis(50)); + } + println!(); + if reply.borrow().trim().is_empty() { + eprintln!("(no reply — check OPENAI_API_KEY / proxy)"); + std::process::exit(1); + } +}