├── LICENSE
├── README.md
├── nyro-chatbot
├── .env.local.example
├── .eslintrc.json
├── .gitignore
├── .husky
│ └── pre-commit
├── .nvmrc
├── .vscode
│ └── settings.json
├── __tests__
│ ├── lib
│ │ └── openapi-conversion.test.ts
│ └── playwright-test
│ │ ├── .gitignore
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── playwright.config.ts
│ │ └── tests
│ │ └── login.spec.ts
├── app
│ ├── [locale]
│ │ ├── [workspaceid]
│ │ │ ├── chat
│ │ │ │ ├── [chatid]
│ │ │ │ │ └── page.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── globals.css
│ │ ├── help
│ │ │ └── page.tsx
│ │ ├── layout.tsx
│ │ ├── loading.tsx
│ │ ├── login
│ │ │ ├── page.tsx
│ │ │ └── password
│ │ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── setup
│ │ │ └── page.tsx
│ ├── api
│ │ ├── assistants
│ │ │ └── openai
│ │ │ │ └── route.ts
│ │ ├── chat
│ │ │ ├── anthropic
│ │ │ │ └── route.ts
│ │ │ ├── azure
│ │ │ │ └── route.ts
│ │ │ ├── custom
│ │ │ │ └── route.ts
│ │ │ ├── google
│ │ │ │ └── route.ts
│ │ │ ├── groq
│ │ │ │ └── route.ts
│ │ │ ├── mistral
│ │ │ │ └── route.ts
│ │ │ ├── openai
│ │ │ │ └── route.ts
│ │ │ ├── openrouter
│ │ │ │ └── route.ts
│ │ │ ├── perplexity
│ │ │ │ └── route.ts
│ │ │ └── tools
│ │ │ │ └── route.ts
│ │ ├── command
│ │ │ └── route.ts
│ │ ├── keys
│ │ │ └── route.ts
│ │ ├── retrieval
│ │ │ ├── process
│ │ │ │ ├── docx
│ │ │ │ │ └── route.ts
│ │ │ │ └── route.ts
│ │ │ └── retrieve
│ │ │ │ └── route.ts
│ │ └── username
│ │ │ ├── available
│ │ │ └── route.ts
│ │ │ └── get
│ │ │ └── route.ts
│ └── auth
│ │ └── callback
│ │ └── route.ts
├── components.json
├── components
│ ├── chat
│ │ ├── assistant-picker.tsx
│ │ ├── chat-command-input.tsx
│ │ ├── chat-files-display.tsx
│ │ ├── chat-help.tsx
│ │ ├── chat-helpers
│ │ │ └── index.ts
│ │ ├── chat-hooks
│ │ │ ├── use-chat-handler.tsx
│ │ │ ├── use-chat-history.tsx
│ │ │ ├── use-prompt-and-command.tsx
│ │ │ ├── use-scroll.tsx
│ │ │ └── use-select-file-handler.tsx
│ │ ├── chat-input.tsx
│ │ ├── chat-messages.tsx
│ │ ├── chat-retrieval-settings.tsx
│ │ ├── chat-scroll-buttons.tsx
│ │ ├── chat-secondary-buttons.tsx
│ │ ├── chat-settings.tsx
│ │ ├── chat-ui.tsx
│ │ ├── file-picker.tsx
│ │ ├── prompt-picker.tsx
│ │ ├── quick-setting-option.tsx
│ │ ├── quick-settings.tsx
│ │ └── tool-picker.tsx
│ ├── icons
│ │ ├── anthropic-svg.tsx
│ │ ├── google-svg.tsx
│ │ ├── nyro-svg.tsx
│ │ └── openai-svg.tsx
│ ├── messages
│ │ ├── message-actions.tsx
│ │ ├── message-codeblock.tsx
│ │ ├── message-markdown-memoized.tsx
│ │ ├── message-markdown.tsx
│ │ ├── message-replies.tsx
│ │ └── message.tsx
│ ├── models
│ │ ├── model-icon.tsx
│ │ ├── model-option.tsx
│ │ └── model-select.tsx
│ ├── setup
│ │ ├── api-step.tsx
│ │ ├── finish-step.tsx
│ │ ├── profile-step.tsx
│ │ └── step-container.tsx
│ ├── sidebar
│ │ ├── items
│ │ │ ├── all
│ │ │ │ ├── sidebar-create-item.tsx
│ │ │ │ ├── sidebar-delete-item.tsx
│ │ │ │ ├── sidebar-display-item.tsx
│ │ │ │ └── sidebar-update-item.tsx
│ │ │ ├── assistants
│ │ │ │ ├── assistant-item.tsx
│ │ │ │ ├── assistant-retrieval-select.tsx
│ │ │ │ ├── assistant-tool-select.tsx
│ │ │ │ └── create-assistant.tsx
│ │ │ ├── chat
│ │ │ │ ├── chat-item.tsx
│ │ │ │ ├── delete-chat.tsx
│ │ │ │ └── update-chat.tsx
│ │ │ ├── collections
│ │ │ │ ├── collection-file-select.tsx
│ │ │ │ ├── collection-item.tsx
│ │ │ │ └── create-collection.tsx
│ │ │ ├── files
│ │ │ │ ├── create-file.tsx
│ │ │ │ └── file-item.tsx
│ │ │ ├── folders
│ │ │ │ ├── delete-folder.tsx
│ │ │ │ ├── folder-item.tsx
│ │ │ │ └── update-folder.tsx
│ │ │ ├── models
│ │ │ │ ├── create-model.tsx
│ │ │ │ └── model-item.tsx
│ │ │ ├── presets
│ │ │ │ ├── create-preset.tsx
│ │ │ │ └── preset-item.tsx
│ │ │ ├── prompts
│ │ │ │ ├── create-prompt.tsx
│ │ │ │ └── prompt-item.tsx
│ │ │ └── tools
│ │ │ │ ├── create-tool.tsx
│ │ │ │ └── tool-item.tsx
│ │ ├── sidebar-content.tsx
│ │ ├── sidebar-create-buttons.tsx
│ │ ├── sidebar-data-list.tsx
│ │ ├── sidebar-search.tsx
│ │ ├── sidebar-switch-item.tsx
│ │ ├── sidebar-switcher.tsx
│ │ └── sidebar.tsx
│ ├── ui
│ │ ├── accordion.tsx
│ │ ├── advanced-settings.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── aspect-ratio.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── brand.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── card.tsx
│ │ ├── chat-app.tsx
│ │ ├── chat-settings-form.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dashboard.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── file-icon.tsx
│ │ ├── file-preview.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── image-picker.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── limit-display.tsx
│ │ ├── menu-bar.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── peek-bar.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── screen-loader.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── submit-button.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea-autosize.tsx
│ │ ├── textarea.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ ├── use-toast.ts
│ │ └── with-tooltip.tsx
│ ├── utility
│ │ ├── alerts.tsx
│ │ ├── announcements.tsx
│ │ ├── change-password.tsx
│ │ ├── command-k.tsx
│ │ ├── drawing-canvas.tsx
│ │ ├── global-state.tsx
│ │ ├── import.tsx
│ │ ├── profile-settings.tsx
│ │ ├── providers.tsx
│ │ ├── theme-switcher.tsx
│ │ ├── translations-provider.tsx
│ │ └── workspace-switcher.tsx
│ └── workspace
│ │ ├── assign-workspaces.tsx
│ │ ├── delete-workspace.tsx
│ │ └── workspace-settings.tsx
├── context
│ └── context.tsx
├── db
│ ├── assistant-collections.ts
│ ├── assistant-files.ts
│ ├── assistant-tools.ts
│ ├── assistants.ts
│ ├── chat-files.ts
│ ├── chats.ts
│ ├── collection-files.ts
│ ├── collections.ts
│ ├── files.ts
│ ├── folders.ts
│ ├── index.ts
│ ├── limits.ts
│ ├── message-file-items.ts
│ ├── messages.ts
│ ├── models.ts
│ ├── presets.ts
│ ├── profile.ts
│ ├── prompts.ts
│ ├── storage
│ │ ├── assistant-images.ts
│ │ ├── files.ts
│ │ ├── message-images.ts
│ │ ├── profile-images.ts
│ │ └── workspace-images.ts
│ ├── tools.ts
│ └── workspaces.ts
├── i18nConfig.js
├── jest.config.ts
├── lib
│ ├── blob-to-b64.ts
│ ├── build-prompt.ts
│ ├── chat-setting-limits.ts
│ ├── consume-stream.ts
│ ├── envs.ts
│ ├── export-old-data.ts
│ ├── generate-local-embedding.ts
│ ├── hooks
│ │ ├── use-copy-to-clipboard.tsx
│ │ └── use-hotkey.tsx
│ ├── i18n.ts
│ ├── models
│ │ ├── fetch-models.ts
│ │ └── llm
│ │ │ ├── anthropic-llm-list.ts
│ │ │ ├── google-llm-list.ts
│ │ │ ├── groq-llm-list.ts
│ │ │ ├── llm-list.ts
│ │ │ ├── mistral-llm-list.ts
│ │ │ ├── openai-llm-list.ts
│ │ │ └── perplexity-llm-list.ts
│ ├── openapi-conversion.ts
│ ├── retrieval
│ │ └── processing
│ │ │ ├── csv.ts
│ │ │ ├── docx.ts
│ │ │ ├── index.ts
│ │ │ ├── json.ts
│ │ │ ├── md.ts
│ │ │ ├── pdf.ts
│ │ │ └── txt.ts
│ ├── server
│ │ ├── server-chat-helpers.ts
│ │ └── server-utils.ts
│ ├── supabase
│ │ ├── browser-client.ts
│ │ ├── client.ts
│ │ ├── middleware.ts
│ │ └── server.ts
│ └── utils.ts
├── middleware.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── prettier.config.cjs
├── public
│ ├── DARK_BRAND_LOGO.png
│ ├── LIGHT_BRAND_LOGO.png
│ ├── favicon.ico
│ ├── favicon2.ico
│ ├── icon-96x39.png
│ ├── locales
│ │ ├── de
│ │ │ └── translation.json
│ │ └── en
│ │ │ └── translation.json
│ ├── manifest.json
│ ├── providers
│ │ ├── groq.png
│ │ ├── meta.png
│ │ ├── mistral.png
│ │ └── perplexity.png
│ ├── readme
│ │ └── screenshot.png
│ └── worker-development.js
├── supabase
│ ├── .gitignore
│ ├── config.toml
│ ├── migrations
│ │ ├── 20240108234540_setup.sql
│ │ ├── 20240108234541_add_profiles.sql
│ │ ├── 20240108234542_add_workspaces.sql
│ │ ├── 20240108234543_add_folders.sql
│ │ ├── 20240108234544_add_files.sql
│ │ ├── 20240108234545_add_file_items.sql
│ │ ├── 20240108234546_add_presets.sql
│ │ ├── 20240108234547_add_assistants.sql
│ │ ├── 20240108234548_add_chats.sql
│ │ ├── 20240108234549_add_messages.sql
│ │ ├── 20240108234550_add_prompts.sql
│ │ ├── 20240108234551_add_collections.sql
│ │ ├── 20240115135033_add_openrouter.sql
│ │ ├── 20240115171510_add_assistant_files.sql
│ │ ├── 20240115171524_add_tools.sql
│ │ ├── 20240115172125_add_assistant_tools.sql
│ │ ├── 20240118224049_add_azure_embeddings.sql
│ │ ├── 20240124234424_tool_improvements.sql
│ │ ├── 20240125192042_upgrade_openai_models.sql
│ │ ├── 20240125194719_add_custom_models.sql
│ │ ├── 20240129232644_add_workspace_images.sql
│ │ ├── 20240212063532_add_at_assistants.sql
│ │ ├── 20240213040255_remove_request_in_body_from_tools.sql
│ │ ├── 20240213085646_add_context_length_to_custom_models.sql
│ │ └── 20240302004845_add_groq.sql
│ ├── seed.sql
│ └── types.ts
├── tailwind.config.ts
├── tsconfig.json
├── types
│ ├── announcement.ts
│ ├── assistant-retrieval-item.ts
│ ├── chat-file.tsx
│ ├── chat-message.ts
│ ├── chat.ts
│ ├── collection-file.ts
│ ├── content-type.ts
│ ├── error-response.ts
│ ├── file-item-chunk.ts
│ ├── images
│ │ ├── assistant-image.ts
│ │ ├── message-image.ts
│ │ └── workspace-image.ts
│ ├── index.ts
│ ├── key-type.ts
│ ├── llms.ts
│ ├── models.ts
│ ├── sharing.ts
│ ├── sidebar-data.ts
│ └── valid-keys.ts
└── worker
│ └── index.js
├── nyro-electron
├── .gitignore
├── favicon.icns
├── favicon.ico
├── main.js
├── package-lock.json
├── package.json
├── preload.js
└── yarn.lock
└── script.sh
/LICENSE:
--------------------------------------------------------------------------------
1 | # Nyro License
2 |
3 | Copyright (c) 2024-present Nyro/PeerEdu, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sublicense copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | 1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | 2. Commercial use, sale, or distribution of the entire Nyro application or any substantial portion thereof is prohibited without explicit written permission from Nyro/PeerEdu, Inc.
10 |
11 | 3. You may not use the name "Nyro" or any Nyro/PeerEdu, Inc. trademarks to endorse or promote products derived from this Software without specific prior written permission.
12 |
13 | 4. Any distribution of the Software or derivative works must be under the same terms and conditions as this license.
14 |
15 | 5. For commercial use or licensing inquiries, please contact Nyro/PeerEdu, Inc. at [insert contact information].
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/nyro-chatbot/.env.local.example:
--------------------------------------------------------------------------------
1 | # Supabase Public
2 | NEXT_PUBLIC_SUPABASE_URL=
3 | NEXT_PUBLIC_SUPABASE_ANON_KEY=
4 |
5 | # Supabase Private
6 | SUPABASE_SERVICE_ROLE_KEY=
7 |
8 | # Ollama
9 | NEXT_PUBLIC_OLLAMA_URL=http://localhost:11434
10 |
11 | # API Keys (Optional: Entering an API key here overrides the API keys globally for all users.)
12 | OPENAI_API_KEY=
13 | ANTHROPIC_API_KEY=
14 | GOOGLE_GEMINI_API_KEY=
15 | MISTRAL_API_KEY=
16 | GROQ_API_KEY=
17 | PERPLEXITY_API_KEY=
18 | OPENROUTER_API_KEY=
19 |
20 | # OpenAI API Information
21 | NEXT_PUBLIC_OPENAI_ORGANIZATION_ID=
22 |
23 | # Azure API Information
24 | AZURE_OPENAI_API_KEY=
25 | AZURE_OPENAI_ENDPOINT=
26 | AZURE_GPT_35_TURBO_NAME=
27 | AZURE_GPT_45_VISION_NAME=
28 | AZURE_GPT_45_TURBO_NAME=
29 | AZURE_EMBEDDINGS_NAME=
30 |
31 | # General Configuration (Optional)
32 | EMAIL_DOMAIN_WHITELIST=
33 | EMAIL_WHITELIST=
34 |
35 | # File size limit for uploads in bytes
36 | NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=10485760
--------------------------------------------------------------------------------
/nyro-chatbot/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/eslintrc",
3 | "root": true,
4 | "extends": [
5 | "next/core-web-vitals",
6 | "prettier",
7 | "plugin:tailwindcss/recommended"
8 | ],
9 | "plugins": ["tailwindcss"],
10 | "rules": {
11 | "tailwindcss/no-custom-classname": "off"
12 | },
13 | "settings": {
14 | "tailwindcss": {
15 | "callees": ["cn", "cva"],
16 | "config": "tailwind.config.js"
17 | }
18 | },
19 | "overrides": [
20 | {
21 | "files": ["*.ts", "*.tsx"],
22 | "parser": "@typescript-eslint/parser"
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/nyro-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
30 | .env*.local
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
39 | .VSCodeCounter
40 | tool-schemas
41 | custom-prompts
42 |
43 | sw.js
44 | sw.js.map
45 | workbox-*.js
46 | workbox-*.js.map
--------------------------------------------------------------------------------
/nyro-chatbot/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | . "$(dirname -- "$0")/_/husky.sh"
4 |
5 | npm run lint:fix && npm run format:write && git add .
6 |
--------------------------------------------------------------------------------
/nyro-chatbot/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.11.0
2 |
--------------------------------------------------------------------------------
/nyro-chatbot/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/nyro-chatbot/__tests__/playwright-test/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | /test-results/
3 | /playwright-report/
4 | /blob-report/
5 | /playwright/.cache/
6 |
--------------------------------------------------------------------------------
/nyro-chatbot/__tests__/playwright-test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playwright-test",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "integration": "playwright test",
8 | "integration:open": "playwright test --ui",
9 | "integration:codegen": "playwright codegen"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "@playwright/test": "^1.41.2",
16 | "@types/node": "^20.11.20"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/nyro-chatbot/__tests__/playwright-test/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, devices } from '@playwright/test';
2 |
3 | /**
4 | * Read environment variables from file.
5 | * https://github.com/motdotla/dotenv
6 | */
7 | // require('dotenv').config();
8 |
9 | /**
10 | * See https://playwright.dev/docs/test-configuration.
11 | */
12 | export default defineConfig({
13 | testDir: './tests',
14 | /* Run tests in files in parallel */
15 | fullyParallel: true,
16 | /* Fail the build on CI if you accidentally left test.only in the source code. */
17 | forbidOnly: !!process.env.CI,
18 | /* Retry on CI only */
19 | retries: process.env.CI ? 2 : 0,
20 | /* Opt out of parallel tests on CI. */
21 | workers: process.env.CI ? 1 : undefined,
22 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */
23 | reporter: 'html',
24 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
25 | use: {
26 | /* Base URL to use in actions like `await page.goto('/')`. */
27 | // baseURL: 'http://127.0.0.1:3000',
28 |
29 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
30 | trace: 'on-first-retry',
31 | },
32 |
33 | /* Configure projects for major browsers */
34 | projects: [
35 | {
36 | name: 'chromium',
37 | use: { ...devices['Desktop Chrome'] },
38 | },
39 |
40 | {
41 | name: 'firefox',
42 | use: { ...devices['Desktop Firefox'] },
43 | },
44 |
45 | {
46 | name: 'webkit',
47 | use: { ...devices['Desktop Safari'] },
48 | },
49 |
50 | /* Test against mobile viewports. */
51 | // {
52 | // name: 'Mobile Chrome',
53 | // use: { ...devices['Pixel 5'] },
54 | // },
55 | // {
56 | // name: 'Mobile Safari',
57 | // use: { ...devices['iPhone 12'] },
58 | // },
59 |
60 | /* Test against branded browsers. */
61 | // {
62 | // name: 'Microsoft Edge',
63 | // use: { ...devices['Desktop Edge'], channel: 'msedge' },
64 | // },
65 | // {
66 | // name: 'Google Chrome',
67 | // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
68 | // },
69 | ],
70 |
71 | /* Run your local dev server before starting the tests */
72 | // webServer: {
73 | // command: 'npm run start',
74 | // url: 'http://127.0.0.1:3000',
75 | // reuseExistingServer: !process.env.CI,
76 | // },
77 | });
78 |
--------------------------------------------------------------------------------
/nyro-chatbot/__tests__/playwright-test/tests/login.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('start chatting is displayed', async ({ page }) => {
4 | await page.goto('http://localhost:3000/');
5 |
6 | //expect the start chatting link to be visible
7 | await expect (page.getByRole('link', { name: 'Start Chatting' })).toBeVisible();
8 | });
9 |
10 | test('No password error message', async ({ page }) => {
11 | await page.goto('http://localhost:3000/login');
12 | //fill in dummy email
13 | await page.getByPlaceholder('you@example.com').fill('dummyemail@gmail.com');
14 | await page.getByRole('button', { name: 'Login' }).click();
15 | //wait for netwrok to be idle
16 | await page.waitForLoadState('networkidle');
17 | //validate that correct message is shown to the user
18 | await expect(page.getByText('Invalid login credentials')).toBeVisible();
19 |
20 | });
21 | test('No password for signup', async ({ page }) => {
22 | await page.goto('http://localhost:3000/login');
23 |
24 | await page.getByPlaceholder('you@example.com').fill('dummyEmail@Gmail.com');
25 | await page.getByRole('button', { name: 'Sign Up' }).click();
26 | //validate appropriate error is thrown for missing password when signing up
27 | await expect(page.getByText('Signup requires a valid')).toBeVisible();
28 | });
29 | test('invalid username for signup', async ({ page }) => {
30 | await page.goto('http://localhost:3000/login');
31 |
32 | await page.getByPlaceholder('you@example.com').fill('dummyEmail');
33 | await page.getByPlaceholder('••••••••').fill('dummypassword');
34 | await page.getByRole('button', { name: 'Sign Up' }).click();
35 | //validate appropriate error is thrown for invalid username when signing up
36 | await expect(page.getByText('Unable to validate email')).toBeVisible();
37 | });
38 | test('password reset message', async ({ page }) => {
39 | await page.goto('http://localhost:3000/login');
40 | await page.getByPlaceholder('you@example.com').fill('demo@gmail.com');
41 | await page.getByRole('button', { name: 'Reset' }).click();
42 | //validate appropriate message is shown
43 | await expect(page.getByText('Check email to reset password')).toBeVisible();
44 | });
45 |
46 | //more tests can be added here
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/[workspaceid]/chat/[chatid]/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { ChatUI } from "@/components/chat/chat-ui"
4 |
5 | export default function ChatIDPage() {
6 | return
7 | }
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/[workspaceid]/chat/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { ChatHelp } from "@/components/chat/chat-help"
4 | import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
5 | import { ChatInput } from "@/components/chat/chat-input"
6 | import { ChatSettings } from "@/components/chat/chat-settings"
7 | import { ChatUI } from "@/components/chat/chat-ui"
8 | import { QuickSettings } from "@/components/chat/quick-settings"
9 | import { Brand } from "@/components/ui/brand"
10 | import { NyroContext } from "@/context/context"
11 | import useHotkey from "@/lib/hooks/use-hotkey"
12 | import { useTheme } from "next-themes"
13 | import { useContext } from "react"
14 |
15 | export default function ChatPage() {
16 | useHotkey("o", () => handleNewChat())
17 | useHotkey("l", () => {
18 | handleFocusChatInput()
19 | })
20 |
21 | const { chatMessages, handleInputBlur, handleInputFocus } = useContext(NyroContext)
22 |
23 | const { handleNewChat, handleFocusChatInput } = useChatHandler()
24 |
25 | const { theme } = useTheme()
26 |
27 | return (
28 | <>
29 | {chatMessages.length === 0 ? (
30 |
31 | {/*
32 |
33 |
*/}
34 |
35 | {/*
36 |
37 |
*/}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ) : (
54 |
55 | )}
56 | >
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/[workspaceid]/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { NyroContext } from "@/context/context"
4 | import { useContext } from "react"
5 |
6 | export default function WorkspacePage() {
7 | const { selectedWorkspace } = useContext(NyroContext)
8 |
9 | return (
10 |
11 |
{selectedWorkspace?.name}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | ::-webkit-scrollbar-track {
6 | background-color: transparent;
7 | }
8 |
9 | ::-webkit-scrollbar-thumb {
10 | background-color: #ccc;
11 | border-radius: 10px;
12 | }
13 |
14 | ::-webkit-scrollbar-thumb:hover {
15 | background-color: #aaa;
16 | }
17 |
18 | ::-webkit-scrollbar-track:hover {
19 | background-color: #f2f2f2;
20 | }
21 |
22 | ::-webkit-scrollbar-corner {
23 | background-color: transparent;
24 | }
25 |
26 | ::-webkit-scrollbar {
27 | width: 6px;
28 | height: 6px;
29 | }
30 |
31 | @layer base {
32 | :root {
33 | --background: 0 0% 100%;
34 | --foreground: 0 0% 3.9%;
35 |
36 | --muted: 0 0% 96.1%;
37 | --muted-foreground: 0 0% 45.1%;
38 |
39 | --popover: 0 0% 100%;
40 | --popover-foreground: 0 0% 3.9%;
41 |
42 | --card: 0 0% 100%;
43 | --card-foreground: 0 0% 3.9%;
44 |
45 | --border: 0 0% 89.8%;
46 | --input: 0 0% 89.8%;
47 |
48 | --primary: 0 0% 9%;
49 | --primary-foreground: 0 0% 98%;
50 |
51 | --secondary: 0 0% 96.1%;
52 | --secondary-foreground: 0 0% 9%;
53 |
54 | --accent: 0 0% 96.1%;
55 | --accent-foreground: 0 0% 9%;
56 |
57 | --destructive: 0 84.2% 60.2%;
58 | --destructive-foreground: 0 0% 98%;
59 |
60 | --ring: 0 0% 63.9%;
61 |
62 | --radius: 0.5rem;
63 | }
64 |
65 | .dark {
66 | --background: 0 0% 3.9%;
67 | --foreground: 0 0% 98%;
68 |
69 | --muted: 0 0% 14.9%;
70 | --muted-foreground: 0 0% 63.9%;
71 |
72 | --popover: 0 0% 3.9%;
73 | --popover-foreground: 0 0% 98%;
74 |
75 | --card: 0 0% 3.9%;
76 | --card-foreground: 0 0% 98%;
77 |
78 | --border: 0 0% 14.9%;
79 | --input: 0 0% 14.9%;
80 |
81 | --primary: 0 0% 98%;
82 | --primary-foreground: 0 0% 9%;
83 |
84 | --secondary: 0 0% 14.9%;
85 | --secondary-foreground: 0 0% 98%;
86 |
87 | --accent: 0 0% 14.9%;
88 | --accent-foreground: 0 0% 98%;
89 |
90 | --destructive: 0 62.8% 30.6%;
91 | --destructive-foreground: 0 85.7% 97.3%;
92 |
93 | --ring: 0 0% 14.9%;
94 | }
95 | }
96 |
97 | @layer base {
98 | * {
99 | @apply border-border;
100 | }
101 | body {
102 | @apply text-foreground;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/help/page.tsx:
--------------------------------------------------------------------------------
1 | export default function HelpPage() {
2 | return (
3 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/loading.tsx:
--------------------------------------------------------------------------------
1 | import { IconLoader2 } from "@tabler/icons-react"
2 |
3 | export default function Loading() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/login/password/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { ChangePassword } from "@/components/utility/change-password"
4 | import { supabase } from "@/lib/supabase/browser-client"
5 | import { useRouter } from "next/navigation"
6 | import { useEffect, useState } from "react"
7 |
8 | export default function ChangePasswordPage() {
9 | const [loading, setLoading] = useState(true)
10 |
11 | const router = useRouter()
12 |
13 | useEffect(() => {
14 | ;(async () => {
15 | const session = (await supabase.auth.getSession()).data.session
16 |
17 | if (!session) {
18 | router.push("/login")
19 | } else {
20 | setLoading(false)
21 | }
22 | })()
23 | }, [])
24 |
25 | if (loading) {
26 | return null
27 | }
28 |
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/[locale]/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { NyroSVG } from "@/components/icons/nyro-svg"
4 | import { IconArrowRight } from "@tabler/icons-react"
5 | import { useTheme } from "next-themes"
6 | import Link from "next/link"
7 |
8 | export default function HomePage() {
9 | const { theme } = useTheme()
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 | {/*
Nyro
*/}
18 |
19 |
23 | Start Chatting
24 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/assistants/openai/route.ts:
--------------------------------------------------------------------------------
1 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
2 | import { ServerRuntime } from "next"
3 | import OpenAI from "openai"
4 |
5 | export const runtime: ServerRuntime = "edge"
6 |
7 | export async function GET() {
8 | try {
9 | const profile = await getServerProfile()
10 |
11 | checkApiKey(profile.openai_api_key, "OpenAI")
12 |
13 | const openai = new OpenAI({
14 | apiKey: profile.openai_api_key || "",
15 | organization: profile.openai_organization_id
16 | })
17 |
18 | const myAssistants = await openai.beta.assistants.list({
19 | limit: 100
20 | })
21 |
22 | return new Response(JSON.stringify({ assistants: myAssistants.data }), {
23 | status: 200
24 | })
25 | } catch (error: any) {
26 | const errorMessage = error.error?.message || "An unexpected error occurred"
27 | const errorCode = error.status || 500
28 | return new Response(JSON.stringify({ message: errorMessage }), {
29 | status: errorCode
30 | })
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/custom/route.ts:
--------------------------------------------------------------------------------
1 | import { Database } from "@/supabase/types"
2 | import { ChatSettings } from "@/types"
3 | import { createClient } from "@supabase/supabase-js"
4 | import { OpenAIStream, StreamingTextResponse } from "ai"
5 | import { ServerRuntime } from "next"
6 | import OpenAI from "openai"
7 | import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
8 |
9 | export const runtime: ServerRuntime = "edge"
10 |
11 | export async function POST(request: Request) {
12 | const json = await request.json()
13 | const { chatSettings, messages, customModelId } = json as {
14 | chatSettings: ChatSettings
15 | messages: any[]
16 | customModelId: string
17 | }
18 |
19 | try {
20 | const supabaseAdmin = createClient(
21 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
22 | process.env.SUPABASE_SERVICE_ROLE_KEY!
23 | )
24 |
25 | const { data: customModel, error } = await supabaseAdmin
26 | .from("models")
27 | .select("*")
28 | .eq("id", customModelId)
29 | .single()
30 |
31 | if (!customModel) {
32 | throw new Error(error.message)
33 | }
34 |
35 | const custom = new OpenAI({
36 | apiKey: customModel.api_key || "",
37 | baseURL: customModel.base_url
38 | })
39 |
40 | const response = await custom.chat.completions.create({
41 | model: chatSettings.model as ChatCompletionCreateParamsBase["model"],
42 | messages: messages as ChatCompletionCreateParamsBase["messages"],
43 | temperature: chatSettings.temperature,
44 | stream: true
45 | })
46 |
47 | const stream = OpenAIStream(response)
48 |
49 | return new StreamingTextResponse(stream)
50 | } catch (error: any) {
51 | let errorMessage = error.message || "An unexpected error occurred"
52 | const errorCode = error.status || 500
53 |
54 | if (errorMessage.toLowerCase().includes("api key not found")) {
55 | errorMessage =
56 | "Custom API Key not found. Please set it in your profile settings."
57 | } else if (errorMessage.toLowerCase().includes("incorrect api key")) {
58 | errorMessage =
59 | "Custom API Key is incorrect. Please fix it in your profile settings."
60 | }
61 |
62 | return new Response(JSON.stringify({ message: errorMessage }), {
63 | status: errorCode
64 | })
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/google/route.ts:
--------------------------------------------------------------------------------
1 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
2 | import { ChatSettings } from "@/types"
3 | import { GoogleGenerativeAI } from "@google/generative-ai"
4 |
5 | export const runtime = "edge"
6 |
7 | export async function POST(request: Request) {
8 | const json = await request.json()
9 | const { chatSettings, messages } = json as {
10 | chatSettings: ChatSettings
11 | messages: any[]
12 | }
13 |
14 | try {
15 | const profile = await getServerProfile()
16 |
17 | checkApiKey(profile.google_gemini_api_key, "Google")
18 |
19 | const genAI = new GoogleGenerativeAI(profile.google_gemini_api_key || "")
20 | const googleModel = genAI.getGenerativeModel({ model: chatSettings.model })
21 |
22 | const lastMessage = messages.pop()
23 |
24 | const chat = googleModel.startChat({
25 | history: messages,
26 | generationConfig: {
27 | temperature: chatSettings.temperature
28 | }
29 | })
30 |
31 | const response = await chat.sendMessageStream(lastMessage.parts)
32 |
33 | const encoder = new TextEncoder()
34 | const readableStream = new ReadableStream({
35 | async start(controller) {
36 | for await (const chunk of response.stream) {
37 | const chunkText = chunk.text()
38 | controller.enqueue(encoder.encode(chunkText))
39 | }
40 | controller.close()
41 | }
42 | })
43 |
44 | return new Response(readableStream, {
45 | headers: { "Content-Type": "text/plain" }
46 | })
47 |
48 | } catch (error: any) {
49 | let errorMessage = error.message || "An unexpected error occurred"
50 | const errorCode = error.status || 500
51 |
52 | if (errorMessage.toLowerCase().includes("api key not found")) {
53 | errorMessage =
54 | "Google Gemini API Key not found. Please set it in your profile settings."
55 | } else if (errorMessage.toLowerCase().includes("api key not valid")) {
56 | errorMessage =
57 | "Google Gemini API Key is incorrect. Please fix it in your profile settings."
58 | }
59 |
60 | return new Response(JSON.stringify({ message: errorMessage }), {
61 | status: errorCode
62 | })
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/groq/route.ts:
--------------------------------------------------------------------------------
1 | import { CHAT_SETTING_LIMITS } from "@/lib/chat-setting-limits"
2 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
3 | import { ChatSettings } from "@/types"
4 | import { OpenAIStream, StreamingTextResponse } from "ai"
5 | import OpenAI from "openai"
6 |
7 | export const runtime = "edge"
8 | export async function POST(request: Request) {
9 | const json = await request.json()
10 | const { chatSettings, messages } = json as {
11 | chatSettings: ChatSettings
12 | messages: any[]
13 | }
14 |
15 | try {
16 | const profile = await getServerProfile()
17 |
18 | checkApiKey(profile.groq_api_key, "G")
19 |
20 | // Groq is compatible with the OpenAI SDK
21 | const groq = new OpenAI({
22 | apiKey: profile.groq_api_key || "",
23 | baseURL: "https://api.groq.com/openai/v1"
24 | })
25 |
26 | const response = await groq.chat.completions.create({
27 | model: chatSettings.model,
28 | messages,
29 | max_tokens:
30 | CHAT_SETTING_LIMITS[chatSettings.model].MAX_TOKEN_OUTPUT_LENGTH,
31 | stream: true
32 | })
33 |
34 | // Convert the response into a friendly text-stream.
35 | const stream = OpenAIStream(response)
36 |
37 | // Respond with the stream
38 | return new StreamingTextResponse(stream)
39 | } catch (error: any) {
40 | let errorMessage = error.message || "An unexpected error occurred"
41 | const errorCode = error.status || 500
42 |
43 | if (errorMessage.toLowerCase().includes("api key not found")) {
44 | errorMessage =
45 | "Groq API Key not found. Please set it in your profile settings."
46 | } else if (errorCode === 401) {
47 | errorMessage =
48 | "Groq API Key is incorrect. Please fix it in your profile settings."
49 | }
50 |
51 | return new Response(JSON.stringify({ message: errorMessage }), {
52 | status: errorCode
53 | })
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/mistral/route.ts:
--------------------------------------------------------------------------------
1 | import { CHAT_SETTING_LIMITS } from "@/lib/chat-setting-limits"
2 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
3 | import { ChatSettings } from "@/types"
4 | import { OpenAIStream, StreamingTextResponse } from "ai"
5 | import OpenAI from "openai"
6 |
7 | export const runtime = "edge"
8 |
9 | export async function POST(request: Request) {
10 | const json = await request.json()
11 | const { chatSettings, messages } = json as {
12 | chatSettings: ChatSettings
13 | messages: any[]
14 | }
15 |
16 | try {
17 | const profile = await getServerProfile()
18 |
19 | checkApiKey(profile.mistral_api_key, "Mistral")
20 |
21 | // Mistral is compatible the OpenAI SDK
22 | const mistral = new OpenAI({
23 | apiKey: profile.mistral_api_key || "",
24 | baseURL: "https://api.mistral.ai/v1"
25 | })
26 |
27 | const response = await mistral.chat.completions.create({
28 | model: chatSettings.model,
29 | messages,
30 | max_tokens:
31 | CHAT_SETTING_LIMITS[chatSettings.model].MAX_TOKEN_OUTPUT_LENGTH,
32 | stream: true
33 | })
34 |
35 | // Convert the response into a friendly text-stream.
36 | const stream = OpenAIStream(response)
37 |
38 | // Respond with the stream
39 | return new StreamingTextResponse(stream)
40 | } catch (error: any) {
41 | let errorMessage = error.message || "An unexpected error occurred"
42 | const errorCode = error.status || 500
43 |
44 | if (errorMessage.toLowerCase().includes("api key not found")) {
45 | errorMessage =
46 | "Mistral API Key not found. Please set it in your profile settings."
47 | } else if (errorCode === 401) {
48 | errorMessage =
49 | "Mistral API Key is incorrect. Please fix it in your profile settings."
50 | }
51 |
52 | return new Response(JSON.stringify({ message: errorMessage }), {
53 | status: errorCode
54 | })
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/openai/route.ts:
--------------------------------------------------------------------------------
1 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
2 | import { ChatSettings } from "@/types"
3 | import { OpenAIStream, StreamingTextResponse } from "ai"
4 | import { ServerRuntime } from "next"
5 | import OpenAI from "openai"
6 | import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
7 |
8 | export const runtime: ServerRuntime = "edge"
9 |
10 | export async function POST(request: Request) {
11 | const json = await request.json()
12 | const { chatSettings, messages } = json as {
13 | chatSettings: ChatSettings
14 | messages: any[]
15 | }
16 |
17 | try {
18 | const profile = await getServerProfile()
19 |
20 | checkApiKey(profile.openai_api_key, "OpenAI")
21 |
22 | const openai = new OpenAI({
23 | apiKey: profile.openai_api_key || "",
24 | organization: profile.openai_organization_id
25 | })
26 |
27 | const response = await openai.chat.completions.create({
28 | model: chatSettings.model as ChatCompletionCreateParamsBase["model"],
29 | messages: messages as ChatCompletionCreateParamsBase["messages"],
30 | temperature: chatSettings.temperature,
31 | max_tokens:
32 | chatSettings.model === "gpt-4-vision-preview" ||
33 | chatSettings.model === "gpt-4o"
34 | ? 4096
35 | : 4096, // TODO: Fix
36 | stream: true
37 | })
38 |
39 | const stream = OpenAIStream(response)
40 |
41 | return new StreamingTextResponse(stream)
42 | } catch (error: any) {
43 | let errorMessage = error.message || "An unexpected error occurred"
44 | const errorCode = error.status || 500
45 |
46 | if (errorMessage.toLowerCase().includes("api key not found")) {
47 | errorMessage =
48 | "OpenAI API Key not found. Please set it in your profile settings."
49 | } else if (errorMessage.toLowerCase().includes("incorrect api key")) {
50 | errorMessage =
51 | "OpenAI API Key is incorrect. Please fix it in your profile settings."
52 | }
53 |
54 | return new Response(JSON.stringify({ message: errorMessage }), {
55 | status: errorCode
56 | })
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/openrouter/route.ts:
--------------------------------------------------------------------------------
1 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
2 | import { ChatSettings } from "@/types"
3 | import { OpenAIStream, StreamingTextResponse } from "ai"
4 | import { ServerRuntime } from "next"
5 | import OpenAI from "openai"
6 | import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
7 |
8 | export const runtime: ServerRuntime = "edge"
9 |
10 | export async function POST(request: Request) {
11 | const json = await request.json()
12 | const { chatSettings, messages } = json as {
13 | chatSettings: ChatSettings
14 | messages: any[]
15 | }
16 |
17 | try {
18 | const profile = await getServerProfile()
19 |
20 | checkApiKey(profile.openrouter_api_key, "OpenRouter")
21 |
22 | const openai = new OpenAI({
23 | apiKey: profile.openrouter_api_key || "",
24 | baseURL: "https://openrouter.ai/api/v1"
25 | })
26 |
27 | const response = await openai.chat.completions.create({
28 | model: chatSettings.model as ChatCompletionCreateParamsBase["model"],
29 | messages: messages as ChatCompletionCreateParamsBase["messages"],
30 | temperature: chatSettings.temperature,
31 | max_tokens: undefined,
32 | stream: true
33 | })
34 |
35 | const stream = OpenAIStream(response)
36 |
37 | return new StreamingTextResponse(stream)
38 | } catch (error: any) {
39 | let errorMessage = error.message || "An unexpected error occurred"
40 | const errorCode = error.status || 500
41 |
42 | if (errorMessage.toLowerCase().includes("api key not found")) {
43 | errorMessage =
44 | "OpenRouter API Key not found. Please set it in your profile settings."
45 | }
46 |
47 | return new Response(JSON.stringify({ message: errorMessage }), {
48 | status: errorCode
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/chat/perplexity/route.ts:
--------------------------------------------------------------------------------
1 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
2 | import { ChatSettings } from "@/types"
3 | import { OpenAIStream, StreamingTextResponse } from "ai"
4 | import OpenAI from "openai"
5 |
6 | export const runtime = "edge"
7 |
8 | export async function POST(request: Request) {
9 | const json = await request.json()
10 | const { chatSettings, messages } = json as {
11 | chatSettings: ChatSettings
12 | messages: any[]
13 | }
14 |
15 | try {
16 | const profile = await getServerProfile()
17 |
18 | checkApiKey(profile.perplexity_api_key, "Perplexity")
19 |
20 | // Perplexity is compatible the OpenAI SDK
21 | const perplexity = new OpenAI({
22 | apiKey: profile.perplexity_api_key || "",
23 | baseURL: "https://api.perplexity.ai/"
24 | })
25 |
26 | const response = await perplexity.chat.completions.create({
27 | model: chatSettings.model,
28 | messages,
29 | stream: true
30 | })
31 |
32 | const stream = OpenAIStream(response)
33 |
34 | return new StreamingTextResponse(stream)
35 | } catch (error: any) {
36 | let errorMessage = error.message || "An unexpected error occurred"
37 | const errorCode = error.status || 500
38 |
39 | if (errorMessage.toLowerCase().includes("api key not found")) {
40 | errorMessage =
41 | "Perplexity API Key not found. Please set it in your profile settings."
42 | } else if (errorCode === 401) {
43 | errorMessage =
44 | "Perplexity API Key is incorrect. Please fix it in your profile settings."
45 | }
46 |
47 | return new Response(JSON.stringify({ message: errorMessage }), {
48 | status: errorCode
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/command/route.ts:
--------------------------------------------------------------------------------
1 | import { CHAT_SETTING_LIMITS } from "@/lib/chat-setting-limits"
2 | import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
3 | import OpenAI from "openai"
4 |
5 | export const runtime = "edge"
6 |
7 | export async function POST(request: Request) {
8 | const json = await request.json()
9 | const { input } = json as {
10 | input: string
11 | }
12 |
13 | try {
14 | const profile = await getServerProfile()
15 |
16 | checkApiKey(profile.openai_api_key, "OpenAI")
17 |
18 | const openai = new OpenAI({
19 | apiKey: profile.openai_api_key || "",
20 | organization: profile.openai_organization_id
21 | })
22 |
23 | const response = await openai.chat.completions.create({
24 | model: "gpt-4-1106-preview",
25 | messages: [
26 | {
27 | role: "system",
28 | content: "Respond to the user."
29 | },
30 | {
31 | role: "user",
32 | content: input
33 | }
34 | ],
35 | temperature: 0,
36 | max_tokens:
37 | CHAT_SETTING_LIMITS["gpt-4-turbo-preview"].MAX_TOKEN_OUTPUT_LENGTH
38 | // response_format: { type: "json_object" }
39 | // stream: true
40 | })
41 |
42 | const content = response.choices[0].message.content
43 |
44 | return new Response(JSON.stringify({ content }), {
45 | status: 200
46 | })
47 | } catch (error: any) {
48 | const errorMessage = error.error?.message || "An unexpected error occurred"
49 | const errorCode = error.status || 500
50 | return new Response(JSON.stringify({ message: errorMessage }), {
51 | status: errorCode
52 | })
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/keys/route.ts:
--------------------------------------------------------------------------------
1 | import { isUsingEnvironmentKey } from "@/lib/envs"
2 | import { createResponse } from "@/lib/server/server-utils"
3 | import { EnvKey } from "@/types/key-type"
4 | import { VALID_ENV_KEYS } from "@/types/valid-keys"
5 |
6 | export async function GET() {
7 | const envKeyMap: Record = {
8 | azure: VALID_ENV_KEYS.AZURE_OPENAI_API_KEY,
9 | openai: VALID_ENV_KEYS.OPENAI_API_KEY,
10 | google: VALID_ENV_KEYS.GOOGLE_GEMINI_API_KEY,
11 | anthropic: VALID_ENV_KEYS.ANTHROPIC_API_KEY,
12 | mistral: VALID_ENV_KEYS.MISTRAL_API_KEY,
13 | groq: VALID_ENV_KEYS.GROQ_API_KEY,
14 | perplexity: VALID_ENV_KEYS.PERPLEXITY_API_KEY,
15 | openrouter: VALID_ENV_KEYS.OPENROUTER_API_KEY,
16 |
17 | openai_organization_id: VALID_ENV_KEYS.OPENAI_ORGANIZATION_ID,
18 |
19 | azure_openai_endpoint: VALID_ENV_KEYS.AZURE_OPENAI_ENDPOINT,
20 | azure_gpt_35_turbo_name: VALID_ENV_KEYS.AZURE_GPT_35_TURBO_NAME,
21 | azure_gpt_45_vision_name: VALID_ENV_KEYS.AZURE_GPT_45_VISION_NAME,
22 | azure_gpt_45_turbo_name: VALID_ENV_KEYS.AZURE_GPT_45_TURBO_NAME,
23 | azure_embeddings_name: VALID_ENV_KEYS.AZURE_EMBEDDINGS_NAME
24 | }
25 |
26 | const isUsingEnvKeyMap = Object.keys(envKeyMap).reduce<
27 | Record
28 | >((acc, provider) => {
29 | const key = envKeyMap[provider]
30 |
31 | if (key) {
32 | acc[provider] = isUsingEnvironmentKey(key as EnvKey)
33 | }
34 | return acc
35 | }, {})
36 |
37 | return createResponse({ isUsingEnvKeyMap }, 200)
38 | }
39 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/username/available/route.ts:
--------------------------------------------------------------------------------
1 | import { Database } from "@/supabase/types"
2 | import { createClient } from "@supabase/supabase-js"
3 |
4 | export const runtime = "edge"
5 |
6 | export async function POST(request: Request) {
7 | const json = await request.json()
8 | const { username } = json as {
9 | username: string
10 | }
11 |
12 | try {
13 | const supabaseAdmin = createClient(
14 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
15 | process.env.SUPABASE_SERVICE_ROLE_KEY!
16 | )
17 |
18 | const { data: usernames, error } = await supabaseAdmin
19 | .from("profiles")
20 | .select("username")
21 | .eq("username", username)
22 |
23 | if (!usernames) {
24 | throw new Error(error.message)
25 | }
26 |
27 | return new Response(JSON.stringify({ isAvailable: !usernames.length }), {
28 | status: 200
29 | })
30 | } catch (error: any) {
31 | const errorMessage = error.error?.message || "An unexpected error occurred"
32 | const errorCode = error.status || 500
33 | return new Response(JSON.stringify({ message: errorMessage }), {
34 | status: errorCode
35 | })
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/api/username/get/route.ts:
--------------------------------------------------------------------------------
1 | import { Database } from "@/supabase/types"
2 | import { createClient } from "@supabase/supabase-js"
3 |
4 | export const runtime = "edge"
5 |
6 | export async function POST(request: Request) {
7 | const json = await request.json()
8 | const { userId } = json as {
9 | userId: string
10 | }
11 |
12 | try {
13 | const supabaseAdmin = createClient(
14 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
15 | process.env.SUPABASE_SERVICE_ROLE_KEY!
16 | )
17 |
18 | const { data, error } = await supabaseAdmin
19 | .from("profiles")
20 | .select("username")
21 | .eq("user_id", userId)
22 | .single()
23 |
24 | if (!data) {
25 | throw new Error(error.message)
26 | }
27 |
28 | return new Response(JSON.stringify({ username: data.username }), {
29 | status: 200
30 | })
31 | } catch (error: any) {
32 | const errorMessage = error.error?.message || "An unexpected error occurred"
33 | const errorCode = error.status || 500
34 | return new Response(JSON.stringify({ message: errorMessage }), {
35 | status: errorCode
36 | })
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/nyro-chatbot/app/auth/callback/route.ts:
--------------------------------------------------------------------------------
1 | import { createClient } from "@/lib/supabase/server"
2 | import { cookies } from "next/headers"
3 | import { NextResponse } from "next/server"
4 |
5 | export async function GET(request: Request) {
6 | const requestUrl = new URL(request.url)
7 | const code = requestUrl.searchParams.get("code")
8 | const next = requestUrl.searchParams.get("next")
9 |
10 | if (code) {
11 | const cookieStore = cookies()
12 | const supabase = createClient(cookieStore)
13 | await supabase.auth.exchangeCodeForSession(code)
14 | }
15 |
16 | if (next) {
17 | return NextResponse.redirect(requestUrl.origin + next)
18 | } else {
19 | return NextResponse.redirect(requestUrl.origin)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/nyro-chatbot/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "gray",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/chat-command-input.tsx:
--------------------------------------------------------------------------------
1 | import { NyroContext } from "@/context/context"
2 | import { FC, useContext } from "react"
3 | import { AssistantPicker } from "./assistant-picker"
4 | import { usePromptAndCommand } from "./chat-hooks/use-prompt-and-command"
5 | import { FilePicker } from "./file-picker"
6 | import { PromptPicker } from "./prompt-picker"
7 | import { ToolPicker } from "./tool-picker"
8 |
9 | interface ChatCommandInputProps {}
10 |
11 | export const ChatCommandInput: FC = ({}) => {
12 | const {
13 | newMessageFiles,
14 | chatFiles,
15 | slashCommand,
16 | isFilePickerOpen,
17 | setIsFilePickerOpen,
18 | hashtagCommand,
19 | focusPrompt,
20 | focusFile
21 | } = useContext(NyroContext)
22 |
23 | const { handleSelectUserFile, handleSelectUserCollection } =
24 | usePromptAndCommand()
25 |
26 | return (
27 | <>
28 |
29 |
30 | file.id
36 | )}
37 | selectedCollectionIds={[]}
38 | onSelectFile={handleSelectUserFile}
39 | onSelectCollection={handleSelectUserCollection}
40 | isFocused={focusFile}
41 | />
42 |
43 |
44 |
45 |
46 | >
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/chat-hooks/use-scroll.tsx:
--------------------------------------------------------------------------------
1 | import { NyroContext } from "@/context/context"
2 | import {
3 | type UIEventHandler,
4 | useCallback,
5 | useContext,
6 | useEffect,
7 | useRef,
8 | useState
9 | } from "react"
10 |
11 | export const useScroll = () => {
12 | const { isGenerating, chatMessages } = useContext(NyroContext)
13 |
14 | const messagesStartRef = useRef(null)
15 | const messagesEndRef = useRef(null)
16 | const isAutoScrolling = useRef(false)
17 |
18 | const [isAtTop, setIsAtTop] = useState(false)
19 | const [isAtBottom, setIsAtBottom] = useState(true)
20 | const [userScrolled, setUserScrolled] = useState(false)
21 | const [isOverflowing, setIsOverflowing] = useState(false)
22 |
23 | useEffect(() => {
24 | setUserScrolled(false)
25 |
26 | if (!isGenerating && userScrolled) {
27 | setUserScrolled(false)
28 | }
29 | }, [isGenerating])
30 |
31 | useEffect(() => {
32 | if (isGenerating && !userScrolled) {
33 | scrollToBottom()
34 | }
35 | }, [chatMessages])
36 |
37 | const handleScroll: UIEventHandler = useCallback(e => {
38 | const target = e.target as HTMLDivElement
39 | const bottom =
40 | Math.round(target.scrollHeight) - Math.round(target.scrollTop) ===
41 | Math.round(target.clientHeight)
42 | setIsAtBottom(bottom)
43 |
44 | const top = target.scrollTop === 0
45 | setIsAtTop(top)
46 |
47 | if (!bottom && !isAutoScrolling.current) {
48 | setUserScrolled(true)
49 | } else {
50 | setUserScrolled(false)
51 | }
52 |
53 | const isOverflow = target.scrollHeight > target.clientHeight
54 | setIsOverflowing(isOverflow)
55 | }, [])
56 |
57 | const scrollToTop = useCallback(() => {
58 | if (messagesStartRef.current) {
59 | messagesStartRef.current.scrollIntoView({ behavior: "instant" })
60 | }
61 | }, [])
62 |
63 | const scrollToBottom = useCallback(() => {
64 | isAutoScrolling.current = true
65 |
66 | setTimeout(() => {
67 | if (messagesEndRef.current) {
68 | messagesEndRef.current.scrollIntoView({ behavior: "instant" })
69 | }
70 |
71 | isAutoScrolling.current = false
72 | }, 100)
73 | }, [])
74 |
75 | return {
76 | messagesStartRef,
77 | messagesEndRef,
78 | isAtTop,
79 | isAtBottom,
80 | userScrolled,
81 | isOverflowing,
82 | handleScroll,
83 | scrollToTop,
84 | scrollToBottom,
85 | setIsAtBottom
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/chat-messages.tsx:
--------------------------------------------------------------------------------
1 | import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
2 | import { NyroContext } from "@/context/context"
3 | import { Tables } from "@/supabase/types"
4 | import { FC, useContext, useState } from "react"
5 | import { Message } from "../messages/message"
6 |
7 | interface ChatMessagesProps {}
8 |
9 | export const ChatMessages: FC = ({}) => {
10 | const { chatMessages, chatFileItems } = useContext(NyroContext)
11 |
12 | const { handleSendEdit } = useChatHandler()
13 |
14 | const [editingMessage, setEditingMessage] = useState>()
15 |
16 | return chatMessages
17 | .sort((a, b) => a.message.sequence_number - b.message.sequence_number)
18 | .map((chatMessage, index, array) => {
19 | const messageFileItems = chatFileItems.filter(
20 | (chatFileItem, _, self) =>
21 | chatMessage.fileItems.includes(chatFileItem.id) &&
22 | self.findIndex(item => item.id === chatFileItem.id) === _
23 | )
24 |
25 | return (
26 | setEditingMessage(undefined)}
34 | onSubmitEdit={handleSendEdit}
35 | />
36 | )
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/chat-retrieval-settings.tsx:
--------------------------------------------------------------------------------
1 | import { NyroContext } from "@/context/context"
2 | import { IconAdjustmentsHorizontal } from "@tabler/icons-react"
3 | import { FC, useContext, useState } from "react"
4 | import { Button } from "../ui/button"
5 | import {
6 | Dialog,
7 | DialogContent,
8 | DialogFooter,
9 | DialogTrigger
10 | } from "../ui/dialog"
11 | import { Label } from "../ui/label"
12 | import { Slider } from "../ui/slider"
13 | import { WithTooltip } from "../ui/with-tooltip"
14 |
15 | interface ChatRetrievalSettingsProps {}
16 |
17 | export const ChatRetrievalSettings: FC = ({}) => {
18 | const { sourceCount, setSourceCount } = useContext(NyroContext)
19 |
20 | const [isOpen, setIsOpen] = useState(false)
21 |
22 | return (
23 |
64 | )
65 | }
66 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/chat-scroll-buttons.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IconCircleArrowDownFilled,
3 | IconCircleArrowUpFilled
4 | } from "@tabler/icons-react"
5 | import { FC } from "react"
6 |
7 | interface ChatScrollButtonsProps {
8 | isAtTop: boolean
9 | isAtBottom: boolean
10 | isOverflowing: boolean
11 | scrollToTop: () => void
12 | scrollToBottom: () => void
13 | }
14 |
15 | export const ChatScrollButtons: FC = ({
16 | isAtTop,
17 | isAtBottom,
18 | isOverflowing,
19 | scrollToTop,
20 | scrollToBottom
21 | }) => {
22 | return (
23 | <>
24 | {!isAtTop && isOverflowing && (
25 |
30 | )}
31 |
32 | {!isAtBottom && isOverflowing && (
33 |
38 | )}
39 | >
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/chat/quick-setting-option.tsx:
--------------------------------------------------------------------------------
1 | import { LLM_LIST } from "@/lib/models/llm/llm-list"
2 | import { Tables } from "@/supabase/types"
3 | import { IconCircleCheckFilled, IconRobotFace } from "@tabler/icons-react"
4 | import Image from "next/image"
5 | import { FC } from "react"
6 | import { ModelIcon } from "../models/model-icon"
7 | import { DropdownMenuItem } from "../ui/dropdown-menu"
8 |
9 | interface QuickSettingOptionProps {
10 | contentType: "presets" | "assistants"
11 | isSelected: boolean
12 | item: Tables<"presets"> | Tables<"assistants">
13 | onSelect: () => void
14 | image: string
15 | }
16 |
17 | export const QuickSettingOption: FC = ({
18 | contentType,
19 | isSelected,
20 | item,
21 | onSelect,
22 | image
23 | }) => {
24 | const modelDetails = LLM_LIST.find(model => model.modelId === item.model)
25 |
26 | return (
27 |
32 |
33 | {contentType === "presets" ? (
34 |
39 | ) : image ? (
40 |
48 | ) : (
49 |
53 | )}
54 |
55 |
56 |
57 |
{item.name}
58 |
59 | {item.description && (
60 |
{item.description}
61 | )}
62 |
63 |
64 |
65 | {isSelected ? (
66 |
67 | ) : null}
68 |
69 |
70 | )
71 | }
72 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/icons/google-svg.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react"
2 |
3 | interface GoogleSVGProps {
4 | height?: number
5 | width?: number
6 | className?: string
7 | }
8 |
9 | export const GoogleSVG: FC = ({
10 | height = 40,
11 | width = 40,
12 | className
13 | }) => {
14 | return (
15 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/messages/message-markdown-memoized.tsx:
--------------------------------------------------------------------------------
1 | import { FC, memo } from "react"
2 | import ReactMarkdown, { Options } from "react-markdown"
3 |
4 | export const MessageMarkdownMemoized: FC = memo(
5 | ReactMarkdown,
6 | (prevProps, nextProps) =>
7 | prevProps.children === nextProps.children &&
8 | prevProps.className === nextProps.className
9 | )
10 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/messages/message-markdown.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from "react"
2 | import remarkGfm from "remark-gfm"
3 | import remarkMath from "remark-math"
4 | import { MessageCodeBlock } from "./message-codeblock"
5 | import { MessageMarkdownMemoized } from "./message-markdown-memoized"
6 |
7 | interface MessageMarkdownProps {
8 | content: string
9 | }
10 |
11 | export const MessageMarkdown: FC = ({ content }) => {
12 | return (
13 | {children}
19 | },
20 | img({ node, ...props }) {
21 | return
22 | },
23 | code({ node, className, children, ...props }) {
24 | const childArray = React.Children.toArray(children)
25 | const firstChild = childArray[0] as React.ReactElement
26 | const firstChildAsString = React.isValidElement(firstChild)
27 | ? (firstChild as React.ReactElement).props.children
28 | : firstChild
29 |
30 | if (firstChildAsString === "▍") {
31 | return ▍
32 | }
33 |
34 | if (typeof firstChildAsString === "string") {
35 | childArray[0] = firstChildAsString.replace("`▍`", "▍")
36 | }
37 |
38 | const match = /language-(\w+)/.exec(className || "")
39 |
40 | if (
41 | typeof firstChildAsString === "string" &&
42 | !firstChildAsString.includes("\n")
43 | ) {
44 | return (
45 |
46 | {childArray}
47 |
48 | )
49 | }
50 |
51 | return (
52 |
58 | )
59 | }
60 | }}
61 | >
62 | {content}
63 |
64 | )
65 | }
66 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/messages/message-replies.tsx:
--------------------------------------------------------------------------------
1 | import { IconMessage } from "@tabler/icons-react"
2 | import { FC, useState } from "react"
3 | import {
4 | Sheet,
5 | SheetContent,
6 | SheetDescription,
7 | SheetHeader,
8 | SheetTitle,
9 | SheetTrigger
10 | } from "../ui/sheet"
11 | import { WithTooltip } from "../ui/with-tooltip"
12 | import { MESSAGE_ICON_SIZE } from "./message-actions"
13 |
14 | interface MessageRepliesProps {}
15 |
16 | export const MessageReplies: FC = ({}) => {
17 | const [isOpen, setIsOpen] = useState(false)
18 |
19 | return (
20 |
21 |
22 | View Replies}
26 | trigger={
27 | setIsOpen(true)}
30 | >
31 |
32 |
33 | {1}
34 |
35 |
36 | }
37 | />
38 |
39 |
40 |
41 |
42 | Are you sure absolutely sure?
43 |
44 | This action cannot be undone. This will permanently delete your
45 | account and remove your data from our servers.
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/models/model-option.tsx:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 | import { FC } from "react"
3 | import { ModelIcon } from "./model-icon"
4 | import { IconInfoCircle } from "@tabler/icons-react"
5 | import { WithTooltip } from "../ui/with-tooltip"
6 |
7 | interface ModelOptionProps {
8 | model: LLM
9 | onSelect: () => void
10 | }
11 |
12 | export const ModelOption: FC = ({ model, onSelect }) => {
13 | return (
14 |
17 | {model.provider !== "ollama" && model.pricing && (
18 |
19 |
20 | Input Cost:{" "}
21 | {model.pricing.inputCost} {model.pricing.currency} per{" "}
22 | {model.pricing.unit}
23 |
24 | {model.pricing.outputCost && (
25 |
26 | Output Cost:{" "}
27 | {model.pricing.outputCost} {model.pricing.currency} per{" "}
28 | {model.pricing.unit}
29 |
30 | )}
31 |
32 | )}
33 |
34 | }
35 | side="bottom"
36 | trigger={
37 |
41 |
42 |
43 |
{model.modelName}
44 |
45 |
46 | }
47 | />
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/setup/finish-step.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react"
2 |
3 | interface FinishStepProps {
4 | displayName: string
5 | }
6 |
7 | export const FinishStep: FC = ({ displayName }) => {
8 | return (
9 |
10 |
11 | Welcome to Nyro
12 | {displayName.length > 0 ? `, ${displayName.split(" ")[0]}` : null}!
13 |
14 |
15 |
Click next to start chatting.
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/setup/step-container.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button"
2 | import {
3 | Card,
4 | CardContent,
5 | CardDescription,
6 | CardFooter,
7 | CardHeader,
8 | CardTitle
9 | } from "@/components/ui/card"
10 | import { FC, useRef } from "react"
11 |
12 | export const SETUP_STEP_COUNT = 3
13 |
14 | interface StepContainerProps {
15 | stepDescription: string
16 | stepNum: number
17 | stepTitle: string
18 | onShouldProceed: (shouldProceed: boolean) => void
19 | children?: React.ReactNode
20 | showBackButton?: boolean
21 | showNextButton?: boolean
22 | }
23 |
24 | export const StepContainer: FC = ({
25 | stepDescription,
26 | stepNum,
27 | stepTitle,
28 | onShouldProceed,
29 | children,
30 | showBackButton = false,
31 | showNextButton = true
32 | }) => {
33 | const buttonRef = useRef(null)
34 |
35 | const handleKeyDown = (e: React.KeyboardEvent) => {
36 | if (e.key === "Enter" && !e.shiftKey) {
37 | if (buttonRef.current) {
38 | buttonRef.current.click()
39 | }
40 | }
41 | }
42 |
43 | return (
44 |
48 |
49 |
50 | {stepTitle}
51 |
52 |
53 | {stepNum} / {SETUP_STEP_COUNT}
54 |
55 |
56 |
57 | {stepDescription}
58 |
59 |
60 | {children}
61 |
62 |
63 |
64 | {showBackButton && (
65 |
72 | )}
73 |
74 |
75 |
76 | {showNextButton && (
77 |
84 | )}
85 |
86 |
87 |
88 | )
89 | }
90 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/items/chat/delete-chat.tsx:
--------------------------------------------------------------------------------
1 | import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
2 | import { Button } from "@/components/ui/button"
3 | import {
4 | Dialog,
5 | DialogContent,
6 | DialogDescription,
7 | DialogFooter,
8 | DialogHeader,
9 | DialogTitle,
10 | DialogTrigger
11 | } from "@/components/ui/dialog"
12 | import { NyroContext } from "@/context/context"
13 | import { deleteChat } from "@/db/chats"
14 | import useHotkey from "@/lib/hooks/use-hotkey"
15 | import { Tables } from "@/supabase/types"
16 | import { IconTrash } from "@tabler/icons-react"
17 | import { FC, useContext, useRef, useState } from "react"
18 |
19 | interface DeleteChatProps {
20 | chat: Tables<"chats">
21 | }
22 |
23 | export const DeleteChat: FC = ({ chat }) => {
24 | useHotkey("Backspace", () => setShowChatDialog(true))
25 |
26 | const { setChats } = useContext(NyroContext)
27 | const { handleNewChat } = useChatHandler()
28 |
29 | const buttonRef = useRef(null)
30 |
31 | const [showChatDialog, setShowChatDialog] = useState(false)
32 |
33 | const handleDeleteChat = async () => {
34 | await deleteChat(chat.id)
35 |
36 | setChats(prevState => prevState.filter(c => c.id !== chat.id))
37 |
38 | setShowChatDialog(false)
39 |
40 | handleNewChat()
41 | }
42 |
43 | const handleKeyDown = (e: React.KeyboardEvent) => {
44 | if (e.key === "Enter") {
45 | buttonRef.current?.click()
46 | }
47 | }
48 |
49 | return (
50 |
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/items/chat/update-chat.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button"
2 | import {
3 | Dialog,
4 | DialogContent,
5 | DialogFooter,
6 | DialogHeader,
7 | DialogTitle,
8 | DialogTrigger
9 | } from "@/components/ui/dialog"
10 | import { Input } from "@/components/ui/input"
11 | import { Label } from "@/components/ui/label"
12 | import { NyroContext } from "@/context/context"
13 | import { updateChat } from "@/db/chats"
14 | import { Tables } from "@/supabase/types"
15 | import { IconEdit } from "@tabler/icons-react"
16 | import { FC, useContext, useRef, useState } from "react"
17 |
18 | interface UpdateChatProps {
19 | chat: Tables<"chats">
20 | }
21 |
22 | export const UpdateChat: FC = ({ chat }) => {
23 | const { setChats } = useContext(NyroContext)
24 |
25 | const buttonRef = useRef(null)
26 |
27 | const [showChatDialog, setShowChatDialog] = useState(false)
28 | const [name, setName] = useState(chat.name)
29 |
30 | const handleUpdateChat = async (e: React.MouseEvent) => {
31 | const updatedChat = await updateChat(chat.id, {
32 | name
33 | })
34 | setChats(prevState =>
35 | prevState.map(c => (c.id === chat.id ? updatedChat : c))
36 | )
37 |
38 | setShowChatDialog(false)
39 | }
40 |
41 | const handleKeyDown = (e: React.KeyboardEvent) => {
42 | if (e.key === "Enter") {
43 | buttonRef.current?.click()
44 | }
45 | }
46 |
47 | return (
48 |
75 | )
76 | }
77 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/items/folders/update-folder.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button"
2 | import {
3 | Dialog,
4 | DialogContent,
5 | DialogFooter,
6 | DialogHeader,
7 | DialogTitle,
8 | DialogTrigger
9 | } from "@/components/ui/dialog"
10 | import { Input } from "@/components/ui/input"
11 | import { Label } from "@/components/ui/label"
12 | import { NyroContext } from "@/context/context"
13 | import { updateFolder } from "@/db/folders"
14 | import { Tables } from "@/supabase/types"
15 | import { IconEdit } from "@tabler/icons-react"
16 | import { FC, useContext, useRef, useState } from "react"
17 |
18 | interface UpdateFolderProps {
19 | folder: Tables<"folders">
20 | }
21 |
22 | export const UpdateFolder: FC = ({ folder }) => {
23 | const { setFolders } = useContext(NyroContext)
24 |
25 | const buttonRef = useRef(null)
26 |
27 | const [showFolderDialog, setShowFolderDialog] = useState(false)
28 | const [name, setName] = useState(folder.name)
29 |
30 | const handleUpdateFolder = async (e: React.MouseEvent) => {
31 | const updatedFolder = await updateFolder(folder.id, {
32 | name
33 | })
34 | setFolders(prevState =>
35 | prevState.map(c => (c.id === folder.id ? updatedFolder : c))
36 | )
37 |
38 | setShowFolderDialog(false)
39 | }
40 |
41 | const handleKeyDown = (e: React.KeyboardEvent) => {
42 | if (e.key === "Enter") {
43 | buttonRef.current?.click()
44 | }
45 | }
46 |
47 | return (
48 |
75 | )
76 | }
77 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/items/prompts/create-prompt.tsx:
--------------------------------------------------------------------------------
1 | import { SidebarCreateItem } from "@/components/sidebar/items/all/sidebar-create-item"
2 | import { Input } from "@/components/ui/input"
3 | import { Label } from "@/components/ui/label"
4 | import { TextareaAutosize } from "@/components/ui/textarea-autosize"
5 | import { NyroContext } from "@/context/context"
6 | import { PROMPT_NAME_MAX } from "@/db/limits"
7 | import { TablesInsert } from "@/supabase/types"
8 | import { FC, useContext, useState } from "react"
9 |
10 | interface CreatePromptProps {
11 | isOpen: boolean
12 | onOpenChange: (isOpen: boolean) => void
13 | }
14 |
15 | export const CreatePrompt: FC = ({
16 | isOpen,
17 | onOpenChange
18 | }) => {
19 | const { profile, selectedWorkspace } = useContext(NyroContext)
20 | const [isTyping, setIsTyping] = useState(false)
21 | const [name, setName] = useState("")
22 | const [content, setContent] = useState("")
23 |
24 | if (!profile) return null
25 | if (!selectedWorkspace) return null
26 |
27 | return (
28 |
39 | }
40 | renderInputs={() => (
41 | <>
42 |
43 |
44 |
45 | setName(e.target.value)}
49 | maxLength={PROMPT_NAME_MAX}
50 | onCompositionStart={() => setIsTyping(true)}
51 | onCompositionEnd={() => setIsTyping(false)}
52 | />
53 |
54 |
55 |
56 |
57 |
58 | setIsTyping(true)}
65 | onCompositionEnd={() => setIsTyping(false)}
66 | />
67 |
68 | >
69 | )}
70 | />
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/items/prompts/prompt-item.tsx:
--------------------------------------------------------------------------------
1 | import { Input } from "@/components/ui/input"
2 | import { Label } from "@/components/ui/label"
3 | import { TextareaAutosize } from "@/components/ui/textarea-autosize"
4 | import { PROMPT_NAME_MAX } from "@/db/limits"
5 | import { Tables } from "@/supabase/types"
6 | import { IconPencil } from "@tabler/icons-react"
7 | import { FC, useState } from "react"
8 | import { SidebarItem } from "../all/sidebar-display-item"
9 |
10 | interface PromptItemProps {
11 | prompt: Tables<"prompts">
12 | }
13 |
14 | export const PromptItem: FC = ({ prompt }) => {
15 | const [name, setName] = useState(prompt.name)
16 | const [content, setContent] = useState(prompt.content)
17 | const [isTyping, setIsTyping] = useState(false)
18 | return (
19 | }
24 | updateState={{ name, content }}
25 | renderInputs={() => (
26 | <>
27 |
28 |
29 |
30 | setName(e.target.value)}
34 | maxLength={PROMPT_NAME_MAX}
35 | onCompositionStart={() => setIsTyping(true)}
36 | onCompositionEnd={() => setIsTyping(false)}
37 | />
38 |
39 |
40 |
41 |
42 |
43 | setIsTyping(true)}
50 | onCompositionEnd={() => setIsTyping(false)}
51 | />
52 |
53 | >
54 | )}
55 | />
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/sidebar-content.tsx:
--------------------------------------------------------------------------------
1 | import { Tables } from "@/supabase/types"
2 | import { ContentType, DataListType } from "@/types"
3 | import { FC, useState } from "react"
4 | import { SidebarCreateButtons } from "./sidebar-create-buttons"
5 | import { SidebarDataList } from "./sidebar-data-list"
6 | import { SidebarSearch } from "./sidebar-search"
7 |
8 | interface SidebarContentProps {
9 | contentType: ContentType
10 | data: DataListType
11 | folders: Tables<"folders">[]
12 | }
13 |
14 | export const SidebarContent: FC = ({
15 | contentType,
16 | data,
17 | folders
18 | }) => {
19 | const [searchTerm, setSearchTerm] = useState("")
20 |
21 | const filteredData: any = data.filter(item =>
22 | item.name.toLowerCase().includes(searchTerm.toLowerCase())
23 | )
24 |
25 | return (
26 | // Subtract 50px for the height of the workspace settings
27 |
28 |
29 | 0}
32 | />
33 |
34 |
35 |
36 |
41 |
42 |
43 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/sidebar-search.tsx:
--------------------------------------------------------------------------------
1 | import { ContentType } from "@/types"
2 | import { FC } from "react"
3 | import { Input } from "../ui/input"
4 |
5 | interface SidebarSearchProps {
6 | contentType: ContentType
7 | searchTerm: string
8 | setSearchTerm: Function
9 | }
10 |
11 | export const SidebarSearch: FC = ({
12 | contentType,
13 | searchTerm,
14 | setSearchTerm
15 | }) => {
16 | return (
17 | setSearchTerm(e.target.value)}
21 | />
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/sidebar/sidebar-switch-item.tsx:
--------------------------------------------------------------------------------
1 | import { ContentType } from "@/types"
2 | import { FC } from "react"
3 | import { TabsTrigger } from "../ui/tabs"
4 | import { WithTooltip } from "../ui/with-tooltip"
5 |
6 | interface SidebarSwitchItemProps {
7 | contentType: ContentType
8 | icon: React.ReactNode
9 | onContentTypeChange: (contentType: ContentType) => void
10 | }
11 |
12 | export const SidebarSwitchItem: FC = ({
13 | contentType,
14 | icon,
15 | onContentTypeChange
16 | }) => {
17 | return (
18 | {contentType[0].toUpperCase() + contentType.substring(1)}
21 | }
22 | trigger={
23 | onContentTypeChange(contentType as ContentType)}
27 | >
28 | {icon}
29 |
30 | }
31 | />
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"
5 | import { ChevronDown } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Accordion = AccordionPrimitive.Root
10 |
11 | const AccordionItem = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
20 | ))
21 | AccordionItem.displayName = "AccordionItem"
22 |
23 | const AccordionTrigger = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, children, ...props }, ref) => (
27 |
28 | svg]:rotate-180",
32 | className
33 | )}
34 | {...props}
35 | >
36 | {children}
37 |
38 |
39 |
40 | ))
41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42 |
43 | const AccordionContent = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef
46 | >(({ className, children, ...props }, ref) => (
47 |
52 | {children}
53 |
54 | ))
55 |
56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName
57 |
58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
59 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/advanced-settings.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Collapsible,
3 | CollapsibleContent,
4 | CollapsibleTrigger
5 | } from "@/components/ui/collapsible"
6 | import { IconChevronDown, IconChevronRight } from "@tabler/icons-react"
7 | import { FC, useState } from "react"
8 |
9 | interface AdvancedSettingsProps {
10 | children: React.ReactNode
11 | }
12 |
13 | export const AdvancedSettings: FC = ({ children }) => {
14 | const [isOpen, setIsOpen] = useState(
15 | false
16 | // localStorage.getItem("advanced-settings-open") === "true"
17 | )
18 |
19 | const handleOpenChange = (isOpen: boolean) => {
20 | setIsOpen(isOpen)
21 | // localStorage.setItem("advanced-settings-open", String(isOpen))
22 | }
23 |
24 | return (
25 |
26 |
27 |
28 |
Advanced Settings
29 | {isOpen ? (
30 |
31 | ) : (
32 |
33 | )}
34 |
35 |
36 |
37 | {children}
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const alertVariants = cva(
7 | "[&>svg]:text-foreground relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-background text-foreground",
12 | destructive:
13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
14 | }
15 | },
16 | defaultVariants: {
17 | variant: "default"
18 | }
19 | }
20 | )
21 |
22 | const Alert = React.forwardRef<
23 | HTMLDivElement,
24 | React.HTMLAttributes & VariantProps
25 | >(({ className, variant, ...props }, ref) => (
26 |
32 | ))
33 | Alert.displayName = "Alert"
34 |
35 | const AlertTitle = React.forwardRef<
36 | HTMLParagraphElement,
37 | React.HTMLAttributes
38 | >(({ className, ...props }, ref) => (
39 |
44 | ))
45 | AlertTitle.displayName = "AlertTitle"
46 |
47 | const AlertDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | AlertDescription.displayName = "AlertDescription"
58 |
59 | export { Alert, AlertTitle, AlertDescription }
60 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/aspect-ratio.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
4 |
5 | const AspectRatio = AspectRatioPrimitive.Root
6 |
7 | export { AspectRatio }
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarImage, AvatarFallback }
51 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
13 | secondary:
14 | "bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
15 | destructive:
16 | "bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
17 | outline: "text-foreground"
18 | }
19 | },
20 | defaultVariants: {
21 | variant: "default"
22 | }
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { Badge, badgeVariants }
37 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/brand.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Link from "next/link"
4 | import { FC } from "react"
5 | import { NyroSVG } from "../icons/nyro-svg"
6 |
7 | interface BrandProps {
8 | theme?: "dark" | "light"
9 | }
10 |
11 | export const Brand: FC = ({ theme = "dark" }) => {
12 | return (
13 |
19 |
20 |
21 |
22 |
23 | Nyro
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from "@radix-ui/react-slot"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 | import * as React from "react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors hover:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border-input bg-background hover:bg-accent hover:text-accent-foreground border",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline"
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "size-10"
27 | }
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default"
32 | }
33 | }
34 | )
35 |
36 | export interface ButtonProps
37 | extends React.ButtonHTMLAttributes,
38 | VariantProps {
39 | asChild?: boolean
40 | }
41 |
42 | const Button = React.forwardRef(
43 | ({ className, variant, size, asChild = false, ...props }, ref) => {
44 | const Comp = asChild ? Slot : "button"
45 | return (
46 |
51 | )
52 | }
53 | )
54 | Button.displayName = "Button"
55 |
56 | export { Button, buttonVariants }
57 |
--------------------------------------------------------------------------------
/nyro-chatbot/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 |
44 | ))
45 | CardTitle.displayName = "CardTitle"
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | CardDescription.displayName = "CardDescription"
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ))
65 | CardContent.displayName = "CardContent"
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ))
77 | CardFooter.displayName = "CardFooter"
78 |
79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
80 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/chat-app.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import React, { ReactNode, useContext } from 'react'
4 | import MenuBar from './menu-bar'
5 | import PeekBar from './peek-bar'
6 | import { NyroContext } from '@/context/context'
7 |
8 | interface ChatAppProps {
9 | children: ReactNode
10 | }
11 |
12 | const ChatApp: React.FC = ({ children }) => {
13 | const {
14 | appRef,
15 | otherRef,
16 | isRetracted,
17 | isTransparent,
18 | isPinned,
19 | handleRetract,
20 | handleExpand,
21 | handleTogglePin,
22 | handleMouseEnter,
23 | handleMouseMove,
24 | handleMouseLeave,
25 | handleMouseDown
26 | } = useContext(NyroContext);
27 |
28 | return (
29 |
36 | {isRetracted ? (
37 |
38 | ) : (
39 | <>
40 |
44 |
49 |
50 | {children}
51 | >
52 | )}
53 |
54 |
55 | );
56 | }
57 |
58 | export default ChatApp;
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/checkbox.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5 | import { Check } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Checkbox = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => (
13 |
21 |
24 |
25 |
26 |
27 | ))
28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName
29 |
30 | export { Checkbox }
31 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/file-icon.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IconFile,
3 | IconFileText,
4 | IconFileTypeCsv,
5 | IconFileTypeDocx,
6 | IconFileTypePdf,
7 | IconJson,
8 | IconMarkdown,
9 | IconPhoto
10 | } from "@tabler/icons-react"
11 | import { FC } from "react"
12 |
13 | interface FileIconProps {
14 | type: string
15 | size?: number
16 | }
17 |
18 | export const FileIcon: FC = ({ type, size = 32 }) => {
19 | if (type.includes("image")) {
20 | return
21 | } else if (type.includes("pdf")) {
22 | return
23 | } else if (type.includes("csv")) {
24 | return
25 | } else if (type.includes("docx")) {
26 | return
27 | } else if (type.includes("plain")) {
28 | return
29 | } else if (type.includes("json")) {
30 | return
31 | } else if (type.includes("markdown")) {
32 | return
33 | } else {
34 | return
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/file-preview.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 | import { Tables } from "@/supabase/types"
3 | import { ChatFile, MessageImage } from "@/types"
4 | import { IconFileFilled } from "@tabler/icons-react"
5 | import Image from "next/image"
6 | import { FC } from "react"
7 | import { DrawingCanvas } from "../utility/drawing-canvas"
8 | import { Dialog, DialogContent } from "./dialog"
9 |
10 | interface FilePreviewProps {
11 | type: "image" | "file" | "file_item"
12 | item: ChatFile | MessageImage | Tables<"file_items">
13 | isOpen: boolean
14 | onOpenChange: (isOpen: boolean) => void
15 | }
16 |
17 | export const FilePreview: FC = ({
18 | type,
19 | item,
20 | isOpen,
21 | onOpenChange
22 | }) => {
23 | return (
24 |
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/hover-card.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const HoverCard = HoverCardPrimitive.Root
9 |
10 | const HoverCardTrigger = HoverCardPrimitive.Trigger
11 |
12 | const HoverCardContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
26 | ))
27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
28 |
29 | export { HoverCard, HoverCardTrigger, HoverCardContent }
30 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | )
21 | }
22 | )
23 | Input.displayName = "Input"
24 |
25 | export { Input }
26 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as LabelPrimitive from "@radix-ui/react-label"
4 | import { cva, type VariantProps } from "class-variance-authority"
5 | import * as React from "react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva(
10 | "text-sm font-semibold leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/limit-display.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react"
2 |
3 | interface LimitDisplayProps {
4 | used: number
5 | limit: number
6 | }
7 |
8 | export const LimitDisplay: FC = ({ used, limit }) => {
9 | return (
10 |
11 | {used}/{limit}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as PopoverPrimitive from "@radix-ui/react-popover"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
17 |
27 |
28 | ))
29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
30 |
31 | export { Popover, PopoverTrigger, PopoverContent }
32 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/progress.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ProgressPrimitive from "@radix-ui/react-progress"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Progress = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, value, ...props }, ref) => (
12 |
20 |
24 |
25 | ))
26 | Progress.displayName = ProgressPrimitive.Root.displayName
27 |
28 | export { Progress }
29 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/radio-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5 | import { Circle } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const RadioGroup = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => {
13 | return (
14 |
19 | )
20 | })
21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
22 |
23 | const RadioGroupItem = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => {
27 | return (
28 |
36 |
37 |
38 |
39 |
40 | )
41 | })
42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
43 |
44 | export { RadioGroup, RadioGroupItem }
45 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/screen-loader.tsx:
--------------------------------------------------------------------------------
1 | import { IconLoader2 } from "@tabler/icons-react"
2 | import { FC } from "react"
3 |
4 | interface ScreenLoaderProps {}
5 |
6 | export const ScreenLoader: FC = () => {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/nyro-chatbot/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 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 |
3 | function Skeleton({
4 | className,
5 | ...props
6 | }: React.HTMLAttributes) {
7 | return (
8 |
12 | )
13 | }
14 |
15 | export { Skeleton }
16 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as SliderPrimitive from "@radix-ui/react-slider"
4 | import * as React from "react"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Slider = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
21 |
22 |
23 |
24 |
25 | ))
26 | Slider.displayName = SliderPrimitive.Root.displayName
27 |
28 | export { Slider }
29 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Toaster as Sonner } from "sonner"
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = "system" } = useTheme()
10 |
11 | return (
12 |
28 | )
29 | }
30 |
31 | export { Toaster }
32 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/submit-button.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import React from "react"
4 | import { useFormStatus } from "react-dom"
5 | import { Button, ButtonProps } from "./button"
6 |
7 | const SubmitButton = React.forwardRef(
8 | (props, ref) => {
9 | const { pending } = useFormStatus()
10 |
11 | return
12 | }
13 | )
14 |
15 | SubmitButton.displayName = "SubmitButton"
16 |
17 | export { SubmitButton }
18 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SwitchPrimitives from "@radix-ui/react-switch"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/textarea-autosize.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 | import { FC } from "react"
3 | import ReactTextareaAutosize from "react-textarea-autosize"
4 |
5 | interface TextareaAutosizeProps {
6 | value: string
7 | onValueChange: (value: string) => void
8 |
9 | textareaRef?: React.RefObject
10 | className?: string
11 |
12 | placeholder?: string
13 | minRows?: number
14 | maxRows?: number
15 | maxLength?: number
16 | onKeyDown?: (event: React.KeyboardEvent) => void
17 | onPaste?: (event: React.ClipboardEvent) => void
18 | onCompositionStart?: (event: React.CompositionEvent) => void
19 | onCompositionEnd?: (event: React.CompositionEvent) => void
20 | }
21 |
22 | export const TextareaAutosize: FC = ({
23 | value,
24 | onValueChange,
25 | textareaRef,
26 | className,
27 | placeholder = "",
28 | minRows = 1,
29 | maxRows = 6,
30 | maxLength,
31 | onKeyDown = () => {},
32 | onPaste = () => {},
33 | onCompositionStart = () => {},
34 | onCompositionEnd = () => {}
35 | }) => {
36 | return (
37 | maxRows ? minRows : maxRows}
45 | placeholder={placeholder}
46 | value={value}
47 | maxLength={maxLength}
48 | onChange={event => onValueChange(event.target.value)}
49 | onKeyDown={onKeyDown}
50 | onPaste={onPaste}
51 | onCompositionStart={onCompositionStart}
52 | onCompositionEnd={onCompositionEnd}
53 | />
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface TextareaProps
6 | extends React.TextareaHTMLAttributes {}
7 |
8 | const Textarea = React.forwardRef(
9 | ({ className, ...props }, ref) => {
10 | return (
11 |
19 | )
20 | }
21 | )
22 | Textarea.displayName = "Textarea"
23 |
24 | export { Textarea }
25 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | Toast,
5 | ToastClose,
6 | ToastDescription,
7 | ToastProvider,
8 | ToastTitle,
9 | ToastViewport
10 | } from "@/components/ui/toast"
11 | import { useToast } from "@/components/ui/use-toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title}}
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/toggle-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
5 | import { VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 | import { toggleVariants } from "@/components/ui/toggle"
9 |
10 | const ToggleGroupContext = React.createContext<
11 | VariantProps
12 | >({
13 | size: "default",
14 | variant: "default"
15 | })
16 |
17 | const ToggleGroup = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef &
20 | VariantProps
21 | >(({ className, variant, size, children, ...props }, ref) => (
22 |
27 |
28 | {children}
29 |
30 |
31 | ))
32 |
33 | ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
34 |
35 | const ToggleGroupItem = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef &
38 | VariantProps
39 | >(({ className, children, variant, size, ...props }, ref) => {
40 | const context = React.useContext(ToggleGroupContext)
41 |
42 | return (
43 |
54 | {children}
55 |
56 | )
57 | })
58 |
59 | ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
60 |
61 | export { ToggleGroup, ToggleGroupItem }
62 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TogglePrimitive from "@radix-ui/react-toggle"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const toggleVariants = cva(
10 | "ring-offset-background hover:bg-muted hover:text-muted-foreground focus-visible:ring-ring data-[state=on]:bg-accent data-[state=on]:text-accent-foreground inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
11 | {
12 | variants: {
13 | variant: {
14 | default: "bg-transparent",
15 | outline:
16 | "border-input hover:bg-accent hover:text-accent-foreground border bg-transparent"
17 | },
18 | size: {
19 | default: "h-10 px-3",
20 | sm: "h-9 px-2.5",
21 | lg: "h-11 px-5"
22 | }
23 | },
24 | defaultVariants: {
25 | variant: "default",
26 | size: "default"
27 | }
28 | }
29 | )
30 |
31 | const Toggle = React.forwardRef<
32 | React.ElementRef,
33 | React.ComponentPropsWithoutRef &
34 | VariantProps
35 | >(({ className, variant, size, ...props }, ref) => (
36 |
41 | ))
42 |
43 | Toggle.displayName = TogglePrimitive.Root.displayName
44 |
45 | export { Toggle, toggleVariants }
46 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider
9 |
10 | const Tooltip = TooltipPrimitive.Root
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger
13 |
14 | const TooltipContent = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, sideOffset = 4, ...props }, ref) => (
18 |
27 | ))
28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName
29 |
30 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
31 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/ui/with-tooltip.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react"
2 | import {
3 | Tooltip,
4 | TooltipContent,
5 | TooltipProvider,
6 | TooltipTrigger
7 | } from "./tooltip"
8 |
9 | interface WithTooltipProps {
10 | display: React.ReactNode
11 | trigger: React.ReactNode
12 |
13 | delayDuration?: number
14 | side?: "left" | "right" | "top" | "bottom"
15 | }
16 |
17 | export const WithTooltip: FC = ({
18 | display,
19 | trigger,
20 |
21 | delayDuration = 500,
22 | side = "right"
23 | }) => {
24 | return (
25 |
26 |
27 | {trigger}
28 |
29 | {display}
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/utility/alerts.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Popover,
3 | PopoverContent,
4 | PopoverTrigger
5 | } from "@/components/ui/popover"
6 | import { IconBell } from "@tabler/icons-react"
7 | import { FC } from "react"
8 | import { SIDEBAR_ICON_SIZE } from "../sidebar/sidebar-switcher"
9 |
10 | interface AlertsProps {}
11 |
12 | export const Alerts: FC = () => {
13 | return (
14 |
15 |
16 |
17 |
18 | {1 > 0 && (
19 |
20 | 1
21 |
22 | )}
23 |
24 |
25 |
26 | placeholder
27 |
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/utility/change-password.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { supabase } from "@/lib/supabase/browser-client"
4 | import { useRouter } from "next/navigation"
5 | import { FC, useState } from "react"
6 | import { Button } from "../ui/button"
7 | import {
8 | Dialog,
9 | DialogContent,
10 | DialogFooter,
11 | DialogHeader,
12 | DialogTitle
13 | } from "../ui/dialog"
14 | import { Input } from "../ui/input"
15 | import { toast } from "sonner"
16 |
17 | interface ChangePasswordProps {}
18 |
19 | export const ChangePassword: FC = () => {
20 | const router = useRouter()
21 |
22 | const [newPassword, setNewPassword] = useState("")
23 | const [confirmPassword, setConfirmPassword] = useState("")
24 |
25 | const handleResetPassword = async () => {
26 | if (!newPassword) return toast.info("Please enter your new password.")
27 |
28 | await supabase.auth.updateUser({ password: newPassword })
29 |
30 | toast.success("Password changed successfully.")
31 |
32 | return router.push("/login")
33 | }
34 |
35 | return (
36 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/utility/providers.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { TooltipProvider } from "@/components/ui/tooltip"
4 | import { ThemeProvider as NextThemesProvider } from "next-themes"
5 | import { ThemeProviderProps } from "next-themes/dist/types"
6 | import { FC } from "react"
7 |
8 | export const Providers: FC = ({ children, ...props }) => {
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/utility/theme-switcher.tsx:
--------------------------------------------------------------------------------
1 | import { IconMoon, IconSun } from "@tabler/icons-react"
2 | import { useTheme } from "next-themes"
3 | import { FC } from "react"
4 | import { SIDEBAR_ICON_SIZE } from "../sidebar/sidebar-switcher"
5 | import { Button } from "../ui/button"
6 |
7 | interface ThemeSwitcherProps {}
8 |
9 | export const ThemeSwitcher: FC = () => {
10 | const { setTheme, theme } = useTheme()
11 |
12 | const handleChange = (theme: "dark" | "light") => {
13 | localStorage.setItem("theme", theme)
14 |
15 | setTheme(theme)
16 | }
17 |
18 | return (
19 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/nyro-chatbot/components/utility/translations-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import initTranslations from "@/lib/i18n"
4 | import { createInstance } from "i18next"
5 | import { I18nextProvider } from "react-i18next"
6 |
7 | export default function TranslationsProvider({
8 | children,
9 | locale,
10 | namespaces,
11 | resources
12 | }: any) {
13 | const i18n = createInstance()
14 |
15 | initTranslations(locale, namespaces, i18n, resources)
16 |
17 | return {children}
18 | }
19 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/assistant-collections.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getAssistantCollectionsByAssistantId = async (
5 | assistantId: string
6 | ) => {
7 | const { data: assistantCollections, error } = await supabase
8 | .from("assistants")
9 | .select(
10 | `
11 | id,
12 | name,
13 | collections (*)
14 | `
15 | )
16 | .eq("id", assistantId)
17 | .single()
18 |
19 | if (!assistantCollections) {
20 | throw new Error(error.message)
21 | }
22 |
23 | return assistantCollections
24 | }
25 |
26 | export const createAssistantCollection = async (
27 | assistantCollection: TablesInsert<"assistant_collections">
28 | ) => {
29 | const { data: createdAssistantCollection, error } = await supabase
30 | .from("assistant_collections")
31 | .insert(assistantCollection)
32 | .select("*")
33 |
34 | if (!createdAssistantCollection) {
35 | throw new Error(error.message)
36 | }
37 |
38 | return createdAssistantCollection
39 | }
40 |
41 | export const createAssistantCollections = async (
42 | assistantCollections: TablesInsert<"assistant_collections">[]
43 | ) => {
44 | const { data: createdAssistantCollections, error } = await supabase
45 | .from("assistant_collections")
46 | .insert(assistantCollections)
47 | .select("*")
48 |
49 | if (!createdAssistantCollections) {
50 | throw new Error(error.message)
51 | }
52 |
53 | return createdAssistantCollections
54 | }
55 |
56 | export const deleteAssistantCollection = async (
57 | assistantId: string,
58 | collectionId: string
59 | ) => {
60 | const { error } = await supabase
61 | .from("assistant_collections")
62 | .delete()
63 | .eq("assistant_id", assistantId)
64 | .eq("collection_id", collectionId)
65 |
66 | if (error) throw new Error(error.message)
67 |
68 | return true
69 | }
70 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/assistant-files.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getAssistantFilesByAssistantId = async (assistantId: string) => {
5 | const { data: assistantFiles, error } = await supabase
6 | .from("assistants")
7 | .select(
8 | `
9 | id,
10 | name,
11 | files (*)
12 | `
13 | )
14 | .eq("id", assistantId)
15 | .single()
16 |
17 | if (!assistantFiles) {
18 | throw new Error(error.message)
19 | }
20 |
21 | return assistantFiles
22 | }
23 |
24 | export const createAssistantFile = async (
25 | assistantFile: TablesInsert<"assistant_files">
26 | ) => {
27 | const { data: createdAssistantFile, error } = await supabase
28 | .from("assistant_files")
29 | .insert(assistantFile)
30 | .select("*")
31 |
32 | if (!createdAssistantFile) {
33 | throw new Error(error.message)
34 | }
35 |
36 | return createdAssistantFile
37 | }
38 |
39 | export const createAssistantFiles = async (
40 | assistantFiles: TablesInsert<"assistant_files">[]
41 | ) => {
42 | const { data: createdAssistantFiles, error } = await supabase
43 | .from("assistant_files")
44 | .insert(assistantFiles)
45 | .select("*")
46 |
47 | if (!createdAssistantFiles) {
48 | throw new Error(error.message)
49 | }
50 |
51 | return createdAssistantFiles
52 | }
53 |
54 | export const deleteAssistantFile = async (
55 | assistantId: string,
56 | fileId: string
57 | ) => {
58 | const { error } = await supabase
59 | .from("assistant_files")
60 | .delete()
61 | .eq("assistant_id", assistantId)
62 | .eq("file_id", fileId)
63 |
64 | if (error) throw new Error(error.message)
65 |
66 | return true
67 | }
68 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/assistant-tools.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getAssistantToolsByAssistantId = async (assistantId: string) => {
5 | const { data: assistantTools, error } = await supabase
6 | .from("assistants")
7 | .select(
8 | `
9 | id,
10 | name,
11 | tools (*)
12 | `
13 | )
14 | .eq("id", assistantId)
15 | .single()
16 |
17 | if (!assistantTools) {
18 | throw new Error(error.message)
19 | }
20 |
21 | return assistantTools
22 | }
23 |
24 | export const createAssistantTool = async (
25 | assistantTool: TablesInsert<"assistant_tools">
26 | ) => {
27 | const { data: createdAssistantTool, error } = await supabase
28 | .from("assistant_tools")
29 | .insert(assistantTool)
30 | .select("*")
31 |
32 | if (!createdAssistantTool) {
33 | throw new Error(error.message)
34 | }
35 |
36 | return createdAssistantTool
37 | }
38 |
39 | export const createAssistantTools = async (
40 | assistantTools: TablesInsert<"assistant_tools">[]
41 | ) => {
42 | const { data: createdAssistantTools, error } = await supabase
43 | .from("assistant_tools")
44 | .insert(assistantTools)
45 | .select("*")
46 |
47 | if (!createdAssistantTools) {
48 | throw new Error(error.message)
49 | }
50 |
51 | return createdAssistantTools
52 | }
53 |
54 | export const deleteAssistantTool = async (
55 | assistantId: string,
56 | toolId: string
57 | ) => {
58 | const { error } = await supabase
59 | .from("assistant_tools")
60 | .delete()
61 | .eq("assistant_id", assistantId)
62 | .eq("tool_id", toolId)
63 |
64 | if (error) throw new Error(error.message)
65 |
66 | return true
67 | }
68 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/chat-files.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getChatFilesByChatId = async (chatId: string) => {
5 | const { data: chatFiles, error } = await supabase
6 | .from("chats")
7 | .select(
8 | `
9 | id,
10 | name,
11 | files (*)
12 | `
13 | )
14 | .eq("id", chatId)
15 | .single()
16 |
17 | if (!chatFiles) {
18 | throw new Error(error.message)
19 | }
20 |
21 | return chatFiles
22 | }
23 |
24 | export const createChatFile = async (chatFile: TablesInsert<"chat_files">) => {
25 | const { data: createdChatFile, error } = await supabase
26 | .from("chat_files")
27 | .insert(chatFile)
28 | .select("*")
29 |
30 | if (!createdChatFile) {
31 | throw new Error(error.message)
32 | }
33 |
34 | return createdChatFile
35 | }
36 |
37 | export const createChatFiles = async (
38 | chatFiles: TablesInsert<"chat_files">[]
39 | ) => {
40 | const { data: createdChatFiles, error } = await supabase
41 | .from("chat_files")
42 | .insert(chatFiles)
43 | .select("*")
44 |
45 | if (!createdChatFiles) {
46 | throw new Error(error.message)
47 | }
48 |
49 | return createdChatFiles
50 | }
51 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/chats.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert, TablesUpdate } from "@/supabase/types"
3 |
4 | export const getChatById = async (chatId: string) => {
5 | const { data: chat } = await supabase
6 | .from("chats")
7 | .select("*")
8 | .eq("id", chatId)
9 | .maybeSingle()
10 |
11 | return chat
12 | }
13 |
14 | export const getChatsByWorkspaceId = async (workspaceId: string) => {
15 | const { data: chats, error } = await supabase
16 | .from("chats")
17 | .select("*")
18 | .eq("workspace_id", workspaceId)
19 | .order("created_at", { ascending: false })
20 |
21 | if (!chats) {
22 | throw new Error(error.message)
23 | }
24 |
25 | return chats
26 | }
27 |
28 | export const createChat = async (chat: TablesInsert<"chats">) => {
29 | const { data: createdChat, error } = await supabase
30 | .from("chats")
31 | .insert([chat])
32 | .select("*")
33 | .single()
34 |
35 | if (error) {
36 | throw new Error(error.message)
37 | }
38 |
39 | return createdChat
40 | }
41 |
42 | export const createChats = async (chats: TablesInsert<"chats">[]) => {
43 | const { data: createdChats, error } = await supabase
44 | .from("chats")
45 | .insert(chats)
46 | .select("*")
47 |
48 | if (error) {
49 | throw new Error(error.message)
50 | }
51 |
52 | return createdChats
53 | }
54 |
55 | export const updateChat = async (
56 | chatId: string,
57 | chat: TablesUpdate<"chats">
58 | ) => {
59 | const { data: updatedChat, error } = await supabase
60 | .from("chats")
61 | .update(chat)
62 | .eq("id", chatId)
63 | .select("*")
64 | .single()
65 |
66 | if (error) {
67 | throw new Error(error.message)
68 | }
69 |
70 | return updatedChat
71 | }
72 |
73 | export const deleteChat = async (chatId: string) => {
74 | const { error } = await supabase.from("chats").delete().eq("id", chatId)
75 |
76 | if (error) {
77 | throw new Error(error.message)
78 | }
79 |
80 | return true
81 | }
82 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/collection-files.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getCollectionFilesByCollectionId = async (
5 | collectionId: string
6 | ) => {
7 | const { data: collectionFiles, error } = await supabase
8 | .from("collections")
9 | .select(
10 | `
11 | id,
12 | name,
13 | files ( id, name, type )
14 | `
15 | )
16 | .eq("id", collectionId)
17 | .single()
18 |
19 | if (!collectionFiles) {
20 | throw new Error(error.message)
21 | }
22 |
23 | return collectionFiles
24 | }
25 |
26 | export const createCollectionFile = async (
27 | collectionFile: TablesInsert<"collection_files">
28 | ) => {
29 | const { data: createdCollectionFile, error } = await supabase
30 | .from("collection_files")
31 | .insert(collectionFile)
32 | .select("*")
33 |
34 | if (!createdCollectionFile) {
35 | throw new Error(error.message)
36 | }
37 |
38 | return createdCollectionFile
39 | }
40 |
41 | export const createCollectionFiles = async (
42 | collectionFiles: TablesInsert<"collection_files">[]
43 | ) => {
44 | const { data: createdCollectionFiles, error } = await supabase
45 | .from("collection_files")
46 | .insert(collectionFiles)
47 | .select("*")
48 |
49 | if (!createdCollectionFiles) {
50 | throw new Error(error.message)
51 | }
52 |
53 | return createdCollectionFiles
54 | }
55 |
56 | export const deleteCollectionFile = async (
57 | collectionId: string,
58 | fileId: string
59 | ) => {
60 | const { error } = await supabase
61 | .from("collection_files")
62 | .delete()
63 | .eq("collection_id", collectionId)
64 | .eq("file_id", fileId)
65 |
66 | if (error) throw new Error(error.message)
67 |
68 | return true
69 | }
70 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/folders.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert, TablesUpdate } from "@/supabase/types"
3 |
4 | export const getFoldersByWorkspaceId = async (workspaceId: string) => {
5 | const { data: folders, error } = await supabase
6 | .from("folders")
7 | .select("*")
8 | .eq("workspace_id", workspaceId)
9 |
10 | if (!folders) {
11 | throw new Error(error.message)
12 | }
13 |
14 | return folders
15 | }
16 |
17 | export const createFolder = async (folder: TablesInsert<"folders">) => {
18 | const { data: createdFolder, error } = await supabase
19 | .from("folders")
20 | .insert([folder])
21 | .select("*")
22 | .single()
23 |
24 | if (error) {
25 | throw new Error(error.message)
26 | }
27 |
28 | return createdFolder
29 | }
30 |
31 | export const updateFolder = async (
32 | folderId: string,
33 | folder: TablesUpdate<"folders">
34 | ) => {
35 | const { data: updatedFolder, error } = await supabase
36 | .from("folders")
37 | .update(folder)
38 | .eq("id", folderId)
39 | .select("*")
40 | .single()
41 |
42 | if (error) {
43 | throw new Error(error.message)
44 | }
45 |
46 | return updatedFolder
47 | }
48 |
49 | export const deleteFolder = async (folderId: string) => {
50 | const { error } = await supabase.from("folders").delete().eq("id", folderId)
51 |
52 | if (error) {
53 | throw new Error(error.message)
54 | }
55 |
56 | return true
57 | }
58 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/index.ts:
--------------------------------------------------------------------------------
1 | import "./assistants"
2 | import "./chats"
3 | import "./file-items"
4 | import "./files"
5 | import "./folders"
6 | import "./messages"
7 | import "./presets"
8 | import "./profile"
9 | import "./prompts"
10 | import "./workspaces"
11 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/limits.ts:
--------------------------------------------------------------------------------
1 | // Profiles
2 | export const PROFILE_BIO_MAX = 500
3 | export const PROFILE_DISPLAY_NAME_MAX = 100
4 | export const PROFILE_CONTEXT_MAX = 1500
5 | export const PROFILE_USERNAME_MIN = 3
6 | export const PROFILE_USERNAME_MAX = 25
7 |
8 | // Workspaces
9 | export const WORKSPACE_NAME_MAX = 100
10 | export const WORKSPACE_DESCRIPTION_MAX = 500
11 | export const WORKSPACE_INSTRUCTIONS_MAX = 1500
12 |
13 | // Chats
14 |
15 | // Presets
16 | export const PRESET_NAME_MAX = 100
17 | export const PRESET_DESCRIPTION_MAX = 500
18 | export const PRESET_PROMPT_MAX = 100000
19 |
20 | // Prompts
21 | export const PROMPT_NAME_MAX = 100
22 | export const PROMPT_CONTENT_MAX = 100000
23 |
24 | // Files
25 | export const FILE_NAME_MAX = 100
26 | export const FILE_DESCRIPTION_MAX = 500
27 |
28 | // Collections
29 | export const COLLECTION_NAME_MAX = 100
30 | export const COLLECTION_DESCRIPTION_MAX = 500
31 |
32 | // Assistant
33 | export const ASSISTANT_NAME_MAX = 100
34 | export const ASSISTANT_DESCRIPTION_MAX = 500
35 | export const ASSISTANT_PROMPT_MAX = 100000
36 |
37 | // Tools
38 | export const TOOL_NAME_MAX = 100
39 | export const TOOL_DESCRIPTION_MAX = 500
40 |
41 | // Models
42 | export const MODEL_NAME_MAX = 100
43 | export const MODEL_DESCRIPTION_MAX = 500
44 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/message-file-items.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert } from "@/supabase/types"
3 |
4 | export const getMessageFileItemsByMessageId = async (messageId: string) => {
5 | const { data: messageFileItems, error } = await supabase
6 | .from("messages")
7 | .select(
8 | `
9 | id,
10 | file_items (*)
11 | `
12 | )
13 | .eq("id", messageId)
14 | .single()
15 |
16 | if (!messageFileItems) {
17 | throw new Error(error.message)
18 | }
19 |
20 | return messageFileItems
21 | }
22 |
23 | export const createMessageFileItems = async (
24 | messageFileItems: TablesInsert<"message_file_items">[]
25 | ) => {
26 | const { data: createdMessageFileItems, error } = await supabase
27 | .from("message_file_items")
28 | .insert(messageFileItems)
29 | .select("*")
30 |
31 | if (!createdMessageFileItems) {
32 | throw new Error(error.message)
33 | }
34 |
35 | return createdMessageFileItems
36 | }
37 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/profile.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert, TablesUpdate } from "@/supabase/types"
3 |
4 | export const getProfileByUserId = async (userId: string) => {
5 | const { data: profile, error } = await supabase
6 | .from("profiles")
7 | .select("*")
8 | .eq("user_id", userId)
9 | .single()
10 |
11 | if (!profile) {
12 | throw new Error(error.message)
13 | }
14 |
15 | return profile
16 | }
17 |
18 | export const getProfilesByUserId = async (userId: string) => {
19 | const { data: profiles, error } = await supabase
20 | .from("profiles")
21 | .select("*")
22 | .eq("user_id", userId)
23 |
24 | if (!profiles) {
25 | throw new Error(error.message)
26 | }
27 |
28 | return profiles
29 | }
30 |
31 | export const createProfile = async (profile: TablesInsert<"profiles">) => {
32 | const { data: createdProfile, error } = await supabase
33 | .from("profiles")
34 | .insert([profile])
35 | .select("*")
36 | .single()
37 |
38 | if (error) {
39 | throw new Error(error.message)
40 | }
41 |
42 | return createdProfile
43 | }
44 |
45 | export const updateProfile = async (
46 | profileId: string,
47 | profile: TablesUpdate<"profiles">
48 | ) => {
49 | const { data: updatedProfile, error } = await supabase
50 | .from("profiles")
51 | .update(profile)
52 | .eq("id", profileId)
53 | .select("*")
54 | .single()
55 |
56 | if (error) {
57 | throw new Error(error.message)
58 | }
59 |
60 | return updatedProfile
61 | }
62 |
63 | export const deleteProfile = async (profileId: string) => {
64 | const { error } = await supabase.from("profiles").delete().eq("id", profileId)
65 |
66 | if (error) {
67 | throw new Error(error.message)
68 | }
69 |
70 | return true
71 | }
72 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/storage/assistant-images.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { Tables } from "@/supabase/types"
3 |
4 | export const uploadAssistantImage = async (
5 | assistant: Tables<"assistants">,
6 | image: File
7 | ) => {
8 | const bucket = "assistant_images"
9 |
10 | const imageSizeLimit = 6000000 // 6MB
11 |
12 | if (image.size > imageSizeLimit) {
13 | throw new Error(`Image must be less than ${imageSizeLimit / 1000000}MB`)
14 | }
15 |
16 | const currentPath = assistant.image_path
17 | let filePath = `${assistant.user_id}/${assistant.id}/${Date.now()}`
18 |
19 | if (currentPath.length > 0) {
20 | const { error: deleteError } = await supabase.storage
21 | .from(bucket)
22 | .remove([currentPath])
23 |
24 | if (deleteError) {
25 | throw new Error("Error deleting old image")
26 | }
27 | }
28 |
29 | const { error } = await supabase.storage
30 | .from(bucket)
31 | .upload(filePath, image, {
32 | upsert: true
33 | })
34 |
35 | if (error) {
36 | throw new Error("Error uploading image")
37 | }
38 |
39 | return filePath
40 | }
41 |
42 | export const getAssistantImageFromStorage = async (filePath: string) => {
43 | try {
44 | const { data, error } = await supabase.storage
45 | .from("assistant_images")
46 | .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs
47 |
48 | if (error) {
49 | throw new Error("Error downloading assistant image")
50 | }
51 |
52 | return data.signedUrl
53 | } catch (error) {
54 | console.error(error)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/storage/files.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { toast } from "sonner"
3 |
4 | export const uploadFile = async (
5 | file: File,
6 | payload: {
7 | name: string
8 | user_id: string
9 | file_id: string
10 | }
11 | ) => {
12 | const SIZE_LIMIT = parseInt(
13 | process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT || "10000000"
14 | )
15 |
16 | if (file.size > SIZE_LIMIT) {
17 | throw new Error(
18 | `File must be less than ${Math.floor(SIZE_LIMIT / 1000000)}MB`
19 | )
20 | }
21 |
22 | const filePath = `${payload.user_id}/${Buffer.from(payload.file_id).toString("base64")}`
23 |
24 | const { error } = await supabase.storage
25 | .from("files")
26 | .upload(filePath, file, {
27 | upsert: true
28 | })
29 |
30 | if (error) {
31 | throw new Error("Error uploading file")
32 | }
33 |
34 | return filePath
35 | }
36 |
37 | export const deleteFileFromStorage = async (filePath: string) => {
38 | const { error } = await supabase.storage.from("files").remove([filePath])
39 |
40 | if (error) {
41 | toast.error("Failed to remove file!")
42 | return
43 | }
44 | }
45 |
46 | export const getFileFromStorage = async (filePath: string) => {
47 | const { data, error } = await supabase.storage
48 | .from("files")
49 | .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs
50 |
51 | if (error) {
52 | console.error(`Error uploading file with path: ${filePath}`, error)
53 | throw new Error("Error downloading file")
54 | }
55 |
56 | return data.signedUrl
57 | }
58 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/storage/message-images.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 |
3 | export const uploadMessageImage = async (path: string, image: File) => {
4 | const bucket = "message_images"
5 |
6 | const imageSizeLimit = 6000000 // 6MB
7 |
8 | if (image.size > imageSizeLimit) {
9 | throw new Error(`Image must be less than ${imageSizeLimit / 1000000}MB`)
10 | }
11 |
12 | const { error } = await supabase.storage.from(bucket).upload(path, image, {
13 | upsert: true
14 | })
15 |
16 | if (error) {
17 | throw new Error("Error uploading image")
18 | }
19 |
20 | return path
21 | }
22 |
23 | export const getMessageImageFromStorage = async (filePath: string) => {
24 | const { data, error } = await supabase.storage
25 | .from("message_images")
26 | .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs
27 |
28 | if (error) {
29 | throw new Error("Error downloading message image")
30 | }
31 |
32 | return data.signedUrl
33 | }
34 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/storage/profile-images.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { Tables } from "@/supabase/types"
3 |
4 | export const uploadProfileImage = async (
5 | profile: Tables<"profiles">,
6 | image: File
7 | ) => {
8 | const bucket = "profile_images"
9 |
10 | const imageSizeLimit = 2000000 // 2MB
11 |
12 | if (image.size > imageSizeLimit) {
13 | throw new Error(`Image must be less than ${imageSizeLimit / 1000000}MB`)
14 | }
15 |
16 | const currentPath = profile.image_path
17 | let filePath = `${profile.user_id}/${Date.now()}`
18 |
19 | if (currentPath.length > 0) {
20 | const { error: deleteError } = await supabase.storage
21 | .from(bucket)
22 | .remove([currentPath])
23 |
24 | if (deleteError) {
25 | throw new Error("Error deleting old image")
26 | }
27 | }
28 |
29 | const { error } = await supabase.storage
30 | .from(bucket)
31 | .upload(filePath, image, {
32 | upsert: true
33 | })
34 |
35 | if (error) {
36 | throw new Error("Error uploading image")
37 | }
38 |
39 | const { data: getPublicUrlData } = supabase.storage
40 | .from(bucket)
41 | .getPublicUrl(filePath)
42 |
43 | return {
44 | path: filePath,
45 | url: getPublicUrlData.publicUrl
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/storage/workspace-images.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { Tables } from "@/supabase/types"
3 |
4 | export const uploadWorkspaceImage = async (
5 | workspace: Tables<"workspaces">,
6 | image: File
7 | ) => {
8 | const bucket = "workspace_images"
9 |
10 | const imageSizeLimit = 6000000 // 6MB
11 |
12 | if (image.size > imageSizeLimit) {
13 | throw new Error(`Image must be less than ${imageSizeLimit / 1000000}MB`)
14 | }
15 |
16 | const currentPath = workspace.image_path
17 | let filePath = `${workspace.user_id}/${workspace.id}/${Date.now()}`
18 |
19 | if (currentPath.length > 0) {
20 | const { error: deleteError } = await supabase.storage
21 | .from(bucket)
22 | .remove([currentPath])
23 |
24 | if (deleteError) {
25 | throw new Error("Error deleting old image")
26 | }
27 | }
28 |
29 | const { error } = await supabase.storage
30 | .from(bucket)
31 | .upload(filePath, image, {
32 | upsert: true
33 | })
34 |
35 | if (error) {
36 | throw new Error("Error uploading image")
37 | }
38 |
39 | return filePath
40 | }
41 |
42 | export const getWorkspaceImageFromStorage = async (filePath: string) => {
43 | try {
44 | const { data, error } = await supabase.storage
45 | .from("workspace_images")
46 | .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs
47 |
48 | if (error) {
49 | throw new Error("Error downloading workspace image")
50 | }
51 |
52 | return data.signedUrl
53 | } catch (error) {
54 | console.error(error)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/nyro-chatbot/db/workspaces.ts:
--------------------------------------------------------------------------------
1 | import { supabase } from "@/lib/supabase/browser-client"
2 | import { TablesInsert, TablesUpdate } from "@/supabase/types"
3 |
4 | export const getHomeWorkspaceByUserId = async (userId: string) => {
5 | const { data: homeWorkspace, error } = await supabase
6 | .from("workspaces")
7 | .select("*")
8 | .eq("user_id", userId)
9 | .eq("is_home", true)
10 | .single()
11 |
12 | if (!homeWorkspace) {
13 | throw new Error(error.message)
14 | }
15 |
16 | return homeWorkspace.id
17 | }
18 |
19 | export const getWorkspaceById = async (workspaceId: string) => {
20 | const { data: workspace, error } = await supabase
21 | .from("workspaces")
22 | .select("*")
23 | .eq("id", workspaceId)
24 | .single()
25 |
26 | if (!workspace) {
27 | throw new Error(error.message)
28 | }
29 |
30 | return workspace
31 | }
32 |
33 | export const getWorkspacesByUserId = async (userId: string) => {
34 | const { data: workspaces, error } = await supabase
35 | .from("workspaces")
36 | .select("*")
37 | .eq("user_id", userId)
38 | .order("created_at", { ascending: false })
39 |
40 | if (!workspaces) {
41 | throw new Error(error.message)
42 | }
43 |
44 | return workspaces
45 | }
46 |
47 | export const createWorkspace = async (
48 | workspace: TablesInsert<"workspaces">
49 | ) => {
50 | const { data: createdWorkspace, error } = await supabase
51 | .from("workspaces")
52 | .insert([workspace])
53 | .select("*")
54 | .single()
55 |
56 | if (error) {
57 | throw new Error(error.message)
58 | }
59 |
60 | return createdWorkspace
61 | }
62 |
63 | export const updateWorkspace = async (
64 | workspaceId: string,
65 | workspace: TablesUpdate<"workspaces">
66 | ) => {
67 | const { data: updatedWorkspace, error } = await supabase
68 | .from("workspaces")
69 | .update(workspace)
70 | .eq("id", workspaceId)
71 | .select("*")
72 | .single()
73 |
74 | if (error) {
75 | throw new Error(error.message)
76 | }
77 |
78 | return updatedWorkspace
79 | }
80 |
81 | export const deleteWorkspace = async (workspaceId: string) => {
82 | const { error } = await supabase
83 | .from("workspaces")
84 | .delete()
85 | .eq("id", workspaceId)
86 |
87 | if (error) {
88 | throw new Error(error.message)
89 | }
90 |
91 | return true
92 | }
93 |
--------------------------------------------------------------------------------
/nyro-chatbot/i18nConfig.js:
--------------------------------------------------------------------------------
1 | const i18nConfig = {
2 | defaultLocale: "en",
3 | locales: [
4 | "ar",
5 | "bn",
6 | "de",
7 | "en",
8 | "es",
9 | "fr",
10 | "he",
11 | "id",
12 | "it",
13 | "ja",
14 | "ko",
15 | "pt",
16 | "ru",
17 | "si",
18 | "sv",
19 | "te",
20 | "vi",
21 | "zh"
22 | ]
23 | }
24 |
25 | module.exports = i18nConfig
26 |
--------------------------------------------------------------------------------
/nyro-chatbot/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "jest"
2 | import nextJest from "next/jest.js"
3 |
4 | const createJestConfig = nextJest({
5 | // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
6 | dir: "./"
7 | })
8 |
9 | // Add any custom config to be passed to Jest
10 | const config: Config = {
11 | coverageProvider: "v8",
12 | testEnvironment: "jsdom"
13 | // Add more setup options before each test is run
14 | // setupFilesAfterEnv: ['/jest.setup.ts'],
15 | }
16 |
17 | // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
18 | export default createJestConfig(config)
19 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/blob-to-b64.ts:
--------------------------------------------------------------------------------
1 | export const convertBlobToBase64 = async (blob: Blob): Promise => {
2 | return new Promise((resolve, reject) => {
3 | const reader = new FileReader()
4 | reader.onloadend = () => resolve(reader.result as string)
5 | reader.onerror = reject
6 | reader.readAsDataURL(blob)
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/consume-stream.ts:
--------------------------------------------------------------------------------
1 | export async function consumeReadableStream(
2 | stream: ReadableStream,
3 | callback: (chunk: string) => void,
4 | signal: AbortSignal
5 | ): Promise {
6 | const reader = stream.getReader()
7 | const decoder = new TextDecoder()
8 |
9 | signal.addEventListener("abort", () => reader.cancel(), { once: true })
10 |
11 | try {
12 | while (true) {
13 | const { done, value } = await reader.read()
14 |
15 | if (done) {
16 | break
17 | }
18 |
19 | if (value) {
20 | callback(decoder.decode(value, { stream: true }))
21 | }
22 | }
23 | } catch (error) {
24 | if (signal.aborted) {
25 | console.error("Stream reading was aborted:", error)
26 | } else {
27 | console.error("Error consuming stream:", error)
28 | }
29 | } finally {
30 | reader.releaseLock()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/envs.ts:
--------------------------------------------------------------------------------
1 | import { EnvKey } from "@/types/key-type"
2 |
3 | // returns true if the key is found in the environment variables
4 | export function isUsingEnvironmentKey(type: EnvKey) {
5 | return Boolean(process.env[type])
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/export-old-data.ts:
--------------------------------------------------------------------------------
1 | export function exportLocalStorageAsJSON() {
2 | const data: { [key: string]: string | null } = {}
3 | for (let i = 0; i < localStorage.length; i++) {
4 | const key = localStorage.key(i)
5 | if (key !== null) {
6 | data[key] = localStorage.getItem(key)
7 | }
8 | }
9 |
10 | const json = JSON.stringify(data)
11 | const blob = new Blob([json], { type: "application/json" })
12 | const url = URL.createObjectURL(blob)
13 |
14 | const a = document.createElement("a")
15 | a.href = url
16 | a.download = "nyro-data.json"
17 | document.body.appendChild(a)
18 | a.click()
19 | document.body.removeChild(a)
20 | URL.revokeObjectURL(url)
21 | }
22 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/generate-local-embedding.ts:
--------------------------------------------------------------------------------
1 | import { pipeline } from "@xenova/transformers"
2 |
3 | export async function generateLocalEmbedding(content: string) {
4 | const generateEmbedding = await pipeline(
5 | "feature-extraction",
6 | "Xenova/all-MiniLM-L6-v2"
7 | )
8 |
9 | const output = await generateEmbedding(content, {
10 | pooling: "mean",
11 | normalize: true
12 | })
13 |
14 | const embedding = Array.from(output.data)
15 |
16 | return embedding
17 | }
18 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/hooks/use-copy-to-clipboard.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react"
2 |
3 | export interface useCopyToClipboardProps {
4 | timeout?: number
5 | }
6 |
7 | export function useCopyToClipboard({
8 | timeout = 2000
9 | }: useCopyToClipboardProps) {
10 | const [isCopied, setIsCopied] = useState(false)
11 |
12 | const copyToClipboard = (value: string) => {
13 | if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
14 | return
15 | }
16 |
17 | if (!value) {
18 | return
19 | }
20 |
21 | navigator.clipboard.writeText(value).then(() => {
22 | setIsCopied(true)
23 |
24 | setTimeout(() => {
25 | setIsCopied(false)
26 | }, timeout)
27 | })
28 | }
29 |
30 | return { isCopied, copyToClipboard }
31 | }
32 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/hooks/use-hotkey.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 |
3 | const useHotkey = (key: string, callback: () => void): void => {
4 | useEffect(() => {
5 | const handleKeyDown = (event: KeyboardEvent): void => {
6 | if (event.ctrlKey && event.shiftKey && event.key === key) {
7 | event.preventDefault()
8 | callback()
9 | }
10 | }
11 |
12 | window.addEventListener("keydown", handleKeyDown)
13 |
14 | return () => {
15 | window.removeEventListener("keydown", handleKeyDown)
16 | }
17 | }, [key, callback])
18 | }
19 |
20 | export default useHotkey
21 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/i18n.ts:
--------------------------------------------------------------------------------
1 | import i18nConfig from "@/i18nConfig"
2 | import { createInstance } from "i18next"
3 | import resourcesToBackend from "i18next-resources-to-backend"
4 | import { initReactI18next } from "react-i18next/initReactI18next"
5 |
6 | export default async function initTranslations(
7 | locale: any,
8 | namespaces: any,
9 | i18nInstance?: any,
10 | resources?: any
11 | ) {
12 | i18nInstance = i18nInstance || createInstance()
13 |
14 | i18nInstance.use(initReactI18next)
15 |
16 | if (!resources) {
17 | i18nInstance.use(
18 | resourcesToBackend(
19 | (language: string, namespace: string) =>
20 | import(`/public/locales/${language}/${namespace}.json`)
21 | )
22 | )
23 | }
24 |
25 | await i18nInstance.init({
26 | lng: locale,
27 | resources,
28 | fallbackLng: i18nConfig.defaultLocale,
29 | supportedLngs: i18nConfig.locales,
30 | defaultNS: namespaces[0],
31 | fallbackNS: namespaces[0],
32 | ns: namespaces,
33 | preload: resources ? [] : i18nConfig.locales
34 | })
35 |
36 | return {
37 | i18n: i18nInstance,
38 | resources: i18nInstance.services.resourceStore.data,
39 | t: i18nInstance.t
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/models/llm/google-llm-list.ts:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 |
3 | const GOOGLE_PLATORM_LINK = "https://ai.google.dev/"
4 |
5 | // Google Models (UPDATED 12/22/23) -----------------------------
6 |
7 | // Gemini 1.5 Flash
8 | const GEMINI_1_5_FLASH: LLM = {
9 | modelId: "gemini-1.5-flash",
10 | modelName: "Gemini 1.5 Flash",
11 | provider: "google",
12 | hostedId: "gemini-1.5-flash",
13 | platformLink: GOOGLE_PLATORM_LINK,
14 | imageInput: true
15 | }
16 |
17 | // Gemini 1.5 Pro (UPDATED 05/28/24)
18 | const GEMINI_1_5_PRO: LLM = {
19 | modelId: "gemini-1.5-pro-latest",
20 | modelName: "Gemini 1.5 Pro",
21 | provider: "google",
22 | hostedId: "gemini-1.5-pro-latest",
23 | platformLink: GOOGLE_PLATORM_LINK,
24 | imageInput: true
25 | }
26 |
27 | // Gemini Pro (UPDATED 12/22/23)
28 | const GEMINI_PRO: LLM = {
29 | modelId: "gemini-pro",
30 | modelName: "Gemini Pro",
31 | provider: "google",
32 | hostedId: "gemini-pro",
33 | platformLink: GOOGLE_PLATORM_LINK,
34 | imageInput: false
35 | }
36 |
37 | // Gemini Pro Vision (UPDATED 12/22/23)
38 | const GEMINI_PRO_VISION: LLM = {
39 | modelId: "gemini-pro-vision",
40 | modelName: "Gemini Pro Vision",
41 | provider: "google",
42 | hostedId: "gemini-pro-vision",
43 | platformLink: GOOGLE_PLATORM_LINK,
44 | imageInput: true
45 | }
46 |
47 | export const GOOGLE_LLM_LIST: LLM[] = [GEMINI_PRO, GEMINI_PRO_VISION, GEMINI_1_5_PRO, GEMINI_1_5_FLASH]
48 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/models/llm/groq-llm-list.ts:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 |
3 | const GROQ_PLATORM_LINK = "https://groq.com/"
4 |
5 | const LLaMA3_8B: LLM = {
6 | modelId: "llama3-8b-8192",
7 | modelName: "LLaMA3-8b-chat",
8 | provider: "groq",
9 | hostedId: "llama3-8b-8192",
10 | platformLink: GROQ_PLATORM_LINK,
11 | imageInput: false,
12 | pricing: {
13 | currency: "USD",
14 | unit: "1M tokens",
15 | inputCost: 0.05,
16 | outputCost: 0.1
17 | }
18 | }
19 |
20 | const LLaMA3_70B: LLM = {
21 | modelId: "llama3-70b-8192",
22 | modelName: "LLaMA3-70b-chat",
23 | provider: "groq",
24 | hostedId: "llama3-70b-4096",
25 | platformLink: GROQ_PLATORM_LINK,
26 | imageInput: false,
27 | pricing: {
28 | currency: "USD",
29 | unit: "1M tokens",
30 | inputCost: 0.59,
31 | outputCost: 0.79
32 | }
33 | }
34 |
35 | const MIXTRAL_8X7B: LLM = {
36 | modelId: "mixtral-8x7b-32768",
37 | modelName: "Mixtral-8x7b-Instruct-v0.1",
38 | provider: "groq",
39 | hostedId: "mixtral-8x7b-32768",
40 | platformLink: GROQ_PLATORM_LINK,
41 | imageInput: false,
42 | pricing: {
43 | currency: "USD",
44 | unit: "1M tokens",
45 | inputCost: 0.27,
46 | outputCost: 0.27
47 | }
48 | }
49 |
50 | const GEMMA_7B_IT: LLM = {
51 | modelId: "gemma-7b-it",
52 | modelName: "Gemma-7b-It",
53 | provider: "groq",
54 | hostedId: "gemma-7b-it",
55 | platformLink: GROQ_PLATORM_LINK,
56 | imageInput: false,
57 | pricing: {
58 | currency: "USD",
59 | unit: "1M tokens",
60 | inputCost: 0.15,
61 | outputCost: 0.15
62 | }
63 | }
64 |
65 | export const GROQ_LLM_LIST: LLM[] = [
66 | LLaMA3_8B,
67 | LLaMA3_70B,
68 | MIXTRAL_8X7B,
69 | GEMMA_7B_IT
70 | ]
71 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/models/llm/llm-list.ts:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 | import { ANTHROPIC_LLM_LIST } from "./anthropic-llm-list"
3 | import { GOOGLE_LLM_LIST } from "./google-llm-list"
4 | import { MISTRAL_LLM_LIST } from "./mistral-llm-list"
5 | import { GROQ_LLM_LIST } from "./groq-llm-list"
6 | import { OPENAI_LLM_LIST } from "./openai-llm-list"
7 | import { PERPLEXITY_LLM_LIST } from "./perplexity-llm-list"
8 |
9 | export const LLM_LIST: LLM[] = [
10 | ...OPENAI_LLM_LIST,
11 | ...GOOGLE_LLM_LIST,
12 | ...MISTRAL_LLM_LIST,
13 | ...GROQ_LLM_LIST,
14 | ...PERPLEXITY_LLM_LIST,
15 | ...ANTHROPIC_LLM_LIST
16 | ]
17 |
18 | export const LLM_LIST_MAP: Record = {
19 | openai: OPENAI_LLM_LIST,
20 | azure: OPENAI_LLM_LIST,
21 | google: GOOGLE_LLM_LIST,
22 | mistral: MISTRAL_LLM_LIST,
23 | groq: GROQ_LLM_LIST,
24 | perplexity: PERPLEXITY_LLM_LIST,
25 | anthropic: ANTHROPIC_LLM_LIST
26 | }
27 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/models/llm/mistral-llm-list.ts:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 |
3 | const MISTRAL_PLATORM_LINK = "https://docs.mistral.ai/"
4 |
5 | // Mistral Models (UPDATED 12/21/23) -----------------------------
6 |
7 | // Mistral 7B (UPDATED 12/21/23)
8 | const MISTRAL_7B: LLM = {
9 | modelId: "mistral-tiny",
10 | modelName: "Mistral Tiny",
11 | provider: "mistral",
12 | hostedId: "mistral-tiny",
13 | platformLink: MISTRAL_PLATORM_LINK,
14 | imageInput: false
15 | }
16 |
17 | // Mixtral (UPDATED 12/21/23)
18 | const MIXTRAL: LLM = {
19 | modelId: "mistral-small-latest",
20 | modelName: "Mistral Small",
21 | provider: "mistral",
22 | hostedId: "mistral-small-latest",
23 | platformLink: MISTRAL_PLATORM_LINK,
24 | imageInput: false,
25 | pricing: {
26 | currency: "USD",
27 | unit: "1M tokens",
28 | inputCost: 2,
29 | outputCost: 6
30 | }
31 | }
32 |
33 | // Mistral Medium (UPDATED 12/21/23)
34 | const MISTRAL_MEDIUM: LLM = {
35 | modelId: "mistral-medium-latest",
36 | modelName: "Mistral Medium",
37 | provider: "mistral",
38 | hostedId: "mistral-medium-latest",
39 | platformLink: MISTRAL_PLATORM_LINK,
40 | imageInput: false,
41 | pricing: {
42 | currency: "USD",
43 | unit: "1M tokens",
44 | inputCost: 2.7,
45 | outputCost: 8.1
46 | }
47 | }
48 |
49 | // Mistral Large (UPDATED 03/05/24)
50 | const MISTRAL_LARGE: LLM = {
51 | modelId: "mistral-large-latest",
52 | modelName: "Mistral Large",
53 | provider: "mistral",
54 | hostedId: "mistral-large-latest",
55 | platformLink: MISTRAL_PLATORM_LINK,
56 | imageInput: false,
57 | pricing: {
58 | currency: "USD",
59 | unit: "1M tokens",
60 | inputCost: 8,
61 | outputCost: 24
62 | }
63 | }
64 |
65 | export const MISTRAL_LLM_LIST: LLM[] = [
66 | MISTRAL_7B,
67 | MIXTRAL,
68 | MISTRAL_MEDIUM,
69 | MISTRAL_LARGE
70 | ]
71 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/models/llm/openai-llm-list.ts:
--------------------------------------------------------------------------------
1 | import { LLM } from "@/types"
2 |
3 | const OPENAI_PLATORM_LINK = "https://platform.openai.com/docs/overview"
4 |
5 | // OpenAI Models (UPDATED 1/25/24) -----------------------------
6 | const GPT4o: LLM = {
7 | modelId: "gpt-4o",
8 | modelName: "GPT-4o",
9 | provider: "openai",
10 | hostedId: "gpt-4o",
11 | platformLink: OPENAI_PLATORM_LINK,
12 | imageInput: true,
13 | pricing: {
14 | currency: "USD",
15 | unit: "1M tokens",
16 | inputCost: 5,
17 | outputCost: 15
18 | }
19 | }
20 |
21 | // GPT-4 Turbo (UPDATED 1/25/24)
22 | const GPT4Turbo: LLM = {
23 | modelId: "gpt-4-turbo-preview",
24 | modelName: "GPT-4 Turbo",
25 | provider: "openai",
26 | hostedId: "gpt-4-turbo-preview",
27 | platformLink: OPENAI_PLATORM_LINK,
28 | imageInput: true,
29 | pricing: {
30 | currency: "USD",
31 | unit: "1M tokens",
32 | inputCost: 10,
33 | outputCost: 30
34 | }
35 | }
36 |
37 | // GPT-4 Vision (UPDATED 12/18/23)
38 | const GPT4Vision: LLM = {
39 | modelId: "gpt-4-vision-preview",
40 | modelName: "GPT-4 Vision",
41 | provider: "openai",
42 | hostedId: "gpt-4-vision-preview",
43 | platformLink: OPENAI_PLATORM_LINK,
44 | imageInput: true,
45 | pricing: {
46 | currency: "USD",
47 | unit: "1M tokens",
48 | inputCost: 10
49 | }
50 | }
51 |
52 | // GPT-4 (UPDATED 1/29/24)
53 | const GPT4: LLM = {
54 | modelId: "gpt-4",
55 | modelName: "GPT-4",
56 | provider: "openai",
57 | hostedId: "gpt-4",
58 | platformLink: OPENAI_PLATORM_LINK,
59 | imageInput: false,
60 | pricing: {
61 | currency: "USD",
62 | unit: "1M tokens",
63 | inputCost: 30,
64 | outputCost: 60
65 | }
66 | }
67 |
68 | // GPT-3.5 Turbo (UPDATED 1/25/24)
69 | const GPT3_5Turbo: LLM = {
70 | modelId: "gpt-3.5-turbo",
71 | modelName: "GPT-3.5 Turbo",
72 | provider: "openai",
73 | hostedId: "gpt-3.5-turbo",
74 | platformLink: OPENAI_PLATORM_LINK,
75 | imageInput: false,
76 | pricing: {
77 | currency: "USD",
78 | unit: "1M tokens",
79 | inputCost: 0.5,
80 | outputCost: 1.5
81 | }
82 | }
83 |
84 | export const OPENAI_LLM_LIST: LLM[] = [
85 | GPT4o,
86 | GPT4Turbo,
87 | GPT4Vision,
88 | GPT4,
89 | GPT3_5Turbo
90 | ]
91 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/csv.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { CSVLoader } from "langchain/document_loaders/fs/csv"
4 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
5 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
6 |
7 | export const processCSV = async (csv: Blob): Promise => {
8 | const loader = new CSVLoader(csv)
9 | const docs = await loader.load()
10 | let completeText = docs.map(doc => doc.pageContent).join("\n\n")
11 |
12 | const splitter = new RecursiveCharacterTextSplitter({
13 | chunkSize: CHUNK_SIZE,
14 | chunkOverlap: CHUNK_OVERLAP,
15 | separators: ["\n\n"]
16 | })
17 | const splitDocs = await splitter.createDocuments([completeText])
18 |
19 | let chunks: FileItemChunk[] = []
20 |
21 | for (let i = 0; i < splitDocs.length; i++) {
22 | const doc = splitDocs[i]
23 |
24 | chunks.push({
25 | content: doc.pageContent,
26 | tokens: encode(doc.pageContent).length
27 | })
28 | }
29 |
30 | return chunks
31 | }
32 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/docx.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
4 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
5 |
6 | export const processDocX = async (text: string): Promise => {
7 | const splitter = new RecursiveCharacterTextSplitter({
8 | chunkSize: CHUNK_SIZE,
9 | chunkOverlap: CHUNK_OVERLAP
10 | })
11 | const splitDocs = await splitter.createDocuments([text])
12 |
13 | let chunks: FileItemChunk[] = []
14 |
15 | for (let i = 0; i < splitDocs.length; i++) {
16 | const doc = splitDocs[i]
17 |
18 | chunks.push({
19 | content: doc.pageContent,
20 | tokens: encode(doc.pageContent).length
21 | })
22 | }
23 |
24 | return chunks
25 | }
26 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./csv"
2 | export * from "./docx"
3 | export * from "./json"
4 | export * from "./md"
5 | export * from "./pdf"
6 | export * from "./txt"
7 |
8 | export const CHUNK_SIZE = 4000
9 | export const CHUNK_OVERLAP = 200
10 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/json.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { JSONLoader } from "langchain/document_loaders/fs/json"
4 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
5 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
6 |
7 | export const processJSON = async (json: Blob): Promise => {
8 | const loader = new JSONLoader(json)
9 | const docs = await loader.load()
10 | let completeText = docs.map(doc => doc.pageContent).join(" ")
11 |
12 | const splitter = new RecursiveCharacterTextSplitter({
13 | chunkSize: CHUNK_SIZE,
14 | chunkOverlap: CHUNK_OVERLAP
15 | })
16 | const splitDocs = await splitter.createDocuments([completeText])
17 |
18 | let chunks: FileItemChunk[] = []
19 |
20 | splitDocs.forEach(doc => {
21 | const docTokens = encode(doc.pageContent).length
22 | })
23 |
24 | for (let i = 0; i < splitDocs.length; i++) {
25 | const doc = splitDocs[i]
26 |
27 | chunks.push({
28 | content: doc.pageContent,
29 | tokens: encode(doc.pageContent).length
30 | })
31 | }
32 |
33 | return chunks
34 | }
35 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/md.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
4 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
5 |
6 | export const processMarkdown = async (
7 | markdown: Blob
8 | ): Promise => {
9 | const fileBuffer = Buffer.from(await markdown.arrayBuffer())
10 | const textDecoder = new TextDecoder("utf-8")
11 | const textContent = textDecoder.decode(fileBuffer)
12 |
13 | const splitter = RecursiveCharacterTextSplitter.fromLanguage("markdown", {
14 | chunkSize: CHUNK_SIZE,
15 | chunkOverlap: CHUNK_OVERLAP
16 | })
17 |
18 | const splitDocs = await splitter.createDocuments([textContent])
19 |
20 | let chunks: FileItemChunk[] = []
21 |
22 | for (let i = 0; i < splitDocs.length; i++) {
23 | const doc = splitDocs[i]
24 |
25 | chunks.push({
26 | content: doc.pageContent,
27 | tokens: encode(doc.pageContent).length
28 | })
29 | }
30 |
31 | return chunks
32 | }
33 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/pdf.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { PDFLoader } from "langchain/document_loaders/fs/pdf"
4 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
5 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
6 |
7 | export const processPdf = async (pdf: Blob): Promise => {
8 | const loader = new PDFLoader(pdf)
9 | const docs = await loader.load()
10 | let completeText = docs.map(doc => doc.pageContent).join(" ")
11 |
12 | const splitter = new RecursiveCharacterTextSplitter({
13 | chunkSize: CHUNK_SIZE,
14 | chunkOverlap: CHUNK_OVERLAP
15 | })
16 | const splitDocs = await splitter.createDocuments([completeText])
17 |
18 | let chunks: FileItemChunk[] = []
19 |
20 | for (let i = 0; i < splitDocs.length; i++) {
21 | const doc = splitDocs[i]
22 |
23 | chunks.push({
24 | content: doc.pageContent,
25 | tokens: encode(doc.pageContent).length
26 | })
27 | }
28 |
29 | return chunks
30 | }
31 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/retrieval/processing/txt.ts:
--------------------------------------------------------------------------------
1 | import { FileItemChunk } from "@/types"
2 | import { encode } from "gpt-tokenizer"
3 | import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
4 | import { CHUNK_OVERLAP, CHUNK_SIZE } from "."
5 |
6 | export const processTxt = async (txt: Blob): Promise => {
7 | const fileBuffer = Buffer.from(await txt.arrayBuffer())
8 | const textDecoder = new TextDecoder("utf-8")
9 | const textContent = textDecoder.decode(fileBuffer)
10 |
11 | const splitter = new RecursiveCharacterTextSplitter({
12 | chunkSize: CHUNK_SIZE,
13 | chunkOverlap: CHUNK_OVERLAP
14 | })
15 | const splitDocs = await splitter.createDocuments([textContent])
16 |
17 | let chunks: FileItemChunk[] = []
18 |
19 | for (let i = 0; i < splitDocs.length; i++) {
20 | const doc = splitDocs[i]
21 |
22 | chunks.push({
23 | content: doc.pageContent,
24 | tokens: encode(doc.pageContent).length
25 | })
26 | }
27 |
28 | return chunks
29 | }
30 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/server/server-chat-helpers.ts:
--------------------------------------------------------------------------------
1 | import { Database, Tables } from "@/supabase/types"
2 | import { VALID_ENV_KEYS } from "@/types/valid-keys"
3 | import { createServerClient } from "@supabase/ssr"
4 | import { cookies } from "next/headers"
5 |
6 | export async function getServerProfile() {
7 | const cookieStore = cookies()
8 | const supabase = createServerClient(
9 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
10 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
11 | {
12 | cookies: {
13 | get(name: string) {
14 | return cookieStore.get(name)?.value
15 | }
16 | }
17 | }
18 | )
19 |
20 | const user = (await supabase.auth.getUser()).data.user
21 | if (!user) {
22 | throw new Error("User not found")
23 | }
24 |
25 | const { data: profile } = await supabase
26 | .from("profiles")
27 | .select("*")
28 | .eq("user_id", user.id)
29 | .single()
30 |
31 | if (!profile) {
32 | throw new Error("Profile not found")
33 | }
34 |
35 | const profileWithKeys = addApiKeysToProfile(profile)
36 |
37 | return profileWithKeys
38 | }
39 |
40 | function addApiKeysToProfile(profile: Tables<"profiles">) {
41 | const apiKeys = {
42 | [VALID_ENV_KEYS.OPENAI_API_KEY]: "openai_api_key",
43 | [VALID_ENV_KEYS.ANTHROPIC_API_KEY]: "anthropic_api_key",
44 | [VALID_ENV_KEYS.GOOGLE_GEMINI_API_KEY]: "google_gemini_api_key",
45 | [VALID_ENV_KEYS.MISTRAL_API_KEY]: "mistral_api_key",
46 | [VALID_ENV_KEYS.GROQ_API_KEY]: "groq_api_key",
47 | [VALID_ENV_KEYS.PERPLEXITY_API_KEY]: "perplexity_api_key",
48 | [VALID_ENV_KEYS.AZURE_OPENAI_API_KEY]: "azure_openai_api_key",
49 | [VALID_ENV_KEYS.OPENROUTER_API_KEY]: "openrouter_api_key",
50 |
51 | [VALID_ENV_KEYS.OPENAI_ORGANIZATION_ID]: "openai_organization_id",
52 |
53 | [VALID_ENV_KEYS.AZURE_OPENAI_ENDPOINT]: "azure_openai_endpoint",
54 | [VALID_ENV_KEYS.AZURE_GPT_35_TURBO_NAME]: "azure_openai_35_turbo_id",
55 | [VALID_ENV_KEYS.AZURE_GPT_45_VISION_NAME]: "azure_openai_45_vision_id",
56 | [VALID_ENV_KEYS.AZURE_GPT_45_TURBO_NAME]: "azure_openai_45_turbo_id",
57 | [VALID_ENV_KEYS.AZURE_EMBEDDINGS_NAME]: "azure_openai_embeddings_id"
58 | }
59 |
60 | for (const [envKey, profileKey] of Object.entries(apiKeys)) {
61 | if (process.env[envKey]) {
62 | ;(profile as any)[profileKey] = process.env[envKey]
63 | }
64 | }
65 |
66 | return profile
67 | }
68 |
69 | export function checkApiKey(apiKey: string | null, keyName: string) {
70 | if (apiKey === null || apiKey === "") {
71 | throw new Error(`${keyName} API Key not found`)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/server/server-utils.ts:
--------------------------------------------------------------------------------
1 | export function createResponse(data: object, status: number): Response {
2 | return new Response(JSON.stringify(data), {
3 | status,
4 | headers: {
5 | "Content-Type": "application/json"
6 | }
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/supabase/browser-client.ts:
--------------------------------------------------------------------------------
1 | import { Database } from "@/supabase/types"
2 | import { createBrowserClient } from "@supabase/ssr"
3 |
4 | export const supabase = createBrowserClient(
5 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
6 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
7 | )
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/supabase/client.ts:
--------------------------------------------------------------------------------
1 | import { createBrowserClient } from "@supabase/ssr"
2 |
3 | export const createClient = () =>
4 | createBrowserClient(
5 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
6 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
7 | )
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/supabase/middleware.ts:
--------------------------------------------------------------------------------
1 | import { createServerClient, type CookieOptions } from "@supabase/ssr"
2 | import { NextResponse, type NextRequest } from "next/server"
3 |
4 | export const createClient = (request: NextRequest) => {
5 | // Create an unmodified response
6 | let response = NextResponse.next({
7 | request: {
8 | headers: request.headers
9 | }
10 | })
11 |
12 | const supabase = createServerClient(
13 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
14 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
15 | {
16 | cookies: {
17 | get(name: string) {
18 | return request.cookies.get(name)?.value
19 | },
20 | set(name: string, value: string, options: CookieOptions) {
21 | // If the cookie is updated, update the cookies for the request and response
22 | request.cookies.set({
23 | name,
24 | value,
25 | ...options
26 | })
27 | response = NextResponse.next({
28 | request: {
29 | headers: request.headers
30 | }
31 | })
32 | response.cookies.set({
33 | name,
34 | value,
35 | ...options
36 | })
37 | },
38 | remove(name: string, options: CookieOptions) {
39 | // If the cookie is removed, update the cookies for the request and response
40 | request.cookies.set({
41 | name,
42 | value: "",
43 | ...options
44 | })
45 | response = NextResponse.next({
46 | request: {
47 | headers: request.headers
48 | }
49 | })
50 | response.cookies.set({
51 | name,
52 | value: "",
53 | ...options
54 | })
55 | }
56 | }
57 | }
58 | )
59 |
60 | return { supabase, response }
61 | }
62 |
--------------------------------------------------------------------------------
/nyro-chatbot/lib/supabase/server.ts:
--------------------------------------------------------------------------------
1 | import { createServerClient, type CookieOptions } from "@supabase/ssr"
2 | import { cookies } from "next/headers"
3 |
4 | export const createClient = (cookieStore: ReturnType) => {
5 | return createServerClient(
6 | process.env.NEXT_PUBLIC_SUPABASE_URL!,
7 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
8 | {
9 | cookies: {
10 | get(name: string) {
11 | return cookieStore.get(name)?.value
12 | },
13 | set(name: string, value: string, options: CookieOptions) {
14 | try {
15 | cookieStore.set({ name, value, ...options })
16 | } catch (error) {
17 | // The `set` method was called from a Server Component.
18 | // This can be ignored if you have middleware refreshing
19 | // user sessions.
20 | }
21 | },
22 | remove(name: string, options: CookieOptions) {
23 | try {
24 | cookieStore.set({ name, value: "", ...options })
25 | } catch (error) {
26 | // The `delete` method was called from a Server Component.
27 | // This can be ignored if you have middleware refreshing
28 | // user sessions.
29 | }
30 | }
31 | }
32 | }
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/nyro-chatbot/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 function formatDate(input: string | number | Date): string {
9 | const date = new Date(input)
10 | return date.toLocaleDateString("en-US", {
11 | month: "long",
12 | day: "numeric",
13 | year: "numeric"
14 | })
15 | }
16 |
17 | export function getMediaTypeFromDataURL(dataURL: string): string | null {
18 | const matches = dataURL.match(/^data:([A-Za-z-+\/]+);base64/)
19 | return matches ? matches[1] : null
20 | }
21 |
22 | export function getBase64FromDataURL(dataURL: string): string | null {
23 | const matches = dataURL.match(/^data:[A-Za-z-+\/]+;base64,(.*)$/)
24 | return matches ? matches[1] : null
25 | }
26 |
--------------------------------------------------------------------------------
/nyro-chatbot/middleware.ts:
--------------------------------------------------------------------------------
1 | import { createClient } from "@/lib/supabase/middleware"
2 | import { i18nRouter } from "next-i18n-router"
3 | import { NextResponse, type NextRequest } from "next/server"
4 | import i18nConfig from "./i18nConfig"
5 |
6 | export async function middleware(request: NextRequest) {
7 | const i18nResult = i18nRouter(request, i18nConfig)
8 | if (i18nResult) return i18nResult
9 |
10 | try {
11 | const { supabase, response } = createClient(request)
12 |
13 | const session = await supabase.auth.getSession()
14 |
15 | const redirectToChat = session && request.nextUrl.pathname === "/"
16 |
17 | if (redirectToChat) {
18 | const { data: homeWorkspace, error } = await supabase
19 | .from("workspaces")
20 | .select("*")
21 | .eq("user_id", session.data.session?.user.id)
22 | .eq("is_home", true)
23 | .single()
24 |
25 | if (!homeWorkspace) {
26 | throw new Error(error?.message)
27 | }
28 |
29 | return NextResponse.redirect(
30 | new URL(`/${homeWorkspace.id}/chat`, request.url)
31 | )
32 | }
33 |
34 | return response
35 | } catch (e) {
36 | return NextResponse.next({
37 | request: {
38 | headers: request.headers
39 | }
40 | })
41 | }
42 | }
43 |
44 | export const config = {
45 | matcher: "/((?!api|static|.*\\..*|_next|auth).*)"
46 | }
47 |
--------------------------------------------------------------------------------
/nyro-chatbot/next.config.js:
--------------------------------------------------------------------------------
1 | const withBundleAnalyzer = require("@next/bundle-analyzer")({
2 | enabled: process.env.ANALYZE === "true"
3 | })
4 |
5 | const withPWA = require("next-pwa")({
6 | dest: "public"
7 | })
8 |
9 | module.exports = withBundleAnalyzer(
10 | withPWA({
11 | reactStrictMode: true,
12 | images: {
13 | remotePatterns: [
14 | {
15 | protocol: "http",
16 | hostname: "localhost"
17 | },
18 | {
19 | protocol: "http",
20 | hostname: "127.0.0.1"
21 | },
22 | {
23 | protocol: "https",
24 | hostname: "**"
25 | }
26 | ]
27 | },
28 | experimental: {
29 | serverComponentsExternalPackages: ["sharp", "onnxruntime-node"]
30 | }
31 | })
32 | )
33 |
--------------------------------------------------------------------------------
/nyro-chatbot/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/prettier.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('prettier').Config} */
2 | module.exports = {
3 | endOfLine: 'lf',
4 | semi: false,
5 | useTabs: false,
6 | singleQuote: false,
7 | arrowParens: 'avoid',
8 | tabWidth: 2,
9 | trailingComma: 'none',
10 | importOrder: [
11 | '^.+\\.scss$',
12 | '^.+\\.css$',
13 | '^(react/(.*)$)|^(react$)',
14 | '^(next/(.*)$)|^(next$)',
15 | '',
16 | '',
17 | '^types$',
18 | '^@/types/(.*)$',
19 | '^@/config/(.*)$',
20 | '^@/lib/(.*)$',
21 | '^@/hooks/(.*)$',
22 | '^@/components/ui/(.*)$',
23 | '^@/components/(.*)$',
24 | '^@/registry/(.*)$',
25 | '^@/styles/(.*)$',
26 | '^@/app/(.*)$',
27 | '',
28 | '^[./]'
29 | ],
30 | importOrderSeparation: false,
31 | importOrderSortSpecifiers: true,
32 | importOrderBuiltinModulesToTop: true,
33 | importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
34 | importOrderMergeDuplicateImports: true,
35 | importOrderCombineTypeAndValueImports: true
36 | }
37 |
--------------------------------------------------------------------------------
/nyro-chatbot/public/DARK_BRAND_LOGO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/DARK_BRAND_LOGO.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/LIGHT_BRAND_LOGO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/LIGHT_BRAND_LOGO.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/favicon.ico
--------------------------------------------------------------------------------
/nyro-chatbot/public/favicon2.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/favicon2.ico
--------------------------------------------------------------------------------
/nyro-chatbot/public/icon-96x39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/icon-96x39.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/locales/de/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "Ask anything. Type \"/\" for prompts, \"@\" for files, and \"#\" for tools.": "Ask anything. Type \"/\" for prompts, \"@\" for files, and \"#\" for tools.",
3 | "Quick Settings": "Quick Settings"
4 | }
5 |
--------------------------------------------------------------------------------
/nyro-chatbot/public/locales/en/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "Ask anything. Type \"/\" for prompts, \"@\" for files, and \"#\" for tools.": "Ask anything. Type \"/\" for prompts, \"@\" for files, and \"#\" for tools."
3 | }
4 |
--------------------------------------------------------------------------------
/nyro-chatbot/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Nyro",
3 | "name": "Nyro",
4 | "icons": [
5 | {
6 | "src": "/icon-96x39.png",
7 | "type": "image/ico",
8 | "sizes": "96x39"
9 | }
10 | ],
11 | "start_url": "/",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#000000"
15 | }
--------------------------------------------------------------------------------
/nyro-chatbot/public/providers/groq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/providers/groq.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/providers/meta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/providers/meta.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/providers/mistral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/providers/mistral.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/providers/perplexity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/providers/perplexity.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/readme/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-chatbot/public/readme/screenshot.png
--------------------------------------------------------------------------------
/nyro-chatbot/public/worker-development.js:
--------------------------------------------------------------------------------
1 | /******/ (() => { // webpackBootstrap
2 | var __webpack_exports__ = {};
3 | self.__WB_DISABLE_DEV_LOGS = true;
4 | /******/ })()
5 | ;
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/.gitignore:
--------------------------------------------------------------------------------
1 | # Supabase
2 | .branches
3 | .temp
4 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240108234543_add_folders.sql:
--------------------------------------------------------------------------------
1 | --------------- FOLDERS ---------------
2 |
3 | -- TABLE --
4 |
5 | CREATE TABLE IF NOT EXISTS folders (
6 | -- ID
7 | id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
8 |
9 | -- RELATIONSHIPS
10 | user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
11 | workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
12 |
13 | -- METADATA
14 | created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
15 | updated_at TIMESTAMPTZ,
16 |
17 | -- REQUIRED
18 | name TEXT NOT NULL,
19 | description TEXT NOT NULL,
20 | type TEXT NOT NULL
21 | );
22 |
23 | -- INDEXES --
24 |
25 | CREATE INDEX folders_user_id_idx ON folders(user_id);
26 | CREATE INDEX folders_workspace_id_idx ON folders(workspace_id);
27 |
28 | -- RLS --
29 |
30 | ALTER TABLE folders ENABLE ROW LEVEL SECURITY;
31 |
32 | CREATE POLICY "Allow full access to own folders"
33 | ON folders
34 | USING (user_id = auth.uid())
35 | WITH CHECK (user_id = auth.uid());
36 |
37 | -- TRIGGERS --
38 |
39 | CREATE TRIGGER update_folders_updated_at
40 | BEFORE UPDATE ON folders
41 | FOR EACH ROW
42 | EXECUTE PROCEDURE update_updated_at_column();
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240115135033_add_openrouter.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE profiles
2 | ADD COLUMN openrouter_api_key TEXT CHECK (char_length(openrouter_api_key) <= 1000);
3 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240115172125_add_assistant_tools.sql:
--------------------------------------------------------------------------------
1 | --------------- ASSISTANT TOOLS ---------------
2 |
3 | -- TABLE --
4 |
5 | CREATE TABLE IF NOT EXISTS assistant_tools (
6 | -- REQUIRED RELATIONSHIPS
7 | user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
8 | assistant_id UUID NOT NULL REFERENCES assistants(id) ON DELETE CASCADE,
9 | tool_id UUID NOT NULL REFERENCES tools(id) ON DELETE CASCADE,
10 |
11 | PRIMARY KEY(assistant_id, tool_id),
12 |
13 | -- METADATA
14 | created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
15 | updated_at TIMESTAMPTZ
16 | );
17 |
18 | -- INDEXES --
19 |
20 | CREATE INDEX assistant_tools_user_id_idx ON assistant_tools(user_id);
21 | CREATE INDEX assistant_tools_assistant_id_idx ON assistant_tools(assistant_id);
22 | CREATE INDEX assistant_tools_tool_id_idx ON assistant_tools(tool_id);
23 |
24 | -- RLS --
25 |
26 | ALTER TABLE assistant_tools ENABLE ROW LEVEL SECURITY;
27 |
28 | CREATE POLICY "Allow full access to own assistant_tools"
29 | ON assistant_tools
30 | USING (user_id = auth.uid())
31 | WITH CHECK (user_id = auth.uid());
32 |
33 | -- TRIGGERS --
34 |
35 | CREATE TRIGGER update_assistant_tools_updated_at
36 | BEFORE UPDATE ON assistant_tools
37 | FOR EACH ROW
38 | EXECUTE PROCEDURE update_updated_at_column();
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240118224049_add_azure_embeddings.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE profiles
2 | ADD COLUMN azure_openai_embeddings_id TEXT CHECK (char_length(azure_openai_embeddings_id) <= 1000);
3 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240124234424_tool_improvements.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE tools
2 | ADD COLUMN custom_headers JSONB NOT NULL DEFAULT '{}',
3 | ADD COLUMN request_in_body BOOLEAN NOT NULL DEFAULT TRUE,
4 | ALTER COLUMN schema SET DEFAULT '{}';
5 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240129232644_add_workspace_images.sql:
--------------------------------------------------------------------------------
1 | -- ALTER TABLE --
2 |
3 | ALTER TABLE workspaces
4 | ADD COLUMN image_path TEXT DEFAULT '' NOT NULL CHECK (char_length(image_path) <= 1000);
5 |
6 | -- STORAGE --
7 |
8 | INSERT INTO storage.buckets (id, name, public) VALUES ('workspace_images', 'workspace_images', false);
9 |
10 | -- FUNCTIONS --
11 |
12 | CREATE OR REPLACE FUNCTION delete_old_workspace_image()
13 | RETURNS TRIGGER
14 | LANGUAGE 'plpgsql'
15 | SECURITY DEFINER
16 | AS $$
17 | DECLARE
18 | status INT;
19 | content TEXT;
20 | BEGIN
21 | IF TG_OP = 'DELETE' THEN
22 | SELECT
23 | INTO status, content
24 | result.status, result.content
25 | FROM public.delete_storage_object_from_bucket('workspace_images', OLD.image_path) AS result;
26 | IF status <> 200 THEN
27 | RAISE WARNING 'Could not delete workspace image: % %', status, content;
28 | END IF;
29 | END IF;
30 | IF TG_OP = 'DELETE' THEN
31 | RETURN OLD;
32 | END IF;
33 | RETURN NEW;
34 | END;
35 | $$;
36 |
37 | -- TRIGGERS --
38 |
39 | CREATE TRIGGER delete_old_workspace_image
40 | AFTER DELETE ON workspaces
41 | FOR EACH ROW
42 | EXECUTE PROCEDURE delete_old_workspace_image();
43 |
44 | -- POLICIES --
45 |
46 | CREATE OR REPLACE FUNCTION public.non_private_workspace_exists(p_name text)
47 | RETURNS boolean
48 | LANGUAGE sql
49 | SECURITY DEFINER
50 | AS $$
51 | SELECT EXISTS (
52 | SELECT 1
53 | FROM workspaces
54 | WHERE (id::text = (storage.filename(p_name))) AND sharing <> 'private'
55 | );
56 | $$;
57 |
58 | CREATE POLICY "Allow public read access on non-private workspace images"
59 | ON storage.objects FOR SELECT TO public
60 | USING (bucket_id = 'workspace_images' AND public.non_private_workspace_exists(name));
61 |
62 | CREATE POLICY "Allow insert access to own workspace images"
63 | ON storage.objects FOR INSERT TO authenticated
64 | WITH CHECK (bucket_id = 'workspace_images' AND (storage.foldername(name))[1] = auth.uid()::text);
65 |
66 | CREATE POLICY "Allow update access to own workspace images"
67 | ON storage.objects FOR UPDATE TO authenticated
68 | USING (bucket_id = 'workspace_images' AND (storage.foldername(name))[1] = auth.uid()::text);
69 |
70 | CREATE POLICY "Allow delete access to own workspace images"
71 | ON storage.objects FOR DELETE TO authenticated
72 | USING (bucket_id = 'workspace_images' AND (storage.foldername(name))[1] = auth.uid()::text);
73 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240212063532_add_at_assistants.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE messages ADD COLUMN assistant_id UUID REFERENCES assistants(id) ON DELETE CASCADE DEFAULT NULL;
2 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240213040255_remove_request_in_body_from_tools.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE tools
2 | DROP COLUMN request_in_body;
3 |
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240213085646_add_context_length_to_custom_models.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE models ADD COLUMN context_length INT NOT NULL DEFAULT 4096;
--------------------------------------------------------------------------------
/nyro-chatbot/supabase/migrations/20240302004845_add_groq.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE profiles ADD COLUMN groq_api_key TEXT CHECK (char_length(groq_api_key) <= 1000);
2 |
--------------------------------------------------------------------------------
/nyro-chatbot/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ['class'],
4 | content: [
5 | './pages/**/*.{ts,tsx}',
6 | './components/**/*.{ts,tsx}',
7 | './app/**/*.{ts,tsx}',
8 | './src/**/*.{ts,tsx}'
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: '2rem',
14 | screens: {
15 | '2xl': '1400px'
16 | }
17 | },
18 | extend: {
19 | colors: {
20 | navbar: '#F5EDED',
21 | border: 'hsl(var(--border))',
22 | input: 'hsl(var(--input))',
23 | ring: 'hsl(var(--ring))',
24 | background: 'hsl(var(--background))',
25 | foreground: 'hsl(var(--foreground))',
26 | primary: {
27 | DEFAULT: 'hsl(var(--primary))',
28 | foreground: 'hsl(var(--primary-foreground))'
29 | },
30 | secondary: {
31 | DEFAULT: 'hsl(var(--secondary))',
32 | foreground: 'hsl(var(--secondary-foreground))'
33 | },
34 | destructive: {
35 | DEFAULT: 'hsl(var(--destructive))',
36 | foreground: 'hsl(var(--destructive-foreground))'
37 | },
38 | muted: {
39 | DEFAULT: 'hsl(var(--muted))',
40 | foreground: 'hsl(var(--muted-foreground))'
41 | },
42 | accent: {
43 | DEFAULT: 'hsl(var(--accent))',
44 | foreground: 'hsl(var(--accent-foreground))'
45 | },
46 | popover: {
47 | DEFAULT: 'hsl(var(--popover))',
48 | foreground: 'hsl(var(--popover-foreground))'
49 | },
50 | card: {
51 | DEFAULT: 'hsl(var(--card))',
52 | foreground: 'hsl(var(--card-foreground))'
53 | }
54 | },
55 | borderRadius: {
56 | lg: 'var(--radius)',
57 | md: 'calc(var(--radius) - 2px)',
58 | sm: 'calc(var(--radius) - 4px)'
59 | },
60 | keyframes: {
61 | 'accordion-down': {
62 | from: { height: 0 },
63 | to: { height: 'var(--radix-accordion-content-height)' }
64 | },
65 | 'accordion-up': {
66 | from: { height: 'var(--radix-accordion-content-height)' },
67 | to: { height: 0 }
68 | }
69 | },
70 | animation: {
71 | 'accordion-down': 'accordion-down 0.2s ease-out',
72 | 'accordion-up': 'accordion-up 0.2s ease-out'
73 | }
74 | }
75 | },
76 | plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')]
77 | }
78 |
--------------------------------------------------------------------------------
/nyro-chatbot/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx",
29 | ".next/types/**/*.ts",
30 | "./.next/types/**/*.ts",
31 | "i18nConfig.js"
32 | ],
33 | "exclude": ["node_modules"]
34 | }
35 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/announcement.ts:
--------------------------------------------------------------------------------
1 | export interface Announcement {
2 | id: string
3 | title: string
4 | content: string
5 | read: boolean
6 | link: string
7 | date: string
8 | }
9 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/assistant-retrieval-item.ts:
--------------------------------------------------------------------------------
1 | export interface AssistantRetrievalItem {
2 | id: string
3 | name: string
4 | type: string
5 | }
6 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/chat-file.tsx:
--------------------------------------------------------------------------------
1 | export interface ChatFile {
2 | id: string
3 | name: string
4 | type: string
5 | file: File | null
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/chat-message.ts:
--------------------------------------------------------------------------------
1 | import { Tables } from "@/supabase/types"
2 |
3 | export interface ChatMessage {
4 | message: Tables<"messages">
5 | fileItems: string[]
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/chat.ts:
--------------------------------------------------------------------------------
1 | import { Tables } from "@/supabase/types"
2 | import { ChatMessage, LLMID } from "."
3 |
4 | export interface ChatSettings {
5 | model: LLMID
6 | prompt: string
7 | temperature: number
8 | contextLength: number
9 | includeProfileContext: boolean
10 | includeWorkspaceInstructions: boolean
11 | embeddingsProvider: "openai" | "local"
12 | }
13 |
14 | export interface ChatPayload {
15 | chatSettings: ChatSettings
16 | workspaceInstructions: string
17 | chatMessages: ChatMessage[]
18 | assistant: Tables<"assistants"> | null
19 | messageFileItems: Tables<"file_items">[]
20 | chatFileItems: Tables<"file_items">[]
21 | }
22 |
23 | export interface ChatAPIPayload {
24 | chatSettings: ChatSettings
25 | messages: Tables<"messages">[]
26 | }
27 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/collection-file.ts:
--------------------------------------------------------------------------------
1 | export interface CollectionFile {
2 | id: string
3 | name: string
4 | type: string
5 | }
6 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/content-type.ts:
--------------------------------------------------------------------------------
1 | export type ContentType =
2 | | "chats"
3 | | "presets"
4 | | "prompts"
5 | | "files"
6 | | "collections"
7 | | "assistants"
8 | | "tools"
9 | | "models"
10 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/error-response.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod"
2 |
3 | export type ErrorResponse = {
4 | error: {
5 | code: number
6 | message: string
7 | }
8 | }
9 |
10 | export const ErrorResponseSchema = z.object({
11 | error: z.object({
12 | code: z.number({ coerce: true }).default(500),
13 | message: z.string().default("Internal Server Error")
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/file-item-chunk.ts:
--------------------------------------------------------------------------------
1 | export type FileItemChunk = {
2 | content: string
3 | tokens: number
4 | }
5 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/images/assistant-image.ts:
--------------------------------------------------------------------------------
1 | export interface AssistantImage {
2 | assistantId: string
3 | path: string
4 | base64: any // base64 image
5 | url: string
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/images/message-image.ts:
--------------------------------------------------------------------------------
1 | export interface MessageImage {
2 | messageId: string
3 | path: string
4 | base64: any // base64 image
5 | url: string
6 | file: File | null
7 | }
8 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/images/workspace-image.ts:
--------------------------------------------------------------------------------
1 | export interface WorkspaceImage {
2 | workspaceId: string
3 | path: string
4 | base64: any // base64 image
5 | url: string
6 | }
7 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./announcement"
2 | export * from "./assistant-retrieval-item"
3 | export * from "./chat"
4 | export * from "./chat-file"
5 | export * from "./chat-message"
6 | export * from "./collection-file"
7 | export * from "./content-type"
8 | export * from "./file-item-chunk"
9 | export * from "./images/assistant-image"
10 | export * from "./images/message-image"
11 | export * from "./images/workspace-image"
12 | export * from "./llms"
13 | export * from "./models"
14 | export * from "./sharing"
15 | export * from "./sidebar-data"
16 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/key-type.ts:
--------------------------------------------------------------------------------
1 | export type EnvKey =
2 | | "OPENAI_API_KEY"
3 | | "ANTHROPIC_API_KEY"
4 | | "GOOGLE_GEMINI_API_KEY"
5 | | "MISTRAL_API_KEY"
6 | | "GROQ_API_KEY"
7 | | "PERPLEXITY_API_KEY"
8 | | "AZURE_OPENAI_API_KEY"
9 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/models.ts:
--------------------------------------------------------------------------------
1 | export type ModelProvider =
2 | | "openai"
3 | | "google"
4 | | "anthropic"
5 | | "mistral"
6 | | "groq"
7 | | "perplexity"
8 | | "ollama"
9 | | "openrouter"
10 | | "custom"
11 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/sharing.ts:
--------------------------------------------------------------------------------
1 | export type Sharing = "private" | "public" | "unlisted"
2 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/sidebar-data.ts:
--------------------------------------------------------------------------------
1 | import { Tables } from "@/supabase/types"
2 |
3 | export type DataListType =
4 | | Tables<"collections">[]
5 | | Tables<"chats">[]
6 | | Tables<"presets">[]
7 | | Tables<"prompts">[]
8 | | Tables<"files">[]
9 | | Tables<"assistants">[]
10 | | Tables<"tools">[]
11 | | Tables<"models">[]
12 |
13 | export type DataItemType =
14 | | Tables<"collections">
15 | | Tables<"chats">
16 | | Tables<"presets">
17 | | Tables<"prompts">
18 | | Tables<"files">
19 | | Tables<"assistants">
20 | | Tables<"tools">
21 | | Tables<"models">
22 |
--------------------------------------------------------------------------------
/nyro-chatbot/types/valid-keys.ts:
--------------------------------------------------------------------------------
1 | export enum VALID_ENV_KEYS {
2 | OPENAI_API_KEY = "OPENAI_API_KEY",
3 | ANTHROPIC_API_KEY = "ANTHROPIC_API_KEY",
4 | GOOGLE_GEMINI_API_KEY = "GOOGLE_GEMINI_API_KEY",
5 | MISTRAL_API_KEY = "MISTRAL_API_KEY",
6 | GROQ_API_KEY = "GROQ_API_KEY",
7 | PERPLEXITY_API_KEY = "PERPLEXITY_API_KEY",
8 | AZURE_OPENAI_API_KEY = "AZURE_OPENAI_API_KEY",
9 | OPENROUTER_API_KEY = "OPENROUTER_API_KEY",
10 |
11 | OPENAI_ORGANIZATION_ID = "OPENAI_ORGANIZATION_ID",
12 |
13 | AZURE_OPENAI_ENDPOINT = "AZURE_OPENAI_ENDPOINT",
14 | AZURE_GPT_35_TURBO_NAME = "AZURE_GPT_35_TURBO_NAME",
15 | AZURE_GPT_45_VISION_NAME = "AZURE_GPT_45_VISION_NAME",
16 | AZURE_GPT_45_TURBO_NAME = "AZURE_GPT_45_TURBO_NAME",
17 | AZURE_EMBEDDINGS_NAME = "AZURE_EMBEDDINGS_NAME"
18 | }
19 |
--------------------------------------------------------------------------------
/nyro-chatbot/worker/index.js:
--------------------------------------------------------------------------------
1 | self.__WB_DISABLE_DEV_LOGS = true
2 |
--------------------------------------------------------------------------------
/nyro-electron/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | .env.local
3 | .env.development
4 | *.env
5 |
--------------------------------------------------------------------------------
/nyro-electron/favicon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-electron/favicon.icns
--------------------------------------------------------------------------------
/nyro-electron/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trynyro/nyro-app/5a5884126d83ecab89c62aabdb391c59da2a0cf4/nyro-electron/favicon.ico
--------------------------------------------------------------------------------
/nyro-electron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nyro",
3 | "version": "1.0.1",
4 | "description": "nyro electron app",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=development electron .",
8 | "dist": "electron-builder",
9 | "rebuild": "electron-rebuild",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "build": {
13 | "appId": "com.nyrollc.nyro",
14 | "productName": "Nyro",
15 | "mac": {
16 | "target": "dmg",
17 | "icon": "favicon.icns"
18 | },
19 | "win": {
20 | "target": "nsis",
21 | "icon": "favicon.ico"
22 | },
23 | "publish": [
24 | {
25 | "provider": "github",
26 | "private": true,
27 | "owner": "aayush-g1414",
28 | "repo": "Nyro",
29 | "token": "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"
30 | }
31 | ]
32 | },
33 | "author": "Nyro LLC",
34 | "license": "ISC",
35 | "devDependencies": {
36 | "cross-env": "^7.0.3",
37 | "electron": "^31.0.2",
38 | "electron-builder": "^24.13.3",
39 | "electron-rebuild": "^3.2.9"
40 | },
41 | "dependencies": {
42 | "electron-clipboard-extended": "^1.1.1",
43 | "electron-updater": "^6.2.1",
44 | "node-powershell": "^5.0.1",
45 | "robotjs": "^0.6.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/nyro-electron/preload.js:
--------------------------------------------------------------------------------
1 | const { contextBridge, ipcRenderer } = require('electron');
2 |
3 | contextBridge.exposeInMainWorld('electron', {
4 | ipcRenderer: {
5 | send: (channel, data) => {
6 | const validChannels = ['windowDrag', 'toggle-retract', 'reset-position','toggle-pin', 'updateMessage'];
7 | if (validChannels.includes(channel)) {
8 | ipcRenderer.send(channel, data);
9 | }
10 | },
11 | on: (channel, func) => {
12 | const validChannels = ['insert-selected-image', 'insert-selected-text', 'retraction-state-changed', 'retraction-progress', 'retraction-complete', 'resize', 'updateMessage'];
13 | if (validChannels.includes(channel)) {
14 | ipcRenderer.on(channel, (event, ...args) => func(...args));
15 | }
16 | },
17 | removeListener: (channel, func) => {
18 | const validChannels = ['insert-selected-image', 'insert-selected-text', 'retraction-state-changed', 'retraction-progress', 'retraction-complete', 'resize', 'updateMessage'];
19 | if (validChannels.includes(channel)) {
20 | ipcRenderer.removeListener(channel, func);
21 | }
22 | },
23 | invoke: (channel, ...args) => {
24 | const validChannels = ['toggle-retract', 'retract-window', 'expand-window', 'reset-position'];
25 | if (validChannels.includes(channel)) {
26 | return ipcRenderer.invoke(channel, ...args);
27 | }
28 | return Promise.reject(new Error(`Invoke to '${channel}' is not allowed`));
29 | }
30 | }
31 | });
--------------------------------------------------------------------------------
/script.sh:
--------------------------------------------------------------------------------
1 | cd nyro-chatbot
2 | npm install
3 | npm run chat
--------------------------------------------------------------------------------