diff --git a/apps/backend/src/lib/ai/prompts.ts b/apps/backend/src/lib/ai/prompts.ts index eca03b24f..5e7ce2809 100644 --- a/apps/backend/src/lib/ai/prompts.ts +++ b/apps/backend/src/lib/ai/prompts.ts @@ -44,15 +44,11 @@ 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" and - * "filter-analytics-table") so the two can never drift apart. + * prompt that generates SQL against them ("build-analytics-query") 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 = ...) @@ -240,6 +236,8 @@ 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. @@ -1315,46 +1313,6 @@ 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 \`), 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
WHERE \` - -1. The query MUST start with \`SELECT * FROM
\` 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.