drizzle-cube 0.4.18 → 0.4.19
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 +1 -1
- package/dist/adapters/express/index.js +1 -1
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +1 -1
- package/dist/adapters/{handler-DefTXJpi.js → handler-BV4JuWNW.js} +84 -78
- package/dist/adapters/{handler-hwoGzGex.cjs → handler-D4MVKkVy.cjs} +10 -10
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +1 -1
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.js +6 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("express"),M=require("cors"),c=require("../mcp-transport-8u9G5oNa.cjs"),n=require("../utils.cjs");function $(g){const{cubes:h,drizzle:w,schema:P,extractSecurityContext:y,engineType:A,cors:x,basePath:f="/cubejs-api/v1",jsonLimit:S="10mb",cache:H,mcp:v={enabled:!0},agent:j}=g;if(!h||h.length===0)throw new Error("At least one cube must be provided in the cubes array");const d=R.Router();x&&d.use(M(x)),d.use(R.json({limit:S})),d.use(R.urlencoded({extended:!0,limit:S}));const u=new c.SemanticLayerCompiler({drizzle:w,schema:P,engineType:A,cache:H});if(h.forEach(r=>{u.registerCube(r)}),d.post(`${f}/load`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=r.headers["x-cache-control"]==="no-cache",i=await u.executeMultiCubeQuery(e,o,{skipCache:a});t.json(n.formatCubeResponse(e,i,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.get(`${f}/load`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));let o;try{o=JSON.parse(e)}catch{return t.status(400).json(n.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=r.headers["x-cache-control"]==="no-cache",m=await u.executeMultiCubeQuery(o,s,{skipCache:i});t.json(n.formatCubeResponse(o,m,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.post(`${f}/batch`,async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).json(n.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).json(n.formatErrorResponse("Queries array cannot be empty",400));const o=await y(r,t),s=r.headers["x-cache-control"]==="no-cache",a=await n.handleBatchRequest(e,o,u,{skipCache:s});t.json(a)}catch(e){console.error("Batch execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),d.get(`${f}/meta`,(r,t)=>{try{const e=u.getMetadata();t.json(n.formatMetaResponse(e))}catch(e){console.error("Metadata error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),d.post(`${f}/sql`,async(r,t)=>{try{const e=r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=e.measures?.[0]||e.dimensions?.[0];if(!a)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const i=a.split(".")[0],m=await u.generateSQL(i,e,o);t.json(n.formatSqlResponse(e,m))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.get(`${f}/sql`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));const o=JSON.parse(e),s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=o.measures?.[0]||o.dimensions?.[0];if(!i)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const m=i.split(".")[0],p=await u.generateSQL(m,o,s);t.json(n.formatSqlResponse(o,p))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.post(`${f}/dry-run`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=await n.handleDryRun(e,o,u);t.json(s)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.get(`${f}/dry-run`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json({error:"Query parameter is required",valid:!1});const o=JSON.parse(e),s=await y(r,t),a=await n.handleDryRun(o,s,u);t.json(a)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.post(`${f}/explain`,async(r,t)=>{try{const e=r.body.query||r.body,o=r.body.options||{},s=await y(r,t),a=u.validateQuery(e);if(!a.isValid)return t.status(400).json({error:`Query validation failed: ${a.errors.join(", ")}`});const i=await u.explainQuery(e,s,o);t.json(i)}catch(e){console.error("Explain error:",e),t.status(500).json({error:e instanceof Error?e.message:"Explain query failed"})}}),j&&d.post(`${f}/agent/chat`,async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("express"),M=require("cors"),c=require("../mcp-transport-8u9G5oNa.cjs"),n=require("../utils.cjs");function $(g){const{cubes:h,drizzle:w,schema:P,extractSecurityContext:y,engineType:A,cors:x,basePath:f="/cubejs-api/v1",jsonLimit:S="10mb",cache:H,mcp:v={enabled:!0},agent:j}=g;if(!h||h.length===0)throw new Error("At least one cube must be provided in the cubes array");const d=R.Router();x&&d.use(M(x)),d.use(R.json({limit:S})),d.use(R.urlencoded({extended:!0,limit:S}));const u=new c.SemanticLayerCompiler({drizzle:w,schema:P,engineType:A,cache:H});if(h.forEach(r=>{u.registerCube(r)}),d.post(`${f}/load`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=r.headers["x-cache-control"]==="no-cache",i=await u.executeMultiCubeQuery(e,o,{skipCache:a});t.json(n.formatCubeResponse(e,i,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.get(`${f}/load`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));let o;try{o=JSON.parse(e)}catch{return t.status(400).json(n.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=r.headers["x-cache-control"]==="no-cache",m=await u.executeMultiCubeQuery(o,s,{skipCache:i});t.json(n.formatCubeResponse(o,m,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.post(`${f}/batch`,async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).json(n.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).json(n.formatErrorResponse("Queries array cannot be empty",400));const o=await y(r,t),s=r.headers["x-cache-control"]==="no-cache",a=await n.handleBatchRequest(e,o,u,{skipCache:s});t.json(a)}catch(e){console.error("Batch execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),d.get(`${f}/meta`,(r,t)=>{try{const e=u.getMetadata();t.json(n.formatMetaResponse(e))}catch(e){console.error("Metadata error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),d.post(`${f}/sql`,async(r,t)=>{try{const e=r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=e.measures?.[0]||e.dimensions?.[0];if(!a)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const i=a.split(".")[0],m=await u.generateSQL(i,e,o);t.json(n.formatSqlResponse(e,m))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.get(`${f}/sql`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));const o=JSON.parse(e),s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=o.measures?.[0]||o.dimensions?.[0];if(!i)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const m=i.split(".")[0],p=await u.generateSQL(m,o,s);t.json(n.formatSqlResponse(o,p))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.post(`${f}/dry-run`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=await n.handleDryRun(e,o,u);t.json(s)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.get(`${f}/dry-run`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json({error:"Query parameter is required",valid:!1});const o=JSON.parse(e),s=await y(r,t),a=await n.handleDryRun(o,s,u);t.json(a)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.post(`${f}/explain`,async(r,t)=>{try{const e=r.body.query||r.body,o=r.body.options||{},s=await y(r,t),a=u.validateQuery(e);if(!a.isValid)return t.status(400).json({error:`Query validation failed: ${a.errors.join(", ")}`});const i=await u.explainQuery(e,s,o);t.json(i)}catch(e){console.error("Explain error:",e),t.status(500).json({error:e instanceof Error?e.message:"Explain query failed"})}}),j&&d.post(`${f}/agent/chat`,async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),{message:o,sessionId:s,history:a}=r.body;if(!o||typeof o!="string")return t.status(400).json({error:"message is required and must be a string"});let i=(j.apiKey||"").trim();if(j.allowClientApiKey){const p=r.headers["x-agent-api-key"];p&&(i=p.trim())}if(!i)return t.status(401).json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const m=await y(r,t);t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const p=e({message:o,sessionId:s,history:a,semanticLayer:u,securityContext:m,agentConfig:j,apiKey:i});for await(const l of p)t.write(`data: ${JSON.stringify(l)}
|
|
2
2
|
|
|
3
3
|
`)}catch(p){const l={type:"error",data:{message:p instanceof Error?p.message:"Stream failed"}};t.write(`data: ${JSON.stringify(l)}
|
|
4
4
|
|
|
@@ -198,7 +198,7 @@ function W(h) {
|
|
|
198
198
|
}
|
|
199
199
|
}), j && c.post(`${p}/agent/chat`, async (r, t) => {
|
|
200
200
|
try {
|
|
201
|
-
const { handleAgentChat: e } = await import("../handler-
|
|
201
|
+
const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), { message: o, sessionId: a, history: n } = r.body;
|
|
202
202
|
if (!o || typeof o != "string")
|
|
203
203
|
return t.status(400).json({ error: "message is required and must be a string" });
|
|
204
204
|
let s = (j.apiKey || "").trim();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var O=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var H=(p,n,b,C)=>{if(n&&typeof n=="object"||typeof n=="function")for(let h of J(n))!D.call(p,h)&&h!==b&&j(p,h,{get:()=>n[h],enumerable:!(C=k(n,h))||C.enumerable});return p};var T=(p,n,b)=>(b=p!=null?O(L(p)):{},H(n||!p||!p.__esModule?j(b,"default",{value:p,enumerable:!0}):b,p));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("../mcp-transport-8u9G5oNa.cjs"),a=require("../utils.cjs"),q=function(n,b,C){const{cubes:h,drizzle:I,schema:A,extractSecurityContext:m,engineType:M,cors:P,basePath:g="/cubejs-api/v1",bodyLimit:v=10485760,cache:N,mcp:R={enabled:!0},agent:w}=b;if(!h||h.length===0)return C(new Error("At least one cube must be provided in the cubes array"));P&&n.register(import("@fastify/cors"),P),n.addHook("onRequest",async(r,t)=>{r.method==="POST"&&(r.body=void 0)});const d=new u.SemanticLayerCompiler({drizzle:I,schema:A,engineType:M,cache:N});if(h.forEach(r=>{d.registerCube(r)}),n.post(`${g}/load`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.get(`${g}/load`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query;let o;try{o=JSON.parse(e)}catch{return t.status(400).send(a.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.post(`${g}/batch`,{bodyLimit:v,schema:{body:{type:"object",required:["queries"],properties:{queries:{type:"array",items:{type:"object"}}}}}},async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).send(a.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).send(a.formatErrorResponse("Queries array cannot be empty",400));const o=await m(r),s=r.headers["x-cache-control"]==="no-cache";return await a.handleBatchRequest(e,o,d,{skipCache:s})}catch(e){return r.log.error(e,"Batch execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),n.get(`${g}/meta`,async(r,t)=>{try{const e=d.getMetadata();return a.formatMetaResponse(e)}catch(e){return r.log.error(e,"Metadata error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),n.post(`${g}/sql`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=await m(r),s=d.validateQuery(e);if(!s.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const i=e.measures?.[0]||e.dimensions?.[0];if(!i)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const c=i.split(".")[0],y=await d.generateSQL(c,e,o);return a.formatSqlResponse(e,y)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.get(`${g}/sql`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=o.measures?.[0]||o.dimensions?.[0];if(!c)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const y=c.split(".")[0],E=await d.generateSQL(y,o,s);return a.formatSqlResponse(o,E)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.post(`${g}/dry-run`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.get(`${g}/dry-run`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.post(`${g}/explain`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=e.options||{},i=await m(r),c=d.validateQuery(o);return c.isValid?await d.explainQuery(o,i,s):t.status(400).send({error:`Query validation failed: ${c.errors.join(", ")}`})}catch(e){return r.log.error(e,"Explain error"),t.status(500).send({error:e instanceof Error?e.message:"Explain query failed"})}}),w&&n.post(`${g}/agent/chat`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-
|
|
1
|
+
"use strict";var O=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var H=(p,n,b,C)=>{if(n&&typeof n=="object"||typeof n=="function")for(let h of J(n))!D.call(p,h)&&h!==b&&j(p,h,{get:()=>n[h],enumerable:!(C=k(n,h))||C.enumerable});return p};var T=(p,n,b)=>(b=p!=null?O(L(p)):{},H(n||!p||!p.__esModule?j(b,"default",{value:p,enumerable:!0}):b,p));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("../mcp-transport-8u9G5oNa.cjs"),a=require("../utils.cjs"),q=function(n,b,C){const{cubes:h,drizzle:I,schema:A,extractSecurityContext:m,engineType:M,cors:P,basePath:g="/cubejs-api/v1",bodyLimit:v=10485760,cache:N,mcp:R={enabled:!0},agent:w}=b;if(!h||h.length===0)return C(new Error("At least one cube must be provided in the cubes array"));P&&n.register(import("@fastify/cors"),P),n.addHook("onRequest",async(r,t)=>{r.method==="POST"&&(r.body=void 0)});const d=new u.SemanticLayerCompiler({drizzle:I,schema:A,engineType:M,cache:N});if(h.forEach(r=>{d.registerCube(r)}),n.post(`${g}/load`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.get(`${g}/load`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query;let o;try{o=JSON.parse(e)}catch{return t.status(400).send(a.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.post(`${g}/batch`,{bodyLimit:v,schema:{body:{type:"object",required:["queries"],properties:{queries:{type:"array",items:{type:"object"}}}}}},async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).send(a.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).send(a.formatErrorResponse("Queries array cannot be empty",400));const o=await m(r),s=r.headers["x-cache-control"]==="no-cache";return await a.handleBatchRequest(e,o,d,{skipCache:s})}catch(e){return r.log.error(e,"Batch execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),n.get(`${g}/meta`,async(r,t)=>{try{const e=d.getMetadata();return a.formatMetaResponse(e)}catch(e){return r.log.error(e,"Metadata error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),n.post(`${g}/sql`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=await m(r),s=d.validateQuery(e);if(!s.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const i=e.measures?.[0]||e.dimensions?.[0];if(!i)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const c=i.split(".")[0],y=await d.generateSQL(c,e,o);return a.formatSqlResponse(e,y)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.get(`${g}/sql`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=o.measures?.[0]||o.dimensions?.[0];if(!c)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const y=c.split(".")[0],E=await d.generateSQL(y,o,s);return a.formatSqlResponse(o,E)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.post(`${g}/dry-run`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.get(`${g}/dry-run`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.post(`${g}/explain`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=e.options||{},i=await m(r),c=d.validateQuery(o);return c.isValid?await d.explainQuery(o,i,s):t.status(400).send({error:`Query validation failed: ${c.errors.join(", ")}`})}catch(e){return r.log.error(e,"Explain error"),t.status(500).send({error:e instanceof Error?e.message:"Explain query failed"})}}),w&&n.post(`${g}/agent/chat`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),o=r.body,{message:s,sessionId:i,history:c}=o;if(!s||typeof s!="string")return t.status(400).send({error:"message is required and must be a string"});let y=(w.apiKey||"").trim();if(w.allowClientApiKey){const l=r.headers["x-agent-api-key"];l&&(y=l.trim())}if(!y)return t.status(401).send({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const E=await m(r);t.raw.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const l=e({message:s,sessionId:i,history:c,semanticLayer:d,securityContext:E,agentConfig:w,apiKey:y});for await(const f of l)t.raw.write(`data: ${JSON.stringify(f)}
|
|
2
2
|
|
|
3
3
|
`)}catch(l){const f={type:"error",data:{message:l instanceof Error?l.message:"Stream failed"}};t.raw.write(`data: ${JSON.stringify(f)}
|
|
4
4
|
|
|
@@ -262,7 +262,7 @@ const N = function(i, k, R) {
|
|
|
262
262
|
}
|
|
263
263
|
}, async (t, r) => {
|
|
264
264
|
try {
|
|
265
|
-
const { handleAgentChat: e } = await import("../handler-
|
|
265
|
+
const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), a = t.body, { message: n, sessionId: s, history: o } = a;
|
|
266
266
|
if (!n || typeof n != "string")
|
|
267
267
|
return r.status(400).send({ error: "message is required and must be a string" });
|
|
268
268
|
let l = (v.apiKey || "").trim();
|
|
@@ -1752,11 +1752,11 @@ function Te(l, t, a) {
|
|
|
1752
1752
|
return t ?? {};
|
|
1753
1753
|
const d = { ...t }, u = a.measures ?? [], e = a.dimensions ?? [], r = (a.timeDimensions ?? []).map((s) => s.dimension);
|
|
1754
1754
|
for (const s of i.dropZones) {
|
|
1755
|
-
const
|
|
1756
|
-
if (Array.isArray(
|
|
1757
|
-
const
|
|
1755
|
+
const h = d[s.key];
|
|
1756
|
+
if (Array.isArray(h) ? h.length > 0 : !!h) continue;
|
|
1757
|
+
const c = s.acceptTypes ?? [];
|
|
1758
1758
|
if (s.key === "sizeField" || s.key === "colorField") {
|
|
1759
|
-
if (
|
|
1759
|
+
if (c.includes("measure")) {
|
|
1760
1760
|
const g = /* @__PURE__ */ new Set();
|
|
1761
1761
|
for (const C of i.dropZones) {
|
|
1762
1762
|
if (C.key === s.key) continue;
|
|
@@ -1769,7 +1769,7 @@ function Te(l, t, a) {
|
|
|
1769
1769
|
continue;
|
|
1770
1770
|
}
|
|
1771
1771
|
const n = [];
|
|
1772
|
-
if (
|
|
1772
|
+
if (c.includes("dimension") && n.push(...e), c.includes("timeDimension") && n.push(...r), c.includes("measure") && n.push(...u), n.length === 0) continue;
|
|
1773
1773
|
let x = n;
|
|
1774
1774
|
if (s.key === "series") {
|
|
1775
1775
|
const g = new Set(
|
|
@@ -1777,8 +1777,8 @@ function Te(l, t, a) {
|
|
|
1777
1777
|
);
|
|
1778
1778
|
if (x = n.filter((A) => !g.has(A)), x.length === 0) continue;
|
|
1779
1779
|
}
|
|
1780
|
-
const b = s.maxItems ?? 1 / 0,
|
|
1781
|
-
|
|
1780
|
+
const b = s.maxItems ?? 1 / 0, y = x.slice(0, b);
|
|
1781
|
+
y.length > 0 && (d[s.key] = y);
|
|
1782
1782
|
}
|
|
1783
1783
|
return d;
|
|
1784
1784
|
}
|
|
@@ -1788,7 +1788,7 @@ Chart config requirements by type:`];
|
|
|
1788
1788
|
for (const a of l) {
|
|
1789
1789
|
const i = P[a];
|
|
1790
1790
|
if (!i) continue;
|
|
1791
|
-
const d = i.description ?? "", u = i.useCase ?? "", e = [d, u].filter(Boolean).join(". "), o = e ? ` — ${e}.` : "", r = i.dropZones.filter((
|
|
1791
|
+
const d = i.description ?? "", u = i.useCase ?? "", e = [d, u].filter(Boolean).join(". "), o = e ? ` — ${e}.` : "", r = i.dropZones.filter((h) => h.mandatory);
|
|
1792
1792
|
if (r.length === 0 && !i.skipQuery) {
|
|
1793
1793
|
t.push(` ${a}${o} chartConfig auto-inferred from query.`);
|
|
1794
1794
|
continue;
|
|
@@ -1797,9 +1797,9 @@ Chart config requirements by type:`];
|
|
|
1797
1797
|
t.push(` ${a}${o} No query needed.`);
|
|
1798
1798
|
continue;
|
|
1799
1799
|
}
|
|
1800
|
-
const s = r.map((
|
|
1801
|
-
const
|
|
1802
|
-
return `${
|
|
1800
|
+
const s = r.map((h) => {
|
|
1801
|
+
const m = h.acceptTypes?.join("/") ?? "any", c = h.maxItems ? ` (max ${h.maxItems})` : "";
|
|
1802
|
+
return `${h.key}=[${m}]${c}`;
|
|
1803
1803
|
});
|
|
1804
1804
|
t.push(` ${a}${o} Requires ${s.join(", ")}.`);
|
|
1805
1805
|
}
|
|
@@ -2143,51 +2143,57 @@ function Se(l) {
|
|
|
2143
2143
|
});
|
|
2144
2144
|
const u = (e, o) => {
|
|
2145
2145
|
const r = d.get(e), s = o === "measures" ? r?.measures : r?.dimensions;
|
|
2146
|
-
return !s || s.length === 0 ? "" : ` Available ${o}: ${s.slice(0, 5).map((
|
|
2146
|
+
return !s || s.length === 0 ? "" : ` Available ${o}: ${s.slice(0, 5).map((h) => `"${h}"`).join(", ")}`;
|
|
2147
2147
|
};
|
|
2148
2148
|
return i.set("execute_query", async (e) => {
|
|
2149
2149
|
try {
|
|
2150
|
-
const o = (
|
|
2151
|
-
if (!Array.isArray(
|
|
2150
|
+
const o = (m, c) => {
|
|
2151
|
+
if (!Array.isArray(m)) return;
|
|
2152
2152
|
const n = [], x = [];
|
|
2153
|
-
for (const b of
|
|
2153
|
+
for (const b of m) {
|
|
2154
2154
|
if (typeof b != "string") {
|
|
2155
2155
|
x.push(b);
|
|
2156
2156
|
continue;
|
|
2157
2157
|
}
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2160
|
-
n.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b,
|
|
2158
|
+
const y = b.split(".");
|
|
2159
|
+
if (y.length === 1) {
|
|
2160
|
+
n.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b, c)}`);
|
|
2161
2161
|
continue;
|
|
2162
2162
|
}
|
|
2163
|
-
if (
|
|
2164
|
-
const g = `${
|
|
2163
|
+
if (y.length === 3 && y[0] === y[1]) {
|
|
2164
|
+
const g = `${y[0]}.${y[2]}`;
|
|
2165
2165
|
x.push(g);
|
|
2166
2166
|
continue;
|
|
2167
2167
|
}
|
|
2168
|
-
if (
|
|
2169
|
-
n.push(`"${b}" is WRONG — "${
|
|
2168
|
+
if (y.length === 2 && y[0] === y[1]) {
|
|
2169
|
+
n.push(`"${b}" is WRONG — "${y[0]}" is the cube name, not a ${c.replace(/s$/, "")}.${u(y[0], c)}`);
|
|
2170
2170
|
continue;
|
|
2171
2171
|
}
|
|
2172
2172
|
x.push(b);
|
|
2173
2173
|
}
|
|
2174
2174
|
if (n.length > 0)
|
|
2175
|
-
throw new Error(`Invalid ${
|
|
2175
|
+
throw new Error(`Invalid ${c}:
|
|
2176
2176
|
${n.join(`
|
|
2177
2177
|
`)}`);
|
|
2178
2178
|
return x;
|
|
2179
2179
|
};
|
|
2180
2180
|
e.measures = o(e.measures, "measures") ?? e.measures, e.dimensions = o(e.dimensions, "dimensions") ?? e.dimensions;
|
|
2181
|
-
const r = (
|
|
2182
|
-
const
|
|
2183
|
-
return
|
|
2181
|
+
const r = (m) => {
|
|
2182
|
+
const c = m.split(".");
|
|
2183
|
+
return c.length === 3 && c[0] === c[1] ? `${c[0]}.${c[2]}` : m;
|
|
2184
2184
|
};
|
|
2185
2185
|
if (Array.isArray(e.filters))
|
|
2186
|
-
for (const
|
|
2187
|
-
typeof
|
|
2186
|
+
for (const m of e.filters)
|
|
2187
|
+
typeof m.member == "string" && (m.member = r(m.member));
|
|
2188
2188
|
if (Array.isArray(e.timeDimensions))
|
|
2189
|
-
for (const
|
|
2190
|
-
typeof
|
|
2189
|
+
for (const m of e.timeDimensions)
|
|
2190
|
+
typeof m.dimension == "string" && (m.dimension = r(m.dimension));
|
|
2191
|
+
if (e.order && typeof e.order == "object" && !Array.isArray(e.order)) {
|
|
2192
|
+
const m = {};
|
|
2193
|
+
for (const [c, n] of Object.entries(e.order))
|
|
2194
|
+
m[r(c)] = n;
|
|
2195
|
+
e.order = m;
|
|
2196
|
+
}
|
|
2191
2197
|
let s;
|
|
2192
2198
|
e.funnel ? s = { funnel: e.funnel } : e.flow ? s = { flow: e.flow } : e.retention ? s = { retention: e.retention } : s = {
|
|
2193
2199
|
measures: e.measures,
|
|
@@ -2197,12 +2203,12 @@ ${n.join(`
|
|
|
2197
2203
|
order: e.order,
|
|
2198
2204
|
limit: e.limit
|
|
2199
2205
|
};
|
|
2200
|
-
const
|
|
2206
|
+
const h = await G(t, a, { query: s });
|
|
2201
2207
|
return {
|
|
2202
2208
|
result: JSON.stringify({
|
|
2203
|
-
rowCount:
|
|
2204
|
-
data:
|
|
2205
|
-
annotation:
|
|
2209
|
+
rowCount: h.data.length,
|
|
2210
|
+
data: h.data,
|
|
2211
|
+
annotation: h.annotation
|
|
2206
2212
|
}, null, 2)
|
|
2207
2213
|
};
|
|
2208
2214
|
} catch (o) {
|
|
@@ -2239,38 +2245,38 @@ ${JSON.stringify(r, null, 2)}`,
|
|
|
2239
2245
|
isError: !0
|
|
2240
2246
|
};
|
|
2241
2247
|
}
|
|
2242
|
-
const
|
|
2243
|
-
if (!
|
|
2248
|
+
const h = t.validateQuery(s);
|
|
2249
|
+
if (!h.isValid)
|
|
2244
2250
|
return {
|
|
2245
2251
|
result: `Invalid query — fix these errors and retry:
|
|
2246
|
-
${
|
|
2252
|
+
${h.errors.join(`
|
|
2247
2253
|
`)}
|
|
2248
2254
|
|
|
2249
2255
|
Attempted query:
|
|
2250
2256
|
${JSON.stringify(s, null, 2)}`,
|
|
2251
2257
|
isError: !0
|
|
2252
2258
|
};
|
|
2253
|
-
const
|
|
2254
|
-
let
|
|
2255
|
-
if (
|
|
2256
|
-
|
|
2259
|
+
const m = !!(s.funnel || s.flow || s.retention);
|
|
2260
|
+
let c;
|
|
2261
|
+
if (m)
|
|
2262
|
+
c = e.chartConfig ?? {};
|
|
2257
2263
|
else {
|
|
2258
|
-
const b = Te(r, e.chartConfig, s),
|
|
2259
|
-
if (!
|
|
2264
|
+
const b = Te(r, e.chartConfig, s), y = Ce(r, b, s);
|
|
2265
|
+
if (!y.isValid)
|
|
2260
2266
|
return {
|
|
2261
2267
|
result: `Chart config invalid — fix these errors and retry:
|
|
2262
|
-
${
|
|
2268
|
+
${y.errors.join(`
|
|
2263
2269
|
`)}`,
|
|
2264
2270
|
isError: !0
|
|
2265
2271
|
};
|
|
2266
|
-
|
|
2272
|
+
c = b;
|
|
2267
2273
|
}
|
|
2268
2274
|
const n = `portlet-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`, x = {
|
|
2269
2275
|
id: n,
|
|
2270
2276
|
title: e.title,
|
|
2271
2277
|
query: e.query,
|
|
2272
2278
|
chartType: r,
|
|
2273
|
-
chartConfig:
|
|
2279
|
+
chartConfig: c,
|
|
2274
2280
|
displayConfig: e.displayConfig
|
|
2275
2281
|
};
|
|
2276
2282
|
return {
|
|
@@ -2300,14 +2306,14 @@ ${c.errors.join(`
|
|
|
2300
2306
|
r.push(`Portlet "${n.title}": missing query`);
|
|
2301
2307
|
continue;
|
|
2302
2308
|
}
|
|
2303
|
-
let
|
|
2309
|
+
let y;
|
|
2304
2310
|
try {
|
|
2305
|
-
|
|
2311
|
+
y = JSON.parse(b);
|
|
2306
2312
|
} catch {
|
|
2307
2313
|
r.push(`Portlet "${n.title}": invalid JSON query`);
|
|
2308
2314
|
continue;
|
|
2309
2315
|
}
|
|
2310
|
-
const g = t.validateQuery(
|
|
2316
|
+
const g = t.validateQuery(y);
|
|
2311
2317
|
g.isValid || r.push(`Portlet "${n.title}": ${g.errors.join(", ")}`);
|
|
2312
2318
|
}
|
|
2313
2319
|
if (r.length > 0)
|
|
@@ -2319,7 +2325,7 @@ ${r.join(`
|
|
|
2319
2325
|
};
|
|
2320
2326
|
const s = {
|
|
2321
2327
|
portlets: o.map((n) => {
|
|
2322
|
-
const x = n.chartType, b = x === "markdown",
|
|
2328
|
+
const x = n.chartType, b = x === "markdown", y = b ? "query" : n.analysisType || "query", g = y === "funnel" ? "funnel" : y === "flow" ? "flow" : y === "retention" ? "retention" : "query", A = n.query || "{}";
|
|
2323
2329
|
let C;
|
|
2324
2330
|
try {
|
|
2325
2331
|
C = JSON.parse(A);
|
|
@@ -2352,13 +2358,13 @@ ${r.join(`
|
|
|
2352
2358
|
}),
|
|
2353
2359
|
filters: e.filters,
|
|
2354
2360
|
colorPalette: e.colorPalette
|
|
2355
|
-
},
|
|
2361
|
+
}, h = e.title, m = s.portlets.length, c = s.filters?.length || 0;
|
|
2356
2362
|
return {
|
|
2357
|
-
result: `Dashboard "${
|
|
2363
|
+
result: `Dashboard "${h}" created with ${m} portlets and ${c} filters.`,
|
|
2358
2364
|
sideEffect: {
|
|
2359
2365
|
type: "dashboard_saved",
|
|
2360
2366
|
data: {
|
|
2361
|
-
title:
|
|
2367
|
+
title: h,
|
|
2362
2368
|
description: e.description,
|
|
2363
2369
|
dashboardConfig: s
|
|
2364
2370
|
}
|
|
@@ -2373,14 +2379,14 @@ ${r.join(`
|
|
|
2373
2379
|
}), i;
|
|
2374
2380
|
}
|
|
2375
2381
|
async function* _e(l) {
|
|
2376
|
-
const { message: t, history: a, semanticLayer: i, securityContext: d, agentConfig: u, apiKey: e } = l, o = l.sessionId || crypto.randomUUID(), r = u.observability, s = crypto.randomUUID(),
|
|
2377
|
-
let
|
|
2382
|
+
const { message: t, history: a, semanticLayer: i, securityContext: d, agentConfig: u, apiKey: e } = l, o = l.sessionId || crypto.randomUUID(), r = u.observability, s = crypto.randomUUID(), h = Date.now();
|
|
2383
|
+
let m;
|
|
2378
2384
|
try {
|
|
2379
|
-
const
|
|
2385
|
+
const f = await import(
|
|
2380
2386
|
/* webpackIgnore: true */
|
|
2381
2387
|
"@anthropic-ai/sdk"
|
|
2382
2388
|
);
|
|
2383
|
-
|
|
2389
|
+
m = f.default || f.Anthropic || f;
|
|
2384
2390
|
} catch {
|
|
2385
2391
|
yield {
|
|
2386
2392
|
type: "error",
|
|
@@ -2390,7 +2396,7 @@ async function* _e(l) {
|
|
|
2390
2396
|
};
|
|
2391
2397
|
return;
|
|
2392
2398
|
}
|
|
2393
|
-
const
|
|
2399
|
+
const c = new m({ apiKey: e }), n = De(), x = Se({ semanticLayer: i, securityContext: d }), b = i.getMetadata(), y = W(b), g = u.model || "claude-sonnet-4-6", A = u.maxTurns || 25, C = u.maxTokens || 4096;
|
|
2394
2400
|
try {
|
|
2395
2401
|
r?.onChatStart?.({
|
|
2396
2402
|
traceId: s,
|
|
@@ -2403,13 +2409,13 @@ async function* _e(l) {
|
|
|
2403
2409
|
}
|
|
2404
2410
|
const k = [];
|
|
2405
2411
|
if (a && a.length > 0) {
|
|
2406
|
-
for (const
|
|
2407
|
-
if (
|
|
2408
|
-
k.push({ role: "user", content:
|
|
2409
|
-
else if (
|
|
2412
|
+
for (const f of a)
|
|
2413
|
+
if (f.role === "user")
|
|
2414
|
+
k.push({ role: "user", content: f.content });
|
|
2415
|
+
else if (f.role === "assistant") {
|
|
2410
2416
|
const F = [];
|
|
2411
|
-
if (
|
|
2412
|
-
for (const w of
|
|
2417
|
+
if (f.content && F.push({ type: "text", text: f.content }), f.toolCalls && f.toolCalls.length > 0) {
|
|
2418
|
+
for (const w of f.toolCalls)
|
|
2413
2419
|
F.push({
|
|
2414
2420
|
type: "tool_use",
|
|
2415
2421
|
id: w.id,
|
|
@@ -2418,25 +2424,25 @@ async function* _e(l) {
|
|
|
2418
2424
|
});
|
|
2419
2425
|
k.push({ role: "assistant", content: F }), k.push({
|
|
2420
2426
|
role: "user",
|
|
2421
|
-
content:
|
|
2427
|
+
content: f.toolCalls.map((w) => ({
|
|
2422
2428
|
type: "tool_result",
|
|
2423
2429
|
tool_use_id: w.id,
|
|
2424
2430
|
content: typeof w.result == "string" ? w.result : JSON.stringify(w.result ?? ""),
|
|
2425
2431
|
...w.status === "error" ? { is_error: !0 } : {}
|
|
2426
2432
|
}))
|
|
2427
2433
|
});
|
|
2428
|
-
} else F.length > 0 && k.push({ role: "assistant", content:
|
|
2434
|
+
} else F.length > 0 && k.push({ role: "assistant", content: f.content });
|
|
2429
2435
|
}
|
|
2430
2436
|
}
|
|
2431
2437
|
k.push({ role: "user", content: t });
|
|
2432
2438
|
let E = 0;
|
|
2433
2439
|
try {
|
|
2434
|
-
for (let
|
|
2435
|
-
E =
|
|
2436
|
-
const F = await
|
|
2440
|
+
for (let f = 0; f < A; f++) {
|
|
2441
|
+
E = f + 1;
|
|
2442
|
+
const F = await c.messages.create({
|
|
2437
2443
|
model: g,
|
|
2438
2444
|
max_tokens: C,
|
|
2439
|
-
system:
|
|
2445
|
+
system: y,
|
|
2440
2446
|
tools: n,
|
|
2441
2447
|
messages: k,
|
|
2442
2448
|
stream: !0
|
|
@@ -2488,7 +2494,7 @@ async function* _e(l) {
|
|
|
2488
2494
|
try {
|
|
2489
2495
|
r?.onGenerationEnd?.({
|
|
2490
2496
|
traceId: s,
|
|
2491
|
-
turn:
|
|
2497
|
+
turn: f,
|
|
2492
2498
|
model: g,
|
|
2493
2499
|
stopReason: O,
|
|
2494
2500
|
inputTokens: L,
|
|
@@ -2530,7 +2536,7 @@ async function* _e(l) {
|
|
|
2530
2536
|
try {
|
|
2531
2537
|
r?.onToolEnd?.({
|
|
2532
2538
|
traceId: s,
|
|
2533
|
-
turn:
|
|
2539
|
+
turn: f,
|
|
2534
2540
|
toolName: p,
|
|
2535
2541
|
toolUseId: S,
|
|
2536
2542
|
isError: !!T.isError,
|
|
@@ -2552,7 +2558,7 @@ async function* _e(l) {
|
|
|
2552
2558
|
try {
|
|
2553
2559
|
r?.onToolEnd?.({
|
|
2554
2560
|
traceId: s,
|
|
2555
|
-
turn:
|
|
2561
|
+
turn: f,
|
|
2556
2562
|
toolName: p,
|
|
2557
2563
|
toolUseId: S,
|
|
2558
2564
|
isError: !0,
|
|
@@ -2569,7 +2575,7 @@ async function* _e(l) {
|
|
|
2569
2575
|
traceId: s,
|
|
2570
2576
|
sessionId: o,
|
|
2571
2577
|
totalTurns: E,
|
|
2572
|
-
durationMs: Date.now() -
|
|
2578
|
+
durationMs: Date.now() - h
|
|
2573
2579
|
});
|
|
2574
2580
|
} catch {
|
|
2575
2581
|
}
|
|
@@ -2577,21 +2583,21 @@ async function* _e(l) {
|
|
|
2577
2583
|
type: "done",
|
|
2578
2584
|
data: { sessionId: o || "", traceId: s }
|
|
2579
2585
|
};
|
|
2580
|
-
} catch (
|
|
2586
|
+
} catch (f) {
|
|
2581
2587
|
try {
|
|
2582
2588
|
r?.onChatEnd?.({
|
|
2583
2589
|
traceId: s,
|
|
2584
2590
|
sessionId: o,
|
|
2585
2591
|
totalTurns: 0,
|
|
2586
|
-
durationMs: Date.now() -
|
|
2587
|
-
error:
|
|
2592
|
+
durationMs: Date.now() - h,
|
|
2593
|
+
error: f instanceof Error ? f.message : "Unknown error"
|
|
2588
2594
|
});
|
|
2589
2595
|
} catch {
|
|
2590
2596
|
}
|
|
2591
2597
|
yield {
|
|
2592
2598
|
type: "error",
|
|
2593
2599
|
data: {
|
|
2594
|
-
message: Fe(
|
|
2600
|
+
message: Fe(f)
|
|
2595
2601
|
}
|
|
2596
2602
|
};
|
|
2597
2603
|
}
|
|
@@ -12,22 +12,22 @@ Add your **markdown** content here:
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
Use --- for horizontal rules.`,description:"Enter markdown text. Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---)."},{key:"accentColorIndex",label:"Accent Color",type:"paletteColor",defaultValue:0,description:"Color from the dashboard palette for headers, bullets, and links"},{key:"fontSize",label:"Font Size",type:"select",defaultValue:"medium",options:[{value:"small",label:"Small"},{value:"medium",label:"Medium"},{value:"large",label:"Large"}],description:"Overall text size for the markdown content"},{key:"alignment",label:"Text Alignment",type:"select",defaultValue:"left",options:[{value:"left",label:"Left"},{value:"center",label:"Center"},{value:"right",label:"Right"}],description:"Horizontal alignment of the markdown content"},{key:"hideHeader",label:"Hide Header",type:"boolean",defaultValue:!0,description:"Hide the portlet header bar (title and action buttons)"},{key:"transparentBackground",label:"Transparent Background",type:"boolean",defaultValue:!1,description:"Remove card background, border, and shadow for seamless integration as section headers"},{key:"autoHeight",label:"Auto Height",type:"boolean",defaultValue:!0,description:"In row and mobile layouts, size to markdown content instead of fixed row height"},{key:"accentBorder",label:"Accent Border",type:"select",defaultValue:"none",options:[{value:"none",label:"None"},{value:"left",label:"Left"},{value:"top",label:"Top"},{value:"bottom",label:"Bottom"}],description:"Add an accent-colored border on one side of the content"}]},fe={label:"Funnel Chart",description:"Show conversion through sequential steps",useCase:"Best for visualizing user journey funnels, sales pipelines, or multi-step processes",dropZones:[{key:"xAxis",label:"Step Name",description:"Step names (auto-populated from funnel steps)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Steps defined in funnel config"},{key:"yAxis",label:"Step Count",description:"Count at each step (auto-calculated)",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Counts calculated from funnel execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"funnelStyle",label:"Funnel Style",type:"buttonGroup",defaultValue:"bars",options:[{value:"bars",label:"Bars"},{value:"funnel",label:"Funnel"}],description:"Visualization style"},{key:"funnelOrientation",label:"Orientation",type:"buttonGroup",defaultValue:"horizontal",options:[{value:"horizontal",label:"Horizontal"},{value:"vertical",label:"Vertical"}]},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!1,description:"Hide the summary footer showing steps count and overall conversion"},{key:"showFunnelConversion",label:"Show Conversion Rate",type:"boolean",defaultValue:!0,description:"Display step-to-step conversion percentage"},{key:"showFunnelAvgTime",label:"Show Avg Time",type:"boolean",defaultValue:!1,description:"Display average time to convert"},{key:"showFunnelMedianTime",label:"Show Median Time",type:"boolean",defaultValue:!1,description:"Display median time to convert"},{key:"showFunnelP90Time",label:"Show P90 Time",type:"boolean",defaultValue:!1,description:"Display 90th percentile time to convert"}]},be={label:"Sankey Chart",description:"Show flow between states or steps",useCase:"Best for visualizing user journey flows, path analysis, or state transitions",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"linkOpacity",label:"Link Opacity",type:"buttonGroup",defaultValue:"0.5",options:[{value:"0.3",label:"Light"},{value:"0.5",label:"Medium"},{value:"0.7",label:"Dark"}],description:"Opacity of flow links"},{key:"showNodeLabels",label:"Show Node Labels",type:"boolean",defaultValue:!0,description:"Display labels on flow nodes"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},ge={label:"Sunburst Chart",description:"Show hierarchical flow as radial rings",useCase:"Best for visualizing forward paths from a starting event in a compact radial layout",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"innerRadius",label:"Inner Radius",type:"number",defaultValue:40,min:0,max:100,step:10,description:"Size of the center hole (0 for full circle)"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},xe={label:"Heatmap",description:"Visualize intensity across two dimensions",useCase:"Best for showing patterns in matrix data like correlations, schedules, or category comparisons",dropZones:[{key:"xAxis",label:"Columns (X-Axis)",description:"Dimension for column categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"yAxis",label:"Rows (Y-Axis)",description:"Dimension for row categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"valueField",label:"Value (Color Intensity)",description:"Measure that determines cell color",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop one measure here"}],displayOptions:["showLegend","showTooltip"],displayOptionsConfig:[{key:"showLabels",label:"Show Cell Values",type:"boolean",defaultValue:!1,description:"Display values inside each cell"},{key:"cellShape",label:"Cell Shape",type:"select",defaultValue:"rect",options:[{value:"rect",label:"Rectangle"},{value:"circle",label:"Circle"}]},{key:"xAxisFormat",label:"X-Axis Format",type:"axisFormat",description:"Number formatting for X-axis labels"},{key:"yAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for Y-axis labels"},{key:"valueFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for cell values and legend"}],validate:i=>i.xAxis?.length?i.yAxis?.length?i.valueField?.length?{isValid:!0}:{isValid:!1,message:"Value measure required"}:{isValid:!1,message:"Y-axis dimension required"}:{isValid:!1,message:"X-axis dimension required"}},we={label:"Retention Matrix",dropZones:[],displayOptionsConfig:[{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the color intensity legend"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Cohort retention matrix visualization",useCase:"Visualize user retention over time by cohort"},ke={label:"Retention Chart",dropZones:[],displayOptionsConfig:[{key:"retentionDisplayMode",label:"Display Mode",type:"select",defaultValue:"line",options:[{value:"line",label:"Line Chart"},{value:"heatmap",label:"Heatmap Table"},{value:"combined",label:"Combined"}],description:"Choose how to visualize retention data"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the legend for breakdown segments"},{key:"showGrid",label:"Show Grid",type:"boolean",defaultValue:!0,description:"Show grid lines on the chart"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Combined retention visualization with line chart and heatmap modes",useCase:"Visualize user retention over time with optional breakdown segmentation"},ve={label:"Box Plot",description:"Show statistical distribution (median, IQR, whiskers) across categories",useCase:"Best for P&L spread per symbol, trade size distribution, latency distribution across platforms",displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Groups)",description:"Dimension to group boxes by (e.g. symbol, platform)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Measures)",description:"Drop 1 measure for auto mode, 3 for avg/stddev/median mode, or 5 for min/q1/median/q3/max mode",mandatory:!0,maxItems:5,acceptTypes:["measure"],emptyText:"Drop 1, 3, or 5 measures here"}],displayOptionsConfig:[{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the value axis"}]},Ce={label:"Waterfall Chart",description:"Show cumulative effect of sequential positive and negative values",useCase:"Best for P&L decomposition, cash flow analysis, budget variance, or any sequential contribution breakdown",clickableElements:{bar:!0},displayOptions:["showTooltip","hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Categories)",description:"Dimension labels for each bar segment (e.g. symbol, transaction type)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Value)",description:"Single measure whose values are summed cumulatively",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop a measure here"}],displayOptionsConfig:[{key:"showTotal",label:"Show Total Bar",type:"boolean",defaultValue:!0,description:"Append a final bar showing the running total"},{key:"showConnectorLine",label:"Show Connector Line",type:"boolean",defaultValue:!0,description:"Draw a dashed step-line connecting bar tops"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display the value above each bar segment"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},Te={label:"Candlestick Chart",description:"Financial candlestick chart showing open/close body and high/low wicks",useCase:"Best for EOD quotes (bid/ask spread per date/symbol), markout distribution bands, or OHLC price data",clickableElements:{bar:!0},displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Time / Category)",description:"Time dimension or category for each candle (e.g. date, symbol)",mandatory:!0,maxItems:1,acceptTypes:["timeDimension","dimension"],emptyText:"Drop a time or dimension here"},{key:"yAxis",label:"OHLC Measures (open, close, high, low)",description:"Drop 2–4 measures in order: open, close, high, low (OHLC mode). For range mode drop 2: high, low.",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here"}],displayOptionsConfig:[{key:"rangeMode",label:"Chart Mode",type:"select",defaultValue:"ohlc",options:[{value:"ohlc",label:"OHLC (open, close, high, low)"},{value:"range",label:"Range (high, low / bid, ask)"}],description:"OHLC: 4 measures. Range: 2 measures (high + low)."},{key:"bullColor",label:"Bullish Colour",type:"color",defaultValue:"#22c55e",description:"Candle colour when close ≥ open"},{key:"bearColor",label:"Bearish Colour",type:"color",defaultValue:"#ef4444",description:"Candle colour when close < open"},{key:"showWicks",label:"Show Wicks",type:"boolean",defaultValue:!0,description:"Draw high/low wicks above and below the body"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the price axis"}]},Ae={label:"Measure Profile",description:"Plot N measures as sequential X-axis points to visualise a profile or shape across intervals",useCase:"Best for markout interval analysis (e.g. avgMinus2m → avgAtEvent → avgPlus2h), metric profiles, or any pattern across ordered measures",displayOptions:["showLegend","showTooltip","hideHeader"],dropZones:[{key:"yAxis",label:"Measures (X-Axis Order)",description:"Add 2 or more measures — they become the X-axis categories in the order listed",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here (displayed left → right)"},{key:"series",label:"Series (Split into Multiple Lines)",description:"Dimension to split data into separate profile lines (e.g. symbol, platform)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop a dimension here to create multiple lines"}],displayOptionsConfig:[{key:"showReferenceLineAtZero",label:"Show Zero Reference Line",type:"boolean",defaultValue:!0,description:"Draw a dashed line at Y = 0"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display value at each data point"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show series legend (only visible with a Series dimension)"},{key:"lineType",label:"Line Interpolation",type:"select",defaultValue:"monotone",options:[{value:"monotone",label:"Smooth (monotone)"},{value:"linear",label:"Linear"},{value:"step",label:"Step"}],description:"How data points are connected"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},De={label:"Gauge Chart",description:"Half-circle arc gauge for a single KPI value versus a maximum target",useCase:"Best for high-water marks vs equity, margin utilisation, or any single value progress toward a goal",clickableElements:{},displayOptions:["hideHeader"],dropZones:[{key:"yAxis",label:"Value Measure",description:"Current value to display on the gauge (e.g. current equity, margin used)",mandatory:!0,maxItems:2,acceptTypes:["measure"],emptyText:"Drop 1 measure here (optional 2nd for dynamic max)"}],displayOptionsConfig:[{key:"minValue",label:"Minimum Value",type:"number",defaultValue:0,description:"Lower bound of the gauge arc (default 0)"},{key:"maxValue",label:"Maximum Value (static)",type:"number",description:"Upper bound of the gauge. Leave empty to use yAxis[1] or default 100"},{key:"thresholds",label:"Threshold Bands",type:"string",placeholder:'[{"value":0.33,"color":"#22c55e"},{"value":0.66,"color":"#f59e0b"},{"value":1,"color":"#ef4444"}]',description:"Array of {value (0–1 fraction), color} bands shown as outer arc markers"},{key:"showCenterLabel",label:"Show Centre Label",type:"boolean",defaultValue:!0,description:"Display current value and field name in the centre of the gauge"},{key:"showPercentage",label:"Show as Percentage",type:"boolean",defaultValue:!1,description:"Display value as % of max instead of raw number"},{key:"leftYAxisFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for the displayed value and axis labels"}]},L={bar:te,line:ae,area:se,pie:ie,scatter:oe,bubble:re,radar:ne,radialBar:le,treemap:de,table:pe,activityGrid:ue,kpiNumber:ce,kpiDelta:me,kpiText:ye,markdown:he,funnel:fe,sankey:be,sunburst:ge,heatmap:xe,retentionHeatmap:we,retentionCombined:ke,boxPlot:ve,waterfall:Ce,candlestick:Te,measureProfile:Ae,gauge:De};function Se(i,t,a){const s=L[i];if(!s)return{isValid:!0,errors:[]};if(s.skipQuery)return{isValid:!0,errors:[]};const n=[];for(const u of s.dropZones){if(!u.mandatory)continue;const e=t?.[u.key];if(!(Array.isArray(e)?e.length>0:!!e)){const l=u.acceptTypes?.join("/")??"fields";n.push(`chartConfig.${u.key} is required for ${i} chart (${u.label}). Accepts: ${l}.`)}}if(i==="bar"){const u=t?.xAxis;if(!(Array.isArray(u)?u.length>0:!!u)){const r=a.dimensions??[],l=a.timeDimensions??[];r.length>0||l.length>0?n.push("chartConfig.xAxis is required for bar charts. Put a dimension in xAxis so bars have category labels."):n.push('Bar charts need an xAxis dimension for category labels. Add a dimension to the query or use "table" chart type instead.')}}if(t?.xAxis&&t?.series){const u=new Set(Array.isArray(t.xAxis)?t.xAxis:[t.xAxis]),r=(Array.isArray(t.series)?t.series:[t.series]).filter(l=>u.has(l));r.length>0&&n.push(`chartConfig.series must not contain the same field as xAxis (found: ${r.join(", ")}). The series field is only for splitting into grouped/stacked sub-series by a DIFFERENT dimension. Remove the duplicate from series.`)}return{isValid:n.length===0,errors:n}}function Fe(i,t,a){const s=L[i];if(!s)return t??{};const n={...t},u=a.measures??[],e=a.dimensions??[],l=(a.timeDimensions??[]).map(o=>o.dimension);for(const o of s.dropZones){const m=n[o.key];if(Array.isArray(m)?m.length>0:!!m)continue;const y=o.acceptTypes??[];if(o.key==="sizeField"||o.key==="colorField"){if(y.includes("measure")){const g=new Set;for(const C of s.dropZones){if(C.key===o.key)continue;const k=n[C.key];Array.isArray(k)?k.forEach(E=>g.add(E)):typeof k=="string"&&g.add(k)}const A=u.filter(C=>!g.has(C));A.length>0&&(n[o.key]=A[0])}continue}const d=[];if(y.includes("dimension")&&d.push(...e),y.includes("timeDimension")&&d.push(...l),y.includes("measure")&&d.push(...u),d.length===0)continue;let x=d;if(o.key==="series"){const g=new Set(Array.isArray(n.xAxis)?n.xAxis:n.xAxis?[n.xAxis]:[]);if(x=d.filter(A=>!g.has(A)),x.length===0)continue}const b=o.maxItems??1/0,c=x.slice(0,b);c.length>0&&(n[o.key]=c)}return n}function Ve(i){const t=[`
|
|
16
|
-
Chart config requirements by type:`];for(const a of i){const s=L[a];if(!s)continue;const n=s.description??"",u=s.useCase??"",e=[n,u].filter(Boolean).join(". "),r=e?` — ${e}.`:"",l=s.dropZones.filter(
|
|
15
|
+
Use --- for horizontal rules.`,description:"Enter markdown text. Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---)."},{key:"accentColorIndex",label:"Accent Color",type:"paletteColor",defaultValue:0,description:"Color from the dashboard palette for headers, bullets, and links"},{key:"fontSize",label:"Font Size",type:"select",defaultValue:"medium",options:[{value:"small",label:"Small"},{value:"medium",label:"Medium"},{value:"large",label:"Large"}],description:"Overall text size for the markdown content"},{key:"alignment",label:"Text Alignment",type:"select",defaultValue:"left",options:[{value:"left",label:"Left"},{value:"center",label:"Center"},{value:"right",label:"Right"}],description:"Horizontal alignment of the markdown content"},{key:"hideHeader",label:"Hide Header",type:"boolean",defaultValue:!0,description:"Hide the portlet header bar (title and action buttons)"},{key:"transparentBackground",label:"Transparent Background",type:"boolean",defaultValue:!1,description:"Remove card background, border, and shadow for seamless integration as section headers"},{key:"autoHeight",label:"Auto Height",type:"boolean",defaultValue:!0,description:"In row and mobile layouts, size to markdown content instead of fixed row height"},{key:"accentBorder",label:"Accent Border",type:"select",defaultValue:"none",options:[{value:"none",label:"None"},{value:"left",label:"Left"},{value:"top",label:"Top"},{value:"bottom",label:"Bottom"}],description:"Add an accent-colored border on one side of the content"}]},fe={label:"Funnel Chart",description:"Show conversion through sequential steps",useCase:"Best for visualizing user journey funnels, sales pipelines, or multi-step processes",dropZones:[{key:"xAxis",label:"Step Name",description:"Step names (auto-populated from funnel steps)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Steps defined in funnel config"},{key:"yAxis",label:"Step Count",description:"Count at each step (auto-calculated)",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Counts calculated from funnel execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"funnelStyle",label:"Funnel Style",type:"buttonGroup",defaultValue:"bars",options:[{value:"bars",label:"Bars"},{value:"funnel",label:"Funnel"}],description:"Visualization style"},{key:"funnelOrientation",label:"Orientation",type:"buttonGroup",defaultValue:"horizontal",options:[{value:"horizontal",label:"Horizontal"},{value:"vertical",label:"Vertical"}]},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!1,description:"Hide the summary footer showing steps count and overall conversion"},{key:"showFunnelConversion",label:"Show Conversion Rate",type:"boolean",defaultValue:!0,description:"Display step-to-step conversion percentage"},{key:"showFunnelAvgTime",label:"Show Avg Time",type:"boolean",defaultValue:!1,description:"Display average time to convert"},{key:"showFunnelMedianTime",label:"Show Median Time",type:"boolean",defaultValue:!1,description:"Display median time to convert"},{key:"showFunnelP90Time",label:"Show P90 Time",type:"boolean",defaultValue:!1,description:"Display 90th percentile time to convert"}]},be={label:"Sankey Chart",description:"Show flow between states or steps",useCase:"Best for visualizing user journey flows, path analysis, or state transitions",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"linkOpacity",label:"Link Opacity",type:"buttonGroup",defaultValue:"0.5",options:[{value:"0.3",label:"Light"},{value:"0.5",label:"Medium"},{value:"0.7",label:"Dark"}],description:"Opacity of flow links"},{key:"showNodeLabels",label:"Show Node Labels",type:"boolean",defaultValue:!0,description:"Display labels on flow nodes"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},ge={label:"Sunburst Chart",description:"Show hierarchical flow as radial rings",useCase:"Best for visualizing forward paths from a starting event in a compact radial layout",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"innerRadius",label:"Inner Radius",type:"number",defaultValue:40,min:0,max:100,step:10,description:"Size of the center hole (0 for full circle)"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},xe={label:"Heatmap",description:"Visualize intensity across two dimensions",useCase:"Best for showing patterns in matrix data like correlations, schedules, or category comparisons",dropZones:[{key:"xAxis",label:"Columns (X-Axis)",description:"Dimension for column categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"yAxis",label:"Rows (Y-Axis)",description:"Dimension for row categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"valueField",label:"Value (Color Intensity)",description:"Measure that determines cell color",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop one measure here"}],displayOptions:["showLegend","showTooltip"],displayOptionsConfig:[{key:"showLabels",label:"Show Cell Values",type:"boolean",defaultValue:!1,description:"Display values inside each cell"},{key:"cellShape",label:"Cell Shape",type:"select",defaultValue:"rect",options:[{value:"rect",label:"Rectangle"},{value:"circle",label:"Circle"}]},{key:"xAxisFormat",label:"X-Axis Format",type:"axisFormat",description:"Number formatting for X-axis labels"},{key:"yAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for Y-axis labels"},{key:"valueFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for cell values and legend"}],validate:i=>i.xAxis?.length?i.yAxis?.length?i.valueField?.length?{isValid:!0}:{isValid:!1,message:"Value measure required"}:{isValid:!1,message:"Y-axis dimension required"}:{isValid:!1,message:"X-axis dimension required"}},we={label:"Retention Matrix",dropZones:[],displayOptionsConfig:[{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the color intensity legend"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Cohort retention matrix visualization",useCase:"Visualize user retention over time by cohort"},ke={label:"Retention Chart",dropZones:[],displayOptionsConfig:[{key:"retentionDisplayMode",label:"Display Mode",type:"select",defaultValue:"line",options:[{value:"line",label:"Line Chart"},{value:"heatmap",label:"Heatmap Table"},{value:"combined",label:"Combined"}],description:"Choose how to visualize retention data"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the legend for breakdown segments"},{key:"showGrid",label:"Show Grid",type:"boolean",defaultValue:!0,description:"Show grid lines on the chart"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Combined retention visualization with line chart and heatmap modes",useCase:"Visualize user retention over time with optional breakdown segmentation"},ve={label:"Box Plot",description:"Show statistical distribution (median, IQR, whiskers) across categories",useCase:"Best for P&L spread per symbol, trade size distribution, latency distribution across platforms",displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Groups)",description:"Dimension to group boxes by (e.g. symbol, platform)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Measures)",description:"Drop 1 measure for auto mode, 3 for avg/stddev/median mode, or 5 for min/q1/median/q3/max mode",mandatory:!0,maxItems:5,acceptTypes:["measure"],emptyText:"Drop 1, 3, or 5 measures here"}],displayOptionsConfig:[{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the value axis"}]},Ce={label:"Waterfall Chart",description:"Show cumulative effect of sequential positive and negative values",useCase:"Best for P&L decomposition, cash flow analysis, budget variance, or any sequential contribution breakdown",clickableElements:{bar:!0},displayOptions:["showTooltip","hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Categories)",description:"Dimension labels for each bar segment (e.g. symbol, transaction type)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Value)",description:"Single measure whose values are summed cumulatively",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop a measure here"}],displayOptionsConfig:[{key:"showTotal",label:"Show Total Bar",type:"boolean",defaultValue:!0,description:"Append a final bar showing the running total"},{key:"showConnectorLine",label:"Show Connector Line",type:"boolean",defaultValue:!0,description:"Draw a dashed step-line connecting bar tops"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display the value above each bar segment"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},Te={label:"Candlestick Chart",description:"Financial candlestick chart showing open/close body and high/low wicks",useCase:"Best for EOD quotes (bid/ask spread per date/symbol), markout distribution bands, or OHLC price data",clickableElements:{bar:!0},displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Time / Category)",description:"Time dimension or category for each candle (e.g. date, symbol)",mandatory:!0,maxItems:1,acceptTypes:["timeDimension","dimension"],emptyText:"Drop a time or dimension here"},{key:"yAxis",label:"OHLC Measures (open, close, high, low)",description:"Drop 2–4 measures in order: open, close, high, low (OHLC mode). For range mode drop 2: high, low.",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here"}],displayOptionsConfig:[{key:"rangeMode",label:"Chart Mode",type:"select",defaultValue:"ohlc",options:[{value:"ohlc",label:"OHLC (open, close, high, low)"},{value:"range",label:"Range (high, low / bid, ask)"}],description:"OHLC: 4 measures. Range: 2 measures (high + low)."},{key:"bullColor",label:"Bullish Colour",type:"color",defaultValue:"#22c55e",description:"Candle colour when close ≥ open"},{key:"bearColor",label:"Bearish Colour",type:"color",defaultValue:"#ef4444",description:"Candle colour when close < open"},{key:"showWicks",label:"Show Wicks",type:"boolean",defaultValue:!0,description:"Draw high/low wicks above and below the body"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the price axis"}]},Ae={label:"Measure Profile",description:"Plot N measures as sequential X-axis points to visualise a profile or shape across intervals",useCase:"Best for markout interval analysis (e.g. avgMinus2m → avgAtEvent → avgPlus2h), metric profiles, or any pattern across ordered measures",displayOptions:["showLegend","showTooltip","hideHeader"],dropZones:[{key:"yAxis",label:"Measures (X-Axis Order)",description:"Add 2 or more measures — they become the X-axis categories in the order listed",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here (displayed left → right)"},{key:"series",label:"Series (Split into Multiple Lines)",description:"Dimension to split data into separate profile lines (e.g. symbol, platform)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop a dimension here to create multiple lines"}],displayOptionsConfig:[{key:"showReferenceLineAtZero",label:"Show Zero Reference Line",type:"boolean",defaultValue:!0,description:"Draw a dashed line at Y = 0"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display value at each data point"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show series legend (only visible with a Series dimension)"},{key:"lineType",label:"Line Interpolation",type:"select",defaultValue:"monotone",options:[{value:"monotone",label:"Smooth (monotone)"},{value:"linear",label:"Linear"},{value:"step",label:"Step"}],description:"How data points are connected"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},De={label:"Gauge Chart",description:"Half-circle arc gauge for a single KPI value versus a maximum target",useCase:"Best for high-water marks vs equity, margin utilisation, or any single value progress toward a goal",clickableElements:{},displayOptions:["hideHeader"],dropZones:[{key:"yAxis",label:"Value Measure",description:"Current value to display on the gauge (e.g. current equity, margin used)",mandatory:!0,maxItems:2,acceptTypes:["measure"],emptyText:"Drop 1 measure here (optional 2nd for dynamic max)"}],displayOptionsConfig:[{key:"minValue",label:"Minimum Value",type:"number",defaultValue:0,description:"Lower bound of the gauge arc (default 0)"},{key:"maxValue",label:"Maximum Value (static)",type:"number",description:"Upper bound of the gauge. Leave empty to use yAxis[1] or default 100"},{key:"thresholds",label:"Threshold Bands",type:"string",placeholder:'[{"value":0.33,"color":"#22c55e"},{"value":0.66,"color":"#f59e0b"},{"value":1,"color":"#ef4444"}]',description:"Array of {value (0–1 fraction), color} bands shown as outer arc markers"},{key:"showCenterLabel",label:"Show Centre Label",type:"boolean",defaultValue:!0,description:"Display current value and field name in the centre of the gauge"},{key:"showPercentage",label:"Show as Percentage",type:"boolean",defaultValue:!1,description:"Display value as % of max instead of raw number"},{key:"leftYAxisFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for the displayed value and axis labels"}]},L={bar:te,line:ae,area:se,pie:ie,scatter:oe,bubble:re,radar:ne,radialBar:le,treemap:de,table:pe,activityGrid:ue,kpiNumber:ce,kpiDelta:me,kpiText:ye,markdown:he,funnel:fe,sankey:be,sunburst:ge,heatmap:xe,retentionHeatmap:we,retentionCombined:ke,boxPlot:ve,waterfall:Ce,candlestick:Te,measureProfile:Ae,gauge:De};function Se(i,t,a){const s=L[i];if(!s)return{isValid:!0,errors:[]};if(s.skipQuery)return{isValid:!0,errors:[]};const n=[];for(const u of s.dropZones){if(!u.mandatory)continue;const e=t?.[u.key];if(!(Array.isArray(e)?e.length>0:!!e)){const l=u.acceptTypes?.join("/")??"fields";n.push(`chartConfig.${u.key} is required for ${i} chart (${u.label}). Accepts: ${l}.`)}}if(i==="bar"){const u=t?.xAxis;if(!(Array.isArray(u)?u.length>0:!!u)){const r=a.dimensions??[],l=a.timeDimensions??[];r.length>0||l.length>0?n.push("chartConfig.xAxis is required for bar charts. Put a dimension in xAxis so bars have category labels."):n.push('Bar charts need an xAxis dimension for category labels. Add a dimension to the query or use "table" chart type instead.')}}if(t?.xAxis&&t?.series){const u=new Set(Array.isArray(t.xAxis)?t.xAxis:[t.xAxis]),r=(Array.isArray(t.series)?t.series:[t.series]).filter(l=>u.has(l));r.length>0&&n.push(`chartConfig.series must not contain the same field as xAxis (found: ${r.join(", ")}). The series field is only for splitting into grouped/stacked sub-series by a DIFFERENT dimension. Remove the duplicate from series.`)}return{isValid:n.length===0,errors:n}}function Fe(i,t,a){const s=L[i];if(!s)return t??{};const n={...t},u=a.measures??[],e=a.dimensions??[],l=(a.timeDimensions??[]).map(o=>o.dimension);for(const o of s.dropZones){const h=n[o.key];if(Array.isArray(h)?h.length>0:!!h)continue;const c=o.acceptTypes??[];if(o.key==="sizeField"||o.key==="colorField"){if(c.includes("measure")){const g=new Set;for(const C of s.dropZones){if(C.key===o.key)continue;const k=n[C.key];Array.isArray(k)?k.forEach(E=>g.add(E)):typeof k=="string"&&g.add(k)}const A=u.filter(C=>!g.has(C));A.length>0&&(n[o.key]=A[0])}continue}const d=[];if(c.includes("dimension")&&d.push(...e),c.includes("timeDimension")&&d.push(...l),c.includes("measure")&&d.push(...u),d.length===0)continue;let x=d;if(o.key==="series"){const g=new Set(Array.isArray(n.xAxis)?n.xAxis:n.xAxis?[n.xAxis]:[]);if(x=d.filter(A=>!g.has(A)),x.length===0)continue}const b=o.maxItems??1/0,y=x.slice(0,b);y.length>0&&(n[o.key]=y)}return n}function Ve(i){const t=[`
|
|
16
|
+
Chart config requirements by type:`];for(const a of i){const s=L[a];if(!s)continue;const n=s.description??"",u=s.useCase??"",e=[n,u].filter(Boolean).join(". "),r=e?` — ${e}.`:"",l=s.dropZones.filter(h=>h.mandatory);if(l.length===0&&!s.skipQuery){t.push(` ${a}${r} chartConfig auto-inferred from query.`);continue}if(s.skipQuery){t.push(` ${a}${r} No query needed.`);continue}const o=l.map(h=>{const m=h.acceptTypes?.join("/")??"any",c=h.maxItems?` (max ${h.maxItems})`:"";return`${h.key}=[${m}]${c}`});t.push(` ${a}${r} Requires ${o.join(", ")}.`)}return t.join(`
|
|
17
17
|
`)}const P=["bar","line","area","pie","scatter","radar","bubble","table","kpiNumber","kpiDelta","funnel","heatmap","sankey","sunburst","retentionHeatmap","retentionCombined","boxPlot","markdown"];function Ee(){return[{name:"discover_cubes",description:"Search for available data cubes by topic or intent. Call this FIRST to understand what data is available. Returns cube names, measures, dimensions, and relationships.",input_schema:{type:"object",properties:{topic:{type:"string",description:'Keyword to search (e.g., "sales", "employees")'},intent:{type:"string",description:'Natural language goal (e.g., "analyze productivity trends")'},limit:{type:"number",description:"Max results (default: 10)"},minScore:{type:"number",description:"Min relevance 0-1 (default: 0.1)"}}}},{name:"get_cube_metadata",description:"Get full metadata for all registered cubes including all measures, dimensions, types, and relationships. Use this for detailed schema information.",input_schema:{type:"object",properties:{}}},{name:"execute_query",description:"Execute a semantic query and return data results. Supports standard queries (measures/dimensions) and analysis modes (funnel/flow/retention). Only provide ONE mode per call.",input_schema:{type:"object",properties:{measures:{type:"array",items:{type:"string",pattern:"^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"},description:'Aggregation measures — MUST be "CubeName.measureName" format (e.g., ["PullRequests.count", "Issues.openCount"]). NEVER use just the cube name.'},dimensions:{type:"array",items:{type:"string",pattern:"^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"},description:'Grouping dimensions — MUST be "CubeName.dimensionName" format (e.g., ["Teams.name", "Employees.department"]). NEVER use just the cube name.'},filters:{type:"array",items:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"]},description:"Filter conditions"},timeDimensions:{type:"array",items:{type:"object",properties:{dimension:{type:"string"},granularity:{type:"string"},dateRange:{}},required:["dimension"]},description:"Time dimensions with optional granularity"},order:{type:"object",description:'Sort order (e.g., {"Employees.count": "desc"})'},limit:{type:"number",description:"Row limit"},funnel:{type:"object",properties:{bindingKey:{type:"string",description:'Entity binding key (e.g., "Events.userId")'},timeDimension:{type:"string",description:'Time dimension (e.g., "Events.timestamp")'},steps:{type:"array",items:{type:"object",properties:{name:{type:"string"},filter:{},timeToConvert:{type:"string",description:'ISO 8601 duration (e.g., "P7D")'}},required:["name"]},description:"Funnel steps (min 2)"},includeTimeMetrics:{type:"boolean"},globalTimeWindow:{type:"string"}},required:["bindingKey","timeDimension","steps"],description:"Funnel analysis config. When provided, measures/dimensions are ignored."},flow:{type:"object",properties:{bindingKey:{type:"string"},timeDimension:{type:"string"},eventDimension:{type:"string",description:"Dimension whose values become node labels"},startingStep:{type:"object",properties:{name:{type:"string"},filter:{}},required:["name"]},stepsBefore:{type:"number",description:"Steps before starting step (0-5)"},stepsAfter:{type:"number",description:"Steps after starting step (0-5)"},entityLimit:{type:"number"},outputMode:{type:"string",enum:["sankey","sunburst"]}},required:["bindingKey","timeDimension","eventDimension","startingStep"],description:"Flow analysis config. When provided, measures/dimensions are ignored."},retention:{type:"object",properties:{timeDimension:{type:"string"},bindingKey:{type:"string"},dateRange:{type:"object",properties:{start:{type:"string",description:"YYYY-MM-DD"},end:{type:"string",description:"YYYY-MM-DD"}},required:["start","end"]},granularity:{type:"string",enum:["day","week","month"]},periods:{type:"number"},retentionType:{type:"string",enum:["classic","rolling"]},cohortFilters:{},activityFilters:{},breakdownDimensions:{type:"array",items:{type:"string"}}},required:["timeDimension","bindingKey","dateRange","granularity","periods"],description:"Retention analysis config. When provided, measures/dimensions are ignored."}}}},{name:"add_portlet",description:`Add a chart visualization to the notebook.
|
|
18
18
|
`+Ve(P)+`
|
|
19
|
-
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:P,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:P,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 _e(i){const{semanticLayer:t,securityContext:a}=i,s=new Map;s.set("discover_cubes",async e=>{const r=await B.handleDiscover(t,{topic:e.topic,intent:e.intent,limit:e.limit,minScore:e.minScore});return{result:JSON.stringify(r,null,2)}}),s.set("get_cube_metadata",async()=>{const e=t.getMetadata();return{result:JSON.stringify(e,null,2)}});const n=new Map;for(const e of t.getMetadata())n.set(e.name,{measures:(e.measures||[]).map(r=>r.name),dimensions:(e.dimensions||[]).map(r=>r.name)});const u=(e,r)=>{const l=n.get(e),o=r==="measures"?l?.measures:l?.dimensions;return!o||o.length===0?"":` Available ${r}: ${o.slice(0,5).map(
|
|
19
|
+
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:P,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:P,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 _e(i){const{semanticLayer:t,securityContext:a}=i,s=new Map;s.set("discover_cubes",async e=>{const r=await B.handleDiscover(t,{topic:e.topic,intent:e.intent,limit:e.limit,minScore:e.minScore});return{result:JSON.stringify(r,null,2)}}),s.set("get_cube_metadata",async()=>{const e=t.getMetadata();return{result:JSON.stringify(e,null,2)}});const n=new Map;for(const e of t.getMetadata())n.set(e.name,{measures:(e.measures||[]).map(r=>r.name),dimensions:(e.dimensions||[]).map(r=>r.name)});const u=(e,r)=>{const l=n.get(e),o=r==="measures"?l?.measures:l?.dimensions;return!o||o.length===0?"":` Available ${r}: ${o.slice(0,5).map(h=>`"${h}"`).join(", ")}`};return s.set("execute_query",async e=>{try{const r=(m,c)=>{if(!Array.isArray(m))return;const d=[],x=[];for(const b of m){if(typeof b!="string"){x.push(b);continue}const y=b.split(".");if(y.length===1){d.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b,c)}`);continue}if(y.length===3&&y[0]===y[1]){const g=`${y[0]}.${y[2]}`;x.push(g);continue}if(y.length===2&&y[0]===y[1]){d.push(`"${b}" is WRONG — "${y[0]}" is the cube name, not a ${c.replace(/s$/,"")}.${u(y[0],c)}`);continue}x.push(b)}if(d.length>0)throw new Error(`Invalid ${c}:
|
|
20
20
|
${d.join(`
|
|
21
|
-
`)}`);return x};e.measures=r(e.measures,"measures")??e.measures,e.dimensions=r(e.dimensions,"dimensions")??e.dimensions;const l=
|
|
21
|
+
`)}`);return x};e.measures=r(e.measures,"measures")??e.measures,e.dimensions=r(e.dimensions,"dimensions")??e.dimensions;const l=m=>{const c=m.split(".");return c.length===3&&c[0]===c[1]?`${c[0]}.${c[2]}`:m};if(Array.isArray(e.filters))for(const m of e.filters)typeof m.member=="string"&&(m.member=l(m.member));if(Array.isArray(e.timeDimensions))for(const m of e.timeDimensions)typeof m.dimension=="string"&&(m.dimension=l(m.dimension));if(e.order&&typeof e.order=="object"&&!Array.isArray(e.order)){const m={};for(const[c,d]of Object.entries(e.order))m[l(c)]=d;e.order=m}let o;e.funnel?o={funnel:e.funnel}:e.flow?o={flow:e.flow}:e.retention?o={retention:e.retention}:o={measures:e.measures,dimensions:e.dimensions,filters:e.filters,timeDimensions:e.timeDimensions,order:e.order,limit:e.limit};const h=await B.handleLoad(t,a,{query:o});return{result:JSON.stringify({rowCount:h.data.length,data:h.data,annotation:h.annotation},null,2)}}catch(r){const l={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: ${r instanceof Error?r.message:"Unknown error"}
|
|
22
22
|
|
|
23
23
|
Attempted query:
|
|
24
|
-
${JSON.stringify(l,null,2)}`,isError:!0}}}),s.set("add_portlet",async e=>{const l={number:"kpiNumber",retention:"retentionHeatmap"}[e.chartType]??e.chartType;let o;try{o=JSON.parse(e.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const
|
|
25
|
-
${
|
|
24
|
+
${JSON.stringify(l,null,2)}`,isError:!0}}}),s.set("add_portlet",async e=>{const l={number:"kpiNumber",retention:"retentionHeatmap"}[e.chartType]??e.chartType;let o;try{o=JSON.parse(e.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const h=t.validateQuery(o);if(!h.isValid)return{result:`Invalid query — fix these errors and retry:
|
|
25
|
+
${h.errors.join(`
|
|
26
26
|
`)}
|
|
27
27
|
|
|
28
28
|
Attempted query:
|
|
29
|
-
${JSON.stringify(o,null,2)}`,isError:!0};const
|
|
30
|
-
${
|
|
31
|
-
`)}`,isError:!0};
|
|
29
|
+
${JSON.stringify(o,null,2)}`,isError:!0};const m=!!(o.funnel||o.flow||o.retention);let c;if(m)c=e.chartConfig??{};else{const b=Fe(l,e.chartConfig,o),y=Se(l,b,o);if(!y.isValid)return{result:`Chart config invalid — fix these errors and retry:
|
|
30
|
+
${y.errors.join(`
|
|
31
|
+
`)}`,isError:!0};c=b}const d=`portlet-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,x={id:d,title:e.title,query:e.query,chartType:l,chartConfig:c,displayConfig:e.displayConfig};return{result:`Portlet "${e.title}" added to notebook (id: ${d}, chart: ${l}). [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:x}}}),s.set("add_markdown",async e=>{const r=`markdown-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,l={id:r,title:e.title,content:e.content};return{result:`Markdown block added to notebook (id: ${r}). [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:l}}}),s.set("save_as_dashboard",async e=>{try{const r=e.portlets;if(!r||r.length===0)return{result:"Dashboard must contain at least one portlet.",isError:!0};const l=[];for(const d of r){if(d.chartType==="markdown")continue;const b=d.query;if(!b){l.push(`Portlet "${d.title}": missing query`);continue}let y;try{y=JSON.parse(b)}catch{l.push(`Portlet "${d.title}": invalid JSON query`);continue}const g=t.validateQuery(y);g.isValid||l.push(`Portlet "${d.title}": ${g.errors.join(", ")}`)}if(l.length>0)return{result:`Dashboard has invalid portlets — fix these errors and retry:
|
|
32
32
|
${l.join(`
|
|
33
|
-
`)}`,isError:!0};const o={portlets:r.map(d=>{const x=d.chartType,b=x==="markdown",
|
|
33
|
+
`)}`,isError:!0};const o={portlets:r.map(d=>{const x=d.chartType,b=x==="markdown",y=b?"query":d.analysisType||"query",g=y==="funnel"?"funnel":y==="flow"?"flow":y==="retention"?"retention":"query",A=d.query||"{}";let C;try{C=JSON.parse(A)}catch{C={}}const k={version:1,analysisType:g,activeView:"chart",charts:{[g]:{chartType:x,chartConfig:d.chartConfig||{},displayConfig:d.displayConfig||{}}},query:b?{}:C};return{id:d.id,title:d.title,analysisConfig:k,dashboardFilterMapping:d.dashboardFilterMapping,w:d.w,h:d.h,x:d.x,y:d.y}}),filters:e.filters,colorPalette:e.colorPalette},h=e.title,m=o.portlets.length,c=o.filters?.length||0;return{result:`Dashboard "${h}" created with ${m} portlets and ${c} filters.`,sideEffect:{type:"dashboard_saved",data:{title:h,description:e.description,dashboardConfig:o}}}}catch(r){return{result:`Failed to save dashboard: ${r instanceof Error?r.message:"Unknown error"}`,isError:!0}}}),s}async function*qe(i){const{message:t,history:a,semanticLayer:s,securityContext:n,agentConfig:u,apiKey:e}=i,r=i.sessionId||crypto.randomUUID(),l=u.observability,o=crypto.randomUUID(),h=Date.now();let m;try{const f=await import("@anthropic-ai/sdk");m=f.default||f.Anthropic||f}catch{yield{type:"error",data:{message:"@anthropic-ai/sdk is required. Install it with: npm install @anthropic-ai/sdk"}};return}const c=new m({apiKey:e}),d=Ee(),x=_e({semanticLayer:s,securityContext:n}),b=s.getMetadata(),y=ee(b),g=u.model||"claude-sonnet-4-6",A=u.maxTurns||25,C=u.maxTokens||4096;try{l?.onChatStart?.({traceId:o,sessionId:r,message:t,model:g,historyLength:a?.length??0})}catch{}const k=[];if(a&&a.length>0){for(const f of a)if(f.role==="user")k.push({role:"user",content:f.content});else if(f.role==="assistant"){const F=[];if(f.content&&F.push({type:"text",text:f.content}),f.toolCalls&&f.toolCalls.length>0){for(const w of f.toolCalls)F.push({type:"tool_use",id:w.id,name:w.name,input:w.input||{}});k.push({role:"assistant",content:F}),k.push({role:"user",content:f.toolCalls.map(w=>({type:"tool_result",tool_use_id:w.id,content:typeof w.result=="string"?w.result:JSON.stringify(w.result??""),...w.status==="error"?{is_error:!0}:{}}))})}else F.length>0&&k.push({role:"assistant",content:f.content})}}k.push({role:"user",content:t});let E=0;try{for(let f=0;f<A;f++){E=f+1;const F=await c.messages.create({model:g,max_tokens:C,system:y,tools:d,messages:k,stream:!0}),w=[];let O=-1,V="",N="",M,R;const j=Date.now();for await(const v of F)switch(v.type){case"content_block_start":{O++;const p=v.content_block;p.type==="tool_use"?(w.push({type:"tool_use",id:p.id,name:p.name,input:{}}),V="",yield{type:"tool_use_start",data:{id:p.id,name:p.name,input:void 0}}):p.type==="text"&&w.push({type:"text",text:""});break}case"content_block_delta":{const p=v.delta;if(p.type==="text_delta"&&p.text){const D=w[O];D&&(D.text=(D.text||"")+p.text),yield{type:"text_delta",data:p.text}}else p.type==="input_json_delta"&&p.partial_json&&(V+=p.partial_json);break}case"content_block_stop":{const p=w[O];if(p?.type==="tool_use"&&V){try{p.input=JSON.parse(V)}catch{p.input={}}V=""}break}case"message_start":{const p=v.message;p?.usage?.input_tokens!=null&&(M=p.usage.input_tokens);break}case"message_delta":{const p=v.delta,D=v.usage;D?.output_tokens!=null&&(R=D.output_tokens),p.stop_reason&&(N=p.stop_reason);break}}try{l?.onGenerationEnd?.({traceId:o,turn:f,model:g,stopReason:N,inputTokens:M,outputTokens:R,durationMs:Date.now()-j})}catch{}if(k.push({role:"assistant",content:w}),N!=="tool_use")break;const _=[];for(const v of w){if(v.type!=="tool_use")continue;const p=v.name,D=v.input||{},S=v.id,Y=x.get(p);if(!Y){_.push({type:"tool_result",tool_use_id:S,content:`Unknown tool: ${p}`,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:`Unknown tool: ${p}`,isError:!0}};continue}const $=Date.now();try{const T=await Y(D);T.sideEffect&&(yield T.sideEffect),_.push({type:"tool_result",tool_use_id:S,content:T.result,...T.isError?{is_error:!0}:{}}),yield{type:"tool_use_result",data:{id:S,name:p,result:T.result,...T.isError?{isError:!0}:{}}};try{l?.onToolEnd?.({traceId:o,turn:f,toolName:p,toolUseId:S,isError:!!T.isError,durationMs:Date.now()-$})}catch{}}catch(T){const z=T instanceof Error?T.message:"Tool execution failed";_.push({type:"tool_result",tool_use_id:S,content:z,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:z,isError:!0}};try{l?.onToolEnd?.({traceId:o,turn:f,toolName:p,toolUseId:S,isError:!0,durationMs:Date.now()-$})}catch{}}}yield{type:"turn_complete",data:{}},k.push({role:"user",content:_})}try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:E,durationMs:Date.now()-h})}catch{}yield{type:"done",data:{sessionId:r||"",traceId:o}}}catch(f){try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:0,durationMs:Date.now()-h,error:f instanceof Error?f.message:"Unknown error"})}catch{}yield{type:"error",data:{message:Ie(f)}}}}function Ie(i){if(!i||!(i instanceof Error))return"Something went wrong. Please try again.";const t=i.message||"",a={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(a[n])return a[n]}if(t.startsWith("{")||t.startsWith("Error: {")){try{const n=JSON.parse(t.replace(/^Error:\s*/,"")),u=n.error?.type||n.type||"";if(a[u])return a[u]}catch{}return"The AI service encountered an error. Please try again."}return t}exports.handleAgentChat=qe;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),a=require("../mcp-transport-8u9G5oNa.cjs"),b=require("../utils.cjs");var D=j=>{const p={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...j},I=(d=>typeof d=="string"?d==="*"?()=>d:c=>d===c?c:null:typeof d=="function"?d:c=>d.includes(c)?c:null)(p.origin),m=(d=>typeof d=="function"?d:Array.isArray(d)?()=>d:()=>[])(p.allowMethods);return async function(c,h){function g(C,i){c.res.headers.set(C,i)}const x=await I(c.req.header("origin")||"",c);if(x&&g("Access-Control-Allow-Origin",x),p.credentials&&g("Access-Control-Allow-Credentials","true"),p.exposeHeaders?.length&&g("Access-Control-Expose-Headers",p.exposeHeaders.join(",")),c.req.method==="OPTIONS"){p.origin!=="*"&&g("Vary","Origin"),p.maxAge!=null&&g("Access-Control-Max-Age",p.maxAge.toString());const C=await m(c.req.header("origin")||"",c);C.length&&g("Access-Control-Allow-Methods",C.join(","));let i=p.allowHeaders;if(!i?.length){const s=c.req.header("Access-Control-Request-Headers");s&&(i=s.split(/\s*,\s*/))}return i?.length&&(g("Access-Control-Allow-Headers",i.join(",")),c.res.headers.append("Vary","Access-Control-Request-Headers")),c.res.headers.delete("Content-Length"),c.res.headers.delete("Content-Type"),new Response(null,{headers:c.res.headers,status:204,statusText:"No Content"})}await h(),p.origin!=="*"&&c.header("Vary","Origin",{append:!0})}};function O(j){const{cubes:v,drizzle:p,schema:I,extractSecurityContext:m,engineType:d,cors:c,basePath:h="/cubejs-api/v1",cache:g,mcp:x={enabled:!0},agent:C}=j;if(!v||v.length===0)throw new Error("At least one cube must be provided in the cubes array");const i=new N.Hono;c&&i.use("/*",D(c));const s=new a.SemanticLayerCompiler({drizzle:p,schema:I,engineType:d,cache:g});if(v.forEach(r=>{s.registerCube(r)}),i.post(`${h}/load`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.get(`${h}/load`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);let n;try{n=JSON.parse(e)}catch{return r.json({error:"Invalid JSON in query parameter"},400)}const o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.post(`${h}/batch`,async r=>{try{const e=await r.req.json(),{queries:n}=e;if(!n||!Array.isArray(n))return r.json({error:'Request body must contain a "queries" array'},400);if(n.length===0)return r.json({error:"Queries array cannot be empty"},400);const o=await m(r),t=r.req.header("x-cache-control")==="no-cache",u=await b.handleBatchRequest(n,o,s,{skipCache:t});return r.json(u)}catch(e){return console.error("Batch execution error:",e),r.json({error:e instanceof Error?e.message:"Batch execution failed"},500)}}),i.get(`${h}/meta`,r=>{try{const e=s.getMetadata();return r.json(b.formatMetaResponse(e))}catch(e){return console.error("Metadata error:",e),r.json({error:e instanceof Error?e.message:"Failed to fetch metadata"},500)}}),i.post(`${h}/sql`,async r=>{try{const e=await r.req.json(),n=await m(r),o=s.validateQuery(e);if(!o.isValid)return r.json({error:`Query validation failed: ${o.errors.join(", ")}`},400);const t=e.measures?.[0]||e.dimensions?.[0];if(!t)return r.json({error:"No measures or dimensions specified"},400);const u=t.split(".")[0],l=await s.generateSQL(u,e,n);return r.json(b.formatSqlResponse(e,l))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.get(`${h}/sql`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);const n=JSON.parse(e),o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=n.measures?.[0]||n.dimensions?.[0];if(!u)return r.json({error:"No measures or dimensions specified"},400);const l=u.split(".")[0],q=await s.generateSQL(l,n,o);return r.json(b.formatSqlResponse(n,q))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.post(`${h}/dry-run`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.get(`${h}/dry-run`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required",valid:!1},400);const n=JSON.parse(e),o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.post(`${h}/explain`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=e.options||{},t=await m(r),u=s.validateQuery(n);if(!u.isValid)return r.json({error:`Query validation failed: ${u.errors.join(", ")}`},400);const l=await s.explainQuery(n,t,o);return r.json(l)}catch(e){return console.error("Explain error:",e),r.json({error:e instanceof Error?e.message:"Explain query failed"},500)}}),C&&i.post(`${h}/agent/chat`,async r=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),a=require("../mcp-transport-8u9G5oNa.cjs"),b=require("../utils.cjs");var D=j=>{const p={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...j},I=(d=>typeof d=="string"?d==="*"?()=>d:c=>d===c?c:null:typeof d=="function"?d:c=>d.includes(c)?c:null)(p.origin),m=(d=>typeof d=="function"?d:Array.isArray(d)?()=>d:()=>[])(p.allowMethods);return async function(c,h){function g(C,i){c.res.headers.set(C,i)}const x=await I(c.req.header("origin")||"",c);if(x&&g("Access-Control-Allow-Origin",x),p.credentials&&g("Access-Control-Allow-Credentials","true"),p.exposeHeaders?.length&&g("Access-Control-Expose-Headers",p.exposeHeaders.join(",")),c.req.method==="OPTIONS"){p.origin!=="*"&&g("Vary","Origin"),p.maxAge!=null&&g("Access-Control-Max-Age",p.maxAge.toString());const C=await m(c.req.header("origin")||"",c);C.length&&g("Access-Control-Allow-Methods",C.join(","));let i=p.allowHeaders;if(!i?.length){const s=c.req.header("Access-Control-Request-Headers");s&&(i=s.split(/\s*,\s*/))}return i?.length&&(g("Access-Control-Allow-Headers",i.join(",")),c.res.headers.append("Vary","Access-Control-Request-Headers")),c.res.headers.delete("Content-Length"),c.res.headers.delete("Content-Type"),new Response(null,{headers:c.res.headers,status:204,statusText:"No Content"})}await h(),p.origin!=="*"&&c.header("Vary","Origin",{append:!0})}};function O(j){const{cubes:v,drizzle:p,schema:I,extractSecurityContext:m,engineType:d,cors:c,basePath:h="/cubejs-api/v1",cache:g,mcp:x={enabled:!0},agent:C}=j;if(!v||v.length===0)throw new Error("At least one cube must be provided in the cubes array");const i=new N.Hono;c&&i.use("/*",D(c));const s=new a.SemanticLayerCompiler({drizzle:p,schema:I,engineType:d,cache:g});if(v.forEach(r=>{s.registerCube(r)}),i.post(`${h}/load`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.get(`${h}/load`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);let n;try{n=JSON.parse(e)}catch{return r.json({error:"Invalid JSON in query parameter"},400)}const o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.post(`${h}/batch`,async r=>{try{const e=await r.req.json(),{queries:n}=e;if(!n||!Array.isArray(n))return r.json({error:'Request body must contain a "queries" array'},400);if(n.length===0)return r.json({error:"Queries array cannot be empty"},400);const o=await m(r),t=r.req.header("x-cache-control")==="no-cache",u=await b.handleBatchRequest(n,o,s,{skipCache:t});return r.json(u)}catch(e){return console.error("Batch execution error:",e),r.json({error:e instanceof Error?e.message:"Batch execution failed"},500)}}),i.get(`${h}/meta`,r=>{try{const e=s.getMetadata();return r.json(b.formatMetaResponse(e))}catch(e){return console.error("Metadata error:",e),r.json({error:e instanceof Error?e.message:"Failed to fetch metadata"},500)}}),i.post(`${h}/sql`,async r=>{try{const e=await r.req.json(),n=await m(r),o=s.validateQuery(e);if(!o.isValid)return r.json({error:`Query validation failed: ${o.errors.join(", ")}`},400);const t=e.measures?.[0]||e.dimensions?.[0];if(!t)return r.json({error:"No measures or dimensions specified"},400);const u=t.split(".")[0],l=await s.generateSQL(u,e,n);return r.json(b.formatSqlResponse(e,l))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.get(`${h}/sql`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);const n=JSON.parse(e),o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=n.measures?.[0]||n.dimensions?.[0];if(!u)return r.json({error:"No measures or dimensions specified"},400);const l=u.split(".")[0],q=await s.generateSQL(l,n,o);return r.json(b.formatSqlResponse(n,q))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.post(`${h}/dry-run`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.get(`${h}/dry-run`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required",valid:!1},400);const n=JSON.parse(e),o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.post(`${h}/explain`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=e.options||{},t=await m(r),u=s.validateQuery(n);if(!u.isValid)return r.json({error:`Query validation failed: ${u.errors.join(", ")}`},400);const l=await s.explainQuery(n,t,o);return r.json(l)}catch(e){return console.error("Explain error:",e),r.json({error:e instanceof Error?e.message:"Explain query failed"},500)}}),C&&i.post(`${h}/agent/chat`,async r=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),n=await r.req.json(),{message:o,sessionId:t,history:u}=n;if(!o||typeof o!="string")return r.json({error:"message is required and must be a string"},400);let l=(C.apiKey||"").trim();if(C.allowClientApiKey){const w=r.req.header("x-agent-api-key");w&&(l=w.trim())}if(!l)return r.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},401);const q=await m(r),R=new TextEncoder,f=new ReadableStream({async start(w){try{const E=e({message:o,sessionId:t,history:u,semanticLayer:s,securityContext:q,agentConfig:C,apiKey:l});for await(const y of E){const S=`data: ${JSON.stringify(y)}
|
|
2
2
|
|
|
3
3
|
`;w.enqueue(R.encode(S))}}catch(E){const y={type:"error",data:{message:E instanceof Error?E.message:"Stream failed"}};w.enqueue(R.encode(`data: ${JSON.stringify(y)}
|
|
4
4
|
|
|
@@ -212,7 +212,7 @@ function te(C) {
|
|
|
212
212
|
}
|
|
213
213
|
}), q && a.post(`${m}/agent/chat`, async (r) => {
|
|
214
214
|
try {
|
|
215
|
-
const { handleAgentChat: e } = await import("../handler-
|
|
215
|
+
const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), n = await r.req.json(), { message: o, sessionId: t, history: c } = n;
|
|
216
216
|
if (!o || typeof o != "string")
|
|
217
217
|
return r.json({ error: "message is required and must be a string" }, 400);
|
|
218
218
|
let u = (q.apiKey || "").trim();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`)),H.enqueue(h.encode(f.serializeSseEvent(x,E))),H.close()}}),O=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive",...g});if(s){const H=R(t,s);Object.entries(H).forEach(([b,P])=>O.set(b,P))}return new r.NextResponse(S,{status:200,headers:O})}return v(x,200,g)}catch(m){if(f.isNotification(p))return process.env.NODE_ENV!=="test"&&console.error("Next.js MCP notification processing error:",m),new r.NextResponse(null,{status:202});process.env.NODE_ENV!=="test"&&console.error("Next.js MCP RPC handler error:",m);const C=m?.code??-32603,g=m?.data,x=m.message||"MCP request failed",h=f.buildJsonRpcError(p.id??null,C,x,g);if(w){const E=new TextEncoder,S=f.primeEventId(),O=new ReadableStream({start(b){b.enqueue(E.encode(`id: ${S}
|
|
4
4
|
|
|
5
|
-
`)),b.enqueue(E.encode(f.serializeSseEvent(h,S))),b.close()}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const b=R(t,s);Object.entries(b).forEach(([P,I])=>H.set(P,I))}return new r.NextResponse(O,{status:200,headers:H})}return v(h,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=j(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-
|
|
5
|
+
`)),b.enqueue(E.encode(f.serializeSseEvent(h,S))),b.close()}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const b=R(t,s);Object.entries(b).forEach(([P,I])=>H.set(P,I))}return new r.NextResponse(O,{status:200,headers:H})}return v(h,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=j(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),y=await t.json(),{message:u,sessionId:p,history:w}=y;if(!u||typeof u!="string")return r.NextResponse.json({error:"message is required and must be a string"},{status:400});let N=(c.apiKey||"").trim();if(c.allowClientApiKey){const x=t.headers.get("x-agent-api-key");x&&(N=x.trim())}if(!N)return r.NextResponse.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},{status:401});const v=await o(t,e),m=new TextEncoder,C=new ReadableStream({async start(x){try{const h=l({message:u,sessionId:p,history:w,semanticLayer:d,securityContext:v,agentConfig:c,apiKey:N});for await(const E of h){const S=`data: ${JSON.stringify(E)}
|
|
6
6
|
|
|
7
7
|
`;x.enqueue(m.encode(S))}}catch(h){const E={type:"error",data:{message:h instanceof Error?h.message:"Stream failed"}};x.enqueue(m.encode(`data: ${JSON.stringify(E)}
|
|
8
8
|
|
|
@@ -519,7 +519,7 @@ function oe(n) {
|
|
|
519
519
|
{ error: "Method not allowed - use POST" },
|
|
520
520
|
{ status: 405 }
|
|
521
521
|
);
|
|
522
|
-
const { handleAgentChat: u } = await import("../handler-
|
|
522
|
+
const { handleAgentChat: u } = await import("../handler-BV4JuWNW.js"), y = await r.json(), { message: l, sessionId: f, history: v } = y;
|
|
523
523
|
if (!l || typeof l != "string")
|
|
524
524
|
return t.json(
|
|
525
525
|
{ error: "message is required and must be a string" },
|
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:
|
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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-cube",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.19",
|
|
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",
|