OpenAI-Compatible Server

Drop-in OpenAI API replacement powered by PraisonAI agents

python
from praisonaiagents import Agent

agent = Agent(
    name="openai-server-agent",
    instructions="Serve requests via the OpenAI-compatible API.",
    llm="openai/gpt-4o-mini",
)
agent.start("Process this chat completion request.")

praisonai serve openai starts a FastAPI server that exposes OpenAI-compatible HTTP endpoints — point any existing OpenAI SDK code at it and it works as a drop-in replacement.

The user points an OpenAI SDK client at the server; chat completions route to your PraisonAI agent.

Diagram

OpenAI-Compatible Server

📱 OpenAI SDK

🔌 /v1/chat/completions

🤖 PraisonAI Agent

🧠 LLM

✅ OpenAI-shaped Response

Quick Start

  1. Simple Usage
    bash
    praisonai serve openai

    The server starts on http://localhost:8765 by default.

    python
    from openai import OpenAI
    
    client = OpenAI(base_url="http://localhost:8765/v1", api_key="anything")
    
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Hello!"}]
    )
    print(response.choices[0].message.content)
  2. With Configuration
    bash
    # Terminal 1 — agents API
    praisonai serve agents --file agents.yaml --port 8000
    
    # Terminal 2 — OpenAI compat layer with auth
    praisonai serve openai --agents-url http://localhost:8000 --api-key $PRAISONAI_API_KEY
    python
    import os
    from openai import OpenAI
    
    client = OpenAI(
        base_url="http://localhost:8765/v1",
        api_key=os.getenv("PRAISONAI_API_KEY"),
    )

How It Works

Diagram
LLMPraisonAI Agentserve openaiOpenAI SDKLLMPraisonAI Agentserve openaiOpenAI SDKPOST /v1/chat/completionsForward requestProcess with toolsCompletionResultOpenAI-shaped response

Endpoints

All endpoints follow the OpenAI REST specification.

MethodPathDescriptionStreaming
POST/v1/chat/completionsChat completionsSSE (stream: true) with [DONE] sentinel
POST/v1/completionsLegacy text completionsNo
GET/v1/modelsList available modelsNo
POST/v1/tools/invokePraisonAI extension — invoke an agent toolNo
GET/__praisonai__/discoveryProvider discoveryNo
GET/healthHealth checkNo

Streaming

Set stream=True to receive tokens via Server-Sent Events:

python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8765/v1", api_key="anything")

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Write a haiku about agents"}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

The stream ends with a data: [DONE] sentinel. Errors during streaming are also sent as SSE events:

data: {"error": {"message": "...", "type": "stream_error"}}

data: [DONE]

Tool Invocation

/v1/tools/invoke is a PraisonAI extension that calls an agent tool by name. It requires --agents-url to point to a running agents server.

bash
curl -X POST http://localhost:8765/v1/tools/invoke \
  -H "Content-Type: application/json" \
  -d '{"tool": "search", "arguments": {"query": "PraisonAI features"}}'

Start the agents server first:

bash
# Terminal 1 — agents API
praisonai serve agents --file agents.yaml --port 8000

# Terminal 2 — OpenAI compat layer
praisonai serve openai --agents-url http://localhost:8000

Authentication

Pass --api-key to require authentication:

bash
praisonai serve openai --api-key my-secret-key

Clients must include the key as the Authorization header:

python
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8765/v1",
    api_key="my-secret-key"
)

CLI Reference

bash
praisonai serve openai [OPTIONS]
OptionTypeDefaultDescription
--host, -hstr"127.0.0.1"Host to bind to
--port, -pint8765Port to bind to
--agents-urlstr"http://127.0.0.1:8000"URL of the running Agents API server
--api-keystrNoneOptional API key for authentication
--reloadboolFalseEnable auto-reload (dev)

Choosing Between serve openai and serve rag --openai-compat

Diagram

Yes, full chat + tools + models

Yes, but only for RAG queries

Multiple providers in one server

Need OpenAI-compatible API?

praisonai serve openai

praisonai serve rag –openai-compat

praisonai serve unified

SurfaceCommandScope
OpenAI serverpraisonai serve openaiStandalone — chat, completions, models, tool invocation
RAG compat flagpraisonai serve rag --openai-compatRAG microservice only, chat endpoint only
Unifiedpraisonai serve unifiedAll providers in a single server

Error Shape

All errors follow the OpenAI error format:

json
{
  "error": {
    "message": "Model not found",
    "type": "api_error"
  }
}

Best Practices

Set an API key for production

Always pass --api-key when running on a public or shared network. The default configuration accepts unauthenticated requests.

bash
praisonai serve openai --api-key $PRAISONAI_API_KEY --host 0.0.0.0
Bind to 0.0.0.0 for external access

The default 127.0.0.1 only accepts local connections. Use --host 0.0.0.0 to accept connections from other machines — always pair with --api-key.

bash
praisonai serve openai --host 0.0.0.0 --port 8765 --api-key $SECRET
Route model names with LiteLLM

The server passes model directly to the underlying LLM. Use LiteLLM-style model names (ollama/llama3, anthropic/claude-3-5-sonnet-20241022) to route to any provider.

When to use the unified server instead

Use praisonai serve unified when you need multiple protocol surfaces (MCP, A2A, agents) alongside the OpenAI-compat layer. Use serve openai for a lightweight standalone deployment that only exposes OpenAI endpoints.


Concurrency & streaming (PraisonAI #3771)

  • Non-blocking handlers. All four handlers — /v1/chat/completions, /v1/completions, /v1/models, /v1/tools/invoke — offload the underlying sync provider.invoke via asyncio.to_thread. A slow provider call on one request does not stall concurrent requests on the same server.
  • Simulated streaming is word-by-word. When the underlying provider doesn’t stream natively, the server splits the response by whitespace and emits one SSE data: frame per word (whitespace preserved). Previously it emitted one frame per character, which most OpenAI-SDK clients handle correctly but at multi-order-of-magnitude higher per-token overhead.
  • No configuration flag — the change is unconditional and backward-compatible for real OpenAI-SDK clients.

Related