[测试、性能、安全、验收与归档]:完成全量测试验收与 OpenSpec 归档,包含 CLI 验收、NewAPI smoke、安全扫描和主 spec 更新
This commit is contained in:
+50
-13
@@ -48,7 +48,7 @@ class OpenAICompatibleLlmProvider:
|
||||
payload = {
|
||||
"model": self.config.llm_model,
|
||||
"messages": [{"role": m.role, "content": m.content} for m in messages],
|
||||
"stream": True,
|
||||
"stream": self.config.llm_stream,
|
||||
}
|
||||
yield from self._post_stream(
|
||||
f"{self.config.llm_base_url}/v1/chat/completions",
|
||||
@@ -60,7 +60,7 @@ class OpenAICompatibleLlmProvider:
|
||||
payload = {
|
||||
"model": self.config.llm_model,
|
||||
"input": [{"role": m.role, "content": m.content} for m in messages],
|
||||
"stream": True,
|
||||
"stream": self.config.llm_stream,
|
||||
}
|
||||
yield from self._post_stream(
|
||||
f"{self.config.llm_base_url}/v1/responses",
|
||||
@@ -86,18 +86,24 @@ class OpenAICompatibleLlmProvider:
|
||||
)
|
||||
try:
|
||||
with self.urlopen(request, timeout=self.timeout_s) as response:
|
||||
raw_body = _read_response_body(response)
|
||||
emitted = False
|
||||
for raw_line in response:
|
||||
line = raw_line.decode("utf-8", errors="replace").strip()
|
||||
if not line or not line.startswith("data:"):
|
||||
continue
|
||||
data = line.removeprefix("data:").strip()
|
||||
if data == "[DONE]":
|
||||
yield ReplyDelta("", finish_reason="stop")
|
||||
return
|
||||
event = json.loads(data)
|
||||
delta = parser(event)
|
||||
if delta is not None:
|
||||
if raw_body.lstrip().startswith(b"data:"):
|
||||
for raw_line in raw_body.splitlines():
|
||||
line = raw_line.decode("utf-8", errors="replace").strip()
|
||||
if not line or not line.startswith("data:"):
|
||||
continue
|
||||
data = line.removeprefix("data:").strip()
|
||||
if data == "[DONE]":
|
||||
yield ReplyDelta("", finish_reason="stop")
|
||||
return
|
||||
event = json.loads(data)
|
||||
delta = parser(event)
|
||||
if delta is not None:
|
||||
emitted = True
|
||||
yield delta
|
||||
else:
|
||||
for delta in _parse_non_stream_json(json.loads(raw_body.decode("utf-8"))):
|
||||
emitted = True
|
||||
yield delta
|
||||
if not emitted:
|
||||
@@ -145,5 +151,36 @@ def _parse_responses_sse(event: dict[str, Any]) -> ReplyDelta | None:
|
||||
return None
|
||||
|
||||
|
||||
def _parse_non_stream_json(event: dict[str, Any]) -> Iterable[ReplyDelta]:
|
||||
if "choices" in event:
|
||||
choices = event.get("choices") or []
|
||||
if choices:
|
||||
message = choices[0].get("message") or {}
|
||||
text = str(message.get("content") or "")
|
||||
if text:
|
||||
yield ReplyDelta(text, is_sentence_boundary=_ends_sentence(text), finish_reason="stop")
|
||||
return
|
||||
if "output_text" in event:
|
||||
text = str(event.get("output_text") or "")
|
||||
if text:
|
||||
yield ReplyDelta(text, is_sentence_boundary=_ends_sentence(text), finish_reason="stop")
|
||||
return
|
||||
output = event.get("output") or []
|
||||
parts: list[str] = []
|
||||
for item in output:
|
||||
for content in item.get("content", []):
|
||||
if content.get("type") in {"output_text", "text"}:
|
||||
parts.append(str(content.get("text") or ""))
|
||||
text = "".join(parts)
|
||||
if text:
|
||||
yield ReplyDelta(text, is_sentence_boundary=_ends_sentence(text), finish_reason="stop")
|
||||
|
||||
|
||||
def _read_response_body(response: Any) -> bytes:
|
||||
if hasattr(response, "read"):
|
||||
return response.read()
|
||||
return b"".join(response)
|
||||
|
||||
|
||||
def _ends_sentence(text: str) -> bool:
|
||||
return text.endswith(("。", "!", "?", ".", "!", "?", "\n"))
|
||||
|
||||
Reference in New Issue
Block a user