├── .github └── workflows │ └── main.yml ├── .gitignore ├── README.md ├── bun.lockb ├── components.json ├── index.html ├── package.json ├── postcss.config.js ├── scripts └── pull.py ├── src ├── app │ ├── ActiveRoute.tsx │ ├── AgentView.tsx │ ├── AppShell.tsx │ ├── ChatView.tsx │ ├── PlaygroundView.tsx │ ├── Sidebar.tsx │ ├── SidebarAgentList.tsx │ └── SidebarChatList.tsx ├── components │ ├── ActionOverlay.tsx │ ├── AdapterPicker.tsx │ ├── AgentAdapterPicker.tsx │ ├── AgentConversation.tsx │ ├── AgentGenerationParams.tsx │ ├── AgentGenerationParamsAccordionItem.tsx │ ├── AgentHistory.tsx │ ├── AgentMessage.tsx │ ├── AgentPicker.tsx │ ├── AgentPromptTemplate.tsx │ ├── AppAccordionItem.tsx │ ├── AttachmentsAccordionItem.tsx │ ├── AutoTextarea.tsx │ ├── ChatConversation.tsx │ ├── ChatConversationMessage.tsx │ ├── ChatSettings.tsx │ ├── Editor.tsx │ ├── ModelPicker.tsx │ ├── ModelSelect.tsx │ ├── Picker.tsx │ ├── PresetPicker.tsx │ ├── PromptInput.tsx │ ├── Select.tsx │ ├── Sheet.tsx │ ├── SidebarItem.tsx │ ├── SidebarSection.tsx │ ├── SliderCheckbox.tsx │ ├── ThemeProvider.tsx │ ├── ToggleDarkButton.tsx │ ├── VariablesAccordionItem.tsx │ └── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── aspect-ratio.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── context-menu.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── menubar.tsx │ │ ├── navigation-menu.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ ├── toggle.tsx │ │ ├── tooltip.tsx │ │ └── use-toast.ts ├── index.css ├── index.tsx ├── lib │ ├── adapters │ │ ├── HuggingFace.ts │ │ ├── Ollama.ts │ │ └── index.ts │ ├── extraction.ts │ └── utils.tsx ├── store │ ├── AdapterFactory.ts │ ├── Agent.ts │ ├── Attachment.ts │ ├── Chat.ts │ ├── Message.ts │ ├── Model.ts │ ├── Playground.ts │ ├── State.ts │ ├── Store.ts │ ├── Template.ts │ ├── defaults.ts │ └── index.ts └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: oven-sh/setup-bun@v1 12 | - run: bun install 13 | - run: bun vite build --base="/llm-workbench/" 14 | - uses: actions/upload-pages-artifact@v2 15 | with: 16 | path: "dist" 17 | 18 | deploy: 19 | runs-on: ubuntu-latest 20 | needs: build 21 | 22 | permissions: 23 | pages: write 24 | id-token: write 25 | 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | 30 | steps: 31 | - uses: actions/configure-pages@v3 32 | - id: deployment 33 | uses: actions/deploy-pages@v2 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | 15 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 16 | 17 | # Runtime data 18 | 19 | pids 20 | _.pid 21 | _.seed 22 | \*.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | 30 | coverage 31 | \*.lcov 32 | 33 | # nyc test coverage 34 | 35 | .nyc_output 36 | 37 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 38 | 39 | .grunt 40 | 41 | # Bower dependency directory (https://bower.io/) 42 | 43 | bower_components 44 | 45 | # node-waf configuration 46 | 47 | .lock-wscript 48 | 49 | # Compiled binary addons (https://nodejs.org/api/addons.html) 50 | 51 | build/Release 52 | 53 | # Dependency directories 54 | 55 | node_modules/ 56 | jspm_packages/ 57 | 58 | # Snowpack dependency directory (https://snowpack.dev/) 59 | 60 | web_modules/ 61 | 62 | # TypeScript cache 63 | 64 | \*.tsbuildinfo 65 | 66 | # Optional npm cache directory 67 | 68 | .npm 69 | 70 | # Optional eslint cache 71 | 72 | .eslintcache 73 | 74 | # Optional stylelint cache 75 | 76 | .stylelintcache 77 | 78 | # Microbundle cache 79 | 80 | .rpt2_cache/ 81 | .rts2_cache_cjs/ 82 | .rts2_cache_es/ 83 | .rts2_cache_umd/ 84 | 85 | # Optional REPL history 86 | 87 | .node_repl_history 88 | 89 | # Output of 'npm pack' 90 | 91 | \*.tgz 92 | 93 | # Yarn Integrity file 94 | 95 | .yarn-integrity 96 | 97 | # dotenv environment variable files 98 | 99 | .env 100 | .env.development.local 101 | .env.test.local 102 | .env.production.local 103 | .env.local 104 | 105 | # parcel-bundler cache (https://parceljs.org/) 106 | 107 | .cache 108 | .parcel-cache 109 | 110 | # Next.js build output 111 | 112 | .next 113 | out 114 | 115 | # Nuxt.js build / generate output 116 | 117 | .nuxt 118 | dist 119 | 120 | # Gatsby files 121 | 122 | .cache/ 123 | 124 | # Comment in the public line in if your project uses Gatsby and not Next.js 125 | 126 | # https://nextjs.org/blog/next-9-1#public-directory-support 127 | 128 | # public 129 | 130 | # vuepress build output 131 | 132 | .vuepress/dist 133 | 134 | # vuepress v2.x temp and cache directory 135 | 136 | .temp 137 | .cache 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.\* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | 177 | models/ 178 | ``` 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLM Workbench 2 | 3 | LLM Workbench is a user-friendly web interface designed for large language models, built with React and MobX, styled using Shadcn UI. It serves as a one-stop solution for all your large language model needs, enabling you to harness the power of free, open-source language models on your local machine. 4 | 5 | ![Chat](https://github.com/knoopx/llm-workbench/assets/100993/3054a0aa-f356-44b8-97f4-5f479c8fbeb8) 6 | ![Playground](https://github.com/knoopx/llm-workbench/assets/100993/1b2817ac-a783-4fb0-b0e7-b81e2643b6ae) 7 | ![Agent](https://github.com/knoopx/llm-workbench/assets/100993/308f366e-3389-468d-a95c-b4ecf618dd22) 8 | 9 | 10 | 11 | ### Getting Started 12 | 13 | To start your journey, choose between a HuggingFace Text Inference Generation Endpoint or Ollama. 14 | 15 | #### HuggingFace Text Inference Generation Endpoint 16 | 17 | ```bash 18 | docker run --gpus all --shm-size 1g -p 8080:80 -v (pwd)/models:/data ghcr.io/huggingface/text-generation-inference:1.1.0 --trust-remote-code --model-id TheBloke/deepseek-coder-33B-instruct-AWQ --quantize awq 19 | ``` 20 | 21 | #### Ollama 22 | 23 | ```bash 24 | OLLAMA_ORIGINS="https://knoopx.github.io" ollama serve 25 | ``` 26 | 27 | or add this line to `/etc/systemd/system/ollama.service`: 28 | 29 | ```bash 30 | Environment=OLLAMA_ORIGINS="https://knoopx.github.io" 31 | ``` 32 | 33 | Restart Ollama using these commands: 34 | 35 | ```bash 36 | systemctl daemon-reload 37 | systemctl restart ollama 38 | ``` 39 | 40 | ## 🎭 Features 41 | 42 | ### 💬 Chat Interface 43 | 44 | - **Simple, clean interface**: We've designed a user-friendly interface that makes it easy for you to interact with the AI model. 45 | - **Output streaming**: See the generated text in real-time as you type your prompt. 46 | - **Regenerate/Continue/Undo/Clear**: Use these buttons to control the generation process. 47 | - **Markdown Rendering**: The AI will generate text that supports Markdown formatting, making it easy for you to create styled content. 48 | - **Generation canceling**: Stop the generation process at any time by clicking the "Cancel" button. 49 | - **Dark mode**: Prefer working in the dark? Toggle on Dark mode for a more comfortable experience. 50 | - **Attachments**: Attach files to your chat messages (pdf, docx, and plain-text supported only). 51 | 52 | ### 🛹 Playground 53 | 54 | - **Copilot-alike inline completion**: Type your prompt and let the AI suggest completions as you type. 55 | - **Tab to accept**: Press the Tab key to accept the suggested completion. 56 | - **Cltr+Enter to re-generate**: Hit Ctrl+Enter to re-generate the response with the same prompt. 57 | 58 | ### 🤖 Agents 59 | 60 | - **Connection Adapters**: We support various connection adapters, including Ollama and HuggingFace TGI (local or remote). 61 | - **Complete generation control**: Customize the agent behavior with system prompts, conversation history, and chat prompt templates using [liquidjs](https://liquidjs.com/). 62 | 63 | # Future Ideas 64 | 65 | - Import/Export chats - Importing and exporting chat data for convenience. 66 | - Token Counter - A feature to count tokens in text. 67 | - Copy fenced block to clipboard - The ability to copy a code block and paste it into the clipboard. 68 | - Collapsible side panels - Side panels that can be expanded or collapsed for better organization. 69 | - [window.ai](https://windowai.io/) integration 70 | 71 | Code Interpreters: 72 | 73 | - Hugging Face agents ([@huggingface/agents](https://github.com/huggingface/agents)) 74 | - Aider ([paul-gauthier/aider](https://github.com/paul-gauthier/aider)) 75 | - Functionary ([MeetKai/functionary](https://github.com/MeetKai/functionary)) 76 | - NexusRaven model ([Nexusflow/NexusRaven-13B](https://huggingface.co/Nexusflow/NexusRaven-13B)) 77 | - Open procedures database ([KillianLucas/open-procedures](https://raw.githubusercontent.com/KillianLucas/open-procedures/main/procedures_db.json)) 78 | - ReACT ([www.promptingguide.ai/techniques/react](https://www.promptingguide.ai/techniques/react)) 79 | 80 | Model management features: 81 | 82 | - Hugging Face Hub ([@huggingface/hub](https://huggingface.co/docs/huggingface.js/hub/modules)) 83 | - GPT4All catalog ([nomic-ai/gpt4all](https://raw.githubusercontent.com/nomic-ai/gpt4all/main/gpt4all-chat/metadata/models2.json)) 84 | - LM Studio catalog [lmstudio-ai/model-catalog](https://raw.githubusercontent.com/lmstudio-ai/model-catalog/main/catalog.json) 85 | 86 | RAG, embeddings and vector search: 87 | 88 | - Client vector search ([yusufhilmi/client-vector-search](https://github.com/yusufhilmi/client-vector-search)) - in-browser vector database. 89 | - Fully local PDF chatbot ([jacoblee93/fully-local-pdf-chatbot](https://github.com/jacoblee93/fully-local-pdf-chatbot)) - related. 90 | - SemanticFinder ([do-me.github.io/SemanticFinder](https://do-me.github.io/SemanticFinder/)) - related. 91 | 92 | Other potential pipelines to consider: 93 | 94 | - TTS (Text-to-Speech) - convert text into speech. 95 | - Reformatting - such as punctuation and re-punctuation models ([ldenoue/distilbert-base-re-punctuate](https://huggingface.co/ldenoue/distilbert-base-re-punctuate)). 96 | - Summarization - summarize long text into shorter versions ([ldenoue/distilbart-cnn-6-6](https://huggingface.co/ldenoue/distilbart-cnn-6-6)). 97 | - Translation - convert text between languages. 98 | - Automatic speech recognition pipeline ([transformers.js](https://huggingface.co/docs/transformers.js/api/pipelines#pipelinesautomaticspeechrecognitionpipeline)) - convert spoken words into written text. 99 | - Named Entity Recognition (NER) - identify and classify entities in text ([Xenova/bert-base-NER](https://huggingface.co/Xenova/bert-base-NER), [wink-nlp](https://winkjs.org/wink-nlp/wink-nlp-in-browsers.html)). 100 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knoopx/llm-workbench/cdc6d06b92b5684268c27650b870c0736fd02598/bun.lockb -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "index.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LLM Workbench 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llm-workbench", 3 | "type": "module", 4 | "homepage": "https://knoopx.github.io/llm-workbench/", 5 | "browserslist": { 6 | "production": [ 7 | ">0.2%", 8 | "not dead", 9 | "not op_mini all" 10 | ], 11 | "development": [ 12 | "last 1 chrome version", 13 | "last 1 firefox version", 14 | "last 1 safari version" 15 | ] 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^20.8.9", 19 | "@types/react": "^18.2.33", 20 | "@types/react-dom": "^18.2.14", 21 | "autoprefixer": "^10.4.16", 22 | "bun-types": "latest", 23 | "postcss": "^8.4.31", 24 | "tailwindcss": "^3.3.5" 25 | }, 26 | "peerDependencies": { 27 | "typescript": "^5.0.0" 28 | }, 29 | "dependencies": { 30 | "@hookform/resolvers": "^3.3.2", 31 | "@huggingface/inference": "^2.6.4", 32 | "@microflash/rehype-starry-night": "^3.0.0", 33 | "@radix-ui/react-accordion": "^1.1.2", 34 | "@radix-ui/react-alert-dialog": "^1.0.5", 35 | "@radix-ui/react-aspect-ratio": "^1.0.3", 36 | "@radix-ui/react-avatar": "^1.0.4", 37 | "@radix-ui/react-checkbox": "^1.0.4", 38 | "@radix-ui/react-collapsible": "^1.0.3", 39 | "@radix-ui/react-context-menu": "^2.1.5", 40 | "@radix-ui/react-dialog": "^1.0.5", 41 | "@radix-ui/react-dropdown-menu": "^2.0.6", 42 | "@radix-ui/react-hover-card": "^1.0.7", 43 | "@radix-ui/react-icons": "^1.3.0", 44 | "@radix-ui/react-label": "^2.0.2", 45 | "@radix-ui/react-menubar": "^1.0.4", 46 | "@radix-ui/react-navigation-menu": "^1.1.4", 47 | "@radix-ui/react-popover": "^1.0.7", 48 | "@radix-ui/react-progress": "^1.0.3", 49 | "@radix-ui/react-radio-group": "^1.1.3", 50 | "@radix-ui/react-scroll-area": "^1.0.5", 51 | "@radix-ui/react-select": "^2.0.0", 52 | "@radix-ui/react-separator": "^1.0.3", 53 | "@radix-ui/react-slider": "^1.1.2", 54 | "@radix-ui/react-slot": "^1.0.2", 55 | "@radix-ui/react-switch": "^1.0.3", 56 | "@radix-ui/react-tabs": "^1.0.4", 57 | "@radix-ui/react-toast": "^1.1.5", 58 | "@radix-ui/react-toggle": "^1.0.3", 59 | "@radix-ui/react-tooltip": "^1.0.7", 60 | "@tailwindcss/typography": "^0.5.10", 61 | "@vitejs/plugin-react": "^4.1.0", 62 | "@xenova/transformers": "^2.7.0", 63 | "class-variance-authority": "^0.7.0", 64 | "clsx": "^2.0.0", 65 | "cmdk": "^0.2.0", 66 | "date-fns": "^2.30.0", 67 | "dedent": "^1.5.1", 68 | "he": "^1.2.0", 69 | "liquidjs": "^10.9.3", 70 | "lucide-react": "^0.290.0", 71 | "mammoth": "^1.6.0", 72 | "minisearch": "^6.2.0", 73 | "mobx": "^6.10.2", 74 | "mobx-react": "^9.0.1", 75 | "mobx-state-tree": "^5.3.0", 76 | "pdfjs-dist": "^3.11.174", 77 | "react": "^18.2.0", 78 | "react-day-picker": "^8.9.1", 79 | "react-dom": "^18.2.0", 80 | "react-hook-form": "^7.47.0", 81 | "react-icons": "^4.11.0", 82 | "react-markdown": "^9.0.0", 83 | "rehype-format": "^5.0.0", 84 | "rehype-highlight": "^7.0.0", 85 | "rehype-katex": "^7.0.0", 86 | "rehype-parse": "^9.0.0", 87 | "rehype-remark": "^10.0.0", 88 | "rehype-stringify": "^10.0.0", 89 | "remark-emoji": "^4.0.1", 90 | "remark-gfm": "^4.0.0", 91 | "remark-math": "^6.0.0", 92 | "remark-retext": "^6.0.0", 93 | "retext-keywords": "^8.0.1", 94 | "tailwind-merge": "^1.14.0", 95 | "tailwindcss-animate": "^1.0.7", 96 | "unified-stream": "^3.0.0", 97 | "use-debounce": "^9.0.4", 98 | "vite": "^4.5.0", 99 | "zod": "^3.22.4" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /scripts/pull.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from argparse import ArgumentParser 3 | from pathlib import Path 4 | 5 | from huggingface_hub import HfApi, hf_hub_download 6 | 7 | parser = ArgumentParser() 8 | parser.add_argument("repo_id", type=str) 9 | parser.add_argument("--quant", type=str, default="Q4_K_M") 10 | 11 | args = parser.parse_args() 12 | api = HfApi() 13 | 14 | files = api.list_repo_files(args.repo_id) 15 | 16 | for file in files: 17 | if args.quant in file and ".gguf" in file: 18 | target_path = Path("models") / args.repo_id 19 | target_path.mkdir(parents=True, exist_ok=True) 20 | 21 | hf_hub_download(args.repo_id, file, local_dir=target_path) 22 | model_file = target_path / "Modelfile" 23 | model_file.write_text(f"FROM {file}") 24 | 25 | model_name = Path(file).stem.replace(f".{args.quant}", "") 26 | subprocess.run(["ollama", "create", model_name, "-f", str(model_file)]) 27 | -------------------------------------------------------------------------------- /src/app/ActiveRoute.tsx: -------------------------------------------------------------------------------- 1 | import { useStore } from "@/store" 2 | import { AgentView } from "./AgentView" 3 | import { ChatView } from "./ChatView" 4 | import { observer } from "mobx-react" 5 | import { PlaygroundView } from "./PlaygroundView" 6 | 7 | export const ActiveRoute = observer(() => { 8 | const { 9 | state: { route, resource }, 10 | } = useStore() 11 | 12 | if (resource) { 13 | if (route === "chat") { 14 | return 15 | } else if (route === "agent") { 16 | return ( 17 |
18 | 19 |
20 | ) 21 | } 22 | } 23 | 24 | if (route === "playground") { 25 | return 26 | } 27 | 28 | return null 29 | }) 30 | -------------------------------------------------------------------------------- /src/app/AgentView.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from "mobx-react" 2 | import { useStore } from "@/store" 3 | import { Input } from "@/components/ui/input" 4 | import { AgentGenerationParams } from "../components/AgentGenerationParams" 5 | import { Button } from "@/components/ui/button" 6 | import { IoMdCopy } from "react-icons/io" 7 | import { Instance, getSnapshot } from "mobx-state-tree" 8 | import { Agent } from "@/store/Agent" 9 | import { AgentConversation } from "../components/AgentConversation" 10 | import { AgentAdapterPicker } from "../components/AgentAdapterPicker" 11 | 12 | export const AgentView = observer(() => { 13 | const { 14 | addAgent, 15 | state: { resource: agent }, 16 | } = useStore() 17 | 18 | return ( 19 |
20 |
21 |
22 | Agent Name 23 |
24 | agent.update({ name: e.target.value })} 29 | /> 30 | 40 |
41 |
42 | 46 |
47 | 48 |
49 | 50 |
51 |
Default Parameters
52 | 53 |
54 |
55 |
56 | ) 57 | }) 58 | -------------------------------------------------------------------------------- /src/app/AppShell.tsx: -------------------------------------------------------------------------------- 1 | import { Sidebar } from "./Sidebar" 2 | import { observer } from "mobx-react" 3 | import { ActiveRoute } from "./ActiveRoute" 4 | 5 | const AppShell = observer(() => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | }) 13 | 14 | export default AppShell 15 | -------------------------------------------------------------------------------- /src/app/ChatView.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from "mobx-react" 2 | import { ChatSettings } from "../components/ChatSettings" 3 | import { ChatConversation } from "../components/ChatConversation" 4 | import { useStore } from "@/store" 5 | 6 | export const ChatView = observer(() => { 7 | const { 8 | state: { resource: chat }, 9 | } = useStore() 10 | 11 | return ( 12 |
13 | 14 | 15 |
16 | ) 17 | }) 18 | -------------------------------------------------------------------------------- /src/app/PlaygroundView.tsx: -------------------------------------------------------------------------------- 1 | import { AgentGenerationParams } from "@/components/AgentGenerationParams" 2 | import { useStore } from "@/store" 3 | import { observer } from "mobx-react" 4 | import { AgentAdapterPicker } from "../components/AgentAdapterPicker" 5 | import { useDebouncedCallback } from "use-debounce" 6 | export const PlaygroundView = observer(() => { 7 | const { 8 | playground: { agent, generate, abortController, output, update }, 9 | } = useStore() 10 | 11 | const debounced = useDebouncedCallback(() => { 12 | generate() 13 | }, 200) 14 | 15 | return ( 16 |
17 |
18 |
19 |