Response Formats
ModelRiver supports two response formats to accommodate different use cases. By default, responses are returned in raw format for maximum compatibility with existing OpenAI/Anthropic clients.
Response Format Options
1. Raw Format (Default)
Returns the exact provider response, maintaining full compatibility with OpenAI/Anthropic SDKs.
Request:
1{2 "workflow": "my-chat-workflow",3 "messages": [4 {"role": "user", "content": "Hello!"}5 ]6}Response:
1{2 "id": "chatcmpl-123",3 "object": "chat.completion",4 "created": 1677652288,5 "model": "gpt-4o",6 "choices": [7 {8 "index": 0,9 "message": {10 "role": "assistant",11 "content": "Hello! How can I help you today?"12 },13 "finish_reason": "stop"14 }15 ],16 "usage": {17 "prompt_tokens": 9,18 "completion_tokens": 12,19 "total_tokens": 2120 }21}Metadata in headers:
X-ModelRiver-Provider: The provider used (e.g.,openai,anthropic)X-ModelRiver-Model: The model used (e.g.,gpt-4o)X-ModelRiver-Workflow: The workflow nameX-ModelRiver-Duration-Ms: Request duration in millisecondsX-ModelRiver-Attempts: Number of provider attempts (includes fallbacks)X-ModelRiver-Cached-Data: Cached fields (JSON string)X-ModelRiver-Offline-Fallback: Set to"true"if offline fallback was used
2. Wrapped Format
Returns the provider response wrapped with additional ModelRiver metadata, logs, and cached data.
Request:
1{2 "workflow": "my-chat-workflow",3 "format": "wrapped",4 "messages": [5 {"role": "user", "content": "Hello!"}6 ]7}Response:
1{2 "data": {3 "id": "chatcmpl-123",4 "object": "chat.completion",5 "created": 1677652288,6 "model": "gpt-4o",7 "choices": [8 {9 "index": 0,10 "message": {11 "role": "assistant",12 "content": "Hello! How can I help you today?"13 },14 "finish_reason": "stop"15 }16 ],17 "usage": {18 "prompt_tokens": 9,19 "completion_tokens": 12,20 "total_tokens": 2121 }22 },23 "customer_data": {24 "user_id": "550e8400-e29b-41d4-a716-446655440000"25 },26 "meta": {27 "status": "success",28 "http_status": 200,29 "workflow": "my-chat-workflow",30 "requested_provider": "openai",31 "requested_model": "gpt-4o",32 "used_provider": "openai",33 "used_model": "gpt-4o",34 "duration_ms": 1250,35 "usage": {36 "prompt_tokens": 9,37 "completion_tokens": 12,38 "total_tokens": 2139 },40 "structured_output": false,41 "attempts": [42 {43 "provider": "openai",44 "model": "gpt-4o",45 "status": "success"46 }47 ],48 "customer_fields": ["user_id"]49 },50 "logs": {51 "columns": [...],52 "rows": [...]53 },54 "backups": [55 {56 "position": 1,57 "provider": "anthropic",58 "model": "claude-3-5-sonnet"59 }60 ]61}Choosing the Right Format
Use Raw Format when:
✅ Drop-in replacement: You want to replace OpenAI/Anthropic API calls without changing client code ✅ SDK compatibility: You're using existing OpenAI/Anthropic SDKs ✅ Minimal overhead: You want the fastest response with minimal data transfer ✅ Standard integration: Your application expects standard OpenAI/Anthropic response format
Example use cases:
- Migrating from OpenAI to ModelRiver
- Using with LangChain, LlamaIndex, or other LLM frameworks
- Mobile apps with bandwidth constraints
- High-throughput production systems
Use Wrapped Format when:
✅ Debugging: You need detailed request logs and metadata ✅ Monitoring: You want to track provider fallbacks and attempts ✅ Analytics: You need cached data for analysis ✅ Internal tools: Building consoles or admin interfaces ✅ Audit trails: Compliance requirements need detailed logging
Example use cases:
- Development and testing
- Admin consoles
- Analytics pipelines
- Debugging production issues
Usage Examples
JavaScript (Raw Format - Default)
1const response = await fetch('https://api.modelriver.com/api/v1/ai', {2 method: 'POST',3 headers: {4 'Authorization': 'Bearer YOUR_API_KEY',5 'Content-Type': 'application/json'6 },7 body: JSON.stringify({8 workflow: 'my-chat-workflow',9 messages: [10 { role: 'user', content: 'Hello!' }11 ]12 })13});14 15const data = await response.json();16// data is exactly like OpenAI's response17console.log(data.choices[0].message.content);18 19// Check headers for metadata20const provider = response.headers.get('X-ModelRiver-Provider');21const duration = response.headers.get('X-ModelRiver-Duration-Ms');22console.log(`Completed in ${duration}ms using ${provider}`);Python (Raw Format with OpenAI SDK)
1import openai2 3# Point OpenAI SDK to ModelRiver4openai.api_base = "https://api.modelriver.com/api/v1"5openai.api_key = "YOUR_API_KEY"6 7# Use exactly like OpenAI8response = openai.ChatCompletion.create(9 model="workflow:my-chat-workflow", # Prefix with "workflow:"10 messages=[11 {"role": "user", "content": "Hello!"}12 ]13)14 15print(response.choices[0].message.content)cURL (Wrapped Format)
curl -X POST https://api.modelriver.com/api/v1/ai \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workflow": "my-chat-workflow", "format": "wrapped", "messages": [ {"role": "user", "content": "Hello!"} ] }'Response:
1{2 "data": { /* OpenAI response */ },3 "meta": { /* ModelRiver metadata */ },4 "customer_data": { /* Cached fields */ },5 "logs": { /* Request logs */ },6 "backups": [ /* Configured fallbacks */ ]7}TypeScript (Type Definitions)
1// Raw Format (OpenAI-compatible)2interface RawResponse {3 id: string;4 object: string;5 created: number;6 model: string;7 choices: Array<{8 index: number;9 message: {10 role: string;11 content: string;12 };13 finish_reason: string;14 }>;15 usage: {16 prompt_tokens: number;17 completion_tokens: number;18 total_tokens: number;19 };20}21 22// Wrapped Format23interface WrappedResponse {24 data: RawResponse;25 customer_data: Record<string, any>;26 meta: {27 status: string;28 http_status: number;29 workflow: string;30 requested_provider: string;31 requested_model: string;32 used_provider: string;33 used_model: string;34 duration_ms: number;35 usage: {36 prompt_tokens: number;37 completion_tokens: number;38 total_tokens: number;39 };40 structured_output: boolean;41 attempts: Array<{42 provider: string;43 model: string;44 status: string;45 reason?: any;46 }>;47 customer_fields: string[];48 };49 logs: {50 columns: Array<{key: string; label: string}>;51 rows: any[];52 };53 backups: Array<{54 position: number;55 provider: string;56 model: string;57 }>;58}Error Handling
Errors are always returned in wrapped format, regardless of the format parameter, since there's no standard provider error format.
Error Response:
1{2 "data": null,3 "error": {4 "message": "Provider request failed",5 "details": { /* error details */ },6 "attempts": [ /* all attempts */ ]7 },8 "meta": {9 "status": "error",10 "http_status": 502,11 "workflow": "my-chat-workflow",12 "duration_ms": 2500,13 ...14 },15 "customer_data": {},16 "logs": { ... },17 "backups": [ ... ]18}Migration Guide
Migrating from OpenAI
Before (OpenAI):
1const response = await openai.chat.completions.create({2 model: "gpt-4o",3 messages: [{role: "user", content: "Hello"}]4});After (ModelRiver - Raw Format):
1const response = await fetch('https://api.modelriver.com/api/v1/ai', {2 method: 'POST',3 headers: {4 'Authorization': 'Bearer YOUR_MODELRIVER_API_KEY',5 'Content-Type': 'application/json'6 },7 body: JSON.stringify({8 workflow: 'my-gpt4-workflow', // Configure in console9 messages: [{role: 'user', content: 'Hello'}]10 })11});12 13const data = await response.json();14// data structure is identical to OpenAI's responseMigrating from Anthropic
Before (Anthropic):
1const response = await anthropic.messages.create({2 model: "claude-3-5-sonnet-20241022",3 max_tokens: 1024,4 messages: [{role: "user", content: "Hello"}]5});After (ModelRiver - Raw Format):
1const response = await fetch('https://api.modelriver.com/api/v1/ai', {2 method: 'POST',3 headers: {4 'Authorization': 'Bearer YOUR_MODELRIVER_API_KEY',5 'Content-Type': 'application/json'6 },7 body: JSON.stringify({8 workflow: 'my-claude-workflow', // Configure in console9 max_tokens: 1024,10 messages: [{role: 'user', content: 'Hello'}]11 })12});13 14const data = await response.json();15// data structure is identical to Anthropic's responseBest Practices
- Default to raw format for production applications
- Use wrapped format during development and debugging
- Check response headers for metadata without payload overhead
- Cache workflow names to avoid repeated console lookups
- Monitor X-ModelRiver-Attempts to detect fallback patterns
- Log X-ModelRiver-Offline-Fallback to identify connectivity issues
Summary
| Aspect | Raw Format | Wrapped Format |
|---|---|---|
| Default | ✅ Yes | ❌ No |
| Provider compatibility | ✅ Full | ❌ Custom |
| Metadata location | Headers | Response body |
| Response size | Smaller | Larger |
| Use case | Production | Debugging/Analytics |
| SDK support | ✅ Yes | ❌ No |
For questions or feature requests, please open an issue on GitHub.