Add verified Ollama DeepSeek review emitter

This commit is contained in:
Brandon Schneider 2026-05-11 23:13:54 -05:00
parent 32ed48ee4e
commit d56ef67ec9
5 changed files with 494 additions and 13 deletions

View file

@ -0,0 +1,363 @@
#!/usr/bin/env python3
"""
Emit Ollama-compatible DeepSeek review answers and receipts.
This is the canonical emitter for receipts with schema
`ollama_deepseek_review_receipt_v1` and
`ollama_deepseek_review_continuation_receipt_v1`.
The integrity rule is deliberately strict: write the answer first, compute
`answer_sha256` from the bytes read back from disk, write the receipt, then
verify the receipt against the answer path before returning success.
"""
from __future__ import annotations
import argparse
import datetime as dt
import hashlib
import json
import os
import re
import sys
import urllib.error
import urllib.request
from dataclasses import dataclass
from pathlib import Path
from typing import Any
REPO = Path(__file__).resolve().parents[3]
DEFAULT_OUTPUT_DIR = REPO / "shared-data" / "artifacts" / "deepseek_review"
DEFAULT_ENDPOINT = "https://ollama.com/v1/chat/completions"
PROVIDER_TAG = "deepseek"
PRIMARY_SCHEMA = "ollama_deepseek_review_receipt_v1"
CONTINUATION_SCHEMA = "ollama_deepseek_review_continuation_receipt_v1"
@dataclass(frozen=True)
class EmittedReview:
answer_path: Path
receipt_path: Path
answer_sha256: str
prompt_sha256: str
def sha256_bytes(data: bytes) -> str:
return "sha256:" + hashlib.sha256(data).hexdigest()
def canonical_json_bytes(value: Any) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def repo_relative(path: Path, repo_root: Path = REPO) -> str:
path = path.resolve()
repo_root = repo_root.resolve()
try:
return str(path.relative_to(repo_root))
except ValueError:
return str(path)
def safe_slug(value: str) -> str:
slug = re.sub(r"[^A-Za-z0-9._-]+", "_", value).strip("_")
return slug or "review"
def utc_timestamp() -> str:
return dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
def iso_from_stamp(stamp: str) -> str:
parsed = dt.datetime.strptime(stamp, "%Y%m%dT%H%M%SZ").replace(tzinfo=dt.timezone.utc)
return parsed.isoformat()
def read_text(path: Path) -> str:
return path.read_text(encoding="utf-8")
def read_context_bundle(paths: list[Path], repo_root: Path = REPO) -> tuple[str, list[str]]:
chunks: list[str] = []
context_files: list[str] = []
for path in paths:
body = read_text(path)
rel = repo_relative(path, repo_root)
chunks.append(f"\n\n===== FILE: {rel} =====\n{body}")
context_files.append(rel)
return "".join(chunks), context_files
def build_messages(
prompt: str,
context_text: str,
previous_answer_path: Path | None,
) -> list[dict[str, str]]:
if previous_answer_path is not None:
previous_answer = read_text(previous_answer_path)
user_content = (
f"{previous_answer}\n\n"
"===== CONTINUATION REQUEST =====\n"
f"{prompt}"
)
else:
user_content = (
f"{context_text}\n\n"
"===== REVIEW REQUEST =====\n"
f"{prompt}"
)
return [
{
"role": "system",
"content": (
"You are an external mathematical reviewer. Answer only from "
"the provided context and make arithmetic checks explicit."
),
},
{"role": "user", "content": user_content},
]
def build_request_body(
model: str,
messages: list[dict[str, str]],
max_tokens: int,
temperature: float,
) -> dict[str, Any]:
return {
"model": model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
"stream": False,
}
def call_ollama_chat(endpoint: str, api_key: str | None, request_bytes: bytes) -> dict[str, Any]:
headers = {"Content-Type": "application/json"}
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
elif not endpoint.startswith(("http://127.0.0.1", "http://localhost")):
raise RuntimeError("OLLAMA_API_KEY is required for non-local Ollama endpoints")
request = urllib.request.Request(
endpoint,
data=request_bytes,
headers=headers,
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=600) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"Ollama endpoint returned HTTP {exc.code}: {detail}") from exc
def extract_answer(response: dict[str, Any]) -> tuple[str, dict[str, int], list[str]]:
choices = response.get("choices")
if not isinstance(choices, list) or not choices:
raise ValueError("response has no choices")
message = choices[0].get("message", {})
if not isinstance(message, dict):
raise ValueError("response choice has no message object")
content = message.get("content", "")
if isinstance(content, str):
answer_text = content
else:
answer_text = json.dumps(content, ensure_ascii=False)
usage_obj = response.get("usage", {})
usage = {
"prompt_tokens": int(usage_obj.get("prompt_tokens", 0) or 0),
"completion_tokens": int(usage_obj.get("completion_tokens", 0) or 0),
"total_tokens": int(usage_obj.get("total_tokens", 0) or 0),
}
if usage["total_tokens"] == 0:
usage["total_tokens"] = usage["prompt_tokens"] + usage["completion_tokens"]
return answer_text, usage, list(message.keys())
def write_text_and_hash(path: Path, text: str) -> str:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8")
return sha256_bytes(path.read_bytes())
def write_receipt(path: Path, receipt: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(receipt, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
def verify_receipt(receipt_path: Path, repo_root: Path = REPO) -> bool:
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
schema = receipt.get("schema")
if schema not in {PRIMARY_SCHEMA, CONTINUATION_SCHEMA}:
raise ValueError(f"{receipt_path}: unknown schema {schema!r}")
answer_path = repo_root / receipt["answer_path"]
actual = sha256_bytes(answer_path.read_bytes())
expected = receipt.get("answer_sha256")
if actual != expected:
raise ValueError(f"{receipt_path}: answer_sha256 mismatch: {actual} != {expected}")
if schema == PRIMARY_SCHEMA:
if not isinstance(receipt.get("context_files"), list):
raise ValueError(f"{receipt_path}: primary receipt missing context_files")
if "previous_answer_path" in receipt:
raise ValueError(f"{receipt_path}: primary receipt must not carry previous_answer_path")
else:
if not receipt.get("previous_answer_path"):
raise ValueError(f"{receipt_path}: continuation receipt missing previous_answer_path")
if "context_files" in receipt:
raise ValueError(f"{receipt_path}: continuation receipt must omit context_files")
return True
def emit_review(
*,
topic: str,
model: str,
prompt: str,
context_paths: list[Path],
previous_answer_path: Path | None,
output_dir: Path = DEFAULT_OUTPUT_DIR,
endpoint: str = DEFAULT_ENDPOINT,
max_tokens: int = 5000,
temperature: float = 0.0,
timestamp: str | None = None,
api_key: str | None = None,
response: dict[str, Any] | None = None,
repo_root: Path = REPO,
) -> EmittedReview:
stamp = timestamp or utc_timestamp()
created_at = iso_from_stamp(stamp)
context_text, context_files = read_context_bundle(context_paths, repo_root)
messages = build_messages(prompt, context_text, previous_answer_path)
request_body = build_request_body(model, messages, max_tokens, temperature)
request_bytes = canonical_json_bytes(request_body)
prompt_sha256 = sha256_bytes(request_bytes)
if response is None:
response = call_ollama_chat(endpoint, api_key or os.getenv("OLLAMA_API_KEY"), request_bytes)
answer_text, usage, message_keys = extract_answer(response)
stem = f"{safe_slug(topic)}_{PROVIDER_TAG}_{safe_slug(model)}"
if previous_answer_path is not None:
stem += "_continuation"
stem += f"_{stamp}"
answer_path = output_dir / f"{stem}.md"
receipt_path = output_dir / f"{stem}.receipt.json"
answer_sha256 = write_text_and_hash(answer_path, answer_text)
receipt: dict[str, Any] = {
"schema": CONTINUATION_SCHEMA if previous_answer_path else PRIMARY_SCHEMA,
"created_at": created_at,
"model": model,
"endpoint": endpoint,
"prompt_sha256": prompt_sha256,
"answer_sha256": answer_sha256,
"usage": usage,
"answer_path": repo_relative(answer_path, repo_root),
}
if previous_answer_path is None:
receipt["context_files"] = context_files
else:
receipt["previous_answer_path"] = repo_relative(previous_answer_path, repo_root)
if message_keys:
receipt["message_keys"] = message_keys
write_receipt(receipt_path, receipt)
verify_receipt(receipt_path, repo_root)
return EmittedReview(answer_path, receipt_path, answer_sha256, prompt_sha256)
def verify_receipt_dir(path: Path, repo_root: Path = REPO) -> int:
count = 0
for receipt_path in sorted(path.glob("*.receipt.json")):
verify_receipt(receipt_path, repo_root)
count += 1
return count
def read_prompt(args: argparse.Namespace) -> str:
if args.prompt_file:
return read_text(Path(args.prompt_file))
if args.prompt:
return args.prompt
return sys.stdin.read()
def _cli() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("prompt", nargs="?", help="review prompt; stdin is used if omitted")
parser.add_argument("--prompt-file", help="read review prompt from a file")
parser.add_argument("--topic", default="review", help="artifact topic slug")
parser.add_argument("--model", default="deepseek-v3.2")
parser.add_argument("--endpoint", default=os.getenv("OLLAMA_ENDPOINT", DEFAULT_ENDPOINT))
parser.add_argument("--context-file", action="append", default=[], help="primary review context file")
parser.add_argument("--continue-from", help="previous answer markdown for continuation receipts")
parser.add_argument("--output-dir", default=str(DEFAULT_OUTPUT_DIR))
parser.add_argument("--max-tokens", type=int, default=5000)
parser.add_argument("--temperature", type=float, default=0.0)
parser.add_argument("--timestamp", help="UTC stamp YYYYMMDDTHHMMSSZ; mainly for tests/replay")
parser.add_argument("--answer-text-file", help="offline mode: write this answer text instead of calling Ollama")
parser.add_argument("--verify-only", nargs="*", help="verify receipt paths; with no paths, verify --output-dir")
args = parser.parse_args()
output_dir = Path(args.output_dir)
if args.verify_only is not None:
if args.verify_only:
for receipt in args.verify_only:
verify_receipt(Path(receipt))
print(f"verified {receipt}")
else:
count = verify_receipt_dir(output_dir)
print(f"verified {count} receipts in {output_dir}")
return
prompt = read_prompt(args)
response = None
if args.answer_text_file:
answer_text = read_text(Path(args.answer_text_file))
response = {
"choices": [{"message": {"role": "assistant", "content": answer_text}}],
"usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
}
emitted = emit_review(
topic=args.topic,
model=args.model,
prompt=prompt,
context_paths=[Path(p) for p in args.context_file],
previous_answer_path=Path(args.continue_from) if args.continue_from else None,
output_dir=output_dir,
endpoint=args.endpoint,
max_tokens=args.max_tokens,
temperature=args.temperature,
timestamp=args.timestamp,
response=response,
)
print(json.dumps({
"answer_path": repo_relative(emitted.answer_path),
"receipt_path": repo_relative(emitted.receipt_path),
"answer_sha256": emitted.answer_sha256,
"prompt_sha256": emitted.prompt_sha256,
}, indent=2))
if __name__ == "__main__":
_cli()

View file

@ -0,0 +1,106 @@
#!/usr/bin/env python3
from __future__ import annotations
import importlib.util
import json
import sys
import tempfile
import unittest
from pathlib import Path
MODULE_PATH = Path(__file__).with_name("ollama_deepseek_review_emitter.py")
SPEC = importlib.util.spec_from_file_location("ollama_deepseek_review_emitter", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
emitter = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = emitter
SPEC.loader.exec_module(emitter)
class OllamaDeepSeekReviewEmitterTest(unittest.TestCase):
def test_primary_receipt_hashes_answer_after_write(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
context = root / "context.md"
context.write_text("canonical arithmetic context", encoding="utf-8")
output = root / "deepseek_review"
emitted = emitter.emit_review(
topic="prime_gap_entropy_collapse",
model="deepseek-v3.2",
prompt="Review this.",
context_paths=[context],
previous_answer_path=None,
output_dir=output,
endpoint="http://localhost:11434/v1/chat/completions",
timestamp="20260512T000000Z",
response={
"choices": [{"message": {"role": "assistant", "content": "answer body"}}],
"usage": {"prompt_tokens": 2, "completion_tokens": 3, "total_tokens": 5},
},
repo_root=root,
)
receipt = json.loads(emitted.receipt_path.read_text(encoding="utf-8"))
self.assertEqual(receipt["schema"], emitter.PRIMARY_SCHEMA)
self.assertEqual(receipt["context_files"], ["context.md"])
self.assertEqual(receipt["answer_sha256"], emitter.sha256_bytes(emitted.answer_path.read_bytes()))
self.assertTrue(emitter.verify_receipt(emitted.receipt_path, root))
def test_continuation_omits_context_and_points_to_previous_answer(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
previous = root / "previous.md"
previous.write_text("first answer", encoding="utf-8")
output = root / "deepseek_review"
emitted = emitter.emit_review(
topic="prime_gap_entropy_collapse",
model="deepseek-v4-flash",
prompt="Continue.",
context_paths=[],
previous_answer_path=previous,
output_dir=output,
endpoint="http://localhost:11434/v1/chat/completions",
timestamp="20260512T000100Z",
response={
"choices": [{"message": {"role": "assistant", "content": "continued", "reasoning": "brief"}}],
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
},
repo_root=root,
)
receipt = json.loads(emitted.receipt_path.read_text(encoding="utf-8"))
self.assertEqual(receipt["schema"], emitter.CONTINUATION_SCHEMA)
self.assertNotIn("context_files", receipt)
self.assertEqual(receipt["previous_answer_path"], "previous.md")
self.assertEqual(receipt["message_keys"], ["role", "content", "reasoning"])
self.assertTrue(emitter.verify_receipt(emitted.receipt_path, root))
def test_verify_receipt_rejects_stale_answer_hash(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
context = root / "context.md"
context.write_text("context", encoding="utf-8")
output = root / "deepseek_review"
emitted = emitter.emit_review(
topic="review",
model="deepseek-v3.2",
prompt="Review.",
context_paths=[context],
previous_answer_path=None,
output_dir=output,
endpoint="http://localhost:11434/v1/chat/completions",
timestamp="20260512T000200Z",
response={"choices": [{"message": {"role": "assistant", "content": "stable"}}]},
repo_root=root,
)
emitted.answer_path.write_text("mutated", encoding="utf-8")
with self.assertRaises(ValueError):
emitter.verify_receipt(emitted.receipt_path, root)
if __name__ == "__main__":
unittest.main()

View file

@ -10,12 +10,11 @@ of the review pipeline, the receipt schema, and the canonical example
`shared-data/artifacts/deepseek_review/`.
Important boundary: the canonical artifacts documented on this page are
Ollama-compatible review receipts. The tracked
`5-Applications/tools-scripts/llm/deepseek_review_adapter.py` is a separate
Anthropic-compatible DeepSeek adapter with its own `schema_version: "1.0"`
receipt format. Do not use that adapter as the emitter for the
`ollama_deepseek_review_receipt_v1` schema unless it has first been reconciled
to this schema.
Ollama-compatible review receipts. Use
`5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py` for this
schema. The tracked `5-Applications/tools-scripts/llm/deepseek_review_adapter.py`
is a separate Anthropic-compatible DeepSeek adapter with its own
`schema_version: "1.0"` receipt format.
---
@ -52,10 +51,12 @@ Anthropic-compatible DeepSeek reviews.
The artifacts in `shared-data/artifacts/deepseek_review/` instead record the
Ollama-compatible schema documented below: `schema`, `created_at`, `endpoint`,
`usage.{prompt_tokens,completion_tokens,total_tokens}`, plain string
`context_files`, and `answer_sha256`. As of this page, no tracked canonical
Ollama emitter script has been identified in the repository. Treat the checked
receipts themselves as the schema authority until such an emitter is added or
the adapter is reconciled.
`context_files`, and `answer_sha256`. The canonical emitter is
`5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py`; it writes
the answer file first, computes `answer_sha256` from bytes read back from disk,
writes the receipt, and immediately verifies the receipt against the answer
path before returning success. This write-time verification gate exists because
the first manually landed Ollama receipts had stale `answer_sha256` values.
### Receipt Schema
@ -188,9 +189,11 @@ When emitting new review artifacts:
2. Write the matching receipt alongside it with the same stem and
`.receipt.json` suffix, using `ollama_deepseek_review_receipt_v1` for the
primary review and `ollama_deepseek_review_continuation_receipt_v1` for any
continuation. Do not generate these receipts with
`5-Applications/tools-scripts/llm/deepseek_review_adapter.py`; it emits a
different Anthropic-compatible schema unless explicitly updated.
continuation. Use
`5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py`; do not
generate these receipts with
`5-Applications/tools-scripts/llm/deepseek_review_adapter.py`, which emits a
different Anthropic-compatible schema.
3. Populate `context_files` with repo-relative paths to every file consumed
by the prompt so future agents can reproduce the prompt body. Continuation
receipts omit `context_files` and `message_keys` records the alternate
@ -208,6 +211,9 @@ When emitting new review artifacts:
- [[Build-System]] — pinned Python interpreter for review tooling and
prompt-hash reproducibility across runs.
- `5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py`
canonical Ollama-compatible emitter for this schema; verifies
`answer_sha256` after writing.
- `5-Applications/tools-scripts/llm/deepseek_review_adapter.py`
Anthropic-compatible DeepSeek adapter with a different receipt schema; useful
as related infrastructure, but not the emitter for the current Ollama-style

View file

@ -81,4 +81,5 @@
|---|---|
| `wiki/DeepSeek-Review-Process.md` | Receipt schema (`ollama_deepseek_review_receipt_v1`), two-stage pipeline (`deepseek-v3.2` + `deepseek-v4-flash` continuation), prime-gap entropy-collapse example |
| `shared-data/artifacts/deepseek_review/` | Paired answer + receipt artifacts for each review run |
| `5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py` | Canonical Ollama-compatible DeepSeek review emitter with write-time answer-hash verification |
| `5-Applications/tools-scripts/llm/deepseek_review_adapter.py` | Related Anthropic-compatible DeepSeek adapter; it does not emit the current Ollama-style receipt schema |

View file

@ -24,6 +24,8 @@ This file is the first stop for coding agents working in this repository.
- Documentation and wiki surfaces: `6-Documentation/`
- Stack receipts: `shared-data/data/stack_solidification/`
- Promoted review receipts: `shared-data/artifacts/deepseek_review/`
- Canonical Ollama/DeepSeek review emitter:
`5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py`
- CAD harness: `5-Applications/text-to-cad/`
- Historical scoped staging maps:
`6-Documentation/docs/stack_solidification_staging_manifest_2026-05-09.md`
@ -34,6 +36,9 @@ This file is the first stop for coding agents working in this repository.
- For Lean changes, run the narrow target first, then the broader `lake build` when feasible.
- For Python shims, run `python3 -m py_compile` on touched files.
- For JSON receipts, run `python3 -m json.tool` or a repo-native receipt parser.
- For promoted Ollama/DeepSeek review receipts, run
`python3 5-Applications/tools-scripts/llm/ollama_deepseek_review_emitter.py --verify-only`
so `answer_sha256` is checked against the answer file after write.
- For hardware claims, distinguish software witness, bitstream presence, SRAM load, flash persistence, UART beacon, and live hardware receipt.
- Before committing, run `git diff --cached --check` and a staged secret scan
for touched source/receipt files. The repository credential hook is a final