-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_spec.py
More file actions
142 lines (125 loc) · 3.89 KB
/
Copy pathgenerate_spec.py
File metadata and controls
142 lines (125 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
"""Generate specs/<name>_FUNCTIONAL_SPEC.md from an extraction JSON via LLM."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from src.llm.client import LLMClient, LLMError # noqa: E402
from src.llm.config import load_llm_settings # noqa: E402
from src.spec_generator import ( # noqa: E402
build_user_prompt,
default_spec_path,
generate_functional_spec,
load_extraction_report,
write_functional_spec,
)
def main() -> int:
parser = argparse.ArgumentParser(
description="Generate a functional specification (Markdown) from extraction JSON via LLM."
)
parser.add_argument(
"extraction",
type=Path,
help="Path to extractions/<name>_extraction.json",
)
parser.add_argument(
"-o",
"--output",
type=Path,
default=None,
help="Output Markdown path (default: specs/<workbook>_FUNCTIONAL_SPEC.md)",
)
parser.add_argument(
"--provider",
choices=["ollama", "openai"],
help="LLM backend: ollama (local) or openai (HTTP API)",
)
parser.add_argument(
"--model",
help="Model name (e.g. llama3.2, gpt-4o-mini)",
)
parser.add_argument(
"--host",
help="Ollama host URL (default: http://localhost:11434)",
)
parser.add_argument(
"--base-url",
help="OpenAI-compatible API base URL",
)
parser.add_argument(
"--api-key",
help="API key (prefer OPENAI_API_KEY env var)",
)
parser.add_argument(
"--config",
type=Path,
default=None,
help="Path to config.json (default: ./config.json)",
)
parser.add_argument(
"--lang",
default="en",
choices=["fr", "en"],
help="Output language (default: en)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print the user prompt without calling the LLM",
)
parser.add_argument(
"--list-models",
action="store_true",
help="List Ollama models and exit",
)
args = parser.parse_args()
try:
settings = load_llm_settings(
args.config,
provider=args.provider,
model=args.model,
host=args.host,
base_url=args.base_url,
api_key=args.api_key,
)
except ValueError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
client = LLMClient(settings)
if args.list_models:
if settings.provider != "ollama":
print("Model listing is only supported for provider=ollama.", file=sys.stderr)
return 1
try:
for name in client.list_models():
print(name)
except LLMError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
return 0
if not args.extraction.exists():
print(f"Error: file not found — {args.extraction}", file=sys.stderr)
return 1
report = load_extraction_report(args.extraction)
if args.dry_run:
print(build_user_prompt(report, language=args.lang))
return 0
print(
f"Generating spec with {settings.provider} / {settings.model} "
f"from {args.extraction}…"
)
output_path = args.output or default_spec_path(args.extraction)
try:
markdown = generate_functional_spec(report, client, language=args.lang)
out = write_functional_spec(markdown, output_path, source_json=args.extraction)
except LLMError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
print(f" -> {out}")
print("Review and validate with the business owner before Phase 4 (prioritization).")
return 0
if __name__ == "__main__":
raise SystemExit(main())