This repository has no description
1#!/usr/bin/env python3
2"""Cloud Run Job entrypoint: generate AI-solve questionnaire for one issue."""
3
4from __future__ import annotations
5
6import argparse
7import json
8import os
9import sys
10
11from dotenv import load_dotenv
12
13from agent.agent import generate_and_save_questionnaire
14from agent.load_issue import resolve_issue_uri
15
16
17def load_env() -> None:
18 load_dotenv()
19
20
21def require_env(name: str) -> str:
22 value = os.getenv(name, "").strip()
23 if not value:
24 print(f"ERROR: {name} is not set", file=sys.stderr)
25 raise SystemExit(1)
26 return value
27
28
29def main(argv: list[str] | None = None) -> None:
30 parser = argparse.ArgumentParser(
31 description="Generate and cache an AI-solve questionnaire for a Tangled issue."
32 )
33 parser.add_argument(
34 "issue_id",
35 nargs="?",
36 help="Issue at:// URI or rkey (rkey requires DB lookup)",
37 )
38 parser.add_argument(
39 "--issue-uri",
40 metavar="URI",
41 help="Full at:// issue URI (overrides positional issue_id)",
42 )
43 parser.add_argument(
44 "--issue-id",
45 metavar="ID",
46 dest="issue_id_flag",
47 help="Same as positional: at:// URI or rkey",
48 )
49 parser.add_argument(
50 "--no-file-tree",
51 action="store_true",
52 help="Skip knot file-tree walk before generation",
53 )
54 parser.add_argument(
55 "--list-tool",
56 action="store_true",
57 help="Expose list_repo_files in addition to read_repo_file",
58 )
59 parser.add_argument(
60 "--no-save",
61 action="store_true",
62 help="Print questionnaire JSON only; do not write to Postgres",
63 )
64 args = parser.parse_args(argv)
65
66 load_env()
67 require_env("DB_CONNECTION_STRING")
68 require_env("ANTHROPIC_API_KEY")
69
70 raw = (
71 args.issue_uri
72 or args.issue_id_flag
73 or args.issue_id
74 or os.getenv("ISSUE_URI", "").strip()
75 or os.getenv("ISSUE_ID", "").strip()
76 )
77 if not raw:
78 print(
79 "ERROR: provide issue id via arg, --issue-uri, ISSUE_URI, or ISSUE_ID",
80 file=sys.stderr,
81 )
82 raise SystemExit(1)
83
84 issue_uri = resolve_issue_uri(raw)
85 print(f"[job] generating questionnaire for {issue_uri}", file=sys.stderr)
86
87 result = generate_and_save_questionnaire(
88 issue_uri,
89 fetch_file_tree=not args.no_file_tree,
90 include_list_tool=args.list_tool,
91 thread_id=f"job-{issue_uri.rsplit('/', 1)[-1]}",
92 save=not args.no_save,
93 )
94
95 if args.no_save and "payload" in result:
96 print(json.dumps(result["payload"], indent=2, ensure_ascii=False))
97 else:
98 print(json.dumps(result, indent=2))
99 if args.no_save:
100 print(
101 f"[job] done (no-save) version={result.get('version')} "
102 f"questions={result.get('question_count')}",
103 file=sys.stderr,
104 )
105 else:
106 print(
107 f"[job] saved version={result.get('version')} "
108 f"questions={result.get('question_count')} "
109 f"updated_at={result.get('updated_at')}",
110 file=sys.stderr,
111 )
112
113
114if __name__ == "__main__":
115 try:
116 main()
117 except KeyboardInterrupt:
118 print("\nInterrupted.", file=sys.stderr)
119 raise SystemExit(130) from None