├── .github └── pull_request_template.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── chatapp ├── .env.example ├── .gitignore ├── README.md ├── app │ ├── Svgcomps │ │ └── ChatList.tsx │ ├── api │ │ ├── chats │ │ │ └── route.ts │ │ ├── listThreads │ │ │ └── route.ts │ │ └── threadID │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components │ └── sidebar.tsx ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ ├── next.svg │ └── vercel.svg ├── tailwind.config.ts ├── tsconfig.json └── utility │ ├── chats.utilities.ts │ └── run.utitities.ts ├── community ├── customer-support │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── components.json │ ├── next.config.mjs │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.mjs │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── threads │ │ │ │ │ ├── [threadId] │ │ │ │ │ └── route.ts │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── components │ │ │ └── ui │ │ │ │ ├── button.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── scroll-area.tsx │ │ │ │ └── sheet.tsx │ │ └── lib │ │ │ ├── types.ts │ │ │ └── utils.ts │ ├── tailwind.config.ts │ └── tsconfig.json └── dell-chatbot │ ├── .env.example │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ ├── components │ │ └── MessageCard.tsx │ ├── constants │ │ ├── index.ts │ │ └── laptopData.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ ├── prompt │ │ └── route.ts │ └── utils │ │ └── index.ts │ ├── next.config.mjs │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.mjs │ ├── tailwind.config.ts │ └── tsconfig.json ├── function-calling-swiggy ├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── app │ ├── components │ │ └── MessageCard.tsx │ ├── constants │ │ └── index.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ ├── prompt │ │ └── route.ts │ └── utils │ │ └── index.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ ├── next.svg │ └── vercel.svg ├── tailwind.config.ts └── tsconfig.json ├── function-calling ├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── app │ ├── components │ │ └── MessageCard.tsx │ ├── constants │ │ └── index.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ ├── prompt │ │ └── route.ts │ └── utils │ │ └── index.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ ├── next.svg │ └── vercel.svg ├── tailwind.config.ts └── tsconfig.json ├── interview-simulator ├── .gitignore ├── README.md ├── app.py ├── assets │ └── 1.png ├── requirements.txt └── utils.py ├── notion-recipe-generator ├── .gitignore ├── README.md ├── assets │ ├── notion-recipe-generator-demo.mp4 │ └── notionImage.png ├── main.py └── requirements.txt ├── supabase-rag ├── .env.example ├── .gitignore ├── Pipfile ├── Pipfile.lock ├── README.md ├── main.py ├── requirements.txt └── utils.py └── translator-app ├── Pipfile ├── Pipfile.lock ├── README.md ├── app.py ├── assets └── translator_app.png └── translator.py /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Tune AI PyCon India 2024 Contest 2 | 3 | ## Project Title (Required) 4 | > A unique descriptive title 5 | 6 | ## Tune AI Products Used (Required) 7 | - [ ] Tune Studio 8 | - [ ] Tune Chat 9 | - [ ] Tune Assisants 10 | 11 | ## Checklist 12 | - [ ] Includes a README 13 | - [ ] Includes instructions on how to run the app 14 | 15 | ## Screenshots (Optional) 16 | Paste screenshots of major parts of the app here. 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **venv** 2 | **.env** 3 | .idea 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/CONTRIBUTING.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Abhishek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tune AI Cookbook 📚 2 | ================= 3 | 4 | Welcome to the Tune AI Cookbook! This repository contains a collection of sample applications to help you get started with various LLM tasks using Tune AI's [Studio](https://studio.tune.app?utm_source=github&utm_medium=repository&utm_campaign=cookbook) and [Chat](https://chat.tune.app?utm_source=github&utm_medium=repository&utm_campaign=cookbook) APIs. 5 | 6 | ## Table of Contents 7 | 8 | - [Overview](#overview) ✨ 9 | - [Getting Started](#getting-started) 🚀 10 | - [Sample Applications](#sample-applications) 📂 11 | - [Contributing](#contributing) 🤝 12 | - [License](#license) 📜 13 | 14 | ## Overview 15 | 16 | This cookbook provides a range of sample applications demonstrating different use cases and features of Tune Studio and Tune Chat. 17 | Each sample application comes with its own README file to help you understand and get started quickly. 18 | 19 | ## Getting Started 20 | 21 | To get started with any of the sample applications, follow these steps: 22 | 23 | 1. Clone the repository: 24 | ```sh 25 | git clone https://github.com/TuneHQ/cookbook.git 26 | cd cookbook 27 | ``` 28 | 29 | 2. Navigate to the sample application directory: 30 | ```sh 31 | cd 32 | ``` 33 | 34 | 3. Follow the instructions in the sample application's README file to set up and run the application. 35 | 36 | ## Sample Applications 37 | 38 | Here are some of the sample applications included in this repository: 39 | 40 | Sure, here is just the table for the sample applications: 41 | 42 | | Application | Description | Link | 43 | | ----------- | ----------- | ---- | 44 | | Chat App 💬 | A simple chat application demonstrating basic chat API integration. | [Chat App](chatapp/README.md) | 45 | | Function Calling (Swiggy) 🍔 | An example of using function calling to get your order history, nearby restaurant etc with swiggy (food delivery) app. | [Function Calling (Swiggy)](function-calling-swiggy/README.md) | 46 | | Function Calling 🛠️ | General-purpose function calling with Tune Studio. | [Function Calling](function-calling/README.md) | 47 | | Supabase RAG ⚡ | Integration with Supabase to build RAG agent | [Supabase RAG](supabase-rag/README.md) | 48 | | Translator App 🌐 | A language translation application using Tune Studio. | [Translator App](translator-app/README.md) | 49 | | Notion Recipe Generator👌 | Recipe Generator using Tune Studio and Notion. | [Notion Recipe Generator](notion-recipe-generator/README.md) | 50 | | Interview Simulator 👔 | Smnart Interview Sim using Llama 3.1 and Streamlit. | [Interview Simulator](interview-simulator/README.md) | 51 | 52 | Each directory contains a README file with detailed instructions for setting up and running the application. 53 | 54 | ## Contributing 55 | 56 | We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to learn how you can contribute to this project. 🛠️ 57 | 58 | ## License 59 | 60 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details. 📜 61 | 62 | --- 63 | 64 | Happy coding, Keep Tuning! 🎉 65 | 66 | The Tune AI Team 67 | -------------------------------------------------------------------------------- /chatapp/.env.example: -------------------------------------------------------------------------------- 1 | TUNE_API_KEY=your-tune-studio-key-here 2 | TUNE_MODEL=your-tune-studio-model-uri-here -------------------------------------------------------------------------------- /chatapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /chatapp/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/chatapp/README.md -------------------------------------------------------------------------------- /chatapp/app/Svgcomps/ChatList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function ChatList({ ...props }) { 4 | return ( 5 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default ChatList; 19 | -------------------------------------------------------------------------------- /chatapp/app/api/chats/route.ts: -------------------------------------------------------------------------------- 1 | import { getMessages } from "@/utility/chats.utilities"; 2 | import { getModelResponse } from "@/utility/run.utitities"; 3 | 4 | const axios = require("axios"); 5 | 6 | export async function POST(request: Request) { 7 | const requestBody = await request.json(); 8 | const threadID = requestBody.threadID; 9 | const content = requestBody.content; 10 | 11 | const resp = await axios 12 | .post( 13 | `https://studio.tune.app/v1/threads/${threadID}/messages`, 14 | { 15 | assistantId: "", 16 | content, 17 | fileIds: [], 18 | role: "user", 19 | runId: "", 20 | threadId: threadID, 21 | }, 22 | { 23 | headers: { 24 | "Content-Type": "application/json", 25 | "x-tune-key": process.env.TUNE_API_KEY, 26 | }, 27 | } 28 | ) 29 | .catch(() => { 30 | return { 31 | success: false, 32 | }; 33 | }) 34 | .then(() => { 35 | return { 36 | success: true, 37 | }; 38 | }); 39 | 40 | if (resp?.success) { 41 | const response = await getModelResponse(threadID); 42 | if (response?.success) { 43 | const respdata = await getMessages(threadID); 44 | return Response.json({ 45 | success: respdata?.data ? true : false, 46 | data: respdata?.data 47 | ? respdata?.data 48 | ?.map( 49 | (val: { role: any; content: { text: { value: any } }[] }) => { 50 | console.log({ resp: val?.content }); 51 | 52 | if (val?.role !== "system") 53 | return { 54 | role: val?.role, 55 | content: val?.content?.[0]?.text?.value || "", 56 | }; 57 | } 58 | ) 59 | ?.filter((val: any) => val) 60 | : [], 61 | }); 62 | } 63 | return Response.json({ 64 | success: false, 65 | data: [], 66 | }); 67 | } 68 | 69 | return Response.json({ 70 | success: resp?.success, 71 | data: [], 72 | }); 73 | } 74 | 75 | export async function GET(request: Request) { 76 | const { searchParams } = new URL(request.url); 77 | const threadID = searchParams.get("threadID"); 78 | if (!threadID) return Response.json({ success: false, data: [] }); 79 | 80 | const resp = await getMessages(threadID); 81 | 82 | return Response.json({ 83 | success: resp?.data ? true : false, 84 | data: resp?.data 85 | ? resp?.data 86 | ?.map((val: { role: any; content: { text: { value: any } }[] }) => { 87 | console.log({ resp: val?.content }); 88 | 89 | if (val?.role !== "system") 90 | return { 91 | role: val?.role, 92 | content: val?.content?.[0]?.text?.value || "", 93 | }; 94 | }) 95 | ?.filter((val: any) => val) 96 | : [], 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /chatapp/app/api/listThreads/route.ts: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | export async function GET() { 4 | const resp = await axios 5 | .get("https://studio.tune.app/v1/threads?limit=1000", { 6 | headers: { 7 | "Content-Type": "application/json", 8 | "x-tune-key": process.env.TUNE_API_KEY, 9 | }, 10 | }) 11 | .catch((err: any) => { 12 | return err; 13 | }) 14 | .then((res: any) => { 15 | return res?.data; 16 | }); 17 | 18 | return Response.json({ 19 | success: resp?.data ? true : false, 20 | data: resp?.data ? resp?.data : [], 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /chatapp/app/api/threadID/route.ts: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | export async function POST(request: Request) { 4 | const requestBody = await request.json(); 5 | const title = requestBody.title; 6 | 7 | const resp = await axios 8 | .post( 9 | `https://studio.tune.app/v1/threads`, 10 | { 11 | datasetId: "", 12 | messages: [], 13 | title: title, 14 | }, 15 | { 16 | headers: { 17 | "Content-Type": "application/json", 18 | "x-tune-key": process.env.TUNE_API_KEY, 19 | }, 20 | } 21 | ) 22 | .catch((err: any) => { 23 | return err; 24 | }) 25 | .then((res: any) => { 26 | return res?.data; 27 | }); 28 | 29 | return Response.json({ 30 | success: resp ? true : false, 31 | data: resp ? resp : {}, 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /chatapp/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/chatapp/app/favicon.ico -------------------------------------------------------------------------------- /chatapp/app/globals.css: -------------------------------------------------------------------------------- 1 | @import url("https://use.typekit.net/bko1rkx.css"); 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | body { 7 | font-family: "proxima-nova", -apple-system, BlinkMacSystemFont, "Segoe UI", 8 | "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", 9 | "Helvetica Neue", sans-serif; 10 | @apply dark:text-dark-text-base dark:bg-dark-background-app bg-light-background-app text-light-text-base h-screen w-screen antialiased overflow-hidden; 11 | } 12 | 13 | *::-webkit-scrollbar { 14 | display: none !important; 15 | width: 0px; 16 | } 17 | 18 | * { 19 | -ms-overflow-style: none; 20 | scrollbar-width: none; 21 | } 22 | 23 | thead { 24 | position: sticky; 25 | top: 0; 26 | z-index: 100; 27 | } 28 | 29 | .addScrollBars::-webkit-scrollbar { 30 | display: block !important; 31 | width: 10px; 32 | } 33 | 34 | .activeMenuWrapper, 35 | .menuActive { 36 | background: #eceff3 !important; 37 | } 38 | 39 | .activeWorkProfile { 40 | background: #fff !important; 41 | @apply border-light-border-base dark:border-dark-border-base border; 42 | } 43 | 44 | .dark .activeMenuWrapper, 45 | .dark .activeWorkProfile { 46 | background: linear-gradient( 47 | 90deg, 48 | rgba(126, 67, 255, 0) 0%, 49 | rgba(244, 102, 193, 0.2) 100% 50 | ), 51 | linear-gradient( 52 | 0deg, 53 | rgba(165, 117, 255, 0.2) 0%, 54 | rgba(165, 117, 255, 0.2) 100% 55 | ), 56 | var(--bg-surface-container-low, #282828) !important; 57 | } 58 | 59 | .projectListing { 60 | display: grid; 61 | grid-template-columns: repeat(auto-fill, minmax(333px, auto)); 62 | overflow: scroll; 63 | padding-bottom: 64px !important; 64 | } 65 | 66 | .baseModelListing { 67 | display: grid; 68 | grid-template-columns: repeat(auto-fill, minmax(256px, auto)); 69 | overflow: scroll; 70 | } 71 | 72 | p, 73 | span, 74 | div { 75 | @apply antialiased; 76 | } 77 | 78 | :hover.playgroundInput .highlight { 79 | @apply bg-light-background-surfaceLowHover dark:bg-dark-background-surfaceLowHover; 80 | } 81 | 82 | .playgroundInput .playgroundTrash { 83 | display: none !important; 84 | } 85 | 86 | :hover.playgroundInput .playgroundTrash { 87 | display: flex !important; 88 | position: absolute; 89 | right: 12px; 90 | top: 12px; 91 | } 92 | 93 | .jsonContainer { 94 | @apply bg-light-background-surfaceLow dark:bg-dark-background-surfaceLow font-mono text-[12px] font-[400]; 95 | } 96 | 97 | .checkboxHolder { 98 | @apply flex justify-center items-center; 99 | } 100 | 101 | .textBase { 102 | @apply text-light-text-base dark:text-dark-text-base; 103 | } 104 | 105 | .textSub { 106 | @apply text-light-text-subtle dark:text-dark-text-subtle; 107 | } 108 | 109 | .textMute { 110 | @apply text-light-text-muted dark:text-dark-text-muted; 111 | } 112 | 113 | .borderBase { 114 | @apply border-light-border-base dark:border-dark-border-base; 115 | } 116 | 117 | .interactiveBg { 118 | background: linear-gradient( 119 | 180deg, 120 | rgba(126, 67, 255, 0) 0%, 121 | rgba(244, 102, 193, 0.2) 100% 122 | ), 123 | #6025e1; 124 | box-shadow: 0px 0px 0px 1px rgba(72, 1, 228, 0.76), 125 | 0px 1px 2px 0px rgba(37, 17, 79, 0.4); 126 | } 127 | 128 | .bgSurface { 129 | @apply bg-light-background-surface dark:bg-dark-background-surface; 130 | } 131 | 132 | .bgSurfaceHigh { 133 | @apply bg-light-background-surfaceHigh dark:bg-dark-background-surfaceHigh; 134 | } 135 | 136 | .bgSurfaceLow { 137 | @apply bg-light-background-surfaceLow dark:bg-dark-background-surfaceLow; 138 | } 139 | 140 | .bgSurfaceLowHover { 141 | @apply bg-light-background-surfaceLowHover dark:bg-dark-background-surfaceLowHover; 142 | } 143 | 144 | .output .tooltip { 145 | display: none !important; 146 | } 147 | 148 | .output:hover .tooltip { 149 | display: inline-block !important; 150 | } 151 | 152 | .furyBg:hover { 153 | opacity: 0.2; 154 | background: conic-gradient( 155 | from 180deg at 50% 50%, 156 | #fff 38.099480867385864deg, 157 | #67f6ff 87.83106923103333deg, 158 | #41afff 258.75deg, 159 | rgba(169, 151, 239, 0.7) 346.8750071525574deg 160 | ); 161 | } 162 | 163 | .langchainBg:hover { 164 | opacity: 0.2; 165 | background: conic-gradient( 166 | from 180deg at 50% 50%, 167 | #fff 38.099480867385864deg, 168 | #67ff76 87.83106923103333deg, 169 | #41ffbb 258.75deg, 170 | rgba(169, 151, 239, 0.7) 346.8750071525574deg 171 | ); 172 | } 173 | 174 | .tuneSlider { 175 | -webkit-appearance: none; 176 | appearance: none; 177 | width: 100%; 178 | height: 6px; 179 | border-radius: 3px; 180 | outline: none; 181 | -webkit-transition: 0.2s; 182 | transition: opacity 0.2s; 183 | @apply bg-light-background-control dark:bg-dark-background-control; 184 | } 185 | 186 | .tuneSlider::-webkit-slider-thumb { 187 | appearance: none; 188 | width: 18px; 189 | height: 18px; 190 | border: 0; 191 | background-image: url("/range-thumb.svg"); 192 | background-size: contain; 193 | background-position: center center; 194 | background-repeat: no-repeat; 195 | cursor: pointer; 196 | margin-top: -6px; 197 | -webkit-appearance: none; 198 | /* box-shadow: -200px 0 0 200px #6025e0; */ 199 | } 200 | 201 | .tuneSlider::-moz-range-thumb { 202 | width: 18px; 203 | height: 18px; 204 | border: 0; 205 | background-image: url("/range-thumb.svg"); 206 | background-size: contain; 207 | background-position: center center; 208 | background-repeat: no-repeat; 209 | cursor: pointer; 210 | } 211 | 212 | .tuneSlider::-moz-range-progress { 213 | @apply bg-light-background-interactiveHover dark:bg-dark-background-interactiveHover; 214 | height: 6px; 215 | border-radius: 3px; 216 | } 217 | 218 | .tuneSlider::-webkit-slider-runnable-track { 219 | height: 6px; 220 | border-radius: 3px; 221 | -webkit-appearance: none; 222 | /* overflow: hidden; */ 223 | } 224 | 225 | @keyframes collapseIn { 226 | 0% { 227 | min-width: 280px; 228 | max-width: 280px; 229 | width: 100%; 230 | opacity: 1; 231 | } 232 | 100% { 233 | min-width: 0px; 234 | max-width: 0px; 235 | width: 0%; 236 | opacity: 0; 237 | } 238 | } 239 | 240 | .collapseSidebar { 241 | animation: collapseIn 0.2s forwards; 242 | display: none !important; 243 | } 244 | 245 | @keyframes collapseOut { 246 | 0% { 247 | min-width: 0px; 248 | max-width: 0px; 249 | width: 0%; 250 | opacity: 0; 251 | } 252 | 100% { 253 | min-width: 280px; 254 | max-width: 280px; 255 | width: 100%; 256 | opacity: 1; 257 | } 258 | } 259 | 260 | .expandSidebar { 261 | animation: collapseOut 0.2s forwards; 262 | } 263 | 264 | .renderCode, 265 | .renderCode pre { 266 | @apply bg-light-background-surfaceLow dark:bg-dark-background-surfaceLow mini; 267 | } 268 | 269 | .renderCode pre code { 270 | @apply bg-light-background-surfaceLow dark:bg-dark-background-surfaceLow text-[#ff6a1f!important] mini; 271 | } 272 | 273 | .renderCode pre { 274 | @apply mini font-mono bg-light-background-surfaceLow dark:bg-dark-background-surfaceLow; 275 | } 276 | 277 | .renderCode .hljs-keyword { 278 | @apply text-light-text-interactive dark:text-dark-text-interactive; 279 | } 280 | 281 | .renderCode .hljs-comment { 282 | @apply text-light-text-muted dark:text-dark-text-muted; 283 | } 284 | 285 | .renderCode span { 286 | @apply text-[#000] dark:text-[#fff]; 287 | } 288 | 289 | .renderCode .hljs-attr { 290 | @apply text-light-text-critical dark:text-dark-text-critical; 291 | } 292 | 293 | .renderCode .hljs-string { 294 | @apply text-[#00A67D] dark:text-[#00A67D]; 295 | } 296 | 297 | .renderCode .hljs-built_in { 298 | @apply text-[#DF3079] dark:text-[#DF3079]; 299 | } 300 | 301 | .renderCode .hljs-number { 302 | @apply text-[#00A67D] dark:text-[#00A67D]; 303 | } 304 | 305 | .apiHolder .bgInput { 306 | box-shadow: none !important; 307 | } 308 | 309 | .borderInput { 310 | @apply border-light-border-borderInput dark:border-dark-border-borderInput; 311 | } 312 | 313 | .playground .bgInput { 314 | box-shadow: none !important; 315 | border: 0px !important; 316 | } 317 | 318 | .playground .bgInput textarea { 319 | padding: 0px !important; 320 | } 321 | 322 | .textCompleteText { 323 | @apply medium; 324 | } 325 | 326 | .DraftEditor-root mark { 327 | @apply bg-light-text-success/20 dark:bg-dark-text-success/20 textBase; 328 | } 329 | 330 | .DraftEditor-root, 331 | .DraftEditor-editorContainer, 332 | .public-DraftEditor-content { 333 | @apply h-full; 334 | } 335 | 336 | .notranslate div { 337 | overflow: auto; 338 | } 339 | 340 | .public-DraftStyleDefault-block { 341 | pointer-events: none; 342 | } 343 | 344 | .playgroundUserCard .prose-nbx { 345 | height: 100%; 346 | max-height: none; 347 | pointer-events: painted; 348 | } 349 | 350 | .shadowMd { 351 | box-shadow: 0px 2px 4px 0px rgba(31, 33, 36, 0.05); 352 | } 353 | 354 | .dark .shadowMd { 355 | box-shadow: 0px 2px 6px 0px rgba(9, 9, 9, 0.24); 356 | } 357 | 358 | .shadowLg { 359 | box-shadow: 0px 2px 4px 0px rgba(31, 33, 36, 0.05), 360 | 0px 4px 12px 0px rgba(31, 33, 36, 0.06), 0px 2px 6px 0px rgba(9, 9, 9, 0.24); 361 | } 362 | 363 | .dark .shadowLg { 364 | box-shadow: 0px 4px 16px 0px rgba(9, 9, 9, 0.5); 365 | } 366 | 367 | .changeLogHolder h1, 368 | .changeLogHolder h2, 369 | .changeLogHolder h3, 370 | .changeLogHolder h4, 371 | .changeLogHolder h5, 372 | .changeLogHolder h6 { 373 | @apply mb-[8px] textBase medium-pl font-[600]; 374 | } 375 | 376 | .changeLogHolder li { 377 | @apply mb-[8px] textMute medium list-outside list-disc ml-[16px]; 378 | } 379 | 380 | .changeLogHolder p, 381 | .changeLogHolder span { 382 | @apply mb-[8px] textMute medium; 383 | } 384 | 385 | .changeLogHolder code { 386 | @apply bg-light-background-interactive mini text-dark-text-onColorr px-[4px]; 387 | } 388 | 389 | .markdownHolder { 390 | font-size: 16px; 391 | } 392 | 393 | .markdownHolder p { 394 | @apply mb-[8px] regular; 395 | } 396 | 397 | .markdownHolder pre { 398 | @apply medium font-mono rounded-md; 399 | } 400 | 401 | /* if p is inside li */ 402 | .markdownHolder li p { 403 | @apply mb-[0px] inline; 404 | } 405 | 406 | .markdownHolder ol { 407 | @apply list-inside list-decimal; 408 | } 409 | 410 | .markdownHolder ul { 411 | @apply list-inside list-disc; 412 | } 413 | 414 | .markdownHolder a { 415 | @apply text-light-text-interactive dark:text-dark-text-interactive; 416 | } 417 | 418 | .markdownHolder th { 419 | white-space: nowrap; 420 | padding: 4px 8px; 421 | } 422 | 423 | ul { 424 | list-style-type: disc; 425 | margin-left: 14px; 426 | @apply list-inside; 427 | } 428 | 429 | ol { 430 | list-style-type: conic-gradient; 431 | list-style: decimal; 432 | margin-left: 16px; 433 | @apply list-inside; 434 | } 435 | 436 | .monaco-editor-background, 437 | .margin-view-overlays { 438 | @apply bg-light-background-input dark:bg-dark-background-input; 439 | } 440 | 441 | .dark input:-webkit-autofill, 442 | .dark input:-webkit-autofill:hover, 443 | .dark input:-webkit-autofill:focus, 444 | .dark input:-webkit-autofill:active { 445 | -webkit-background-clip: text; 446 | -webkit-text-fill-color: #ffffff; 447 | transition: background-color 5000s ease-in-out 0s; 448 | box-shadow: inset 0 0 20px 20px #23232329; 449 | } 450 | 451 | input:-webkit-autofill, 452 | input:-webkit-autofill:hover, 453 | input:-webkit-autofill:focus, 454 | input:-webkit-autofill:active { 455 | -webkit-background-clip: text; 456 | -webkit-text-fill-color: #000; 457 | transition: background-color 5000s ease-in-out 0s; 458 | box-shadow: inset 0 0 20px 20px #ffffff; 459 | } 460 | .isSkelton { 461 | background: #eee; 462 | background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%); 463 | border-radius: 5px; 464 | background-size: 200% 100%; 465 | animation: 1.5s shine linear infinite; 466 | } 467 | 468 | .dark .language-shell { 469 | color: #fff !important; 470 | } 471 | 472 | .language-shell { 473 | color: #000 !important; 474 | } 475 | -------------------------------------------------------------------------------- /chatapp/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import "chainfury/dist/esm/index.css"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "ChatApp", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /chatapp/components/sidebar.tsx: -------------------------------------------------------------------------------- 1 | import SvgPlus from "chainfury/dist/esm/Icons/Plus"; 2 | 3 | import ChatList from "@/app/Svgcomps/ChatList"; 4 | import { useRouter } from "next/navigation"; 5 | 6 | const Sidebar = ({ 7 | loading, 8 | threadList, 9 | threadListLoader, 10 | threadID, 11 | setThreadID, 12 | setChats, 13 | }: { 14 | loading: boolean; 15 | threadList: any[]; 16 | threadListLoader: boolean; 17 | threadID: string; 18 | setThreadID: (threadID: string) => void; 19 | setChats: (chats: any[]) => void; 20 | }) => { 21 | const router = useRouter(); 22 | return ( 23 |
24 | 37 |
38 | RECENT CHATS 39 |
40 |
41 |
42 | {threadList 43 | .map((thread, index) => ( 44 |
{ 54 | if (threadListLoader || loading) return; 55 | setThreadID(thread?.id); 56 | }} 57 | > 58 | 65 | 66 | {thread?.title.charAt(0).toUpperCase()} 67 | {thread?.title.slice(1)} 68 | 69 |
70 | )) 71 | .reverse()} 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default Sidebar; 78 | -------------------------------------------------------------------------------- /chatapp/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /chatapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.7.4", 13 | "chainfury": "^0.0.1-alpha.7", 14 | "next": "14.2.10", 15 | "react": "^18", 16 | "react-dom": "^18", 17 | "react-markdown": "^9.0.1", 18 | "rehype-raw": "^7.0.0", 19 | "remark-gfm": "^4.0.0", 20 | "unique-names-generator": "^4.7.1" 21 | }, 22 | "devDependencies": { 23 | "@tailwindcss/typography": "^0.5.12", 24 | "@types/node": "^20", 25 | "@types/react": "^18", 26 | "@types/react-dom": "^18", 27 | "eslint": "^8", 28 | "eslint-config-next": "14.2.2", 29 | "postcss": "^8", 30 | "tailwindcss": "^3.4.1", 31 | "typescript": "^5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chatapp/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /chatapp/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chatapp/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chatapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /chatapp/utility/chats.utilities.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const getMessages = async (threadID: string) => { 4 | return await axios 5 | .get(`https://studio.tune.app/v1/threads/${threadID}/messages`, { 6 | headers: { 7 | "Content-Type": "application/json", 8 | "x-tune-key": process.env.TUNE_API_KEY, 9 | }, 10 | }) 11 | .catch((err: any) => { 12 | return err; 13 | }) 14 | .then((res: any) => { 15 | return res?.data; 16 | }); 17 | }; 18 | 19 | export { getMessages }; 20 | -------------------------------------------------------------------------------- /chatapp/utility/run.utitities.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const getModelResponse = async (threadID: string) => { 4 | return await axios 5 | .post( 6 | `https://studio.tune.app/v1/threads/${threadID}/runs`, 7 | { 8 | model: process.env.TUNE_MODEL, 9 | threadId: threadID, 10 | }, 11 | { 12 | headers: { 13 | "Content-Type": "application/json", 14 | "x-tune-key": process.env.TUNE_API_KEY, 15 | }, 16 | } 17 | ) 18 | .catch((err: any) => { 19 | return { success: false }; 20 | }) 21 | .then((res: any) => { 22 | return { success: true }; 23 | }); 24 | }; 25 | 26 | export { getModelResponse }; 27 | -------------------------------------------------------------------------------- /community/customer-support/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /community/customer-support/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /community/customer-support/README.md: -------------------------------------------------------------------------------- 1 | # Customer Support AI 2 | 3 | Customer Support AI is an intelligent application that leverages the power of AI to resolve customer queries about products or services efficiently. Instead of requiring customers to search through extensive website content, this application utilizes the Tune Studio API's threads and assistant capabilities to provide accurate and timely responses. 4 | 5 | ## Features 6 | 7 | - AI-powered customer support assistant 8 | - Customizable assistant prompts 9 | - Updatable knowledge base 10 | - Real-time query resolution 11 | - Integration with Tune Studio API 12 | 13 | ## Getting Started 14 | 15 | ### Prerequisites 16 | 17 | - Node.js (v14 or later) 18 | - npm (v6 or later) 19 | - Tune Studio API key 20 | 21 | ### Installation 22 | 23 | 1. Clone the repository: 24 | 25 | ``` 26 | git clone 27 | ``` 28 | 29 | 2. Navigate to the project directory: 30 | 31 | ``` 32 | cd customer-support 33 | ``` 34 | 35 | 3. Install dependencies: 36 | 37 | ``` 38 | npm install 39 | ``` 40 | 41 | 4. Create a `.env` file in the root directory and add your Tune Studio API key: 42 | 43 | ``` 44 | TUNE_STUDIO_API_KEY=your_api_key_here 45 | ``` 46 | 47 | 5. Start the application: 48 | 49 | ``` 50 | npm start 51 | ``` 52 | 53 | ## Usage 54 | 55 | 1. **Assistant Prompt Editing**: Customize the AI assistant's behavior by editing its prompt through the admin interface. 56 | 57 | 2. **Knowledge Base Update**: Keep your AI assistant up-to-date by adding new information to its knowledge base. Currently supports text input, with URL and PDF support coming soon. 58 | 59 | 3. **Query Resolution**: Users can input their questions, and the AI assistant will provide relevant answers based on the current knowledge base and prompt settings. 60 | 61 | ## Upcoming Features 62 | 63 | - URL input for knowledge base updates 64 | - PDF document support for knowledge base expansion 65 | -------------------------------------------------------------------------------- /community/customer-support/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /community/customer-support/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /community/customer-support/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "customer-support", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-dialog": "^1.1.1", 13 | "@radix-ui/react-icons": "^1.3.0", 14 | "@radix-ui/react-scroll-area": "^1.1.0", 15 | "@radix-ui/react-slot": "^1.1.0", 16 | "class-variance-authority": "^0.7.0", 17 | "clsx": "^2.1.1", 18 | "lucide-react": "^0.445.0", 19 | "next": "14.2.13", 20 | "react": "^18", 21 | "react-dom": "^18", 22 | "tailwind-merge": "^2.5.2", 23 | "tailwindcss-animate": "^1.0.7" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^20", 27 | "@types/react": "^18", 28 | "@types/react-dom": "^18", 29 | "eslint": "^8", 30 | "eslint-config-next": "14.2.13", 31 | "postcss": "^8", 32 | "tailwindcss": "^3.4.1", 33 | "typescript": "^5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /community/customer-support/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /community/customer-support/src/app/api/threads/[threadId]/route.ts: -------------------------------------------------------------------------------- 1 | import { getErrorMessage } from "@/lib/utils"; 2 | 3 | export async function POST(request: Request) { 4 | try { 5 | const { threadId, content } = await request.json(); 6 | 7 | const first = await fetch( 8 | `https://studio.tune.app/v1/threads/${threadId}/messages`, 9 | { 10 | method: "POST", 11 | headers: { 12 | "Content-Type": "application/json", 13 | "x-tune-key": process.env.TUNE_API_KEY!, 14 | }, 15 | body: JSON.stringify({ 16 | assistantId: "", 17 | content, 18 | fileIds: [], 19 | role: "user", 20 | runId: "", 21 | threadId: threadId, 22 | }), 23 | } 24 | ); 25 | console.log("first", first); 26 | 27 | const second = await fetch( 28 | `https://studio.tune.app/v1/threads/${threadId}/runs`, 29 | { 30 | method: "POST", 31 | headers: { 32 | "Content-Type": "application/json", 33 | "x-tune-key": process.env.TUNE_API_KEY!, 34 | }, 35 | body: JSON.stringify({ 36 | model: process.env.TUNE_MODEL, 37 | threadId: threadId, 38 | }), 39 | } 40 | ); 41 | console.log("second", second); 42 | 43 | const messagesData = await getMessages(threadId); 44 | const messages = filterMessages(messagesData.data); 45 | 46 | return Response.json({ 47 | success: true, 48 | data: messages, 49 | }); 50 | } catch (error) { 51 | return Response.json({ 52 | status: false, 53 | message: `Error posting message: ${getErrorMessage(error)}`, 54 | }); 55 | } 56 | } 57 | 58 | export async function GET( 59 | request: Request, 60 | { params: { threadId } }: { params: { threadId: string } } 61 | ) { 62 | try { 63 | if (!threadId) throw "Thread ID is missing"; 64 | 65 | const resp = await getMessages(threadId); 66 | 67 | return Response.json({ 68 | success: resp?.data ? true : false, 69 | data: filterMessages(resp.data), 70 | }); 71 | } catch (error) { 72 | return Response.json({ 73 | status: false, 74 | message: `Error getting messages: ${getErrorMessage(error)}`, 75 | }); 76 | } 77 | } 78 | 79 | async function getMessages(threadId: string) { 80 | return fetch(`https://studio.tune.app/v1/threads/${threadId}/messages`, { 81 | headers: { 82 | "Content-Type": "application/json", 83 | "x-tune-key": process.env.TUNE_API_KEY!, 84 | }, 85 | }).then((res) => res.json()); 86 | } 87 | 88 | interface TuneMessage { 89 | role: string; 90 | content: { text: { value: string } }[]; 91 | } 92 | 93 | function filterMessages(messages: TuneMessage[]) { 94 | return messages 95 | .map((message: TuneMessage) => { 96 | if (message?.role !== "system") 97 | return { 98 | role: message?.role, 99 | content: 100 | typeof message?.content?.[0]?.text === "string" 101 | ? message?.content?.[0]?.text 102 | : message?.content?.[0]?.text?.value, 103 | }; 104 | }) 105 | .filter((message) => message); 106 | } 107 | -------------------------------------------------------------------------------- /community/customer-support/src/app/api/threads/route.ts: -------------------------------------------------------------------------------- 1 | import { Thread } from "@/lib/types"; 2 | import { getErrorMessage } from "@/lib/utils"; 3 | 4 | interface TuneThread { 5 | id: number; 6 | object: "thread"; 7 | createdAt: string; 8 | metadata: object; 9 | title: string; 10 | toolResources: object; 11 | } 12 | 13 | export async function GET() { 14 | try { 15 | const res = await fetch("https://studio.tune.app/v1/threads?limit=1000", { 16 | headers: { 17 | "Content-Type": "application/json", 18 | "x-tune-key": process.env.TUNE_API_KEY!, 19 | }, 20 | }); 21 | const data = await res.json(); 22 | const threads: Thread[] = data.data 23 | .map((thread: TuneThread) => ({ 24 | id: thread.id, 25 | title: thread.title.charAt(0).toUpperCase() + thread.title.slice(1), 26 | createdAt: new Date(thread.createdAt).toLocaleString(), 27 | })) 28 | .reverse(); 29 | return Response.json({ 30 | status: true, 31 | data: threads, 32 | }); 33 | } catch (error) { 34 | return Response.json({ 35 | status: false, 36 | message: `Error fetching threads: ${getErrorMessage(error)}`, 37 | }); 38 | } 39 | } 40 | 41 | export async function POST(request: Request) { 42 | try { 43 | const { title } = await request.json(); 44 | 45 | const resp = await fetch(`https://studio.tune.app/v1/threads`, { 46 | method: "POST", 47 | headers: { 48 | "Content-Type": "application/json", 49 | "x-tune-key": process.env.TUNE_API_KEY!, 50 | }, 51 | body: JSON.stringify({ 52 | datasetId: "", 53 | messages: [], 54 | title: title, 55 | }), 56 | }); 57 | const respData = await resp.json(); 58 | return Response.json({ 59 | status: true, 60 | data: respData.data, 61 | }); 62 | } catch (error) { 63 | return Response.json({ 64 | status: false, 65 | message: `Error creating thread: ${getErrorMessage(error)}`, 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /community/customer-support/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/community/customer-support/src/app/favicon.ico -------------------------------------------------------------------------------- /community/customer-support/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 240 10% 3.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 240 10% 3.9%; 13 | --primary: 240 5.9% 10%; 14 | --primary-foreground: 0 0% 98%; 15 | --secondary: 240 4.8% 95.9%; 16 | --secondary-foreground: 240 5.9% 10%; 17 | --muted: 240 4.8% 95.9%; 18 | --muted-foreground: 240 3.8% 46.1%; 19 | --accent: 240 4.8% 95.9%; 20 | --accent-foreground: 240 5.9% 10%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 0 0% 98%; 23 | --border: 240 5.9% 90%; 24 | --input: 240 5.9% 90%; 25 | --ring: 240 10% 3.9%; 26 | --chart-1: 12 76% 61%; 27 | --chart-2: 173 58% 39%; 28 | --chart-3: 197 37% 24%; 29 | --chart-4: 43 74% 66%; 30 | --chart-5: 27 87% 67%; 31 | --radius: 0.5rem; 32 | } 33 | .dark { 34 | --background: 240 10% 3.9%; 35 | --foreground: 0 0% 98%; 36 | --card: 240 10% 3.9%; 37 | --card-foreground: 0 0% 98%; 38 | --popover: 240 10% 3.9%; 39 | --popover-foreground: 0 0% 98%; 40 | --primary: 0 0% 98%; 41 | --primary-foreground: 240 5.9% 10%; 42 | --secondary: 240 3.7% 15.9%; 43 | --secondary-foreground: 0 0% 98%; 44 | --muted: 240 3.7% 15.9%; 45 | --muted-foreground: 240 5% 64.9%; 46 | --accent: 240 3.7% 15.9%; 47 | --accent-foreground: 0 0% 98%; 48 | --destructive: 0 62.8% 30.6%; 49 | --destructive-foreground: 0 0% 98%; 50 | --border: 240 3.7% 15.9%; 51 | --input: 240 3.7% 15.9%; 52 | --ring: 240 4.9% 83.9%; 53 | --chart-1: 220 70% 50%; 54 | --chart-2: 160 60% 45%; 55 | --chart-3: 30 80% 55%; 56 | --chart-4: 280 65% 60%; 57 | --chart-5: 340 75% 55%; 58 | } 59 | } 60 | @layer base { 61 | * { 62 | @apply border-border; 63 | } 64 | body { 65 | @apply bg-background text-foreground; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /community/customer-support/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import { cn } from "@/lib/utils"; 4 | import "./globals.css"; 5 | 6 | const fontHeading = Inter({ 7 | subsets: ["latin"], 8 | display: "swap", 9 | variable: "--font-heading", 10 | }); 11 | 12 | const fontBody = Inter({ 13 | subsets: ["latin"], 14 | display: "swap", 15 | variable: "--font-body", 16 | }); 17 | 18 | export const metadata: Metadata = { 19 | title: "Customer Support", 20 | description: "An RAG app built with Tune Studio", 21 | }; 22 | 23 | export default function RootLayout({ 24 | children, 25 | }: Readonly<{ 26 | children: React.ReactNode; 27 | }>) { 28 | return ( 29 | 30 | 33 | {children} 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /community/customer-support/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | import { Button } from "@/components/ui/button"; 5 | import { Card } from "@/components/ui/card"; 6 | import { ScrollArea } from "@/components/ui/scroll-area"; 7 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; 8 | import { 9 | Headphones, 10 | Settings, 11 | BookOpen, 12 | MessageSquare, 13 | Send, 14 | Menu, 15 | Plus, 16 | Loader, 17 | } from "lucide-react"; 18 | import { GitHubLogoIcon } from "@radix-ui/react-icons"; 19 | import { Message, Thread } from "@/lib/types"; 20 | 21 | export default function Dashboard() { 22 | const [messages, setMessages] = useState([ 23 | { id: 1, content: "Hello! How can I help you today?", sender: "assistant" }, 24 | { id: 2, content: "I have a question about your product.", sender: "user" }, 25 | ]); 26 | const [inputMessage, setInputMessage] = useState(""); 27 | const [isSidebarOpen, setIsSidebarOpen] = useState(false); 28 | const [threads, setThreads] = useState([]); 29 | const [selectedThread, setSelectedThread] = useState(1); 30 | const [threadListLoader, setThreadListLoader] = useState(false); 31 | 32 | useEffect(() => { 33 | setThreadListLoader(true); 34 | // Fetch threads 35 | fetch("/api/threads") 36 | .then((res) => res.json()) 37 | .then((data) => { 38 | if (data.status) { 39 | setThreads(data.data); 40 | } else { 41 | alert(data.message); 42 | } 43 | }) 44 | .finally(() => setThreadListLoader(false)); 45 | }, []); 46 | 47 | useEffect(() => { 48 | if (!selectedThread) return; 49 | setThreadListLoader(true); 50 | fetch(`/api/threads/${selectedThread}`) 51 | .then((res) => res.json()) 52 | .then((data) => { 53 | if (data.status) { 54 | setMessages(data.data.messages); 55 | } else { 56 | alert(data.message); 57 | } 58 | }) 59 | .finally(() => setThreadListLoader(false)); 60 | }, [selectedThread]); 61 | 62 | const handleSendMessage = () => { 63 | if (inputMessage.trim()) { 64 | setMessages([ 65 | ...messages, 66 | { id: messages.length + 1, content: inputMessage, sender: "user" }, 67 | ]); 68 | setInputMessage(""); 69 | } 70 | }; 71 | 72 | const handleCreateNewChat = () => { 73 | const newThread = { 74 | id: threads.length + 1, 75 | title: `Chat ${threads.length + 1}`, 76 | createdAt: new Date().toLocaleString(), 77 | }; 78 | setThreads([...threads, newThread]); 79 | setSelectedThread(newThread.id); 80 | setMessages([]); 81 | }; 82 | 83 | return ( 84 |
85 | {/* Header */} 86 |
87 |
88 | 89 | Customer Support 90 |
91 | 95 |
96 | 97 | {/* Main content */} 98 |
99 | {/* Sidebar - hidden on mobile */} 100 | 109 | 110 | {/* Main container */} 111 |
112 |
113 | 114 | 115 | 118 | 119 | 120 | 127 | 128 | 129 |
130 | 136 |
137 |
138 |
139 | ); 140 | } 141 | 142 | function Sidebar({ 143 | threads, 144 | selectedThread, 145 | setSelectedThread, 146 | handleCreateNewChat, 147 | threadListLoader, 148 | }: Readonly<{ 149 | threads: Thread[]; 150 | selectedThread: number; 151 | setSelectedThread: (id: number) => void; 152 | handleCreateNewChat: () => void; 153 | threadListLoader: boolean; 154 | }>) { 155 | return ( 156 |
157 |
Settings
158 | 164 | 170 |
Manage threads
171 | {threadListLoader ? ( 172 |
173 | 174 |
175 | ) : ( 176 | 177 | {threads.map((thread) => ( 178 | 186 | ))} 187 | 188 | )} 189 | 192 |
193 | ); 194 | } 195 | 196 | function ChatMessages({ 197 | messages, 198 | inputMessage, 199 | setInputMessage, 200 | handleSendMessage, 201 | }: Readonly<{ 202 | messages: Message[]; 203 | inputMessage: string; 204 | setInputMessage: (value: string) => void; 205 | handleSendMessage: () => void; 206 | }>) { 207 | return ( 208 | 209 | 210 | {messages.map((message) => ( 211 |
217 | 224 | {message.content} 225 | 226 |
227 | ))} 228 |
229 |
230 | setInputMessage(e.target.value)} 234 | placeholder="Type your message..." 235 | className="flex-1 p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-primary" 236 | onKeyDown={(e) => { 237 | if (e.key === "Enter") { 238 | handleSendMessage(); 239 | } 240 | }} 241 | /> 242 | 245 |
246 |
247 | ); 248 | } 249 | -------------------------------------------------------------------------------- /community/customer-support/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /community/customer-support/src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

