[测试、性能、安全、验收与归档]:完成全量测试验收与 OpenSpec 归档,包含 CLI 验收、NewAPI smoke、安全扫描和主 spec 更新

This commit is contained in:
mkbk
2026-06-17 18:42:11 +08:00
parent 667c8941f2
commit 1b884ac6a9
15 changed files with 522 additions and 28 deletions
+50 -13
View File
@@ -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"))