Diagnose table-padding truncation and complete local-first investigation
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
This commit is contained in:
parent
509e86d9c5
commit
90d1f86b8f
50 changed files with 2197 additions and 13 deletions
|
|
@ -11,9 +11,16 @@ RELATIONS = {'requires', 'enables', 'creates-context-for', 'refines', 'generaliz
|
|||
|
||||
def json_block(text):
|
||||
blocks = re.findall(r'```json\s*\n(.*?)\n```', text, re.S)
|
||||
if len(blocks) != 1:
|
||||
if len(blocks) == 1:
|
||||
return json.loads(blocks[0])
|
||||
if blocks or '```' in text:
|
||||
raise ValueError('exactly one JSON block required')
|
||||
return json.loads(blocks[0])
|
||||
# Accept one unambiguous terminal JSON document without Markdown fences.
|
||||
# Never repair malformed JSON or infer data from prose.
|
||||
starts = list(re.finditer(r'(?m)^[ \t]*(?=[{\[])', text))
|
||||
if len(starts) == 1:
|
||||
return json.loads(text[starts[0].start():].strip())
|
||||
raise ValueError('exactly one JSON block or terminal document required')
|
||||
|
||||
|
||||
def screen(candidates, relations):
|
||||
|
|
|
|||
|
|
@ -86,10 +86,29 @@ def plan(root):
|
|||
"review_status": "unreviewed"}
|
||||
|
||||
|
||||
def diagnostic_summary(data):
|
||||
"""Allowlist numeric usage/settings; never persist raw debug data."""
|
||||
debug = data.get("debug") or {}
|
||||
payload = (debug.get("provider_request") or {}).get("payload") or {}
|
||||
body = (debug.get("provider_response") or {}).get("body") or {}
|
||||
usage = body.get("usage") or {}
|
||||
settings = {key: payload[key] for key in ("temperature", "max_tokens", "top_p")
|
||||
if type(payload.get(key)) in (int, float)}
|
||||
settings["reasoning_effort_present"] = "reasoning_effort" in payload
|
||||
settings["reasoning_present"] = "reasoning" in payload
|
||||
counts = {key: usage[key] for key in ("prompt_tokens", "completion_tokens", "total_tokens", "cost")
|
||||
if type(usage.get(key)) in (int, float)}
|
||||
details = usage.get("completion_tokens_details") or {}
|
||||
counts["completion_tokens_details"] = {key: details[key] for key in
|
||||
("reasoning_tokens", "accepted_prediction_tokens", "rejected_prediction_tokens")
|
||||
if type(details.get(key)) in (int, float)}
|
||||
return {"available": bool(debug), "effective_settings": settings, "provider_usage": counts}
|
||||
|
||||
|
||||
class LLMConnectAdapter:
|
||||
"""Client of llm-connect's /execute contract; credentials stay at the service."""
|
||||
|
||||
def __init__(self, endpoint, model, max_tokens=4000, reasoning_effort=None):
|
||||
def __init__(self, endpoint, model, max_tokens=4000, reasoning_effort=None, diagnostics=False):
|
||||
parsed = urlsplit(endpoint)
|
||||
if (parsed.scheme not in ("http", "https") or not parsed.hostname
|
||||
or parsed.username or parsed.password or parsed.query or parsed.fragment):
|
||||
|
|
@ -100,11 +119,13 @@ class LLMConnectAdapter:
|
|||
self.config = RunConfig(model_name=model, temperature=0.2, max_tokens=max_tokens)
|
||||
if reasoning_effort is not None:
|
||||
self.config.model_params["reasoning_effort"] = reasoning_effort
|
||||
self.rejected_content = None
|
||||
self.diagnostics = diagnostics
|
||||
self.responses = []
|
||||
|
||||
def generate(self, generation):
|
||||
payload = {"prompt": generation.prompt, "config": self.config.to_dict()}
|
||||
req = request.Request(self.endpoint + "/execute",
|
||||
req = request.Request(self.endpoint + ("/execute?debug=1" if self.diagnostics else "/execute"),
|
||||
data=json.dumps(payload).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
|
|
@ -124,6 +145,8 @@ class LLMConnectAdapter:
|
|||
metadata.update({key: extra[key] for key in (
|
||||
"provider", "response_id", "latency_seconds", "profile", "profile_model",
|
||||
"profile_provider") if key in extra})
|
||||
if self.diagnostics:
|
||||
metadata["diagnostics"] = diagnostic_summary(data)
|
||||
content = data.get("content")
|
||||
metadata["content_characters"] = len(content) if isinstance(content, str) else None
|
||||
metadata["content_sha256"] = (hashlib.sha256(content.encode()).hexdigest()
|
||||
|
|
@ -131,6 +154,7 @@ class LLMConnectAdapter:
|
|||
self.responses.append({"stage": generation.stage_id, **metadata})
|
||||
if (not isinstance(data.get("content"), str) or not data["content"].strip()
|
||||
or data.get("finish_reason") != "stop"):
|
||||
self.rejected_content = content if isinstance(content, str) else None
|
||||
raise ValueError("llm-connect returned empty or incomplete generation; see receipt")
|
||||
return AssistedGenerationResult(markdown=data["content"], provider="llm-connect",
|
||||
metadata=metadata)
|
||||
|
|
@ -167,6 +191,7 @@ def execute(root, adapter, max_calls=4):
|
|||
if isinstance(adapter, LLMConnectAdapter):
|
||||
manifest["execution"] = {"endpoint": adapter.endpoint,
|
||||
"config": adapter.config.to_dict(),
|
||||
"diagnostics_requested": adapter.diagnostics,
|
||||
"call_limit_scope": "service requests; server retries unknown"}
|
||||
receipt.write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
limited = LimitedAdapter(adapter, max_calls)
|
||||
|
|
@ -187,6 +212,12 @@ def execute(root, adapter, max_calls=4):
|
|||
if isinstance(adapter, LLMConnectAdapter):
|
||||
manifest["service_requests_attempted"] = limited.calls
|
||||
manifest["responses"] = adapter.responses
|
||||
if adapter.rejected_content is not None:
|
||||
rejected = root / "reports/rejected-response.md"
|
||||
rejected.write_text(adapter.rejected_content)
|
||||
manifest["rejected_response"] = {"path": str(rejected.relative_to(root)),
|
||||
"sha256": digest(rejected),
|
||||
"admission": "rejected_incomplete"}
|
||||
receipt.write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
return manifest
|
||||
|
||||
|
|
@ -207,6 +238,8 @@ def main():
|
|||
providers.add_argument("--llm-connect-url", help="llm-connect service base URL")
|
||||
run.add_argument("--model", help="Model or runtime profile requested from llm-connect")
|
||||
run.add_argument("--max-tokens", type=int, default=4000)
|
||||
run.add_argument("--diagnostics", action="store_true",
|
||||
help="Retain only allowlisted effective settings and token counts")
|
||||
run.add_argument("--reasoning-effort", choices=["low", "medium", "high"],
|
||||
help="Explicit service model_params override; provider support may vary")
|
||||
run.add_argument("--max-calls", type=int, default=4)
|
||||
|
|
@ -225,7 +258,7 @@ def main():
|
|||
if not args.model:
|
||||
raise ValueError("Live generation requires --model")
|
||||
adapter = LLMConnectAdapter(args.llm_connect_url, args.model, args.max_tokens,
|
||||
args.reasoning_effort)
|
||||
args.reasoning_effort, args.diagnostics)
|
||||
result = execute(args.root, adapter, args.max_calls)
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
except (ValueError, OSError, InfospaceError) as exc:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue