openai-http.ts 是 OpenClaw Gateway 提供的 OpenAI 兼容 HTTP API 实现。
这个文件实现了 /v1/chat/completions 端点,让 OpenClaw 可以伪装成 OpenAI API 服务器。
一、API 实现方式详解
1.1 整体架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ┌─────────────────────────────────────────────────────────────────────────────┐ │ 你的 PC App │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ OpenClawClient 类 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ health_check│ │ chat │ │create_session│ │ │ │ │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ │ │ ┌────────────────┼────────────────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ │ │ ChatSession 类 │ │ │ │ │ │ - history: 存储对话历史 │ │ │ │ │ │ - chat(): 发送消息并自动携带历史 │ │ │ │ │ └─────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ HTTP POST │ │ ▼ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ 网络请求 ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ OpenClaw Gateway │ │ http://服务器IP:18789 │ └─────────────────────────────────────────────────────────────────────────────┘
|
1.2 核心类详解
OpenClawClient 类 - 客户端入口
1 2 3 4 5 6 7 8 9 10 11
| class OpenClawClient: def __init__( self, base_url: str = "http://127.0.0.1:18789", token: str = "", timeout: int = 60, max_retries: int = 3 ): self.base_url = base_url self.token = token self._sessions = {}
|
职责:
- 管理与 OpenClaw 的连接配置
- 提供单次对话方法
chat()
- 创建和管理用户会话
create_session()
ChatSession 类 - 会话管理
1 2 3 4 5 6
| class ChatSession: def __init__(self, client: OpenClawClient, session_id: str): self.client = client self.session_id = session_id self.history = [] self.system_prompt = None
|
职责:
1.3 请求构建过程
当你调用 session.chat("你好") 时,内部发生了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| def chat(self, message: str, stream: bool = False): messages = [] if self.system_prompt: messages.append({ "role": "system", "content": "你是一个友好的助手" }) for msg in self.history: messages.append(msg.to_dict()) messages.append({"role": "user", "content": message}) response = self.client._chat_completion(messages, ...) self.history.append(Message(USER, message)) self.history.append(Message(ASSISTANT, response.content)) return response
|
1.4 HTTP 请求详解
非流式请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| def _chat_completion(self, messages, model, user_id): url = f"{self.base_url}/v1/chat/completions" payload = { "model": "openclaw:main", "messages": messages, "stream": False, "user": user_id } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}" } response = requests.post(url, headers=headers, json=payload, timeout=60) data = response.json() content = data["choices"][0]["message"]["content"] return content
|
实际发送的 HTTP 请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| POST /v1/chat/completions HTTP/1.1 Host: 1.12.242.236:18789 Authorization: Bearer da8cc9fa2142b41922e1abaa05e2f9b873cec612c1cad467 Content-Type: application/json
{ "model": "openclaw:main", "messages": [ {"role": "system", "content": "你是一个友好的助手"}, {"role": "user", "content": "我叫小明"}, {"role": "assistant", "content": "你好小明!"}, {"role": "user", "content": "我叫什么名字?"} ], "stream": false, "user": "user-001" }
|
流式请求 (SSE)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def _chat_stream(self, messages, model, user_id): url = f"{self.base_url}/v1/chat/completions" payload = { "model": "openclaw:main", "messages": messages, "stream": True } response = requests.post(url, json=payload, stream=True) for line in response.iter_lines(): if line.startswith(b"data: "): data_str = line[6:].decode() if data_str == "[DONE]": break data = json.loads(data_str) content = data["choices"][0]["delta"].get("content", "") if content: yield content
|
流式响应示例:
1 2 3 4 5 6 7 8 9 10 11
| data: {"choices":[{"delta":{"role":"assistant"}}]}
data: {"choices":[{"delta":{"content":"你"}}]}
data: {"choices":[{"delta":{"content":"叫"}}]}
data: {"choices":[{"delta":{"content":"小"}}]}
data: {"choices":[{"delta":{"content":"明"}}]}
data: [DONE]
|
二、OpenClaw 如何处理 HTTP 请求并回复
2.1 完整请求处理流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| ┌─────────────────────────────────────────────────────────────────────────────┐ │ 你的 PC App │ │ POST /v1/chat/completions │ │ {"messages": [...], "model": "openclaw:main"} │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ① HTTP 服务器层 (server-http.ts) │ │ - 接收 HTTP 请求 │ │ - 路由到对应处理器 │ │ - /v1/chat/completions → handleChatCompletions() │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ② 认证层 (auth.ts) │ │ - 验证 Authorization: Bearer <token> │ │ - Token 不匹配 → 返回 401 Unauthorized │ │ - 速率限制检查 → 超限返回 429 │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ 认证通过 ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ③ OpenAI API 处理层 (openai-http.ts) │ │ - 解析请求体 (model, messages, stream, user) │ │ - 确定目标 Agent (从 model 字段解析) │ │ - 确定 Session Key (基于 user 或生成) │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ④ Agent 路由层 │ │ - model: "openclaw:main" → 路由到 main agent │ │ - model: "openclaw:assistant" → 路由到 assistant agent │ │ - 加载 Agent 配置 (系统提示、工具、模型设置等) │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ⑤ 会话管理层 │ │ - 根据 session key 查找或创建会话 │ │ - 可能加载历史上下文 (如果服务端有持久化) │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ⑥ LLM 调用层 │ │ - 构建完整的 prompt (系统提示 + 用户消息) │ │ - 调用配置的 AI 模型 (MiniMax M2.5) │ │ - API: https://api.minimaxi.com/anthropic │ │ - 等待模型生成回复 │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ⑦ 响应构建层 │ │ - 将 AI 回复转换为 OpenAI 格式 │ │ - 非流式: 一次性返回完整 JSON │ │ - 流式: 逐块返回 SSE 事件 │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 你的 PC App │ │ 收到响应: │ │ { │ │ "id": "chatcmpl_xxx", │ │ "choices": [{"message": {"content": "你叫小明"}}], │ │ "model": "openclaw:main" │ │ } │ └─────────────────────────────────────────────────────────────────────────────┘
|
2.2 关键源码位置
| 层级 |
文件路径 |
职责 |
| HTTP 服务器 |
src/gateway/server-http.ts |
接收请求、路由分发 |
| 认证 |
src/gateway/auth.ts |
Token 验证、速率限制 |
| OpenAI API |
src/gateway/openai-http.ts |
处理 /v1/chat/completions |
| Agent 核心 |
src/agent/ |
Agent 逻辑、工具调用 |
| LLM 调用 |
src/llm/ |
调用各种 AI 模型 |
2.3 请求处理代码分析
让我展示 OpenClaw 中关键的处理逻辑:
让我继续查看关键的处理函数:
现在让我完整解释 OpenClaw 如何处理请求:
三、OpenClaw 处理请求的详细代码流程
3.1 入口点:handleOpenAiHttpRequest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
export async function handleOpenAiHttpRequest(req, res, opts) { const handled = await handleGatewayPostJsonEndpoint(req, res, { pathname: "/v1/chat/completions", auth: opts.auth, maxBodyBytes: 1024 * 1024, }); const payload = coerceRequest(handled.body); const stream = Boolean(payload.stream); const model = payload.model || "openclaw"; const user = payload.user; const agentId = resolveAgentIdForRequest({ req, model }); const sessionKey = resolveOpenAiSessionKey({ req, agentId, user }); const prompt = buildAgentPrompt(payload.messages); const runId = `chatcmpl_${randomUUID()}`; if (!stream) { const result = await agentCommand(commandInput, runtime, deps); const content = resolveAgentResponseText(result); sendJson(res, 200, { id: runId, choices: [{ message: { role: "assistant", content } }], }); } else { } }
|
3.2 消息格式转换:buildAgentPrompt
你发送的 OpenAI 格式消息:
1 2 3 4 5 6 7 8
| { "messages": [ {"role": "system", "content": "你是助手"}, {"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好!"}, {"role": "user", "content": "我叫什么?"} ] }
|
被转换为 OpenClaw 内部格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function buildAgentPrompt(messages) { const systemParts = []; const conversationEntries = []; for (const msg of messages) { if (msg.role === "system") { systemParts.push(msg.content); } else { conversationEntries.push({ role: msg.role, entry: { sender: msg.role === "user" ? "User" : "Assistant", body: msg.content } }); } } const message = buildAgentMessageFromConversationEntries(conversationEntries); return { message, extraSystemPrompt: systemParts.join("\n\n") }; }
|
3.3 Agent 执行:agentCommand
1 2 3 4 5 6 7 8 9
| const result = await agentCommand({ message: "...", extraSystemPrompt: "...", sessionKey: "openai:user-001:main", runId: "chatcmpl_xxx", deliver: false, messageChannel: "webchat" }, runtime, deps);
|
这个函数内部:
- 加载 Agent 配置(系统提示、工具列表等)
- 调用配置的 LLM(你配置的是 MiniMax M2.5)
- 等待 AI 生成回复
- 返回结果
3.4 流式响应处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| if (stream) { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); const unsubscribe = onAgentEvent((evt) => { if (evt.runId !== runId) return; if (evt.stream === "assistant") { const content = resolveAssistantStreamDeltaText(evt); writeSse(res, { id: runId, choices: [{ delta: { content: content }, finish_reason: null }] }); } }); agentCommand(commandInput, runtime, deps) .finally(() => { writeDone(res); res.end(); unsubscribe(); }); }
|
四、完整数据流图示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| ┌─────────────────────────────────────────────────────────────────────────────┐ │ 你的 PC App │ │ │ │ requests.post("/v1/chat/completions", json={ │ │ "model": "openclaw:main", │ │ "messages": [ │ │ {"role": "user", "content": "我叫小明"}, │ │ {"role": "assistant", "content": "你好小明!"}, │ │ {"role": "user", "content": "我叫什么?"} │ │ ] │ │ }) │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ════════════════╪════════════════ HTTP POST │ 网络传输 ════════════════╪════════════════ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ OpenClaw Gateway (server-http.ts) │ │ │ │ ① 接收请求 → ② 验证 Token → ③ 路由到 openai-http.ts │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ handleOpenAiHttpRequest (openai-http.ts) │ │ │ │ ① 解析 messages 数组 │ │ ② 构建内部 prompt 格式 │ │ ③ 确定 agentId = "main", sessionKey = "openai:user:main" │ │ ④ 调用 agentCommand() │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ agentCommand (commands/agent.ts) │ │ │ │ ① 加载 Agent 配置 (agents.defaults) │ │ ② 构建完整的 prompt: │ │ System: "你是小白🐰..." │ │ + extraSystemPrompt (如果有) │ │ + 对话历史: "User: 我叫小明\nAssistant: 你好小明!\nUser: 我叫什么?" │ │ ③ 调用 LLM │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ LLM 调用层 │ │ │ │ POST https://api.minimaxi.com/anthropic/v1/messages │ │ Headers: Authorization: Bearer <MiniMax API Key> │ │ Body: { │ │ "model": "MiniMax-M2.5", │ │ "messages": [...], │ │ "max_tokens": 8192 │ │ } │ │ │ │ 等待 MiniMax AI 生成回复... │ │ 返回: "你叫小明呀!🐰" │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 响应构建 │ │ │ │ sendJson(res, 200, { │ │ "id": "chatcmpl_xxx", │ │ "object": "chat.completion", │ │ "choices": [{ │ │ "message": { │ │ "role": "assistant", │ │ "content": "你叫小明呀!🐰" │ │ }, │ │ "finish_reason": "stop" │ │ }] │ │ }) │ └───────────────────────────────────┬─────────────────────────────────────────┘ │ ════════════════╪════════════════ HTTP Response │ 网络传输 ════════════════╪════════════════ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 你的 PC App │ │ │ │ response = requests.post(...) │ │ data = response.json() │ │ content = data["choices"][0]["message"]["content"] │ │ print(content) # "你叫小明呀!🐰" │ └─────────────────────────────────────────────────────────────────────────────┘
|
五、关键点总结
| 方面 |
说明 |
| API 格式 |
完全兼容 OpenAI Chat Completions API |
| 认证 |
Bearer Token,在 HTTP Header 中传递 |
| 会话管理 |
客户端维护 history,每次请求带上完整历史 |
| 多轮对话 |
OpenClaw 把所有 messages 拼成一个 prompt 发给 AI |
| 流式响应 |
使用 SSE (Server-Sent Events) 协议 |
| 底层 AI |
调用你配置的 MiniMax M2.5 模型 |
希望这个详细解释能帮助你理解整个系统是如何工作的!如果还有任何疑问,随时问我。