Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the first agent-facing intake packet for a 1C user task."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from route_1c_question import route_question # noqa: E402
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_INDEX = ROOT / "reports" / "1c-sql" / "upo" / "unified-object-route-index.json"
|
||||
|
||||
|
||||
def decode_arg(value: str | None, encoded: str | None) -> str | None:
|
||||
if encoded:
|
||||
return base64.b64decode(encoded).decode("utf-8")
|
||||
return value
|
||||
|
||||
|
||||
def fact_summary(fact_checks: list[dict[str, Any]]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
confirmed = []
|
||||
unresolved = []
|
||||
for check in fact_checks:
|
||||
result = check.get("result") or {}
|
||||
row = {
|
||||
"path": check.get("path"),
|
||||
"status": check.get("status"),
|
||||
"exists": result.get("exists") if result else None,
|
||||
"confidence": result.get("confidence"),
|
||||
"reason": result.get("reason"),
|
||||
"object": result.get("object"),
|
||||
"match": result.get("match"),
|
||||
}
|
||||
if result.get("exists") is True:
|
||||
confirmed.append(row)
|
||||
else:
|
||||
unresolved.append(row)
|
||||
return confirmed, unresolved
|
||||
|
||||
|
||||
def next_commands(route: dict[str, Any], *, index: Path, view: str) -> list[dict[str, Any]]:
|
||||
commands = []
|
||||
decision = route.get("decision") or {}
|
||||
if decision.get("needs_docs_rag"):
|
||||
commands.append(
|
||||
{
|
||||
"tool": "docs_rag",
|
||||
"purpose": "official_documentation_context",
|
||||
"api": "/api/rag/query",
|
||||
"payload": {
|
||||
"question": route.get("question"),
|
||||
"source_type": decision.get("safe_rag_scope") or "official_1c_docs",
|
||||
"limit": 5,
|
||||
},
|
||||
}
|
||||
)
|
||||
for path in route.get("fact_paths") or []:
|
||||
commands.append(
|
||||
{
|
||||
"tool": "fact_resolver",
|
||||
"purpose": "current_configuration_fact",
|
||||
"command": f"python scripts/resolve_1c_fact.py --index {index} --path <utf8-base64:{path}> --view {view}",
|
||||
"api": "/api/1c/fact",
|
||||
"payload": {
|
||||
"source_kind": "route_index",
|
||||
"source_path": str(index),
|
||||
"path": path,
|
||||
"view": view,
|
||||
},
|
||||
}
|
||||
)
|
||||
if decision.get("needs_current_config") and not route.get("fact_paths"):
|
||||
commands.append(
|
||||
{
|
||||
"tool": "task_evidence",
|
||||
"purpose": "discover_objects_and_relevant_metadata",
|
||||
"command": f"python scripts/build_1c_task_evidence.py --index {index} --text <utf8-base64 task> --view {view}",
|
||||
}
|
||||
)
|
||||
return commands
|
||||
|
||||
|
||||
def build_intake(text: str, *, index: Path, view: str) -> dict[str, Any]:
|
||||
route = route_question(text, index_path=index, view=view)
|
||||
confirmed, unresolved = fact_summary(route.get("fact_checks") or [])
|
||||
decision = route.get("decision") or {}
|
||||
code_allowed = not unresolved and bool(confirmed or not decision.get("needs_current_config"))
|
||||
if decision.get("needs_current_config") and not confirmed and not route.get("fact_paths"):
|
||||
code_allowed = False
|
||||
|
||||
return {
|
||||
"schema": "onec_agent_intake.v1",
|
||||
"task": {"text": text},
|
||||
"index": str(index),
|
||||
"view": view,
|
||||
"route": route,
|
||||
"source_policy": {
|
||||
"allowed_for_current_facts": ["route_index", "metadata_snapshot_explicit_current", "1c_agent_current"],
|
||||
"allowed_for_documentation": ["official_1c_docs"],
|
||||
"examples_are_current_facts": False,
|
||||
"blocked_as_current_fact_sources": ["metadata.example", "synthetic-example", "rag_examples", "old_exports"],
|
||||
},
|
||||
"facts": {
|
||||
"confirmed": confirmed,
|
||||
"unresolved": unresolved,
|
||||
"confirmed_count": len(confirmed),
|
||||
"unresolved_count": len(unresolved),
|
||||
},
|
||||
"answer_policy": {
|
||||
"code_generation_allowed": code_allowed,
|
||||
"must_check_current_config_before_code": bool(decision.get("current_config_required_before_code")),
|
||||
"must_not_use_examples_as_facts": True,
|
||||
"safe_rag_scope": decision.get("safe_rag_scope"),
|
||||
},
|
||||
"next_commands": next_commands(route, index=index, view=view),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Build first agent intake packet for a 1C task.")
|
||||
parser.add_argument("--text")
|
||||
parser.add_argument("--text-b64")
|
||||
parser.add_argument("--index", type=Path, default=DEFAULT_INDEX)
|
||||
parser.add_argument("--view", choices=["effective", "base"], default="effective")
|
||||
parser.add_argument("--output", type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
text = decode_arg(args.text, args.text_b64)
|
||||
if not text:
|
||||
raise SystemExit("Use --text or --text-b64.")
|
||||
result = build_intake(text, index=args.index, view=args.view)
|
||||
output = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
|
||||
if args.output:
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(output, encoding="utf-8")
|
||||
print(json.dumps({"output": str(args.output), "route": result["route"]["decision"]["route"], "code_allowed": result["answer_policy"]["code_generation_allowed"]}, ensure_ascii=False))
|
||||
else:
|
||||
print(output)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user