Skip to main content

HTTP

An inferlet can issue HTTP requests to external services from inside the sandbox. This page covers the request and response shape. Read this after I/O overview.

Pie SDK status

HTTP is fully supported in the Rust SDK today via the wstd::http crate. The Python and JavaScript SDKs do not yet bind WASI HTTP. The non-Rust tabs below show the intended API shape; the runtime support is in progress. For a working three-language tutorial against this API shape, see Tutorial: build the agent.

Single request

use inferlet::wstd::http::{Client, Method, Request};
use inferlet::wstd::io::{empty, AsyncRead};
use inferlet::Result;

async fn fetch(url: &str) -> Result<Vec<u8>> {
let client = Client::new();
let req = Request::builder()
.uri(url)
.method(Method::GET)
.body(empty())
.map_err(|e| e.to_string())?;

let resp = client.send(req).await.map_err(|e| e.to_string())?;
let mut buf = Vec::new();
resp.into_body()
.read_to_end(&mut buf)
.await
.map_err(|e| e.to_string())?;
Ok(buf)
}

The Rust path uses WASI HTTP through wstd::http::Client. Connections multiplex automatically: many concurrent requests reuse the underlying transport without you managing it.

Concurrent requests

Issue many requests in parallel with the language's standard async primitive.

use futures::future;

let urls = vec!["https://a.example/", "https://b.example/", "https://c.example/"];
let results = future::join_all(urls.iter().map(|u| fetch(u))).await;

Concurrent fetches share the inferlet's event loop. The model's forward passes do not stall on HTTP: while requests are in flight, g.next()? continues to schedule passes.

JSON request and response

A common pattern: POST a JSON body, read JSON back.

use inferlet::wstd::http::{body::IntoBody, Client, Method, Request};
use inferlet::wstd::io::AsyncRead;

async fn post_json(url: &str, body: &serde_json::Value) -> Result<serde_json::Value> {
let client = Client::new();
let req = Request::builder()
.uri(url)
.method(Method::POST)
.header("content-type", "application/json")
.body(body.to_string().into_body())
.map_err(|e| e.to_string())?;

let resp = client.send(req).await.map_err(|e| e.to_string())?;
let mut buf = Vec::new();
resp.into_body()
.read_to_end(&mut buf)
.await
.map_err(|e| e.to_string())?;
serde_json::from_slice(&buf).map_err(|e| e.to_string())
}

Streaming response bodies

For server-sent events or chunked responses, read the body incrementally:

use inferlet::wstd::http::{Client, Method, Request};
use inferlet::wstd::io::{empty, AsyncRead};

let client = Client::new();
let req = Request::builder()
.uri("https://example.com/stream")
.method(Method::GET)
.body(empty())
.map_err(|e| e.to_string())?;

let resp = client.send(req).await.map_err(|e| e.to_string())?;
let mut body = resp.into_body();
let mut chunk = vec![0u8; 4096];

loop {
let n = body.read(&mut chunk).await.map_err(|e| e.to_string())?;
if n == 0 { break; }
process_chunk(&chunk[..n]);
}

The http-server inferlet shows the inverse pattern: an inferlet that serves HTTP, including SSE streaming.

Sandbox boundary

HTTP requests leave the inferlet's sandbox and reach the network through the engine. The engine does not currently enforce per-host allowlists, but the policy may tighten in future releases. Treat outbound HTTP as a privileged operation when you are deciding what an untrusted inferlet should do.

Common services

Examples of services that work well from inside an inferlet:

  • Wikipedia REST API (https://en.wikipedia.org/api/rest_v1/). No auth, free, ideal for retrieval examples.
  • Search APIs (Bing, Google CSE, Brave). Bring your own key.
  • Vector store endpoints (Pinecone, Qdrant, Weaviate). Authenticate with the API key from runtime::env(...) (when supported) or pass it as inferlet input.
  • Internal HTTP services for tool execution, code sandboxes, or document parsers.

Next