GoClaw's gateway serves an OpenAI-compatible POST /v1/chat/completions, streaming and non-streaming, so OpenAI SDK clients and tools such as Cursor can talk to the default agent without a GoClaw-specific client.
internal/gateway/openai_compat.go
flowchart TD
REQ["<b>POST /v1/chat/completions</b>"] --> PARSE["<b>Parse OAIRequest</b><br/>• model, messages, stream (max_tokens, temperature ignored)<br/>• Last user message = prompt; earlier messages = history<br/>• Session: openai-compat:<client-ip> (no history written)<br/>• Scope: default agent + channel=openai"]
PARSE --> SUB{"/subagents command?"}
SUB -->|Yes| SUBH["<b>HandleSubagentCommand</b><br/>Pass-through"]
SUB -->|No| STR{"stream?"}
STR -->|"stream = false"| SYNC["<b>ProcessStream → DrainToResultObserved</b><br/>→ OAIResponse JSON"]
STR -->|"stream = true"| SSE["<b>ProcessStream events → SSE stream</b><br/>data: {"choices":[{"delta":{"content":…}}]}<br/>data: [DONE]"]
| # | Feature | OpenClaw | GoClaw | Status |
|---|---|---|---|---|
| 1.1 | /v1/chat/completions POST |
✅ | ✅ HandleChatCompletions |
Done |
| 1.2 | Streaming (SSE) | ✅ | ✅ handleStreamingCompletion — text/event-stream |
Done |
| 1.3 | Non-streaming | ✅ | ✅ JSON response | Done |
| 1.4 | Token usage estimation | ✅ | ✅ ~4 chars/token approximation | Done |
| 1.5 | Subagent command routing | — | ✅ HandleSubagentCommand pass-through (/subagents spawn|list|status|stop) |
GoClaw only |
| 1.6 | Session persistence | ✅ | A session openai-compat:<ip> is created, but no history is written to it: the client sends the conversation in messages |
Partial |
| 1.7 | Error formatting | ✅ | ✅ UserFacingErrorMessage → OpenAI error JSON |
Done |
| 1.8 | Trust proxy IP | — | ✅ extractIPFromRequest respects trustProxy config |
GoClaw only |
| 1.9 | Model routing | model → provider | Uses default agent | Partial |
| 1.10 | /v1/models list |
✅ | — | TODO |
| 1.11 | Function calling pass-through | ✅ | — | TODO |
| 1.12 | Embeddings API | ✅ | — | TODO |
| 1.13 | Multi-model support | ✅ model param → provider | — | TODO |
| Type | Fields |
|---|---|
OAIRequest |
model, messages[], stream, max_tokens, temperature (max_tokens and temperature are read and ignored; each message's content must be a string) |
OAIResponse |
id, object, created, model, choices[], usage |
OAIStreamChunk |
id, object, created, model, choices[] (index, delta, finish_reason) |
OAIUsage |
prompt_tokens, completion_tokens, total_tokens |
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1760000000,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"chunk"},"finish_reason":null}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1760000000,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"","content":""},"finish_reason":"stop"}]}
data: [DONE]
Two more frames can appear in a stream. data: {"buffer_reset":true} tells the
client to discard the text received so far (the reply is being retried).
data: {"error":{"message":"…"}} reports a failure and ends the stream: no finish
chunk and no data: [DONE] follow it.