pawan-api 0.5.7

HTTP API server for Pawan — Axum + SSE streaming
Documentation

pawan-api

HTTP API server for Pawan — Axum + SSE streaming for real-time agent interactions.

Overview

pawan-api provides a RESTful HTTP API and Server-Sent Events (SSE) streaming interface for the Pawan coding agent. It enables web applications and other services to interact with Pawan's AI-powered coding capabilities through standard HTTP protocols.

Features

  • RESTful API — Standard HTTP endpoints for agent interactions
  • SSE Streaming — Real-time streaming of agent responses and tool calls
  • Session Management — Create, list, retrieve, and delete conversation sessions
  • Multi-Agent Support — Manage multiple agent instances concurrently
  • Health Monitoring — Built-in health check and status endpoints
  • Model Discovery — List available models and their configurations
  • CORS Support — Cross-origin resource sharing for web applications
  • Aegis Integration — Peer discovery via Aegis network configuration

Installation

Install from crates.io:

cargo install pawan-api

# Or build from source
git clone https://github.com/dirmacs/pawan && cd pawan
cargo build --release --bin pawan-api

The source-built binary will be available at target/release/pawan-api.

Usage

Starting the Server

# Start with default configuration
pawan-api

# Start with custom workspace
pawan-api --workspace /path/to/project

# Start with custom config
pawan-api --config /path/to/pawan.toml

The server starts on port 3300 by default.

Configuration

The server reads configuration from pawan.toml in the workspace directory:

provider = "nvidia"
model = "qwen/qwen3.5-122b-a10b"
temperature = 0.6
max_tokens = 4096

API Endpoints

Health Check

GET /health

Response:

{
  "status": "ok",
  "version": "0.5.6",
  "uptime_secs": 1234,
  "agent_id": "pawan@hostname"
}

List Agents

GET /agents

Response:

{
  "self": "pawan@hostname",
  "peers": [
    {
      "name": "agent1",
      "agent_id": "pawan@agent1",
      "ip": "192.168.1.100",
      "groups": ["production"]
    }
  ]
}

List Models

GET /models

Response:

{
  "models": [
    {
      "name": "qwen/qwen3.5-122b-a10b",
      "provider": "Nvidia",
      "is_default": true
    },
    {
      "name": "minimaxai/minimax-m2.5",
      "provider": "Nvidia",
      "is_default": false
    }
  ]
}

Chat (Non-Streaming)

POST /chat
Content-Type: application/json

{
  "session_id": "optional-session-id",
  "message": "Fix the failing test in src/lib.rs",
  "model": "optional-model-override"
}

Response:

{
  "session_id": "abc-123-def-456",
  "content": "I'll help you fix the failing test...",
  "iterations": 3,
  "tool_calls": 5
}

Chat (Streaming)

POST /chat/stream
Content-Type: application/json

{
  "session_id": "optional-session-id",
  "message": "Explain how the agent loop works",
  "model": "optional-model-override"
}

Response: Server-Sent Events stream

event: token
data: {"content": "The"}

event: token
data: {"content": " agent"}

event: tool_start
data: {"name": "read"}

event: tool_complete
data: {"name": "read", "success": true, "duration_ms": 42, "result_preview": "..."}

event: done
data: {"session_id": "abc-123", "content": "The agent loop works by...", "iterations": 2, "tool_calls": 3}

Session Management

List Sessions

GET /sessions

Response:

[
  {
    "id": "abc-123-def-456",
    "model": "qwen/qwen3.5-122b-a10b",
    "created_at": "2026-04-14T12:00:00Z",
    "updated_at": "2026-04-14T12:05:00Z",
    "message_count": 10
  }
]

Get Session

GET /sessions/{id}

Response:

{
  "id": "abc-123-def-456",
  "model": "qwen/qwen3.5-122b-a10b",
  "created_at": "2026-04-14T12:00:00Z",
  "updated_at": "2026-04-14T12:05:00Z",
  "messages": [
    {
      "role": "user",
      "content": "Fix the test",
      "timestamp": "2026-04-14T12:01:00Z"
    },
    {
      "role": "assistant",
      "content": "I'll fix it...",
      "timestamp": "2026-04-14T12:02:00Z",
      "tool_calls": [...]
    }
  ]
}

Create Session

POST /sessions

Response:

{
  "session_id": "new-uuid-generated"
}

Delete Session

DELETE /sessions/{id}

Response: 204 No Content

SSE Event Types

token

Emitted for each token in the agent's response.

{
  "content": "The"
}

tool_start

Emitted when a tool execution begins.

{
  "name": "read"
}

