[{"data":1,"prerenderedAt":3040},["ShallowReactive",2],{"project-hackathon-natixis":3},{"id":4,"title":5,"description":6,"extension":7,"favorite":8,"icon":9,"meta":10,"publishedAt":3030,"readingTime":1625,"shortDescription":3031,"slug":3032,"status":3033,"stem":3034,"tags":3035,"type":3038,"__hash__":3039},"projects\u002Fprojects\u002Fhackathon-natixis.md","Natixis Hackathon: Generative SQL Analytics","An intensive 4-week challenge to build an AI-powered data assistant. Our team developed a GenAI agent that transforms natural language into executable SQL queries, interactive visualizations, and natural language insights.","md",false,"i-ph-database-duotone",{"body":11},{"type":12,"value":13,"toc":2971},"minimark",[14,21,26,38,45,49,54,57,85,89,99,113,117,120,134,138,141,145,183,187,224,228,232,280,284,315,326,337,341,344,360,367,387,390,393,409,426,430,440,451,455,492,498,502,510,514,517,521,619,623,670,674,680,684,690,694,701,710,713,718,732,737,765,771,779,786,793,796,800,888,893,1081,1088,1093,1182,1189,1196,1199,1203,1211,1216,1342,1349,1353,1356,1360,1366,1742,1746,1752,1774,1778,1784,1904,1908,1913,1988,1992,1997,2378,2382,2387,2520,2524,2530,2534,2578,2582,2614,2618,2622,2686,2690,2719,2723,2761,2765,2769,2800,2804,2832,2835,2839,2843,2917,2921,2924,2958,2961,2967],[15,16,17],"note",{},[18,19,20],"p",{},"Our team and I won the hackathon, taking first place among 13 teams.",[22,23,25],"h2",{"id":24},"the-challenge","The Challenge",[18,27,28,29,33,34,37],{},"Organized by ",[30,31,32],"strong",{},"Natixis",", this hackathon followed a high-intensity format: ",[30,35,36],{},"three consecutive Saturdays"," of on-site development, bridged by two full weeks of remote collaboration.",[18,39,40,41,44],{},"Working in a ",[30,42,43],{},"team of four",", our goal was to bridge the gap between non-technical stakeholders and complex financial databases by creating an autonomous \"Data Talk\" agent.",[22,46,48],{"id":47},"how-we-built-it","How We Built It",[50,51,53],"h3",{"id":52},"data-engineering-schema-design","Data Engineering & Schema Design",[18,55,56],{},"Before building the AI layer, we handled a significant data migration task. I led the effort to:",[58,59,60,79],"ul",{},[61,62,63,66,67,70,71,74,75,78],"li",{},[30,64,65],{},"ETL Pipeline:"," Convert fragmented datasets from ",[30,68,69],{},".xlsx"," and ",[30,72,73],{},".csv"," formats into a structured ",[30,76,77],{},"SQL database",".",[61,80,81,84],{},[30,82,83],{},"Schema Optimization:"," Design robust SQL schemas that allow an LLM to understand relationships (foreign keys, indexing) for accurate query generation.",[50,86,88],{"id":87},"natural-language-to-sql-nl-to-sql","Natural Language to SQL (NL-to-SQL)",[18,90,91,92,70,95,98],{},"Using the ",[30,93,94],{},"Vercel AI SDK",[30,96,97],{},"Ollama",", we implemented an agentic workflow:",[58,100,101,107],{},[61,102,103,106],{},[30,104,105],{},"Prompt Engineering:"," Fine-tuning the agent to translate complex business questions (e.g., \"What was our highest growth margin last quarter?\") into valid, optimized SQL.",[61,108,109,112],{},[30,110,111],{},"Self-Correction:"," If a query fails, the agent analyzes the SQL error and self-corrects the syntax before returning a result.",[50,114,116],{"id":115},"automated-insights-visualization","Automated Insights & Visualization",[18,118,119],{},"Data is only useful if it's readable. Our Nuxt application goes beyond raw tables:",[58,121,122,128],{},[61,123,124,127],{},[30,125,126],{},"Dynamic Charts:"," The agent automatically determines the best visualization type (Bar, Line, Pie) based on the query result and renders it using interactive components.",[61,129,130,133],{},[30,131,132],{},"Narrative Explanations:"," A final LLM pass summarizes the data findings in plain English, highlighting anomalies or key trends.",[22,135,137],{"id":136},"impact-results","Impact & Results",[18,139,140],{},"This project demonstrated that a modern stack (Nuxt + local LLMs) can drastically reduce the time needed for data discovery. By the final Saturday, our team presented a working prototype capable of handling multi-table joins and generating real-time financial dashboards from simple chat prompts.",[22,142,144],{"id":143},"features","Features",[58,146,147,153,159,165,171,177],{},[61,148,149,152],{},[30,150,151],{},"Natural Language Queries",": Ask questions about anomalies in plain French or English",[61,154,155,158],{},[30,156,157],{},"SQL Execution",": Automatic SQL query generation and execution against MySQL database",[61,160,161,164],{},[30,162,163],{},"Visualizations",": Automatic chart generation (Line, Bar, Area, Donut, Bubble, Gantt)",[61,166,167,170],{},[30,168,169],{},"KPI Cards",": Dynamic KPI generation with trends and icons",[61,172,173,176],{},[30,174,175],{},"AI-Powered",": Uses Ollama models with tool calling capabilities",[61,178,179,182],{},[30,180,181],{},"Dark Mode",": Full light\u002Fdark theme support",[22,184,186],{"id":185},"technical-stack","Technical Stack",[58,188,189,199,207,215],{},[61,190,191,194,195,198],{},[30,192,193],{},"Frontend\u002FAPI:"," ",[30,196,197],{},"Nuxt 3"," for a seamless, reactive user interface.",[61,200,201,194,204,206],{},[30,202,203],{},"Orchestration:",[30,205,94],{}," to manage streams and tool-calling logic.",[61,208,209,194,212,214],{},[30,210,211],{},"Inference:",[30,213,97],{}," for running LLMs locally, ensuring data privacy during development.",[61,216,217,194,220,223],{},[30,218,219],{},"Storage:",[30,221,222],{},"PostgreSQL"," for the converted data warehouse.",[22,225,227],{"id":226},"quick-start","Quick Start",[50,229,231],{"id":230},"prerequisites","Prerequisites",[58,233,234,240,255,264],{},[61,235,236,239],{},[30,237,238],{},"Docker & Docker Compose"," (for MySQL database)",[61,241,242,245,246,250,251,254],{},[30,243,244],{},"Python 3.13+"," (project managed with ",[247,248,249],"code",{},"uv","; ",[247,252,253],{},"pip"," works too)",[61,256,257,260,261],{},[30,258,259],{},"Bun"," (package manager): ",[247,262,263],{},"npm install -g bun",[61,265,266,268,269,272,273,272,276,279],{},[30,267,97],{}," running locally with a compatible model (e.g., ",[247,270,271],{},"llama3.2",", ",[247,274,275],{},"qwen2.5",[247,277,278],{},"mistral",")",[50,281,283],{"id":282},"_1-start-mysql-database","1. Start MySQL Database",[285,286,291],"pre",{"className":287,"code":288,"language":289,"meta":290,"style":290},"language-bash shiki shiki-themes material-theme-lighter catppuccin-latte catppuccin-macchiato","docker compose up -d mysql\n","bash","",[247,292,293],{"__ignoreMap":290},[294,295,298,302,306,309,312],"span",{"class":296,"line":297},"line",1,[294,299,301],{"class":300},"sqbHp","docker",[294,303,305],{"class":304},"sJlHP"," compose",[294,307,308],{"class":304}," up",[294,310,311],{"class":304}," -d",[294,313,314],{"class":304}," mysql\n",[18,316,317,318,321,322,325],{},"The ",[247,319,320],{},"natixis"," database is created automatically from ",[247,323,324],{},"init.sql",":",[58,327,328,334],{},[61,329,330,331],{},"Default: ",[247,332,333],{},"mysql:\u002F\u002Froot:@localhost:3306\u002Fnatixis",[61,335,336],{},"Root password is empty for local development only",[50,338,340],{"id":339},"_2-load-data-into-database","2. Load Data into Database",[18,342,343],{},"Install Python dependencies:",[285,345,347],{"className":287,"code":346,"language":289,"meta":290,"style":290},"uv sync          # or: pip install -e .\n",[247,348,349],{"__ignoreMap":290},[294,350,351,353,356],{"class":296,"line":297},[294,352,249],{"class":300},[294,354,355],{"class":304}," sync",[294,357,359],{"class":358},"sv490","          # or: pip install -e .\n",[18,361,362,363,366],{},"Place source files in ",[247,364,365],{},".\u002Fdata\u002F"," directory:",[58,368,369,375,381],{},[61,370,371,374],{},[247,372,373],{},"Configuration.xlsx"," - Control and typology configuration",[61,376,377,380],{},[247,378,379],{},"anomaly_dump_result.csv"," - Anomaly data",[61,382,383,386],{},[247,384,385],{},"GenericAnomaly_dump_result_chunk_*.xlsx"," - Generic anomaly chunks",[18,388,389],{},"These datasets are not tracked in the repository - use the files shared with the project.",[18,391,392],{},"Then run the Jupyter notebook:",[285,394,396],{"className":287,"code":395,"language":289,"meta":290,"style":290},"jupyter notebook data_exploration.ipynb\n",[247,397,398],{"__ignoreMap":290},[294,399,400,403,406],{"class":296,"line":297},[294,401,402],{"class":300},"jupyter",[294,404,405],{"class":304}," notebook",[294,407,408],{"class":304}," data_exploration.ipynb\n",[18,410,411,412,70,415,418,419,272,422,425],{},"Execute the ",[247,413,414],{},"insert_into_sql",[247,416,417],{},"reset_and_load"," cells to populate ",[247,420,421],{},"generic_anomalies",[247,423,424],{},"anomalies",", and configuration tables.",[50,427,429],{"id":428},"_3-configure-environment","3. Configure Environment",[18,431,432,433,436,437,325],{},"Create ",[247,434,435],{},".env"," file in ",[247,438,439],{},"\u002Fchat",[285,441,445],{"className":442,"code":443,"language":444,"meta":290,"style":290},"language-env shiki shiki-themes material-theme-lighter catppuccin-latte catppuccin-macchiato","DATABASE_URL=\"mysql:\u002F\u002Froot:@localhost:3306\u002Fnatixis\"\n","env",[247,446,447],{"__ignoreMap":290},[294,448,449],{"class":296,"line":297},[294,450,443],{},[50,452,454],{"id":453},"_4-run-the-chat-application","4. Run the Chat Application",[285,456,458],{"className":287,"code":457,"language":289,"meta":290,"style":290},"cd chat\nbun install\nbun run dev --host\n",[247,459,460,469,478],{"__ignoreMap":290},[294,461,462,466],{"class":296,"line":297},[294,463,465],{"class":464},"sMj0x","cd",[294,467,468],{"class":304}," chat\n",[294,470,472,475],{"class":296,"line":471},2,[294,473,474],{"class":300},"bun",[294,476,477],{"class":304}," install\n",[294,479,481,483,486,489],{"class":296,"line":480},3,[294,482,474],{"class":300},[294,484,485],{"class":304}," run",[294,487,488],{"class":304}," dev",[294,490,491],{"class":304}," --host\n",[18,493,494,495],{},"The app will be available at ",[247,496,497],{},"http:\u002F\u002Flocalhost:3000",[22,499,501],{"id":500},"project-structure","Project Structure",[285,503,508],{"className":504,"code":506,"language":507},[505],"language-text",".\n├── data_exploration.ipynb    # Jupyter notebook for data loading\n├── init.sql                  # MySQL initialization script\n├── docker-compose.yml        # Docker services configuration\n├── data\u002F                     # Source data files (not tracked)\n│   ├── Configuration.xlsx\n│   ├── anomaly_dump_result.csv\n│   └── GenericAnomaly_dump_result_chunk_*.xlsx\n└── chat\u002F                     # Nuxt application\n    ├── app\u002F                  # Vue components and pages\n    ├── server\u002F               # API endpoints\n    ├── shared\u002F               # Shared utilities and tools\n    └── nuxt.config.ts        # Nuxt configuration\n","text",[247,509,506],{"__ignoreMap":290},[22,511,513],{"id":512},"database-schema","Database Schema",[18,515,516],{},"The database contains the following main tables:",[50,518,520],{"id":519},"core-tables","Core Tables",[522,523,524,537],"table",{},[525,526,527],"thead",{},[528,529,530,534],"tr",{},[531,532,533],"th",{},"Table",[531,535,536],{},"Description",[538,539,540,550,559,569,579,589,599,609],"tbody",{},[528,541,542,547],{},[543,544,545],"td",{},[247,546,424],{},[543,548,549],{},"Standard anomaly records",[528,551,552,556],{},[543,553,554],{},[247,555,421],{},[543,557,558],{},"Generic anomaly records (default for analysis)",[528,560,561,566],{},[543,562,563],{},[247,564,565],{},"typologies",[543,567,568],{},"Anomaly classification typologies",[528,570,571,576],{},[543,572,573],{},[247,574,575],{},"functional_controls",[543,577,578],{},"Control definitions and ownership",[528,580,581,586],{},[543,582,583],{},[247,584,585],{},"business_objects",[543,587,588],{},"Business object definitions",[528,590,591,596],{},[543,592,593],{},[247,594,595],{},"business_object_fields",[543,597,598],{},"Field definitions for business objects",[528,600,601,606],{},[543,602,603],{},[247,604,605],{},"business_data",[543,607,608],{},"Business data definitions",[528,610,611,616],{},[543,612,613],{},[247,614,615],{},"business_data_field_link",[543,617,618],{},"Links between fields and business data",[50,620,622],{"id":621},"key-fields-in-anomalies","Key Fields in Anomalies",[58,624,625,631,640,646,652,658,664],{},[61,626,627,630],{},[247,628,629],{},"anomaly_kuid"," - Unique identifier (primary key)",[61,632,633,272,636,639],{},[247,634,635],{},"title_txt",[247,637,638],{},"description_txt"," - Anomaly details",[61,641,642,645],{},[247,643,644],{},"priority_typ"," - Priority level (CRITICAL, HIGH, etc.)",[61,647,648,651],{},[247,649,650],{},"detection_time"," - When anomaly was detected",[61,653,654,657],{},[247,655,656],{},"hotfix_flg"," - Hotfix eligibility flag",[61,659,660,663],{},[247,661,662],{},"object_identification_fields"," - JSON with contract\u002Fobject context",[61,665,666,669],{},[247,667,668],{},"error_fields"," - JSON with error details and resolution status",[22,671,673],{"id":672},"chat-application-architecture","Chat Application Architecture",[285,675,678],{"className":676,"code":677,"language":507},[505],"chat\u002F\n├── app\u002F\n│   ├── app.vue                 # Root component with UI providers\n│   ├── components\u002F\n│   │   ├── Helper.vue          # Help tooltip component\n│   │   ├── ModelSelect.vue     # Model selector dropdown\n│   │   ├── Reasoning.vue       # AI reasoning display\n│   │   └── tool\u002F               # Tool UI components\n│   │       ├── SqlDisplay.vue  # SQL execution display\n│   │       ├── Chart.vue       # Chart visualizations\n│   │       └── KPI.vue         # KPI cards display\n│   └── pages\u002F\n│       ├── index.vue           # Landing page with prompt suggestions\n│       └── chat.vue            # Main chat interface\n├── server\u002F\n│   └── api\u002Fchat.ts             # Chat API endpoint with streaming\n├── shared\u002Futils\u002Ftools\u002F\n│   ├── executeSql.ts           # SQL query execution tool\n│   ├── chart.ts                # Chart visualization tool\n│   └── kpi.ts                  # KPI display tool\n├── nuxt.config.ts              # Nuxt configuration\n└── package.json                # Dependencies\n",[247,679,677],{"__ignoreMap":290},[50,681,683],{"id":682},"data-flow","Data Flow",[285,685,688],{"className":686,"code":687,"language":507},[505],"User Message\n    ↓\nAI Streaming (server\u002Fapi\u002Fchat.ts)\n    ↓\nTool Selection (toolChoice: 'auto')\n    ↓\nTool Execution (server-side)\n    ↓\nStream Results to Client\n    ↓\ngroupParts() in chat.vue\n    ↓\nComponent Selection\n    ├── type: 'reasoning'           → \u003CReasoning \u002F>\n    ├── type: 'tool-executeSqlTool' → \u003CToolSqlDisplay \u002F>\n    ├── type: 'tool-chartTool'      → \u003CToolChart \u002F>\n    └── type: 'tool-kpiTool'        → \u003CToolKPI \u002F>\n    ↓\nRendered Message\n",[247,689,687],{"__ignoreMap":290},[22,691,693],{"id":692},"available-tools","Available Tools",[50,695,697,698],{"id":696},"_1-executesqltool","1. ",[247,699,700],{},"executeSqlTool",[18,702,703,706,707],{},[30,704,705],{},"Server Tool",": ",[247,708,709],{},"shared\u002Futils\u002Ftools\u002FexecuteSql.ts",[18,711,712],{},"Executes SQL SELECT queries against the database.",[18,714,715],{},[30,716,717],{},"Parameters:",[58,719,720,726],{},[61,721,722,725],{},[247,723,724],{},"query",": SQL SELECT query (MySQL syntax)",[61,727,728,731],{},[247,729,730],{},"reason",": Explanation for debugging",[18,733,734],{},[30,735,736],{},"Example usage by AI:",[285,738,742],{"className":739,"code":740,"language":741,"meta":290,"style":290},"language-sql shiki shiki-themes material-theme-lighter catppuccin-latte catppuccin-macchiato","SELECT priority_typ, COUNT(*) AS anomaly_count\nFROM generic_anomalies\nGROUP BY priority_typ\nLIMIT 10\n","sql",[247,743,744,749,754,759],{"__ignoreMap":290},[294,745,746],{"class":296,"line":297},[294,747,748],{},"SELECT priority_typ, COUNT(*) AS anomaly_count\n",[294,750,751],{"class":296,"line":471},[294,752,753],{},"FROM generic_anomalies\n",[294,755,756],{"class":296,"line":480},[294,757,758],{},"GROUP BY priority_typ\n",[294,760,762],{"class":296,"line":761},4,[294,763,764],{},"LIMIT 10\n",[18,766,767,770],{},[30,768,769],{},"Security:"," Only SELECT queries are allowed. Results are limited to 50 rows.",[18,772,773,706,776],{},[30,774,775],{},"UI Component",[247,777,778],{},"app\u002Fcomponents\u002Ftool\u002FSqlDisplay.vue",[50,780,782,783],{"id":781},"_2-charttool","2. ",[247,784,785],{},"chartTool",[18,787,788,706,790],{},[30,789,705],{},[247,791,792],{},"shared\u002Futils\u002Ftools\u002Fchart.ts",[18,794,795],{},"Generates data visualizations.",[18,797,798],{},[30,799,717],{},[58,801,802,824,830,836,842,851,857,867,879],{},[61,803,804,706,807,272,809,272,812,272,815,272,818,272,821],{},[247,805,806],{},"chartType",[247,808,296],{},[247,810,811],{},"bar",[247,813,814],{},"area",[247,816,817],{},"donut",[247,819,820],{},"bubble",[247,822,823],{},"gantt",[61,825,826,829],{},[247,827,828],{},"title",": Chart title",[61,831,832,835],{},[247,833,834],{},"data",": Array of data objects",[61,837,838,841],{},[247,839,840],{},"xKey",": Field for X-axis",[61,843,844,272,847,850],{},[247,845,846],{},"xKeyStart",[247,848,849],{},"xKeyEnd",": Start\u002Fend fields for Gantt charts",[61,852,853,856],{},[247,854,855],{},"radiusKey",": Size field for Bubble charts",[61,858,859,862,863,866],{},[247,860,861],{},"series",": Array of ",[247,864,865],{},"{ key, name, color? }"," for Y-axis values",[61,868,869,272,872,272,875,878],{},[247,870,871],{},"showMarkers",[247,873,874],{},"showLegend",[247,876,877],{},"isStacked",": Display options",[61,880,881,272,884,887],{},[247,882,883],{},"xLabel",[247,885,886],{},"yLabel",": Axis labels",[18,889,890],{},[30,891,892],{},"Example:",[285,894,898],{"className":895,"code":896,"language":897,"meta":290,"style":290},"language-typescript shiki shiki-themes material-theme-lighter catppuccin-latte catppuccin-macchiato","{\n  chartType: 'donut',\n  title: 'Anomalies by Priority',\n  data: [{ priority: 'CRITICAL', count: 150 }, { priority: 'HIGH', count: 89 }],\n  xKey: 'priority',\n  series: [{ key: 'count', name: 'Anomalies' }]\n}\n","typescript",[247,899,900,906,926,942,1015,1032,1075],{"__ignoreMap":290},[294,901,902],{"class":296,"line":297},[294,903,905],{"class":904},"sMKYs","{\n",[294,907,908,912,914,918,920,923],{"class":296,"line":471},[294,909,911],{"class":910},"sL87A","  chartType",[294,913,325],{"class":904},[294,915,917],{"class":916},"srDDN"," '",[294,919,817],{"class":304},[294,921,922],{"class":916},"'",[294,924,925],{"class":904},",\n",[294,927,928,931,933,935,938,940],{"class":296,"line":480},[294,929,930],{"class":910},"  title",[294,932,325],{"class":904},[294,934,917],{"class":916},[294,936,937],{"class":304},"Anomalies by Priority",[294,939,922],{"class":916},[294,941,925],{"class":904},[294,943,944,947,949,953,956,959,962,964,967,969,972,975,977,981,984,987,989,991,993,996,998,1000,1002,1004,1007,1010,1013],{"class":296,"line":761},[294,945,946],{"class":910},"  data",[294,948,325],{"class":904},[294,950,952],{"class":951},"suonz"," [",[294,954,955],{"class":904},"{",[294,957,958],{"class":951}," priority",[294,960,325],{"class":961},"sn2um",[294,963,917],{"class":916},[294,965,966],{"class":304},"CRITICAL",[294,968,922],{"class":916},[294,970,971],{"class":904},",",[294,973,974],{"class":951}," count",[294,976,325],{"class":961},[294,978,980],{"class":979},"sZm5v"," 150",[294,982,983],{"class":904}," },",[294,985,986],{"class":904}," {",[294,988,958],{"class":951},[294,990,325],{"class":961},[294,992,917],{"class":916},[294,994,995],{"class":304},"HIGH",[294,997,922],{"class":916},[294,999,971],{"class":904},[294,1001,974],{"class":951},[294,1003,325],{"class":961},[294,1005,1006],{"class":979}," 89",[294,1008,1009],{"class":904}," }",[294,1011,1012],{"class":951},"]",[294,1014,925],{"class":904},[294,1016,1018,1021,1023,1025,1028,1030],{"class":296,"line":1017},5,[294,1019,1020],{"class":910},"  xKey",[294,1022,325],{"class":904},[294,1024,917],{"class":916},[294,1026,1027],{"class":304},"priority",[294,1029,922],{"class":916},[294,1031,925],{"class":904},[294,1033,1035,1038,1040,1042,1044,1047,1049,1051,1054,1056,1058,1061,1063,1065,1068,1070,1072],{"class":296,"line":1034},6,[294,1036,1037],{"class":910},"  series",[294,1039,325],{"class":904},[294,1041,952],{"class":951},[294,1043,955],{"class":904},[294,1045,1046],{"class":951}," key",[294,1048,325],{"class":961},[294,1050,917],{"class":916},[294,1052,1053],{"class":304},"count",[294,1055,922],{"class":916},[294,1057,971],{"class":904},[294,1059,1060],{"class":951}," name",[294,1062,325],{"class":961},[294,1064,917],{"class":916},[294,1066,1067],{"class":304},"Anomalies",[294,1069,922],{"class":916},[294,1071,1009],{"class":904},[294,1073,1074],{"class":951},"]\n",[294,1076,1078],{"class":296,"line":1077},7,[294,1079,1080],{"class":904},"}\n",[18,1082,1083,706,1085],{},[30,1084,775],{},[247,1086,1087],{},"app\u002Fcomponents\u002Ftool\u002FChart.vue",[18,1089,1090],{},[30,1091,1092],{},"Supported Chart Types:",[522,1094,1095,1108],{},[525,1096,1097],{},[528,1098,1099,1102,1105],{},[531,1100,1101],{},"Type",[531,1103,1104],{},"Use Case",[531,1106,1107],{},"Example",[538,1109,1110,1122,1134,1146,1158,1170],{},[528,1111,1112,1116,1119],{},[543,1113,1114],{},[247,1115,296],{},[543,1117,1118],{},"Time series trends",[543,1120,1121],{},"Anomalies over time",[528,1123,1124,1128,1131],{},[543,1125,1126],{},[247,1127,811],{},[543,1129,1130],{},"Category comparisons",[543,1132,1133],{},"Anomalies by priority",[528,1135,1136,1140,1143],{},[543,1137,1138],{},[247,1139,814],{},[543,1141,1142],{},"Cumulative metrics",[543,1144,1145],{},"Volume over time",[528,1147,1148,1152,1155],{},[543,1149,1150],{},[247,1151,817],{},[543,1153,1154],{},"Proportions",[543,1156,1157],{},"Distribution by type",[528,1159,1160,1164,1167],{},[543,1161,1162],{},[247,1163,820],{},[543,1165,1166],{},"Multi-dimensional data",[543,1168,1169],{},"Risk vs. volume vs. severity",[528,1171,1172,1176,1179],{},[543,1173,1174],{},[247,1175,823],{},[543,1177,1178],{},"Timelines",[543,1180,1181],{},"Remediation schedules",[50,1183,1185,1186],{"id":1184},"_3-kpitool","3. ",[247,1187,1188],{},"kpiTool",[18,1190,1191,706,1193],{},[30,1192,705],{},[247,1194,1195],{},"shared\u002Futils\u002Ftools\u002Fkpi.ts",[18,1197,1198],{},"Displays KPI cards with metrics.",[18,1200,1201],{},[30,1202,717],{},[58,1204,1205],{},[61,1206,1207,1210],{},[247,1208,1209],{},"kpis",": Array of KPI objects (max 6 recommended)",[18,1212,1213],{},[30,1214,1215],{},"KPI Object:",[285,1217,1219],{"className":895,"code":1218,"language":897,"meta":290,"style":290},"{\n  label: 'Critical Anomalies',           \u002F\u002F Short metric name\n  value: '150',                          \u002F\u002F Formatted value (string or number)\n  description: 'Active critical issues', \u002F\u002F Context description\n  icon: 'i-lucide-alert-triangle',       \u002F\u002F Lucide icon name\n  trend: 'up',                           \u002F\u002F 'up' | 'down' | 'stable'\n  trendValue: '+12%'                     \u002F\u002F Optional trend percentage\n}\n",[247,1220,1221,1225,1244,1263,1282,1301,1320,1337],{"__ignoreMap":290},[294,1222,1223],{"class":296,"line":297},[294,1224,905],{"class":904},[294,1226,1227,1230,1232,1234,1237,1239,1241],{"class":296,"line":471},[294,1228,1229],{"class":910},"  label",[294,1231,325],{"class":904},[294,1233,917],{"class":916},[294,1235,1236],{"class":304},"Critical Anomalies",[294,1238,922],{"class":916},[294,1240,971],{"class":904},[294,1242,1243],{"class":358},"           \u002F\u002F Short metric name\n",[294,1245,1246,1249,1251,1253,1256,1258,1260],{"class":296,"line":480},[294,1247,1248],{"class":910},"  value",[294,1250,325],{"class":904},[294,1252,917],{"class":916},[294,1254,1255],{"class":304},"150",[294,1257,922],{"class":916},[294,1259,971],{"class":904},[294,1261,1262],{"class":358},"                          \u002F\u002F Formatted value (string or number)\n",[294,1264,1265,1268,1270,1272,1275,1277,1279],{"class":296,"line":761},[294,1266,1267],{"class":910},"  description",[294,1269,325],{"class":904},[294,1271,917],{"class":916},[294,1273,1274],{"class":304},"Active critical issues",[294,1276,922],{"class":916},[294,1278,971],{"class":904},[294,1280,1281],{"class":358}," \u002F\u002F Context description\n",[294,1283,1284,1287,1289,1291,1294,1296,1298],{"class":296,"line":1017},[294,1285,1286],{"class":910},"  icon",[294,1288,325],{"class":904},[294,1290,917],{"class":916},[294,1292,1293],{"class":304},"i-lucide-alert-triangle",[294,1295,922],{"class":916},[294,1297,971],{"class":904},[294,1299,1300],{"class":358},"       \u002F\u002F Lucide icon name\n",[294,1302,1303,1306,1308,1310,1313,1315,1317],{"class":296,"line":1034},[294,1304,1305],{"class":910},"  trend",[294,1307,325],{"class":904},[294,1309,917],{"class":916},[294,1311,1312],{"class":304},"up",[294,1314,922],{"class":916},[294,1316,971],{"class":904},[294,1318,1319],{"class":358},"                           \u002F\u002F 'up' | 'down' | 'stable'\n",[294,1321,1322,1325,1327,1329,1332,1334],{"class":296,"line":1077},[294,1323,1324],{"class":910},"  trendValue",[294,1326,325],{"class":904},[294,1328,917],{"class":916},[294,1330,1331],{"class":304},"+12%",[294,1333,922],{"class":916},[294,1335,1336],{"class":358},"                     \u002F\u002F Optional trend percentage\n",[294,1338,1340],{"class":296,"line":1339},8,[294,1341,1080],{"class":904},[18,1343,1344,706,1346],{},[30,1345,775],{},[247,1347,1348],{},"app\u002Fcomponents\u002Ftool\u002FKPI.vue",[22,1350,1352],{"id":1351},"adding-a-new-tool","Adding a New Tool",[18,1354,1355],{},"To add a new tool, implement both server-side and client-side layers:",[50,1357,1359],{"id":1358},"step-1-define-server-tool","Step 1: Define Server Tool",[18,1361,1362,1363,325],{},"Create a new file in ",[247,1364,1365],{},"shared\u002Futils\u002Ftools\u002F",[285,1367,1369],{"className":895,"code":1368,"language":897,"meta":290,"style":290},"\u002F\u002F shared\u002Futils\u002Ftools\u002FmyTool.ts\nimport { tool, type UIToolInvocation } from 'ai'\nimport { z } from 'zod'\n\nexport type MyUIToolInvocation = UIToolInvocation\u003Ctypeof myTool>\n\nexport const myTool = tool({\n  description: 'Brief description of what the tool does and when to use it.',\n  \n  inputSchema: z.object({\n    param1: z.string().describe('Parameter description'),\n    param2: z.number().optional().describe('Optional parameter')\n  }),\n  \n  outputSchema: z.object({\n    result: z.string()\n  }),\n  \n  execute: async ({ param1, param2 }) => {\n    return { result: 'processed data' }\n  }\n})\n",[247,1370,1371,1376,1409,1429,1435,1467,1471,1492,1507,1513,1532,1568,1608,1618,1623,1641,1658,1667,1672,1705,1728,1734],{"__ignoreMap":290},[294,1372,1373],{"class":296,"line":297},[294,1374,1375],{"class":358},"\u002F\u002F shared\u002Futils\u002Ftools\u002FmyTool.ts\n",[294,1377,1378,1382,1384,1388,1390,1393,1396,1398,1401,1403,1406],{"class":296,"line":471},[294,1379,1381],{"class":1380},"sthAO","import",[294,1383,986],{"class":904},[294,1385,1387],{"class":1386},"s0g_q"," tool",[294,1389,971],{"class":904},[294,1391,1392],{"class":1380}," type",[294,1394,1395],{"class":1386}," UIToolInvocation",[294,1397,1009],{"class":904},[294,1399,1400],{"class":1380}," from",[294,1402,917],{"class":916},[294,1404,1405],{"class":304},"ai",[294,1407,1408],{"class":916},"'\n",[294,1410,1411,1413,1415,1418,1420,1422,1424,1427],{"class":296,"line":480},[294,1412,1381],{"class":1380},[294,1414,986],{"class":904},[294,1416,1417],{"class":1386}," z",[294,1419,1009],{"class":904},[294,1421,1400],{"class":1380},[294,1423,917],{"class":916},[294,1425,1426],{"class":304},"zod",[294,1428,1408],{"class":916},[294,1430,1431],{"class":296,"line":761},[294,1432,1434],{"emptyLinePlaceholder":1433},true,"\n",[294,1436,1437,1440,1443,1447,1450,1452,1456,1460,1464],{"class":296,"line":1017},[294,1438,1439],{"class":1380},"export",[294,1441,1392],{"class":1442},"s_I8y",[294,1444,1446],{"class":1445},"sqyRn"," MyUIToolInvocation",[294,1448,1449],{"class":961}," =",[294,1451,1395],{"class":1445},[294,1453,1455],{"class":1454},"sYxAZ","\u003C",[294,1457,1459],{"class":1458},"sTjAX","typeof",[294,1461,1463],{"class":1462},"sqgQz"," myTool",[294,1465,1466],{"class":1454},">\n",[294,1468,1469],{"class":296,"line":1034},[294,1470,1434],{"emptyLinePlaceholder":1433},[294,1472,1473,1475,1478,1481,1484,1487,1490],{"class":296,"line":1077},[294,1474,1439],{"class":1380},[294,1476,1477],{"class":1442}," const",[294,1479,1480],{"class":1386}," myTool ",[294,1482,1483],{"class":961},"=",[294,1485,1387],{"class":1486},"s0QKf",[294,1488,1489],{"class":1386},"(",[294,1491,905],{"class":904},[294,1493,1494,1496,1498,1500,1503,1505],{"class":296,"line":1339},[294,1495,1267],{"class":951},[294,1497,325],{"class":961},[294,1499,917],{"class":916},[294,1501,1502],{"class":304},"Brief description of what the tool does and when to use it.",[294,1504,922],{"class":916},[294,1506,925],{"class":904},[294,1508,1510],{"class":296,"line":1509},9,[294,1511,1512],{"class":1386},"  \n",[294,1514,1516,1519,1521,1523,1525,1528,1530],{"class":296,"line":1515},10,[294,1517,1518],{"class":951},"  inputSchema",[294,1520,325],{"class":961},[294,1522,1417],{"class":1386},[294,1524,78],{"class":961},[294,1526,1527],{"class":1486},"object",[294,1529,1489],{"class":1386},[294,1531,905],{"class":904},[294,1533,1535,1538,1540,1542,1544,1547,1550,1552,1555,1557,1559,1562,1564,1566],{"class":296,"line":1534},11,[294,1536,1537],{"class":951},"    param1",[294,1539,325],{"class":961},[294,1541,1417],{"class":1386},[294,1543,78],{"class":961},[294,1545,1546],{"class":1486},"string",[294,1548,1549],{"class":1386},"()",[294,1551,78],{"class":961},[294,1553,1554],{"class":1486},"describe",[294,1556,1489],{"class":1386},[294,1558,922],{"class":916},[294,1560,1561],{"class":304},"Parameter description",[294,1563,922],{"class":916},[294,1565,279],{"class":1386},[294,1567,925],{"class":904},[294,1569,1571,1574,1576,1578,1580,1583,1585,1587,1590,1592,1594,1596,1598,1600,1603,1605],{"class":296,"line":1570},12,[294,1572,1573],{"class":951},"    param2",[294,1575,325],{"class":961},[294,1577,1417],{"class":1386},[294,1579,78],{"class":961},[294,1581,1582],{"class":1486},"number",[294,1584,1549],{"class":1386},[294,1586,78],{"class":961},[294,1588,1589],{"class":1486},"optional",[294,1591,1549],{"class":1386},[294,1593,78],{"class":961},[294,1595,1554],{"class":1486},[294,1597,1489],{"class":1386},[294,1599,922],{"class":916},[294,1601,1602],{"class":304},"Optional parameter",[294,1604,922],{"class":916},[294,1606,1607],{"class":1386},")\n",[294,1609,1611,1614,1616],{"class":296,"line":1610},13,[294,1612,1613],{"class":904},"  }",[294,1615,279],{"class":1386},[294,1617,925],{"class":904},[294,1619,1621],{"class":296,"line":1620},14,[294,1622,1512],{"class":1386},[294,1624,1626,1629,1631,1633,1635,1637,1639],{"class":296,"line":1625},15,[294,1627,1628],{"class":951},"  outputSchema",[294,1630,325],{"class":961},[294,1632,1417],{"class":1386},[294,1634,78],{"class":961},[294,1636,1527],{"class":1486},[294,1638,1489],{"class":1386},[294,1640,905],{"class":904},[294,1642,1644,1647,1649,1651,1653,1655],{"class":296,"line":1643},16,[294,1645,1646],{"class":951},"    result",[294,1648,325],{"class":961},[294,1650,1417],{"class":1386},[294,1652,78],{"class":961},[294,1654,1546],{"class":1486},[294,1656,1657],{"class":1386},"()\n",[294,1659,1661,1663,1665],{"class":296,"line":1660},17,[294,1662,1613],{"class":904},[294,1664,279],{"class":1386},[294,1666,925],{"class":904},[294,1668,1670],{"class":296,"line":1669},18,[294,1671,1512],{"class":1386},[294,1673,1675,1678,1680,1683,1686,1690,1692,1695,1698,1702],{"class":296,"line":1674},19,[294,1676,1677],{"class":1486},"  execute",[294,1679,325],{"class":961},[294,1681,1682],{"class":1442}," async",[294,1684,1685],{"class":904}," ({",[294,1687,1689],{"class":1688},"smoPz"," param1",[294,1691,971],{"class":904},[294,1693,1694],{"class":1688}," param2",[294,1696,1697],{"class":904}," })",[294,1699,1701],{"class":1700},"s66VR"," =>",[294,1703,1704],{"class":904}," {\n",[294,1706,1708,1711,1713,1716,1718,1720,1723,1725],{"class":296,"line":1707},20,[294,1709,1710],{"class":1380},"    return",[294,1712,986],{"class":904},[294,1714,1715],{"class":951}," result",[294,1717,325],{"class":961},[294,1719,917],{"class":916},[294,1721,1722],{"class":304},"processed data",[294,1724,922],{"class":916},[294,1726,1727],{"class":904}," }\n",[294,1729,1731],{"class":296,"line":1730},21,[294,1732,1733],{"class":904},"  }\n",[294,1735,1737,1740],{"class":296,"line":1736},22,[294,1738,1739],{"class":904},"}",[294,1741,1607],{"class":1386},[50,1743,1745],{"id":1744},"step-2-export-tool","Step 2: Export Tool",[18,1747,1748,1749,325],{},"Add to ",[247,1750,1751],{},"shared\u002Futils\u002Findex.ts",[285,1753,1755],{"className":895,"code":1754,"language":897,"meta":290,"style":290},"export * from '.\u002Ftools\u002FmyTool'\n",[247,1756,1757],{"__ignoreMap":290},[294,1758,1759,1761,1765,1767,1769,1772],{"class":296,"line":297},[294,1760,1439],{"class":1380},[294,1762,1764],{"class":1763},"sLx3F"," *",[294,1766,1400],{"class":1380},[294,1768,917],{"class":916},[294,1770,1771],{"class":304},".\u002Ftools\u002FmyTool",[294,1773,1408],{"class":916},[50,1775,1777],{"id":1776},"step-3-register-in-chat-api","Step 3: Register in Chat API",[18,1779,1780,1781,325],{},"Update ",[247,1782,1783],{},"server\u002Fapi\u002Fchat.ts",[285,1785,1787],{"className":895,"code":1786,"language":897,"meta":290,"style":290},"import { myTool } from '~\u002Fshared\u002Futils'\n\nconst result = await streamText({\n  model: ollama(model, { \u002F* ... *\u002F }),\n  tools: {\n    executeSqlTool,\n    chartTool,\n    kpiTool,\n    myTool\n  },\n})\n",[247,1788,1789,1808,1812,1832,1858,1867,1874,1881,1888,1893,1898],{"__ignoreMap":290},[294,1790,1791,1793,1795,1797,1799,1801,1803,1806],{"class":296,"line":297},[294,1792,1381],{"class":1380},[294,1794,986],{"class":904},[294,1796,1463],{"class":1386},[294,1798,1009],{"class":904},[294,1800,1400],{"class":1380},[294,1802,917],{"class":916},[294,1804,1805],{"class":304},"~\u002Fshared\u002Futils",[294,1807,1408],{"class":916},[294,1809,1810],{"class":296,"line":471},[294,1811,1434],{"emptyLinePlaceholder":1433},[294,1813,1814,1817,1820,1822,1825,1828,1830],{"class":296,"line":480},[294,1815,1816],{"class":1442},"const",[294,1818,1819],{"class":1386}," result ",[294,1821,1483],{"class":961},[294,1823,1824],{"class":1380}," await",[294,1826,1827],{"class":1486}," streamText",[294,1829,1489],{"class":1386},[294,1831,905],{"class":904},[294,1833,1834,1837,1839,1842,1845,1847,1849,1852,1854,1856],{"class":296,"line":761},[294,1835,1836],{"class":951},"  model",[294,1838,325],{"class":961},[294,1840,1841],{"class":1486}," ollama",[294,1843,1844],{"class":1386},"(model",[294,1846,971],{"class":904},[294,1848,986],{"class":904},[294,1850,1851],{"class":358}," \u002F* ... *\u002F",[294,1853,1009],{"class":904},[294,1855,279],{"class":1386},[294,1857,925],{"class":904},[294,1859,1860,1863,1865],{"class":296,"line":1017},[294,1861,1862],{"class":951},"  tools",[294,1864,325],{"class":961},[294,1866,1704],{"class":904},[294,1868,1869,1872],{"class":296,"line":1034},[294,1870,1871],{"class":1386},"    executeSqlTool",[294,1873,925],{"class":904},[294,1875,1876,1879],{"class":296,"line":1077},[294,1877,1878],{"class":1386},"    chartTool",[294,1880,925],{"class":904},[294,1882,1883,1886],{"class":296,"line":1339},[294,1884,1885],{"class":1386},"    kpiTool",[294,1887,925],{"class":904},[294,1889,1890],{"class":296,"line":1509},[294,1891,1892],{"class":1386},"    myTool\n",[294,1894,1895],{"class":296,"line":1515},[294,1896,1897],{"class":904},"  },\n",[294,1899,1900,1902],{"class":296,"line":1534},[294,1901,1739],{"class":904},[294,1903,1607],{"class":1386},[50,1905,1907],{"id":1906},"step-4-update-system-prompt","Step 4: Update System Prompt",[18,1909,1910,1911,325],{},"Add tool documentation to the system prompt in ",[247,1912,1783],{},[285,1914,1916],{"className":895,"code":1915,"language":897,"meta":290,"style":290},"## myTool\n- Purpose: What the tool does\n- When to use: Specific use cases\n- Required parameters: param1, param2\n- Output: Description of result format\n",[247,1917,1918,1923,1936,1951,1970],{"__ignoreMap":290},[294,1919,1920],{"class":296,"line":297},[294,1921,1922],{"class":1386},"## myTool\n",[294,1924,1925,1928,1931,1933],{"class":296,"line":471},[294,1926,1927],{"class":961},"-",[294,1929,1930],{"class":910}," Purpose",[294,1932,325],{"class":904},[294,1934,1935],{"class":1386}," What the tool does\n",[294,1937,1938,1940,1943,1946,1948],{"class":296,"line":480},[294,1939,1927],{"class":961},[294,1941,1942],{"class":1386}," When to ",[294,1944,1945],{"class":910},"use",[294,1947,325],{"class":904},[294,1949,1950],{"class":1386}," Specific use cases\n",[294,1952,1953,1955,1958,1961,1963,1965,1967],{"class":296,"line":761},[294,1954,1927],{"class":961},[294,1956,1957],{"class":1386}," Required ",[294,1959,1960],{"class":910},"parameters",[294,1962,325],{"class":904},[294,1964,1689],{"class":1386},[294,1966,971],{"class":904},[294,1968,1969],{"class":1386}," param2\n",[294,1971,1972,1974,1977,1979,1982,1985],{"class":296,"line":1017},[294,1973,1927],{"class":961},[294,1975,1976],{"class":910}," Output",[294,1978,325],{"class":904},[294,1980,1981],{"class":1386}," Description ",[294,1983,1984],{"class":1458},"of",[294,1986,1987],{"class":1386}," result format\n",[50,1989,1991],{"id":1990},"step-5-create-ui-component","Step 5: Create UI Component",[18,1993,432,1994,325],{},[247,1995,1996],{},"app\u002Fcomponents\u002Ftool\u002FMyTool.vue",[285,1998,2002],{"className":1999,"code":2000,"language":2001,"meta":290,"style":290},"language-vue shiki shiki-themes material-theme-lighter catppuccin-latte catppuccin-macchiato","\u003Cscript setup lang=\"ts\">\nimport type { MyUIToolInvocation } from '~\u002Fshared\u002Futils'\n\nconst props = defineProps\u003C{\n  invocation: MyUIToolInvocation\n  isStreaming?: boolean\n}>()\n\nconst output = computed(() => props.invocation.output)\nconst state = computed(() => props.invocation.state)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"state !== 'output-available'\" class=\"my-4 flex items-center gap-2 text-gray-500\">\n    \u003CUIcon name=\"i-lucide-loader-2\" class=\"animate-spin\" \u002F>\n    \u003Cspan>Processing...\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n\n  \u003Cdiv v-else-if=\"output\" class=\"my-4 p-4 rounded-lg border bg-gray-50 dark:bg-gray-900\">\n    \u003Ch3 class=\"font-semibold\">Tool Result\u003C\u002Fh3>\n    \u003Cp>{{ output.result }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue",[247,2003,2004,2031,2051,2055,2071,2082,2094,2103,2107,2138,2166,2175,2179,2188,2222,2255,2272,2281,2285,2316,2344,2361,2369],{"__ignoreMap":290},[294,2005,2006,2008,2012,2016,2019,2021,2024,2027,2029],{"class":296,"line":297},[294,2007,1455],{"class":961},[294,2009,2011],{"class":2010},"sc0Ia","script",[294,2013,2015],{"class":2014},"sbKHD"," setup",[294,2017,2018],{"class":2014}," lang",[294,2020,1483],{"class":961},[294,2022,2023],{"class":916},"\"",[294,2025,2026],{"class":304},"ts",[294,2028,2023],{"class":916},[294,2030,1466],{"class":961},[294,2032,2033,2035,2037,2039,2041,2043,2045,2047,2049],{"class":296,"line":471},[294,2034,1381],{"class":1380},[294,2036,1392],{"class":1380},[294,2038,986],{"class":904},[294,2040,1446],{"class":1386},[294,2042,1009],{"class":904},[294,2044,1400],{"class":1380},[294,2046,917],{"class":916},[294,2048,1805],{"class":304},[294,2050,1408],{"class":916},[294,2052,2053],{"class":296,"line":480},[294,2054,1434],{"emptyLinePlaceholder":1433},[294,2056,2057,2059,2062,2064,2067,2069],{"class":296,"line":761},[294,2058,1816],{"class":1442},[294,2060,2061],{"class":1386}," props ",[294,2063,1483],{"class":961},[294,2065,2066],{"class":1486}," defineProps",[294,2068,1455],{"class":1454},[294,2070,905],{"class":904},[294,2072,2073,2077,2079],{"class":296,"line":1017},[294,2074,2076],{"class":2075},"se7m0","  invocation",[294,2078,325],{"class":961},[294,2080,2081],{"class":1445}," MyUIToolInvocation\n",[294,2083,2084,2087,2090],{"class":296,"line":1034},[294,2085,2086],{"class":2075},"  isStreaming",[294,2088,2089],{"class":961},"?:",[294,2091,2093],{"class":2092},"sQubY"," boolean\n",[294,2095,2096,2098,2101],{"class":296,"line":1077},[294,2097,1739],{"class":904},[294,2099,2100],{"class":1454},">",[294,2102,1657],{"class":1386},[294,2104,2105],{"class":296,"line":1339},[294,2106,1434],{"emptyLinePlaceholder":1433},[294,2108,2109,2111,2114,2116,2119,2121,2123,2125,2128,2130,2133,2135],{"class":296,"line":1509},[294,2110,1816],{"class":1442},[294,2112,2113],{"class":1386}," output ",[294,2115,1483],{"class":961},[294,2117,2118],{"class":1486}," computed",[294,2120,1489],{"class":1386},[294,2122,1549],{"class":904},[294,2124,1701],{"class":1700},[294,2126,2127],{"class":1386}," props",[294,2129,78],{"class":961},[294,2131,2132],{"class":1386},"invocation",[294,2134,78],{"class":961},[294,2136,2137],{"class":1386},"output)\n",[294,2139,2140,2142,2145,2147,2149,2151,2153,2155,2157,2159,2161,2163],{"class":296,"line":1515},[294,2141,1816],{"class":1442},[294,2143,2144],{"class":1386}," state ",[294,2146,1483],{"class":961},[294,2148,2118],{"class":1486},[294,2150,1489],{"class":1386},[294,2152,1549],{"class":904},[294,2154,1701],{"class":1700},[294,2156,2127],{"class":1386},[294,2158,78],{"class":961},[294,2160,2132],{"class":1386},[294,2162,78],{"class":961},[294,2164,2165],{"class":1386},"state)\n",[294,2167,2168,2171,2173],{"class":296,"line":1534},[294,2169,2170],{"class":961},"\u003C\u002F",[294,2172,2011],{"class":2010},[294,2174,1466],{"class":961},[294,2176,2177],{"class":296,"line":1570},[294,2178,1434],{"emptyLinePlaceholder":1433},[294,2180,2181,2183,2186],{"class":296,"line":1610},[294,2182,1455],{"class":961},[294,2184,2185],{"class":2010},"template",[294,2187,1466],{"class":961},[294,2189,2190,2193,2196,2199,2201,2203,2206,2208,2211,2213,2215,2218,2220],{"class":296,"line":1620},[294,2191,2192],{"class":961},"  \u003C",[294,2194,2195],{"class":2010},"div",[294,2197,2198],{"class":2014}," v-if",[294,2200,1483],{"class":961},[294,2202,2023],{"class":916},[294,2204,2205],{"class":304},"state !== 'output-available'",[294,2207,2023],{"class":916},[294,2209,2210],{"class":2014}," class",[294,2212,1483],{"class":961},[294,2214,2023],{"class":916},[294,2216,2217],{"class":304},"my-4 flex items-center gap-2 text-gray-500",[294,2219,2023],{"class":916},[294,2221,1466],{"class":961},[294,2223,2224,2227,2230,2232,2234,2236,2239,2241,2243,2245,2247,2250,2252],{"class":296,"line":1625},[294,2225,2226],{"class":961},"    \u003C",[294,2228,2229],{"class":2010},"UIcon",[294,2231,1060],{"class":2014},[294,2233,1483],{"class":961},[294,2235,2023],{"class":916},[294,2237,2238],{"class":304},"i-lucide-loader-2",[294,2240,2023],{"class":916},[294,2242,2210],{"class":2014},[294,2244,1483],{"class":961},[294,2246,2023],{"class":916},[294,2248,2249],{"class":304},"animate-spin",[294,2251,2023],{"class":916},[294,2253,2254],{"class":961}," \u002F>\n",[294,2256,2257,2259,2261,2263,2266,2268,2270],{"class":296,"line":1643},[294,2258,2226],{"class":961},[294,2260,294],{"class":2010},[294,2262,2100],{"class":961},[294,2264,2265],{"class":1386},"Processing...",[294,2267,2170],{"class":961},[294,2269,294],{"class":2010},[294,2271,1466],{"class":961},[294,2273,2274,2277,2279],{"class":296,"line":1660},[294,2275,2276],{"class":961},"  \u003C\u002F",[294,2278,2195],{"class":2010},[294,2280,1466],{"class":961},[294,2282,2283],{"class":296,"line":1669},[294,2284,1434],{"emptyLinePlaceholder":1433},[294,2286,2287,2289,2291,2294,2296,2298,2301,2303,2305,2307,2309,2312,2314],{"class":296,"line":1674},[294,2288,2192],{"class":961},[294,2290,2195],{"class":2010},[294,2292,2293],{"class":2014}," v-else-if",[294,2295,1483],{"class":961},[294,2297,2023],{"class":916},[294,2299,2300],{"class":304},"output",[294,2302,2023],{"class":916},[294,2304,2210],{"class":2014},[294,2306,1483],{"class":961},[294,2308,2023],{"class":916},[294,2310,2311],{"class":304},"my-4 p-4 rounded-lg border bg-gray-50 dark:bg-gray-900",[294,2313,2023],{"class":916},[294,2315,1466],{"class":961},[294,2317,2318,2320,2322,2324,2326,2328,2331,2333,2335,2338,2340,2342],{"class":296,"line":1707},[294,2319,2226],{"class":961},[294,2321,50],{"class":2010},[294,2323,2210],{"class":2014},[294,2325,1483],{"class":961},[294,2327,2023],{"class":916},[294,2329,2330],{"class":304},"font-semibold",[294,2332,2023],{"class":916},[294,2334,2100],{"class":961},[294,2336,2337],{"class":1386},"Tool Result",[294,2339,2170],{"class":961},[294,2341,50],{"class":2010},[294,2343,1466],{"class":961},[294,2345,2346,2348,2350,2352,2355,2357,2359],{"class":296,"line":1730},[294,2347,2226],{"class":961},[294,2349,18],{"class":2010},[294,2351,2100],{"class":961},[294,2353,2354],{"class":1386},"{{ output.result }}",[294,2356,2170],{"class":961},[294,2358,18],{"class":2010},[294,2360,1466],{"class":961},[294,2362,2363,2365,2367],{"class":296,"line":1736},[294,2364,2276],{"class":961},[294,2366,2195],{"class":2010},[294,2368,1466],{"class":961},[294,2370,2372,2374,2376],{"class":296,"line":2371},23,[294,2373,2170],{"class":961},[294,2375,2185],{"class":2010},[294,2377,1466],{"class":961},[50,2379,2381],{"id":2380},"step-6-register-component-in-chat-page","Step 6: Register Component in Chat Page",[18,2383,1780,2384,325],{},[247,2385,2386],{},"app\u002Fpages\u002Fchat.vue",[285,2388,2390],{"className":1999,"code":2389,"language":2001,"meta":290,"style":290},"\u003CToolMyTool\n  v-else-if=\"block.type === 'tool' && block.part.type === 'tool-myTool'\"\n  :invocation=\"(block.part as any).toolInvocation || block.part\"\n  :is-streaming=\"block.isStreaming\"\n\u002F>\n",[247,2391,2392,2399,2451,2495,2515],{"__ignoreMap":290},[294,2393,2394,2396],{"class":296,"line":297},[294,2395,1455],{"class":961},[294,2397,2398],{"class":2010},"ToolMyTool\n",[294,2400,2401,2404,2406,2408,2411,2413,2416,2419,2421,2424,2426,2429,2432,2434,2437,2439,2441,2443,2445,2448],{"class":296,"line":471},[294,2402,2403],{"class":1380},"  v-else-if",[294,2405,1483],{"class":961},[294,2407,2023],{"class":916},[294,2409,2410],{"class":1386},"block",[294,2412,78],{"class":961},[294,2414,2415],{"class":1386},"type ",[294,2417,2418],{"class":961},"===",[294,2420,917],{"class":916},[294,2422,2423],{"class":304},"tool",[294,2425,922],{"class":916},[294,2427,2428],{"class":961}," &&",[294,2430,2431],{"class":1386}," block",[294,2433,78],{"class":961},[294,2435,2436],{"class":1386},"part",[294,2438,78],{"class":961},[294,2440,2415],{"class":1386},[294,2442,2418],{"class":961},[294,2444,917],{"class":916},[294,2446,2447],{"class":304},"tool-myTool",[294,2449,2450],{"class":916},"'\"\n",[294,2452,2453,2456,2458,2460,2462,2465,2467,2470,2473,2476,2478,2480,2483,2486,2488,2490,2492],{"class":296,"line":480},[294,2454,2455],{"class":904},"  :",[294,2457,2132],{"class":2014},[294,2459,1483],{"class":961},[294,2461,2023],{"class":916},[294,2463,2464],{"class":1386},"(block",[294,2466,78],{"class":961},[294,2468,2469],{"class":1386},"part ",[294,2471,2472],{"class":1380},"as",[294,2474,2475],{"class":2092}," any",[294,2477,279],{"class":1386},[294,2479,78],{"class":961},[294,2481,2482],{"class":1386},"toolInvocation ",[294,2484,2485],{"class":961},"||",[294,2487,2431],{"class":1386},[294,2489,78],{"class":961},[294,2491,2436],{"class":1386},[294,2493,2494],{"class":916},"\"\n",[294,2496,2497,2499,2502,2504,2506,2508,2510,2513],{"class":296,"line":761},[294,2498,2455],{"class":904},[294,2500,2501],{"class":2014},"is-streaming",[294,2503,1483],{"class":961},[294,2505,2023],{"class":916},[294,2507,2410],{"class":1386},[294,2509,78],{"class":961},[294,2511,2512],{"class":1386},"isStreaming",[294,2514,2494],{"class":916},[294,2516,2517],{"class":296,"line":1017},[294,2518,2519],{"class":1386},"\u002F>\n",[22,2521,2523],{"id":2522},"system-prompt-guidelines","System Prompt Guidelines",[18,2525,2526,2527,2529],{},"The system prompt (",[247,2528,1783],{},") controls AI behavior:",[50,2531,2533],{"id":2532},"key-rules","Key Rules",[2535,2536,2537,2545,2554,2560,2566,2572],"ol",{},[61,2538,2539,2542,2543],{},[30,2540,2541],{},"Schema Compliance",": Only use columns\u002Ftables defined in ",[247,2544,324],{},[61,2546,2547,2550,2551,2553],{},[30,2548,2549],{},"Default Table",": Use ",[247,2552,421],{}," for general analysis",[61,2555,2556,2559],{},[30,2557,2558],{},"Language",": Respond in the user's language",[61,2561,2562,2565],{},[30,2563,2564],{},"No SQL Visibility",": Never show raw SQL to users",[61,2567,2568,2571],{},[30,2569,2570],{},"Explicit Requests",": Only use charts\u002FKPIs when explicitly requested",[61,2573,2574,2577],{},[30,2575,2576],{},"Proactive Suggestions",": Offer visualizations without auto-executing",[50,2579,2581],{"id":2580},"critical-definitions","Critical Definitions",[58,2583,2584,2595,2605],{},[61,2585,2586,706,2589,2591,2592],{},[30,2587,2588],{},"Critical Anomaly",[247,2590,644],{}," IN ('CRITICAL', 'CRITIQUE', 'HIGH', 'HAUTE') OR ",[247,2593,2594],{},"hotfix_flg = 1",[61,2596,2597,2600,2601,2604],{},[30,2598,2599],{},"Open\u002FUnresolved",": Check ",[247,2602,2603],{},"error_fields.resolved_value_txt"," for resolution status",[61,2606,2607,2550,2610,2613],{},[30,2608,2609],{},"Owner",[247,2611,2612],{},"functional_controls.responsible_login_id"," as default owner",[22,2615,2617],{"id":2616},"development","Development",[50,2619,2621],{"id":2620},"scripts","Scripts",[285,2623,2625],{"className":287,"code":2624,"language":289,"meta":290,"style":290},"bun run dev        # Start development server\nbun run build      # Build for production\nbun run preview    # Preview production build\nbun run lint       # Run ESLint\nbun run typecheck  # Type check with vue-tsc\n",[247,2626,2627,2638,2650,2662,2674],{"__ignoreMap":290},[294,2628,2629,2631,2633,2635],{"class":296,"line":297},[294,2630,474],{"class":300},[294,2632,485],{"class":304},[294,2634,488],{"class":304},[294,2636,2637],{"class":358},"        # Start development server\n",[294,2639,2640,2642,2644,2647],{"class":296,"line":471},[294,2641,474],{"class":300},[294,2643,485],{"class":304},[294,2645,2646],{"class":304}," build",[294,2648,2649],{"class":358},"      # Build for production\n",[294,2651,2652,2654,2656,2659],{"class":296,"line":480},[294,2653,474],{"class":300},[294,2655,485],{"class":304},[294,2657,2658],{"class":304}," preview",[294,2660,2661],{"class":358},"    # Preview production build\n",[294,2663,2664,2666,2668,2671],{"class":296,"line":761},[294,2665,474],{"class":300},[294,2667,485],{"class":304},[294,2669,2670],{"class":304}," lint",[294,2672,2673],{"class":358},"       # Run ESLint\n",[294,2675,2676,2678,2680,2683],{"class":296,"line":1017},[294,2677,474],{"class":300},[294,2679,485],{"class":304},[294,2681,2682],{"class":304}," typecheck",[294,2684,2685],{"class":358},"  # Type check with vue-tsc\n",[50,2687,2689],{"id":2688},"environment-variables","Environment Variables",[522,2691,2692,2704],{},[525,2693,2694],{},[528,2695,2696,2699,2701],{},[531,2697,2698],{},"Variable",[531,2700,536],{},[531,2702,2703],{},"Required",[538,2705,2706],{},[528,2707,2708,2713,2716],{},[543,2709,2710],{},[247,2711,2712],{},"DATABASE_URL",[543,2714,2715],{},"MySQL connection string",[543,2717,2718],{},"Yes",[50,2720,2722],{"id":2721},"tech-stack","Tech Stack",[58,2724,2725,2731,2737,2743,2749,2755],{},[61,2726,2727,2730],{},[30,2728,2729],{},"Framework",": Nuxt 4 + Vue 3",[61,2732,2733,2736],{},[30,2734,2735],{},"UI",": Nuxt UI 4 (based on Tailwind CSS)",[61,2738,2739,2742],{},[30,2740,2741],{},"AI",": AI SDK + Ollama",[61,2744,2745,2748],{},[30,2746,2747],{},"Database",": MySQL via NuxtHub",[61,2750,2751,2754],{},[30,2752,2753],{},"Charts",": nuxt-charts",[61,2756,2757,2760],{},[30,2758,2759],{},"Utilities",": VueUse, Zod",[22,2762,2764],{"id":2763},"deployment","Deployment",[50,2766,2768],{"id":2767},"nuxthub-recommended","NuxtHub (Recommended)",[285,2770,2772],{"className":287,"code":2771,"language":289,"meta":290,"style":290},"cd chat\nbun run build\nnpx hub deploy\n",[247,2773,2774,2780,2789],{"__ignoreMap":290},[294,2775,2776,2778],{"class":296,"line":297},[294,2777,465],{"class":464},[294,2779,468],{"class":304},[294,2781,2782,2784,2786],{"class":296,"line":471},[294,2783,474],{"class":300},[294,2785,485],{"class":304},[294,2787,2788],{"class":304}," build\n",[294,2790,2791,2794,2797],{"class":296,"line":480},[294,2792,2793],{"class":300},"npx",[294,2795,2796],{"class":304}," hub",[294,2798,2799],{"class":304}," deploy\n",[50,2801,2803],{"id":2802},"manual-deployment","Manual Deployment",[285,2805,2807],{"className":287,"code":2806,"language":289,"meta":290,"style":290},"cd chat\nbun run build\nbun run preview\n",[247,2808,2809,2815,2823],{"__ignoreMap":290},[294,2810,2811,2813],{"class":296,"line":297},[294,2812,465],{"class":464},[294,2814,468],{"class":304},[294,2816,2817,2819,2821],{"class":296,"line":471},[294,2818,474],{"class":300},[294,2820,485],{"class":304},[294,2822,2788],{"class":304},[294,2824,2825,2827,2829],{"class":296,"line":480},[294,2826,474],{"class":300},[294,2828,485],{"class":304},[294,2830,2831],{"class":304}," preview\n",[18,2833,2834],{},"Set production environment variables for database connection.",[22,2836,2838],{"id":2837},"troubleshooting","Troubleshooting",[50,2840,2842],{"id":2841},"common-issues","Common Issues",[2535,2844,2845,2864,2885,2901],{},[61,2846,2847,2850],{},[30,2848,2849],{},"Ollama Connection Failed",[58,2851,2852,2858],{},[61,2853,2854,2855],{},"Ensure Ollama is running: ",[247,2856,2857],{},"ollama serve",[61,2859,2860,2861],{},"Check model availability: ",[247,2862,2863],{},"ollama pull llama3.2",[61,2865,2866,2869],{},[30,2867,2868],{},"Database Connection Error",[58,2870,2871,2877],{},[61,2872,2873,2874],{},"Verify MySQL is running: ",[247,2875,2876],{},"docker ps",[61,2878,2879,2880,2882,2883],{},"Check ",[247,2881,435],{}," has correct ",[247,2884,2712],{},[61,2886,2887,2890],{},[30,2888,2889],{},"Empty Query Results",[58,2891,2892,2895],{},[61,2893,2894],{},"Ensure data is loaded via Jupyter notebook",[61,2896,2897,2898],{},"Verify tables exist: ",[247,2899,2900],{},"SHOW TABLES;",[61,2902,2903,2906],{},[30,2904,2905],{},"Tool Not Called",[58,2907,2908,2911],{},[61,2909,2910],{},"System prompt may need adjustment",[61,2912,2879,2913,2916],{},[247,2914,2915],{},"toolChoice: 'auto'"," in streamText config",[50,2918,2920],{"id":2919},"debug-mode","Debug Mode",[18,2922,2923],{},"View AI reasoning by checking console logs:",[285,2925,2927],{"className":895,"code":2926,"language":897,"meta":290,"style":290},"\u002F\u002F In chat.ts, the tool execution logs\nconsole.log('⚡ executing SQL:', query)\n",[247,2928,2929,2934],{"__ignoreMap":290},[294,2930,2931],{"class":296,"line":297},[294,2932,2933],{"class":358},"\u002F\u002F In chat.ts, the tool execution logs\n",[294,2935,2936,2939,2941,2944,2946,2948,2951,2953,2955],{"class":296,"line":471},[294,2937,2938],{"class":1386},"console",[294,2940,78],{"class":961},[294,2942,2943],{"class":1486},"log",[294,2945,1489],{"class":1386},[294,2947,922],{"class":916},[294,2949,2950],{"class":304},"⚡ executing SQL:",[294,2952,922],{"class":916},[294,2954,971],{"class":904},[294,2956,2957],{"class":1386}," query)\n",[2959,2960],"hr",{},[18,2962,2963],{},[2964,2965,2966],"em",{},"Curious about the ETL logic or the prompt structure we used? I can share how we optimized the LLM's SQL accuracy.",[2968,2969,2970],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sqbHp, html code.shiki .sqbHp{--shiki-light:#E2931D;--shiki-light-font-style:inherit;--shiki-default:#1E66F5;--shiki-default-font-style:italic;--shiki-dark:#8AADF4;--shiki-dark-font-style:italic}html pre.shiki code .sJlHP, html code.shiki .sJlHP{--shiki-light:#91B859;--shiki-default:#40A02B;--shiki-dark:#A6DA95}html pre.shiki code .sv490, html code.shiki .sv490{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7C7F93;--shiki-default-font-style:italic;--shiki-dark:#939AB7;--shiki-dark-font-style:italic}html pre.shiki code .sMj0x, html code.shiki .sMj0x{--shiki-light:#6182B8;--shiki-light-font-style:inherit;--shiki-default:#D20F39;--shiki-default-font-style:italic;--shiki-dark:#ED8796;--shiki-dark-font-style:italic}html pre.shiki code .sMKYs, html code.shiki .sMKYs{--shiki-light:#39ADB5;--shiki-default:#7C7F93;--shiki-dark:#939AB7}html pre.shiki code .sL87A, html code.shiki .sL87A{--shiki-light:#E2931D;--shiki-default:#4C4F69;--shiki-dark:#CAD3F5}html pre.shiki code .srDDN, html code.shiki .srDDN{--shiki-light:#39ADB5;--shiki-default:#40A02B;--shiki-dark:#A6DA95}html pre.shiki code .suonz, html code.shiki .suonz{--shiki-light:#E53935;--shiki-default:#4C4F69;--shiki-dark:#CAD3F5}html pre.shiki code .sn2um, html code.shiki .sn2um{--shiki-light:#39ADB5;--shiki-default:#179299;--shiki-dark:#8BD5CA}html pre.shiki code .sZm5v, html code.shiki .sZm5v{--shiki-light:#F76D47;--shiki-default:#FE640B;--shiki-dark:#F5A97F}html pre.shiki code .sthAO, html code.shiki .sthAO{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#8839EF;--shiki-default-font-style:inherit;--shiki-dark:#C6A0F6;--shiki-dark-font-style:inherit}html pre.shiki code .s0g_q, html code.shiki .s0g_q{--shiki-light:#90A4AE;--shiki-default:#4C4F69;--shiki-dark:#CAD3F5}html pre.shiki code .s_I8y, html code.shiki .s_I8y{--shiki-light:#9C3EDA;--shiki-default:#8839EF;--shiki-dark:#C6A0F6}html pre.shiki code .sqyRn, html code.shiki .sqyRn{--shiki-light:#E2931D;--shiki-light-font-style:inherit;--shiki-default:#DF8E1D;--shiki-default-font-style:italic;--shiki-dark:#EED49F;--shiki-dark-font-style:italic}html pre.shiki code .sYxAZ, html code.shiki .sYxAZ{--shiki-light:#39ADB5;--shiki-default:#04A5E5;--shiki-dark:#91D7E3}html pre.shiki code .sTjAX, html code.shiki .sTjAX{--shiki-light:#39ADB5;--shiki-default:#8839EF;--shiki-dark:#C6A0F6}html pre.shiki code .sqgQz, html code.shiki .sqgQz{--shiki-light:#90A4AE;--shiki-light-font-style:inherit;--shiki-default:#4C4F69;--shiki-default-font-style:italic;--shiki-dark:#CAD3F5;--shiki-dark-font-style:italic}html pre.shiki code .s0QKf, html code.shiki .s0QKf{--shiki-light:#6182B8;--shiki-light-font-style:inherit;--shiki-default:#1E66F5;--shiki-default-font-style:italic;--shiki-dark:#8AADF4;--shiki-dark-font-style:italic}html pre.shiki code .smoPz, html code.shiki .smoPz{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E64553;--shiki-default-font-style:italic;--shiki-dark:#EE99A0;--shiki-dark-font-style:italic}html pre.shiki code .s66VR, html code.shiki .s66VR{--shiki-light:#9C3EDA;--shiki-default:#179299;--shiki-dark:#8BD5CA}html pre.shiki code .sLx3F, html code.shiki .sLx3F{--shiki-light:#39ADB5;--shiki-default:#D20F39;--shiki-dark:#ED8796}html pre.shiki code .sc0Ia, html code.shiki .sc0Ia{--shiki-light:#E53935;--shiki-default:#1E66F5;--shiki-dark:#8AADF4}html pre.shiki code .sbKHD, html code.shiki .sbKHD{--shiki-light:#9C3EDA;--shiki-default:#DF8E1D;--shiki-dark:#EED49F}html pre.shiki code .se7m0, html code.shiki .se7m0{--shiki-light:#E53935;--shiki-light-font-style:inherit;--shiki-default:#4C4F69;--shiki-default-font-style:italic;--shiki-dark:#CAD3F5;--shiki-dark-font-style:italic}html pre.shiki code .sQubY, html code.shiki .sQubY{--shiki-light:#E2931D;--shiki-default:#8839EF;--shiki-dark:#C6A0F6}",{"title":290,"searchDepth":471,"depth":471,"links":2972},[2973,2974,2979,2980,2981,2982,2989,2990,2994,2997,3005,3013,3017,3022,3026],{"id":24,"depth":471,"text":25},{"id":47,"depth":471,"text":48,"children":2975},[2976,2977,2978],{"id":52,"depth":480,"text":53},{"id":87,"depth":480,"text":88},{"id":115,"depth":480,"text":116},{"id":136,"depth":471,"text":137},{"id":143,"depth":471,"text":144},{"id":185,"depth":471,"text":186},{"id":226,"depth":471,"text":227,"children":2983},[2984,2985,2986,2987,2988],{"id":230,"depth":480,"text":231},{"id":282,"depth":480,"text":283},{"id":339,"depth":480,"text":340},{"id":428,"depth":480,"text":429},{"id":453,"depth":480,"text":454},{"id":500,"depth":471,"text":501},{"id":512,"depth":471,"text":513,"children":2991},[2992,2993],{"id":519,"depth":480,"text":520},{"id":621,"depth":480,"text":622},{"id":672,"depth":471,"text":673,"children":2995},[2996],{"id":682,"depth":480,"text":683},{"id":692,"depth":471,"text":693,"children":2998},[2999,3001,3003],{"id":696,"depth":480,"text":3000},"1. executeSqlTool",{"id":781,"depth":480,"text":3002},"2. chartTool",{"id":1184,"depth":480,"text":3004},"3. kpiTool",{"id":1351,"depth":471,"text":1352,"children":3006},[3007,3008,3009,3010,3011,3012],{"id":1358,"depth":480,"text":1359},{"id":1744,"depth":480,"text":1745},{"id":1776,"depth":480,"text":1777},{"id":1906,"depth":480,"text":1907},{"id":1990,"depth":480,"text":1991},{"id":2380,"depth":480,"text":2381},{"id":2522,"depth":471,"text":2523,"children":3014},[3015,3016],{"id":2532,"depth":480,"text":2533},{"id":2580,"depth":480,"text":2581},{"id":2616,"depth":471,"text":2617,"children":3018},[3019,3020,3021],{"id":2620,"depth":480,"text":2621},{"id":2688,"depth":480,"text":2689},{"id":2721,"depth":480,"text":2722},{"id":2763,"depth":471,"text":2764,"children":3023},[3024,3025],{"id":2767,"depth":480,"text":2768},{"id":2802,"depth":480,"text":2803},{"id":2837,"depth":471,"text":2838,"children":3027},[3028,3029],{"id":2841,"depth":480,"text":2842},{"id":2919,"depth":480,"text":2920},"2026-03-07","A team-based project building an NL-to-SQL agent with Nuxt, Ollama, and Vercel AI SDK.","hackathon-natixis","Completed","projects\u002Fhackathon-natixis",[3036,97,94,222,3037],"Nuxt","ETL","Hackathon","kur_MTBVaanKrUifke7hCnJiMpkUw7fmJyEyKGNSE40",1777982168046]