print reasoning tokens to console during testing

This commit is contained in:
Denton Social
2026-09-24 04:09:24 -05:00
parent 8c10a94c32
commit 0a9b602ec7
3 changed files with 24 additions and 12 deletions
+5 -3
View File
@@ -216,9 +216,11 @@ unit tests (24) + box integration tests (2).
`codegen.timeout` in config for harder prompts.
- Set `codegen.stream: true` to echo the codegen output as an SSE token stream
to stdout during body writes — including the chain-of-thought, so a long
(~7 min) write shows live progress. Echoing is console-only; the returned
content is identical either way. Integration tests already force streaming;
see it with `-s` on the box.
(~7 min) write shows live progress. The client reads reasoning from either
`reasoning` (ollama) or `reasoning_content` (other OpenAI-compatible
backends) — do not drop one for the other. Echoing is console-only; the
returned content is identical either way. Integration tests already force
streaming; see it with `-s` on the box.
### Code principles
- **No mocking.** The decision engine is always real SemIf; the LLM is always a
+4 -3
View File
@@ -97,8 +97,9 @@ class CodegenClient:
"""Read an OpenAI-compatible SSE stream, echo tokens to stdout.
Only `content` deltas are accumulated into the returned body;
`reasoning_content` (chain-of-thought) is echoed to the console but
never part of the result.
reasoning (chain-of-thought) is echoed to the console but never part
of the result. Backends disagree on the field name: ollama streams it
as `reasoning`, DeepSeek/vllm-style as `reasoning_content`, so read both.
"""
import sys
@@ -117,7 +118,7 @@ class CodegenClient:
choice = chunk.get("choices", [{}])[0]
delta = choice.get("delta", {}) or {}
text = delta.get("content") or ""
reasoning = delta.get("reasoning_content") or ""
reasoning = delta.get("reasoning_content") or delta.get("reasoning") or ""
if text or reasoning:
sys.stdout.write(text + reasoning)
sys.stdout.flush()
+15 -6
View File
@@ -257,9 +257,14 @@ def _sse_frame(payload: dict) -> str:
class _StreamingOpenAI(BaseHTTPRequestHandler):
"""Replies with an OpenAI-compatible SSE token stream (COT then content)."""
"""Replies with an OpenAI-compatible SSE token stream (COT then content).
Reasoning field name matches the backend: ollama emits `reasoning`,
DeepSeek/vllm-style `reasoning_content`. Defaults to ollama's.
"""
reasoning: str = "thinking about the body..."
reasoning_key: str = "reasoning"
content: str = GOOD_BODY
received: list = []
@@ -271,11 +276,12 @@ class _StreamingOpenAI(BaseHTTPRequestHandler):
self.send_header("Content-Type", "text/event-stream")
self.end_headers()
reasoning = type(self).reasoning
reasoning_key = type(self).reasoning_key
content = type(self).content
step = max(len(reasoning) // 4, 1)
for i in range(0, len(reasoning), step):
frame = _sse_frame(
{"choices": [{"delta": {"reasoning_content": reasoning[i : i + step]}}]}
{"choices": [{"delta": {reasoning_key: reasoning[i : i + step]}}]}
)
self.wfile.write(frame.encode("utf-8"))
step = max(len(content) // 4, 1)
@@ -292,8 +298,10 @@ class _StreamingOpenAI(BaseHTTPRequestHandler):
pass
def _streaming_server() -> tuple[ThreadingHTTPServer, str]:
handler = type("Handler", (_StreamingOpenAI,), {"received": []})
def _streaming_server(reasoning_key: str = "reasoning") -> tuple[ThreadingHTTPServer, str]:
handler = type(
"Handler", (_StreamingOpenAI,), {"reasoning_key": reasoning_key, "received": []}
)
httpd = ThreadingHTTPServer(("127.0.0.1", 0), handler)
threading.Thread(target=httpd.serve_forever, daemon=True).start()
return httpd, f"http://127.0.0.1:{httpd.server_address[1]}/v1"
@@ -312,8 +320,9 @@ def test_chat_stream_accumulates_full_content():
httpd.server_close()
def test_chat_stream_verbose_echoes_tokens(capsys):
httpd, base = _streaming_server()
@pytest.mark.parametrize("reasoning_key", ["reasoning", "reasoning_content"])
def test_chat_stream_verbose_echoes_tokens(reasoning_key, capsys):
httpd, base = _streaming_server(reasoning_key=reasoning_key)
try:
client = CodegenClient(base_url=base, model="test", timeout=10, stream=True)
out = client.chat([{"role": "user", "content": "hi"}])