41 | )) 42 | CardTitle.displayName = "CardTitle" 43 | 44 | const CardDescription = React.forwardRef< 45 | HTMLParagraphElement, 46 | React.HTMLAttributes 47 | >(({ className, ...props }, ref) => ( 48 |

53 | )) 54 | CardDescription.displayName = "CardDescription" 55 | 56 | const CardContent = React.forwardRef< 57 | HTMLDivElement, 58 | React.HTMLAttributes 59 | >(({ className, ...props }, ref) => ( 60 |

61 | )) 62 | CardContent.displayName = "CardContent" 63 | 64 | const CardFooter = React.forwardRef< 65 | HTMLDivElement, 66 | React.HTMLAttributes 67 | >(({ className, ...props }, ref) => ( 68 |
73 | )) 74 | CardFooter.displayName = "CardFooter" 75 | 76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 77 | -------------------------------------------------------------------------------- /community/customer-support/src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ScrollArea = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, children, ...props }, ref) => ( 12 | 17 | 18 | {children} 19 | 20 | 21 | 22 | 23 | )) 24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 25 | 26 | const ScrollBar = React.forwardRef< 27 | React.ElementRef, 28 | React.ComponentPropsWithoutRef 29 | >(({ className, orientation = "vertical", ...props }, ref) => ( 30 | 43 | 44 | 45 | )) 46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 47 | 48 | export { ScrollArea, ScrollBar } 49 | -------------------------------------------------------------------------------- /community/customer-support/src/components/ui/sheet.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SheetPrimitive from "@radix-ui/react-dialog" 5 | import { Cross2Icon } from "@radix-ui/react-icons" 6 | import { cva, type VariantProps } from "class-variance-authority" 7 | 8 | import { cn } from "@/lib/utils" 9 | 10 | const Sheet = SheetPrimitive.Root 11 | 12 | const SheetTrigger = SheetPrimitive.Trigger 13 | 14 | const SheetClose = SheetPrimitive.Close 15 | 16 | const SheetPortal = SheetPrimitive.Portal 17 | 18 | const SheetOverlay = React.forwardRef< 19 | React.ElementRef, 20 | React.ComponentPropsWithoutRef 21 | >(({ className, ...props }, ref) => ( 22 | 30 | )) 31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 32 | 33 | const sheetVariants = cva( 34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", 35 | { 36 | variants: { 37 | side: { 38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", 39 | bottom: 40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", 41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", 42 | right: 43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", 44 | }, 45 | }, 46 | defaultVariants: { 47 | side: "right", 48 | }, 49 | } 50 | ) 51 | 52 | interface SheetContentProps 53 | extends React.ComponentPropsWithoutRef, 54 | VariantProps {} 55 | 56 | const SheetContent = React.forwardRef< 57 | React.ElementRef, 58 | SheetContentProps 59 | >(({ side = "right", className, children, ...props }, ref) => ( 60 | 61 | 62 | 67 | 68 | 69 | Close 70 | 71 | {children} 72 | 73 | 74 | )) 75 | SheetContent.displayName = SheetPrimitive.Content.displayName 76 | 77 | const SheetHeader = ({ 78 | className, 79 | ...props 80 | }: React.HTMLAttributes) => ( 81 |
88 | ) 89 | SheetHeader.displayName = "SheetHeader" 90 | 91 | const SheetFooter = ({ 92 | className, 93 | ...props 94 | }: React.HTMLAttributes) => ( 95 |
102 | ) 103 | SheetFooter.displayName = "SheetFooter" 104 | 105 | const SheetTitle = React.forwardRef< 106 | React.ElementRef, 107 | React.ComponentPropsWithoutRef 108 | >(({ className, ...props }, ref) => ( 109 | 114 | )) 115 | SheetTitle.displayName = SheetPrimitive.Title.displayName 116 | 117 | const SheetDescription = React.forwardRef< 118 | React.ElementRef, 119 | React.ComponentPropsWithoutRef 120 | >(({ className, ...props }, ref) => ( 121 | 126 | )) 127 | SheetDescription.displayName = SheetPrimitive.Description.displayName 128 | 129 | export { 130 | Sheet, 131 | SheetPortal, 132 | SheetOverlay, 133 | SheetTrigger, 134 | SheetClose, 135 | SheetContent, 136 | SheetHeader, 137 | SheetFooter, 138 | SheetTitle, 139 | SheetDescription, 140 | } 141 | -------------------------------------------------------------------------------- /community/customer-support/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type Thread = { 2 | id: number; 3 | title: string; 4 | createdAt: string; 5 | }; 6 | 7 | export type Message = { 8 | id: number; 9 | content: string; 10 | sender: "user" | "assistant"; 11 | }; 12 | -------------------------------------------------------------------------------- /community/customer-support/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | export const getErrorMessage = (error: unknown): string => { 9 | if (typeof error === "string") return error; 10 | if (error instanceof Error) return error.message; 11 | if (error && typeof error === "object" && "message" in error) 12 | return String(error.message); 13 | if (error instanceof Array) return error[0]; 14 | return "System Error!"; 15 | }; 16 | -------------------------------------------------------------------------------- /community/customer-support/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | darkMode: ["class"], 5 | content: [ 6 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 9 | ], 10 | plugins: [require("tailwindcss-animate")], 11 | theme: { 12 | extend: { 13 | borderRadius: { 14 | lg: "var(--radius)", 15 | md: "calc(var(--radius) - 2px)", 16 | sm: "calc(var(--radius) - 4px)", 17 | }, 18 | colors: { 19 | background: "hsl(var(--background))", 20 | foreground: "hsl(var(--foreground))", 21 | card: { 22 | DEFAULT: "hsl(var(--card))", 23 | foreground: "hsl(var(--card-foreground))", 24 | }, 25 | popover: { 26 | DEFAULT: "hsl(var(--popover))", 27 | foreground: "hsl(var(--popover-foreground))", 28 | }, 29 | primary: { 30 | DEFAULT: "hsl(var(--primary))", 31 | foreground: "hsl(var(--primary-foreground))", 32 | }, 33 | secondary: { 34 | DEFAULT: "hsl(var(--secondary))", 35 | foreground: "hsl(var(--secondary-foreground))", 36 | }, 37 | muted: { 38 | DEFAULT: "hsl(var(--muted))", 39 | foreground: "hsl(var(--muted-foreground))", 40 | }, 41 | accent: { 42 | DEFAULT: "hsl(var(--accent))", 43 | foreground: "hsl(var(--accent-foreground))", 44 | }, 45 | destructive: { 46 | DEFAULT: "hsl(var(--destructive))", 47 | foreground: "hsl(var(--destructive-foreground))", 48 | }, 49 | border: "hsl(var(--border))", 50 | input: "hsl(var(--input))", 51 | ring: "hsl(var(--ring))", 52 | chart: { 53 | "1": "hsl(var(--chart-1))", 54 | "2": "hsl(var(--chart-2))", 55 | "3": "hsl(var(--chart-3))", 56 | "4": "hsl(var(--chart-4))", 57 | "5": "hsl(var(--chart-5))", 58 | }, 59 | }, 60 | }, 61 | }, 62 | }; 63 | export default config; 64 | -------------------------------------------------------------------------------- /community/customer-support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /community/dell-chatbot/.env.example: -------------------------------------------------------------------------------- 1 | TUNE_KEY= https://studio.tune.app/profile 2 | STUDIO_MODEL=openai/o1-mini -------------------------------------------------------------------------------- /community/dell-chatbot/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /community/dell-chatbot/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | # .env*.local 30 | 31 | # vercel 32 | .vercel 33 | .env 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /community/dell-chatbot/README.md: -------------------------------------------------------------------------------- 1 | # Dell Chatbot - Laptop Buying Assistant 2 | 3 | This project is a Dell laptop recommendation chatbot. Users can interact with the chatbot to receive recommendations based on laptop specifications, details, and pricing. The chatbot utilizes function calling to fetch laptop specs, provide details on available models, and retrieve price information for specific laptops. We're using openai/o1-mini model for this project due to its price and performance. 4 | 5 | 6 | ## Demo Video 7 | 8 | https://github.com/user-attachments/assets/3dc4c8b7-a714-4ee7-821e-2d12c36f20fb 9 | 10 | ## Initial Setup 11 | 12 | ### 1. Install Dependencies 13 | Run the following command to install the required npm packages: 14 | 15 | ```bash 16 | npm install 17 | ``` 18 | 19 | ### 2. Environment Variables 20 | Create a `.env` file in the root directory and add the following environment variables: 21 | 22 | ```plaintext 23 | TUNE_KEY= # Obtain from https://studio.tune.app 24 | STUDIO_MODEL= # Obtain from https://studio.tune.app 25 | ``` 26 | 27 | ### 3. Start the Server 28 | Run the following command to start the server: 29 | 30 | ```bash 31 | npm run dev 32 | ``` 33 | 34 | The server will start on port 3000. 35 | 36 | ## Available Features 37 | 38 | - **Laptop Recommendations:** Users can request trending or specific Dell laptops based on screen size, RAM, and storage requirements. 39 | - **Laptop Details:** The chatbot can provide detailed specs for particular models. 40 | - **Pricing:** The chatbot can fetch and display the price of a specific Dell laptop in Indian Rupees. 41 | 42 | ## Example Interactions 43 | 44 | - Get recommendations for laptops based on your preferences (e.g., "Show me laptops with 16GB RAM and 512GB SSD"). 45 | - Find the price of a specific laptop model. 46 | - Ask for specs such as screen size, RAM, or storage for different models. 47 | 48 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/components/MessageCard.tsx: -------------------------------------------------------------------------------- 1 | import ReactMarkdown from "react-markdown"; 2 | import remarkGfm from "remark-gfm"; 3 | import rehype from "rehype-raw"; 4 | import { ChatInterface } from "../page"; 5 | 6 | const MessageCard = ({ 7 | chat, 8 | loading, 9 | loadingTxt, 10 | }: { 11 | chat: ChatInterface; 12 | loading: boolean; 13 | loadingTxt?: string; 14 | }) => { 15 | return ( 16 |
17 |
22 | {chat?.isSender ? chat?.sender?.charAt(0) : ""} 23 |
24 |
25 | {chat?.sender} 26 | {loadingTxt && loading ? ( 27 |
28 | 33 | 41 | 50 | 59 | 68 | 69 | 70 | {loadingTxt} 71 |
72 | ) : ( 73 | "" 74 | )} 75 | {!loading ? ( 76 | value} 81 | components={{ 82 | table: (props: any) => ( 83 |
84 | 85 | 86 | ), 87 | }} 88 | > 89 | {chat.msg} 90 | 91 | ) : ( 92 |
93 | )} 94 | 95 | 96 | ); 97 | }; 98 | 99 | export default MessageCard; 100 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/constants/index.ts: -------------------------------------------------------------------------------- 1 | const constants = { 2 | tools: [ 3 | { 4 | type: "function", 5 | function: { 6 | name: "listLaptops", 7 | description: 8 | "Provide a list of Dell laptop model names based on user preferences such as screen size, RAM, and hard disk space.", 9 | parameters: { 10 | type: "object", 11 | properties: { 12 | models: { 13 | type: "array", 14 | description: "An array of specific Dell laptop model names.", 15 | items: { 16 | type: "string", 17 | description: "The model name of the Dell laptop.", 18 | }, 19 | }, 20 | }, 21 | required: ["models"], 22 | }, 23 | }, 24 | }, 25 | { 26 | type: "function", 27 | function: { 28 | name: "showLaptopPrice", 29 | description: 30 | "Show the price of a specific Dell laptop model in Indian Rupees.", 31 | parameters: { 32 | type: "object", 33 | properties: { 34 | model: { 35 | type: "string", 36 | description: "The model name of the Dell laptop.", 37 | }, 38 | }, 39 | required: ["model"], 40 | }, 41 | }, 42 | }, 43 | ], 44 | }; 45 | 46 | export default constants; 47 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/constants/laptopData.ts: -------------------------------------------------------------------------------- 1 | export const laptopData = [ 2 | { 3 | model: "Dell XPS 13 Plus", 4 | screenSize: "13.4 inch", 5 | processor: "Intel Core i7-1280P", 6 | ram: "16 GB", 7 | storage: "512 GB SSD", 8 | price: 1399000, 9 | }, 10 | { 11 | model: "Dell G16", 12 | screenSize: "16 inch", 13 | processor: "Intel Core i7-12700H", 14 | ram: "16 GB", 15 | storage: "512 GB SSD", 16 | price: 1499000, 17 | }, 18 | { 19 | model: "Dell Precision 5760", 20 | screenSize: "17 inch", 21 | processor: "Intel Core i7-11850H", 22 | ram: "32 GB", 23 | storage: "2 TB SSD", 24 | price: 3941000, 25 | }, 26 | { 27 | model: "Dell Latitude 9430", 28 | screenSize: "14 inch", 29 | processor: "Intel Core i7-1265U", 30 | ram: "16 GB", 31 | storage: "512 GB SSD", 32 | price: 2169000, 33 | }, 34 | { 35 | model: "Dell Inspiron 15 3511", 36 | screenSize: "15.6 inch", 37 | processor: "Intel Core i5-1135G7", 38 | ram: "8 GB", 39 | storage: "512 GB SSD", 40 | price: 599000, 41 | }, 42 | { 43 | model: "Dell Alienware X16", 44 | screenSize: "16 inch", 45 | processor: "Intel Core i9-13900HK", 46 | ram: "32 GB", 47 | storage: "1 TB SSD", 48 | price: 2499000, 49 | }, 50 | { 51 | model: "Dell Inspiron 14 5406", 52 | screenSize: "14 inch", 53 | processor: "Intel Core i5-1135G7", 54 | ram: "8 GB", 55 | storage: "256 GB SSD", 56 | price: 749000, 57 | }, 58 | { 59 | model: "Dell XPS 15 9500", 60 | screenSize: "15.6 inch", 61 | processor: "Intel Core i7-10750H", 62 | ram: "16 GB", 63 | storage: "1 TB SSD", 64 | price: 1749000, 65 | }, 66 | { 67 | model: "Dell Latitude 5510", 68 | screenSize: "15.6 inch", 69 | processor: "Intel Core i5-10210U", 70 | ram: "8 GB", 71 | storage: "256 GB SSD", 72 | price: 849000, 73 | }, 74 | { 75 | model: "Dell G5 15 SE", 76 | screenSize: "15.6 inch", 77 | processor: "AMD Ryzen 7 4800H", 78 | ram: "16 GB", 79 | storage: "512 GB SSD", 80 | price: 1099000, 81 | }, 82 | { 83 | model: "Dell Vostro 15 5502", 84 | screenSize: "15.6 inch", 85 | processor: "Intel Core i7-1165G7", 86 | ram: "8 GB", 87 | storage: "512 GB SSD", 88 | price: 899000, 89 | }, 90 | { 91 | model: "Dell Alienware M15 R6", 92 | screenSize: "15.6 inch", 93 | processor: "Intel Core i7-11800H", 94 | ram: "16 GB", 95 | storage: "1 TB SSD", 96 | price: 2199000, 97 | }, 98 | { 99 | model: "Dell Precision 3551", 100 | screenSize: "15.6 inch", 101 | processor: "Intel Core i7-10850H", 102 | ram: "16 GB", 103 | storage: "512 GB SSD", 104 | price: 1349000, 105 | }, 106 | { 107 | model: "Dell XPS 17 9700", 108 | screenSize: "17 inch", 109 | processor: "Intel Core i9-10885H", 110 | ram: "32 GB", 111 | storage: "2 TB SSD", 112 | price: 2899000, 113 | }, 114 | { 115 | model: "Dell Latitude 7320", 116 | screenSize: "13.3 inch", 117 | processor: "Intel Core i7-1185G7", 118 | ram: "16 GB", 119 | storage: "512 GB SSD", 120 | price: 1799000, 121 | }, 122 | { 123 | model: "Dell Inspiron 16 Plus", 124 | screenSize: "16 inch", 125 | processor: "Intel Core i7-11800H", 126 | ram: "16 GB", 127 | storage: "1 TB SSD", 128 | price: 1599000, 129 | }, 130 | { 131 | model: "Dell G7 17", 132 | screenSize: "17 inch", 133 | processor: "Intel Core i7-10750H", 134 | ram: "16 GB", 135 | storage: "1 TB SSD", 136 | price: 1899000, 137 | }, 138 | { 139 | model: "Dell Latitude 5521", 140 | screenSize: "15.6 inch", 141 | processor: "Intel Core i5-1145G7", 142 | ram: "8 GB", 143 | storage: "256 GB SSD", 144 | price: 1099000, 145 | }, 146 | { 147 | model: "Dell Precision 7560", 148 | screenSize: "15.6 inch", 149 | processor: "Intel Core i7-11850H", 150 | ram: "32 GB", 151 | storage: "2 TB SSD", 152 | price: 3299000, 153 | }, 154 | { 155 | model: "Dell Inspiron 13 5310", 156 | screenSize: "13.3 inch", 157 | processor: "Intel Core i5-11320H", 158 | ram: "8 GB", 159 | storage: "256 GB SSD", 160 | price: 799000, 161 | }, 162 | { 163 | model: "Dell XPS 13 2-in-1", 164 | screenSize: "13.4 inch", 165 | processor: "Intel Core i7-1165G7", 166 | ram: "16 GB", 167 | storage: "512 GB SSD", 168 | price: 1299000, 169 | }, 170 | { 171 | model: "Dell Latitude 7420", 172 | screenSize: "14 inch", 173 | processor: "Intel Core i7-1185G7", 174 | ram: "16 GB", 175 | storage: "512 GB SSD", 176 | price: 1799000, 177 | }, 178 | { 179 | model: "Dell G3 15", 180 | screenSize: "15.6 inch", 181 | processor: "Intel Core i5-10300H", 182 | ram: "8 GB", 183 | storage: "512 GB SSD", 184 | price: 849000, 185 | }, 186 | { 187 | model: "Dell Precision 5540", 188 | screenSize: "15.6 inch", 189 | processor: "Intel Core i7-9850H", 190 | ram: "16 GB", 191 | storage: "1 TB SSD", 192 | price: 2149000, 193 | }, 194 | { 195 | model: "Dell Inspiron 14 5410", 196 | screenSize: "14 inch", 197 | processor: "Intel Core i7-1165G7", 198 | ram: "8 GB", 199 | storage: "256 GB SSD", 200 | price: 749000, 201 | }, 202 | { 203 | model: "Dell XPS 13 9310", 204 | screenSize: "13.4 inch", 205 | processor: "Intel Core i7-1165G7", 206 | ram: "16 GB", 207 | storage: "512 GB SSD", 208 | price: 149000, 209 | }, 210 | { 211 | model: "Dell XPS 15 9500", 212 | screenSize: "15.6 inch", 213 | processor: "Intel Core i7-1165G7", 214 | ram: "16 GB", 215 | storage: "512 GB SSD", 216 | price: 199000, 217 | }, 218 | { 219 | model: "Dell Inspiron 15 3511", 220 | screenSize: "15.6 inch", 221 | processor: "Intel Core i5-1135G7", 222 | ram: "8 GB", 223 | storage: "512 GB SSD", 224 | price: 599000, 225 | }, 226 | { 227 | model: "Dell Inspiron 14 5406", 228 | screenSize: "14 inch", 229 | processor: "Intel Core i5-1135G7", 230 | ram: "8 GB", 231 | storage: "256 GB SSD", 232 | price: 749000, 233 | }, 234 | { 235 | model: "Dell Inspiron 13 5310", 236 | screenSize: "13.3 inch", 237 | processor: "Intel Core i5-11320H", 238 | ram: "8 GB", 239 | storage: "256 GB SSD", 240 | price: 799000, 241 | }, 242 | { 243 | model: "Dell Inspiron 15 3511", 244 | screenSize: "15.6 inch", 245 | processor: "Intel Core i5-1135G7", 246 | ram: "8 GB", 247 | storage: "512 GB SSD", 248 | price: 599000, 249 | }, 250 | { 251 | model: "Dell Inspiron 14 5406", 252 | screenSize: "14 inch", 253 | processor: "Intel Core i5-1135G7", 254 | ram: "8 GB", 255 | storage: "256 GB SSD", 256 | price: 749000, 257 | }, 258 | { 259 | model: "Dell Inspiron 13 5310", 260 | screenSize: "13.3 inch", 261 | processor: "Intel Core i5-11320H", 262 | ram: "8 GB", 263 | storage: "256 GB SSD", 264 | price: 799000, 265 | }, 266 | { 267 | model: "Dell Inspiron 15 3511", 268 | screenSize: "15.6 inch", 269 | processor: "Intel Core i5-1135G7", 270 | ram: "8 GB", 271 | storage: "512 GB SSD", 272 | price: 599000, 273 | }, 274 | { 275 | model: "Dell Inspiron 14 5406", 276 | screenSize: "14 inch", 277 | processor: "Intel Core i5-1135G7", 278 | ram: "8 GB", 279 | storage: "256 GB SSD", 280 | price: 749000, 281 | }, 282 | { 283 | model: "Dell Inspiron 13 5310", 284 | screenSize: "13.3 inch", 285 | processor: "Intel Core i5-11320H", 286 | ram: "8 GB", 287 | storage: "256 GB SSD", 288 | price: 799000, 289 | }, 290 | { 291 | model: "Dell Inspiron 15 3511", 292 | screenSize: "15.6 inch", 293 | processor: "Intel Core i5-1135G7", 294 | ram: "8 GB", 295 | storage: "512 GB SSD", 296 | price: 599000, 297 | }, 298 | { 299 | model: "Dell Inspiron 14 5406", 300 | screenSize: "14 inch", 301 | processor: "Intel Core i5-1135G7", 302 | ram: "8 GB", 303 | storage: "256 GB SSD", 304 | price: 749000, 305 | }, 306 | { 307 | model: "Dell Inspiron 13 5310", 308 | screenSize: "13.3 inch", 309 | processor: "Intel Core i5-11320H", 310 | ram: "8 GB", 311 | storage: "256 GB SSD", 312 | price: 799000, 313 | }, 314 | { 315 | model: "Dell Inspiron 15 3511", 316 | screenSize: "15.6 inch", 317 | processor: "Intel Core i5-1135G7", 318 | ram: "8 GB", 319 | storage: "512 GB SSD", 320 | price: 599000, 321 | }, 322 | { 323 | model: "Dell Inspiron 14 5406", 324 | screenSize: "14 inch", 325 | processor: "Intel Core i5-1135G7", 326 | ram: "8 GB", 327 | storage: "256 GB SSD", 328 | price: 749000, 329 | }, 330 | { 331 | model: "Dell Inspiron 13 5310", 332 | screenSize: "13.3 inch", 333 | processor: "Intel Core i5-11320H", 334 | ram: "8 GB", 335 | storage: "256 GB SSD", 336 | price: 799000, 337 | }, 338 | { 339 | model: "Dell Inspiron 15 3511", 340 | screenSize: "15.6 inch", 341 | processor: "Intel Core i5-1135G7", 342 | ram: "8 GB", 343 | storage: "512 GB SSD", 344 | price: 599000, 345 | }, 346 | { 347 | model: "Dell Inspiron 14 5406", 348 | screenSize: "14 inch", 349 | processor: "Intel Core i5-1135G7", 350 | ram: "8 GB", 351 | storage: "256 GB SSD", 352 | price: 749000, 353 | }, 354 | { 355 | model: "Dell Inspiron 13 5310", 356 | screenSize: "13.3 inch", 357 | processor: "Intel Core i5-11320H", 358 | ram: "8 GB", 359 | storage: "256 GB SSD", 360 | price: 799000, 361 | }, 362 | { 363 | model: "Dell Inspiron 15 3511", 364 | screenSize: "15.6 inch", 365 | processor: "Intel Core i5-1135G7", 366 | ram: "8 GB", 367 | storage: "512 GB SSD", 368 | price: 599000, 369 | }, 370 | { 371 | model: "Dell Inspiron 14 5406", 372 | screenSize: "14 inch", 373 | processor: "Intel Core i5-1135G7", 374 | ram: "8 GB", 375 | storage: "256 GB SSD", 376 | price: 749000, 377 | }, 378 | { 379 | model: "Dell Inspiron 13 5310", 380 | screenSize: "13.3 inch", 381 | processor: "Intel Core i5-11320H", 382 | ram: "8 GB", 383 | storage: "256 GB SSD", 384 | price: 799000, 385 | }, 386 | { 387 | model: "Dell Inspiron 15 3511", 388 | screenSize: "15.6 inch", 389 | processor: "Intel Core i5-1135G7", 390 | ram: "8 GB", 391 | storage: "512 GB SSD", 392 | price: 599000, 393 | }, 394 | { 395 | model: "Dell Inspiron 14 5406", 396 | screenSize: "14 inch", 397 | processor: "Intel Core i5-1135G7", 398 | ram: "8 GB", 399 | storage: "256 GB SSD", 400 | price: 749000, 401 | }, 402 | ]; 403 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/community/dell-chatbot/app/favicon.ico -------------------------------------------------------------------------------- /community/dell-chatbot/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 7 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 8 | sans-serif; 9 | line-height: 1.5; 10 | font-weight: 400; 11 | 12 | color-scheme: light dark; 13 | color: rgba(255, 255, 255, 0.87); 14 | background-color: #242424; 15 | 16 | font-synthesis: none; 17 | text-rendering: optimizeLegibility; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | -webkit-text-size-adjust: 100%; 21 | } 22 | 23 | ::selection { 24 | background: #646cff; 25 | color: #fff; 26 | } 27 | 28 | ::-webkit-scrollbar { 29 | display: none; 30 | } 31 | 32 | html, 33 | body, 34 | div, 35 | textarea, 36 | .noScrollBar { 37 | scrollbar-width: none; 38 | font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 39 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 40 | sans-serif !important; 41 | } 42 | 43 | *::-webkit-scrollbar-track, 44 | *::-webkit-scrollbar-thumb { 45 | background: transparent !important; 46 | } 47 | 48 | *::-moz-scrollbar, 49 | *::-webkit-scrollbar { 50 | display: none !important; 51 | width: 0 !important; 52 | height: 0; 53 | } 54 | 55 | a { 56 | font-weight: 500; 57 | color: #646cff; 58 | text-decoration: inherit; 59 | } 60 | a:hover { 61 | color: #535bf2; 62 | } 63 | 64 | body { 65 | margin: 0; 66 | display: flex; 67 | place-items: center; 68 | min-width: 320px; 69 | min-height: 100vh; 70 | @apply bg-[#FFFFFF] text-[#353849]; 71 | } 72 | 73 | body { 74 | @apply overflow-hidden h-screen w-screen bg-[#FFFFFF] text-[#353849]; 75 | } 76 | 77 | .revealCardAnimation { 78 | animation: revealCardAnimation 0.5s ease-in-out; 79 | } 80 | 81 | @keyframes revealCardAnimation { 82 | 0% { 83 | opacity: 0; 84 | scale: 0.9; 85 | } 86 | 100% { 87 | opacity: 1; 88 | scale: 1; 89 | } 90 | } 91 | 92 | .markdownHolder { 93 | font-size: 16px; 94 | } 95 | 96 | .markdownHolder p { 97 | @apply mb-[8px]; 98 | } 99 | 100 | /* if p is inside li */ 101 | .markdownHolder li p { 102 | @apply mb-[0px] inline; 103 | } 104 | 105 | .markdownHolder ol { 106 | @apply list-inside list-decimal; 107 | } 108 | 109 | .markdownHolder ul { 110 | @apply list-inside list-disc; 111 | } 112 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "TuneStudio | Function Calling", 9 | description: "Template by TuneStudio", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/prompt/route.ts: -------------------------------------------------------------------------------- 1 | import constants from "../constants"; 2 | import { TuneAIStream } from "../utils"; 3 | import { StreamingTextResponse } from "ai"; 4 | 5 | export async function POST(req: Request) { 6 | const { messages } = await req.json(); 7 | const tuneResp = await TuneAIStream({ 8 | messages: messages, 9 | model: process.env.STUDIO_MODEL || "", 10 | stream: false, 11 | tools: constants.tools, 12 | temperature: 0.5, 13 | max_tokens: 600, 14 | }); 15 | console.log("Tune Response", tuneResp); 16 | 17 | return new StreamingTextResponse(tuneResp); 18 | } 19 | -------------------------------------------------------------------------------- /community/dell-chatbot/app/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { laptopData } from "../constants/laptopData"; 2 | import { createParser } from "eventsource-parser"; 3 | import { VirtualConsole } from "jsdom"; 4 | import fetch from "node-fetch"; 5 | 6 | export const TuneAIStream = async ({ 7 | messages, 8 | model, 9 | stream, 10 | max_tokens, 11 | stop, 12 | temperature, 13 | tools, 14 | }: { 15 | stream: boolean; 16 | messages: { 17 | role: string; 18 | content: string; 19 | }[]; 20 | model: string; 21 | max_tokens?: number; 22 | stop?: string[]; 23 | temperature?: number; 24 | tools?: { 25 | type: string; 26 | function: { 27 | name: string; 28 | description: string; 29 | parameters: { 30 | type: string; 31 | properties: Record; 32 | required: string[]; 33 | }; 34 | }; 35 | }[]; 36 | }) => { 37 | const response = await fetch(`https://proxy.tune.app/chat/completions`, { 38 | method: "POST", 39 | headers: { 40 | "Content-Type": "application/json", 41 | Authorization: process.env.TUNE_KEY || "", 42 | }, 43 | body: JSON.stringify({ 44 | messages: messages, 45 | model: model, 46 | stream: stream, 47 | stop, 48 | temperature, 49 | max_tokens: max_tokens, 50 | tools, 51 | }), 52 | }); 53 | if (response.status !== 200) { 54 | throw new Error("Error: " + response.status); 55 | } 56 | if (!stream) { 57 | const json = (await response?.json()) as any; 58 | if (json?.choices?.[0]?.message?.tool_calls?.[0]?.function) { 59 | return streamFunction( 60 | json?.choices?.[0]?.message?.content, 61 | json?.choices?.[0]?.message?.tool_calls?.[0]?.function, 62 | messages?.at(-1)?.content || "" 63 | ); 64 | } else { 65 | return streamText(json?.choices?.[0]?.message?.content || ""); 66 | } 67 | } 68 | const encoder = new TextEncoder(); 69 | const decoder = new TextDecoder(); 70 | 71 | const streamResp = new ReadableStream({ 72 | async start(controller) { 73 | const onParse: any = async (event: { type: string; data: any }) => { 74 | if (event.type === "event") { 75 | const data = event.data; 76 | if (data === "[DONE]") { 77 | controller.close(); 78 | return; 79 | } 80 | try { 81 | const json = JSON.parse(data); 82 | if (json?.error?.code > 399 && json?.error?.message) { 83 | controller.error(json?.error?.message); 84 | return; 85 | } 86 | 87 | const text = json?.choices?.[0]?.delta?.content; 88 | const queue = encoder.encode(text); 89 | controller.enqueue(queue); 90 | } catch (e) { 91 | console.error(e); 92 | controller.error(e); 93 | } 94 | } 95 | }; 96 | const parser = createParser(onParse); 97 | if (response?.body) { 98 | for await (const chunk of response?.body as any) { 99 | parser.feed(decoder.decode(chunk)); 100 | } 101 | } 102 | }, 103 | }); 104 | return streamResp; 105 | }; 106 | 107 | const streamFunction = async ( 108 | content: string, 109 | tool_calls: { 110 | arguments: string; 111 | name: string; 112 | }, 113 | user_query: string 114 | ) => { 115 | const stream = new ReadableStream({ 116 | async start(controller) { 117 | if (content) { 118 | controller.enqueue( 119 | JSON.stringify({ 120 | data: content, 121 | type: "function_thought", 122 | }) 123 | ); 124 | controller.enqueue("\n\n\n\n\n\n"); 125 | } 126 | controller.enqueue( 127 | JSON.stringify({ 128 | data: `Calling function ${tool_calls.name} with arguments (${ 129 | Object.entries(JSON.parse(tool_calls.arguments))?.map( 130 | ([key, value]) => `${key} = ${value},` 131 | ) || "" 132 | })`, 133 | type: "function", 134 | }) 135 | ); 136 | controller.enqueue("\n\n\n\n\n\n"); 137 | let functionResponse = ""; 138 | 139 | if (tool_calls?.name === "listLaptops") { 140 | const args = JSON.parse(tool_calls.arguments); 141 | functionResponse = await listLaptops(args?.models); 142 | } else if (tool_calls?.name === "showLaptopPrice") { 143 | const args = JSON.parse(tool_calls.arguments); 144 | functionResponse = await showLaptopPrice(args?.model); 145 | } 146 | 147 | let finalResponse = ``; 148 | const stream = await TuneAIStream({ 149 | messages: [ 150 | { 151 | role: "user", 152 | content: `Use Below information to answer the question: 153 | 154 | User query: ${user_query} 155 | 156 | Function Response: 157 | ${functionResponse} 158 | 159 | If function could not find any laptop matching the user query, reply with "I couldn’t find a laptop matching your specifications. Would you like to check other options?" 160 | `, 161 | }, 162 | ], 163 | model: process.env.STUDIO_MODEL || "", 164 | stream: true, 165 | temperature: 0.5, 166 | }); 167 | 168 | const reader = stream.getReader(); 169 | const decoder = new TextDecoder(); 170 | while (true && reader) { 171 | const { done, value } = await reader.read(); 172 | if (done) { 173 | break; 174 | } 175 | 176 | const text = decoder.decode(value); 177 | 178 | controller.enqueue(JSON.stringify({ data: text, type: "text" })); 179 | finalResponse = finalResponse + text; 180 | controller.enqueue("\n\n\n\n\n\n"); 181 | } 182 | controller.enqueue("\n\n\n\n\n\n"); 183 | controller.enqueue(JSON.stringify({ data: finalResponse, type: "bye" })); 184 | 185 | controller.close(); 186 | }, 187 | }); 188 | return stream; 189 | }; 190 | 191 | const streamText = async (streamTxt: string) => { 192 | const stream = new ReadableStream({ 193 | start(controller) { 194 | function pushData() { 195 | controller.enqueue(JSON.stringify({ data: streamTxt, type: "text" })); 196 | controller.close(); 197 | } 198 | pushData(); 199 | }, 200 | }); 201 | return stream; 202 | }; 203 | 204 | async function listLaptops(models: string[]) { 205 | const filteredLaptops = laptopData.filter((laptop) => 206 | models.includes(laptop.model) 207 | ); 208 | 209 | if (filteredLaptops.length > 0) { 210 | return filteredLaptops 211 | .map( 212 | (laptop) => 213 | `Model: ${laptop.model}, Screen Size: ${laptop.screenSize}", RAM: ${laptop.ram}GB, Storage: ${laptop.storage}` 214 | ) 215 | .join("\n"); 216 | } else { 217 | return "I couldn’t find any laptops matching the specified models. Would you like to check other models?"; 218 | } 219 | } 220 | 221 | async function showLaptopPrice(model: string) { 222 | const laptop = laptopData.find((laptop) => laptop.model === model); 223 | 224 | if (laptop) { 225 | return `The price of ${model} is ₹${laptop.price}.`; 226 | } else { 227 | return "I couldn’t find the price for the specified laptop model. Would you like to check another model?"; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /community/dell-chatbot/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | esmExternals: false, 5 | }, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /community/dell-chatbot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "function-calling", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@tailwindcss/typography": "^0.5.13", 13 | "ai": "^3.0.35", 14 | "axios": "^1.7.7", 15 | "chainfury": "^0.0.1-alpha.7", 16 | "eventsource-parser": "^1.1.2", 17 | "jsdom": "^24.0.0", 18 | "llama-tokenizer-js": "^1.2.1", 19 | "next": "14.2.3", 20 | "node-fetch": "^3.3.2", 21 | "react": "^18", 22 | "react-dom": "^18", 23 | "react-markdown": "^9.0.1", 24 | "rehype-raw": "^7.0.0", 25 | "remark-gfm": "^4.0.0" 26 | }, 27 | "devDependencies": { 28 | "@types/jsdom": "^21.1.6", 29 | "@types/node": "^20", 30 | "@types/react": "^18", 31 | "@types/react-dom": "^18", 32 | "eslint": "^8", 33 | "eslint-config-next": "14.2.3", 34 | "postcss": "^8", 35 | "tailwindcss": "^3.4.1", 36 | "typescript": "^5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /community/dell-chatbot/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /community/dell-chatbot/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | typography: () => ({ 18 | nbx: { 19 | css: [ 20 | { 21 | ".title1": { 22 | fontFamily: "'Inter', sans-serif", 23 | fontWeight: 500, 24 | fontSize: "24px", 25 | lineHeight: "32px", 26 | }, 27 | ".title2": { 28 | fontFamily: "'Inter', sans-serif", 29 | fontWeight: 500, 30 | fontSize: "20px", 31 | lineHeight: "normal", 32 | }, 33 | ".large": { 34 | fontFamily: "'Inter', sans-serif", 35 | fontWeight: 400, 36 | fontSize: "18px", 37 | lineHeight: "24px", 38 | }, 39 | ".large-pl": { 40 | fontFamily: "'Inter', sans-serif", 41 | fontWeight: 500, 42 | fontSize: "18px", 43 | lineHeight: "24px", 44 | }, 45 | ".regular": { 46 | fontFamily: "'Inter', sans-serif", 47 | fontWeight: 400, 48 | fontSize: "16px", 49 | lineHeight: "normal", 50 | }, 51 | ".regular-pl": { 52 | fontFamily: "'Inter', sans-serif", 53 | fontWeight: 500, 54 | fontSize: "16px", 55 | lineHeight: "normal", 56 | }, 57 | ".medium": { 58 | fontFamily: "'Inter', sans-serif", 59 | fontWeight: 400, 60 | fontSize: "14px", 61 | lineHeight: "normal", 62 | }, 63 | ".medium-pl": { 64 | fontFamily: "'Inter', sans-serif", 65 | fontWeight: 500, 66 | fontSize: "14px", 67 | lineHeight: "normal", 68 | }, 69 | ".small": { 70 | fontFamily: "'Inter', sans-serif", 71 | fontWeight: 400, 72 | fontSize: "13px", 73 | lineHeight: "normal", 74 | }, 75 | ".small-pl": { 76 | fontFamily: "'Inter', sans-serif", 77 | fontWeight: 500, 78 | fontSize: "13px", 79 | lineHeight: "normal", 80 | }, 81 | ".mini": { 82 | fontFamily: "'Inter', sans-serif", 83 | fontWeight: 400, 84 | fontSize: "12px", 85 | lineHeight: "normal", 86 | }, 87 | ".mini-pl": { 88 | fontFamily: "'Inter', sans-serif", 89 | fontWeight: 500, 90 | fontSize: "12px", 91 | lineHeight: "normal", 92 | }, 93 | ".xmini": { 94 | fontFamily: "'Inter', sans-serif", 95 | fontWeight: 400, 96 | fontSize: "12px", 97 | lineHeight: "normal", 98 | }, 99 | ".xmini-pl": { 100 | fontFamily: "'Inter', sans-serif", 101 | fontWeight: 500, 102 | fontSize: "12px", 103 | lineHeight: "normal", 104 | }, 105 | }, 106 | ], 107 | }, 108 | }), 109 | }, 110 | plugins: [require("@tailwindcss/typography")], 111 | }; 112 | export default config; 113 | -------------------------------------------------------------------------------- /community/dell-chatbot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /function-calling-swiggy/.env.example: -------------------------------------------------------------------------------- 1 | TUNE_KEY= https://studio.tune.app/profile 2 | STUDIO_MODEL=rohan/tune-gpt4 https://studio.tune.app/models 3 | SWIGGY_SESSION_ID= -------------------------------------------------------------------------------- /function-calling-swiggy/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /function-calling-swiggy/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | .env 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /function-calling-swiggy/README.md: -------------------------------------------------------------------------------- 1 | # Project Setup and Configuration 2 | 3 | ## Initial Setup 4 | 5 | ### 1. Install Dependencies 6 | 7 | Run the following command to install the required npm packages: 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | ### 2. Environment Variables 14 | 15 | Create a `.env` file in the root directory and add the following environment variables: 16 | 17 | ```plaintext 18 | TUNE_KEY= # Obtain from https://studio.tune.app 19 | STUDIO_MODEL= # Obtain from https://studio.tune.app 20 | SWIGGY_SESSION_ID= # Obtain from https://www.swiggy.com/ 21 | ``` 22 | 23 | ### 3. Start the Server 24 | 25 | Run the following command to start the server: 26 | 27 | ```bash 28 | npm run dev 29 | ``` 30 | 31 | The server will start on port `3000`. 32 | 33 | ## How to Obtain Swiggy Session ID 34 | 35 | 1. Log in to [Swiggy](https://www.swiggy.com/). 36 | 2. Open the developer tools in your browser. 37 | 3. Copy the value of the cookie named `_session_tid`. 38 | 39 | **Note:** This workaround is necessary as Swiggy does not provide developer APIs at this time. 40 | -------------------------------------------------------------------------------- /function-calling-swiggy/app/components/MessageCard.tsx: -------------------------------------------------------------------------------- 1 | import ReactMarkdown from "react-markdown"; 2 | import remarkGfm from "remark-gfm"; 3 | import rehype from "rehype-raw"; 4 | import { ChatInterface } from "../page"; 5 | 6 | const MessageCard = ({ 7 | chat, 8 | loading, 9 | loadingTxt, 10 | }: { 11 | chat: ChatInterface; 12 | loading: boolean; 13 | loadingTxt?: string; 14 | }) => { 15 | return ( 16 |
17 |
22 | {chat?.isSender ? chat?.sender?.charAt(0) : ""} 23 |
24 |
25 | {chat?.sender} 26 | {loadingTxt && loading ? ( 27 |
28 | 33 | 41 | 50 | 59 | 68 | 69 | 70 | {loadingTxt} 71 |
72 | ) : ( 73 | "" 74 | )} 75 | {!loading ? ( 76 | value} 81 | components={{ 82 | table: (props: any) => ( 83 |
84 |
85 | 86 | ), 87 | }} 88 | > 89 | {chat.msg} 90 | 91 | ) : ( 92 |
93 | )} 94 | 95 | 96 | ); 97 | }; 98 | 99 | export default MessageCard; 100 | -------------------------------------------------------------------------------- /function-calling-swiggy/app/constants/index.ts: -------------------------------------------------------------------------------- 1 | const constants = { 2 | tools: [ 3 | { 4 | type: "function", 5 | function: { 6 | name: "get_swiggy_orders", 7 | description: "Get the recent food orders from Swiggy.", 8 | parameters: { 9 | type: "object", 10 | properties: {}, 11 | }, 12 | }, 13 | }, 14 | { 15 | type: "function", 16 | function: { 17 | name: "get_nearby_restaurants", 18 | description: "Get the nearby restaurants to a location.", 19 | parameters: { 20 | type: "object", 21 | properties: { 22 | latitude: { 23 | type: "number", 24 | description: "The latitude of the location.", 25 | }, 26 | longitude: { 27 | type: "number", 28 | description: "The longitude of the location.", 29 | }, 30 | }, 31 | }, 32 | }, 33 | }, 34 | { 35 | type: "function", 36 | function: { 37 | name: "get_restaurant_menu", 38 | description: "Get the nearby restaurants to a location.", 39 | parameters: { 40 | type: "object", 41 | properties: { 42 | menu_url: { 43 | type: "string", 44 | description: "The URL of the restaurant's menu.", 45 | }, 46 | }, 47 | required: ["menu_url"], 48 | }, 49 | }, 50 | }, 51 | ], 52 | }; 53 | 54 | export default constants; 55 | -------------------------------------------------------------------------------- /function-calling-swiggy/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuneHQ/cookbook/ec72b26a949c7ac084cab4df525753acae5d97f5/function-calling-swiggy/app/favicon.ico -------------------------------------------------------------------------------- /function-calling-swiggy/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 7 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 8 | sans-serif; 9 | line-height: 1.5; 10 | font-weight: 400; 11 | 12 | color-scheme: light dark; 13 | color: rgba(255, 255, 255, 0.87); 14 | background-color: #242424; 15 | 16 | font-synthesis: none; 17 | text-rendering: optimizeLegibility; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | -webkit-text-size-adjust: 100%; 21 | } 22 | 23 | ::selection { 24 | background: #646cff; 25 | color: #fff; 26 | } 27 | 28 | ::-webkit-scrollbar { 29 | display: none; 30 | } 31 | 32 | html, 33 | body, 34 | div, 35 | textarea, 36 | .noScrollBar { 37 | scrollbar-width: none; 38 | font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 39 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 40 | sans-serif !important; 41 | } 42 | 43 | *::-webkit-scrollbar-track, 44 | *::-webkit-scrollbar-thumb { 45 | background: transparent !important; 46 | } 47 | 48 | *::-moz-scrollbar, 49 | *::-webkit-scrollbar { 50 | display: none !important; 51 | width: 0 !important; 52 | height: 0; 53 | } 54 | 55 | a { 56 | font-weight: 500; 57 | color: #646cff; 58 | text-decoration: inherit; 59 | } 60 | a:hover { 61 | color: #535bf2; 62 | } 63 | 64 | body { 65 | margin: 0; 66 | display: flex; 67 | place-items: center; 68 | min-width: 320px; 69 | min-height: 100vh; 70 | @apply bg-[#FFFFFF] text-[#353849]; 71 | } 72 | 73 | body { 74 | @apply overflow-hidden h-screen w-screen bg-[#FFFFFF] text-[#353849]; 75 | } 76 | 77 | .revealCardAnimation { 78 | animation: revealCardAnimation 0.5s ease-in-out; 79 | } 80 | 81 | @keyframes revealCardAnimation { 82 | 0% { 83 | opacity: 0; 84 | scale: 0.9; 85 | } 86 | 100% { 87 | opacity: 1; 88 | scale: 1; 89 | } 90 | } 91 | 92 | .markdownHolder { 93 | font-size: 16px; 94 | } 95 | 96 | .markdownHolder p { 97 | @apply mb-[8px]; 98 | } 99 | 100 | /* if p is inside li */ 101 | .markdownHolder li p { 102 | @apply mb-[0px] inline; 103 | } 104 | 105 | .markdownHolder ol { 106 | @apply list-inside list-decimal; 107 | } 108 | 109 | .markdownHolder ul { 110 | @apply list-inside list-disc; 111 | } 112 | -------------------------------------------------------------------------------- /function-calling-swiggy/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "TuneStudio | Function Calling", 9 | description: "Template by TuneStudio", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /function-calling-swiggy/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import "chainfury/dist/esm/index.css"; 4 | import { Pause, Send, TextArea } from "chainfury"; 5 | import { useState, useRef, useEffect } from "react"; 6 | import MessageCard from "./components/MessageCard"; 7 | 8 | const faqs = [ 9 | "Can you suggest me a good restaurant?", 10 | "What was my last order?", 11 | "How much I spent on my last 3 orders?", 12 | "I want to eat something spicy", 13 | ]; 14 | 15 | export interface ChatInterface { 16 | msg: string; 17 | attachment_name?: string; 18 | isSender: boolean; 19 | sender: string; 20 | message_id?: string; 21 | } 22 | 23 | const safeParse = (str: string) => { 24 | try { 25 | return JSON.parse(str); 26 | } catch (error) { 27 | return str; 28 | } 29 | }; 30 | 31 | const scrollToBottom = (force = false) => { 32 | const chatsHolder = document.querySelector(".chatsHolder"); 33 | if (!chatsHolder) return; 34 | // check if user is already at the bottom of the chatsHolder 35 | const isAtBottom = 36 | chatsHolder.scrollHeight - 40 - chatsHolder.scrollTop <= 37 | chatsHolder.clientHeight; 38 | if (chatsHolder && (isAtBottom || force)) { 39 | chatsHolder.scrollTo({ 40 | top: chatsHolder.scrollHeight, 41 | behavior: "smooth", 42 | }); 43 | } 44 | }; 45 | 46 | export default function Home() { 47 | const [chats, setChats] = useState([]); 48 | const [answer, setAnswer] = useState(""); 49 | const [loading, setLoading] = useState(false); 50 | const [search, setSearch] = useState(""); 51 | const [loadingTxt, setLoadingTxt] = useState(""); 52 | const [currentLocation, setCurrentLocation] = useState({ 53 | lat: 12.8400727, 54 | lon: 80.243273, 55 | } as { 56 | lat?: number; 57 | lon?: number; 58 | }); 59 | let abortController = useRef(new AbortController()); 60 | 61 | const stopGenerating = () => { 62 | if (abortController?.current?.abort) { 63 | abortController?.current.abort(); 64 | } 65 | }; 66 | 67 | async function getLocation() { 68 | if (navigator.geolocation) { 69 | (await navigator.geolocation.getCurrentPosition((position) => { 70 | setCurrentLocation({ 71 | lat: position.coords.latitude, 72 | lon: position.coords.longitude, 73 | }); 74 | })) as any; 75 | } 76 | } 77 | 78 | const handleSearch = async () => { 79 | try { 80 | const searchValue = search.trim(); 81 | setSearch(""); 82 | if (loading) return; 83 | setLoading(true); 84 | abortController.current = new AbortController(); 85 | abortController.current.signal.addEventListener("abort", () => { 86 | setLoading(false); 87 | setAnswer(""); 88 | }); 89 | const url = `/prompt`; 90 | 91 | const response = await fetch(url, { 92 | method: "POST", 93 | signal: abortController.current?.signal, 94 | headers: { 95 | "Content-Type": "application/json", 96 | }, 97 | body: JSON.stringify({ 98 | messages: [ 99 | { 100 | role: "system", 101 | content: `You are an very enthsiastic AI bot by TuneHQ. Its important you add information marked as [important]. 102 | Users location is ${currentLocation.lat}, ${currentLocation.lon}. 103 | Current time is ${new Date().toLocaleTimeString()}.`, 104 | }, 105 | ...chats?.slice(0, chats.length - 1).map((chat) => { 106 | return { 107 | role: chat.isSender ? "user" : "assistant", 108 | content: chat.msg?.includes("data:image/png;base64") 109 | ? "Image Generated" 110 | : chat.msg, 111 | }; 112 | }), 113 | ], 114 | }), 115 | }); 116 | if (!response.ok) { 117 | alert("Something went wrong, please try again later."); 118 | setLoading(false); 119 | return; 120 | } 121 | if (!response?.headers?.get("content-type")) { 122 | const text = await response.text(); 123 | setAnswer(text); 124 | setLoading(false); 125 | setTimeout(() => { 126 | setAnswer(""); 127 | }, 10000); 128 | return; 129 | } 130 | 131 | const reader = response?.body?.getReader(); 132 | let decoder = new TextDecoder(); 133 | 134 | let message = ""; 135 | while (true && reader) { 136 | const chunkReader = await reader.read(); 137 | 138 | const { done, value } = chunkReader; 139 | if (done) { 140 | break; 141 | } 142 | 143 | const text = decoder.decode(value); 144 | const lines = text.trim().split("\n\n\n\n\n\n"); 145 | 146 | for (const line of lines) { 147 | console.log("eventData ->", { line }); 148 | 149 | const eventData = safeParse(line); 150 | if (eventData?.sources) continue; 151 | if (eventData.error) { 152 | // if eventData has error 153 | console.log("error ->", eventData.error); 154 | setLoading(false); 155 | // show error 156 | alert(eventData.error); 157 | return; 158 | } 159 | if (eventData?.type === "text" || eventData?.type === "image") { 160 | setLoadingTxt(""); 161 | console.log("data ->", eventData.data); 162 | message = message + eventData.data; 163 | } else if (eventData?.type !== "bye") { 164 | setLoadingTxt(eventData.data); 165 | } else if (eventData?.type === "bye") { 166 | message = eventData.data; 167 | } else { 168 | setLoadingTxt(""); 169 | setLoading(false); 170 | } 171 | 172 | setAnswer(message); 173 | } 174 | } 175 | 176 | setAnswer(message); 177 | 178 | setTimeout(() => { 179 | setAnswer(""); 180 | setLoading(false); 181 | }, 700); 182 | } catch (error) { 183 | console.log(error); 184 | setAnswer(""); 185 | setLoading(false); 186 | } 187 | }; 188 | 189 | useEffect(() => { 190 | getLocation(); 191 | }, []); 192 | 193 | useEffect(() => { 194 | if ( 195 | !chats[chats.length - 1]?.isSender && 196 | chats[chats.length - 1]?.msg === "" 197 | ) 198 | handleSearch(); 199 | }, [chats]); 200 | 201 | useEffect(() => { 202 | scrollToBottom(); 203 | if (answer !== "" && chats.length) { 204 | let tempChats = [...chats]; 205 | tempChats[tempChats.length - 1].msg = answer; 206 | setChats(tempChats); 207 | } 208 | }, [answer, loading]); 209 | 210 | return ( 211 |
212 |
213 | {chats?.map((chat, id) => ( 214 | 224 | ))} 225 |
226 | 227 |
228 | {!chats.length ? ( 229 |
230 | {faqs?.map((val, id) => ( 231 |
{ 233 | setSearch(val); 234 | setChats((prev) => [ 235 | ...prev, 236 | { 237 | msg: val, 238 | isSender: true, 239 | sender: "You", 240 | }, 241 | { 242 | msg: "", 243 | isSender: false, 244 | sender: "Tune", 245 | }, 246 | ]); 247 | }} 248 | key={id} 249 | className="revealCardAnimation medium p-[12px] border cursor-pointer rounded-[8px] border-[#DFE1E6] w-full bg-[#FFFFFF] hover:bg-[#ECEFF3]" 250 | > 251 | {val} 252 |
253 | ))} 254 |
255 | ) : ( 256 | "" 257 | )} 258 |