You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Added support for the "build-analytics-query" system prompt in the AI query handler, allowing for more complex SQL query generation.
- Updated the step limit logic in the AI query handler to accommodate the new prompt.
- Introduced new components: AiQueryBar and AiQueryDialog for improved user interaction with AI-driven analytics queries.
- Implemented a new QueryDataGrid component to display results from AI-generated queries.
- Enhanced the toolbar in the DataGrid to support custom search functionalities and improved layout.
- Updated documentation and SQL query guidelines to reflect changes in event data structure and extraction requirements.
This update significantly improves the user experience for querying analytics data through AI, providing a more intuitive interface and robust backend support.
- Always use LIMIT to avoid returning too many rows (default to LIMIT 100)
974
-
- Use appropriate date functions: toDate(), toStartOfDay(), toStartOfWeek(), etc.
975
-
- For counting, use COUNT(*) or COUNT(DISTINCT column)
985
+
- Use relative date ranges: now() - INTERVAL X DAY
986
+
- Use date functions: toDate(), toStartOfDay(), toStartOfWeek(), etc.
987
+
- For counting, use count() or count(DISTINCT column)
976
988
977
989
**Example Queries:**
978
-
- Count users: \`SELECT COUNT(*) FROM users\`
990
+
- Count users: \`SELECT count() FROM users\`
979
991
- Recent signups: \`SELECT * FROM users ORDER BY signed_up_at DESC LIMIT 10\`
980
-
- Events today: \`SELECT COUNT(*) FROM events WHERE toDate(event_at) = today()\`
981
-
- Event types: \`SELECT event_type, COUNT(*) as countFROM events GROUP BY event_type ORDER BY count DESC LIMIT 10\`
992
+
- Events today: \`SELECT count() FROM events WHERE toDate(event_at) = today()\`
993
+
- Page views by path: \`SELECT JSONExtractString(toString(data), 'path') as path, count() as views FROM events WHERE event_type = '$page-view' GROUP BY path ORDER BY views DESC LIMIT 20\`
982
994
983
995
**Focus:**
984
996
- Help users write efficient, correct ClickHouse SQL queries
985
997
- Explain query results clearly
986
998
- Suggest relevant queries based on user questions
987
999
- Use the queryAnalytics tool to execute queries and return results
1000
+
`,
1001
+
1002
+
"build-analytics-query": `
1003
+
## Context: Analytics Query Builder
1004
+
1005
+
You are a ClickHouse SQL expert helping the user build queries that drive a data grid on the Stack Auth analytics page. The user asks questions in natural language; you translate them into accurate, one-shot ClickHouse SQL. You have complete schema knowledge below — use it to generate correct queries immediately without needing to inspect the data first.
1006
+
1007
+
**HARD RULE — how the tool works:**
1008
+
Call \`queryAnalytics\` with your SQL query. The grid runs the full query independently — you only receive a preview (first 50 rows) to confirm the query is correct. The frontend only applies the query after the agent comes to a complete stop, so avoid being too chatty in the first few turns unless the user asks for it.
1009
+
1. Do NOT paste SQL into chat text in place of a tool call — the UI will not pick it up.
1010
+
2. You only see a small preview in the tool result — the user sees the full result set in the grid.
1011
+
3. Because you only get 50 preview rows, do NOT try to analyze full result sets from the tool output. If the user asks about the data, describe the query and let them read the grid.
1012
+
4. The grid wraps your query as a subquery: \`SELECT * FROM (<your query>) LIMIT 50 OFFSET ...\` and paginates via infinite scroll. Your LIMIT sets the **maximum total rows** the user can scroll through — use generous limits (e.g. 1000 for aggregates) so the grid can paginate the full result.
1013
+
1014
+
### DATA SCHEMA (project/branch filtering is automatic — do NOT add WHERE project_id = ...)
1015
+
1016
+
**users** table:
1017
+
| Column | Type | Notes |
1018
+
|--------|------|-------|
1019
+
| id | UUID | Primary key |
1020
+
| display_name | Nullable(String) | Typically populated |
1021
+
| primary_email | Nullable(String) | Usually present |
3. SELECT queries only — no INSERT / UPDATE / DELETE / DDL
1086
+
4. ALWAYS include LIMIT — this caps the total rows the user can scroll through in the grid (default 100 for row samples, 1000 for aggregates)
1087
+
5. Use relative date ranges: \`now() - INTERVAL X DAY\`
1088
+
6. team_id is always NULL — never filter on it
1089
+
7. Metadata fields are almost always empty — safe to ignore
1090
+
8. Prefer aggregates (count, sum, avg, quantile, GROUP BY) when the user is asking a question
1091
+
9. Use ClickHouse date helpers: toDate(), toStartOfDay(), toStartOfWeek(), toStartOfMonth()
1092
+
1093
+
### COMMON QUERY PATTERNS
1094
+
1095
+
Signups by day:
1096
+
\`\`\`sql
1097
+
SELECT toDate(signed_up_at) as date, count() as signups
1098
+
FROM users WHERE signed_up_at >= now() - INTERVAL 30 DAY
1099
+
GROUP BY date ORDER BY date DESC LIMIT 100
1100
+
\`\`\`
1101
+
1102
+
Page views by path:
1103
+
\`\`\`sql
1104
+
SELECT JSONExtractString(toString(data), 'path') as path, count() as views
1105
+
FROM events WHERE event_type = '$page-view' AND event_at >= now() - INTERVAL 7 DAY
1106
+
GROUP BY path ORDER BY views DESC LIMIT 20
1107
+
\`\`\`
1108
+
1109
+
Token refreshes by country:
1110
+
\`\`\`sql
1111
+
SELECT JSONExtractString(toString(data), 'ip_info.country_code') as country,
1112
+
count() as refreshes, count(DISTINCT user_id) as unique_users
1113
+
FROM events WHERE event_type = '$token-refresh' AND event_at >= now() - INTERVAL 7 DAY
1114
+
GROUP BY country ORDER BY refreshes DESC LIMIT 50
1115
+
\`\`\`
1116
+
1117
+
Email verification adoption:
1118
+
\`\`\`sql
1119
+
SELECT primary_email_verified, count() as users
1120
+
FROM users WHERE signed_up_at >= now() - INTERVAL 30 DAY
1121
+
GROUP BY primary_email_verified LIMIT 10
1122
+
\`\`\`
1123
+
1124
+
Event volume trends by type:
1125
+
\`\`\`sql
1126
+
SELECT toDate(event_at) as date, event_type, count() as event_count
1127
+
FROM events WHERE event_at >= now() - INTERVAL 30 DAY
1128
+
GROUP BY date, event_type ORDER BY date DESC, event_count DESC LIMIT 100
1129
+
\`\`\`
1130
+
1131
+
### INTERACTION STYLE
1132
+
1133
+
- Generate accurate one-shot queries using the schema above. Do NOT run inspection queries unless the user asks about something genuinely ambiguous that the schema doesn't cover.
1134
+
- Keep chat messages short — the user sees the grid directly.
1135
+
- If the user refers to a previous query, modify it incrementally — don't start from scratch.
1136
+
- If \`queryAnalytics\` returns an error, adjust and retry. Do NOT invent columns or fabricate data.
1137
+
- If the user asks about event types or data that don't exist in the schema above, explain what IS available and generate the closest useful query instead.
description: "Run a read-only ClickHouse SQL query against the project's analytics database for INSPECTION. Only SELECT queries are allowed. Project filtering is automatic. Results are capped at 50 rows for your context — always include a LIMIT clause and prefer aggregates (count, sum, min, max, avg, quantile, GROUP BY) over SELECT *.",
18
+
description: `Set and validate a ClickHouse SQL query for the analytics data grid. The grid runs the full query independently — you only receive a preview of the first ${MAX_ROWS_FOR_AI} rows to confirm correctness. Only SELECT queries are allowed. Project filtering is automatic. Always include a LIMIT clause.`,
19
19
inputSchema: z.object({
20
20
query: z
21
21
.string()
22
-
.describe("The ClickHouse SQL query to execute. Only SELECT queries are allowed. Always include a LIMIT clause (≤20 for row samples)."),
22
+
.describe("The ClickHouse SQL query to execute. Only SELECT queries are allowed. Always include a LIMIT clause unless the system prompt tells you to do otherwise."),
0 commit comments