From 59428443e663ef5c287255acf5d72aefe969097c Mon Sep 17 00:00:00 2001 From: Karthik Kalyanaraman <105607645+karthikscale3@users.noreply.github.com> Date: Sun, 28 Apr 2024 12:20:18 -0700 Subject: [PATCH] Release 1.1.2 (#74) * Pagination bug * Bug fix * chore: add docker cmd * Compatibility fixes for SDK version 2.0.0 (#69) * Pagination bug * Bug fix * Fix for schema changes * Render tool calling * Support for Langgraph, Qdrant & Groq (#73) * Pagination bug * Bug fix * Add langgraph support * QDrant support * Add Groq support * update README * update README --------- Co-authored-by: Darshit Suratwala Co-authored-by: darshit-s3 <119623510+darshit-s3@users.noreply.github.com> --- Dockerfile | 2 + README.md | 3 + components/project/traces/trace-row.tsx | 34 +- components/shared/langgraph-view.tsx | 122 +++++++ components/shared/vendor-metadata.tsx | 48 +++ components/traces/trace_graph.tsx | 6 + docker-compose.yaml | 1 - package-lock.json | 401 ++++++++++++++++++++++-- package.json | 1 + public/groq.png | Bin 0 -> 6254 bytes public/qdrant.png | Bin 0 -> 8110 bytes 11 files changed, 595 insertions(+), 23 deletions(-) create mode 100644 components/shared/langgraph-view.tsx create mode 100644 public/groq.png create mode 100644 public/qdrant.png diff --git a/Dockerfile b/Dockerfile index f5aa40ae..27888e67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,3 +15,5 @@ COPY . . RUN npm install EXPOSE 3000 + +CMD [ "/bin/sh", "-c", "npm run create-tables && npm run dev" ] diff --git a/README.md b/README.md index 5561fbc3..b2c6a731 100644 --- a/README.md +++ b/README.md @@ -213,10 +213,13 @@ Langtrace automatically captures traces from the following vendors: | OpenAI | LLM | :white_check_mark: | :white_check_mark: | | Anthropic | LLM | :white_check_mark: | :white_check_mark: | | Azure OpenAI | LLM | :white_check_mark: | :white_check_mark: | +| Cohere | LLM | :white_check_mark: | :white_check_mark: | +| Groq | LLM | :x: | :white_check_mark: | | Langchain | Framework | :x: | :white_check_mark: | | LlamaIndex | Framework | :white_check_mark: | :white_check_mark: | | Pinecone | Vector Database | :white_check_mark: | :white_check_mark: | | ChromaDB | Vector Database | :white_check_mark: | :white_check_mark: | +| QDrant | Vector Database | :x: | :white_check_mark: | --- diff --git a/components/project/traces/trace-row.tsx b/components/project/traces/trace-row.tsx index 64922495..4116d950 100644 --- a/components/project/traces/trace-row.tsx +++ b/components/project/traces/trace-row.tsx @@ -1,5 +1,6 @@ "use client"; +import LanggraphView from "@/components/shared/langgraph-view"; import { calculateTotalTime, convertTracesToHierarchy, @@ -36,9 +37,16 @@ export const TraceRow = ({ let prompts: any = {}; let responses: any = {}; let cost = { total: 0, input: 0, output: 0 }; + let langgraph = false; for (const span of trace) { if (span.attributes) { const attributes = JSON.parse(span.attributes); + if (attributes["langtrace.service.name"]) { + vendor = attributes["langtrace.service.name"].toLowerCase(); + if (vendor === "langgraph") { + langgraph = true; + } + } userId = attributes["user.id"]; if (attributes["llm.prompts"] && attributes["llm.responses"]) { prompts = attributes["llm.prompts"]; @@ -46,7 +54,6 @@ export const TraceRow = ({ } if (attributes["llm.token.counts"]) { model = attributes["llm.model"]; - vendor = attributes["langtrace.service.name"].toLowerCase(); const currentcounts = JSON.parse(attributes["llm.token.counts"]); tokenCounts = { input_tokens: tokenCounts.input_tokens @@ -213,6 +220,26 @@ export const TraceRow = ({ )} + {langgraph && ( + + )} {selectedTab === "trace" && ( @@ -235,6 +262,11 @@ export const TraceRow = ({ )} + {selectedTab === "langgraph" && ( +
+ +
+ )} )} diff --git a/components/shared/langgraph-view.tsx b/components/shared/langgraph-view.tsx new file mode 100644 index 00000000..39344e4a --- /dev/null +++ b/components/shared/langgraph-view.tsx @@ -0,0 +1,122 @@ +import { Span } from "@/lib/clients/scale3_clickhouse/models/span"; +import ReactFlow, { + Background, + BaseEdge, + Controls, + EdgeLabelRenderer, + getBezierPath, +} from "reactflow"; +import "reactflow/dist/style.css"; + +const CustomEdge = ({ id, data, ...props }: any) => { + const [edgePath, labelX, labelY] = getBezierPath(props); + + return ( + <> + + +
+ {data.label} +
+
+ + ); +}; + +export default function LanggraphView({ trace }: { trace: Span[] }) { + // construct the nodes and edges from the trace + let x = 0; + let y = 0; + const nodes = [ + { id: "__start__", data: { label: "Start" }, position: { x: 0, y: 0 } }, + ]; + const edges = []; + try { + for (const span of trace) { + const attributes = JSON.parse(span.attributes); + if (Object.keys(attributes).length > 0) { + const vendor = attributes["langtrace.service.name"].toLowerCase(); + const node = attributes["langgraph.node"]; + const edge = attributes["langgraph.edge"]; + const task = attributes["langgraph.task.name"]; + if (vendor === "langgraph" && node) { + x += 200; + y += 200; + const pNode = JSON.parse(node); + nodes.push({ + id: pNode?.name || span.span_id, + data: { + label: + `${pNode?.name} (action: ${pNode.action})}` || span.span_id, + }, + position: { x, y }, + }); + } + + if (vendor === "langgraph" && edge) { + const pEdge = JSON.parse(edge); + if (task === "add_conditional_edges") { + const pathMap = pEdge?.path_map; + if (pathMap) { + for (const k of Object.keys(pathMap)) { + edges.push({ + id: `${pEdge?.source || "source"}-${ + pathMap[k] || "destination" + }`, + data: { label: `${pEdge?.path} (output: ${k})` || "" }, + source: pEdge?.source || "source", + target: pathMap[k] || "destination", + type: "custom", + }); + } + } + } else { + edges.push({ + id: `${pEdge?.source || "source"}-${ + pEdge?.destination || "destination" + }`, + source: pEdge?.source || "source", + target: pEdge?.destination || "destination", + }); + } + } + } + } + } catch (e) { + console.error(e); + } + + nodes.push({ + id: "__end__", + data: { label: "End" }, + position: { x: 0, y: y + 200 }, + }); + + return ( +
+ + + + +
+ ); +} diff --git a/components/shared/vendor-metadata.tsx b/components/shared/vendor-metadata.tsx index 4508ec0b..233331da 100644 --- a/components/shared/vendor-metadata.tsx +++ b/components/shared/vendor-metadata.tsx @@ -42,6 +42,14 @@ export function vendorBadgeColor(vendor: string) { return "bg-red-500"; } + if (vendor.includes("qdrant")) { + return "bg-grey-500"; + } + + if (vendor.includes("qdrant")) { + return "bg-grey-500"; + } + return "bg-gray-500"; } @@ -82,6 +90,14 @@ export function vendorColor(vendor: string) { return "bg-red-200"; } + if (vendor.includes("qdrant")) { + return "bg-grey-200"; + } + + if (vendor.includes("groq")) { + return "bg-slate-200"; + } + return "bg-gray-800"; } @@ -109,6 +125,22 @@ export function VendorLogo({ serviceName = attributes["langtrace.service.name"].toLowerCase(); } + if (span.name.includes("groq") || serviceName.includes("groq")) { + const color = vendorColor("groq"); + return ( + Groq Logo + ); + } + if (span.name.includes("perplexity") || serviceName.includes("perplexity")) { const color = vendorColor("perplexity"); return ( @@ -237,6 +269,22 @@ export function VendorLogo({ ); } + if (span.name.includes("qdrant") || serviceName.includes("qdrant")) { + const color = vendorColor("qdrant"); + return ( + Qdrant Logo + ); + } + return (
= ({ color = "bg-indigo-500"; else if (span.name.includes("langchain") || serviceName.includes("langchain")) color = "bg-purple-500"; + else if (span.name.includes("cohere") || serviceName.includes("cohere")) + color = "bg-red-500"; + else if (span.name.includes("qdrant") || serviceName.includes("qdrant")) + color = "bg-grey-500"; + else if (span.name.includes("groq") || serviceName.includes("groq")) + color = "bg-slate-500"; else if ( span.name.includes("llamaindex") || serviceName.includes("llamaindex") diff --git a/docker-compose.yaml b/docker-compose.yaml index a7447299..078e6d4e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,7 +10,6 @@ services: working_dir: /app env_file: - .env - command: /bin/sh -c "npm run create-tables && npm run dev" ports: - "3000:3000" # Uncmment this for development diff --git a/package-lock.json b/package-lock.json index 4568a4a0..c936e48f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,6 @@ "f": "^1.4.0", "framer-motion": "^11.0.5", "fs": "^0.0.1-security", - "interweave": "^13.1.0", "js-tiktoken": "^1.0.10", "langchain": "^0.1.20", "lucide-react": "^0.323.0", @@ -76,6 +75,7 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-syntax-highlighter": "^15.5.0", + "reactflow": "^11.11.2", "shiki-processor": "^0.1.3", "sonner": "^1.3.1", "sql-bricks": "^3.0.1", @@ -3109,6 +3109,102 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@reactflow/background": { + "version": "11.3.12", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.12.tgz", + "integrity": "sha512-jBuWVb43JQy5h4WOS7G0PU8voGTEJNA+qDmx8/jyBtrjbasTesLNfQvboTGjnQYYiJco6mw5vrtQItAJDNoIqw==", + "dependencies": { + "@reactflow/core": "11.11.2", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.12", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.12.tgz", + "integrity": "sha512-L9F3+avFRShoprdT+5oOijm5gVsz2rqWCXBzOAgD923L1XFGIspdiHLLf8IlPGsT+mfl0GxbptZhaEeEzl1e3g==", + "dependencies": { + "@reactflow/core": "11.11.2", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.2", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.2.tgz", + "integrity": "sha512-+GfgyskweL1PsgRSguUwfrT2eDotlFgaKfDLm7x0brdzzPJY2qbCzVetaxedaiJmIli3817iYbILvE9qLKwbRA==", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.12", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.12.tgz", + "integrity": "sha512-SRDU77c2PCF54PV/MQfkz7VOW46q7V1LZNOQlXAp7dkNyAOI6R+tb9qBUtUJOvILB+TCN6pRfD9fQ+2T99bW3Q==", + "dependencies": { + "@reactflow/core": "11.11.2", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.12.tgz", + "integrity": "sha512-6LHJGuI1zHyRrZHw5gGlVLIWnvVxid9WIqw8FMFSg+oF2DuS3pAPwSoZwypy7W22/gDNl9eD1Dcl/OtFtDFQ+w==", + "dependencies": { + "@reactflow/core": "11.11.2", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.12.tgz", + "integrity": "sha512-4kJRvNna/E3y2MZW9/80wTKwkhw4pLJiz3D5eQrD13XcmojSb1rArO9CiwyrI+rMvs5gn6NlCFB4iN1F+Q+lxQ==", + "dependencies": { + "@reactflow/core": "11.11.2", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@remixicon/react": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@remixicon/react/-/react-4.2.0.tgz", @@ -3267,21 +3363,142 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, "node_modules/@types/d3-ease": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", @@ -3295,6 +3512,21 @@ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, "node_modules/@types/d3-scale": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", @@ -3303,6 +3535,16 @@ "@types/d3-time": "*" } }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" + }, "node_modules/@types/d3-shape": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", @@ -3316,11 +3558,33 @@ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, "node_modules/@types/d3-timer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -3351,6 +3615,11 @@ "@types/node": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -4635,6 +4904,11 @@ "node": ">=6" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, "node_modules/clickhouse": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/clickhouse/-/clickhouse-2.6.0.tgz", @@ -5125,6 +5399,26 @@ "node": ">=12" } }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-dsv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-2.0.0.tgz", @@ -5196,6 +5490,14 @@ "node": ">=12" } }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-shape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", @@ -5237,6 +5539,39 @@ "node": ">=12" } }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -5729,11 +6064,6 @@ "node": ">=6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -7130,21 +7460,6 @@ "node": ">=12" } }, - "node_modules/interweave": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/interweave/-/interweave-13.1.0.tgz", - "integrity": "sha512-JIDq0+2NYg0cgL7AB26fBcV0yZdiJvPDBp+aF6k8gq6Cr1kH5Gd2/Xqn7j8z+TGb8jCWZn739jzalCz+nPYwcA==", - "dependencies": { - "escape-html": "^1.0.3" - }, - "funding": { - "type": "ko-fi", - "url": "https://ko-fi.com/milesjohnson" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -10909,6 +11224,23 @@ "react-dom": ">=16.8.0" } }, + "node_modules/reactflow": { + "version": "11.11.2", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.2.tgz", + "integrity": "sha512-o1fT3stSdhzW+SedCGNSmEvZvULZygZIMLyW67NcWNZrgwx1wuJfzLg5fuQ0Nzf389wItumZX/zP3zdaPX7lEw==", + "dependencies": { + "@reactflow/background": "11.3.12", + "@reactflow/controls": "11.2.12", + "@reactflow/core": "11.11.2", + "@reactflow/minimap": "11.7.12", + "@reactflow/node-resizer": "2.2.12", + "@reactflow/node-toolbar": "1.3.12" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -13134,6 +13466,33 @@ "zod": "^3.22.4" } }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 8d8ba00f..11557f41 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-syntax-highlighter": "^15.5.0", + "reactflow": "^11.11.2", "shiki-processor": "^0.1.3", "sonner": "^1.3.1", "sql-bricks": "^3.0.1", diff --git a/public/groq.png b/public/groq.png new file mode 100644 index 0000000000000000000000000000000000000000..694b427c432e89d2ec0696cd72498c00b8a550b3 GIT binary patch literal 6254 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 KwJH_c$NvQ)5P{R9@b3CU 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 PUlULjE#(?T>zDrrgaQon literal 0 HcmV?d00001