tool_complete

Emitted when a tool execution completes.

{
  "name": "read",
  "success": true,
  "duration_ms": 42,
  "result_preview": "fn main() {..."
}

done

Emitted when the agent completes the request.

{
  "session_id": "abc-123",
  "content": "Complete response...",
  "iterations": 3,
  "tool_calls": 5
}

error

Emitted when an error occurs.

{
  "message": "Failed to read file: permission denied"
}

Client Examples

cURL

# Health check
curl http://localhost:3300/health

# List models
curl http://localhost:3300/models

# Chat (non-streaming)
curl -X POST http://localhost:3300/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello, Pawan!"}'

# Chat (streaming)
curl -X POST http://localhost:3300/chat/stream \
  -H "Content-Type: application/json" \
  -d '{"message": "Explain Rust"}'

# List sessions
curl http://localhost:3300/sessions

JavaScript (Fetch API)

// Non-streaming chat
const response = await fetch('http://localhost:3300/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    message: 'Fix the failing test',
    session_id: 'my-session'
  })
});
const data = await response.json();
console.log(data.content);

// Streaming chat
const eventSource = new EventSource('http://localhost:3300/chat/stream', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: 'Explain Rust' })
});

eventSource.addEventListener('token', (e) => {
  const data = JSON.parse(e.data);
  console.log('Token:', data.content);
});

eventSource.addEventListener('done', (e) => {
  const data = JSON.parse(e.data);
  console.log('Complete:', data.content);
  eventSource.close();
});

Python

import requests
import sseclient

# Non-streaming
response = requests.post('http://localhost:3300/chat', json={
    'message': 'Hello, Pawan!'
})
print(response.json()['content'])

# Streaming
response = requests.post('http://localhost:3300/chat/stream', json={
    'message': 'Explain Rust'
}, stream=True)

client = sseclient.SSEClient(response)
for event in client.events():
    if event.event == 'token':
        print(event.data, end='', flush=True)
    elif event.event == 'done':
        print('\nComplete!')
        break

Architecture

Application State

The server maintains shared state across all HTTP handlers:

pub struct AppState {
    agents: Arc<RwLock<HashMap<String, PawanAgent>>>,
    config: Arc<PawanConfig>,
    workspace: PathBuf,
    agent_id: String,
    start_time: std::time::Instant,
}

Session Management

  • Sessions are stored in ~/.pawan/sessions/
  • Each session has a unique UUID
  • Sessions persist across server restarts
  • Automatic session resumption via session_id

Agent Lifecycle

  1. Creation: Agent created on first request with new session_id
  2. Execution: Agent processes message with tool calling
  3. Callbacks: Token, tool start, and tool completion events streamed
  4. Persistence: Session saved automatically after completion
  5. Archival: Optional archival to Eruka memory system

CORS Configuration

The server includes CORS support for web applications:

let cors = CorsLayer::new()
    .allow_origin(Any)
    .allow_methods([Method::GET, Method::POST, Method::DELETE])
    .allow_headers(Any);

Aegis Integration

The server can discover peer agents via Aegis network configuration:

# ~/.config/aegis/aegis-net.toml
[peers.agent1]
ip = "192.168.1.100"
groups = ["production"]

[peers.agent2]
ip = "192.168.1.101"
groups = ["development"]

Access via /agents endpoint.

Error Handling

The server returns appropriate HTTP status codes:

  • 200 OK — Successful request
  • 204 No Content — Successful deletion
  • 400 Bad Request — Invalid request body
  • 404 Not Found — Session not found
  • 500 Internal Server Error — Agent execution error

Error responses include descriptive messages:

{
  "error": "Failed to execute agent: permission denied"
}

Development

Running in Development

cd pawan
cargo run --bin pawan-api

# With custom workspace
cargo run --bin pawan-api -- --workspace /path/to/project

Testing

# Run web server tests
cargo test --bin pawan-api

# Run with logging
RUST_LOG=debug cargo run --bin pawan-api

Performance Considerations

  • Concurrent Sessions: Multiple sessions handled via RwLock<HashMap>
  • Streaming: SSE provides low-latency real-time updates
  • Memory: Sessions persisted to disk, not kept in memory
  • Scalability: Stateless design allows horizontal scaling

Security

  • CORS: Configurable for production deployments
  • Input Validation: All requests validated before processing
  • File Access: Agent respects workspace boundaries
  • No Authentication: Currently designed for trusted environments

License

MIT

See Also

  • Pawan — Main CLI coding agent
  • pawan-core — Core library
  • Axum — Web framework
  • SSE — Server-Sent Events