【mitmproxy】提取 OpenCode 的 API 接口

前面介绍了 如何通过 mitmproxy的本地捕获代理模式获取 OpenCode 发起的 AI API 请求的详细信息

本文介绍如何提取 OpenCodeAPI 接口

捕获到opencode.ai的流量后,可以导出https://opencode.ai/zen/v1/messages接口的CURL

简化一下导出的CURL(譬如:简化系统提示词,工具列表),发给AI,让AI 转化为 Python 代码

然后再让AI优化微调一下(譬如:抽取函数send_message,创建SSLContext对象,添加重试机制)

调式成功:

示例代码:

python 复制代码
import json
import ssl
import time
from typing import Optional, Dict, Any

import httpx

URL = "https://opencode.ai/zen/v1/messages"
DEFAULT_HEADERS = {
    "Content-Type": "application/json",
    "User-Agent": "ai-sdk/anthropic/3.0.71 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.13",
    "anthropic-version": "2023-06-01",
    "x-api-key": "public",
    "x-opencode-client": "cli",
    "Connection": "close",
    "Accept": "*/*",
    "Host": "opencode.ai",
}
DEFAULT_PROJECT = "5941d5072d7e941c77cbda55b6a9a3a45bf3a089"
DEFAULT_SESSION = "ses_23ac15dd8ffeiKiFBtkudFHNd2"


def send_message(
        model: str,
        message: str,
        project: Optional[str] = None,
        session: Optional[str] = None,
        request_id: Optional[str] = None,
        system_prompt: Optional[str] = None,
        max_tokens: int = 32000,
        max_retries: int = 3,
        timeout: int = 60,
) -> Dict[str, Any]:
    """
    向 opencode.ai 发送消息并获取 AI 回复。

    参数:
        model: 模型名称,如 "big-pickle"
        message: 用户消息内容
        project: x-opencode-project(可选)
        session: x-opencode-session(可选)
        request_id: x-opencode-request(可选,默认自动生成)
        system_prompt: 系统提示词(可选)
        max_tokens: 最大 token 数
        max_retries: 失败重试次数
        timeout: 请求超时(秒)

    返回:
        包含以下键的字典:
            - text: AI 文本回复
            - thinking: AI 思考内容(如有)
            - full: 原始完整响应数据列表
    """
    if request_id is None:
        import uuid
        request_id = f"msg_{uuid.uuid4().hex[:20]}"

    headers = {
        **DEFAULT_HEADERS,
        "x-opencode-project": project or DEFAULT_PROJECT,
        "x-opencode-session": session or DEFAULT_SESSION,
        "x-opencode-request": request_id,
    }

    system_text = system_prompt or (
        "You are opencode, an interactive CLI tool that helps users with software engineering tasks. "
        "Use the instructions below and the tools available to you to assist the user."
    )

    payload = {
        "model": model,
        "max_tokens": max_tokens,
        "stream": True,
        "system": [{"type": "text", "text": system_text}],
        "messages": [
            {
                "role": "user",
                "content": [{"type": "text", "text": message}],
            }
        ],
    }

    ssl_context = ssl.create_default_context()

    for attempt in range(max_retries):
        try:
            full_response = []
            with httpx.Client(http2=False, verify=ssl_context, timeout=timeout) as client:
                with client.stream("POST", URL, headers=headers, json=payload) as response:
                    if response.status_code != 200:
                        raise RuntimeError(f"HTTP {response.status_code}: {response.text}")

                    text_parts = []
                    thinking_parts = []
                    current_event = None

                    for line in response.iter_lines():
                        if line.startswith("event:"):
                            current_event = line.split(":", 1)[1].strip()
                        elif line.startswith("data:") and current_event:
                            data_str = line.split(":", 1)[1].strip()
                            try:
                                data_obj = json.loads(data_str)
                                full_response.append(data_obj)
                                delta = data_obj.get("delta", {})
                                if delta.get("type") == "thinking_delta":
                                    thinking_parts.append(delta["thinking"])
                                elif delta.get("type") == "text_delta":
                                    text_parts.append(delta["text"])
                            except json.JSONDecodeError:
                                pass

            return {
                "text": "".join(text_parts),
                "thinking": "".join(thinking_parts),
                "full": full_response,
            }

        except (httpx.ConnectError, httpx.ReadError, httpx.RemoteProtocolError) as e:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt
                time.sleep(wait_time)
            else:
                raise RuntimeError(f"请求失败(已重试 {max_retries} 次): {e}") from e


if __name__ == "__main__":
    result = send_message("big-pickle", "你好")

    if result["thinking"]:
        print("<thinking>")
        print(result["thinking"])
        print("</thinking>")
    print(result["text"])
相关推荐
andxe3 分钟前
安科士 AndXe 技术博客:400G QSFP112 SR4 光模块|AI 算力与超算短距互联最优方案
网络·人工智能·光模块·光通信
shiyi.十一1 小时前
第2章:应用层 — 知识要点与架构
网络·计算机网络·架构
DFT计算杂谈2 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
中微极客3 小时前
2026主流AI Agent框架技术选型与性能对比
运维·网络·人工智能
猿的天空7 小时前
机器人双手迎来全栈训练系统:灵初智能EgoSteer让灵巧手无所不能
网络·人工智能·计算机·ai·程序员·机器人·编程
Xzaveir_7777 小时前
OPPO、vivo、荣耀号码认证:多终端拨测矩阵与异常复现
大数据·网络·科技·矩阵·产品经理·ai-native
wuqingshun3141597 小时前
从网络角度来看,用户从输入网址到网页显示,期间发生了什么?
网络
牛马工作号8 小时前
Zigbee 专业详解:从协议到排障
网络·物联网·安全
牛马工作号8 小时前
Wi‑Fi 完全指南:从 802.11 协议到 Wi‑Fi 7、AC+AP 与 Mesh 工程组网
网络·网络协议·智能路由器
智慧光迅AINOPOL8 小时前
智慧工厂全光网怎么扛住AGV与强电磁?光电复合缆与工业级方案
网络·全光网解决方案·全光网·酒店全光解决方案·泛住宿全光网解决方案