AiProxyController(OpenAI 兼容代理转发)¶
cn.myapps.ai.controller.AiProxyController —— 将客户端 OpenAI 兼容大模型请求(chat/completions、completions、embeddings、models 等)反向代理转发至配置的上游网关(ai.base-url,如 http://ai-proxy.myapps.cn/v1)。透传上游响应(含状态码、响应头、响应体),并对请求体做必要规整(注入默认 model、去除 stream / stream_options、量化 temperature)。
基址特殊:类级
@RequestMapping(path = "/ai/api")为**硬编码**(不使用${myapps.context-path.ai:}占位符)。因此无论部署侧如何配置 context-path,该控制器的路径前缀恒为/ai/api。这是为了在 lite 统一部署(context-path 为/)下与各模块路径前缀/ai保持一致。
- Tag:AI 代理
- 基址:
/ai/api(硬编码) - 鉴权:否(obpm-ai 无鉴权过滤器,详见 index.md「鉴权说明」)
- 响应:透传上游响应,
ResponseEntity<byte[]>,非 统一Resource
1. 转发 OpenAI 兼容 API¶
按 /llm-proxy/** 后的子路径(如 chat/completions)转发至上游 {ai.base-url}/<子路径>,支持七种 HTTP 方法。请求体字节透传(POST/PUT/PATCH 的 JSON 体在转发前会被规整),上游响应字节与状态码、响应头原样回传(去除 hop-by-hop 与 Access-Control-* 头,避免与本机 CorsFilter 冲突)。
- 接口类型:REST 资源
- 请求方式:
GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS(同一 Java 方法,方法级@RequestMapping(method = {...})列举全部七种) - 请求路径:
/ai/api/llm-proxy/**(通配;如/ai/api/llm-proxy/chat/completions、/ai/api/llm-proxy/v1/embeddings、/ai/api/llm-proxy/models) - 鉴权:否
- Tag:AI 代理
请求参数¶
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| 子路径 | path | string | 是 | /llm-proxy/ 之后的路径片段(如 chat/completions),转发至上游 {ai.base-url}/<子路径> |
| query | query | string | 否 | 任意查询串,原样拼接到上游 URI |
| headers | header | — | 否 | 客户端请求头转发至上游,但跳过 hop-by-hop 头(connection/keep-alive/transfer-encoding/host 等)与 Authorization(避免误用客户端 key) |
| body | body | byte[] | 否 | 请求体字节;POST/PUT/PATCH 的 application/json 体在转发前会被规整(见下文「请求体规整」) |
请求体规整(仅 POST/PUT/PATCH + application/json)¶
- 注入默认
model:若请求体未指定model字段,注入aiConfig.getModel()。 chat/completions路径额外处理:- 强制覆盖
model为aiConfig.getModel(); - 移除
stream、stream_options字段(上游如智谱不兼容 LangChain 显式的stream:false); temperature量化为两位小数(BigDecimal.setScale(2, HALF_UP)),避免 Float 误差。
请求示例¶
POST /ai/api/llm-proxy/chat/completions HTTP/1.1
Content-Type: application/json
{
"messages": [
{ "role": "user", "content": "你好" }
],
"temperature": 0.7
}
转发后上游实际收到(假设 aiConfig.getModel() 为 qwen-plus):
响应¶
结构:Mono<ResponseEntity<byte[]>>,透传上游响应,非 统一 Resource。
- 状态码:上游返回的 HTTP 状态码原样回传。
- 响应头:上游响应头透传(跳过
transfer-encoding/connection/Access-Control-*)。 - 响应体:上游响应字节原样回传(可能是 JSON,也可能是 SSE 流式
data:行,由上游决定)。
成功示例(HTTP 200,JSON 形式,OpenAI 兼容):
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"model": "qwen-plus",
"choices": [
{ "index": 0, "message": { "role": "assistant", "content": "你好!" }, "finish_reason": "stop" }
]
}
失败示例:
- 上游错误(如 401 / 429):原样透传上游状态码与响应体。
- 转发异常(HTTP 500):
{"error":{"message":"<异常消息>"}},Content-Type由WebClient默认决定。 - 预检
OPTIONS(HTTP 200,空体):直接由控制器返回,不转发上游(上游通常无 CORS 头),正常情况下由本机CorsFilter在过滤器链最前处理。
异步超时¶
该端点经 AiProxyAsyncTimeoutInterceptor 设置 Servlet 异步超时为 300000 ms(5 分钟)(见 cn.myapps.ai.config.AiProxyWebMvcConfig),避免大模型慢响应被全局默认超时中断。