Screen research evidence and record bounded llm-connect refinement
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
This commit is contained in:
parent
2ec23bb22c
commit
509e86d9c5
24 changed files with 688 additions and 9 deletions
95
scripts/research_admission.py
Normal file
95
scripts/research_admission.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
"""Conservative structural screening; never grants research admission."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
RELATIONS = {'requires', 'enables', 'creates-context-for', 'refines', 'generalizes',
|
||||
'complements', 'alternative-to', 'conflicts-with', 'commonly-combined-with'}
|
||||
|
||||
|
||||
def json_block(text):
|
||||
blocks = re.findall(r'```json\s*\n(.*?)\n```', text, re.S)
|
||||
if len(blocks) != 1:
|
||||
raise ValueError('exactly one JSON block required')
|
||||
return json.loads(blocks[0])
|
||||
|
||||
|
||||
def screen(candidates, relations):
|
||||
findings = []
|
||||
ids = set()
|
||||
try:
|
||||
ledger = json_block(candidates)
|
||||
if not isinstance(ledger, dict) or not isinstance(ledger.get('candidates'), list):
|
||||
raise ValueError('candidate ledger required')
|
||||
for item in ledger['candidates']:
|
||||
if not isinstance(item, dict):
|
||||
raise ValueError('candidate must be an object')
|
||||
cid = item.get('id')
|
||||
if not isinstance(cid, str) or not re.fullmatch(r'P[0-9]{2,}', cid) or cid in ids:
|
||||
raise ValueError('candidate IDs must be unique Pnn identities')
|
||||
ids.add(cid)
|
||||
uses = item.get('known_uses')
|
||||
if not isinstance(uses, list):
|
||||
raise ValueError('known_uses list required')
|
||||
for use in uses:
|
||||
if (not isinstance(use, dict) or not all(
|
||||
isinstance(use.get(k), str) and use[k].strip()
|
||||
for k in ('implementation', 'source_locator'))):
|
||||
findings.append(f'{cid}: known use lacks implementation/source locator')
|
||||
recurrence = item.get('recurrence')
|
||||
if recurrence not in ('unknown', 'supported'):
|
||||
findings.append(f'{cid}: recurrence must be unknown or supported')
|
||||
if recurrence == 'supported':
|
||||
located = {(u.get('implementation'), u.get('source_locator')) for u in uses
|
||||
if isinstance(u, dict) and isinstance(u.get('implementation'), str)
|
||||
and isinstance(u.get('source_locator'), str) and u['source_locator'].strip()}
|
||||
if len({x[0] for x in located}) < 2:
|
||||
findings.append(f'{cid}: recurrence lacks two located implementations')
|
||||
except (ValueError, TypeError) as exc:
|
||||
findings.append(f'candidate contract: {exc}')
|
||||
try:
|
||||
edges = json_block(relations)
|
||||
if not isinstance(edges, list):
|
||||
raise ValueError('relation array required')
|
||||
for edge in edges:
|
||||
if not isinstance(edge, dict):
|
||||
raise ValueError('relation must be an object')
|
||||
for field in ('subject_candidate_id', 'object_candidate_id'):
|
||||
endpoint = edge.get(field)
|
||||
if not isinstance(endpoint, str) or endpoint not in ids:
|
||||
findings.append(f'invalid {field}: {endpoint!r}; must resolve to candidate ledger')
|
||||
if edge.get('relation') not in RELATIONS:
|
||||
findings.append('invalid relation type')
|
||||
for field in ('rationale', 'source_claim_and_locator', 'counterexample'):
|
||||
if not isinstance(edge.get(field), str) or not edge[field].strip():
|
||||
findings.append(f'relation missing {field}')
|
||||
if edge.get('observation_versus_hypothesis') not in ('observation', 'hypothesis'):
|
||||
findings.append('relation missing observation/hypothesis classification')
|
||||
except (ValueError, TypeError) as exc:
|
||||
findings.append(f'relation contract: {exc}')
|
||||
return {'structural_status': 'blocked' if findings else 'passed',
|
||||
'admission': 'not_granted', 'semantic_review': 'required', 'findings': findings}
|
||||
|
||||
|
||||
def audit(root):
|
||||
root = Path(root)
|
||||
paths = [root / 'artifacts/generated' / name for name in
|
||||
('candidates.md', 'relation-proposals.md')]
|
||||
result = screen(*(p.read_text() for p in paths))
|
||||
result['artifact_sha256'] = {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
for p in paths}
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('root', type=Path)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
result = audit(args.root)
|
||||
except (OSError, ValueError) as exc:
|
||||
parser.exit(1, f'{exc}\n')
|
||||
print(json.dumps(result, indent=2))
|
||||
raise SystemExit(0 if result['structural_status'] == 'passed' else 1)
|
||||
|
|
@ -89,7 +89,7 @@ def plan(root):
|
|||
class LLMConnectAdapter:
|
||||
"""Client of llm-connect's /execute contract; credentials stay at the service."""
|
||||
|
||||
def __init__(self, endpoint, model, max_tokens=4000):
|
||||
def __init__(self, endpoint, model, max_tokens=4000, reasoning_effort=None):
|
||||
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):
|
||||
|
|
@ -98,6 +98,8 @@ class LLMConnectAdapter:
|
|||
raise ValueError("--max-tokens must be positive")
|
||||
self.endpoint, self.model = endpoint.rstrip("/"), model
|
||||
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.responses = []
|
||||
|
||||
def generate(self, generation):
|
||||
|
|
@ -122,6 +124,10 @@ class LLMConnectAdapter:
|
|||
metadata.update({key: extra[key] for key in (
|
||||
"provider", "response_id", "latency_seconds", "profile", "profile_model",
|
||||
"profile_provider") if key in extra})
|
||||
content = data.get("content")
|
||||
metadata["content_characters"] = len(content) if isinstance(content, str) else None
|
||||
metadata["content_sha256"] = (hashlib.sha256(content.encode()).hexdigest()
|
||||
if isinstance(content, str) else None)
|
||||
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"):
|
||||
|
|
@ -201,6 +207,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("--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)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
|
|
@ -216,7 +224,8 @@ def main():
|
|||
else:
|
||||
if not args.model:
|
||||
raise ValueError("Live generation requires --model")
|
||||
adapter = LLMConnectAdapter(args.llm_connect_url, args.model, args.max_tokens)
|
||||
adapter = LLMConnectAdapter(args.llm_connect_url, args.model, args.max_tokens,
|
||||
args.reasoning_effort)
|
||||
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