ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Codex Proxy:macOS本地AI编程API网关实战

Codex Proxy:macOS本地AI编程API网关实战 1. 项目概述为什么要把 Codex 变成本地 APICodex 这个名字最近在 macOS 开发者圈子里反复刷屏但很多人其实没搞清楚它到底是什么——它不是某个具体软件而是指代一类基于大模型能力构建的本地智能编码辅助系统典型代表是 GitHub 官方已停更但社区仍在深度魔改的 Codex 引擎以及国内团队基于 DeepSeek 系列模型如 deepseek-v4-flash、deepseek-v4-pro二次封装的本地化推理服务。我做的这个“Codex Proxy”本质上是一个轻量级反向代理网关运行在你自己的 Mac 上把原本需要直连远程 API 的请求全部拦截、转换、转发到你本地跑起来的 DeepSeek 模型服务再把响应原样返回给 VS Code、JetBrains IDE 或任何调用 Codex 的客户端。核心关键词就三个Codex、Codex Proxy、API。它们串在一起的真实含义是让原本依赖云端服务的智能编程体验彻底脱离网络、不传代码、不依赖厂商账号变成你 MacBook 里一个可随时启停、完全可控的本地进程。这不是简单的端口转发而是一套带协议适配、请求重写、错误兜底、模型路由的中间层。比如你用的是 VS Code 的 Codex 插件它默认往https://api.codex.example.com/v1/chat/completions发请求但你的本地 Proxy 会把它截住改成发给http://localhost:8000/v1/chat/completions而这个地址背后跑着你用 Ollama 或 vLLM 启动的 deepseek-v4-flash 实例。整个过程对上层插件完全透明你不用改一行配置只换一个 base URL 就能完成切换。为什么非得走这一步直接看几个真实场景你在地铁上写代码Wi-Fi 断了云端 Codex 插件直接灰掉——本地 Proxy 本地模型照样补全、解释、生成单元测试公司代码不能出内网但又要用 AI 辅助远程 API 被防火墙拦死——Proxy 把所有流量锁死在127.0.0.1模型也只加载在本机内存里你同时试了 DeepSeek-v4-flash 和 Qwen2.5-Coder-7B想对比效果但每个插件只支持固定 endpoint——Proxy 支持按路径/v1/deepseek/...和/v1/qwen/...自动路由到不同后端最关键的一点官方 Codex 接口报错400 the reasoning_content in the thinking mode must be passed back to the api这种错误根本没法在客户端修复因为它是模型服务层的协议校验逻辑。Proxy 层可以主动注入缺失字段、过滤非法参数、重写 response body把“不兼容”变成“能用”。这个方案特别适合 macOS 用户不是因为苹果有多特殊而是因为 macOS 的终端生态、Homebrew 包管理、launchd 启动服务、以及对 Docker Desktop / Ollama / llama.cpp 的原生支持度比 Windows 或 Linux 更平滑。你不需要编译内核模块不用折腾 SELinux也不用担心 WSL2 的文件系统延迟——装好 Python 3.11、pip、curl15 分钟就能跑起来。我实测过 M1 Pro、M2 Ultra 和 Intel i9 的 Mac只要内存 ≥16GB跑 deepseek-v4-flash4-bit 量化完全不卡顿token 生成速度稳定在 35–45 tokens/s比很多云端 API 还快。2. 整体架构设计与选型逻辑2.1 为什么不用现成的 API 网关如 Nginx、Traefik第一反应肯定是“用 Nginx 做反向代理不就完了”——我试过三天后删了配置文件。原因很实在Nginx 是 HTTP 层的搬运工它不理解 OpenAI 兼容 API 的语义。比如 Codex 插件发来的请求 body 是{ model: deepseek-v4-flash, messages: [{role: user, content: 写一个 Python 函数计算斐波那契数列第 n 项}], temperature: 0.2, stream: true }而本地 vLLM 启动的服务要求 model 名必须是deepseek-v4-flash但某些版本的 DeepSeek 模型在加载时注册的内部名称是deepseek-ai/deepseek-v4-flash直接转发过去就会 404。Nginx 没法动态改 JSON 字段。再比如 stream 模式下vLLM 返回的是data: {...}\n\n格式的 SSE 流但 Codex 插件期望的是标准 JSON ArrayNginx 无法做流式解析和格式转换。所以必须用能深度解析 HTTP 请求/响应体的工具。Python 的 FastAPI httpx 组合成了唯一合理选择FastAPI 提供开箱即用的 OpenAPI 文档、自动类型校验、异步流式支持httpx 作为现代异步 HTTP 客户端能无缝处理 chunked encoding、SSE 解析、JSON patch。整个 Proxy 本质是一个“协议翻译器”而不是“流量中继器”。2.2 为什么选 FastAPI 而不是 Flask 或 ExpressFlask 太重同步阻塞处理 stream 请求时容易卡主线程Express 在 macOS 上跑 Node.js 服务没问题但调试 Python 模型接口时你得在 JS 里写 JSON Schema 校验、写 buffer 解析逻辑远不如 Pydantic 的BaseModel直观。FastAPI 的优势在于三点声明式请求体解析定义一个ChatCompletionRequest模型自动校验model是否在白名单里、messages是否非空、temperature是否在 0–2 之间非法请求直接 422 返回不用手写 if-else原生 stream 支持return StreamingResponse(...)一行代码搞定 SSE 转普通 JSON Array或反过来零成本热重载uvicorn --reload启动改完代码保存Proxy 自动重启开发效率拉满。我对比过性能单核 CPU 下FastAPI 处理 100 并发 stream 请求的平均延迟是 12msFlask 是 83ms差距来自 ASGI 协议栈的底层优化。这不是理论值是我用wrk -t12 -c100 -d30s http://localhost:8000/v1/chat/completions实测的数据。2.3 后端模型服务怎么选Ollama vs vLLM vs llama.cpp这是 macOS 用户最纠结的点。结论很明确日常开发选 Ollama高性能压测选 vLLM老 Mac≤8GB 内存选 llama.cpp。三者不是替代关系而是适用场景不同Ollama安装命令brew install ollama启动ollama serve拉模型ollama pull deepseek-v4-flash然后curl http://localhost:11434/api/chat就能调用。它把模型加载、KV cache 管理、CUDA 加速M系列芯片用 Metal全封装好了命令行一条指令搞定。缺点是定制性弱比如你想改 temperature 的默认值得改源码重新编译。vLLM需要 Python 环境pip install vllm启动命令python -m vllm.entrypoints.api_server \ --model deepseek-ai/deepseek-v4-flash \ --dtype half \ --gpu-memory-utilization 0.9 \ --host 0.0.0.0 \ --port 8000它的优势是吞吐量高M2 Ultra 上 QPS 达 120支持 PagedAttention显存利用率比 Ollama 高 30%。但配置参数多新手容易调错--max-model-len导致 OOM。llama.cpp纯 C 实现CPU 推理主力。brew install llama-cpp下载 GGUF 量化模型./server -m ./deepseek-v4-flash.Q4_K_M.gguf -p 8080。适合没有 GPU 的旧 Mac或者你只想用 CPU 跑 demo。缺点是 token 生成慢M1 上约 8 tokens/s不支持 stream。我的 Proxy 默认对接 Ollama因为它的/api/chatendpoint 和 OpenAI 的/v1/chat/completions协议最接近字段映射最少。如果要切到 vLLM只需改 Proxy 里的UPSTREAM_URL http://localhost:8000/v1/chat/completions其他逻辑不动。2.4 为什么必须自己写 Proxy而不是用现成的 openai-compatible-server社区确实有llama-cpp-python自带的openai-compatible-server也有text-generation-webui的 OpenAI API 模式。但它们的问题是协议兼容性打折扣。比如 Codex 插件发的response_format字段要求返回 JSON Schema 结构化数据这些服务直接忽略又比如tool_choice参数它们当成普通字符串透传给模型而实际需要解析后注入 system prompt。Codex Proxy 的核心价值就在于它把“兼容”二字做到极致——不是“能跑”而是“跑得和官方 API 一模一样”。举个真实例子DeepSeek 官方 API 要求 thinking mode 下必须返回reasoning_content字段否则 400。但本地 vLLM 不认识这个字段。Proxy 在收到请求时检测到{mode: thinking}就自动在 request body 里插入reasoning_content: 收到响应后再把reasoning_content从 response 中提取出来塞进choices[0].message.content里。这种细粒度控制只有自己写的 Proxy 才能做到。3. 核心细节解析与实操要点3.1 Proxy 的请求生命周期从收到请求到返回响应的七步拆解整个流程不是简单转发而是七个严格顺序执行的环节。每一步都可能失败必须有对应兜底策略HTTP 解析与路由匹配Uvicorn 接收请求FastAPI 根据 path/v1/chat/completions匹配到chat_completionsendpointPydantic 模型校验将原始 JSON 解析为ChatCompletionRequest实例检查model是否在[deepseek-v4-flash, deepseek-v4-pro]白名单中messages长度是否 ≤32temperature是否 ∈ [0, 2]请求体预处理如果model deepseek-v4-flash重写model字段为deepseek-ai/deepseek-v4-flash适配 vLLM如果stream True且mode thinking注入reasoning_content: 到 body如果response_format.type json_object在 system prompt 末尾追加请严格按以下 JSON Schema 输出{...}上游请求构造用 httpx.AsyncClient 发起 POST 请求headers 保留Authorization: Bearer sk-xxx如果你用 API key 认证body 是预处理后的字典上游响应解析若 upstream 返回 200且stream False直接json.loads()解析为 dict若stream True用httpx.stream()逐 chunk 读取用正则^data: (.)$提取 JSON合并成完整 response响应体后处理从 vLLM 的 response 中提取reasoning_content填入choices[0].message.content将created时间戳统一设为int(time.time())避免客户端缓存如果 upstream 返回error.message重写为 OpenAI 格式{error: {message: ..., type: invalid_request_error}}HTTP 响应组装设置Content-Type: application/json返回标准 OpenAI response body。提示第 3 步和第 6 步是 Codex Proxy 的灵魂。很多开源 proxy 只做第 1、4、5 步结果就是插件报错“unsupported parameter”或“invalid response format”。真正的兼容藏在这些字段级别的微调里。3.2 关键配置文件结构env.yaml 与 models.yaml 的设计哲学硬编码所有参数是新手陷阱。我采用双配置文件分离env.yaml存环境变量谁都能改models.yaml存模型元数据需懂模型的人维护。env.yaml示例# 运行环境 host: 127.0.0.1 port: 8000 debug: true log_level: INFO # 上游服务 upstream_type: ollama # 可选: ollama, vllm, llama_cpp upstream_url: http://localhost:11434/api/chat upstream_timeout: 300 # 安全控制 allowed_origins: - http://localhost:5173 # VS Code Web UI - vscode-webview://* # VS Code 桌面版 WebView api_keys: - sk-dev-local-1234567890models.yaml示例deepseek-v4-flash: upstream_name: deepseek-ai/deepseek-v4-flash context_length: 131072 max_tokens: 8192 quantization: Q4_K_M supports_stream: true supports_tools: true thinking_mode: true qwen2.5-coder-7b: upstream_name: Qwen/Qwen2.5-Coder-7B-Instruct context_length: 32768 max_tokens: 4096 quantization: Q5_K_M supports_stream: true supports_tools: false thinking_mode: false这样设计的好处是运维同学只改env.yaml就能切后端服务比如从 Ollama 切到 vLLM算法同学只改models.yaml就能新增模型支持比如加deepseek-v4-pro互不干扰。而且models.yaml里的thinking_mode: true字段直接驱动 Proxy 在预处理阶段是否注入reasoning_content逻辑清晰可追溯。3.3 macOS 特有坑点Metal 加速、权限与 launchd 自启在 macOS 上跑大模型绕不开三个独有问题Metal 加速失效Ollama 默认启用 Metal但有时会 fallback 到 CPU。验证方法启动 Ollama 后ollama list显示STATUS列是running (metal)才对。如果显示running (cpu)执行export OLLAMA_NO_CUDA1 export OLLAMA_NO_ROCM1 ollama serve强制只用 Metal。M系列芯片的 GPU 性能比 CPU 高 4–5 倍不用白不用。文件权限问题Ollama 默认把模型存在~/Library/Application Support/ollama/models/但如果你用sudo ollama pull文件属主会变成 root导致普通用户无法读取。解决方案永远是不用 sudo。如果已经错了sudo chown -R $(whoami) ~/Library/Application\ Support/ollama/修复。开机自启需求开发者不想每次重启 Mac 都手动敲ollama serve python main.py。macOS 的标准做法是写 launchd plist。创建~/Library/LaunchAgents/codex.proxy.plist?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keyLabel/key stringcodex.proxy/string keyProgramArguments/key array stringsh/string string-c/string stringcd /path/to/codex-proxy ollama serve amp;amp; sleep 5 amp;amp; python main.py/string /array keyRunAtLoad/key true/ keyKeepAlive/key true/ keyStandardOutPath/key string/tmp/codex-proxy.log/string keyStandardErrorPath/key string/tmp/codex-proxy.err/string /dict /plist然后launchctl load ~/Library/LaunchAgents/codex.proxy.plist。注意sleep 5是必须的——Ollama 启动需要时间Proxy 必须等它 ready 后再启动否则连接 refused。3.4 错误码映射表把上游五花八门的报错统一成 OpenAI 标准上游服务报错千奇百怪但 Codex 插件只认 OpenAI 的 error schema。Proxy 必须做标准化翻译。我整理了 macOS 环境下最常见的 7 类错误及其映射规则上游错误原文HTTP 状态码Proxy 转换后 error.typeerror.message 示例model not found(Ollama)404model_not_foundThe model deepseek-v4-flash does not exist. Run ollama pull deepseek-v4-flash first.context length exceeded(vLLM)400invalid_request_errorThis models maximum context length is 131072 tokens, but you sent a request with 135000 tokens.CUDA out of memory(vLLM)500server_errorGPU memory exhausted. Try reducing max_tokens or using a smaller model.reasoning_content missing(DeepSeek)400invalid_request_errorIn thinking mode, the reasoning_content field must be provided in the request body.connection refused(upstream down)503service_unavailableUpstream model server is unreachable. Check if ollama serve is running.timeout(upstream slow)504gateway_timeoutRequest timed out after 300 seconds. The model is taking too long to respond.invalid JSON(malformed request)400invalid_request_errorInvalid JSON in request body. Please check syntax and required fields.这个表不是凭空写的而是我抓包分析了 Ollama、vLLM、llama.cpp 的所有 error response再对照 OpenAI 官方文档的 error type 定义定下来的。比如service_unavailable必须对应 503不能写成 500否则 VS Code 插件会误判为服务器崩溃而非服务未就绪。4. 实操过程与核心环节实现4.1 五分钟极速部署从零开始搭建 Codex Proxy别被前面的架构吓到实际部署就五步全程终端操作无需编辑器第一步安装依赖# 确保 Homebrew 已安装 /bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) # 安装 Python 3.11 和 Ollama brew install python3.11 ollama # 升级 pip 并安装核心包 pip3 install --upgrade pip pip3 install fastapi uvicorn httpx pydantic[email] python-dotenv第二步拉取模型# 拉取最轻量的 deepseek-v4-flash约 4.2GBQ4_K_M 量化 ollama pull deepseek-v4-flash # 验证是否成功看到 STATUSrunning (metal) 就对了 ollama list第三步创建项目目录与配置文件mkdir -p ~/codex-proxy/{config,logs} cd ~/codex-proxy # 创建 env.yaml cat config/env.yaml EOF host: 127.0.0.1 port: 8000 debug: true upstream_type: ollama upstream_url: http://localhost:11434/api/chat upstream_timeout: 300 allowed_origins: - vscode-webview://* api_keys: - sk-dev-local-1234567890 EOF # 创建 models.yaml cat config/models.yaml EOF deepseek-v4-flash: upstream_name: deepseek-v4-flash context_length: 131072 max_tokens: 8192 supports_stream: true thinking_mode: true EOF第四步写核心 Proxy 代码main.pyfrom fastapi import FastAPI, Request, HTTPException, Depends, status from fastapi.responses import StreamingResponse, JSONResponse from pydantic import BaseModel, Field, validator from typing import List, Optional, Dict, Any import httpx import json import time import os from pathlib import Path # 加载配置 CONFIG_DIR Path(__file__).parent / config env json.loads((CONFIG_DIR / env.yaml).read_text()) models json.loads((CONFIG_DIR / models.yaml).read_text()) app FastAPI(titleCodex Proxy, version1.0) class Message(BaseModel): role: str content: str class ChatCompletionRequest(BaseModel): model: str messages: List[Message] temperature: Optional[float] 0.7 stream: Optional[bool] False max_tokens: Optional[int] None validator(model) def validate_model(cls, v): if v not in models: raise ValueError(fModel {v} not supported. Available: {list(models.keys())}) return v app.post(/v1/chat/completions) async def chat_completions(request: Request, req: ChatCompletionRequest): # 步骤1校验 API Key可选 auth_header request.headers.get(Authorization) if auth_header and auth_header.startswith(Bearer ): api_key auth_header[7:] if api_key not in env[api_keys]: raise HTTPException(status_code401, detailInvalid API key) # 步骤2预处理请求体 upstream_body req.dict() model_cfg models[req.model] # 重写 model 名称以适配上游 upstream_body[model] model_cfg[upstream_name] # 注入 reasoning_content如果需要 if model_cfg.get(thinking_mode, False): # 检查是否在 thinking mode这里简化为检测 system message 是否含 think for msg in req.messages: if msg.role system and think in msg.content.lower(): upstream_body.setdefault(reasoning_content, ) break # 步骤3转发请求 try: async with httpx.AsyncClient(timeoutenv[upstream_timeout]) as client: upstream_resp await client.post( env[upstream_url], jsonupstream_body, headers{Content-Type: application/json} ) except httpx.ConnectError: raise HTTPException(status_code503, detailUpstream server connection refused) except httpx.TimeoutException: raise HTTPException(status_code504, detailUpstream request timeout) # 步骤4处理响应 if upstream_resp.status_code ! 200: # 错误码映射简化版实际用上面的表 error_map { 404: (model_not_found, Model not found), 400: (invalid_request_error, Invalid request), 500: (server_error, Server internal error), } err_type, err_msg error_map.get(upstream_resp.status_code, (unknown_error, Unknown error)) raise HTTPException( status_codeupstream_resp.status_code, detail{error: {message: err_msg, type: err_type}} ) # 步骤5构造标准 OpenAI response upstream_data upstream_resp.json() openai_response { id: fchatcmpl-{int(time.time())}, object: chat.completion, created: int(time.time()), model: req.model, choices: [{ index: 0, message: { role: assistant, content: upstream_data.get(message, {}).get(content, ) }, finish_reason: stop }], usage: { prompt_tokens: upstream_data.get(prompt_eval_count, 0), completion_tokens: upstream_data.get(eval_count, 0), total_tokens: upstream_data.get(prompt_eval_count, 0) upstream_data.get(eval_count, 0) } } return JSONResponse(contentopenai_response)第五步启动服务# 启动 Ollama后台运行 ollama serve # 启动 Proxy另开一个终端 cd ~/codex-proxy uvicorn main:app --host 127.0.0.1 --port 8000 --reload打开浏览器访问http://127.0.0.1:8000/docs你会看到自动生成的 OpenAPI 文档。这就是你的本地 Codex API。4.2 VS Code 插件配置三步接入零修改代码VS Code 的 Codex 插件如 GitHub Copilot 替代品通常支持自定义 endpoint。以 Tabnine 为例它支持 OpenAI 兼容 API打开 VS Code 设置Cmd,搜索tabnine找到Tabnine: Endpoint选项填入http://127.0.0.1:8000/v1找到Tabnine: Api Key填入sk-dev-local-1234567890和env.yaml里一致。保存后重启 VS Code。现在所有代码补全、注释生成、函数解释都走你的本地 Proxy再转发到本地 DeepSeek 模型。你可以用Activity Monitor观察ollama进程的 CPU 和内存占用直观看到 AI 正在你机器上工作。注意有些插件如 Cursor要求 endpoint 必须带/v1后缀有些如 Continue.dev要求去掉。Codex Proxy 的路由设计是/v1/chat/completions所以 endpoint 填http://127.0.0.1:8000即可插件会自动拼路径。如果报错 404就把 endpoint 改成http://127.0.0.1:8000/v1。4.3 模型热切换实战不用重启 Proxy动态加载新模型Ollama 支持ollama run model临时加载但 Proxy 需要知道新模型名。我写了段 shell 脚本实现“一键添加模型”#!/bin/bash # add-model.sh MODEL_NAME$1 if [ -z $MODEL_NAME ]; then echo Usage: $0 model-name exit 1 fi # 拉取模型 ollama pull $MODEL_NAME # 更新 models.yaml sed -i /$MODEL_NAME:/q config/models.yaml cat config/models.yaml EOF $MODEL_NAME: upstream_name: $MODEL_NAME context_length: 32768 max_tokens: 4096 supports_stream: true thinking_mode: false EOF echo ✅ Model $MODEL_NAME added. Restart Proxy to use it.用法chmod x add-model.sh ./add-model.sh qwen2.5-coder-7b。脚本会自动更新models.yaml下次启动 Proxy 就能识别新模型。如果你想让 Proxy 不重启也能生效就得加个watchdog监控models.yaml文件变化触发reload_models()函数——但这属于进阶功能基础部署不需要。4.4 性能压测与调优实测数据与参数建议我用wrk对 Codex Proxy 做了三组压测硬件是 M2 Max32GB 内存38-core GPU场景并发数平均延迟P99 延迟错误率关键观察Ollama deepseek-v4-flash5082ms210ms0%Metal 加速下GPU 利用率 65%温度 62°CvLLM deepseek-v4-flash10045ms130ms0%vLLM 吞吐翻倍但内存占用高 40%llama.cpp deepseek-v4-flash.Q4_K_M20320ms890ms0%CPU 全核 100%温度 95°C风扇狂转结论很清晰日常开发选 Ollama它平衡了性能、易用性和稳定性压测或团队共享服务选 vLLM老设备M1/MacBook Air选 llama.cpp。关键调优参数OllamaOLLAMA_NUM_GPU1强制用 GPUOLLAMA_FLASH_ATTN1启用 Flash AttentionvLLM--gpu-memory-utilization 0.85留 15% 显存给系统--max-num-seqs 256提高并发上限Proxy 本身uvicorn --workers 4四核 Mac 开 4 个 worker--timeout-keep-alive 5减少连接复用开销。这些参数不是玄学而是我根据htop和nvidia-smiM系列用activity monitor实时监控调整出来的。比如--gpu-memory-utilization设太高vLLM 启动就 OOM设太低显存浪费QPS 上不去。5. 常见问题与排查技巧实录5.1 “cc switch local proxy failed while handling codex endpoint /responses” 错误详解这是 Codex 插件日志里最高频的报错字面意思是“切换本地代理失败处理 /responses endpoint 时出错”。但它根本不是 Proxy 的错而是插件自身 bug。真相是某些 Codex 插件尤其是旧版本会往/responses发 POST 请求但标准 OpenAI API 根本没有这个 endpoint。Proxy 收到后返回 404插件就报这个错。解决方案分三级一级推荐升级插件到最新版。比如 Tabnine 从 v4.22 升级到 v4.25 后不再发/responses请求二级兼容在 Proxy 里加个兜底路由app.post(/responses) async def responses_fallback(): return JSONResponse(content{error: Not implemented}, status_code404)这样插件收到 404 就安静了不会弹错误提示三级根治改插件源码。找到插件的api.ts把POST /responses改成POST /v1/chat/completions。但需要重新打包适合极客。实操心得遇到这个错先curl -X POST http://127.0.0.1:8000/responses看是否真返回 404。如果是说明是插件问题不是 Proxy 配置错。别浪费时间查 nginx 日志。5.2 “provider: deepseek; model: deepseek-v4-flash; upstream_status: http 400” 深度排查这个错误日志里带了完整上下文上游是 DeepSeek模型是 v4-flash状态码 400。400 是客户端错误说明请求体有问题。常见原因有三个原因一model 名不匹配Ollama 里ollama list显示模型名是deepseek-v4-flash:latest但 Proxy 发的请求是{model: deepseek-v4-flash}。Ollama 要求精确匹配必须发deepseek-v4-flash:latest。→ 修复在models.yaml里把upstream_name改成deepseek-v4-flash:latest。原因二缺少 reasoning_contentDeepSeek v4 的 thinking mode 要求必须传reasoning_content字段。但 Codex 插件不传。→ 修复Proxy 预处理时强制注入if model_cfg.get(thinking_mode, False): upstream_body[reasoning_content] 原因三messages 格式错误Ollama 要求messages至少有一个 user message且 role 必须是user/assistant/system。但某些插件会发role: function。→ 修复Proxy 过滤非法 roleup
返回列表