11import type { z } from 'zod' ;
22import type { AIResponseJsonSchema } from '../prompts/agent' ;
3- import { validateSQL } from '../utils/sql-validator' ;
43import { executeQuery } from '../utils/query-executor' ;
4+ import { validateSQL } from '../utils/sql-validator' ;
55import type { StreamingUpdate } from '../utils/stream-utils' ;
66
77export 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
1313export 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
5366async 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+ }
0 commit comments