├── .dockerignore ├── .env ├── .gitignore ├── .vscode └── launch.json ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── LICENSE-3rd-party.txt ├── README.md ├── SECURITY.md ├── __tests__ └── utils │ └── app │ └── importExports.test.ts ├── components ├── .DS_Store ├── Avatar │ ├── AgentAvatar.tsx │ ├── BotAvatar.tsx │ ├── SystemAvatar.tsx │ └── UserAvatar.tsx ├── Buttons │ └── SidebarActionButton │ │ ├── SidebarActionButton.tsx │ │ └── index.ts ├── Chat │ ├── Chat.tsx │ ├── ChatHeader.tsx │ ├── ChatInput.tsx │ ├── ChatInteractionMessage.tsx │ ├── ChatLoader.tsx │ ├── ChatMessage.tsx │ ├── ErrorMessageDiv.tsx │ ├── MemoizedChatMessage.tsx │ └── Regenerate.tsx ├── Chatbar │ ├── Chatbar.context.tsx │ ├── Chatbar.state.tsx │ ├── Chatbar.tsx │ └── components │ │ ├── ChatFolders.tsx │ │ ├── ChatbarSettings.tsx │ │ ├── ClearConversations.tsx │ │ ├── Conversation.tsx │ │ └── Conversations.tsx ├── Folder │ ├── Folder.tsx │ └── index.ts ├── Markdown │ ├── Chart.tsx │ ├── CodeBlock.tsx │ ├── CustomComponents.tsx │ ├── CustomDetails.tsx │ ├── CustomSummary.tsx │ ├── Image.tsx │ ├── Loading.tsx │ ├── MemoizedReactMarkdown.tsx │ └── Video.tsx ├── Mobile │ └── Navbar.tsx ├── Search │ ├── Search.tsx │ └── index.ts ├── Settings │ ├── Import.tsx │ └── SettingDialog.tsx ├── Sidebar │ ├── Sidebar.tsx │ ├── SidebarButton.tsx │ ├── components │ │ └── OpenCloseButton.tsx │ └── index.ts └── Spinner │ ├── Spinner.tsx │ └── index.ts ├── config.json ├── constants └── constants.tsx ├── hooks ├── useConversationOperations.ts ├── useCreateReducer.ts └── useFolderOperations.ts ├── next-env.d.ts ├── next-i18next.config.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── _document.tsx ├── aiq-auth.tsx ├── api │ ├── chat.ts │ └── home │ │ ├── home.context.tsx │ │ ├── home.state.tsx │ │ ├── home.tsx │ │ └── index.ts └── index.tsx ├── postcss.config.js ├── prettier.config.js ├── public ├── audio │ └── recording.wav ├── favicon.jpg ├── favicon1.png ├── locales │ └── en │ │ └── common.json ├── nvidia.jpg └── screenshots │ ├── hitl_prompt.png │ ├── hitl_settings.png │ ├── ui_generate_example.png │ ├── ui_generate_example_settings.png │ └── ui_home_page.png ├── styles └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types ├── chat.ts ├── data.ts ├── env.ts ├── error.ts ├── export.ts ├── folder.ts ├── index.ts ├── settings.ts └── storage.ts ├── utils ├── app │ ├── api.ts │ ├── clean.ts │ ├── codeblock.ts │ ├── const.ts │ ├── conversation.ts │ ├── folders.ts │ ├── helper.ts │ ├── importExport.ts │ ├── prompts.ts │ └── settings.ts └── data │ └── throttle.ts └── vitest.config.ts /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .gitignore 3 | .git 4 | tests/ 5 | *.md 6 | .idea 7 | out/ 8 | k8s 9 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_WORKFLOW=AIQ Toolkit 2 | NEXT_PUBLIC_WEBSOCKET_CHAT_COMPLETION_URL=ws://127.0.0.1:8000/websocket 3 | NEXT_PUBLIC_HTTP_CHAT_COMPLETION_URL=http://127.0.0.1:8000/chat/stream 4 | NEXT_PUBLIC_WEB_SOCKET_DEFAULT_ON=false 5 | NEXT_PUBLIC_CHAT_HISTORY_DEFAULT_ON=false 6 | NEXT_PUBLIC_RIGHT_MENU_OPEN=false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | package-lock.json 6 | 7 | # testing 8 | /coverage 9 | 10 | # production 11 | /build 12 | 13 | # next 14 | /.next 15 | /out 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | # environment files 25 | public/__ENV.js 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | yarn.lock 32 | *.pem 33 | .vscode 34 | 35 | # PyCharm build files 36 | .idea/ 37 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | // { 8 | // "name": "Attach to Chrome", 9 | // "port": 3000, 10 | // "request": "attach", 11 | // "type": "chrome", 12 | // "webRoot": "${workspaceFolder}" 13 | // }, 14 | { 15 | "name": "Debug1", 16 | "type": "node-terminal", 17 | "request": "launch", 18 | "command": "npm run dev", 19 | "cwd": "${workspaceFolder}", 20 | "serverReadyAction": { 21 | "pattern": "started server on .+, url: (https?://.+)", 22 | "uriFormat": "%s", 23 | "action": "debugWithChrome" 24 | }, 25 | "debugStdLib": true 26 | }, 27 | { 28 | "name": "Debug2", 29 | "type": "node-terminal", 30 | "request": "launch", 31 | "command": "NEXT_PRIVATE_TURBOPACK=true npm run dev", 32 | "cwd": "${workspaceFolder}", 33 | "env": { 34 | "NODE_ENV": "development" 35 | }, 36 | "serverReadyAction": { 37 | "pattern": "started server on .+, url: (https?://.+)", 38 | "uriFormat": "%s", 39 | "action": "debugWithChrome" 40 | }, 41 | "console": "integratedTerminal", 42 | "outputCapture": "std" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | **Welcome to AIQ Toolkit-UI** 4 | 5 | We appreciate your interest in contributing to our project. 6 | 7 | Before you get started, please read our guidelines for contributing. 8 | 9 | ## Types of Contributions 10 | 11 | We welcome the following types of contributions: 12 | 13 | - Bug fixes 14 | - New features 15 | - Documentation improvements 16 | - Code optimizations 17 | - Translations 18 | - Tests 19 | 20 | ## Get Started 21 | 22 | To get started, fork the project on GitHub and clone it locally on your machine. Then, create a new branch to work on your changes. 23 | 24 | ```bash 25 | git clone https://github.com/NVIDIA/AIQ Toolkit-UI.git 26 | cd AIQ Toolkit-UI 27 | git checkout -b my-branch-name 28 | ``` 29 | 30 | Before submitting your pull request, please make sure your changes pass our automated tests and adhere to our code style guidelines. 31 | 32 | ## Pull Request Process 33 | 34 | 1. Fork the project on GitHub. 35 | 2. Clone your forked repository locally on your machine. 36 | 3. Create a new branch from the main branch. 37 | 4. Make your changes on the new branch. 38 | 5. Ensure that your changes adhere to our code style guidelines and pass our automated tests. 39 | 6. Commit your changes and push them to your forked repository. 40 | 7. Submit a pull request to the main branch of the main repository. 41 | 42 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS base 2 | 3 | # Install dependencies only when needed 4 | FROM base AS deps 5 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. 6 | RUN apk add --no-cache libc6-compat 7 | 8 | 9 | WORKDIR /app 10 | 11 | # Install dependencies based on the preferred package manager 12 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 13 | 14 | 15 | RUN npm i 16 | 17 | # Rebuild the source code only when needed 18 | FROM base AS builder 19 | WORKDIR /app 20 | 21 | 22 | COPY --from=deps /app/node_modules ./node_modules 23 | COPY . . 24 | 25 | RUN apk update 26 | 27 | # Set working directory 28 | WORKDIR /app 29 | # install node modules 30 | COPY package.json /app/package.json 31 | RUN npm install 32 | # Copy all files from current directory to working dir in image 33 | COPY . . 34 | # Build the assets 35 | RUN yarn build 36 | 37 | # Next.js collects completely anonymous telemetry data about general usage. 38 | # Learn more here: https://nextjs.org/telemetry 39 | # Uncomment the following line in case you want to disable telemetry during the build. 40 | # ENV NEXT_TELEMETRY_DISABLED 1 41 | 42 | RUN npm run build 43 | 44 | # Production image, copy all the files and run next 45 | FROM base AS runner 46 | WORKDIR /app 47 | 48 | ENV NODE_ENV production 49 | # Uncomment the following line in case you want to disable telemetry during runtime. 50 | # ENV NEXT_TELEMETRY_DISABLED 1 51 | 52 | RUN addgroup --system --gid 1001 nodejs 53 | RUN adduser --system --uid 1001 nextjs 54 | 55 | COPY --from=builder /app/public ./public 56 | 57 | # Set the correct permission for prerender cache 58 | RUN mkdir .next 59 | RUN chown nextjs:nodejs .next 60 | 61 | # Automatically leverage output traces to reduce image size 62 | # https://nextjs.org/docs/advanced-features/output-file-tracing 63 | COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./ 64 | COPY --from=builder --chown=nextjs:nodejs /app/public ./public 65 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 66 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 67 | COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json 68 | 69 | USER nextjs 70 | 71 | EXPOSE 3000 72 | 73 | ENV PORT 3000 74 | # set hostname to localhost 75 | ENV HOSTNAME "0.0.0.0" 76 | 77 | # server.js is created by next build from the standalone output 78 | # https://nextjs.org/docs/pages/api-reference/next-config-js/output 79 | CMD ["node", "server.js"] 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: MIT 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a 5 | # copy of this software and associated documentation files (the "Software"), 6 | # to deal in the Software without restriction, including without limitation 7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | # and/or sell copies of the Software, and to permit persons to whom the 9 | # Software is furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | /* 22 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 23 | * SPDX-License-Identifier: MIT 24 | * 25 | * Permission is hereby granted, free of charge, to any person obtaining a 26 | * copy of this software and associated documentation files (the "Software"), 27 | * to deal in the Software without restriction, including without limitation 28 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 29 | * and/or sell copies of the Software, and to permit persons to whom the 30 | * Software is furnished to do so, subject to the following conditions: 31 | * 32 | * The above copyright notice and this permission notice shall be included in 33 | * all copies or substantial portions of the Software. 34 | * 35 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 38 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 | * DEALINGS IN THE SOFTWARE. 42 | */ 43 | 44 | MIT License 45 | 46 | Copyright (c) 2024 Ivan Fioravanti 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in all 56 | copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 64 | SOFTWARE. 65 | 66 | MIT License 67 | 68 | Copyright (c) 2024 Mckay Wrigley 69 | 70 | Permission is hereby granted, free of charge, to any person obtaining a copy 71 | of this software and associated documentation files (the "Software"), to deal 72 | in the Software without restriction, including without limitation the rights 73 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 74 | copies of the Software, and to permit persons to whom the Software is 75 | furnished to do so, subject to the following conditions: 76 | 77 | The above copyright notice and this permission notice shall be included in all 78 | copies or substantial portions of the Software. 79 | 80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 81 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 82 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 83 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 84 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 85 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 86 | SOFTWARE. 87 | -------------------------------------------------------------------------------- /LICENSE-3rd-party.txt: -------------------------------------------------------------------------------- 1 | AIQ Toolkit-UI Third-Party Licenses 2 | ================================= 3 | 4 | This file contains third-party license information for software packages used in this project. 5 | The licenses below apply to one or more packages included in this project. For each license, 6 | we list the packages that are distributed under it and include the full license text. 7 | 8 | ------------------------------------------------------------ 9 | MIT License 10 | ------------------------------------------------------------ 11 | The MIT License is a permissive free software license. Many of the packages used in this 12 | project are distributed under the MIT License. The full text of the MIT License is provided 13 | below. 14 | 15 | Packages under the MIT License: 16 | - @dqbd/tiktoken @ ^1.0.2 17 | - @radix-ui/react-select @ ^2.1.2 18 | - @tabler/icons-react @ ^2.9.0 19 | - @types/jwt-decode @ ^3.1.0 20 | - axios @ ^1.6.8 21 | - chart.js @ ^4.4.1 22 | - eventsource-parser @ ^0.1.0 23 | - file-saver @ ^2.0.5 24 | - html-to-image @ ^1.11.11 25 | - i18next @ ^22.4.13 26 | - jsonwebtoken @ ^9.0.2 27 | - jwt-decode @ ^4.0.0 28 | - lodash @ ^4.17.21 29 | - lucide-react @ ^0.454.0 30 | - next @ 13.5.3 31 | - next-auth @ ^4.24.7 32 | - next-i18next @ ^13.2.2 33 | - pptxgenjs @ ^3.12.0 34 | - react @ 18.2.0 35 | - react-bootstrap-modal @ ^4.2.0 36 | - react-chartjs-2 @ ^5.2.0 37 | - react-dom @ 18.2.0 38 | - react-force-graph-2d @ ^1.25.5 39 | - react-hot-toast @ ^2.4.0 40 | - react-i18next @ ^12.2.0 41 | - react-markdown @ ^8.0.5 42 | - react-query @ ^3.39.3 43 | - react-syntax-highlighter @ ^15.5.0 44 | - recharts @ ^2.12.7 45 | - rehype-mathjax @ ^4.0.2 46 | - rehype-raw @ ^7.0.0 47 | - remark-gfm @ ^3.0.1 48 | - remark-math @ ^5.1.1 49 | - remark-unwrap-images @ ^4.0.0 50 | - uuid @ ^9.0.1 51 | 52 | Dev Dependencies under the MIT License: 53 | - @tailwindcss/typography @ ^0.5.9 54 | - @trivago/prettier-plugin-sort-imports @ ^1.4.4 55 | - @types/jsdom @ ^21.1.1 56 | - @types/node @ 18.15.0 57 | - @types/react @ 18.0.28 58 | - @types/react-dom @ 18.0.11 59 | - @types/react-syntax-highlighter @ ^15.5.6 60 | - @types/uuid @ ^9.0.1 61 | - @vitest/coverage-c8 @ ^0.29.7 62 | - autoprefixer @ ^10.4.14 63 | - endent @ ^2.1.0 64 | - eslint @ 8.36.0 65 | - eslint-config-next @ 13.2.4 66 | - gpt-3-encoder @ ^1.1.4 67 | - jsdom @ ^21.1.1 68 | - postcss @ ^8.4.21 69 | - prettier @ ^2.8.7 70 | - prettier-plugin-tailwindcss @ ^0.2.5 71 | - tailwindcss @ ^3.3.3 72 | - vitest @ ^0.29.7 73 | - next-runtime-env @ ^1.3.0 74 | 75 | Full MIT License Text: 76 | -------------------------------------------------- 77 | MIT License 78 | 79 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 80 | and associated documentation files (the "Software"), to deal in the Software without restriction, 81 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 82 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 83 | subject to the following conditions: 84 | 85 | The above copyright notice and this permission notice shall be included in all copies or 86 | substantial portions of the Software. 87 | 88 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 89 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 90 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 92 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 93 | -------------------------------------------------- 94 | 95 | ------------------------------------------------------------ 96 | Apache License, Version 2.0 97 | ------------------------------------------------------------ 98 | The Apache License, Version 2.0 is a permissive license that also provides an express grant of patent rights. 99 | 100 | Packages under the Apache License, Version 2.0: 101 | - @datadog/browser-rum @ ^5.11.0 102 | - @mozilla/readability @ ^0.4.4 103 | - typescript @ 4.9.5 104 | 105 | Full Apache License, Version 2.0 Text: 106 | -------------------------------------------------- 107 | Apache License 108 | Version 2.0, January 2004 109 | http://www.apache.org/licenses/ 110 | 111 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 112 | 113 | 1. Definitions. 114 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined in this document. 115 | "Licensor" shall mean the copyright owner or entity 116 | authorized by the copyright owner that is granting the License. 117 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. 118 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 119 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 120 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 121 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work. 122 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the modifications represent, as a whole, an original work of authorship. 123 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work. 124 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 125 | 126 | 2. Grant of Copyright License. 127 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 128 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, 129 | publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works. 130 | 131 | 3. Grant of Patent License. 132 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 133 | no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, 134 | sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor 135 | that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work. 136 | 137 | 4. Redistribution. 138 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, 139 | and in Source or Object form, provided that You meet the following conditions: 140 | (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and 141 | (b) You must cause any modified files to carry prominent notices stating that You changed the files; and 142 | (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, 143 | and attribution notices from the Source form of the Work; and 144 | (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute 145 | must include a readable copy of the attribution notices contained within such NOTICE file. 146 | 147 | 5. Submission of Contributions. 148 | Unless You explicitly state otherwise, any Contribution submitted for inclusion in the Work shall be under the terms and conditions of this License. 149 | 150 | 6. Trademarks. 151 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor. 152 | 153 | 7. Disclaimer of Warranty. 154 | The Work is provided on an "AS IS" basis, without warranties or conditions of any kind, either express or implied. 155 | 156 | 8. Limitation of Liability. 157 | In no event shall any Contributor be liable for any damages arising from the use of the Work. 158 | 159 | END OF TERMS AND CONDITIONS 160 | -------------------------------------------------- 161 | 162 | END OF THIRD-PARTY LICENSES 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AIQ Toolkit - UI 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | [![AIQ Toolkit](https://img.shields.io/badge/AIQToolkit-Frontend-green)](https://github.com/NVIDIA/AIQToolkit/tree/main) 5 | 6 | This is the official frontend user interface component for [AIQ Toolkit](https://github.com/NVIDIA/AIQToolkit/tree/main), an open-source library for building AI agents and workflows. 7 | 8 | This project builds upon the work of: 9 | - [chatbot-ui](https://github.com/mckaywrigley/chatbot-ui) by Mckay Wrigley 10 | - [chatbot-ollama](https://github.com/ivanfioravanti/chatbot-ollama) by Ivan Fioravanti 11 | 12 | ## Features 13 | - 🎨 Modern and responsive user interface 14 | - 🔄 Real-time streaming responses 15 | - 🤝 Human-in-the-loop workflow support 16 | - 🌙 Light/Dark theme 17 | - 🔌 WebSocket and HTTP API integration 18 | - 🐳 Docker support 19 | 20 | ## Getting Started 21 | 22 | ### Prerequisites 23 | - [AIQ Toolkit](https://github.com/NVIDIA/AIQToolkit/tree/main) installed and configured 24 | - Git 25 | - Node.js (v18 or higher) 26 | - npm or Docker 27 | 28 | ### Installation 29 | 30 | Clone the repository: 31 | ```bash 32 | git clone git@github.com:NVIDIA/AIQToolkit.git 33 | cd AIQToolkit 34 | ``` 35 | 36 | Install dependencies: 37 | ```bash 38 | npm ci 39 | ``` 40 | 41 | ### Running the Application 42 | 43 | #### Local Development 44 | ```bash 45 | npm run dev 46 | ``` 47 | The application will be available at `http://localhost:3000` 48 | 49 | #### Docker Deployment 50 | ```bash 51 | # Build the Docker image 52 | docker build -t AIQ Toolkit-UI . 53 | 54 | # Run the container with environment variables from .env 55 | # Ensure the .env file is present before running this command. 56 | # Skip --env-file .env if no overrides are needed. 57 | docker run --env-file .env -p 3000:3000 AIQ Toolkit-UI 58 | ``` 59 | 60 | ![AIQ Toolkit Web User Interface](public/screenshots/ui_home_page.png) 61 | 62 | ## Configuration 63 | 64 | ### HTTP API Connection 65 | Settings can be configured by selecting the `Settings` icon located on the bottom left corner of the home page. 66 | 67 | ![AIQ Toolkit Web UI Settings](public/screenshots/ui_generate_example_settings.png) 68 | 69 | ### Settings Options 70 | NOTE: Most of the time, you will want to select /chat/stream for intermediate results streaming. 71 | 72 | - `Theme`: Light or Dark Theme 73 | - `HTTP URL for Chat Completion`: REST API endpoint 74 | - /generate - Single response generation 75 | - /generate/stream - Streaming response generation 76 | - /chat - Single response chat completion 77 | - /chat/stream - Streaming chat completion 78 | - `WebSocket URL for Completion`: WebSocket URL to connect to running AIQ Toolkit server 79 | - `WebSocket Schema`: Workflow schema type over WebSocket connection 80 | 81 | ## Usage Examples 82 | 83 | ### Simple Calculator Example 84 | 85 | #### Setup and Configuration 86 | 1. Set up [AIQ Toolkit Get Started ](https://github.com/NVIDIA/AIQToolkit/blob/main/docs/source/intro/get-started.md) 87 | 2. Start workflow by following the [Simple Calculator Example](https://github.com/NVIDIA/AIQToolkit/blob/main/examples/simple_calculator/README.md) 88 | ```bash 89 | aiq serve --config_file=examples/simple_calculator/configs/config.yml 90 | ``` 91 | 92 | #### Testing the Calculator 93 | Interact with the chat interface by prompting the agent with the message: 94 | ``` 95 | Is 4 + 4 greater than the current hour of the day? 96 | ``` 97 | 98 | ![AIQ Toolkit Web UI Workflow Result](public/screenshots/ui_generate_example.png) 99 | 100 | ## API Integration 101 | 102 | ### Server Communication 103 | The UI supports both HTTP requests (OpenAI compatible) and WebSocket connections for server communication. For detailed information about WebSocket messaging integration, please refer to the [WebSocket Documentation](https://github.com/NVIDIA/AIQToolkit/blob/main/docs/source/references/websockets.md) in the AIQ Toolkit documentation. 104 | 105 | 106 | 107 | ## License 108 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. The project includes code from [chatbot-ui](https://github.com/mckaywrigley/chatbot-ui) and [chatbot-ollama](https://github.com/ivanfioravanti/chatbot-ollama), which are also MIT licensed. 109 | 110 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | This security policy outlines the process for reporting vulnerabilities and secrets found within this GitHub repository. It is essential that all contributors and users adhere to this policy in order to maintain a secure and stable environment. 4 | 5 | ## Reporting a Vulnerability 6 | 7 | If you discover a vulnerability within the code, dependencies, or any other component of this repository, please follow these steps: 8 | 9 | 1. **Do not disclose the vulnerability publicly.** Publicly disclosing a vulnerability may put the project at risk and could potentially harm other users. 10 | 11 | 2. **Contact the repository maintainer(s) privately.** Send a private message or email to the maintainer(s) with a detailed description of the vulnerability. Include the following information: 12 | 13 | - The affected component(s) 14 | - Steps to reproduce the issue 15 | - Potential impact of the vulnerability 16 | - Any possible mitigations or workarounds 17 | 18 | 3. **Wait for a response from the maintainer(s).** Please be patient, as they may need time to investigate and verify the issue. The maintainer(s) should acknowledge receipt of your report and provide an estimated time frame for addressing the vulnerability. 19 | 20 | 4. **Cooperate with the maintainer(s).** If requested, provide additional information or assistance to help resolve the issue. 21 | 22 | 5. **Do not disclose the vulnerability until the maintainer(s) have addressed it.** Once the issue has been resolved, the maintainer(s) may choose to publicly disclose the vulnerability and credit you for the discovery. 23 | 24 | ## Reporting Secrets 25 | 26 | If you discover any secrets, such as API keys or passwords, within the repository, follow these steps: 27 | 28 | 1. **Do not share the secret or use it for unauthorized purposes.** Misusing a secret could have severe consequences for the project and its users. 29 | 30 | 2. **Contact the repository maintainer(s) privately.** Notify them of the discovered secret, its location, and any potential risks associated with it. 31 | 32 | 3. **Wait for a response and further instructions.** 33 | 34 | ## Responsible Disclosure 35 | 36 | We encourage responsible disclosure of vulnerabilities and secrets. If you follow the steps outlined in this policy, we will work with you to understand and address the issue. We will not take legal action against individuals who discover and report vulnerabilities or secrets in accordance with this policy. 37 | 38 | ## Patching and Updates 39 | 40 | We are committed to maintaining the security of our project. When vulnerabilities are reported and confirmed, we will: 41 | 42 | 1. Work diligently to develop and apply a patch or implement a mitigation strategy. 43 | 2. Keep the reporter informed about the progress of the fix. 44 | 3. Update the repository with the necessary patches and document the changes in the release notes or changelog. 45 | 4. Credit the reporter for the discovery, if they wish to be acknowledged. 46 | 47 | ## Contributing to Security 48 | 49 | We welcome contributions that help improve the security of our project. If you have suggestions or want to contribute code to address security issues, please follow the standard contribution guidelines for this repository. When submitting a pull request related to security, please mention that it addresses a security issue and provide any necessary context. 50 | 51 | By adhering to this security policy, you contribute to the overall security and stability of the project. Thank you for your cooperation and responsible handling of vulnerabilities and secrets. 52 | -------------------------------------------------------------------------------- /__tests__/utils/app/importExports.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | cleanData, 3 | isExportFormatV1, 4 | isExportFormatV2, 5 | isExportFormatV3, 6 | isExportFormatV4, 7 | isLatestExportFormat, 8 | } from '@/utils/app/importExport'; 9 | 10 | import { ExportFormatV1, ExportFormatV2, ExportFormatV4 } from '@/types/export'; 11 | 12 | 13 | import { describe, expect, it } from 'vitest'; 14 | 15 | describe('Export Format Functions', () => { 16 | describe('isExportFormatV1', () => { 17 | it('should return true for v1 format', () => { 18 | const obj = [{ id: 1 }]; 19 | expect(isExportFormatV1(obj)).toBe(true); 20 | }); 21 | 22 | it('should return false for non-v1 formats', () => { 23 | const obj = { version: 3, history: [], folders: [] }; 24 | expect(isExportFormatV1(obj)).toBe(false); 25 | }); 26 | }); 27 | 28 | describe('isExportFormatV2', () => { 29 | it('should return true for v2 format', () => { 30 | const obj = { history: [], folders: [] }; 31 | expect(isExportFormatV2(obj)).toBe(true); 32 | }); 33 | 34 | it('should return false for non-v2 formats', () => { 35 | const obj = { version: 3, history: [], folders: [] }; 36 | expect(isExportFormatV2(obj)).toBe(false); 37 | }); 38 | }); 39 | 40 | describe('isExportFormatV3', () => { 41 | it('should return true for v3 format', () => { 42 | const obj = { version: 3, history: [], folders: [] }; 43 | expect(isExportFormatV3(obj)).toBe(true); 44 | }); 45 | 46 | it('should return false for non-v3 formats', () => { 47 | const obj = { version: 4, history: [], folders: [] }; 48 | expect(isExportFormatV3(obj)).toBe(false); 49 | }); 50 | }); 51 | 52 | describe('isExportFormatV4', () => { 53 | it('should return true for v4 format', () => { 54 | const obj = { version: 4, history: [], folders: [], prompts: [] }; 55 | expect(isExportFormatV4(obj)).toBe(true); 56 | }); 57 | 58 | it('should return false for non-v4 formats', () => { 59 | const obj = { version: 5, history: [], folders: [], prompts: [] }; 60 | expect(isExportFormatV4(obj)).toBe(false); 61 | }); 62 | }); 63 | }); 64 | 65 | describe('cleanData Functions', () => { 66 | describe('cleaning v1 data', () => { 67 | it('should return the latest format', () => { 68 | const data = [ 69 | { 70 | id: 1, 71 | name: 'conversation 1', 72 | messages: [ 73 | { 74 | role: 'user', 75 | content: "what's up ?", 76 | }, 77 | { 78 | role: 'assistant', 79 | content: 'Hi', 80 | }, 81 | ], 82 | }, 83 | ] as ExportFormatV1; 84 | const obj = cleanData(data); 85 | expect(isLatestExportFormat(obj)).toBe(true); 86 | expect(obj).toEqual({ 87 | version: 4, 88 | history: [ 89 | { 90 | id: 1, 91 | name: 'conversation 1', 92 | messages: [ 93 | { 94 | role: 'user', 95 | content: "what's up ?", 96 | }, 97 | { 98 | role: 'assistant', 99 | content: 'Hi', 100 | }, 101 | ], 102 | folderId: null, 103 | }, 104 | ], 105 | folders: [], 106 | prompts: [], 107 | }); 108 | }); 109 | }); 110 | 111 | describe('cleaning v2 data', () => { 112 | it('should return the latest format', () => { 113 | const data = { 114 | history: [ 115 | { 116 | id: '1', 117 | name: 'conversation 1', 118 | messages: [ 119 | { 120 | role: 'user', 121 | content: "what's up ?", 122 | }, 123 | { 124 | role: 'assistant', 125 | content: 'Hi', 126 | }, 127 | ], 128 | }, 129 | ], 130 | folders: [ 131 | { 132 | id: 1, 133 | name: 'folder 1', 134 | }, 135 | ], 136 | } as ExportFormatV2; 137 | const obj = cleanData(data); 138 | expect(isLatestExportFormat(obj)).toBe(true); 139 | expect(obj).toEqual({ 140 | version: 4, 141 | history: [ 142 | { 143 | id: '1', 144 | name: 'conversation 1', 145 | messages: [ 146 | { 147 | role: 'user', 148 | content: "what's up ?", 149 | }, 150 | { 151 | role: 'assistant', 152 | content: 'Hi', 153 | }, 154 | ], 155 | folderId: null, 156 | }, 157 | ], 158 | folders: [ 159 | { 160 | id: '1', 161 | name: 'folder 1', 162 | type: 'chat', 163 | }, 164 | ], 165 | prompts: [], 166 | }); 167 | }); 168 | }); 169 | 170 | describe('cleaning v4 data', () => { 171 | it('should return the latest format', () => { 172 | const data = { 173 | version: 4, 174 | history: [ 175 | { 176 | id: '1', 177 | name: 'conversation 1', 178 | messages: [ 179 | { 180 | role: 'user', 181 | content: "what's up ?", 182 | }, 183 | { 184 | role: 'assistant', 185 | content: 'Hi', 186 | }, 187 | ], 188 | folderId: null, 189 | }, 190 | ], 191 | folders: [ 192 | { 193 | id: '1', 194 | name: 'folder 1', 195 | type: 'chat', 196 | }, 197 | ], 198 | } as ExportFormatV4; 199 | 200 | const obj = cleanData(data); 201 | expect(isLatestExportFormat(obj)).toBe(true); 202 | expect(obj).toEqual({ 203 | version: 4, 204 | history: [ 205 | { 206 | id: '1', 207 | name: 'conversation 1', 208 | messages: [ 209 | { 210 | role: 'user', 211 | content: "what's up ?", 212 | }, 213 | { 214 | role: 'assistant', 215 | content: 'Hi', 216 | }, 217 | ], 218 | folderId: null, 219 | }, 220 | ], 221 | folders: [ 222 | { 223 | id: '1', 224 | name: 'folder 1', 225 | type: 'chat', 226 | }, 227 | ] 228 | }); 229 | }); 230 | }); 231 | }); 232 | -------------------------------------------------------------------------------- /components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/NeMo-Agent-Toolkit-UI/9816cd9801e782324f48a0a77c299c78406f0919/components/.DS_Store -------------------------------------------------------------------------------- /components/Avatar/AgentAvatar.tsx: -------------------------------------------------------------------------------- 1 | import { IconUserPentagon } from '@tabler/icons-react'; 2 | import React from 'react'; 3 | export const AgentAvatar = ({height = 7, width = 7}) => { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } -------------------------------------------------------------------------------- /components/Avatar/BotAvatar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const BotAvatar = ({height = 30, width = 30, src= ''}) => { 4 | 5 | const onError = (event: { target: { src: string; }; }) => { 6 | console.error('error loading bot avatar'); 7 | event.target.src = `nvidia.jpg`; 8 | }; 9 | 10 | return bot-avatar 18 | } -------------------------------------------------------------------------------- /components/Avatar/SystemAvatar.tsx: -------------------------------------------------------------------------------- 1 | import { IconPasswordUser, IconUserPentagon } from '@tabler/icons-react'; 2 | import React from 'react'; 3 | export const SystemAgentAvatar = ({height = 7, width = 7}) => { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } -------------------------------------------------------------------------------- /components/Avatar/UserAvatar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getInitials } from '@/utils/app/helper'; 3 | 4 | export const UserAvatar = ({src = '', height = 30, width = 30}) => { 5 | const profilePicUrl = src || `` 6 | 7 | const onError = (event: { target: { src: string; }; }) => { 8 | const svg = ` 9 | 10 | 11 | user 12 | 13 | `; 14 | event.target.src = `data:image/svg+xml;base64,${window.btoa(svg)}`; 15 | }; 16 | 17 | return {'user-avatar'} 26 | 27 | } -------------------------------------------------------------------------------- /components/Buttons/SidebarActionButton/SidebarActionButton.tsx: -------------------------------------------------------------------------------- 1 | import { MouseEventHandler, ReactElement } from 'react'; 2 | 3 | interface Props { 4 | handleClick: MouseEventHandler; 5 | children: ReactElement; 6 | } 7 | 8 | const SidebarActionButton = ({ handleClick, children }: Props) => ( 9 | 15 | ); 16 | 17 | export default SidebarActionButton; 18 | -------------------------------------------------------------------------------- /components/Buttons/SidebarActionButton/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './SidebarActionButton'; 2 | -------------------------------------------------------------------------------- /components/Chat/ChatHeader.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { env } from 'next-runtime-env' 4 | 5 | import React, { useContext, useState, useRef, useEffect } from 'react'; 6 | import { 7 | IconArrowsSort, 8 | IconMobiledataOff, 9 | IconSun, 10 | IconMoonFilled, 11 | IconUserFilled, 12 | IconChevronLeft, 13 | IconChevronRight 14 | } from '@tabler/icons-react'; 15 | import HomeContext from '@/pages/api/home/home.context'; 16 | import { getWorkflowName } from '@/utils/app/helper'; 17 | 18 | export const ChatHeader = ({ webSocketModeRef = {} }) => { 19 | const [isMenuOpen, setIsMenuOpen] = useState(false); 20 | const [isExpanded, setIsExpanded] = useState(env('NEXT_PUBLIC_RIGHT_MENU_OPEN') === 'true' || process?.env?.NEXT_PUBLIC_RIGHT_MENU_OPEN === 'true' ? true : false); 21 | const menuRef = useRef(null); 22 | 23 | const workflow = getWorkflowName() 24 | 25 | const { 26 | state: { 27 | chatHistory, 28 | webSocketMode, 29 | webSocketConnected, 30 | lightMode, 31 | selectedConversation 32 | }, 33 | dispatch: homeDispatch, 34 | } = useContext(HomeContext); 35 | 36 | 37 | const handleLogin = () => { 38 | console.log('Login clicked'); 39 | setIsMenuOpen(false); 40 | }; 41 | 42 | useEffect(() => { 43 | const handleClickOutside = (event) => { 44 | if (menuRef.current && !menuRef.current.contains(event.target)) { 45 | setIsMenuOpen(false); 46 | } 47 | }; 48 | 49 | document.addEventListener('mousedown', handleClickOutside); 50 | return () => document.removeEventListener('mousedown', handleClickOutside); 51 | }, []); 52 | 53 | return ( 54 |
55 | { 56 | selectedConversation?.messages?.length > 0 ? 57 |
58 | {workflow} 59 |
60 | : 61 |
62 |
63 | Hi, I'm {workflow} 64 |
65 |
66 | How can I assist you today? 67 |
68 |
69 | } 70 | 71 | {/* Collapsible Menu */} 72 |
73 | 81 | 82 |
83 | {/* Chat History Toggle */} 84 |
85 | 103 |
104 | 105 | {/* WebSocket Mode Toggle */} 106 |
107 | 133 |
134 | 135 | {/* Theme Toggle Button */} 136 |
137 | 153 |
154 | 155 | {/* User Icon with Dropdown Menu */} 156 |
157 | 163 | {isMenuOpen && ( 164 |
165 |
166 | 172 |
173 |
174 | )} 175 |
176 |
177 |
178 |
179 | ); 180 | }; -------------------------------------------------------------------------------- /components/Chat/ChatInteractionMessage.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { IconInfoCircle, IconX } from "@tabler/icons-react"; 3 | import { useState } from "react"; 4 | import { toast } from "react-hot-toast"; 5 | 6 | export const InteractionModal = ({ isOpen, interactionMessage, onClose, onSubmit }) => { 7 | if (!isOpen || !interactionMessage) return null; 8 | 9 | const { content } = interactionMessage; 10 | const [userInput, setUserInput] = useState(""); 11 | const [error, setError] = useState(""); 12 | 13 | // Validation for Text Input 14 | const handleTextSubmit = () => { 15 | if (content?.required && !userInput.trim()) { 16 | setError("This field is required."); 17 | return; 18 | } 19 | setError(""); 20 | onSubmit({interactionMessage, userResponse: userInput}); 21 | onClose(); 22 | }; 23 | 24 | // Handle Choice Selection 25 | const handleChoiceSubmit = (option = '') => { 26 | if (content?.required && !option) { 27 | setError("Please select an option."); 28 | return; 29 | } 30 | setError(""); 31 | onSubmit({interactionMessage, userResponse: option}); 32 | onClose(); 33 | }; 34 | 35 | // Handle Radio Selection 36 | const handleRadioSubmit = () => { 37 | if (content?.required && !userInput) { 38 | setError("Please select an option."); 39 | return; 40 | } 41 | setError(""); 42 | onSubmit({interactionMessage, userResponse: userInput}); 43 | onClose(); 44 | }; 45 | 46 | if(content.input_type === 'notification'){ 47 | toast.custom((t) => ( 48 |
51 | 52 | {content?.text || 'No content found for this notification'} 53 | 59 |
60 | ), { 61 | position: 'top-right', 62 | duration: Infinity, 63 | id: 'notification-toast' 64 | }) 65 | return null 66 | } 67 | 68 | return ( 69 |
70 |
71 |

{content?.text}

72 | 73 | {content.input_type === "text" && ( 74 |
75 |