66 lines
2.7 KiB
Python
66 lines
2.7 KiB
Python
"""Responses 的完成对象是最终依据;delta 仅用于过程显示。"""
|
||
|
||
import json
|
||
from collections.abc import Iterable
|
||
|
||
from muse.任务运行.接口 import 工具请求, 模型协议错误, 模型用量, 模型结果
|
||
|
||
|
||
def 解析响应(事件: Iterable[dict]) -> 模型结果:
|
||
响应 = None
|
||
片段 = []
|
||
for 项 in 事件:
|
||
类型 = 项.get("type")
|
||
if 类型 == "response.output_text.delta":
|
||
片段.append(项.get("delta", ""))
|
||
elif 类型 in {"response.completed", "response.failed", "response.incomplete"}:
|
||
响应 = 项.get("response")
|
||
if not isinstance(响应, dict):
|
||
raise 模型协议错误("Responses 终态缺少响应对象")
|
||
if 响应.get("status") != 类型.split(".")[1]:
|
||
raise 模型协议错误("Responses 事件与响应状态不一致")
|
||
elif 类型 == "error":
|
||
return 模型结果("failed", "".join(片段), None, None, 失败码=项.get("code"))
|
||
if 响应 is None:
|
||
return 模型结果("incomplete", "".join(片段), None, None, 失败码="TERMINAL_MISSING")
|
||
状态 = 响应["status"]
|
||
文本, 工具 = [], []
|
||
for 输出 in 响应.get("output", []):
|
||
if 输出.get("type") == "message":
|
||
for 内容 in 输出.get("content", []):
|
||
if 内容.get("type") == "output_text":
|
||
文本.append(内容["text"])
|
||
elif 内容.get("type") == "refusal":
|
||
状态 = "failed"
|
||
elif 输出.get("type") == "function_call":
|
||
try:
|
||
参数 = json.loads(输出["arguments"])
|
||
if not isinstance(参数, dict):
|
||
raise ValueError()
|
||
工具.append(工具请求(输出["call_id"], 输出["name"], 参数))
|
||
except (ValueError, KeyError):
|
||
raise 模型协议错误("模型工具调用参数不符合对象合同") from None
|
||
用量 = 响应.get("usage")
|
||
统计 = None
|
||
if 用量 is not None:
|
||
try:
|
||
统计 = 模型用量(
|
||
用量["input_tokens"],
|
||
用量["output_tokens"],
|
||
(用量.get("input_tokens_details") or {}).get("cached_tokens", 0),
|
||
)
|
||
except (KeyError, TypeError):
|
||
raise 模型协议错误("Responses 用量字段不完整") from None
|
||
if 工具 and 状态 == "completed":
|
||
状态 = "tool_calls"
|
||
return 模型结果(
|
||
状态,
|
||
"".join(文本),
|
||
响应.get("model"),
|
||
统计,
|
||
响应.get("id"),
|
||
tuple(工具),
|
||
回放协议="responses",
|
||
回放内容=tuple(响应.get("output", [])),
|
||
)
|