open-standards/mesh/mesh-rs/crates/mesh-openai/examples/serve.rs
Transaction Science ec2b3841ee
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 12s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m11s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 59s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m13s
mesh: a grant rides over the OpenAI-compatible HTTP binding, and a receipt can be shown back as a witness
The grant work (#294, #301, #306) was reachable only through the library.
The reference HTTP server passed no grant to every request, had no way to
learn which steward a grant must name, and no way to witness a receipt
against the journal. Its binaries kept the journal in memory. It also read
whatever request body length a client declared.

- **Grants over HTTP.** A request's body carries its presentation in
  `jcp_presentation` (`presentation_in`). The presentation is signed for a
  request id, so a granted request is served as exactly the `ServeRequest`
  the body alone determines (`granted_serve_request`, plus a
  `_responses` variant). Its prompt-token figure stays the body's estimate
  rather than the server's tokenizer count, which a caller cannot
  reproduce. The body must name its own `metadata.joule_ceiling_uj`,
  since a server default would otherwise enter the id unseen.
- **Endpoints.** `GET /v1/mesh/steward` returns the steward's did:key,
  the subject a grant must name. `POST /v1/mesh/witness`, with the
  `receipt` and `envelope_hex` a response carried, answers 200 when the
  journal holds the receipt and 409 when it does not.
  `Mesh::witness_wire` checks a receipt in that wire form: the envelope
  must verify under this steward and seal exactly that wire receipt.
- **Statuses** (`http_status`): 401 when the caller is not the grant's
  issuer, 403 when the steward refuses the grant, 503 once it has stopped
  honouring grants, 500 for an unwritable journal, 400 otherwise.
- **Journal in the binaries.** With `MESH_JOURNAL` set, the reference
  server example and serve_openai_falcon record to that file and replay
  it before serving, reporting any unresolved serve.
- **Body cap.** A body declared over 4 MiB is refused (413) before it is
  read.
- **Spec.** Mesh SPEC §2.3 describes the binding (informative).

Checked on the build host:
- mesh-rs: 81 tests pass. mesh-openai with the server feature passes 15,
  including an end-to-end socket test: learn the steward, grant to it,
  present over HTTP, witness the receipt, refuse a tampered one, refuse
  an oversize body.
- The gguf and openai server features build, every Cargo.lock is
  current, and clippy is clean.
- Mutations each fail tests: a server that drops the grant, a granted
  path that recounts tokens, a wire witness that skips the seal check,
  and no body cap.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 02:20:35 -04:00

115 lines
5.3 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Run the Mesh OpenAI-compatible reference server — a real
//! `/v1/chat/completions` endpoint any OpenAI client (curl, the SDKs, vLLM-style
//! tooling) can drive, returning energy-metered serving + a sealed receipt.
//!
//! ```text
//! cargo run --example serve --features server -- 127.0.0.1:8088 &
//! curl -s localhost:8088/v1/chat/completions \
//! -d '{"model":"gpt-mini","messages":[{"role":"user","content":"hi"}],"max_tokens":8}'
//! ```
//!
//! **What a receipt's figure is.** Each request's `usage.energy` carries the
//! energy of the served span — the deterministic reference engine's work, in
//! this process (the HTTP handling around it is outside the span) — at the
//! grade of whatever read it, in `usage.energy.provenance` and in the sealed
//! `mesh/1` receipt. The meter is the one this build can read:
//!
//! - `--features server,metal` (macOS): `mesh_meter::ProcessMeter`, this
//! process's own CPU and GPU energy over the span. The engine runs in this
//! process and uses no GPU, so where the GPU counter is proved live its GPU
//! energy is exactly zero and the span covers the whole request: `HwShunt`,
//! the kernel's count of this process's CPU energy. That count is every
//! thread of this process, so a request served while another of its
//! threads works reads that work too. Where the GPU counter is not proved
//! live (a GPU busy with other work fails its probe) the span covers part
//! of the request and is sealed as an estimate of all of it, `Estimator`.
//! A serve too short for the CPU count to advance over it (this engine's
//! are microseconds) has no reading either, and is the estimator's figure
//! for the serve, `Estimator`.
//! - `--features server,hw-meter` (or `hw-meter-rapl`, `-nvml`, `-apple`):
//! `mesh_meter::HwMeter::detect`, the machine's counter, a reading only
//! where a live one covers the whole request (a RAPL package on Linux) and
//! `mesh_meter::EstimatorMeter` otherwise. A machine counter reads
//! everything the host does over the span, not this request alone.
//! - `--features server` alone: `mesh_meter::EstimatorMeter`, wall time × an
//! assumed draw, `Estimator`.
//!
//! No build seals a figure nothing read: a `mesh/1` receipt has no coverage
//! field, so `HwShunt` there claims a hardware reading of the whole request
//! (SPEC §3.3), and only a meter that read one may say so.
use std::net::TcpListener;
use ed25519_dalek::SigningKey;
use mesh_core::Mesh;
use mesh_engine::ReferenceEngine;
use mesh_meter::Meter;
use mesh_openai::{server, ChatBackend};
use mesh_router::BackendSpec;
use mesh_schema::Fidelity;
/// A trivial tokenizer bridge: bytes in, a deterministic note out. A real
/// deployment pairs the model's own tokenizer with the engine that serves it.
pub struct ByteBackend;
impl ChatBackend for ByteBackend {
fn encode(&self, text: &str) -> Vec<u32> {
text.bytes().map(u32::from).collect()
}
fn decode(&self, ids: &[u32]) -> String {
format!("[mesh reference engine served {} tokens]", ids.len())
}
}
/// The meter this build serves with: see the module docs for each feature's
/// meter and what its figures cover.
pub fn meter() -> Box<dyn Meter> {
#[cfg(feature = "metal")]
let meter: Box<dyn Meter> = Box::new(mesh_meter::ProcessMeter::new(
jouleclaw_backend_apple::gpu_liveness_load,
mesh_meter::SpanUses::CpuGpu,
));
#[cfg(all(feature = "hw-meter", not(feature = "metal")))]
let meter: Box<dyn Meter> = Box::new(mesh_meter::HwMeter::detect());
#[cfg(not(feature = "hw-meter"))]
let meter: Box<dyn Meter> = Box::new(mesh_meter::EstimatorMeter::default());
meter
}
/// The deterministic reference engine behind one steward-signed receipt
/// chain, metered by `meter`.
pub fn mesh(meter: &dyn Meter) -> Mesh<'_> {
let sk = SigningKey::from_bytes(&[8u8; 32]);
let mut mesh = Mesh::new(meter, sk, "did:example:mesh-demo");
mesh.register(
BackendSpec::new("tiny", "ssm-1b", Fidelity::F2, 50_000),
Box::new(ReferenceEngine::new("tiny", "ssm-1b", Fidelity::F2, 50_000, 32)),
);
mesh
}
fn main() -> std::io::Result<()> {
let addr = std::env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8088".to_string());
let listener = TcpListener::bind(&addr)?;
let meter = meter();
eprintln!("mesh OpenAI server: http://{addr}/v1/chat/completions (meter: {})", meter.name());
let mut mesh = journalled(mesh(meter.as_ref()))?;
server::run(&listener, &mut mesh, &ByteBackend, 100_000_000)
}
/// With `MESH_JOURNAL` set, record grant decisions and seals in that file and
/// replay what it already holds before serving, so a restart gives no budget
/// back and the receipt chain continues (Mesh SPEC §2.2). Without it the
/// journal is in memory, which a steward that restarts must not rely on.
pub fn journalled(mesh: mesh_core::Mesh<'_>) -> std::io::Result<mesh_core::Mesh<'_>> {
let Some(path) = std::env::var_os("MESH_JOURNAL") else {
return Ok(mesh);
};
let (journal, recorded) = mesh_core::FileJournal::open(&path)?;
let mesh = mesh
.with_journal(journal, &recorded)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
for u in mesh.unresolved() {
eprintln!("unresolved: request {} on backend {}, debited {} µJ", u.request_id.to_hex(), u.backend, u.debited_uj);
}
Ok(mesh)
}