drizzle-cube 0.4.18 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/express/index.cjs +6 -6
- package/dist/adapters/express/index.js +75 -74
- package/dist/adapters/fastify/index.cjs +5 -5
- package/dist/adapters/fastify/index.js +91 -90
- package/dist/adapters/{handler-DefTXJpi.js → handler-CbDMdSY5.js} +95 -82
- package/dist/adapters/{handler-hwoGzGex.cjs → handler-DtdjM1Vx.cjs} +18 -14
- package/dist/adapters/hono/index.cjs +6 -6
- package/dist/adapters/hono/index.js +66 -65
- package/dist/adapters/nextjs/index.cjs +5 -5
- package/dist/adapters/nextjs/index.js +116 -115
- package/dist/server/index.cjs +6 -2
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +14 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -743,7 +743,7 @@ Chart config requirements by type:`];for(const t of i){const s=ct[t];if(!s)conti
|
|
|
743
743
|
`+HT(rt)+`
|
|
744
744
|
The query is validated before adding. The portlet fetches its own data.`,input_schema:{type:"object",properties:{title:{type:"string",description:"Title for the visualization"},query:{type:"string",description:'JSON string of the query. Standard: {"measures":[...],"dimensions":[...]}. Funnel: {"funnel":{"bindingKey":"...","timeDimension":"...","steps":[...]}}. Flow: {"flow":{"bindingKey":"...","timeDimension":"...","eventDimension":"...","startingStep":{...}}}. Retention: {"retention":{"timeDimension":"...","bindingKey":"...","dateRange":{"start":"...","end":"..."},"granularity":"...","periods":N}}.'},chartType:{type:"string",enum:rt,description:"Chart type to render"},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",properties:{showLegend:{type:"boolean"},showGrid:{type:"boolean"},showTooltip:{type:"boolean"},stacked:{type:"boolean"},orientation:{type:"string",enum:["horizontal","vertical"]}},description:"Chart display configuration"}},required:["title","query","chartType"]}},{name:"add_markdown",description:"Add an explanation or analysis text block to the notebook. Use markdown formatting. Use this to explain findings, methodology, and insights alongside visualizations.",input_schema:{type:"object",properties:{title:{type:"string",description:"Optional title for the text block"},content:{type:"string",description:"Markdown content to display"}},required:["content"]}},{name:"save_as_dashboard",description:"Convert the current notebook analysis into a persistent dashboard. Constructs a professional DashboardConfig with proper grid layout, section headers (markdown portlets), and dashboard-level filters. Call this when the user asks to save/export the notebook as a dashboard.",input_schema:{type:"object",properties:{title:{type:"string",description:"Dashboard title"},description:{type:"string",description:"Optional dashboard description"},portlets:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique portlet ID"},title:{type:"string",description:"Portlet title"},chartType:{type:"string",enum:rt,description:'Chart type. Use "markdown" for section headers.'},query:{type:"string",description:"JSON string of the query. Omit or leave empty for markdown portlets."},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",description:"Chart display configuration (for markdown: { content, hideHeader, transparentBackground, autoHeight })"},dashboardFilterMapping:{type:"array",items:{type:"string"},description:"Array of dashboard filter IDs that apply to this portlet"},analysisType:{type:"string",enum:["query","funnel","flow","retention"],description:'Analysis type (default: "query")'},w:{type:"number",description:"Grid width (1-12)"},h:{type:"number",description:"Grid height in row units"},x:{type:"number",description:"Grid x position (0-11)"},y:{type:"number",description:"Grid y position"}},required:["id","title","chartType","w","h","x","y"]},description:"Array of portlet configurations for the dashboard"},filters:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique filter ID"},label:{type:"string",description:"Display label for the filter"},filter:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"],description:"The filter definition"},isUniversalTime:{type:"boolean",description:"When true, applies to all time dimensions in portlets"}},required:["id","label","filter"]},description:"Dashboard-level filters"},colorPalette:{type:"string",description:"Color palette name"}},required:["title","portlets"]}}]}function Zs(i){const{semanticLayer:e,securityContext:t}=i,s=new Map;s.set("discover_cubes",async E=>{const a=await Xo(e,{topic:E.topic,intent:E.intent,limit:E.limit,minScore:E.minScore});return{result:JSON.stringify(a,null,2)}}),s.set("get_cube_metadata",async()=>{const E=e.getMetadata();return{result:JSON.stringify(E,null,2)}});const n=new Map;for(const E of e.getMetadata())n.set(E.name,{measures:(E.measures||[]).map(a=>a.name),dimensions:(E.dimensions||[]).map(a=>a.name)});const r=(E,a)=>{const A=n.get(E),T=a==="measures"?A?.measures:A?.dimensions;return!T||T.length===0?"":` Available ${a}: ${T.slice(0,5).map(R=>`"${R}"`).join(", ")}`};return s.set("execute_query",async E=>{try{const a=(l,S)=>{if(!Array.isArray(l))return;const N=[],I=[];for(const u of l){if(typeof u!="string"){I.push(u);continue}const c=u.split(".");if(c.length===1){N.push(`"${u}" is not valid — must be "CubeName.fieldName".${r(u,S)}`);continue}if(c.length===3&&c[0]===c[1]){const O=`${c[0]}.${c[2]}`;I.push(O);continue}if(c.length===2&&c[0]===c[1]){N.push(`"${u}" is WRONG — "${c[0]}" is the cube name, not a ${S.replace(/s$/,"")}.${r(c[0],S)}`);continue}I.push(u)}if(N.length>0)throw new Error(`Invalid ${S}:
|
|
745
745
|
${N.join(`
|
|
746
|
-
`)}`);return I};E.measures=a(E.measures,"measures")??E.measures,E.dimensions=a(E.dimensions,"dimensions")??E.dimensions;const A=l=>{const S=l.split(".");return S.length===3&&S[0]===S[1]?`${S[0]}.${S[2]}`:l};if(Array.isArray(E.filters))for(const l of E.filters)typeof l.member=="string"&&(l.member=A(l.member));if(Array.isArray(E.timeDimensions))for(const l of E.timeDimensions)typeof l.dimension=="string"&&(l.dimension=A(l.dimension));let T;E.funnel?T={funnel:E.funnel}:E.flow?T={flow:E.flow}:E.retention?T={retention:E.retention}:T={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit};const R=await Ko(e,t,{query:T});return{result:JSON.stringify({rowCount:R.data.length,data:R.data,annotation:R.annotation},null,2)}}catch(a){const A={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit,...E.funnel?{funnel:E.funnel}:{},...E.flow?{flow:E.flow}:{},...E.retention?{retention:E.retention}:{}};return{result:`Query execution failed: ${a instanceof Error?a.message:"Unknown error"}
|
|
746
|
+
`)}`);return I};E.measures=a(E.measures,"measures")??E.measures,E.dimensions=a(E.dimensions,"dimensions")??E.dimensions;const A=l=>{const S=l.split(".");return S.length===3&&S[0]===S[1]?`${S[0]}.${S[2]}`:l};if(Array.isArray(E.filters))for(const l of E.filters)typeof l.member=="string"&&(l.member=A(l.member));if(Array.isArray(E.timeDimensions))for(const l of E.timeDimensions)typeof l.dimension=="string"&&(l.dimension=A(l.dimension));if(E.order&&typeof E.order=="object"&&!Array.isArray(E.order)){const l={};for(const[S,N]of Object.entries(E.order))l[A(S)]=N;E.order=l}let T;E.funnel?T={funnel:E.funnel}:E.flow?T={flow:E.flow}:E.retention?T={retention:E.retention}:T={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit};const R=await Ko(e,t,{query:T});return{result:JSON.stringify({rowCount:R.data.length,data:R.data,annotation:R.annotation},null,2)}}catch(a){const A={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit,...E.funnel?{funnel:E.funnel}:{},...E.flow?{flow:E.flow}:{},...E.retention?{retention:E.retention}:{}};return{result:`Query execution failed: ${a instanceof Error?a.message:"Unknown error"}
|
|
747
747
|
|
|
748
748
|
Attempted query:
|
|
749
749
|
${JSON.stringify(A,null,2)}`,isError:!0}}}),s.set("add_portlet",async E=>{const A={number:"kpiNumber",retention:"retentionHeatmap"}[E.chartType]??E.chartType;let T;try{T=JSON.parse(E.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const R=e.validateQuery(T);if(!R.isValid)return{result:`Invalid query — fix these errors and retry:
|
|
@@ -755,4 +755,8 @@ ${JSON.stringify(T,null,2)}`,isError:!0};const l=!!(T.funnel||T.flow||T.retentio
|
|
|
755
755
|
${c.errors.join(`
|
|
756
756
|
`)}`,isError:!0};S=u}const N=`portlet-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,I={id:N,title:E.title,query:E.query,chartType:A,chartConfig:S,displayConfig:E.displayConfig};return{result:`Portlet "${E.title}" added to notebook (id: ${N}, chart: ${A}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_portlet",data:I}}}),s.set("add_markdown",async E=>{const a=`markdown-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,A={id:a,title:E.title,content:E.content};return{result:`Markdown block added to notebook (id: ${a}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_markdown",data:A}}}),s.set("save_as_dashboard",async E=>{try{const a=E.portlets;if(!a||a.length===0)return{result:"Dashboard must contain at least one portlet.",isError:!0};const A=[];for(const N of a){if(N.chartType==="markdown")continue;const u=N.query;if(!u){A.push(`Portlet "${N.title}": missing query`);continue}let c;try{c=JSON.parse(u)}catch{A.push(`Portlet "${N.title}": invalid JSON query`);continue}const O=e.validateQuery(c);O.isValid||A.push(`Portlet "${N.title}": ${O.errors.join(", ")}`)}if(A.length>0)return{result:`Dashboard has invalid portlets — fix these errors and retry:
|
|
757
757
|
${A.join(`
|
|
758
|
-
`)}`,isError:!0};const T={portlets:a.map(N=>{const I=N.chartType,u=I==="markdown",c=u?"query":N.analysisType||"query",O=c==="funnel"?"funnel":c==="flow"?"flow":c==="retention"?"retention":"query",C=N.query||"{}";let D;try{D=JSON.parse(C)}catch{D={}}const f={version:1,analysisType:O,activeView:"chart",charts:{[O]:{chartType:I,chartConfig:N.chartConfig||{},displayConfig:N.displayConfig||{}}},query:u?{}:D};return{id:N.id,title:N.title,analysisConfig:f,dashboardFilterMapping:N.dashboardFilterMapping,w:N.w,h:N.h,x:N.x,y:N.y}}),filters:E.filters,colorPalette:E.colorPalette},R=E.title,l=T.portlets.length,S=T.filters?.length||0;return{result:`Dashboard "${R}" created with ${l} portlets and ${S} filters.`,sideEffect:{type:"dashboard_saved",data:{title:R,description:E.description,dashboardConfig:T}}}}catch(a){return{result:`Failed to save dashboard: ${a instanceof Error?a.message:"Unknown error"}`,isError:!0}}}),s}async function*YT(i){const{message:e,history:t,semanticLayer:s,securityContext:n,agentConfig:r,apiKey:E}=i,a=i.sessionId||crypto.randomUUID(),A=r.observability,T=crypto.randomUUID(),R=Date.now();let l;try{const p=await import("@anthropic-ai/sdk");l=p.default||p.Anthropic||p}catch{yield{type:"error",data:{message:"@anthropic-ai/sdk is required. Install it with: npm install @anthropic-ai/sdk"}};return}const S=new l({apiKey:E}),N=js(),I=Zs({semanticLayer:s,securityContext:n}),u=s.getMetadata(),c=Qs(u),O=r.model||"claude-sonnet-4-6",C=r.maxTurns||25,D=r.maxTokens||4096;try{A?.onChatStart?.({traceId:T,sessionId:a,message:e,model:O,historyLength:t?.length??0})}catch{}const f=[];if(t&&t.length>0){for(const p of t)if(p.role==="user")f.push({role:"user",content:p.content});else if(p.role==="assistant"){const M=[];if(p.content&&M.push({type:"text",text:p.content}),p.toolCalls&&p.toolCalls.length>0){for(const y of p.toolCalls)M.push({type:"tool_use",id:y.id,name:y.name,input:y.input||{}});f.push({role:"assistant",content:M}),f.push({role:"user",content:p.toolCalls.map(y=>({type:"tool_result",tool_use_id:y.id,content:typeof y.result=="string"?y.result:JSON.stringify(y.result??""),...y.status==="error"?{is_error:!0}:{}}))})}else M.length>0&&f.push({role:"assistant",content:p.content})}}f.push({role:"user",content:e});let m=0;try{for(let p=0;p<C;p++){m=p+1;const M=await S.messages.create({model:O,max_tokens:D,system:c,tools:N,messages:f,stream:!0}),y=[];let B=-1,g="",W="",Oe,z;const k=Date.now();for await(const q of M)switch(q.type){case"content_block_start":{B++;const G=q.content_block;G.type==="tool_use"?(y.push({type:"tool_use",id:G.id,name:G.name,input:{}}),g="",yield{type:"tool_use_start",data:{id:G.id,name:G.name,input:void 0}}):G.type==="text"&&y.push({type:"text",text:""});break}case"content_block_delta":{const G=q.delta;if(G.type==="text_delta"&&G.text){const J=y[B];J&&(J.text=(J.text||"")+G.text),yield{type:"text_delta",data:G.text}}else G.type==="input_json_delta"&&G.partial_json&&(g+=G.partial_json);break}case"content_block_stop":{const G=y[B];if(G?.type==="tool_use"&&g){try{G.input=JSON.parse(g)}catch{G.input={}}g=""}break}case"message_start":{const G=q.message;G?.usage?.input_tokens!=null&&(Oe=G.usage.input_tokens);break}case"message_delta":{const G=q.delta,J=q.usage;J?.output_tokens!=null&&(z=J.output_tokens),G.stop_reason&&(W=G.stop_reason);break}}try{A?.onGenerationEnd?.({traceId:T,turn:p,model:O,stopReason:W,inputTokens:Oe,outputTokens:z,durationMs:Date.now()-k})}catch{}if(f.push({role:"assistant",content:y}),W!=="tool_use")break;const se=[];for(const q of y){if(q.type!=="tool_use")continue;const G=q.name,J=q.input||{},h=q.id,b=I.get(G);if(!b){se.push({type:"tool_result",tool_use_id:h,content:`Unknown tool: ${G}`,is_error:!0}),yield{type:"tool_use_result",data:{id:h,name:G,result:`Unknown tool: ${G}`,isError:!0}};continue}const H=Date.now();try{const Y=await b(J);Y.sideEffect&&(yield Y.sideEffect),se.push({type:"tool_result",tool_use_id:h,content:Y.result,...Y.isError?{is_error:!0}:{}}),yield{type:"tool_use_result",data:{id:h,name:G,result:Y.result,...Y.isError?{isError:!0}:{}}};try{A?.onToolEnd?.({traceId:T,turn:p,toolName:G,toolUseId:h,isError:!!Y.isError,durationMs:Date.now()-H})}catch{}}catch(Y){const X=Y instanceof Error?Y.message:"Tool execution failed";se.push({type:"tool_result",tool_use_id:h,content:X,is_error:!0}),yield{type:"tool_use_result",data:{id:h,name:G,result:X,isError:!0}};try{A?.onToolEnd?.({traceId:T,turn:p,toolName:G,toolUseId:h,isError:!0,durationMs:Date.now()-H})}catch{}}}yield{type:"turn_complete",data:{}},f.push({role:"user",content:se})}try{A?.onChatEnd?.({traceId:T,sessionId:a,totalTurns:m,durationMs:Date.now()-R})}catch{}yield{type:"done",data:{sessionId:a||"",traceId:T}}}catch(p){try{A?.onChatEnd?.({traceId:T,sessionId:a,totalTurns:0,durationMs:Date.now()-R,error:p instanceof Error?p.message:"Unknown error"})}catch{}yield{type:"error",data:{message:wT(p)}}}}function wT(i){if(!i||!(i instanceof Error))return"Something went wrong. Please try again.";const e=i.message||"",t={overloaded_error:"The AI service is temporarily overloaded. Please try again in a moment.",rate_limit_error:"Too many requests. Please wait a moment and try again.",api_error:"The AI service encountered an error. Please try again.",authentication_error:"Authentication failed. Please check your API key configuration.",invalid_request_error:"There was a problem with the request. Please try again."},s=i;if(s.status||s.type){const n=s.error?.type||s.type||"";if(t[n])return t[n]}if(e.startsWith("{")||e.startsWith("Error: {")){try{const n=JSON.parse(e.replace(/^Error:\s*/,"")),r=n.error?.type||n.type||"";if(t[r])return t[r]}catch{}return"The AI service encountered an error. Please try again."}return e}const rs={funnel:{description:"Track conversion through sequential steps. Entities (identified by bindingKey) move through ordered steps.",structure:{funnel:{bindingKey:"Cube.dimension - identifies entities moving through funnel",timeDimension:"Cube.dimension - time field for ordering events",steps:[{name:"string - human readable step name",filter:{member:"Cube.dimension",operator:"equals | notEquals | contains | ...",values:["array of filter values"]},timeToConvert:'optional - max time window e.g. "7 days"'}],dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}},flow:{description:"Analyze paths users take before/after a specific event. Shows event sequences.",structure:{flow:{bindingKey:"Cube.dimension - identifies entities",timeDimension:"Cube.dimension - time field for ordering",eventDimension:"Cube.dimension - the event type field",startingStep:{filter:{member:"Cube.dimension",operator:"equals",values:["event value"]}},stepsBefore:"number - how many steps to show before starting step",stepsAfter:"number - how many steps to show after starting step",dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}},retention:{description:"Measure how many users return over time periods after initial activity.",structure:{retention:{bindingKey:"Cube.dimension - identifies entities",timeDimension:"Cube.dimension - time field for cohort assignment",granularity:"day | week | month - period size",periods:"number - how many periods to analyze",dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}}};function vT(i,e){const t=[];for(let s=0;s<=e.length;s++)t[s]=[s];for(let s=0;s<=i.length;s++)t[0][s]=s;for(let s=1;s<=e.length;s++)for(let n=1;n<=i.length;n++)e.charAt(s-1)===i.charAt(n-1)?t[s][n]=t[s-1][n-1]:t[s][n]=Math.min(t[s-1][n-1]+1,t[s][n-1]+1,t[s-1][n]+1);return t[e.length][i.length]}function x(i,e){const t=i.toLowerCase().trim(),s=e.toLowerCase().trim();if(t===s)return 1;if(s.includes(t))return .9;const n=s.split(/[\s_-]+/);for(const A of n){if(A===t)return .85;if(A.startsWith(t))return .75}const r=vT(t,s),E=Math.max(t.length,s.length),a=1-r/E;return a>.5?a*.7:0}function he(i,e){let t=0;for(const s of e){const n=x(i,s);n>t&&(t=n)}return t}function $T(i){const e=new Set(["a","an","the","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","must","can","and","or","but","if","then","else","when","where","why","how","what","which","who","this","that","these","those","i","me","my","we","our","you","your","he","she","it","they","them","their","in","on","at","to","for","of","with","by","from","up","down","out","over","under","about","into","through","during","before","after","above","below","between","show","me","get","find","list","give","tell","display","want","need","see","know"]);return i.toLowerCase().replace(/[^\w\s]/g," ").split(/\s+/).filter(t=>t.length>2&&!e.has(t))}function VT(i,e){let t=0;const s=[],n=new Map,r=new Map;for(const T of e){const R=x(T,i.name);R>.5&&(t+=R*2,s.includes("name")||s.push("name"));const l=x(T,i.title);if(l>.5&&(t+=l*1.5,s.includes("title")||s.push("title")),i.description){const S=x(T,i.description);S>.3&&(t+=S,s.includes("description")||s.push("description"))}if(i.exampleQuestions)for(const S of i.exampleQuestions){const N=x(T,S);N>.3&&(t+=N*1.5,s.includes("exampleQuestions")||s.push("exampleQuestions"))}for(const S of i.measures){let N=0;const I=S.name.split(".").pop()||S.name;if(N=Math.max(N,x(T,I)),N=Math.max(N,x(T,S.title)),S.description&&(N=Math.max(N,x(T,S.description)*.8)),S.synonyms&&(N=Math.max(N,he(T,S.synonyms))),N>.4){t+=N,s.includes("measures")||s.push("measures");const u=n.get(S.name)||0;n.set(S.name,Math.max(u,N))}}for(const S of i.dimensions){let N=0;const I=S.name.split(".").pop()||S.name;if(N=Math.max(N,x(T,I)),N=Math.max(N,x(T,S.title)),S.description&&(N=Math.max(N,x(T,S.description)*.8)),S.synonyms&&(N=Math.max(N,he(T,S.synonyms))),N>.4){t+=N,s.includes("dimensions")||s.push("dimensions");const u=r.get(S.name)||0;r.set(S.name,Math.max(u,N))}}}const E=Math.min(1,t/(e.length*2)),a=Array.from(n.entries()).sort((T,R)=>R[1]-T[1]).slice(0,5).map(([T])=>T),A=Array.from(r.entries()).sort((T,R)=>R[1]-T[1]).slice(0,5).map(([T])=>T);return{score:E,matchedOn:s,suggestedMeasures:a,suggestedDimensions:A}}function Nt(i){const e=!!i.meta?.eventStream,t=i.dimensions.some(r=>r.type==="time"),s=i.dimensions.some(r=>r.name.toLowerCase().includes("id")||r.type==="number"||i.meta?.eventStream?.bindingKey&&r.name===i.meta.eventStream.bindingKey),n=e||t&&s;return{query:!0,funnel:n,flow:n,retention:n}}function Es(i){const e=Nt(i);if(!e.funnel&&!e.flow&&!e.retention)return;const t=[];if(i.meta?.eventStream?.bindingKey){const r=i.dimensions.find(E=>E.name===i.meta?.eventStream?.bindingKey);t.push({dimension:i.meta.eventStream.bindingKey,description:r?.description||"Configured binding key"})}for(const r of i.dimensions)(r.name.split(".").pop()?.toLowerCase()||"").includes("id")&&!t.some(a=>a.dimension===r.name)&&t.push({dimension:r.name,description:r.description||"Potential entity identifier"});const s=[];if(i.meta?.eventStream?.timeDimension){const r=i.dimensions.find(E=>E.name===i.meta?.eventStream?.timeDimension);s.push({dimension:i.meta.eventStream.timeDimension,description:r?.description||"Configured time dimension"})}for(const r of i.dimensions)r.type==="time"&&!s.some(E=>E.dimension===r.name)&&s.push({dimension:r.name,description:r.description});const n=[];for(const r of i.dimensions){const E=r.name.split(".").pop()?.toLowerCase()||"";r.type==="string"&&(E.includes("type")||E.includes("event")||E.includes("status")||E.includes("state")||E.includes("action"))&&n.push({dimension:r.name,description:r.description||"Potential event type dimension"})}return{candidateBindingKeys:t,candidateTimeDimensions:s,candidateEventDimensions:n}}function as(i,e){const t=[];if(!e)return t;if(e.candidateBindingKeys.length>1&&t.push("Choose bindingKey based on what entity to track through the analysis"),e.candidateEventDimensions.length>0){const s=e.candidateEventDimensions[0].dimension;t.push(`Query ${s} dimension to discover available values for funnel steps`)}return t.push("Use /mcp/load with a standard query to discover dimension values before building analysis queries"),t}function dt(i,e={}){const{topic:t,intent:s,limit:n=10,minScore:r=.1}=e,E=[t,s].filter(Boolean).join(" ");if(!E.trim())return i.slice(0,n).map(T=>{const R=Nt(T),l=Es(T),S=as(T,l),N=R.funnel||R.flow||R.retention;return{cube:T.name,title:T.title,description:T.description,relevanceScore:1,matchedOn:[],suggestedMeasures:T.measures.slice(0,5).map(I=>I.name),suggestedDimensions:T.dimensions.slice(0,5).map(I=>I.name),capabilities:R,analysisConfig:l,hints:S.length>0?S:void 0,querySchemas:N?rs:void 0}});const a=$T(E);if(a.length===0)return[];const A=[];for(const T of i){const{score:R,matchedOn:l,suggestedMeasures:S,suggestedDimensions:N}=VT(T,a);if(R>=r){const I=Nt(T),u=Es(T),c=as(T,u),O=I.funnel||I.flow||I.retention;A.push({cube:T.name,title:T.title,description:T.description,relevanceScore:R,matchedOn:l,suggestedMeasures:S,suggestedDimensions:N,capabilities:I,analysisConfig:u,hints:c.length>0?c:void 0,querySchemas:O?rs:void 0})}}return A.sort((T,R)=>R.relevanceScore-T.relevanceScore).slice(0,n)}function be(i,e,t){let s=null;for(const n of i){if(!t||t==="measure")for(const r of n.measures){const E=r.name.split(".").pop()||r.name;let a=x(e,E);a=Math.max(a,x(e,r.title)),r.synonyms&&(a=Math.max(a,he(e,r.synonyms))),a>.5&&(!s||a>s.score)&&(s={field:r.name,cube:n.name,score:a,type:"measure"})}if(!t||t==="dimension")for(const r of n.dimensions){const E=r.name.split(".").pop()||r.name;let a=x(e,E);a=Math.max(a,x(e,r.title)),r.synonyms&&(a=Math.max(a,he(e,r.synonyms))),a>.5&&(!s||a>s.score)&&(s={field:r.name,cube:n.name,score:a,type:"dimension"})}}return s}function WT(){const i=new Date,e=i.toISOString().split("T")[0],t=a=>a.toISOString().split("T")[0],s=a=>new Date(a.getFullYear(),a.getMonth(),1),n=a=>new Date(a.getFullYear(),0,1),r=a=>{const A=Math.floor(a.getMonth()/3);return new Date(a.getFullYear(),A*3,1)},E=a=>{const A=a.getDay(),T=a.getDate()-A+(A===0?-6:1);return new Date(a.getFullYear(),a.getMonth(),T)};return[{pattern:/\btoday\b/i,getDateRange:()=>[e,e],granularity:"day"},{pattern:/\byesterday\b/i,getDateRange:()=>{const a=new Date(i);a.setDate(a.getDate()-1);const A=t(a);return[A,A]},granularity:"day"},{pattern:/\bthis week\b/i,getDateRange:()=>[t(E(i)),e],granularity:"day"},{pattern:/\blast week\b/i,getDateRange:()=>{const a=new Date(E(i));a.setDate(a.getDate()-7);const A=new Date(a);return A.setDate(A.getDate()+6),[t(a),t(A)]},granularity:"day"},{pattern:/\bthis month\b/i,getDateRange:()=>[t(s(i)),e],granularity:"day"},{pattern:/\blast month\b/i,getDateRange:()=>{const a=new Date(i.getFullYear(),i.getMonth()-1,1),A=new Date(i.getFullYear(),i.getMonth(),0);return[t(a),t(A)]},granularity:"day"},{pattern:/\bthis quarter\b/i,getDateRange:()=>[t(r(i)),e],granularity:"month"},{pattern:/\blast quarter\b/i,getDateRange:()=>{const a=new Date(r(i));a.setMonth(a.getMonth()-3);const A=new Date(r(i));return A.setDate(A.getDate()-1),[t(a),t(A)]},granularity:"month"},{pattern:/\bthis year\b/i,getDateRange:()=>[t(n(i)),e],granularity:"month"},{pattern:/\blast year\b/i,getDateRange:()=>{const a=new Date(i.getFullYear()-1,0,1),A=new Date(i.getFullYear()-1,11,31);return[t(a),t(A)]},granularity:"month"},{pattern:/\blast (\d+) days?\b/i,getDateRange:()=>{const a=new Date(i);return a.setDate(a.getDate()-7),[t(a),e]},granularity:"day"},{pattern:/\blast (\d+) weeks?\b/i,getDateRange:()=>{const a=new Date(i);return a.setDate(a.getDate()-28),[t(a),e]},granularity:"week"},{pattern:/\blast (\d+) months?\b/i,getDateRange:()=>{const a=new Date(i);return a.setMonth(a.getMonth()-3),[t(a),e]},granularity:"month"},{pattern:/\bq([1-4])\b/i,getDateRange:()=>[t(new Date(i.getFullYear(),0,1)),t(new Date(i.getFullYear(),2,31))],granularity:"month"}]}const Et={funnel:/\b(funnel|conversion|drop.?off|steps?|journey|pipeline|stages?)\b/i,flow:/\b(flows?|paths?|sequence|before|after|next|previous|user.?journey)\b/i,retention:/\b(retention|cohort|return|churn|comeback|retained|day.?\d+)\b/i};function xT(i){const e=i.toLowerCase();return Et.funnel.test(e)?"funnel":Et.flow.test(e)?"flow":Et.retention.test(e)?"retention":"query"}function os(i,e){const t=e||"the relevant cube";switch(i){case"funnel":return[`Use /mcp/discover to get ${t} funnel configuration and schema`,"Query the event dimension to discover available event types for funnel steps","Build funnel query with discovered values using the schema from discover"];case"flow":return[`Use /mcp/discover to get ${t} flow configuration and schema`,"Query the event dimension to discover available event types","Build flow query specifying the starting event and steps before/after"];case"retention":return[`Use /mcp/discover to get ${t} retention configuration and schema`,"Build retention query specifying granularity (day/week/month) and number of periods"]}}function qT(i){const e=WT(),t=i.toLowerCase();for(const s of e){const n=t.match(s.pattern);if(n){if(n[1]&&/^\d+$/.test(n[1])){const r=parseInt(n[1],10),E=new Date,a=E.toISOString().split("T")[0],A=T=>T.toISOString().split("T")[0];if(/days?/.test(t)){const T=new Date(E);return T.setDate(T.getDate()-r),{dateRange:[A(T),a],granularity:"day"}}if(/weeks?/.test(t)){const T=new Date(E);return T.setDate(T.getDate()-r*7),{dateRange:[A(T),a],granularity:r<=4?"day":"week"}}if(/months?/.test(t)){const T=new Date(E);return T.setMonth(T.getMonth()-r),{dateRange:[A(T),a],granularity:r<=3?"day":"month"}}}if(/^q[1-4]$/i.test(n[0])){const r=parseInt(n[1],10),a=new Date().getFullYear(),A=(r-1)*3,T=new Date(a,A,1),R=new Date(a,A+3,0),l=S=>S.toISOString().split("T")[0];return{dateRange:[l(T),l(R)],granularity:"month"}}return{dateRange:s.getDateRange(),granularity:s.granularity}}}return null}function XT(i){const e=i.toLowerCase(),t=[{pattern:/\b(total|sum|combined)\b/i,type:"sum"},{pattern:/\b(count|number of|how many)\b/i,type:"count"},{pattern:/\b(average|avg|mean)\b/i,type:"avg"},{pattern:/\b(maximum|max|highest|top)\b/i,type:"max"},{pattern:/\b(minimum|min|lowest|bottom)\b/i,type:"min"}];for(const{pattern:s,type:n}of t)if(s.test(e))return{type:n,confidence:.8};return null}function KT(i){const e=i.toLowerCase(),t=[],s=/\bby\s+(\w+(?:\s+\w+)?)/gi;let n;for(;(n=s.exec(e))!==null;)t.push(n[1].trim());const r=/\bper\s+(\w+)/gi;for(;(n=r.exec(e))!==null;)t.push(n[1].trim());const E=/\bfor each\s+(\w+)/gi;for(;(n=E.exec(e))!==null;)t.push(n[1].trim());return t}function kT(i,e,t){const s=[],n=[],r={},E=xT(e);let a;if(t){const c=i.find(O=>O.name===t);c?(a=[c],s.push(`Using specified cube: ${t}`)):(n.push(`Specified cube '${t}' not found`),a=[])}else a=dt(i,{intent:e,limit:3}).map(O=>i.find(C=>C.name===O.cube)).filter(O=>O!==void 0),a.length>0&&s.push(`Identified relevant cubes: ${a.map(O=>O.name).join(", ")}`);if(a.length===0){const c=E!=="query",O=c?os(E,void 0):void 0;return{query:{},confidence:c?.7:0,reasoning:c?[`Detected ${E} intent from natural language`]:["Could not identify relevant cubes for this query"],warnings:n,analysisMode:E,nextSteps:O}}const A=a[0];let T=.5;const R=XT(e);R&&(s.push(`Detected ${R.type} aggregation intent`),T+=.1);const l=[],S=e.toLowerCase();for(const c of A.measures){const C=[(c.name.split(".").pop()||c.name).toLowerCase(),c.title.toLowerCase(),...(c.synonyms||[]).map(D=>D.toLowerCase())];for(const D of C)if(S.includes(D)){l.push(c.name),s.push(`Matched measure '${c.name}' via keyword '${D}'`),T+=.15;break}}if(l.length===0&&R){const c=A.measures.filter(O=>O.type===R.type);if(c.length>0)l.push(c[0].name),s.push(`Suggested ${c[0].name} based on ${R.type} intent`);else if(R.type==="count"){const O=A.measures.find(C=>C.type==="count"||C.type==="countDistinct");O&&(l.push(O.name),s.push(`Suggested ${O.name} for counting`))}}l.length===0&&A.measures.length>0&&(l.push(A.measures[0].name),s.push(`Using default measure: ${A.measures[0].name}`),n.push("Could not determine specific measure from query, using default")),r.measures=l;const N=KT(e),I=[];for(const c of N){const O=be(a,c,"dimension");O&&(I.push(O.field),s.push(`Matched dimension '${O.field}' from grouping keyword '${c}'`),T+=.1)}for(const c of a)for(const O of c.dimensions){const D=[(O.name.split(".").pop()||O.name).toLowerCase(),O.title.toLowerCase(),...(O.synonyms||[]).map(f=>f.toLowerCase())];for(const f of D)if(S.includes(f)&&!I.includes(O.name)&&(S.includes(`by ${f}`)||S.includes(`per ${f}`))){I.push(O.name),s.push(`Matched dimension '${O.name}' as grouping`),T+=.1;break}}I.length>0&&(r.dimensions=I);const u=qT(e);if(u){const c=A.dimensions.find(O=>O.type==="time");if(c){const O={dimension:c.name,dateRange:u.dateRange};u.granularity&&(O.granularity=u.granularity),r.timeDimensions=[O],s.push(`Applied time filter: ${u.dateRange[0]} to ${u.dateRange[1]}`),T+=.15}else n.push("Time expression found but no time dimension in cube")}if(T=Math.min(1,T),E!=="query"){const c=a.length>0?a[0].name:void 0;return{query:{},confidence:.7,reasoning:[`Detected ${E} intent from natural language`,...c?[`Found relevant cube: ${c}`]:[]],warnings:n.length>0?n:void 0,analysisMode:E,nextSteps:os(E,c)}}return{query:r,confidence:T,reasoning:s,warnings:n.length>0?n:void 0,analysisMode:"query"}}function JT(i,e){const t=[];for(let s=0;s<=e.length;s++)t[s]=[s];for(let s=0;s<=i.length;s++)t[0][s]=s;for(let s=1;s<=e.length;s++)for(let n=1;n<=i.length;n++)e.charAt(s-1)===i.charAt(n-1)?t[s][n]=t[s-1][n-1]:t[s][n]=Math.min(t[s-1][n-1]+1,t[s][n-1]+1,t[s-1][n]+1);return t[e.length][i.length]}function Ae(i,e){let t=null;for(const s of e){const n=JT(i.toLowerCase(),s.toLowerCase());n<=3&&(!t||n<t.distance)&&(t={field:s,distance:n})}return t}function QT(i,e,t,s){const n=i.split(".");if(n.length!==2){t.push({type:"syntax_error",message:`Invalid measure format: '${i}'. Expected 'CubeName.measureName'`,field:i});return}const[r,E]=n,a=e.find(T=>T.name===r);if(!a){const T=e.map(l=>l.name),R=Ae(r,T);R?(t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Did you mean '${R.field}'?`,correctedValue:`${R.field}.${E}`}),s.set(i,`${R.field}.${E}`)):t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Available cubes: ${T.join(", ")}`});return}if(!a.measures.some(T=>T.name===i)){const T=be(e,E,"measure");if(T&&T.cube===r)t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${T.field}'?`,correctedValue:T.field}),s.set(i,T.field);else{const R=a.measures.map(S=>S.name.split(".").pop()),l=Ae(E,R);if(l){const S=`${r}.${l.field}`;t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${l.field}'?`,correctedValue:S}),s.set(i,S)}else t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Available measures: ${R.slice(0,5).join(", ")}${R.length>5?"...":""}`})}}}function te(i,e,t,s){const n=i.split(".");if(n.length!==2){t.push({type:"syntax_error",message:`Invalid dimension format: '${i}'. Expected 'CubeName.dimensionName'`,field:i});return}const[r,E]=n,a=e.find(T=>T.name===r);if(!a){const T=e.map(l=>l.name),R=Ae(r,T);R?(t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Did you mean '${R.field}'?`,correctedValue:`${R.field}.${E}`}),s.set(i,`${R.field}.${E}`)):t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Available cubes: ${T.join(", ")}`});return}if(!a.dimensions.some(T=>T.name===i)){const T=be(e,E,"dimension");if(T&&T.cube===r)t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${T.field}'?`,correctedValue:T.field}),s.set(i,T.field);else{const R=a.dimensions.map(S=>S.name.split(".").pop()),l=Ae(E,R);if(l){const S=`${r}.${l.field}`;t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${l.field}'?`,correctedValue:S}),s.set(i,S)}else t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Available dimensions: ${R.slice(0,5).join(", ")}${R.length>5?"...":""}`})}}}function Pe(i,e,t,s){for(const n of i){if("and"in n&&Array.isArray(n.and)){Pe(n.and,e,t,s);continue}if("or"in n&&Array.isArray(n.or)){Pe(n.or,e,t,s);continue}if("member"in n){const r=n.member,E=r.split(".");if(E.length!==2){t.push({type:"invalid_filter",message:`Invalid filter member format: '${r}'`,field:r});continue}const[a,A]=E,T=e.find(S=>S.name===a);if(!T){const S=e.map(I=>I.name),N=Ae(a,S);N&&s.set(r,`${N.field}.${A}`),t.push({type:"cube_not_found",message:`Cube '${a}' not found in filter`,field:r,suggestion:N?`Did you mean '${N.field}'?`:void 0,correctedValue:N?`${N.field}.${A}`:void 0});continue}const R=T.dimensions.some(S=>S.name===r),l=T.measures.some(S=>S.name===r);if(!R&&!l){const S=[...T.dimensions.map(I=>I.name.split(".").pop()),...T.measures.map(I=>I.name.split(".").pop())],N=Ae(A,S);if(N){const I=`${a}.${N.field}`;s.set(r,I),t.push({type:"invalid_filter",message:`Filter field '${A}' not found on cube '${a}'`,field:r,suggestion:`Did you mean '${N.field}'?`,correctedValue:I})}else t.push({type:"invalid_filter",message:`Filter field '${A}' not found on cube '${a}'`,field:r})}}}}function jT(i,e,t,s,n){const r=i.funnel;if(r)if(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"funnel.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"funnel.timeDimension is required"}),!r.steps||!Array.isArray(r.steps))t.push({type:"syntax_error",message:"funnel.steps array is required"});else if(r.steps.length<2)t.push({type:"syntax_error",message:"funnel requires at least 2 steps"});else for(let E=0;E<r.steps.length;E++){const a=r.steps[E];a.name||s.push({type:"best_practice",message:`Step ${E+1} is missing a name`,suggestion:"Add descriptive names to funnel steps"}),a.filter&&"member"in a.filter&&Pe([a.filter],e,t,n)}}function ZT(i,e,t,s,n){const r=i.flow;r&&(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"flow.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"flow.timeDimension is required"}),r.eventDimension?typeof r.eventDimension=="string"&&te(r.eventDimension,e,t,n):t.push({type:"syntax_error",message:"flow.eventDimension is required"}),r.stepsBefore===void 0&&r.stepsAfter===void 0&&s.push({type:"best_practice",message:"Neither stepsBefore nor stepsAfter specified",suggestion:"Set stepsBefore and/or stepsAfter to see event sequences"}))}function zT(i,e,t,s,n){const r=i.retention;r&&(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"retention.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"retention.timeDimension is required"}),r.granularity||s.push({type:"best_practice",message:"retention.granularity not specified",suggestion:'Specify granularity: "day", "week", or "month"'}),r.periods||s.push({type:"best_practice",message:"retention.periods not specified",suggestion:"Specify number of periods to analyze"}))}function eA(i,e){const t=[],s=[],n=new Map;if(i.funnel)return jT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.flow)return ZT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.retention)return zT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.measures)for(const E of i.measures)QT(E,e,t,n);if(i.dimensions)for(const E of i.dimensions)te(E,e,t,n);if(i.timeDimensions)for(const E of i.timeDimensions){te(E.dimension,e,t,n);const[a]=E.dimension.split("."),A=e.find(T=>T.name===a);if(A){const T=A.dimensions.find(R=>R.name===E.dimension);T&&T.type!=="time"&&s.push({type:"best_practice",message:`Dimension '${E.dimension}' is not a time type (it's '${T.type}')`,field:E.dimension,suggestion:'Use a dimension with type "time" for timeDimensions'})}}i.filters&&Pe(i.filters,e,t,n),!i.measures?.length&&!i.dimensions?.length&&t.push({type:"syntax_error",message:"Query must have at least one measure or dimension"}),i.measures&&i.measures.length>10&&s.push({type:"performance",message:`Query has ${i.measures.length} measures, which may impact performance`,suggestion:"Consider splitting into multiple queries"}),i.dimensions&&i.dimensions.length>5&&s.push({type:"performance",message:`Query has ${i.dimensions.length} dimensions, which may produce many rows`,suggestion:"Consider adding filters or reducing dimensions"});let r;if(n.size>0){const E=JSON.parse(JSON.stringify(i));E.measures&&(E.measures=E.measures.map(a=>n.get(a)||a)),E.dimensions&&(E.dimensions=E.dimensions.map(a=>n.get(a)||a)),E.timeDimensions&&(E.timeDimensions=E.timeDimensions.map(a=>({...a,dimension:n.get(a.dimension)||a.dimension}))),r=E}return{isValid:t.length===0,errors:t,warnings:s,correctedQuery:n.size>0?r:void 0}}function tA(i){return new ge({drizzle:i.drizzle,schema:i.schema})}exports.BaseDatabaseExecutor=Ie;exports.CTEBuilder=Cs;exports.CalculatedMeasureResolver=ne;exports.ComparisonQueryBuilder=ms;exports.DrizzlePlanBuilder=Ps;exports.DrizzleSqlBuilder=cs;exports.DuckDBExecutor=Ss;exports.EXPLAIN_ANALYSIS_PROMPT=Js;exports.FlowQueryBuilder=_s;exports.FunnelQueryBuilder=Ls;exports.IdentityOptimiser=hs;exports.JoinPathResolver=Se;exports.LogicalPlanBuilder=fs;exports.LogicalPlanner=ds;exports.MemoryCacheProvider=Qo;exports.MySQLExecutor=It;exports.OptimiserPipeline=vn;exports.PostgresExecutor=As;exports.QueryExecutor=Ms;exports.RetentionQueryBuilder=Ds;exports.SQLiteExecutor=ls;exports.STEP0_VALIDATION_PROMPT=qs;exports.STEP1_SYSTEM_PROMPT=Ks;exports.STEP2_SYSTEM_PROMPT=ks;exports.SYSTEM_PROMPT_TEMPLATE=Xs;exports.SemanticLayerCompiler=ge;exports.aiValidateQuery=eA;exports.buildAgentSystemPrompt=Qs;exports.buildExplainAnalysisPrompt=tT;exports.buildStep0Prompt=jo;exports.buildStep1Prompt=zo;exports.buildStep2Prompt=eT;exports.buildSystemPrompt=Zo;exports.createDatabaseExecutor=Tt;exports.createDrizzleSemanticLayer=tA;exports.createDuckDBExecutor=Ns;exports.createMultiCubeContext=_n;exports.createMySQLExecutor=Rs;exports.createPostgresExecutor=at;exports.createSQLiteExecutor=ot;exports.createToolExecutor=Zs;exports.defineCube=pn;exports.discoverCubes=dt;exports.findBestFieldMatch=be;exports.fnv1aHash=lt;exports.formatCubeSchemaForExplain=sT;exports.formatExistingIndexes=nT;exports.generateCacheKey=us;exports.getCubeInvalidationPattern=yn;exports.getJoinType=_e;exports.getToolDefinitions=js;exports.handleAgentChat=YT;exports.normalizeQuery=Os;exports.resolveCubeReference=K;exports.resolveSqlExpression=F;exports.suggestQuery=kT;
|
|
758
|
+
`)}`,isError:!0};const T={portlets:a.map(N=>{const I=N.chartType,u=I==="markdown",c=u?"query":N.analysisType||"query",O=c==="funnel"?"funnel":c==="flow"?"flow":c==="retention"?"retention":"query",C=N.query||"{}";let D;try{D=JSON.parse(C)}catch{D={}}const f={version:1,analysisType:O,activeView:"chart",charts:{[O]:{chartType:I,chartConfig:N.chartConfig||{},displayConfig:N.displayConfig||{}}},query:u?{}:D};return{id:N.id,title:N.title,analysisConfig:f,dashboardFilterMapping:N.dashboardFilterMapping,w:N.w,h:N.h,x:N.x,y:N.y}}),filters:E.filters,colorPalette:E.colorPalette},R=E.title,l=T.portlets.length,S=T.filters?.length||0;return{result:`Dashboard "${R}" created with ${l} portlets and ${S} filters.`,sideEffect:{type:"dashboard_saved",data:{title:R,description:E.description,dashboardConfig:T}}}}catch(a){return{result:`Failed to save dashboard: ${a instanceof Error?a.message:"Unknown error"}`,isError:!0}}}),s}async function*YT(i){const{message:e,history:t,semanticLayer:s,securityContext:n,agentConfig:r,apiKey:E}=i,a=i.sessionId||crypto.randomUUID(),A=r.observability,T=crypto.randomUUID(),R=Date.now();let l;try{const p=await import("@anthropic-ai/sdk");l=p.default||p.Anthropic||p}catch{yield{type:"error",data:{message:"@anthropic-ai/sdk is required. Install it with: npm install @anthropic-ai/sdk"}};return}const S=new l({apiKey:E}),N=js(),I=Zs({semanticLayer:s,securityContext:n}),u=s.getMetadata();let c=Qs(u);i.systemContext&&(c+=`
|
|
759
|
+
|
|
760
|
+
## User Context
|
|
761
|
+
|
|
762
|
+
${i.systemContext}`);const O=r.model||"claude-sonnet-4-6",C=r.maxTurns||25,D=r.maxTokens||4096;try{A?.onChatStart?.({traceId:T,sessionId:a,message:e,model:O,historyLength:t?.length??0})}catch{}const f=[];if(t&&t.length>0){for(const p of t)if(p.role==="user")f.push({role:"user",content:p.content});else if(p.role==="assistant"){const M=[];if(p.content&&M.push({type:"text",text:p.content}),p.toolCalls&&p.toolCalls.length>0){for(const y of p.toolCalls)M.push({type:"tool_use",id:y.id,name:y.name,input:y.input||{}});f.push({role:"assistant",content:M}),f.push({role:"user",content:p.toolCalls.map(y=>({type:"tool_result",tool_use_id:y.id,content:typeof y.result=="string"?y.result:JSON.stringify(y.result??""),...y.status==="error"?{is_error:!0}:{}}))})}else M.length>0&&f.push({role:"assistant",content:p.content})}}f.push({role:"user",content:e});let m=0;try{for(let p=0;p<C;p++){m=p+1;const M=await S.messages.create({model:O,max_tokens:D,system:c,tools:N,messages:f,stream:!0}),y=[];let B=-1,g="",W="",Oe,z;const k=Date.now();for await(const q of M)switch(q.type){case"content_block_start":{B++;const G=q.content_block;G.type==="tool_use"?(y.push({type:"tool_use",id:G.id,name:G.name,input:{}}),g="",yield{type:"tool_use_start",data:{id:G.id,name:G.name,input:void 0}}):G.type==="text"&&y.push({type:"text",text:""});break}case"content_block_delta":{const G=q.delta;if(G.type==="text_delta"&&G.text){const J=y[B];J&&(J.text=(J.text||"")+G.text),yield{type:"text_delta",data:G.text}}else G.type==="input_json_delta"&&G.partial_json&&(g+=G.partial_json);break}case"content_block_stop":{const G=y[B];if(G?.type==="tool_use"&&g){try{G.input=JSON.parse(g)}catch{G.input={}}g=""}break}case"message_start":{const G=q.message;G?.usage?.input_tokens!=null&&(Oe=G.usage.input_tokens);break}case"message_delta":{const G=q.delta,J=q.usage;J?.output_tokens!=null&&(z=J.output_tokens),G.stop_reason&&(W=G.stop_reason);break}}try{A?.onGenerationEnd?.({traceId:T,turn:p,model:O,stopReason:W,inputTokens:Oe,outputTokens:z,durationMs:Date.now()-k})}catch{}if(f.push({role:"assistant",content:y}),W!=="tool_use")break;const se=[];for(const q of y){if(q.type!=="tool_use")continue;const G=q.name,J=q.input||{},h=q.id,b=I.get(G);if(!b){se.push({type:"tool_result",tool_use_id:h,content:`Unknown tool: ${G}`,is_error:!0}),yield{type:"tool_use_result",data:{id:h,name:G,result:`Unknown tool: ${G}`,isError:!0}};continue}const H=Date.now();try{const Y=await b(J);Y.sideEffect&&(yield Y.sideEffect),se.push({type:"tool_result",tool_use_id:h,content:Y.result,...Y.isError?{is_error:!0}:{}}),yield{type:"tool_use_result",data:{id:h,name:G,result:Y.result,...Y.isError?{isError:!0}:{}}};try{A?.onToolEnd?.({traceId:T,turn:p,toolName:G,toolUseId:h,isError:!!Y.isError,durationMs:Date.now()-H})}catch{}}catch(Y){const X=Y instanceof Error?Y.message:"Tool execution failed";se.push({type:"tool_result",tool_use_id:h,content:X,is_error:!0}),yield{type:"tool_use_result",data:{id:h,name:G,result:X,isError:!0}};try{A?.onToolEnd?.({traceId:T,turn:p,toolName:G,toolUseId:h,isError:!0,durationMs:Date.now()-H})}catch{}}}yield{type:"turn_complete",data:{}},f.push({role:"user",content:se})}try{A?.onChatEnd?.({traceId:T,sessionId:a,totalTurns:m,durationMs:Date.now()-R})}catch{}yield{type:"done",data:{sessionId:a||"",traceId:T}}}catch(p){try{A?.onChatEnd?.({traceId:T,sessionId:a,totalTurns:0,durationMs:Date.now()-R,error:p instanceof Error?p.message:"Unknown error"})}catch{}yield{type:"error",data:{message:wT(p)}}}}function wT(i){if(!i||!(i instanceof Error))return"Something went wrong. Please try again.";const e=i.message||"",t={overloaded_error:"The AI service is temporarily overloaded. Please try again in a moment.",rate_limit_error:"Too many requests. Please wait a moment and try again.",api_error:"The AI service encountered an error. Please try again.",authentication_error:"Authentication failed. Please check your API key configuration.",invalid_request_error:"There was a problem with the request. Please try again."},s=i;if(s.status||s.type){const n=s.error?.type||s.type||"";if(t[n])return t[n]}if(e.startsWith("{")||e.startsWith("Error: {")){try{const n=JSON.parse(e.replace(/^Error:\s*/,"")),r=n.error?.type||n.type||"";if(t[r])return t[r]}catch{}return"The AI service encountered an error. Please try again."}return e}const rs={funnel:{description:"Track conversion through sequential steps. Entities (identified by bindingKey) move through ordered steps.",structure:{funnel:{bindingKey:"Cube.dimension - identifies entities moving through funnel",timeDimension:"Cube.dimension - time field for ordering events",steps:[{name:"string - human readable step name",filter:{member:"Cube.dimension",operator:"equals | notEquals | contains | ...",values:["array of filter values"]},timeToConvert:'optional - max time window e.g. "7 days"'}],dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}},flow:{description:"Analyze paths users take before/after a specific event. Shows event sequences.",structure:{flow:{bindingKey:"Cube.dimension - identifies entities",timeDimension:"Cube.dimension - time field for ordering",eventDimension:"Cube.dimension - the event type field",startingStep:{filter:{member:"Cube.dimension",operator:"equals",values:["event value"]}},stepsBefore:"number - how many steps to show before starting step",stepsAfter:"number - how many steps to show after starting step",dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}},retention:{description:"Measure how many users return over time periods after initial activity.",structure:{retention:{bindingKey:"Cube.dimension - identifies entities",timeDimension:"Cube.dimension - time field for cohort assignment",granularity:"day | week | month - period size",periods:"number - how many periods to analyze",dateRange:'[start, end] array OR string like "last 7 days", "last 3 months", "this quarter"'}}}};function vT(i,e){const t=[];for(let s=0;s<=e.length;s++)t[s]=[s];for(let s=0;s<=i.length;s++)t[0][s]=s;for(let s=1;s<=e.length;s++)for(let n=1;n<=i.length;n++)e.charAt(s-1)===i.charAt(n-1)?t[s][n]=t[s-1][n-1]:t[s][n]=Math.min(t[s-1][n-1]+1,t[s][n-1]+1,t[s-1][n]+1);return t[e.length][i.length]}function x(i,e){const t=i.toLowerCase().trim(),s=e.toLowerCase().trim();if(t===s)return 1;if(s.includes(t))return .9;const n=s.split(/[\s_-]+/);for(const A of n){if(A===t)return .85;if(A.startsWith(t))return .75}const r=vT(t,s),E=Math.max(t.length,s.length),a=1-r/E;return a>.5?a*.7:0}function he(i,e){let t=0;for(const s of e){const n=x(i,s);n>t&&(t=n)}return t}function $T(i){const e=new Set(["a","an","the","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","must","can","and","or","but","if","then","else","when","where","why","how","what","which","who","this","that","these","those","i","me","my","we","our","you","your","he","she","it","they","them","their","in","on","at","to","for","of","with","by","from","up","down","out","over","under","about","into","through","during","before","after","above","below","between","show","me","get","find","list","give","tell","display","want","need","see","know"]);return i.toLowerCase().replace(/[^\w\s]/g," ").split(/\s+/).filter(t=>t.length>2&&!e.has(t))}function VT(i,e){let t=0;const s=[],n=new Map,r=new Map;for(const T of e){const R=x(T,i.name);R>.5&&(t+=R*2,s.includes("name")||s.push("name"));const l=x(T,i.title);if(l>.5&&(t+=l*1.5,s.includes("title")||s.push("title")),i.description){const S=x(T,i.description);S>.3&&(t+=S,s.includes("description")||s.push("description"))}if(i.exampleQuestions)for(const S of i.exampleQuestions){const N=x(T,S);N>.3&&(t+=N*1.5,s.includes("exampleQuestions")||s.push("exampleQuestions"))}for(const S of i.measures){let N=0;const I=S.name.split(".").pop()||S.name;if(N=Math.max(N,x(T,I)),N=Math.max(N,x(T,S.title)),S.description&&(N=Math.max(N,x(T,S.description)*.8)),S.synonyms&&(N=Math.max(N,he(T,S.synonyms))),N>.4){t+=N,s.includes("measures")||s.push("measures");const u=n.get(S.name)||0;n.set(S.name,Math.max(u,N))}}for(const S of i.dimensions){let N=0;const I=S.name.split(".").pop()||S.name;if(N=Math.max(N,x(T,I)),N=Math.max(N,x(T,S.title)),S.description&&(N=Math.max(N,x(T,S.description)*.8)),S.synonyms&&(N=Math.max(N,he(T,S.synonyms))),N>.4){t+=N,s.includes("dimensions")||s.push("dimensions");const u=r.get(S.name)||0;r.set(S.name,Math.max(u,N))}}}const E=Math.min(1,t/(e.length*2)),a=Array.from(n.entries()).sort((T,R)=>R[1]-T[1]).slice(0,5).map(([T])=>T),A=Array.from(r.entries()).sort((T,R)=>R[1]-T[1]).slice(0,5).map(([T])=>T);return{score:E,matchedOn:s,suggestedMeasures:a,suggestedDimensions:A}}function Nt(i){const e=!!i.meta?.eventStream,t=i.dimensions.some(r=>r.type==="time"),s=i.dimensions.some(r=>r.name.toLowerCase().includes("id")||r.type==="number"||i.meta?.eventStream?.bindingKey&&r.name===i.meta.eventStream.bindingKey),n=e||t&&s;return{query:!0,funnel:n,flow:n,retention:n}}function Es(i){const e=Nt(i);if(!e.funnel&&!e.flow&&!e.retention)return;const t=[];if(i.meta?.eventStream?.bindingKey){const r=i.dimensions.find(E=>E.name===i.meta?.eventStream?.bindingKey);t.push({dimension:i.meta.eventStream.bindingKey,description:r?.description||"Configured binding key"})}for(const r of i.dimensions)(r.name.split(".").pop()?.toLowerCase()||"").includes("id")&&!t.some(a=>a.dimension===r.name)&&t.push({dimension:r.name,description:r.description||"Potential entity identifier"});const s=[];if(i.meta?.eventStream?.timeDimension){const r=i.dimensions.find(E=>E.name===i.meta?.eventStream?.timeDimension);s.push({dimension:i.meta.eventStream.timeDimension,description:r?.description||"Configured time dimension"})}for(const r of i.dimensions)r.type==="time"&&!s.some(E=>E.dimension===r.name)&&s.push({dimension:r.name,description:r.description});const n=[];for(const r of i.dimensions){const E=r.name.split(".").pop()?.toLowerCase()||"";r.type==="string"&&(E.includes("type")||E.includes("event")||E.includes("status")||E.includes("state")||E.includes("action"))&&n.push({dimension:r.name,description:r.description||"Potential event type dimension"})}return{candidateBindingKeys:t,candidateTimeDimensions:s,candidateEventDimensions:n}}function as(i,e){const t=[];if(!e)return t;if(e.candidateBindingKeys.length>1&&t.push("Choose bindingKey based on what entity to track through the analysis"),e.candidateEventDimensions.length>0){const s=e.candidateEventDimensions[0].dimension;t.push(`Query ${s} dimension to discover available values for funnel steps`)}return t.push("Use /mcp/load with a standard query to discover dimension values before building analysis queries"),t}function dt(i,e={}){const{topic:t,intent:s,limit:n=10,minScore:r=.1}=e,E=[t,s].filter(Boolean).join(" ");if(!E.trim())return i.slice(0,n).map(T=>{const R=Nt(T),l=Es(T),S=as(T,l),N=R.funnel||R.flow||R.retention;return{cube:T.name,title:T.title,description:T.description,relevanceScore:1,matchedOn:[],suggestedMeasures:T.measures.slice(0,5).map(I=>I.name),suggestedDimensions:T.dimensions.slice(0,5).map(I=>I.name),capabilities:R,analysisConfig:l,hints:S.length>0?S:void 0,querySchemas:N?rs:void 0}});const a=$T(E);if(a.length===0)return[];const A=[];for(const T of i){const{score:R,matchedOn:l,suggestedMeasures:S,suggestedDimensions:N}=VT(T,a);if(R>=r){const I=Nt(T),u=Es(T),c=as(T,u),O=I.funnel||I.flow||I.retention;A.push({cube:T.name,title:T.title,description:T.description,relevanceScore:R,matchedOn:l,suggestedMeasures:S,suggestedDimensions:N,capabilities:I,analysisConfig:u,hints:c.length>0?c:void 0,querySchemas:O?rs:void 0})}}return A.sort((T,R)=>R.relevanceScore-T.relevanceScore).slice(0,n)}function be(i,e,t){let s=null;for(const n of i){if(!t||t==="measure")for(const r of n.measures){const E=r.name.split(".").pop()||r.name;let a=x(e,E);a=Math.max(a,x(e,r.title)),r.synonyms&&(a=Math.max(a,he(e,r.synonyms))),a>.5&&(!s||a>s.score)&&(s={field:r.name,cube:n.name,score:a,type:"measure"})}if(!t||t==="dimension")for(const r of n.dimensions){const E=r.name.split(".").pop()||r.name;let a=x(e,E);a=Math.max(a,x(e,r.title)),r.synonyms&&(a=Math.max(a,he(e,r.synonyms))),a>.5&&(!s||a>s.score)&&(s={field:r.name,cube:n.name,score:a,type:"dimension"})}}return s}function WT(){const i=new Date,e=i.toISOString().split("T")[0],t=a=>a.toISOString().split("T")[0],s=a=>new Date(a.getFullYear(),a.getMonth(),1),n=a=>new Date(a.getFullYear(),0,1),r=a=>{const A=Math.floor(a.getMonth()/3);return new Date(a.getFullYear(),A*3,1)},E=a=>{const A=a.getDay(),T=a.getDate()-A+(A===0?-6:1);return new Date(a.getFullYear(),a.getMonth(),T)};return[{pattern:/\btoday\b/i,getDateRange:()=>[e,e],granularity:"day"},{pattern:/\byesterday\b/i,getDateRange:()=>{const a=new Date(i);a.setDate(a.getDate()-1);const A=t(a);return[A,A]},granularity:"day"},{pattern:/\bthis week\b/i,getDateRange:()=>[t(E(i)),e],granularity:"day"},{pattern:/\blast week\b/i,getDateRange:()=>{const a=new Date(E(i));a.setDate(a.getDate()-7);const A=new Date(a);return A.setDate(A.getDate()+6),[t(a),t(A)]},granularity:"day"},{pattern:/\bthis month\b/i,getDateRange:()=>[t(s(i)),e],granularity:"day"},{pattern:/\blast month\b/i,getDateRange:()=>{const a=new Date(i.getFullYear(),i.getMonth()-1,1),A=new Date(i.getFullYear(),i.getMonth(),0);return[t(a),t(A)]},granularity:"day"},{pattern:/\bthis quarter\b/i,getDateRange:()=>[t(r(i)),e],granularity:"month"},{pattern:/\blast quarter\b/i,getDateRange:()=>{const a=new Date(r(i));a.setMonth(a.getMonth()-3);const A=new Date(r(i));return A.setDate(A.getDate()-1),[t(a),t(A)]},granularity:"month"},{pattern:/\bthis year\b/i,getDateRange:()=>[t(n(i)),e],granularity:"month"},{pattern:/\blast year\b/i,getDateRange:()=>{const a=new Date(i.getFullYear()-1,0,1),A=new Date(i.getFullYear()-1,11,31);return[t(a),t(A)]},granularity:"month"},{pattern:/\blast (\d+) days?\b/i,getDateRange:()=>{const a=new Date(i);return a.setDate(a.getDate()-7),[t(a),e]},granularity:"day"},{pattern:/\blast (\d+) weeks?\b/i,getDateRange:()=>{const a=new Date(i);return a.setDate(a.getDate()-28),[t(a),e]},granularity:"week"},{pattern:/\blast (\d+) months?\b/i,getDateRange:()=>{const a=new Date(i);return a.setMonth(a.getMonth()-3),[t(a),e]},granularity:"month"},{pattern:/\bq([1-4])\b/i,getDateRange:()=>[t(new Date(i.getFullYear(),0,1)),t(new Date(i.getFullYear(),2,31))],granularity:"month"}]}const Et={funnel:/\b(funnel|conversion|drop.?off|steps?|journey|pipeline|stages?)\b/i,flow:/\b(flows?|paths?|sequence|before|after|next|previous|user.?journey)\b/i,retention:/\b(retention|cohort|return|churn|comeback|retained|day.?\d+)\b/i};function xT(i){const e=i.toLowerCase();return Et.funnel.test(e)?"funnel":Et.flow.test(e)?"flow":Et.retention.test(e)?"retention":"query"}function os(i,e){const t=e||"the relevant cube";switch(i){case"funnel":return[`Use /mcp/discover to get ${t} funnel configuration and schema`,"Query the event dimension to discover available event types for funnel steps","Build funnel query with discovered values using the schema from discover"];case"flow":return[`Use /mcp/discover to get ${t} flow configuration and schema`,"Query the event dimension to discover available event types","Build flow query specifying the starting event and steps before/after"];case"retention":return[`Use /mcp/discover to get ${t} retention configuration and schema`,"Build retention query specifying granularity (day/week/month) and number of periods"]}}function qT(i){const e=WT(),t=i.toLowerCase();for(const s of e){const n=t.match(s.pattern);if(n){if(n[1]&&/^\d+$/.test(n[1])){const r=parseInt(n[1],10),E=new Date,a=E.toISOString().split("T")[0],A=T=>T.toISOString().split("T")[0];if(/days?/.test(t)){const T=new Date(E);return T.setDate(T.getDate()-r),{dateRange:[A(T),a],granularity:"day"}}if(/weeks?/.test(t)){const T=new Date(E);return T.setDate(T.getDate()-r*7),{dateRange:[A(T),a],granularity:r<=4?"day":"week"}}if(/months?/.test(t)){const T=new Date(E);return T.setMonth(T.getMonth()-r),{dateRange:[A(T),a],granularity:r<=3?"day":"month"}}}if(/^q[1-4]$/i.test(n[0])){const r=parseInt(n[1],10),a=new Date().getFullYear(),A=(r-1)*3,T=new Date(a,A,1),R=new Date(a,A+3,0),l=S=>S.toISOString().split("T")[0];return{dateRange:[l(T),l(R)],granularity:"month"}}return{dateRange:s.getDateRange(),granularity:s.granularity}}}return null}function XT(i){const e=i.toLowerCase(),t=[{pattern:/\b(total|sum|combined)\b/i,type:"sum"},{pattern:/\b(count|number of|how many)\b/i,type:"count"},{pattern:/\b(average|avg|mean)\b/i,type:"avg"},{pattern:/\b(maximum|max|highest|top)\b/i,type:"max"},{pattern:/\b(minimum|min|lowest|bottom)\b/i,type:"min"}];for(const{pattern:s,type:n}of t)if(s.test(e))return{type:n,confidence:.8};return null}function KT(i){const e=i.toLowerCase(),t=[],s=/\bby\s+(\w+(?:\s+\w+)?)/gi;let n;for(;(n=s.exec(e))!==null;)t.push(n[1].trim());const r=/\bper\s+(\w+)/gi;for(;(n=r.exec(e))!==null;)t.push(n[1].trim());const E=/\bfor each\s+(\w+)/gi;for(;(n=E.exec(e))!==null;)t.push(n[1].trim());return t}function kT(i,e,t){const s=[],n=[],r={},E=xT(e);let a;if(t){const c=i.find(O=>O.name===t);c?(a=[c],s.push(`Using specified cube: ${t}`)):(n.push(`Specified cube '${t}' not found`),a=[])}else a=dt(i,{intent:e,limit:3}).map(O=>i.find(C=>C.name===O.cube)).filter(O=>O!==void 0),a.length>0&&s.push(`Identified relevant cubes: ${a.map(O=>O.name).join(", ")}`);if(a.length===0){const c=E!=="query",O=c?os(E,void 0):void 0;return{query:{},confidence:c?.7:0,reasoning:c?[`Detected ${E} intent from natural language`]:["Could not identify relevant cubes for this query"],warnings:n,analysisMode:E,nextSteps:O}}const A=a[0];let T=.5;const R=XT(e);R&&(s.push(`Detected ${R.type} aggregation intent`),T+=.1);const l=[],S=e.toLowerCase();for(const c of A.measures){const C=[(c.name.split(".").pop()||c.name).toLowerCase(),c.title.toLowerCase(),...(c.synonyms||[]).map(D=>D.toLowerCase())];for(const D of C)if(S.includes(D)){l.push(c.name),s.push(`Matched measure '${c.name}' via keyword '${D}'`),T+=.15;break}}if(l.length===0&&R){const c=A.measures.filter(O=>O.type===R.type);if(c.length>0)l.push(c[0].name),s.push(`Suggested ${c[0].name} based on ${R.type} intent`);else if(R.type==="count"){const O=A.measures.find(C=>C.type==="count"||C.type==="countDistinct");O&&(l.push(O.name),s.push(`Suggested ${O.name} for counting`))}}l.length===0&&A.measures.length>0&&(l.push(A.measures[0].name),s.push(`Using default measure: ${A.measures[0].name}`),n.push("Could not determine specific measure from query, using default")),r.measures=l;const N=KT(e),I=[];for(const c of N){const O=be(a,c,"dimension");O&&(I.push(O.field),s.push(`Matched dimension '${O.field}' from grouping keyword '${c}'`),T+=.1)}for(const c of a)for(const O of c.dimensions){const D=[(O.name.split(".").pop()||O.name).toLowerCase(),O.title.toLowerCase(),...(O.synonyms||[]).map(f=>f.toLowerCase())];for(const f of D)if(S.includes(f)&&!I.includes(O.name)&&(S.includes(`by ${f}`)||S.includes(`per ${f}`))){I.push(O.name),s.push(`Matched dimension '${O.name}' as grouping`),T+=.1;break}}I.length>0&&(r.dimensions=I);const u=qT(e);if(u){const c=A.dimensions.find(O=>O.type==="time");if(c){const O={dimension:c.name,dateRange:u.dateRange};u.granularity&&(O.granularity=u.granularity),r.timeDimensions=[O],s.push(`Applied time filter: ${u.dateRange[0]} to ${u.dateRange[1]}`),T+=.15}else n.push("Time expression found but no time dimension in cube")}if(T=Math.min(1,T),E!=="query"){const c=a.length>0?a[0].name:void 0;return{query:{},confidence:.7,reasoning:[`Detected ${E} intent from natural language`,...c?[`Found relevant cube: ${c}`]:[]],warnings:n.length>0?n:void 0,analysisMode:E,nextSteps:os(E,c)}}return{query:r,confidence:T,reasoning:s,warnings:n.length>0?n:void 0,analysisMode:"query"}}function JT(i,e){const t=[];for(let s=0;s<=e.length;s++)t[s]=[s];for(let s=0;s<=i.length;s++)t[0][s]=s;for(let s=1;s<=e.length;s++)for(let n=1;n<=i.length;n++)e.charAt(s-1)===i.charAt(n-1)?t[s][n]=t[s-1][n-1]:t[s][n]=Math.min(t[s-1][n-1]+1,t[s][n-1]+1,t[s-1][n]+1);return t[e.length][i.length]}function Ae(i,e){let t=null;for(const s of e){const n=JT(i.toLowerCase(),s.toLowerCase());n<=3&&(!t||n<t.distance)&&(t={field:s,distance:n})}return t}function QT(i,e,t,s){const n=i.split(".");if(n.length!==2){t.push({type:"syntax_error",message:`Invalid measure format: '${i}'. Expected 'CubeName.measureName'`,field:i});return}const[r,E]=n,a=e.find(T=>T.name===r);if(!a){const T=e.map(l=>l.name),R=Ae(r,T);R?(t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Did you mean '${R.field}'?`,correctedValue:`${R.field}.${E}`}),s.set(i,`${R.field}.${E}`)):t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Available cubes: ${T.join(", ")}`});return}if(!a.measures.some(T=>T.name===i)){const T=be(e,E,"measure");if(T&&T.cube===r)t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${T.field}'?`,correctedValue:T.field}),s.set(i,T.field);else{const R=a.measures.map(S=>S.name.split(".").pop()),l=Ae(E,R);if(l){const S=`${r}.${l.field}`;t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${l.field}'?`,correctedValue:S}),s.set(i,S)}else t.push({type:"measure_not_found",message:`Measure '${E}' not found on cube '${r}'`,field:i,suggestion:`Available measures: ${R.slice(0,5).join(", ")}${R.length>5?"...":""}`})}}}function te(i,e,t,s){const n=i.split(".");if(n.length!==2){t.push({type:"syntax_error",message:`Invalid dimension format: '${i}'. Expected 'CubeName.dimensionName'`,field:i});return}const[r,E]=n,a=e.find(T=>T.name===r);if(!a){const T=e.map(l=>l.name),R=Ae(r,T);R?(t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Did you mean '${R.field}'?`,correctedValue:`${R.field}.${E}`}),s.set(i,`${R.field}.${E}`)):t.push({type:"cube_not_found",message:`Cube '${r}' not found`,field:i,suggestion:`Available cubes: ${T.join(", ")}`});return}if(!a.dimensions.some(T=>T.name===i)){const T=be(e,E,"dimension");if(T&&T.cube===r)t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${T.field}'?`,correctedValue:T.field}),s.set(i,T.field);else{const R=a.dimensions.map(S=>S.name.split(".").pop()),l=Ae(E,R);if(l){const S=`${r}.${l.field}`;t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Did you mean '${l.field}'?`,correctedValue:S}),s.set(i,S)}else t.push({type:"dimension_not_found",message:`Dimension '${E}' not found on cube '${r}'`,field:i,suggestion:`Available dimensions: ${R.slice(0,5).join(", ")}${R.length>5?"...":""}`})}}}function Pe(i,e,t,s){for(const n of i){if("and"in n&&Array.isArray(n.and)){Pe(n.and,e,t,s);continue}if("or"in n&&Array.isArray(n.or)){Pe(n.or,e,t,s);continue}if("member"in n){const r=n.member,E=r.split(".");if(E.length!==2){t.push({type:"invalid_filter",message:`Invalid filter member format: '${r}'`,field:r});continue}const[a,A]=E,T=e.find(S=>S.name===a);if(!T){const S=e.map(I=>I.name),N=Ae(a,S);N&&s.set(r,`${N.field}.${A}`),t.push({type:"cube_not_found",message:`Cube '${a}' not found in filter`,field:r,suggestion:N?`Did you mean '${N.field}'?`:void 0,correctedValue:N?`${N.field}.${A}`:void 0});continue}const R=T.dimensions.some(S=>S.name===r),l=T.measures.some(S=>S.name===r);if(!R&&!l){const S=[...T.dimensions.map(I=>I.name.split(".").pop()),...T.measures.map(I=>I.name.split(".").pop())],N=Ae(A,S);if(N){const I=`${a}.${N.field}`;s.set(r,I),t.push({type:"invalid_filter",message:`Filter field '${A}' not found on cube '${a}'`,field:r,suggestion:`Did you mean '${N.field}'?`,correctedValue:I})}else t.push({type:"invalid_filter",message:`Filter field '${A}' not found on cube '${a}'`,field:r})}}}}function jT(i,e,t,s,n){const r=i.funnel;if(r)if(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"funnel.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"funnel.timeDimension is required"}),!r.steps||!Array.isArray(r.steps))t.push({type:"syntax_error",message:"funnel.steps array is required"});else if(r.steps.length<2)t.push({type:"syntax_error",message:"funnel requires at least 2 steps"});else for(let E=0;E<r.steps.length;E++){const a=r.steps[E];a.name||s.push({type:"best_practice",message:`Step ${E+1} is missing a name`,suggestion:"Add descriptive names to funnel steps"}),a.filter&&"member"in a.filter&&Pe([a.filter],e,t,n)}}function ZT(i,e,t,s,n){const r=i.flow;r&&(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"flow.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"flow.timeDimension is required"}),r.eventDimension?typeof r.eventDimension=="string"&&te(r.eventDimension,e,t,n):t.push({type:"syntax_error",message:"flow.eventDimension is required"}),r.stepsBefore===void 0&&r.stepsAfter===void 0&&s.push({type:"best_practice",message:"Neither stepsBefore nor stepsAfter specified",suggestion:"Set stepsBefore and/or stepsAfter to see event sequences"}))}function zT(i,e,t,s,n){const r=i.retention;r&&(r.bindingKey?typeof r.bindingKey=="string"&&te(r.bindingKey,e,t,n):t.push({type:"syntax_error",message:"retention.bindingKey is required"}),r.timeDimension?typeof r.timeDimension=="string"&&te(r.timeDimension,e,t,n):t.push({type:"syntax_error",message:"retention.timeDimension is required"}),r.granularity||s.push({type:"best_practice",message:"retention.granularity not specified",suggestion:'Specify granularity: "day", "week", or "month"'}),r.periods||s.push({type:"best_practice",message:"retention.periods not specified",suggestion:"Specify number of periods to analyze"}))}function eA(i,e){const t=[],s=[],n=new Map;if(i.funnel)return jT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.flow)return ZT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.retention)return zT(i,e,t,s,n),{isValid:t.length===0,errors:t,warnings:s,correctedQuery:void 0};if(i.measures)for(const E of i.measures)QT(E,e,t,n);if(i.dimensions)for(const E of i.dimensions)te(E,e,t,n);if(i.timeDimensions)for(const E of i.timeDimensions){te(E.dimension,e,t,n);const[a]=E.dimension.split("."),A=e.find(T=>T.name===a);if(A){const T=A.dimensions.find(R=>R.name===E.dimension);T&&T.type!=="time"&&s.push({type:"best_practice",message:`Dimension '${E.dimension}' is not a time type (it's '${T.type}')`,field:E.dimension,suggestion:'Use a dimension with type "time" for timeDimensions'})}}i.filters&&Pe(i.filters,e,t,n),!i.measures?.length&&!i.dimensions?.length&&t.push({type:"syntax_error",message:"Query must have at least one measure or dimension"}),i.measures&&i.measures.length>10&&s.push({type:"performance",message:`Query has ${i.measures.length} measures, which may impact performance`,suggestion:"Consider splitting into multiple queries"}),i.dimensions&&i.dimensions.length>5&&s.push({type:"performance",message:`Query has ${i.dimensions.length} dimensions, which may produce many rows`,suggestion:"Consider adding filters or reducing dimensions"});let r;if(n.size>0){const E=JSON.parse(JSON.stringify(i));E.measures&&(E.measures=E.measures.map(a=>n.get(a)||a)),E.dimensions&&(E.dimensions=E.dimensions.map(a=>n.get(a)||a)),E.timeDimensions&&(E.timeDimensions=E.timeDimensions.map(a=>({...a,dimension:n.get(a.dimension)||a.dimension}))),r=E}return{isValid:t.length===0,errors:t,warnings:s,correctedQuery:n.size>0?r:void 0}}function tA(i){return new ge({drizzle:i.drizzle,schema:i.schema})}exports.BaseDatabaseExecutor=Ie;exports.CTEBuilder=Cs;exports.CalculatedMeasureResolver=ne;exports.ComparisonQueryBuilder=ms;exports.DrizzlePlanBuilder=Ps;exports.DrizzleSqlBuilder=cs;exports.DuckDBExecutor=Ss;exports.EXPLAIN_ANALYSIS_PROMPT=Js;exports.FlowQueryBuilder=_s;exports.FunnelQueryBuilder=Ls;exports.IdentityOptimiser=hs;exports.JoinPathResolver=Se;exports.LogicalPlanBuilder=fs;exports.LogicalPlanner=ds;exports.MemoryCacheProvider=Qo;exports.MySQLExecutor=It;exports.OptimiserPipeline=vn;exports.PostgresExecutor=As;exports.QueryExecutor=Ms;exports.RetentionQueryBuilder=Ds;exports.SQLiteExecutor=ls;exports.STEP0_VALIDATION_PROMPT=qs;exports.STEP1_SYSTEM_PROMPT=Ks;exports.STEP2_SYSTEM_PROMPT=ks;exports.SYSTEM_PROMPT_TEMPLATE=Xs;exports.SemanticLayerCompiler=ge;exports.aiValidateQuery=eA;exports.buildAgentSystemPrompt=Qs;exports.buildExplainAnalysisPrompt=tT;exports.buildStep0Prompt=jo;exports.buildStep1Prompt=zo;exports.buildStep2Prompt=eT;exports.buildSystemPrompt=Zo;exports.createDatabaseExecutor=Tt;exports.createDrizzleSemanticLayer=tA;exports.createDuckDBExecutor=Ns;exports.createMultiCubeContext=_n;exports.createMySQLExecutor=Rs;exports.createPostgresExecutor=at;exports.createSQLiteExecutor=ot;exports.createToolExecutor=Zs;exports.defineCube=pn;exports.discoverCubes=dt;exports.findBestFieldMatch=be;exports.fnv1aHash=lt;exports.formatCubeSchemaForExplain=sT;exports.formatExistingIndexes=nT;exports.generateCacheKey=us;exports.getCubeInvalidationPattern=yn;exports.getJoinType=_e;exports.getToolDefinitions=js;exports.handleAgentChat=YT;exports.normalizeQuery=Os;exports.resolveCubeReference=K;exports.resolveSqlExpression=F;exports.suggestQuery=kT;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -41,6 +41,16 @@ export declare interface AgentConfig {
|
|
|
41
41
|
allowClientApiKey?: boolean;
|
|
42
42
|
/** Optional observability hooks for tracing */
|
|
43
43
|
observability?: AgentObservabilityHooks;
|
|
44
|
+
/**
|
|
45
|
+
* Build per-request system context for the LLM prompt from the authenticated security context.
|
|
46
|
+
* Called on every agent chat request with the resolved security context.
|
|
47
|
+
* The returned string is appended to the system prompt under "## User Context".
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* buildSystemContext: (securityContext) =>
|
|
51
|
+
* `User: ${securityContext.userName}, Role: ${securityContext.role}`
|
|
52
|
+
*/
|
|
53
|
+
buildSystemContext?: (securityContext: Record<string, unknown>) => string | undefined;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
/**
|
|
@@ -2240,6 +2250,8 @@ export declare function handleAgentChat(options: {
|
|
|
2240
2250
|
securityContext: SecurityContext;
|
|
2241
2251
|
agentConfig: AgentConfig;
|
|
2242
2252
|
apiKey: string;
|
|
2253
|
+
/** Per-request context appended to the system prompt (e.g. user info, tenant context) */
|
|
2254
|
+
systemContext?: string;
|
|
2243
2255
|
}): AsyncGenerator<AgentSSEEvent>;
|
|
2244
2256
|
|
|
2245
2257
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -32919,6 +32919,12 @@ ${N.join(`
|
|
|
32919
32919
|
if (Array.isArray(E.timeDimensions))
|
|
32920
32920
|
for (const S of E.timeDimensions)
|
|
32921
32921
|
typeof S.dimension == "string" && (S.dimension = A(S.dimension));
|
|
32922
|
+
if (E.order && typeof E.order == "object" && !Array.isArray(E.order)) {
|
|
32923
|
+
const S = {};
|
|
32924
|
+
for (const [l, N] of Object.entries(E.order))
|
|
32925
|
+
S[A(l)] = N;
|
|
32926
|
+
E.order = S;
|
|
32927
|
+
}
|
|
32922
32928
|
let o;
|
|
32923
32929
|
E.funnel ? o = { funnel: E.funnel } : E.flow ? o = { flow: E.flow } : E.retention ? o = { retention: E.retention } : o = {
|
|
32924
32930
|
measures: E.measures,
|
|
@@ -33121,7 +33127,14 @@ async function* IA(i) {
|
|
|
33121
33127
|
};
|
|
33122
33128
|
return;
|
|
33123
33129
|
}
|
|
33124
|
-
const l = new S({ apiKey: E }), N = vT(), I = $T({ semanticLayer: s, securityContext: n }), u = s.getMetadata()
|
|
33130
|
+
const l = new S({ apiKey: E }), N = vT(), I = $T({ semanticLayer: s, securityContext: n }), u = s.getMetadata();
|
|
33131
|
+
let c = AT(u);
|
|
33132
|
+
i.systemContext && (c += `
|
|
33133
|
+
|
|
33134
|
+
## User Context
|
|
33135
|
+
|
|
33136
|
+
${i.systemContext}`);
|
|
33137
|
+
const O = r.model || "claude-sonnet-4-6", C = r.maxTurns || 25, D = r.maxTokens || 4096;
|
|
33125
33138
|
try {
|
|
33126
33139
|
A?.onChatStart?.({
|
|
33127
33140
|
traceId: o,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-cube",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.20",
|
|
4
4
|
"description": "Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.",
|
|
5
5
|
"main": "./dist/server/index.js",
|
|
6
6
|
"types": "./dist/server/index.d.ts",
|