verbose live UI + tool-level SSE events + Groq default + regression tests
This commit is contained in:
31
api/prompts/__init__.py
Normal file
31
api/prompts/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Prompt loader + renderer.
|
||||
|
||||
Every prompt — system or user — lives as a `.txt` file in this directory.
|
||||
Two ways to consume them:
|
||||
|
||||
- `load(name)` returns the file's text unchanged. Use for system prompts
|
||||
(and any user prompt that has no placeholders).
|
||||
- `render(name, **vars)` loads then runs `str.format(**vars)` on it. Use
|
||||
for user prompts that interpolate question/SQL/rows/findings/etc.
|
||||
Placeholders are standard Python format syntax: `{name}`. Literal braces
|
||||
must be doubled: `{{` / `}}`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
PROMPTS_DIR = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def load(name: str) -> str:
|
||||
"""Read the named prompt file (no substitution)."""
|
||||
return (PROMPTS_DIR / f"{name}.txt").read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def render(name: str, /, **vars: Any) -> str:
|
||||
"""Load a prompt and substitute `{placeholder}` values from kwargs."""
|
||||
return load(name).format(**vars)
|
||||
5
api/prompts/compare_periods.interpret.txt
Normal file
5
api/prompts/compare_periods.interpret.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Task: compare two SQL result sets representing the same metric across two periods.
|
||||
|
||||
Output: two to four sentences. Lead with the most material difference and cite specific numbers from both periods (e.g. "61 in 1995 → 87 in 1996, +43%"). No preamble.
|
||||
|
||||
If the rows look essentially unchanged, say so plainly. If one period is empty, call that out.
|
||||
7
api/prompts/compare_periods.interpret.user.txt
Normal file
7
api/prompts/compare_periods.interpret.user.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Question: {question}
|
||||
|
||||
Period A ({label_a}):
|
||||
{rows_a}
|
||||
|
||||
Period B ({label_b}):
|
||||
{rows_b}
|
||||
16
api/prompts/compare_periods.pair.txt
Normal file
16
api/prompts/compare_periods.pair.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
Task: build two parallel Postgres SELECT queries that compute the same metric across two different time windows, so an analyst can compare them.
|
||||
|
||||
Output: a JSON object with exactly this shape, in one fenced ```json block:
|
||||
{
|
||||
"a": {"label": "<short period label, e.g. '1995'>", "sql": "<SELECT …>"},
|
||||
"b": {"label": "<short period label, e.g. '1996'>", "sql": "<SELECT …>"}
|
||||
}
|
||||
|
||||
Constraints:
|
||||
- The two queries must return result sets with the same columns and shape — otherwise the diff is meaningless.
|
||||
- Dates are stored as YYMMDD integers; convert with TO_DATE(LPAD(date::text, 6, '0'), 'YYMMDD') when filtering by date.
|
||||
- Quote `"order"` if you reference it.
|
||||
- search_path is `financial`; don't qualify table names with the schema.
|
||||
- Read-only.
|
||||
|
||||
If either period is given as "(infer)", pick a sensible window from the question and reflect it in the label.
|
||||
10
api/prompts/compare_periods.pair.user.txt
Normal file
10
api/prompts/compare_periods.pair.user.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Question: {question}
|
||||
|
||||
Period A: {period_a}
|
||||
Period B: {period_b}
|
||||
|
||||
Schema:
|
||||
{schema_block}
|
||||
|
||||
Metrics:
|
||||
{metrics_block}
|
||||
5
api/prompts/direct_answer.interpret.txt
Normal file
5
api/prompts/direct_answer.interpret.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Task: answer the analyst's question given the SQL that was run and the result rows.
|
||||
|
||||
Output: one to three sentences that directly answer the question, citing specific numbers from the rows. No preamble ("Here is…", "Based on the results…"), no SQL block, no enumeration of every row.
|
||||
|
||||
If the result is empty or doesn't actually answer the question as asked, say so plainly and stop.
|
||||
7
api/prompts/direct_answer.interpret.user.txt
Normal file
7
api/prompts/direct_answer.interpret.user.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Question: {question}
|
||||
|
||||
SQL:
|
||||
{sql}
|
||||
|
||||
Rows (up to 20):
|
||||
{rows}
|
||||
17
api/prompts/planner.system.txt
Normal file
17
api/prompts/planner.system.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Task: pick which Analyses to run to answer the analyst's question.
|
||||
|
||||
Given the question, the list of warehouse tables, the metric catalog, and the catalog of available Analyses (each with a name, description, and args schema), choose the smallest set of Analyses whose combined output will produce a defensible answer.
|
||||
|
||||
Output: a JSON object with exactly this shape, in one fenced ```json block:
|
||||
{
|
||||
"rationale": "<one sentence on why this plan>",
|
||||
"steps": [
|
||||
{"analysis": "<analysis_name>", "args": { ... }, "why": "<one sentence>"}
|
||||
]
|
||||
}
|
||||
|
||||
Rules:
|
||||
- Only use analyses that appear in the catalog. If none fits the question well, pick `direct_answer`.
|
||||
- The `args` keys must match the analysis's args_schema; additional keys are ignored, missing optional ones are fine.
|
||||
- Most questions are single-step. Only chain analyses when the second genuinely needs the first's output.
|
||||
- Comparing two periods is ONE `compare_periods` step, not two `direct_answer` steps.
|
||||
9
api/prompts/planner.user.txt
Normal file
9
api/prompts/planner.user.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Question: {question}
|
||||
|
||||
Warehouse tables: {table_names}
|
||||
|
||||
Metric catalog:
|
||||
{metrics_block}
|
||||
|
||||
Analysis catalog:
|
||||
{catalog}
|
||||
7
api/prompts/synthesize.system.txt
Normal file
7
api/prompts/synthesize.system.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Task: write a final answer for the analyst given the original question and a list of Analysis findings.
|
||||
|
||||
Each finding has an Analysis name, its one-line interpretation, and the SQL it ran.
|
||||
|
||||
Output: a single coherent paragraph of two to five sentences that directly answers the question, weaving together the relevant findings. Cite specific numbers. Don't list the SQL, don't enumerate the Analyses by name, don't add any "Summary:" header or preamble.
|
||||
|
||||
If every finding errored, say plainly that the question couldn't be answered and briefly mention why (e.g. "the planner picked an Analysis that couldn't resolve the metric").
|
||||
4
api/prompts/synthesize.user.txt
Normal file
4
api/prompts/synthesize.user.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Question: {question}
|
||||
|
||||
Findings:
|
||||
{findings_block}
|
||||
15
api/prompts/text_to_sql.system.txt
Normal file
15
api/prompts/text_to_sql.system.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Task: translate the analyst's question into a single Postgres SELECT (or WITH … SELECT) against the BIRD `financial` schema (PKDD'99 Czech bank).
|
||||
|
||||
Output: exactly one fenced ```sql block, nothing else — no prose, no second block, no trailing semicolon required.
|
||||
|
||||
Constraints:
|
||||
- search_path is set to `financial`; don't qualify table names with the schema.
|
||||
- Quote identifiers that collide with SQL keywords (notably `"order"`).
|
||||
- Dates in the source are YYMMDD integers (e.g. 950315 → 1995-03-15). Convert with TO_DATE(LPAD(date::text, 6, '0'), 'YYMMDD') whenever you need a real date for filtering, comparison, or display.
|
||||
- Loan `status` values: 'A' finished OK, 'B' finished defaulted, 'C' running OK, 'D' running in debt.
|
||||
- Transaction `type` values: 'PRIJEM' credit, 'VYDAJ' debit.
|
||||
- Read-only: never emit DDL or DML.
|
||||
|
||||
When the question maps to a metric in the catalog you're given, copy the metric's SQL fragment verbatim — don't paraphrase it.
|
||||
|
||||
If the question is ambiguous, pick the most defensible interpretation and proceed; don't ask for clarification.
|
||||
10
api/prompts/text_to_sql.user.txt
Normal file
10
api/prompts/text_to_sql.user.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Question:
|
||||
{question}
|
||||
|
||||
Schema (only the tables that look relevant; you may use any of these):
|
||||
{schema_block}
|
||||
|
||||
Metric catalog (copy the SQL verbatim when the question maps to one):
|
||||
{metrics_block}
|
||||
|
||||
Return only the SQL in a ```sql fenced block.{retry_hint}
|
||||
Reference in New Issue
Block a user