HIP-11: Chat Interface Standard. Status Draft. Hanzo's own standard — read this before implementing against it.
This proposal defines the comprehensive chat interface standard for AI model interactions across the Hanzo ecosystem. It establishes the canonical implementation for conversational AI, including message formats, session management, streaming protocols, multimodal support, and Model Context Protocol (MCP) integration. All chat applications in the Hanzo ecosystem MUST implement this interface to ensure consistency and interoperability.
Repository: github.com/hanzoai/chat Port: 3081 Status: Production-ready (LibreChat fork with Hanzo extensions)
interface Message {
// Unique identifier
id: string;
// Message role in conversation
role: "user" | "assistant" | "system" | "function" | "tool";
// Message content (text, structured data, or multimodal)
content: string | MessageContent[];
// Metadata
timestamp: number;
model?: string;
tokens?: {
prompt: number;
completion: number;
total: number;
};
// Optional fields
name?: string; // For function/tool messages
function_call?: { // AI requesting function execution
name: string;
arguments: string;
};
tool_calls?: ToolCall[]; // Multiple tool invocations
// Multimodal attachments
attachments?: Attachment[];
// User feedback
feedback?: {
rating: number; // 1-5 stars
comment?: string;
};
}
interface MessageContent {
type: "text" | "image" | "file" | "code";
text?: string;
image_url?: {
url: string;
detail?: "low" | "high" | "auto";
};
file?: {
url: string;
mime_type: string;
name: string;
size: number;
};
code?: {
language: string;
content: string;
};
}
interface Attachment {
id: string;
type: "image" | "document" | "audio" | "video" | "code";
url: string;
name: string;
size: number;
mime_type: string;
metadata?: Record<string, any>;
}
interface ToolCall {
id: string;
type: "function";
function: {
name: string;
arguments: string;
};
}
interface ChatSession {
id: string;
title: string;
messages: Message[];
model: string;
// Timestamps
created: number;
updated: number;
// User management
user_id: string;
shared_with?: string[]; // User IDs with access
public?: boolean; // Publicly accessible
// Configuration
settings: {
temperature?: number;
max_tokens?: number;
top_p?: number;
frequency_penalty?: number;
presence_penalty?: number;
stop_sequences?: string[];
system_prompt?: string;
};
// MCP tools
enabled_tools?: string[]; // Tool IDs from MCP registry
// Metadata
tags?: string[];
folder_id?: string;
archived?: boolean;
// Usage tracking
usage?: {
total_tokens: number;
total_cost: number; // In cents
message_count: number;
};
}
interface Folder {
id: string;
name: string;
user_id: string;
parent_id?: string; // For nested folders
created: number;
updated: number;
}
# Create new session
POST /api/chat/sessions
Body:
title?: string
model: string
settings?: SessionSettings
Response:
session: ChatSession
# List sessions with pagination
GET /api/chat/sessions
Query:
page?: number (default: 1)
limit?: number (default: 20, max: 100)
folder_id?: string
archived?: boolean
search?: string
Response:
sessions: ChatSession[]
total: number
page: number
pages: number
# Get specific session
GET /api/chat/sessions/:id
Response:
session: ChatSession
# Update session
PATCH /api/chat/sessions/:id
Body:
title?: string
settings?: SessionSettings
tags?: string[]
folder_id?: string
archived?: boolean
Response:
session: ChatSession
# Delete session
DELETE /api/chat/sessions/:id
Response:
success: boolean
# Share session
POST /api/chat/sessions/:id/share
Body:
user_ids?: string[]
public?: boolean
Response:
share_url?: string
# Send message (streaming response)
POST /api/chat/sessions/:id/messages
Headers:
Accept: text/event-stream # For SSE streaming
Body:
content: string | MessageContent[]
attachments?: Attachment[]
tools?: string[] # MCP tool IDs to enable
Response:
Stream of SSE events (see Streaming Protocol)
# Edit message
PATCH /api/chat/messages/:id
Body:
content: string | MessageContent[]
Response:
message: Message
# Delete message
DELETE /api/chat/messages/:id
Response:
success: boolean
# Regenerate response
POST /api/chat/messages/:id/regenerate
Body:
model?: string # Override model
settings?: SessionSettings # Override settings
Response:
Stream of SSE events
# Upload file for attachment
POST /api/chat/files
Headers:
Content-Type: multipart/form-data
Body:
file: File
session_id?: string
Response:
attachment: Attachment
# Get file
GET /api/chat/files/:id
Response:
File content with appropriate Content-Type
# Delete file
DELETE /api/chat/files/:id
Response:
success: boolean
Server-Sent Events (SSE) format for real-time streaming:
// Token streaming
event: token
data: {"content": "Hello", "index": 0}
event: token
data: {"content": " world", "index": 1}
// Tool use
event: tool_call
data: {"id": "call_123", "name": "calculator", "arguments": "{\"expression\": \"2+2\"}"}
event: tool_result
data: {"id": "call_123", "result": "4"}
// Completion metadata
event: done
data: {
"message_id": "msg_456",
"model": "claude-3-opus",
"tokens": {
"prompt": 10,
"completion": 2,
"total": 12
},
"cost": 0.0024 // In dollars
}
// Error handling
event: error
data: {"code": "rate_limit", "message": "Rate limit exceeded"}
interface MCPTool {
id: string;
name: string;
description: string;
parameters: JSONSchema;
handler: (params: any) => Promise<any>;
}
// Tool registration
POST /api/chat/tools
Body:
tool: MCPTool
Response:
id: string
// Tool execution (internal)
interface ToolExecution {
tool_id: string;
call_id: string;
parameters: any;
result?: any;
error?: string;
duration_ms: number;
}
Alternative to SSE for bidirectional communication:
// WebSocket connection
ws://localhost:3081/api/chat/ws
// Client → Server messages
{
"type": "session.join",
"session_id": "sess_123"
}
{
"type": "message.send",
"content": "Hello",
"attachments": []
}
{
"type": "message.stop" // Cancel generation
}
// Server → Client messages
{
"type": "token",
"content": "Hello"
}
{
"type": "message.complete",
"message": Message
}
{
"type": "error",
"error": {
"code": "invalid_session",
"message": "Session not found"
}
}
interface AuthContext {
user_id: string;
email: string;
roles: string[];
permissions: string[];
api_key?: string;
oauth_provider?: string;
}
// JWT-based authentication
interface JWTPayload {
sub: string; // User ID
email: string;
roles: string[];
exp: number;
iat: number;
}
// API key authentication
interface APIKey {
id: string;
key: string; // Hashed
name: string;
user_id: string;
permissions: string[];
created: number;
last_used?: number;
expires?: number;
}
Tiers:
free:
requests_per_minute: 20
requests_per_day: 500
max_tokens_per_request: 2000
concurrent_sessions: 3
pro:
requests_per_minute: 60
requests_per_day: 5000
max_tokens_per_request: 4000
concurrent_sessions: 10
enterprise:
requests_per_minute: 300
requests_per_day: unlimited
max_tokens_per_request: 32000
concurrent_sessions: unlimited
interface ErrorResponse {
error: {
code: string;
message: string;
details?: any;
timestamp: number;
request_id: string;
};
}
// Standard error codes
enum ErrorCode {
// Client errors (4xx)
INVALID_REQUEST = "invalid_request",
UNAUTHORIZED = "unauthorized",
FORBIDDEN = "forbidden",
NOT_FOUND = "not_found",
RATE_LIMITED = "rate_limited",
INVALID_MODEL = "invalid_model",
CONTEXT_LENGTH_EXCEEDED = "context_length_exceeded",
// Server errors (5xx)
INTERNAL_ERROR = "internal_error",
MODEL_ERROR = "model_error",
GATEWAY_ERROR = "gateway_error",
SERVICE_UNAVAILABLE = "service_unavailable"
}
│ File Storage │
│ (S3/Local) │
└──────────────┘
# docker-compose.yml
version: '3.8'
services:
chat:
image: hanzoai/chat:latest
ports:
- "3081:3081"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/chat
- REDIS_URL=redis://redis:6379
- LLM_GATEWAY_URL=http://gateway:4000
- JWT_SECRET=${JWT_SECRET}
- S3_BUCKET=${S3_BUCKET}
depends_on:
- db
- redis
- gateway
db:
image: postgres:16
volumes:
- chat_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=chat
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
gateway:
image: hanzoai/llm-gateway:latest
ports:
- "4000:4000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# ... other provider keys
volumes:
chat_data:
redis_data:
# Clone repository
git clone https://github.com/hanzoai/chat
cd chat
# Configure environment
cp .env.example .env
# Edit .env with your configuration
# Start with Docker
docker compose up -d
# Or start development server
npm install
npm run dev
# Access at http://localhost:3081
npm run test:unit
npm run test:integration
# Using k6
k6 run tests/load/chat-api.js
# OWASP ZAP scan
npm run test:security
Copyright and related rights waived via CC0.