Add filter-analytics-table system prompt for row filters.

Constrain AI search to SELECT * FROM <table> WHERE ... so analytics table views keep the same columns.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Developing-Gamer 2026-07-14 11:08:33 -07:00
parent a21139958c
commit fbfa37c63d

View File

@ -44,11 +44,15 @@ export const SYSTEM_PROMPT_IDS = [
"create-dashboard",
"run-query",
"build-analytics-query",
"filter-analytics-table",
"rewrite-template-source",
] as const;
export type SystemPromptId = typeof SYSTEM_PROMPT_IDS[number];
/**
* Schema documentation for the analytics ClickHouse tables, shared by every
* prompt that generates SQL against them ("build-analytics-query") so the two can never drift apart.
* prompt that generates SQL against them ("build-analytics-query" and
* "filter-analytics-table") so the two can never drift apart.
*/
const ANALYTICS_TABLES_SCHEMA_DOC = `### DATA SCHEMA (project/branch filtering is automatic — do NOT add WHERE project_id = ...)
@ -236,8 +240,6 @@ Key insights: No timestamp column — do NOT attempt \`ORDER BY created_at\` or
| provider_account_id | String | External account identifier |
| created_at | DateTime64(3, 'UTC') | Link time |`;
export type SystemPromptId = typeof SYSTEM_PROMPT_IDS[number];
/**
* Context-specific system prompts that are appended to the base prompt.
* These should be concise and focus on the specific use case.
@ -1313,6 +1315,46 @@ GROUP BY date, event_type ORDER BY date DESC, event_count DESC LIMIT 100
- If the user refers to a previous query, modify it incrementally don't start from scratch.
- If \`queryAnalytics\` returns an error, adjust and retry. Do NOT invent columns or fabricate data.
- 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.
`,
"filter-analytics-table": `
## Context: Analytics Table Row Filter
You power the search bar above a data grid on the Hexclave analytics page. The grid shows every column of ONE table (\`SELECT * FROM <table>\`), and the user typed a request that a plain substring search can't express. Your ONLY job is to narrow down WHICH ROWS are shown — the set of columns must never change. The table the user is viewing is stated at the start of the conversation.
**HARD RULES query shape:**
Call \`queryAnalytics\` with a query of EXACTLY this shape:
\`SELECT * FROM <table> WHERE <condition>\`
1. The query MUST start with \`SELECT * FROM <table>\` for the exact table the user is viewing. The frontend validates this shape and rejects anything else.
2. After the table name, only a \`WHERE\` clause is allowed at the top level — no column lists, aliases, aggregates, GROUP BY, ORDER BY, LIMIT, JOIN, or UNION. The grid layers sorting and pagination on top itself.
3. Subqueries INSIDE the WHERE condition are allowed and encouraged for cross-table filters, e.g. \`WHERE toString(id) IN (SELECT user_id FROM events WHERE ...)\`. Mind the types: \`users.id\` is a UUID while \`events.user_id\` is a String, so wrap the UUID side in \`toString()\`.
4. Do NOT paste SQL into chat text in place of a tool call the UI only picks up tool calls, and only after you come to a complete stop, so avoid chatty responses.
5. You only see a small preview of the results in the tool output the user sees the full filtered table in the grid.
6. If \`queryAnalytics\` returns an error, fix the condition and retry.
7. If the user refines the filter (e.g. "only verified ones"), modify your previous WHERE condition incrementally.
8. If the request CANNOT be expressed as a row filter on this table (e.g. it asks for aggregations, trends, grouped counts, joins that add columns, or data from a different table), do NOT call the tool. Reply with ONE short sentence explaining why, and if possible suggest the closest row filter you COULD apply instead.
${ANALYTICS_TABLES_SCHEMA_DOC}
### CRITICAL SQL RULES
1. **JSON extraction REQUIRES toString() wrapper:**
- CORRECT: \`JSONExtractString(toString(data), 'path')\`
- WRONG: \`JSONExtractString(data, 'path')\` — this WILL FAIL
2. **Nested JSON uses dot notation:** \`JSONExtractString(toString(data), 'ip_info.country_code')\`
3. SELECT queries only no INSERT / UPDATE / DELETE / DDL
4. Use relative date ranges (\`now() - INTERVAL X DAY\`) and ClickHouse date helpers: toDate(), toStartOfDay(), toStartOfWeek(), toStartOfMonth()
5. team_id is always NULL never filter on it
6. Prefer case-insensitive matching (\`ILIKE '%...%'\`) for user-typed text unless the user clearly means an exact value
### EXAMPLES (user is viewing \`users\`)
- "signed up in the last 7 days" \`SELECT * FROM users WHERE signed_up_at >= now() - INTERVAL 7 DAY\`
- "verified gmail accounts" \`SELECT * FROM users WHERE primary_email_verified = 1 AND primary_email ILIKE '%@gmail.com%'\`
- "people with a page view this week" \`SELECT * FROM users WHERE toString(id) IN (SELECT user_id FROM events WHERE event_type = '$page-view' AND event_at >= now() - INTERVAL 7 DAY)\`
- "signups per day last month" no tool call; explain that's an aggregation this view can't display, and offer e.g. "show signups from last month" as a filter instead.
`,
"rewrite-template-source": `You rewrite email template TSX source into standalone draft TSX.