🔄 卡若AI 同步 2026-02-25 09:31 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个

This commit is contained in:
2026-02-25 09:31:09 +08:00
parent e615af3678
commit 1b9e4eb537
3 changed files with 97 additions and 2 deletions

View File

@@ -13,7 +13,7 @@ from typing import Any, Dict, List, Optional, Tuple
import yaml
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.responses import HTMLResponse, StreamingResponse
from pydantic import BaseModel
# 仓库根目录(本脚本在 运营中枢/scripts/karuo_ai_gateway/main.py
@@ -253,6 +253,19 @@ class OpenAIChatCompletionsRequest(BaseModel):
stream: Optional[bool] = None
class OpenAIResponsesRequest(BaseModel):
"""
OpenAI Responses API 兼容(简化版):
- input: 字符串 / 数组
- model: 可选
- stream: 可选
"""
model: Optional[str] = "karuo-ai"
input: Any
stream: Optional[bool] = None
def _messages_to_prompt(messages: List[Dict[str, Any]]) -> str:
"""
优先取最后一条 user 消息;否则拼接全部文本。
@@ -301,6 +314,29 @@ def _template_reply(prompt: str, matched_skill: str, skill_path: str, error: str
"""
def _as_openai_stream(reply: str, model: str, created: int):
"""
OpenAI Chat Completions 流式SSE最小兼容实现。
"""
chunk0 = {
"id": f"chatcmpl-{created}",
"object": "chat.completion.chunk",
"created": created,
"model": model,
"choices": [{"index": 0, "delta": {"role": "assistant"}, "finish_reason": None}],
}
chunk1 = {
"id": f"chatcmpl-{created}",
"object": "chat.completion.chunk",
"created": created,
"model": model,
"choices": [{"index": 0, "delta": {"content": reply}, "finish_reason": "stop"}],
}
yield f"data: {json.dumps(chunk0, ensure_ascii=False)}\n\n"
yield f"data: {json.dumps(chunk1, ensure_ascii=False)}\n\n"
yield "data: [DONE]\n\n"
@app.get("/", response_class=HTMLResponse)
def index():
"""简单欢迎页,可后续改为对话页。"""
@@ -465,7 +501,7 @@ async def openai_chat_completions(req: OpenAIChatCompletionsRequest, request: Re
_log_access(cfg, record)
now = int(time.time())
return {
payload = {
"id": f"chatcmpl-{now}",
"object": "chat.completion",
"created": now,
@@ -478,6 +514,63 @@ async def openai_chat_completions(req: OpenAIChatCompletionsRequest, request: Re
}
],
}
if bool(req.stream):
model_name = str(req.model or "karuo-ai")
return StreamingResponse(
_as_openai_stream(reply=reply, model=model_name, created=now),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
)
return payload
def _responses_input_to_prompt(input_value: Any) -> str:
"""
将 Responses API 的 input 转成 prompt。
"""
if isinstance(input_value, str):
return input_value.strip()
if isinstance(input_value, list):
chunks: List[str] = []
for item in input_value:
if isinstance(item, str):
chunks.append(item)
continue
if isinstance(item, dict):
# 兼容 {"type":"input_text","text":"..."} / {"text":"..."}
txt = str(item.get("text", "")).strip()
if txt:
chunks.append(txt)
continue
# 兼容 {"content":[...]} 形式
c = item.get("content")
if isinstance(c, list):
for part in c:
if isinstance(part, dict):
t = str(part.get("text", "")).strip()
if t:
chunks.append(t)
if chunks:
return "\n".join(chunks).strip()
return ""
@app.post("/v1/responses")
async def openai_responses(req: OpenAIResponsesRequest, request: Request):
"""
OpenAI Responses API 简化兼容:
- 复用 chat/completions 逻辑
"""
prompt = _responses_input_to_prompt(req.input)
if not prompt:
raise HTTPException(status_code=400, detail="empty input")
chat_req = OpenAIChatCompletionsRequest(
model=req.model or "karuo-ai",
messages=[{"role": "user", "content": prompt}],
stream=req.stream,
)
return await openai_chat_completions(chat_req, request)
@app.get("/v1/health")

View File

@@ -133,3 +133,4 @@
| 2026-02-25 05:57:01 | 🔄 卡若AI 同步 2026-02-25 05:56 | 更新:水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 06:51:14 | 🔄 卡若AI 同步 2026-02-25 06:51 | 更新:水桥平台对接、水溪整理归档、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 09:24:48 | 🔄 卡若AI 同步 2026-02-25 09:24 | 更新:水桥平台对接、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 09:28:50 | 🔄 卡若AI 同步 2026-02-25 09:28 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 |

View File

@@ -136,3 +136,4 @@
| 2026-02-25 05:57:01 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 05:56 | 更新:水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 06:51:14 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 06:51 | 更新:水桥平台对接、水溪整理归档、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 09:24:48 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 09:24 | 更新:水桥平台对接、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 09:28:50 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 09:28 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |