Skip to content

Commit d01ae59

Browse files
authored
Merge pull request #27 from haydenbleasel/ultracite
Ultracite
2 parents e737a75 + f973afe commit d01ae59

File tree

638 files changed

+44175
-36340
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

638 files changed

+44175
-36340
lines changed

.vscode/settings.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
{
2-
"typescript.tsdk": "node_modules\\typescript\\lib"
2+
"typescript.tsdk": "node_modules/typescript/lib",
3+
"editor.defaultFormatter": "esbenp.prettier-vscode",
4+
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
5+
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
6+
"[javascriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
7+
"[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
8+
"[json]": { "editor.defaultFormatter": "biomejs.biome" },
9+
"[jsonc]": { "editor.defaultFormatter": "biomejs.biome" },
10+
"[css]": { "editor.defaultFormatter": "biomejs.biome" },
11+
"[graphql]": { "editor.defaultFormatter": "biomejs.biome" },
12+
"editor.formatOnSave": true,
13+
"editor.formatOnPaste": true,
14+
"emmet.showExpandedAbbreviation": "never",
15+
"editor.codeActionsOnSave": {
16+
"source.fixAll.biome": "explicit",
17+
"source.organizeImports.biome": "explicit"
18+
}
319
}

apps/api/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
2323
.env.local
2424

2525
# caches
26-
.eslintcache
2726
.cache
2827
*.tsbuildinfo
2928

apps/api/biome.json

Lines changed: 0 additions & 37 deletions
This file was deleted.

apps/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@
2424
"nats": "^2.29.3",
2525
"openai": "^5.9.0"
2626
}
27-
}
27+
}
Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,74 @@
11
import type { z } from 'zod';
22
import type { AIResponseJsonSchema } from '../prompts/agent';
3-
import { validateSQL } from '../utils/sql-validator';
43
import { executeQuery } from '../utils/query-executor';
4+
import { validateSQL } from '../utils/sql-validator';
55
import type { StreamingUpdate } from '../utils/stream-utils';
66

77
export interface ChartHandlerContext {
8-
user: any;
9-
website: any;
10-
debugInfo: Record<string, unknown>;
11-
startTime: number;
12-
aiTime: number;
8+
user: any;
9+
website: any;
10+
debugInfo: Record<string, unknown>;
11+
startTime: number;
12+
aiTime: number;
1313
}
1414

1515
export async function* handleChartResponse(
16-
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
17-
context: ChartHandlerContext
16+
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
17+
context: ChartHandlerContext
1818
): AsyncGenerator<StreamingUpdate> {
19-
if (!parsedAiJson.sql) {
20-
yield {
21-
type: 'error',
22-
content: "AI did not provide a query for the chart.",
23-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
24-
};
25-
return;
26-
}
19+
if (!parsedAiJson.sql) {
20+
yield {
21+
type: 'error',
22+
content: 'AI did not provide a query for the chart.',
23+
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined,
24+
};
25+
return;
26+
}
2727

28-
if (!validateSQL(parsedAiJson.sql)) {
29-
yield {
30-
type: 'error',
31-
content: "Generated query failed security validation.",
32-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
33-
};
34-
return;
35-
}
28+
if (!validateSQL(parsedAiJson.sql)) {
29+
yield {
30+
type: 'error',
31+
content: 'Generated query failed security validation.',
32+
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined,
33+
};
34+
return;
35+
}
3636

37-
try {
38-
const queryResult = await executeQuery(parsedAiJson.sql);
39-
const totalTime = Date.now() - context.startTime;
37+
try {
38+
const queryResult = await executeQuery(parsedAiJson.sql);
39+
const totalTime = Date.now() - context.startTime;
4040

41-
if (context.user.role === 'ADMIN') {
42-
context.debugInfo.processing = {
43-
aiTime: context.aiTime,
44-
queryTime: Date.now() - context.startTime - context.aiTime,
45-
totalTime
46-
};
47-
}
48-
49-
yield {
50-
type: 'complete',
51-
content: queryResult.data.length > 0
52-
? `Found ${queryResult.data.length} data points. Displaying as a ${parsedAiJson.chart_type?.replace(/_/g, ' ') || 'chart'}.`
53-
: "No data found for your query.",
54-
data: {
55-
hasVisualization: queryResult.data.length > 0,
56-
chartType: parsedAiJson.chart_type,
57-
data: queryResult.data,
58-
responseType: 'chart'
59-
},
60-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
61-
};
62-
} catch (queryError: unknown) {
63-
console.error('❌ SQL execution error', {
64-
error: queryError instanceof Error ? queryError.message : 'Unknown error',
65-
sql: parsedAiJson.sql
66-
});
67-
yield {
68-
type: 'error',
69-
content: "Database query failed. The data might not be available.",
70-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
71-
};
41+
if (context.user.role === 'ADMIN') {
42+
context.debugInfo.processing = {
43+
aiTime: context.aiTime,
44+
queryTime: Date.now() - context.startTime - context.aiTime,
45+
totalTime,
46+
};
7247
}
73-
}
48+
49+
yield {
50+
type: 'complete',
51+
content:
52+
queryResult.data.length > 0
53+
? `Found ${queryResult.data.length} data points. Displaying as a ${parsedAiJson.chart_type?.replace(/_/g, ' ') || 'chart'}.`
54+
: 'No data found for your query.',
55+
data: {
56+
hasVisualization: queryResult.data.length > 0,
57+
chartType: parsedAiJson.chart_type,
58+
data: queryResult.data,
59+
responseType: 'chart',
60+
},
61+
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined,
62+
};
63+
} catch (queryError: unknown) {
64+
console.error('❌ SQL execution error', {
65+
error: queryError instanceof Error ? queryError.message : 'Unknown error',
66+
sql: parsedAiJson.sql,
67+
});
68+
yield {
69+
type: 'error',
70+
content: 'Database query failed. The data might not be available.',
71+
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined,
72+
};
73+
}
74+
}
Lines changed: 68 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,89 @@
11
import type { z } from 'zod';
22
import type { AIResponseJsonSchema } from '../prompts/agent';
3-
import { validateSQL } from '../utils/sql-validator';
43
import { executeQuery } from '../utils/query-executor';
4+
import { validateSQL } from '../utils/sql-validator';
55
import type { StreamingUpdate } from '../utils/stream-utils';
66

77
export interface MetricHandlerContext {
8-
user: any;
9-
website: any;
10-
debugInfo: Record<string, unknown>;
8+
user: any;
9+
website: any;
10+
debugInfo: Record<string, unknown>;
1111
}
1212

1313
export async function* handleMetricResponse(
14-
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
15-
context: MetricHandlerContext
14+
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
15+
context: MetricHandlerContext
1616
): AsyncGenerator<StreamingUpdate> {
17-
if (parsedAiJson.sql) {
18-
if (!validateSQL(parsedAiJson.sql)) {
19-
yield {
20-
type: 'error',
21-
content: "Generated query failed security validation.",
22-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
23-
};
24-
return;
25-
}
17+
if (parsedAiJson.sql) {
18+
if (!validateSQL(parsedAiJson.sql)) {
19+
yield {
20+
type: 'error',
21+
content: 'Generated query failed security validation.',
22+
debugInfo:
23+
context.user.role === 'ADMIN' ? context.debugInfo : undefined,
24+
};
25+
return;
26+
}
2627

27-
try {
28-
const queryResult = await executeQuery(parsedAiJson.sql);
29-
const metricValue = extractMetricValue(queryResult.data, parsedAiJson.metric_value);
30-
yield* sendMetricResponse(parsedAiJson, metricValue, context);
31-
} catch (queryError: unknown) {
32-
console.error('❌ Metric SQL execution error', {
33-
error: queryError instanceof Error ? queryError.message : 'Unknown error',
34-
sql: parsedAiJson.sql
35-
});
36-
yield* sendMetricResponse(parsedAiJson, parsedAiJson.metric_value, context);
37-
}
38-
} else {
39-
yield* sendMetricResponse(parsedAiJson, parsedAiJson.metric_value, context);
28+
try {
29+
const queryResult = await executeQuery(parsedAiJson.sql);
30+
const metricValue = extractMetricValue(
31+
queryResult.data,
32+
parsedAiJson.metric_value
33+
);
34+
yield* sendMetricResponse(parsedAiJson, metricValue, context);
35+
} catch (queryError: unknown) {
36+
console.error('❌ Metric SQL execution error', {
37+
error:
38+
queryError instanceof Error ? queryError.message : 'Unknown error',
39+
sql: parsedAiJson.sql,
40+
});
41+
yield* sendMetricResponse(
42+
parsedAiJson,
43+
parsedAiJson.metric_value,
44+
context
45+
);
4046
}
47+
} else {
48+
yield* sendMetricResponse(parsedAiJson, parsedAiJson.metric_value, context);
49+
}
4150
}
4251

43-
function extractMetricValue(queryData: unknown[], defaultValue: unknown): unknown {
44-
if (!queryData.length || !queryData[0]) return defaultValue;
52+
function extractMetricValue(
53+
queryData: unknown[],
54+
defaultValue: unknown
55+
): unknown {
56+
if (!(queryData.length && queryData[0])) return defaultValue;
4557

46-
const firstRow = queryData[0] as Record<string, unknown>;
47-
const valueKey = Object.keys(firstRow).find(key => typeof firstRow[key] === 'number') ||
48-
Object.keys(firstRow)[0];
58+
const firstRow = queryData[0] as Record<string, unknown>;
59+
const valueKey =
60+
Object.keys(firstRow).find((key) => typeof firstRow[key] === 'number') ||
61+
Object.keys(firstRow)[0];
4962

50-
return valueKey ? firstRow[valueKey] : defaultValue;
63+
return valueKey ? firstRow[valueKey] : defaultValue;
5164
}
5265

5366
async function* sendMetricResponse(
54-
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
55-
metricValue: unknown,
56-
context: MetricHandlerContext
67+
parsedAiJson: z.infer<typeof AIResponseJsonSchema>,
68+
metricValue: unknown,
69+
context: MetricHandlerContext
5770
): AsyncGenerator<StreamingUpdate> {
58-
const formattedValue = typeof metricValue === 'number'
59-
? metricValue.toLocaleString()
60-
: metricValue;
71+
const formattedValue =
72+
typeof metricValue === 'number'
73+
? metricValue.toLocaleString()
74+
: metricValue;
6175

62-
yield {
63-
type: 'complete',
64-
content: parsedAiJson.text_response ||
65-
`${parsedAiJson.metric_label || 'Result'}: ${formattedValue}`,
66-
data: {
67-
hasVisualization: false,
68-
responseType: 'metric',
69-
metricValue: metricValue,
70-
metricLabel: parsedAiJson.metric_label
71-
},
72-
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined
73-
};
74-
}
76+
yield {
77+
type: 'complete',
78+
content:
79+
parsedAiJson.text_response ||
80+
`${parsedAiJson.metric_label || 'Result'}: ${formattedValue}`,
81+
data: {
82+
hasVisualization: false,
83+
responseType: 'metric',
84+
metricValue,
85+
metricLabel: parsedAiJson.metric_label,
86+
},
87+
debugInfo: context.user.role === 'ADMIN' ? context.debugInfo : undefined,
88+
};
89+
}

apps/api/src/agent/index.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
export { validateSQL } from './utils/sql-validator';
2-
export { executeQuery } from './utils/query-executor';
3-
export { getAICompletion } from './utils/ai-client';
4-
export { parseAIResponse } from './utils/response-parser';
5-
export { createStreamingResponse, generateThinkingSteps } from './utils/stream-utils';
6-
export type { StreamingUpdate } from './utils/stream-utils';
7-
8-
export { handleMetricResponse } from './handlers/metric-handler';
1+
export type { ChartHandlerContext } from './handlers/chart-handler';
92
export { handleChartResponse } from './handlers/chart-handler';
103
export type { MetricHandlerContext } from './handlers/metric-handler';
11-
export type { ChartHandlerContext } from './handlers/chart-handler';
12-
4+
export { handleMetricResponse } from './handlers/metric-handler';
5+
export type { AssistantContext, AssistantRequest } from './processor';
136
export { processAssistantRequest } from './processor';
14-
export type { AssistantRequest, AssistantContext } from './processor';
15-
16-
export { comprehensiveUnifiedPrompt, AIResponseJsonSchema, AIPlanSchema } from './prompts/agent';
7+
export {
8+
AIPlanSchema,
9+
AIResponseJsonSchema,
10+
comprehensiveUnifiedPrompt,
11+
} from './prompts/agent';
12+
export { getAICompletion } from './utils/ai-client';
13+
export { executeQuery } from './utils/query-executor';
14+
export { parseAIResponse } from './utils/response-parser';
15+
export { validateSQL } from './utils/sql-validator';
16+
export type { StreamingUpdate } from './utils/stream-utils';
17+
export {
18+
createStreamingResponse,
19+
generateThinkingSteps,
20+
} from './utils/stream-utils';

0 commit comments

Comments
 (0)