Files
OpenShell/examples/inference
Piotr Mlocek 34fd3cbf64 feat(inference): inference interception and routing (!38)
Closes #67

## Summary

Implements transparent inference interception and routing for sandboxed AI agents. The sandbox proxy intercepts outbound AI SDK calls (OpenAI, Anthropic) and reroutes them through the gateway to policy-controlled backends — enabling organizations to redirect inference traffic to local or self-hosted models without modifying agent code.

**Decision model** — a tri-state OPA evaluation for every CONNECT request:
1. Binary + endpoint explicitly allowed in `network_policies` → **allow** (pass through)
2. Not explicitly allowed + `inference.allowed_routes` configured → **inspect for inference** (TLS intercept, detect API patterns, route through gateway)
3. Otherwise → **deny**

No endpoint declarations or binary lists needed for inference routing. Just configure `inference.allowed_routes`.

## Key Changes

### Sandbox (interception)
- **OPA policy**: New `network_action` Rego rule with three outcomes (`allow`, `inspect_for_inference`, `deny`). New `NetworkAction` enum replaces `PolicyDecision.allowed` bool for the proxy's main decision path.
- **Proxy**: New `InspectForInference` path — TLS-terminates client, parses HTTP, detects inference API patterns (`POST /v1/chat/completions`, `/v1/completions`, `/v1/messages`), strips auth headers, forwards via gRPC.
- **New module**: `l7/inference.rs` — `InferenceApiPattern`, `detect_inference_pattern()`, HTTP request/response parsing.
- **gRPC client**: New `proxy_inference()` for sandbox→gateway forwarding.
- **Sandbox init**: Creates OPA engine when inference is configured, even without `network_policies`.

### Gateway (dispatch)
- **InferenceService**: `ProxyInference` RPC loads sandbox policy, resolves allowed routes, dispatches to router. Full CRUD for inference routes.
- **Proto**: `InferenceRoute`, `InferenceRouteSpec`, `ProxyInferenceRequest/Response`, Inference gRPC service.

### Router (backend proxying)
- **New crate**: `navigator-router` with `Router`, `proxy_with_candidates()`, protocol-based route selection, backend HTTP proxying with auth header rewriting.
- **Mock support** for testing (`mock://` scheme).

### CLI
- `nav inference create/update/delete/list` commands for route management.

### Python SDK
- Updated protobuf bindings. Removed old `inference.py` client (replaced by transparent interception).

### Documentation
- New `architecture/inference-routing.md` — end-to-end system documentation.
- Updated `architecture/sandbox.md` — proxy, OPA, and source index sections.
- Updated `architecture/README.md` — new subsystem overview and diagram.

## Addendum: Chunked Transfer Compatibility

This branch now also fixes intercepted SDK requests that send chunked request bodies:

- `inspect_for_inference` now accepts `Transfer-Encoding: chunked` and decodes chunked request bodies before forwarding to the gateway
- Removed the prior `411 Length Required` response for chunked intercepted requests
- Added request/response header sanitization for framing and hop-by-hop headers (`content-length`, `transfer-encoding`, `connection`, etc.) to keep forwarded requests and returned responses valid
- Added unit tests for chunked parsing and header sanitization

Note: this improves compatibility for streaming-style SDK request patterns; true token-by-token passthrough response streaming is still a separate follow-up.

## Minimal Policy for Inference Routing

```yaml
inference:
  allowed_routes:
    - local
```

Any outgoing connection from a binary not explicitly allowed in `network_policies` will be intercepted and checked for inference API patterns.

## Test Plan

- [x] `cargo test --workspace` — all tests pass
- [x] `mise run pre-commit` — all checks pass
- [x] E2E: OpenAI chat completions routed through gateway
- [x] E2E: Anthropic messages routed through gateway
- [x] E2E: Python OpenAI SDK from sandbox (`examples/inference/inference.py`)
- [x] E2E test: `e2e/python/test_inference_routing.py`
2026-02-23 11:48:48 -08:00
..

Inference Routing Example

This example demonstrates Navigator's inference interception and routing. A sandbox process makes standard API calls (OpenAI, Anthropic, etc.) and Navigator transparently intercepts, enforces policy, and reroutes them to a configured backend — without any code changes in the sandboxed application.

How It Works

  1. The sandbox proxy intercepts outgoing HTTPS connections.
  2. OPA policy determines the action: if the binary has no explicit network policy but inference routing is configured, the connection is inspected.
  3. The proxy TLS-terminates, parses the HTTP request, and detects known inference patterns (e.g., POST /v1/chat/completions).
  4. Matching requests are forwarded through the gateway to the policy-allowed inference backend. Non-inference requests are denied.

Files

File Description
inference.py Python script that calls the OpenAI SDK — works unmodified inside a sandbox
sandbox-policy.yaml Sandbox policy with inference routing enabled (route hint: local)

Quick Start

1. Start a Navigator cluster

mise run cluster
navigator cluster status

2. Create an inference route

Point the route at any OpenAI-compatible endpoint (local or remote):

# Local model (e.g., LM Studio, Ollama, vLLM)
navigator inference create \
  --routing-hint local \
  --base-url http://<HOST>:<PORT> \
  --model-id <MODEL_NAME>

# Remote provider (e.g., OpenAI, NVIDIA NIM)
navigator inference create \
  --routing-hint local \
  --base-url https://api.openai.com \
  --api-key sk-... \
  --model-id gpt-4o-mini

If --protocol is omitted, Navigator auto-detects supported protocols by probing the endpoint (sends minimal requests with max_tokens: 1).

Verify the route:

navigator inference list

3. Run the example inside a sandbox

navigator sandbox create \
  --policy examples/inference/sandbox-policy.yaml \
  --keep \
  --name inference-demo \
  -- python examples/inference/inference.py

The script targets https://api.openai.com by default, but Navigator intercepts the connection and routes it to whatever backend the local route points at.

Expected output:

model=<backend model name>
content=NAV_OK

4. (Optional) Interactive session

navigator sandbox connect inference-demo
# Inside the sandbox:
python examples/inference/inference.py

5. Cleanup

navigator sandbox delete inference-demo
navigator inference delete <route-name>

Customizing the Policy

Edit sandbox-policy.yaml to control which routes are available:

inference:
  allowed_routes:
    - local          # matches the --routing-hint used in step 2
    - production     # add more route hints as needed

The allowed_routes list determines which inference routes a sandbox can use. Routes are matched by their routing_hint field.

Supported Protocols

Navigator detects and routes the following inference API patterns:

Pattern Protocol Kind
POST /v1/chat/completions openai_chat_completions Chat completion
POST /v1/completions openai_completions Text completion
POST /v1/responses openai_responses Responses API
POST /v1/messages anthropic_messages Anthropic messages