├── frontend ├── launch_server.sh ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ ├── components │ │ ├── PromptBarButton.tsx │ │ ├── ResearchProgressFooter.module.css │ │ ├── ResearchProgressItem.module.css │ │ ├── PromptBarButton.module.css │ │ ├── ResearchTypeSelector.module.css │ │ ├── PromptBar.module.css │ │ ├── ResearchProgressList.module.css │ │ ├── ResearchProgressItem.tsx │ │ ├── ReportViewer.tsx │ │ ├── ResearchStrategyEditor.module.css │ │ ├── PromptBar.tsx │ │ ├── ResearchTypeSelector.tsx │ │ ├── ResearchProgressFooter.tsx │ │ ├── ResearchProgressList.tsx │ │ ├── ResearchStrategyEditor.tsx │ │ ├── ResearchProgressHeader.tsx │ │ ├── ResearchProgressHeader.module.css │ │ └── ReportViewer.module.css │ ├── config │ │ └── index.ts │ └── types │ │ └── ApplicationState.ts ├── postcss.config.mjs ├── public │ ├── vercel.svg │ ├── window.svg │ ├── file.svg │ ├── globe.svg │ └── next.svg ├── eslint.config.mjs ├── env.example ├── tsconfig.json ├── .gitignore ├── package.json ├── next.config.ts ├── DISCLAIMER.txt ├── CONTRIBUTING.md └── README.md ├── backend ├── requirements.txt ├── frame │ ├── prompts │ │ ├── udr_minimal │ │ │ ├── 2.auto.txt │ │ │ ├── 1.code_skill.py │ │ │ ├── 0.code_skill.py │ │ │ └── 3.auto.txt │ │ └── udr_minimal_generating │ │ │ ├── 1.auto.txt │ │ │ ├── 0.code_skill.py │ │ │ └── 2.routine_generating.txt │ ├── __init__.py │ ├── tidings.py │ ├── trace.py │ ├── errands │ │ ├── message_generating_routine_call.txt │ │ ├── message_code_call.txt │ │ ├── message_routine_call.txt │ │ ├── message_code_variables.txt │ │ ├── message_routine_variables.txt │ │ ├── message_data_processing.txt │ │ ├── message_code_skill_processing.txt │ │ ├── message_code_processing.txt │ │ ├── message_routine_processing.txt │ │ ├── message_type.txt │ │ └── message_generating_routine_processing.txt │ ├── routines.py │ ├── errands4.py │ └── clients.py ├── .gitignore ├── env.example ├── launch_server.sh ├── sessions.py ├── CHANGELOG.md ├── insert_license.py ├── DISCLAIMER.txt ├── CONTRIBUTING.md ├── items.py ├── scan_research_dry.py └── setup.py ├── LICENSE.txt └── README.md /frontend/launch_server.sh: -------------------------------------------------------------------------------- 1 | npm run dev > npm_run_dev.txt 2>&1 & disown $! -------------------------------------------------------------------------------- /frontend/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/UniversalDeepResearch/HEAD/frontend/src/app/favicon.ico -------------------------------------------------------------------------------- /frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | python-dotenv 4 | attr 5 | datasets 6 | docstring_parser 7 | evaluate 8 | openai==1.65.1 9 | transformers 10 | tavily-python -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal/2.auto.txt: -------------------------------------------------------------------------------- 1 | The following is the prompt data to be used in later procedures. 2 | 3 | PROMPT: 4 | Produce a comprehensive report on the major Christian events that took place this year between the 20th and 22nd of April 2025. -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal_generating/1.auto.txt: -------------------------------------------------------------------------------- 1 | The following is the prompt data to be used in later procedures. 2 | 3 | PROMPT: 4 | Produce a comprehensive report on the major Christian events that took place this year between the 20th and 22nd of April 2025. -------------------------------------------------------------------------------- /frontend/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /frontend/env.example: -------------------------------------------------------------------------------- 1 | # Backend API Configuration 2 | # The base URL of your backend server (without protocol) 3 | NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost 4 | # The port your backend server is running on 5 | NEXT_PUBLIC_BACKEND_PORT=8000 6 | # API version to use (v1 or v2) 7 | NEXT_PUBLIC_API_VERSION=v2 8 | 9 | # Runtime Configuration 10 | # Enable dry run mode (true/false) 11 | NEXT_PUBLIC_DRY_RUN=false 12 | # Enable V2 API (true/false) 13 | NEXT_PUBLIC_ENABLE_V2_API=true 14 | 15 | # Frontend Configuration 16 | # The port for the frontend development server 17 | NEXT_PUBLIC_FRONTEND_PORT=3000 18 | # The host for the frontend development server 19 | NEXT_PUBLIC_FRONTEND_HOST=localhost -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | SPDX-License-Identifier: Apache-2.0 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | 23 | # Virtual Environment 24 | venv/ 25 | ENV/ 26 | env/ 27 | .venv/ 28 | 29 | # IDE 30 | .idea/ 31 | .vscode/ 32 | *.swp 33 | *.swo 34 | 35 | # Environment variables 36 | .env 37 | .env.local 38 | 39 | # Logs 40 | *.log 41 | 42 | # OS 43 | .DS_Store 44 | Thumbs.db 45 | 46 | # user-added 47 | instances/ 48 | tavily_api.txt 49 | nvdev_api.txt 50 | mock_instances/ 51 | logs/ 52 | uvicorn_main.txt 53 | 54 | .DS_Store 55 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 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 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /backend/frame/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /frontend/.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # development logs 34 | npm_run_dev.txt 35 | uvicorn_main.txt 36 | 37 | # env files (can opt-in for committing if needed) 38 | .env* 39 | 40 | # vercel 41 | .vercel 42 | 43 | # typescript 44 | *.tsbuildinfo 45 | next-env.d.ts 46 | 47 | 48 | **/.DS_Store -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@heroicons/react": "^2.2.0", 13 | "next": "15.4.0", 14 | "react": "^19.0.0", 15 | "react-dom": "^19.0.0", 16 | "react-icons": "^5.1.0", 17 | "react-markdown": "^10.1.0", 18 | "remark-gfm": "^4.0.1", 19 | "uuid": "^11.1.0" 20 | }, 21 | "devDependencies": { 22 | "@eslint/eslintrc": "^3", 23 | "@tailwindcss/postcss": "^4", 24 | "@types/node": "^20", 25 | "@types/react": "^19", 26 | "@types/react-dom": "^19", 27 | "eslint": "^9", 28 | "eslint-config-next": "15.3.0", 29 | "tailwindcss": "^4", 30 | "typescript": "^5" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/next.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | import type { NextConfig } from "next"; 18 | 19 | const nextConfig: NextConfig = { 20 | /* config options here */ 21 | }; 22 | 23 | export default nextConfig; 24 | -------------------------------------------------------------------------------- /frontend/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/env.example: -------------------------------------------------------------------------------- 1 | # Universal Deep Research Backend (UDR-B) - Environment Configuration 2 | # Copy this file to .env and customize the values for your deployment 3 | 4 | # Server Configuration 5 | HOST=0.0.0.0 6 | PORT=8000 7 | LOG_LEVEL=info 8 | 9 | # CORS Configuration 10 | FRONTEND_URL=http://localhost:3000 11 | ALLOW_CREDENTIALS=true 12 | 13 | # Model Configuration 14 | DEFAULT_MODEL=llama-3.1-nemotron-253b 15 | LLM_BASE_URL=https://integrate.api.nvidia.com/v1 16 | LLM_API_KEY_FILE=nvdev_api.txt 17 | LLM_TEMPERATURE=0.2 18 | LLM_TOP_P=0.7 19 | LLM_MAX_TOKENS=2048 20 | 21 | # Search Configuration 22 | TAVILY_API_KEY_FILE=tavily_api.txt 23 | MAX_SEARCH_RESULTS=10 24 | 25 | # Research Configuration 26 | MAX_TOPICS=1 27 | MAX_SEARCH_PHRASES=1 28 | MOCK_DIRECTORY=mock_instances/stocks_24th_3_sections 29 | RANDOM_SEED=42 30 | 31 | # Logging Configuration 32 | LOG_DIR=logs 33 | TRACE_ENABLED=true 34 | COPY_INTO_STDOUT=false 35 | 36 | # FrameV4 Configuration 37 | LONG_CONTEXT_CUTOFF=8192 38 | FORCE_LONG_CONTEXT=false 39 | MAX_ITERATIONS=1024 40 | INTERACTION_LEVEL=none 41 | 42 | # Model-specific overrides (optional) 43 | # LLAMA_3_1_8B_BASE_URL=https://integrate.api.nvidia.com/v1 44 | # LLAMA_3_1_8B_MODEL=nvdev/meta/llama-3.1-8b-instruct 45 | # LLAMA_3_1_8B_TEMPERATURE=0.2 46 | # LLAMA_3_1_8B_TOP_P=0.7 47 | # LLAMA_3_1_8B_MAX_TOKENS=2048 -------------------------------------------------------------------------------- /frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/globals.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | @import "tailwindcss"; 18 | 19 | :root { 20 | --background: #ffffff; 21 | --foreground: #171717; 22 | } 23 | 24 | @theme inline { 25 | --color-background: var(--background); 26 | --color-foreground: var(--foreground); 27 | --font-sans: var(--font-geist-sans); 28 | --font-mono: var(--font-geist-mono); 29 | } 30 | 31 | @media (prefers-color-scheme: dark) { 32 | :root { 33 | --background: #0a0a0a; 34 | --foreground: #ededed; 35 | } 36 | } 37 | 38 | body { 39 | background: var(--background); 40 | color: var(--foreground); 41 | font-family: Arial, Helvetica, sans-serif; 42 | } 43 | -------------------------------------------------------------------------------- /frontend/src/components/PromptBarButton.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 'use client'; 18 | 19 | import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; 20 | import styles from './PromptBarButton.module.css'; 21 | 22 | interface PromptBarButtonProps { 23 | onClick: () => void; 24 | disabled?: boolean; 25 | } 26 | 27 | export default function PromptBarButton({ onClick, disabled = false }: PromptBarButtonProps) { 28 | return ( 29 | 37 | ); 38 | } -------------------------------------------------------------------------------- /backend/launch_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Universal Deep Research Backend (UDR-B) - Server Launch Script 4 | # This script launches the FastAPI server with proper configuration 5 | 6 | # Load environment variables if .env file exists 7 | if [ -f ".env" ]; then 8 | echo "Loading environment variables from .env file..." 9 | export $(cat .env | grep -v '^#' | xargs) 10 | fi 11 | 12 | # Set default values if not provided 13 | HOST=${HOST:-0.0.0.0} 14 | PORT=${PORT:-8000} 15 | LOG_LEVEL=${LOG_LEVEL:-info} 16 | 17 | echo "Starting Universal Deep Research Backend (UDR-B)..." 18 | echo "Host: $HOST" 19 | echo "Port: $PORT" 20 | echo "Log Level: $LOG_LEVEL" 21 | 22 | # Launch the server 23 | uvicorn main:app \ 24 | --reload \ 25 | --env-file .env \ 26 | --access-log \ 27 | --log-level=$LOG_LEVEL \ 28 | --host $HOST \ 29 | --port $PORT \ 30 | > uvicorn_main.txt 2>&1 & 31 | 32 | # Wait a moment for the process to start 33 | sleep 2 34 | 35 | # Find the uvicorn process ID using ps and grep 36 | PID=$(ps aux | grep "uvicorn main:app" | grep -v grep | awk '{print $2}' | head -1) 37 | 38 | if [ -n "$PID" ]; then 39 | echo "Server started with PID: $PID" 40 | echo "To stop the server, run: kill $PID" 41 | else 42 | echo "Warning: Could not find uvicorn process ID" 43 | fi 44 | 45 | # Disown the process so it continues running after the script exits 46 | disown $! 47 | 48 | echo "Server started in background. Check uvicorn_main.txt for logs." 49 | echo "API available at: http://$HOST:$PORT" -------------------------------------------------------------------------------- /backend/sessions.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """ 16 | Session management utilities for the Universal Deep Research Backend (UDR-B). 17 | 18 | This module provides functions for generating unique session identifiers 19 | that combine timestamps with random components for tracking research sessions. 20 | """ 21 | 22 | import uuid 23 | from datetime import datetime 24 | 25 | 26 | def generate_session_key() -> str: 27 | """ 28 | Generates a unique session key combining timestamp and random components. 29 | Format: {timestamp}-{random_uuid} 30 | Example: "20240315T123456Z-a1b2c3d4" 31 | 32 | Returns: 33 | str: A unique session identifier 34 | """ 35 | timestamp = datetime.now().strftime("%Y%m%dT%H%M%SZ") 36 | random_component = str(uuid.uuid4())[:8] # First 8 chars of UUID 37 | return f"{timestamp}-{random_component}" 38 | -------------------------------------------------------------------------------- /frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | import type { Metadata } from "next"; 18 | import { Geist, Geist_Mono } from "next/font/google"; 19 | import "./globals.css"; 20 | 21 | const geistSans = Geist({ 22 | variable: "--font-geist-sans", 23 | subsets: ["latin"], 24 | }); 25 | 26 | const geistMono = Geist_Mono({ 27 | variable: "--font-geist-mono", 28 | subsets: ["latin"], 29 | }); 30 | 31 | export const metadata: Metadata = { 32 | title: "Deep Research", 33 | description: "Frame-powered Universal Deep Research demo", 34 | }; 35 | 36 | export default function RootLayout({ 37 | children, 38 | }: Readonly<{ 39 | children: React.ReactNode; 40 | }>) { 41 | return ( 42 | 43 |
46 | {children} 47 | 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal/1.code_skill.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | def perform_search(search_phrase: str): 16 | """ 17 | Performs a search using the Tavily API and returns a list of search result objects. 18 | 19 | Args: 20 | search_phrase (str): The phrase to search for using the Tavily API. 21 | 22 | Returns: 23 | List[Dict[str, Any]]: A list of search result dictionaries obtained from the Tavily API. Each dictionary has 'content' field with text retrieved from the result, and a 'url' field that keeps the url of the search result. 24 | 25 | Raises: 26 | TavilyClientError: If there is an issue with the Tavily API client or the search request. 27 | KeyError: If the expected 'results' key is missing in the search response. 28 | 29 | """ 30 | from tavily import TavilyClient 31 | 32 | client = TavilyClient(api_key="tvly-dev-XXXX") 33 | search_response = client.search(search_phrase, include_raw_content=True) 34 | return search_response["results"] 35 | -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal_generating/0.code_skill.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | def perform_search(search_phrase: str): 16 | """ 17 | Performs a search using the Tavily API and returns a list of search result objects. 18 | 19 | Args: 20 | search_phrase (str): The phrase to search for using the Tavily API. 21 | 22 | Returns: 23 | List[Dict[str, Any]]: A list of search result dictionaries obtained from the Tavily API. Each dictionary has 'content' field with text retrieved from the result, and a 'url' field that keeps the url of the search result. 24 | 25 | Raises: 26 | TavilyClientError: If there is an issue with the Tavily API client or the search request. 27 | KeyError: If the expected 'results' key is missing in the search response. 28 | 29 | """ 30 | from tavily import TavilyClient 31 | 32 | client = TavilyClient(api_key="tvly-dev-XXXX") 33 | search_response = client.search(search_phrase, include_raw_content=True) 34 | return search_response["results"] 35 | -------------------------------------------------------------------------------- /backend/frame/tidings.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from typing import Any 16 | 17 | 18 | class Tiding: 19 | def __init__( 20 | self, natural_name: str, python_name: str, description: str, content: Any 21 | ) -> None: 22 | self.natural_name = natural_name 23 | self.python_name = python_name 24 | self.description = description 25 | self.content = content 26 | 27 | def __str__(self) -> str: 28 | return f"Natural name: {self.natural_name}\nPython name: {self.python_name}\nDescription: {self.description}\nContent: {self.content}" 29 | 30 | def to_dict(self) -> dict: 31 | return { 32 | "natural_name": self.natural_name, 33 | "python_name": self.python_name, 34 | "description": self.description, 35 | "content": self.content, 36 | "type": type(self.content).__name__, 37 | } 38 | 39 | def from_dict(self, tiding: dict) -> None: 40 | self.natural_name = tiding["natural_name"] 41 | self.python_name = tiding["python_name"] 42 | self.description = tiding["description"] 43 | self.content = tiding["content"] 44 | -------------------------------------------------------------------------------- /backend/frame/trace.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import os 16 | import sys 17 | from typing import Callable 18 | 19 | 20 | class Trace: 21 | def __init__( 22 | self, 23 | path: str | None = None, 24 | copy_into_stdout: bool = False, 25 | hook: Callable = None, 26 | ) -> None: 27 | self.path = path 28 | self.entries = [] 29 | self.copy_into_stdout = copy_into_stdout 30 | self.hook = hook 31 | 32 | # set self.file to a new file if path is not None or to stdout if it is None 33 | self.file = open(path, "w") if path is not None else open(os.devnull, "w") 34 | 35 | def write(self, entry: str) -> None: 36 | self.entries.append(entry) 37 | print(entry, file=self.file, flush=True) 38 | if self.copy_into_stdout: 39 | print(entry, file=sys.stdout, flush=True) 40 | if self.hook is not None: 41 | self.hook(entry) 42 | 43 | def close(self) -> None: 44 | self.file.close() 45 | 46 | def __call__(self, *args) -> None: 47 | for arg in args: 48 | self.write(str(arg)) 49 | 50 | def write_separator(self) -> None: 51 | self.write("#" * 80) 52 | -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal/0.code_skill.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | def send_message(type, description, **kwargs): 16 | """ 17 | Sends a message by appending it to the global `__messages` list. 18 | 19 | This function creates a message dictionary with a specified type and description, 20 | along with any additional keyword arguments provided. The constructed message is 21 | then added to the global `__messages` list for further processing or logging. 22 | 23 | Args: 24 | type (str): The type of the message (e.g., "error", "info", "warning"). 25 | description (str): A brief description of the message content. 26 | **kwargs: Additional key-value pairs to include in the message dictionary. 27 | 28 | Raises: 29 | NameError: If the global variable `__messages` is not defined. 30 | 31 | Example: 32 | send_message( 33 | type="info", 34 | description="Process completed successfully", 35 | report="Reporting information" # optional, added for this particular example 36 | ) 37 | """ 38 | global __messages 39 | message = {"type": type, "description": description, **kwargs} 40 | __messages.append(message) 41 | -------------------------------------------------------------------------------- /frontend/src/components/ResearchProgressFooter.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .footer { 18 | display: flex; 19 | justify-content: space-between; 20 | align-items: center; 21 | padding: 1rem; 22 | border-top: 1px solid rgb(229 231 235); 23 | background-color: rgb(243 244 246); 24 | } 25 | 26 | .stats { 27 | display: flex; 28 | align-items: center; 29 | gap: 1rem; 30 | } 31 | 32 | .time { 33 | color: rgb(17 24 39); 34 | font-size: 0.875rem; 35 | font-weight: 500; 36 | } 37 | 38 | .searchCount, 39 | .queryCount { 40 | display: flex; 41 | align-items: center; 42 | gap: 0.5rem; 43 | color: rgb(17 24 39); 44 | font-size: 0.875rem; 45 | } 46 | 47 | .searchIcon, 48 | .queryIcon { 49 | height: 1rem; 50 | width: 1rem; 51 | color: rgb(107 114 128); 52 | } 53 | 54 | /* Dark mode styles */ 55 | @media (prefers-color-scheme: dark) { 56 | .footer { 57 | border-top-color: rgb(55 65 81); 58 | background-color: rgb(31 41 55); 59 | } 60 | 61 | .time { 62 | color: rgb(243 244 246); 63 | } 64 | 65 | .searchCount, 66 | .queryCount { 67 | color: rgb(243 244 246); 68 | } 69 | 70 | .searchIcon, 71 | .queryIcon { 72 | color: rgb(156 163 175); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /frontend/src/components/ResearchProgressItem.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .item { 18 | display: flex; 19 | padding: 1rem; 20 | gap: 1rem; 21 | border-bottom: 1px solid rgb(229 231 235); 22 | transition: background-color 0.2s ease-in-out; 23 | } 24 | 25 | .item:hover { 26 | background-color: rgb(229 231 235); 27 | } 28 | 29 | .iconContainer { 30 | display: flex; 31 | align-items: flex-start; 32 | padding-top: 0.25rem; 33 | } 34 | 35 | .icon { 36 | height: 1.25rem; 37 | width: 1.25rem; 38 | color: rgb(107 114 128); 39 | } 40 | 41 | .content { 42 | flex: 1; 43 | display: flex; 44 | flex-direction: column; 45 | gap: 0.25rem; 46 | } 47 | 48 | .description { 49 | color: rgb(17 24 39); 50 | font-size: 0.875rem; 51 | line-height: 1.25rem; 52 | } 53 | 54 | .timestamp { 55 | color: rgb(107 114 128); 56 | font-size: 0.75rem; 57 | } 58 | 59 | /* Dark mode styles */ 60 | @media (prefers-color-scheme: dark) { 61 | .item { 62 | border-bottom-color: rgb(55 65 81); 63 | } 64 | 65 | .item:hover { 66 | background-color: rgb(55 65 81); 67 | } 68 | 69 | .icon { 70 | color: rgb(156 163 175); 71 | } 72 | 73 | .description { 74 | color: rgb(243 244 246); 75 | } 76 | 77 | .timestamp { 78 | color: rgb(156 163 175); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /frontend/src/components/PromptBarButton.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .button { 18 | position: absolute; 19 | right: 0.5rem; 20 | bottom: 0.75rem; 21 | padding: 0.5rem; 22 | background: none; 23 | border: none; 24 | cursor: pointer; 25 | border-radius: 0.5rem; 26 | transition: all 0.2s ease-in-out; 27 | } 28 | 29 | .button:hover { 30 | background-color: rgb(229 231 235); 31 | } 32 | 33 | .button.disabled { 34 | cursor: not-allowed; 35 | opacity: 0.5; 36 | } 37 | 38 | .button.disabled:hover { 39 | background-color: transparent; 40 | } 41 | 42 | .icon { 43 | height: 1.25rem; 44 | width: 1.25rem; 45 | color: rgb(156 163 175); 46 | transition: color 0.2s ease-in-out; 47 | } 48 | 49 | .button:hover .icon { 50 | color: rgb(107 114 128); 51 | } 52 | 53 | .button.disabled:hover .icon { 54 | color: rgb(156 163 175); 55 | } 56 | 57 | /* Dark mode styles */ 58 | @media (prefers-color-scheme: dark) { 59 | .button:hover { 60 | background-color: rgb(55 65 81); 61 | } 62 | 63 | .icon { 64 | color: rgb(107 114 128); 65 | } 66 | 67 | .button:hover .icon { 68 | color: rgb(156 163 175); 69 | } 70 | 71 | .button.disabled:hover { 72 | background-color: transparent; 73 | } 74 | 75 | .button.disabled:hover .icon { 76 | color: rgb(107 114 128); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /frontend/src/components/ResearchTypeSelector.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .container { 18 | display: flex; 19 | gap: 0.5rem; 20 | } 21 | 22 | .button { 23 | display: flex; 24 | align-items: center; 25 | gap: 0.5rem; 26 | padding: 0.2rem 0.5rem; 27 | background-color: rgb(243 244 246); 28 | border: 1px solid transparent; 29 | border-radius: 0.5rem; 30 | cursor: pointer; 31 | transition: all 0.2s ease-in-out; 32 | } 33 | 34 | .button:disabled:not(.selected) { 35 | cursor: not-allowed; 36 | opacity: 0.5; 37 | } 38 | 39 | .button:not(:disabled):hover { 40 | background-color: rgb(229 231 235); 41 | } 42 | 43 | .button.selected { 44 | border-color: rgb(75 85 99); 45 | } 46 | 47 | .icon { 48 | width: 0.875rem; 49 | height: 0.875rem; 50 | color: rgb(75 85 99); 51 | } 52 | 53 | .label { 54 | font-size: 0.75rem; 55 | font-weight: 500; 56 | color: rgb(55 65 81); 57 | } 58 | 59 | /* Dark mode styles */ 60 | @media (prefers-color-scheme: dark) { 61 | .button { 62 | background-color: rgb(55 65 81); 63 | } 64 | 65 | .button:hover { 66 | background-color: rgb(75 85 99); 67 | } 68 | 69 | .button.selected { 70 | border-color: rgb(209 213 219); 71 | } 72 | 73 | .icon { 74 | color: rgb(209 213 219); 75 | } 76 | 77 | .label { 78 | color: rgb(229 231 235); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the Universal Deep Research Backend (UDR-B) will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.0] - 2025-07-15 9 | 10 | ### Added 11 | 12 | - Initial release of Universal Deep Research Backend (UDR-B) 13 | - FastAPI-based REST API with streaming responses 14 | - Intelligent research capabilities using LLMs and web search 15 | - Multi-model support through compatible APIs (OpenAI, NVIDIA, local vLLM) 16 | - Tavily API integration for web search functionality 17 | - Session management with persistent research sessions 18 | - Advanced FrameV4 reasoning framework 19 | - Dry run mode for testing with mock data 20 | - Comprehensive configuration system with environment variables 21 | - Real-time progress updates via Server-Sent Events 22 | - CORS support for frontend integration 23 | - Logging and tracing capabilities 24 | - Mock data for development and testing 25 | 26 | ### Technical Features 27 | 28 | - `/api/research` endpoint for comprehensive research workflow 29 | - `/api/research2` endpoint for advanced FrameV4-based research 30 | - Modular architecture with configurable components 31 | - Support for multiple LLM providers and models 32 | - Environment-based configuration management 33 | - Setup script for easy deployment 34 | - Comprehensive error handling and validation 35 | 36 | ### Documentation 37 | 38 | - Complete README with setup and usage instructions 39 | - API endpoint documentation 40 | - Configuration guide 41 | - Troubleshooting section 42 | - Disclaimer for research/demonstration use 43 | 44 | ### Research/Demonstration Focus 45 | 46 | - Prototype implementation for AI research concepts 47 | - Experimental features for academic research 48 | - Not intended for production use 49 | - Comprehensive legal disclaimer included 50 | 51 | --- 52 | 53 | ## Version History 54 | 55 | - **1.0.0**: Initial research prototype release 56 | -------------------------------------------------------------------------------- /backend/frame/errands/message_generating_routine_call.txt: -------------------------------------------------------------------------------- 1 | You are a helpful assistant for generating a Python function call snippet. Your task is to process the MESSAGE, the generated CODE (a function definition), and the TIDINGS (a set of variable names and values). Your goal is to output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as arguments (not their values), and captures the returned generator. The function defined in CODE always returns a generator of dictionaries (Generator[dict[str, Any], None, None]). 2 | 3 | ===SEPARATOR=== 4 | You are given: 5 | - MESSAGE: the original user message (for context) that was used to produce CODE 6 | - CODE: a Python function definition (already generated) 7 | - TIDINGS: a set of variable names and values (as would be passed to the function), some of which might be useful to pass to CODE in order to accomplish the intent of code in MESSAGE 8 | 9 | Your job is to: 10 | 1. Output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as named arguments (keyword arguments) where applicable/relevant/good fit. 11 | 2. Capture the returned generator into a __generator variable, such as 12 | __generator = function_from_CODE(var1=var1, var2=var2, ...) 13 | where var1, var2, ... are the variable names from TIDINGS. 14 | 3. Output only the Python code line, with no extra text, comments, or formatting. 15 | 16 | If there are no tidings, call the function defined in CODE with no arguments: 17 | __generator = function_from_CODE() 18 | 19 | This is the end of the instructions. Below is the input to process. 20 | 21 | MESSAGE: 22 | {message} 23 | 24 | CODE: 25 | {code} 26 | 27 | TIDINGS: 28 | {tidings} 29 | 30 | Reminders: 31 | - Output just the function call to the functiond defined in CODE so that the intent of MESSAGE (from which CODE was generated) is fulfilled. 32 | - To pass variables into the function call, use variable names described under TIDINGS. 33 | - Do not output any other text. Do not output any surrounding backticks or code tags. -------------------------------------------------------------------------------- /backend/frame/errands/message_code_call.txt: -------------------------------------------------------------------------------- 1 | You are a helpful assistant for generating a Python function call snippet. Your task is to process the MESSAGE, the generated CODE (a function definition), and the TIDINGS (a set of variable names and values). Your goal is to output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as arguments (not their values), and captures the return values into variables named __output and __vars. The function defined in CODE always returns a tuple (output: str, variables: dict[str, Any]). 2 | 3 | ===SEPARATOR=== 4 | You are given: 5 | - MESSAGE: the original user message (for context) that was used to produce CODE 6 | - CODE: a Python function definition (already generated) 7 | - TIDINGS: a set of variable names and values (as would be passed to the function), some of which might be useful to pass to CODE in order to accomplish the intent of code in MESSAGE 8 | 9 | Your job is to: 10 | 1. Output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as named arguments (keyword arguments) where applicable/relevant/good fit. 11 | 2. Capture the return values into variables named __output and __vars, like this: 12 | __output, __vars = function_from_CODE(var1=var1, var2=var2, ...) 13 | where var1, var2, ... are the variable names from TIDINGS. 14 | 3. Output only the Python code line, with no extra text, comments, or formatting. 15 | 16 | If there are no tidings, call the function defined in CODE with no arguments: 17 | __output, __vars = function_from_CODE() 18 | 19 | This is the end of the instructions. Below is the input to process. 20 | 21 | MESSAGE: 22 | {message} 23 | 24 | CODE: 25 | {code} 26 | 27 | TIDINGS: 28 | {tidings} 29 | 30 | Reminders: 31 | - Output just the function call to the functiond defined in CODE so that the intent of MESSAGE (from which CODE was generated) is fulfilled. 32 | - To pass variables into the function call, use variable names described under TIDINGS. 33 | - Do not output any other text. Do not output any surrounding backticks or code tags. -------------------------------------------------------------------------------- /backend/frame/errands/message_routine_call.txt: -------------------------------------------------------------------------------- 1 | You are a helpful assistant for generating a Python function call snippet. Your task is to process the MESSAGE, the generated CODE (a function definition), and the TIDINGS (a set of variable names and values). Your goal is to output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as arguments (not their values), and captures the return values into variables named __output and __vars. The function defined in CODE always returns a tuple (output: str, variables: dict[str, Any]). 2 | 3 | ===SEPARATOR=== 4 | You are given: 5 | - MESSAGE: the original user message (for context) that was used to produce CODE 6 | - CODE: a Python function definition (already generated) 7 | - TIDINGS: a set of variable names and values (as would be passed to the function), some of which might be useful to pass to CODE in order to accomplish the intent of code in MESSAGE 8 | 9 | Your job is to: 10 | 1. Output a single line of Python code that calls the function defined in CODE, passing the variable names from TIDINGS as named arguments (keyword arguments) where applicable/relevant/good fit. 11 | 2. Capture the return values into variables named __output and __vars, like this: 12 | __output, __vars = function_from_CODE(var1=var1, var2=var2, ...) 13 | where var1, var2, ... are the variable names from TIDINGS. 14 | 3. Output only the Python code line, with no extra text, comments, or formatting. 15 | 16 | If there are no tidings, call the function defined in CODE with no arguments: 17 | __output, __vars = function_from_CODE() 18 | 19 | This is the end of the instructions. Below is the input to process. 20 | 21 | MESSAGE: 22 | {message} 23 | 24 | CODE: 25 | {code} 26 | 27 | TIDINGS: 28 | {tidings} 29 | 30 | Reminders: 31 | - Output just the function call to the functiond defined in CODE so that the intent of MESSAGE (from which CODE was generated) is fulfilled. 32 | - To pass variables into the function call, use variable names described under TIDINGS. 33 | - Do not output any other text. Do not output any surrounding backticks or code tags. -------------------------------------------------------------------------------- /backend/frame/errands/message_code_variables.txt: -------------------------------------------------------------------------------- 1 | You are an expert Python code and documentation analyst. Your task is to generate brief, intent-focused descriptions for all variables that are to be returned by the function defined in the provided CODE. Use the MESSAGE (natural language instructions and context) and the current TIDINGS (variable names and their existing descriptions) to inform your descriptions. 2 | 3 | ===SEPARATOR=== 4 | You are an expert Python code and documentation analyst. Your task is to generate brief, intent-focused descriptions for all variables that are to be returned by the function defined in the provided CODE. Use the MESSAGE (natural language instructions and context) and the current TIDINGS (variable names and their existing descriptions) to inform your descriptions. 5 | 6 | Guidelines: 7 | - If a variable's description in TIDINGS is already sufficient and perhaps better than what you could generate, copy it exactly as is. 8 | - Otherwise, write a new description that is brief, captures the type hints if possible, and describes the intent and role of the variable. Include other names if the MESSAGE gives any. 9 | - The description should not be too long or too short; it should be just informative enough. 10 | - Focus on the intent and role of each variable. 11 | 12 | # Note: For each line in TIDINGS, the format is: 13 | # variable_name: value # description 14 | # Only use the description after the '#' for reference; ignore the value. 15 | 16 | For each variable that will be returned by the function in CODE, output a mapping in the following format: 17 | 18 | variable_name # description 19 | 20 | Only include variables that are returned by the function. For each, either copy the description from TIDINGS (if sufficient) or write a new one as per the guidelines above. 21 | 22 | Reminders: 23 | - Output the descriptions in the form variable_name # description 24 | - Use expert description but don't get too verbose 25 | - Do not output any other text. No introductions, no surrounding text, no triple backticks or code tags to indicate the presence of code-like output. Output just the descriptions in the requested format. Do not output any other text. 26 | 27 | Input: 28 | MESSAGE: 29 | {message} 30 | 31 | CODE: 32 | {code} 33 | 34 | TIDINGS (variable: value # description): 35 | {tidings} -------------------------------------------------------------------------------- /backend/frame/errands/message_routine_variables.txt: -------------------------------------------------------------------------------- 1 | You are an expert Python code and documentation analyst. Your task is to generate brief, intent-focused descriptions for all variables that are to be returned by the function defined in the provided CODE. Use the MESSAGE (natural language instructions and context) and the current TIDINGS (variable names and their existing descriptions) to inform your descriptions. 2 | 3 | ===SEPARATOR=== 4 | 5 | You are an expert Python code and documentation analyst. Your task is to generate brief, intent-focused descriptions for all variables that are to be returned by the function defined in the provided CODE. Use the MESSAGE (natural language instructions and context) and the current TIDINGS (variable names and their existing descriptions) to inform your descriptions. 6 | 7 | Guidelines: 8 | - If a variable's description in TIDINGS is already sufficient and perhaps better than what you could generate, copy it exactly as is. 9 | - Otherwise, write a new description that is brief, captures the type hints if possible, and describes the intent and role of the variable. Include other names if the MESSAGE gives any. 10 | - The description should not be too long or too short; it should be just informative enough. 11 | - Focus on the intent and role of each variable. 12 | 13 | # Note: For each line in TIDINGS, the format is: 14 | # variable_name: value # description 15 | # Only use the description after the '#' for reference; ignore the value. 16 | 17 | For each variable that will be returned by the function in CODE, output a mapping in the following format: 18 | 19 | variable_name # description 20 | 21 | Only include variables that are returned by the function. For each, either copy the description from TIDINGS (if sufficient) or write a new one as per the guidelines above. 22 | 23 | Reminders: 24 | - Output the descriptions in the form variable_name # description 25 | - Use expert description but don't get too verbose 26 | - Do not output any other text. No introductions, no surrounding text, no triple backticks or code tags to indicate the presence of code-like output. Output just the descriptions in the requested format. Do not output any other text. 27 | 28 | Input: 29 | MESSAGE: 30 | {message} 31 | 32 | CODE: 33 | {code} 34 | 35 | TIDINGS (variable: value # description): 36 | {tidings} -------------------------------------------------------------------------------- /frontend/src/config/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | export interface AppConfig { 18 | // Backend API Configuration 19 | backend: { 20 | baseUrl: string; 21 | port: number; 22 | apiVersion: 'v1' | 'v2'; 23 | }; 24 | 25 | // Runtime Configuration 26 | runtime: { 27 | dryRun: boolean; 28 | enableV2Api: boolean; 29 | }; 30 | 31 | // Frontend Configuration 32 | frontend: { 33 | port: number; 34 | host: string; 35 | }; 36 | } 37 | 38 | // Default configuration 39 | const defaultConfig: AppConfig = { 40 | backend: { 41 | baseUrl: process.env.NEXT_PUBLIC_BACKEND_BASE_URL || 'http://localhost', 42 | port: parseInt(process.env.NEXT_PUBLIC_BACKEND_PORT || '8000'), 43 | apiVersion: (process.env.NEXT_PUBLIC_API_VERSION as 'v1' | 'v2') || 'v2', 44 | }, 45 | runtime: { 46 | dryRun: process.env.NEXT_PUBLIC_DRY_RUN === 'true', 47 | enableV2Api: process.env.NEXT_PUBLIC_ENABLE_V2_API !== 'false', 48 | }, 49 | frontend: { 50 | port: parseInt(process.env.NEXT_PUBLIC_FRONTEND_PORT || '3000'), 51 | host: process.env.NEXT_PUBLIC_FRONTEND_HOST || 'localhost', 52 | }, 53 | }; 54 | 55 | // Helper function to get the full backend URL 56 | export const getBackendUrl = (config: AppConfig = defaultConfig): string => { 57 | return `${config.backend.baseUrl}:${config.backend.port}`; 58 | }; 59 | 60 | // Helper function to get the API endpoint 61 | export const getApiEndpoint = (config: AppConfig = defaultConfig): string => { 62 | const baseUrl = getBackendUrl(config); 63 | const endpoint = config.runtime.enableV2Api ? '/api/research2' : '/api/research'; 64 | return `${baseUrl}${endpoint}`; 65 | }; 66 | 67 | export default defaultConfig; -------------------------------------------------------------------------------- /frontend/src/components/PromptBar.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .promptContainer { 18 | position: relative; 19 | width: 100%; 20 | max-width: 42rem; 21 | } 22 | 23 | .promptBody { 24 | position: relative; 25 | width: 100%; 26 | } 27 | 28 | .promptTextarea { 29 | width: 100%; 30 | padding: 1rem; 31 | padding-right: 3rem; 32 | background-color: rgb(243 244 246); 33 | border-radius: 1rem; 34 | border: none; 35 | resize: none; 36 | overflow: hidden; 37 | min-height: 1.5rem; 38 | max-height: 200px; 39 | color: rgb(17 24 39); 40 | transition: all 0.2s ease-in-out; 41 | line-height: 1.5; 42 | } 43 | 44 | .promptTextarea:hover { 45 | background-color: rgb(229 231 235); 46 | box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); 47 | } 48 | 49 | .promptTextarea:focus { 50 | outline: none; 51 | box-shadow: 0 0 0 2px rgb(209 213 219); 52 | background-color: rgb(243 244 246); 53 | } 54 | 55 | .promptTextarea::placeholder { 56 | color: rgb(107 114 128); 57 | } 58 | 59 | .promptTextarea.disabled { 60 | background-color: rgb(229 231 235); 61 | color: rgb(156 163 175); 62 | cursor: not-allowed; 63 | } 64 | 65 | .promptTextarea.disabled:hover { 66 | background-color: rgb(229 231 235); 67 | box-shadow: none; 68 | } 69 | 70 | .promptBarFooter { 71 | display: flex; 72 | justify-content: space-between; 73 | align-items: center; 74 | margin-top: 0.5rem; 75 | width: 100%; 76 | } 77 | 78 | .shortcutHint { 79 | position: relative; 80 | font-size: 0.75rem; 81 | color: rgb(107 114 128); 82 | display: flex; 83 | align-items: center; 84 | gap: 0.25rem; 85 | opacity: 0; 86 | transition: opacity 0.2s ease-in-out; 87 | } 88 | 89 | .shortcutHint.visible { 90 | opacity: 1; 91 | } 92 | -------------------------------------------------------------------------------- /backend/frame/prompts/udr_minimal/3.auto.txt: -------------------------------------------------------------------------------- 1 | 1. Send a message of type "prompt_received" with description saying what PROMPT has been received, e.g. "Received research request: {PROMPT}" 2 | 2. Send a message of type "prompt_analysis_started", with description indicating that we are now analyzing the research request. 3 | 3. Take the PROMPT and ask a language model to produce 3 search phrases that could help with retrieving results from search engine for the purpose of compiling a report the user asks for in the PROMPT. The search phrases should be simple and objective, e.g. "important events 1972" or "energy consumption composition in India today". Use a long prompt for the model that describes in detail what is supposed to be performed and the expected output format. Instruct the model to return the search phrases on one line each. Tell the model not to output any other text -- just the newline-separated phrases. Then, parse the output of the language model line by line and save the resulting search phrases as "phrases" for further research, skipping over empty lines. 4 | 4. Send a message of type "prompt_analysis_completed", with a description saying as much. 5 | 5. For each phrase in phrases output by step 3., perform the following: 6 | - Send a message of type "search_started", with the description indicating what search phrase we are using for the search, e.g. "Searching for phrase '{phrase}'" 7 | - Perform search with the phrase. Once the search returns some results, append their contents to CONTEXT one by one, separating them by double newlines from what is already present in the CONTEXT. 8 | 6. Send a message with type "report_building", with the description indicating that the report is being built. 9 | 7. Take CONTEXT. Call the language model, instructing it to take CONTEXT (to be appended into the LM call) and produce a deep research report on the topic requested in PROMPT. The resulting report should go into detail wherever possible, rely only on the information available in CONTEXT, address the instruction given in the PROMPT, and be formatted in Markdown. This is to be communicated in the prompt. Do not shy away from using long, detailed and descriptive prompts! Tell the model not to output any other text, just the report. The result produced by the language model is to be called REPORT. 10 | 8. Send a message with type "report_done", indicating that the report has been completed. Add "report" as a field containing the REPORT to be an additional payload to the message. 11 | 9. Output the REPORT. 12 | -------------------------------------------------------------------------------- /frontend/src/components/ResearchProgressList.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | .wrapper { 18 | width: 100%; 19 | height: 400px; /* Same as max-height of container */ 20 | display: flex; 21 | flex-direction: column; 22 | } 23 | 24 | .content { 25 | width: 100%; 26 | height: 100%; 27 | opacity: 0; 28 | transform: translateY(10px); 29 | animation: fadeIn 0.3s ease-out forwards; 30 | } 31 | 32 | @keyframes fadeIn { 33 | from { 34 | opacity: 0; 35 | transform: translateY(10px); 36 | } 37 | to { 38 | opacity: 1; 39 | transform: translateY(0); 40 | } 41 | } 42 | 43 | .container { 44 | width: 100%; 45 | max-height: 400px; 46 | min-height: 0; /* Allows container to shrink below max-height */ 47 | display: flex; 48 | flex-direction: column; 49 | background-color: rgb(243 244 246); 50 | border-radius: 1rem; 51 | overflow: hidden; 52 | transition: all 0.3s ease-out; 53 | } 54 | 55 | .list { 56 | flex: 1; 57 | overflow-y: auto; 58 | scrollbar-width: none; 59 | -ms-overflow-style: none; 60 | transition: all 0.3s ease-out; 61 | } 62 | 63 | .list::-webkit-scrollbar { 64 | display: none; 65 | } 66 | 67 | .list:hover { 68 | scrollbar-width: thin; 69 | -ms-overflow-style: auto; 70 | } 71 | 72 | .list:hover::-webkit-scrollbar { 73 | display: block; 74 | width: 8px; 75 | } 76 | 77 | .list:hover::-webkit-scrollbar-track { 78 | background: rgb(229 231 235); 79 | border-radius: 4px; 80 | } 81 | 82 | .list:hover::-webkit-scrollbar-thumb { 83 | background: rgb(156 163 175); 84 | border-radius: 4px; 85 | } 86 | 87 | /* Dark mode styles */ 88 | @media (prefers-color-scheme: dark) { 89 | .container { 90 | background-color: rgb(31 41 55); 91 | } 92 | 93 | .list:hover::-webkit-scrollbar-track { 94 | background: rgb(55 65 81); 95 | } 96 | 97 | .list:hover::-webkit-scrollbar-thumb { 98 | background: rgb(107 114 128); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /frontend/src/components/ResearchProgressItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 'use client'; 18 | 19 | import { ArrowDownOnSquareIcon, BoltIcon, ExclamationCircleIcon, BeakerIcon, MagnifyingGlassIcon, DocumentMagnifyingGlassIcon, DocumentCheckIcon, ClipboardDocumentListIcon, ArchiveBoxIcon, Cog8ToothIcon, LightBulbIcon, DocumentTextIcon, CheckCircleIcon, ChatBubbleLeftEllipsisIcon } from '@heroicons/react/24/outline'; 20 | import { ResearchEvent } from './ResearchProgressList'; 21 | import styles from './ResearchProgressItem.module.css'; 22 | 23 | interface ResearchProgressItemProps { 24 | event: ResearchEvent; 25 | } 26 | 27 | const iconMap = { 28 | 'started': BoltIcon, 29 | 'completed': CheckCircleIcon, 30 | 'error': ExclamationCircleIcon, 31 | 32 | 'prompt_received': ArrowDownOnSquareIcon, 33 | 'prompt_analysis_started': BeakerIcon, 34 | 'prompt_analysis_completed': DocumentTextIcon, 35 | 'task_analysis_completed': DocumentTextIcon, 36 | 'topic_exploration_started': DocumentMagnifyingGlassIcon, 37 | 'topic_exploration_completed': DocumentCheckIcon, 38 | 'search_started': MagnifyingGlassIcon, 39 | 'search_result_processing_started': ClipboardDocumentListIcon, 40 | 'aggregation_started': ArchiveBoxIcon, 41 | 'research_completed': CheckCircleIcon, 42 | 'report_building': Cog8ToothIcon, 43 | 'report_processing': LightBulbIcon, 44 | 'report_done': DocumentTextIcon, 45 | 'generic': ChatBubbleLeftEllipsisIcon 46 | }; 47 | 48 | export default function ResearchProgressItem({ event }: ResearchProgressItemProps) { 49 | const Icon = event.type in iconMap ? iconMap[event.type] : ChatBubbleLeftEllipsisIcon; 50 | const iconColor = event.type === 'error' ? 'text-red-500' : 'text-gray-500'; 51 | 52 | return ( 53 |{event.description}
59 | 60 | {new Date(event.timestamp).toLocaleTimeString()} 61 | 62 | tags, or inline) and surrounding text
8 |
9 | Your job is to:
10 | 1. Extract all code that is intended to be stored as a reusable skill (i.e., class or function definitions), as indicated by the message and its instructions.
11 | 2. If the code is not in Python, translate it to Python as faithfully as possible, using standard libraries and idiomatic Python where appropriate.
12 | 3. If the code is pseudo-code, incomplete, or contains small errors (e.g., typos, misspelled variable names, or incorrect standard library usage), correct these issues to produce valid, well-formed Python class/function definitions.
13 | 4. If the surrounding instructions request modifications (e.g., change argument names, add docstrings, use a different algorithm, add or adjust comments), make those changes in the final code.
14 | 5. If the message provides only a code body, or describes arguments or docstrings in natural language, construct the appropriate function or class definition, inferring argument names, types, and docstrings as needed from the context.
15 | 6. All class and function definitions should be type-annotated wherever possible, and include informative docstrings that describe their purpose, arguments, and return values.
16 | 7. Comments provided in the input should be preserved and adjusted as necessary. Add comments to the code wherever the operations performed are not easily understood at a glance.
17 | 8. If there are multiple class or function definitions, output them together as a single, coherent Python module.
18 | 9. Do not include any explanatory text, comments about your process, or formatting (such as markdown backticks or code tags) in your output—output only the final Python class and function definitions.
19 |
20 | Examples:
21 |
22 | Example 1:
23 | MESSAGE:
24 | Please store this function for future use:
25 | ```
26 | def add(a, b):
27 | return a + b
28 | ```
29 | Add type annotations and a docstring.
30 |
31 | OUTPUT:
32 | def add(a: int, b: int) -> int:
33 | """Add two integers and return the result."""
34 | return a + b
35 |
36 | Example 2:
37 | MESSAGE:
38 | Convert this JavaScript function to Python and make it reusable:
39 | ```
40 | function greet(name) {
41 | // Print a greeting
42 | console.log('Hello, ' + name);
43 | }
44 | ```
45 |
46 | OUTPUT:
47 | def greet(name: str) -> None:
48 | """Print a greeting to the given name."""
49 | # Print a greeting
50 | print(f"Hello, {name}")
51 |
52 | Example 3:
53 | MESSAGE:
54 | Here's a code body. Please turn it into a function that takes a list of numbers and returns their mean. Add type annotations and a docstring.
55 | ```
56 | total = 0
57 | for x in numbers:
58 | total += x
59 | return total / len(numbers)
60 | ```
61 |
62 | OUTPUT:
63 | def mean(numbers: list[float]) -> float:
64 | """Calculate and return the mean of a list of numbers."""
65 | total = 0
66 | for x in numbers:
67 | total += x
68 | return total / len(numbers)
69 |
70 | Example 4:
71 | MESSAGE:
72 | Hi, I will now teach you how to handle a person entity. Below is some code functionality for what I have in mind:
73 | ```
74 | class Person:
75 | def __init__(self, name: str, age: int) -> None:
76 | self.name = name # The person's name
77 | self.age = age # The person's age
78 |
79 | def greet(self) -> str:
80 | return f"Hello, my name is {self.name}."
81 | ```
82 |
83 | OUTPUT:
84 | class Person:
85 | """A class representing a person with a name and age."""
86 |
87 | def __init__(self, name: str, age: int) -> None:
88 | """Initialize a new Person with a name and age."""
89 | self.name = name # The person's name
90 | self.age = age # The person's age
91 |
92 | def greet(self) -> str:
93 | """Return a greeting message including the person's name."""
94 | return f"Hello, my name is {self.name}."
95 |
96 | Example 5:
97 | MESSAGE:
98 | Here is a new skill for you -- computing the kth fibonacci number.
99 |
100 | def fib(k: int) -> int:
101 | if k < 0:
102 | raise ValueError("k must be a non-negative integer")
103 | elif k == 0:
104 | return 0
105 | elif k == 1:
106 | return 1
107 | a, b = 0, 1
108 | for _ in range(2, k + 1):
109 | a, b = b, a + b
110 | return b
111 |
112 | OUTPUT:
113 | def fib(k: int) -> int:
114 | """Compute and return the k-th Fibonacci number (0-indexed)."""
115 | if k < 0:
116 | raise ValueError("k must be a non-negative integer")
117 | elif k == 0:
118 | return 0
119 | elif k == 1:
120 | return 1
121 | a, b = 0, 1
122 | for _ in range(2, k + 1):
123 | a, b = b, a + b
124 | return b
125 |
126 | Reminders:
127 | - Output only the final, properly formatted Python class and function definitions, with no extra text or formatting.
128 | - Add type annotations and docstrings wherever possible.
129 | - Preserve and add comments as appropriate.
130 | - If translation or correction is not possible, output the closest valid Python class/function definitions that fulfill the user's intent.
131 | - Do not output any surrounding backticks (```) or code tags—output just the code directly.
132 |
133 | This is the end of the classification instructions. Below is the input to process.
134 |
135 | MESSAGE:
136 | {message}
--------------------------------------------------------------------------------
/backend/setup.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #!/usr/bin/env python3
16 | """
17 | Setup script for the Universal Deep Research Backend (UDR-B).
18 |
19 | This script helps users configure the backend by:
20 | 1. Creating necessary directories
21 | 2. Setting up environment configuration
22 | 3. Validating API key files
23 | 4. Installing dependencies
24 | """
25 |
26 | import os
27 | import subprocess
28 | import sys
29 | from pathlib import Path
30 |
31 |
32 | def print_header(title: str):
33 | """Print a formatted header."""
34 | print(f"\n{'='*60}")
35 | print(f" {title}")
36 | print(f"{'='*60}")
37 |
38 |
39 | def print_step(step: str):
40 | """Print a step message."""
41 | print(f"\n→ {step}")
42 |
43 |
44 | def check_python_version():
45 | """Check if Python version is compatible."""
46 | print_step("Checking Python version...")
47 | if sys.version_info < (3, 8):
48 | print("❌ Python 3.8 or higher is required")
49 | sys.exit(1)
50 | print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor} detected")
51 |
52 |
53 | def create_directories():
54 | """Create necessary directories."""
55 | print_step("Creating directories...")
56 | directories = ["logs", "instances", "mock_instances"]
57 |
58 | for directory in directories:
59 | Path(directory).mkdir(exist_ok=True)
60 | print(f"✅ Created directory: {directory}")
61 |
62 |
63 | def setup_env_file():
64 | """Set up environment configuration file."""
65 | print_step("Setting up environment configuration...")
66 |
67 | env_file = Path(".env")
68 | env_example = Path("env.example")
69 |
70 | if env_file.exists():
71 | print("✅ .env file already exists")
72 | return
73 |
74 | if env_example.exists():
75 | # Copy example file
76 | with open(env_example, "r") as f:
77 | content = f.read()
78 |
79 | with open(env_file, "w") as f:
80 | f.write(content)
81 |
82 | print("✅ Created .env file from env.example")
83 | print("⚠️ Please edit .env file with your configuration")
84 | else:
85 | print("❌ env.example file not found")
86 | print("Creating basic .env file...")
87 |
88 | basic_env = """# Universal Deep Research Backend (UDR-B) - Environment Configuration
89 | HOST=0.0.0.0
90 | PORT=8000
91 | LOG_LEVEL=info
92 | FRONTEND_URL=http://localhost:3000
93 | DEFAULT_MODEL=llama-3.1-nemotron-253b
94 | LLM_API_KEY_FILE=nvdev_api.txt
95 | TAVILY_API_KEY_FILE=tavily_api.txt
96 | MAX_TOPICS=1
97 | MAX_SEARCH_PHRASES=1
98 | LOG_DIR=logs
99 | """
100 | with open(env_file, "w") as f:
101 | f.write(basic_env)
102 |
103 | print("✅ Created basic .env file")
104 | print("⚠️ Please edit .env file with your configuration")
105 |
106 |
107 | def check_api_keys():
108 | """Check for required API key files."""
109 | print_step("Checking API key files...")
110 |
111 | required_files = ["nvdev_api.txt", "tavily_api.txt"]
112 |
113 | missing_files = []
114 | for file in required_files:
115 | if Path(file).exists():
116 | print(f"✅ {file} found")
117 | else:
118 | print(f"❌ {file} missing")
119 | missing_files.append(file)
120 |
121 | if missing_files:
122 | print(f"\n⚠️ Missing API key files: {', '.join(missing_files)}")
123 | print("Please create these files with your API keys:")
124 | for file in missing_files:
125 | print(f" - {file}")
126 | print("\nExample:")
127 | print(" echo 'your-api-key-here' > nvdev_api.txt")
128 | print(" echo 'your-tavily-key-here' > tavily_api.txt")
129 |
130 |
131 | def install_dependencies():
132 | """Install Python dependencies."""
133 | print_step("Installing dependencies...")
134 |
135 | try:
136 | subprocess.run(
137 | [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
138 | check=True,
139 | capture_output=True,
140 | text=True,
141 | )
142 | print("✅ Dependencies installed successfully")
143 | except subprocess.CalledProcessError as e:
144 | print(f"❌ Failed to install dependencies: {e}")
145 | print("Please run: pip install -r requirements.txt")
146 |
147 |
148 | def validate_setup():
149 | """Validate the setup."""
150 | print_step("Validating setup...")
151 |
152 | # Check if main modules can be imported
153 | try:
154 | import clients
155 | import config
156 | import main
157 | import scan_research
158 |
159 | print("✅ All modules can be imported")
160 | except ImportError as e:
161 | print(f"❌ Import error: {e}")
162 | return False
163 |
164 | # Check if directories exist
165 | required_dirs = ["logs", "instances"]
166 | for directory in required_dirs:
167 | if not Path(directory).exists():
168 | print(f"❌ Directory missing: {directory}")
169 | return False
170 |
171 | print("✅ Setup validation passed")
172 | return True
173 |
174 |
175 | def main():
176 | """Main setup function."""
177 | print_header("Universal Deep Research Backend (UDR-B) Setup")
178 |
179 | # Change to script directory
180 | script_dir = Path(__file__).parent
181 | os.chdir(script_dir)
182 |
183 | check_python_version()
184 | create_directories()
185 | setup_env_file()
186 | check_api_keys()
187 | install_dependencies()
188 |
189 | if validate_setup():
190 | print_header("Setup Complete!")
191 | print("🎉 Your Universal Deep Research Backend (UDR-B) is ready!")
192 | print("\nNext steps:")
193 | print("1. Edit .env file with your configuration")
194 | print("2. Create API key files (nvdev_api.txt, tavily_api.txt)")
195 | print("3. Run: ./launch_server.sh")
196 | print("4. Or run: uvicorn main:app --reload")
197 | print("\nFor more information, see README.md")
198 | else:
199 | print_header("Setup Failed")
200 | print("❌ Please fix the issues above and run setup again")
201 |
202 |
203 | if __name__ == "__main__":
204 | main()
205 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # NVR Universal Deep Research - Frontend
2 |
3 | A Next.js frontend application for the NVR Universal Deep Research system that provides an interactive interface for intelligent research and reporting capabilities.
4 |
5 | This software is provided exclusively for research and demonstration purposes. It is intended solely as a prototype to demonstrate research concepts and methodologies in artificial intelligence and automated research systems.
6 |
7 | - This software is not intended for production deployment, commercial use, or any real-world application where reliability, accuracy, or safety is required.
8 | - This software contains experimental features, unproven methodologies, and research-grade implementations that may contain bugs, security vulnerabilities, or other issues.
9 | - The software is provided "AS IS" without any warranties. Neither NVIDIA Corporation nor the authors shall be liable for any damages arising from the use of this software to the fullest extent permitted by law.
10 |
11 | By using this software, you acknowledge that you have read and understood the complete DISCLAIMER file and agree to be bound by its terms. For the complete legal disclaimer, please see the [DISCLAIMER](DISCLAIMER.txt) file in this directory.
12 |
13 | ## Features
14 |
15 | - Interactive research interface with real-time progress tracking
16 | - Configurable research strategies
17 | - Support for both V1 and V2 API endpoints
18 | - Dry run mode for testing
19 | - Real-time report generation and viewing
20 |
21 | ## Configuration
22 |
23 | The frontend application is highly configurable through environment variables. Copy the example environment file and customize it for your deployment:
24 |
25 | ```bash
26 | cp env.example .env.local
27 | ```
28 |
29 | ### Environment Variables
30 |
31 | #### Backend API Configuration
32 |
33 | - `NEXT_PUBLIC_BACKEND_BASE_URL`: The base URL of your backend server (default: `http://localhost`)
34 | - `NEXT_PUBLIC_BACKEND_PORT`: The port your backend server is running on (default: `8000`)
35 | - `NEXT_PUBLIC_API_VERSION`: API version to use - `v1` or `v2` (default: `v2`)
36 |
37 | #### Runtime Configuration
38 |
39 | - `NEXT_PUBLIC_DRY_RUN`: Enable dry run mode - `true` or `false` (default: `false`)
40 | - `NEXT_PUBLIC_ENABLE_V2_API`: Enable V2 API - `true` or `false` (default: `true`)
41 |
42 | #### Frontend Configuration
43 |
44 | - `NEXT_PUBLIC_FRONTEND_PORT`: The port for the frontend development server (default: `3000`)
45 | - `NEXT_PUBLIC_FRONTEND_HOST`: The host for the frontend development server (default: `localhost`)
46 |
47 | ### Example Configuration
48 |
49 | For a production deployment with a backend on a different server:
50 |
51 | ```bash
52 | NEXT_PUBLIC_BACKEND_BASE_URL=http://your-backend-server.com
53 | NEXT_PUBLIC_BACKEND_PORT=8000
54 | NEXT_PUBLIC_API_VERSION=v2
55 | NEXT_PUBLIC_DRY_RUN=false
56 | NEXT_PUBLIC_ENABLE_V2_API=true
57 | ```
58 |
59 | For local development:
60 |
61 | ```bash
62 | NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost
63 | NEXT_PUBLIC_BACKEND_PORT=8000
64 | NEXT_PUBLIC_API_VERSION=v2
65 | NEXT_PUBLIC_DRY_RUN=true
66 | NEXT_PUBLIC_ENABLE_V2_API=true
67 | ```
68 |
69 | ## Getting Started
70 |
71 | 1. **Install dependencies:**
72 |
73 | ```bash
74 | npm install
75 | ```
76 |
77 | 2. **Configure environment variables:**
78 |
79 | ```bash
80 | cp env.example .env.local
81 | # Edit .env.local with your configuration
82 | ```
83 |
84 | 3. **Run the development server:**
85 |
86 | ```bash
87 | npm run dev
88 | ```
89 |
90 | 4. **Open your browser:**
91 | Navigate to [http://localhost:3000](http://localhost:3000) to see the application.
92 |
93 | ## Available Scripts
94 |
95 | - `npm run dev` - Start the development server with Turbopack
96 | - `npm run build` - Build the application for production
97 | - `npm run start` - Start the production server
98 | - `npm run lint` - Run ESLint
99 |
100 | ## Project Structure
101 |
102 | ```
103 | src/
104 | ├── app/ # Next.js app directory
105 | │ ├── page.tsx # Main application page
106 | │ ├── layout.tsx # Root layout
107 | │ └── globals.css # Global styles
108 | ├── components/ # React components
109 | │ ├── PromptBar.tsx # Main input interface
110 | │ ├── ResearchProgressList.tsx # Progress tracking
111 | │ ├── ReportViewer.tsx # Report display
112 | │ └── ... # Other UI components
113 | ├── config/ # Configuration management
114 | │ └── index.ts # App configuration
115 | └── types/ # TypeScript type definitions
116 | └── ApplicationState.ts
117 | ```
118 |
119 | ## Backend Requirements
120 |
121 | This frontend requires a compatible backend server running the NVR Universal Deep Research API. The backend should:
122 |
123 | - Support both `/api/research` (V1) and `/api/research2` (V2) endpoints
124 | - Accept POST requests with JSON payloads
125 | - Return Server-Sent Events (SSE) for real-time progress updates
126 | - Support the dry run mode parameter
127 |
128 | ## Deployment
129 |
130 | The application can be deployed to any platform that supports Next.js:
131 |
132 | 1. Build the application: `npm run build`
133 | 2. Start the production server: `npm run start`
134 | 3. Set the appropriate environment variables for your deployment
135 |
136 | ### Example Deployment Platforms
137 |
138 | - **Vercel**: Connect your Git repository and set environment variables in the dashboard
139 | - **Netlify**: Build and deploy with environment variable configuration
140 | - **Railway**: Deploy with automatic environment variable management
141 | - **Self-hosted**: Run on your own server with proper environment configuration
142 |
143 | ## Troubleshooting
144 |
145 | ### Common Issues
146 |
147 | 1. **Backend Connection Errors**: Ensure your backend server is running and the `NEXT_PUBLIC_BACKEND_BASE_URL` and `NEXT_PUBLIC_BACKEND_PORT` are correctly configured.
148 |
149 | 2. **CORS Errors**: Make sure your backend server allows requests from your frontend domain.
150 |
151 | 3. **API Version Issues**: If you're experiencing API compatibility issues, try switching between `v1` and `v2` using the `NEXT_PUBLIC_API_VERSION` environment variable.
152 |
153 | ### Development Tips
154 |
155 | - Use `NEXT_PUBLIC_DRY_RUN=true` during development to avoid making actual API calls
156 | - The application supports hot reloading - changes to your code will be reflected immediately
157 | - Check the browser console for detailed error messages
158 |
159 | ## Contributing
160 |
161 | 1. Fork the repository
162 | 2. Create a feature branch
163 | 3. Make your changes
164 | 4. Test thoroughly
165 | 5. Submit a pull request
166 |
167 | ## License and Disclaimer
168 |
169 | This software is provided for research and demonstration purposes only. Please refer to the [DISCLAIMER](DISCLAIMER.txt) file for complete terms and conditions regarding the use of this software. You can find the license in [LICENSE](LICENSE.txt).
170 |
171 | **Do not use this code in production.**
172 |
--------------------------------------------------------------------------------
/frontend/src/components/ReportViewer.module.css:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 | * SPDX-License-Identifier: Apache-2.0
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | .wrapper {
18 | width: 100%;
19 | height: 100%;
20 | display: flex;
21 | flex-direction: column;
22 | }
23 |
24 | .content {
25 | width: 100%;
26 | height: 100%;
27 | opacity: 0;
28 | transform: translateY(20px);
29 | animation: fadeInUp 0.5s ease-out forwards;
30 | }
31 |
32 | @keyframes fadeInUp {
33 | 0% {
34 | opacity: 0;
35 | transform: translateY(20px);
36 | }
37 | 100% {
38 | opacity: 1;
39 | transform: translateY(0);
40 | }
41 | }
42 |
43 | .container {
44 | width: 100%;
45 | max-height: 100%;
46 | min-height: 0; /* Allows container to shrink below max-height */
47 | display: flex;
48 | flex-direction: column;
49 | background-color: rgb(243 244 246);
50 | border-radius: 1rem;
51 | overflow: hidden;
52 | transition: all 0.3s ease-out;
53 | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
54 | 0 2px 4px -1px rgba(0, 0, 0, 0.06);
55 | }
56 |
57 | .header {
58 | height: 3.5rem;
59 | border-bottom: 1px solid rgb(229 231 235);
60 | background-color: rgb(243 244 246);
61 | display: flex;
62 | align-items: center;
63 | justify-content: space-between;
64 | padding: 1rem;
65 | }
66 |
67 | .title {
68 | color: rgb(17 24 39);
69 | font-size: 1rem;
70 | font-weight: 600;
71 | }
72 |
73 | .reportContent {
74 | flex: 1;
75 | overflow-y: auto;
76 | padding: 1rem;
77 | scrollbar-width: none;
78 | -ms-overflow-style: none;
79 | transition: all 0.3s ease-out;
80 | }
81 |
82 | .reportContent::-webkit-scrollbar {
83 | display: none;
84 | }
85 |
86 | .reportContent:hover {
87 | scrollbar-width: thin;
88 | -ms-overflow-style: auto;
89 | }
90 |
91 | .reportContent:hover::-webkit-scrollbar {
92 | display: block;
93 | width: 8px;
94 | }
95 |
96 | .reportContent:hover::-webkit-scrollbar-track {
97 | background: rgb(229 231 235);
98 | border-radius: 4px;
99 | }
100 |
101 | .reportContent:hover::-webkit-scrollbar-thumb {
102 | background: rgb(156 163 175);
103 | border-radius: 4px;
104 | }
105 |
106 | /* Markdown styling */
107 | .reportContent h1 {
108 | font-size: 1.5rem;
109 | font-weight: 600;
110 | margin-top: 1.5rem;
111 | margin-bottom: 1rem;
112 | color: rgb(17 24 39);
113 | }
114 |
115 | .reportContent h2 {
116 | font-size: 1.25rem;
117 | font-weight: 600;
118 | margin-top: 1.25rem;
119 | margin-bottom: 0.75rem;
120 | color: rgb(17 24 39);
121 | }
122 |
123 | .reportContent h3 {
124 | font-size: 1.125rem;
125 | font-weight: 600;
126 | margin-top: 1rem;
127 | margin-bottom: 0.5rem;
128 | color: rgb(17 24 39);
129 | }
130 |
131 | .reportContent p {
132 | margin-bottom: 1rem;
133 | line-height: 1.6;
134 | color: rgb(55 65 81);
135 | }
136 |
137 | .reportContent ul,
138 | .reportContent ol {
139 | margin-bottom: 1rem;
140 | padding-left: 1.5rem;
141 | color: rgb(55 65 81);
142 | }
143 |
144 | .reportContent li {
145 | margin-bottom: 0.5rem;
146 | line-height: 1.6;
147 | }
148 |
149 | .reportContent a {
150 | color: rgb(79 70 229);
151 | text-decoration: none;
152 | }
153 |
154 | .reportContent a:hover {
155 | text-decoration: underline;
156 | }
157 |
158 | .reportContent code {
159 | background-color: rgb(229 231 235);
160 | padding: 0.2rem 0.4rem;
161 | border-radius: 0.25rem;
162 | font-family: monospace;
163 | font-size: 0.875rem;
164 | color: rgb(17 24 39);
165 | }
166 |
167 | .reportContent pre {
168 | background-color: rgb(17 24 39);
169 | padding: 1rem;
170 | border-radius: 0.5rem;
171 | overflow-x: auto;
172 | margin-bottom: 1rem;
173 | }
174 |
175 | .reportContent pre code {
176 | background-color: transparent;
177 | padding: 0;
178 | color: rgb(209 213 219);
179 | }
180 |
181 | .reportContent blockquote {
182 | border-left: 4px solid rgb(156 163 175);
183 | padding-left: 1rem;
184 | color: rgb(107 114 128);
185 | margin-bottom: 1rem;
186 | }
187 |
188 | .reportContent hr {
189 | border: 0;
190 | height: 1px;
191 | background-color: rgb(229 231 235);
192 | margin: 0.5rem 0;
193 | position: relative;
194 | }
195 |
196 | .reportContent hr::before {
197 | content: "";
198 | position: absolute;
199 | top: -0.125rem;
200 | left: 0;
201 | right: 0;
202 | height: 0.125rem;
203 | background: transparent;
204 | }
205 |
206 | .reportContent hr::after {
207 | content: "";
208 | position: absolute;
209 | bottom: -0.125rem;
210 | left: 0;
211 | right: 0;
212 | height: 0.125rem;
213 | background: transparent;
214 | }
215 |
216 | .reportContent table {
217 | width: 100%;
218 | border-collapse: collapse;
219 | margin-bottom: 1rem;
220 | }
221 |
222 | .reportContent th,
223 | .reportContent td {
224 | border: 1px solid rgb(229 231 235);
225 | padding: 0.5rem 0.75rem;
226 | text-align: left;
227 | }
228 |
229 | .reportContent th {
230 | background-color: rgb(243 244 246);
231 | font-weight: 600;
232 | }
233 |
234 | /* Dark mode styles */
235 | @media (prefers-color-scheme: dark) {
236 | .container,
237 | .header {
238 | background-color: rgb(31 41 55);
239 | }
240 |
241 | .header {
242 | border-color: rgb(55 65 81);
243 | }
244 |
245 | .title {
246 | color: rgb(243 244 246);
247 | }
248 |
249 | .reportContent h1,
250 | .reportContent h2,
251 | .reportContent h3 {
252 | color: rgb(243 244 246);
253 | }
254 |
255 | .reportContent p,
256 | .reportContent li {
257 | color: rgb(209 213 219);
258 | }
259 |
260 | .reportContent a {
261 | color: rgb(129 140 248);
262 | }
263 |
264 | .reportContent code {
265 | background-color: rgb(55 65 81);
266 | color: rgb(209 213 219);
267 | }
268 |
269 | .reportContent pre {
270 | background-color: rgb(17 24 39);
271 | }
272 |
273 | .reportContent pre code {
274 | color: rgb(243 244 246);
275 | }
276 |
277 | .reportContent blockquote {
278 | border-color: rgb(107 114 128);
279 | color: rgb(156 163 175);
280 | }
281 |
282 | .reportContent th,
283 | .reportContent td {
284 | border-color: rgb(55 65 81);
285 | }
286 |
287 | .reportContent th {
288 | background-color: rgb(31 41 55);
289 | }
290 |
291 | .reportContent:hover::-webkit-scrollbar-track {
292 | background: rgb(55 65 81);
293 | }
294 |
295 | .reportContent:hover::-webkit-scrollbar-thumb {
296 | background: rgb(107 114 128);
297 | }
298 |
299 | .reportContent hr {
300 | background-color: rgb(75 85 99);
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/backend/frame/errands4.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | import re
16 |
17 | from frame.clients import Client
18 |
19 |
20 | def namify_model_path(hf_model_path: str) -> str:
21 | return hf_model_path.replace("/", "_").replace("-", "_").replace(" ", "_")
22 |
23 |
24 | class Errand:
25 | def __init__(self, pre_prompt: str, prompt: str) -> None:
26 | self.pre_prompt = pre_prompt
27 | self.prompt = prompt
28 |
29 | def run(self, runner: Client, args: dict, completion_config: dict = {}) -> str:
30 | prompt_with_args = self.prompt
31 | for arg_name, arg_value in args.items():
32 | prompt_with_args = prompt_with_args.replace("{" + arg_name + "}", arg_value)
33 |
34 | ret = runner.run(self.pre_prompt, prompt_with_args, completion_config)
35 | return ret
36 |
37 |
38 | class FileErrand(
39 | Errand
40 | ): # NOTE: this is only for easier debug for now; production code should use errands directly
41 | def __init__(self, errand_path: str) -> None:
42 | with open(errand_path, "r") as f:
43 | errand = f.read()
44 |
45 | errand_parts = errand.split("===SEPARATOR===")
46 | if len(errand_parts) != 2:
47 | raise ValueError(
48 | f"Errand file {errand_path} is not valid. It should contain exactly one separator."
49 | )
50 | pre_prompt = errand_parts[0].strip()
51 | prompt = errand_parts[1].strip()
52 | super().__init__(pre_prompt, prompt)
53 |
54 |
55 | class MultipleChoiceErrand(FileErrand):
56 | def __init__(self, errand_path: str, choices: list[str]) -> None:
57 | super().__init__(errand_path)
58 | self.choices = choices
59 |
60 | def run(
61 | self, runner: Client, args: dict, completion_config: dict = {}
62 | ) -> str | None:
63 | raw_completion = super().run(runner, args, completion_config)
64 |
65 | for choice in self.choices:
66 | if choice in raw_completion:
67 | return choice
68 |
69 | return None
70 |
71 |
72 | import os
73 | from pathlib import Path
74 |
75 |
76 | class MessageTypeErrand(MultipleChoiceErrand):
77 | def __init__(self) -> None:
78 | errand_path = os.path.join(
79 | Path(__file__).resolve().parent, "errands/message_type.txt"
80 | )
81 | choices = [
82 | "code_skill",
83 | "routine_skill",
84 | "query_skill",
85 | "code",
86 | "routine",
87 | "query",
88 | "data",
89 | ]
90 | super().__init__(errand_path, choices)
91 |
92 |
93 | class MessageCodeProcessingErrand(FileErrand):
94 | def __init__(self) -> None:
95 | errand_path = os.path.join(
96 | Path(__file__).resolve().parent, "errands/message_code_processing.txt"
97 | )
98 | super().__init__(errand_path)
99 |
100 |
101 | class MessageCodeSkillProcessingErrand(FileErrand):
102 | def __init__(self) -> None:
103 | errand_path = os.path.join(
104 | Path(__file__).resolve().parent, "errands/message_code_skill_processing.txt"
105 | )
106 | super().__init__(errand_path)
107 |
108 |
109 | class MessageCodeCallErrand(FileErrand):
110 | def __init__(self) -> None:
111 | errand_path = os.path.join(
112 | Path(__file__).resolve().parent, "errands/message_code_call.txt"
113 | )
114 | super().__init__(errand_path)
115 |
116 |
117 | class MessageCodeVariablesErrand(FileErrand):
118 | def __init__(self) -> None:
119 | errand_path = os.path.join(
120 | Path(__file__).resolve().parent, "errands/message_code_variables.txt"
121 | )
122 | super().__init__(errand_path)
123 |
124 |
125 | class MessageRoutineProcessingErrand(FileErrand):
126 | def __init__(self) -> None:
127 | errand_path = os.path.join(
128 | Path(__file__).resolve().parent, "errands/message_routine_processing.txt"
129 | )
130 | super().__init__(errand_path)
131 |
132 |
133 | class MessageGeneratingRoutineProcessingErrand(FileErrand):
134 | def __init__(self) -> None:
135 | errand_path = os.path.join(
136 | Path(__file__).resolve().parent,
137 | "errands/message_generating_routine_processing.txt",
138 | )
139 | super().__init__(errand_path)
140 |
141 |
142 | class MessageRoutineCallErrand(FileErrand):
143 | def __init__(self) -> None:
144 | errand_path = os.path.join(
145 | Path(__file__).resolve().parent, "errands/message_routine_call.txt"
146 | )
147 | super().__init__(errand_path)
148 |
149 |
150 | class MessageGeneratingRoutineCallErrand(FileErrand):
151 | def __init__(self) -> None:
152 | errand_path = os.path.join(
153 | Path(__file__).resolve().parent,
154 | "errands/message_generating_routine_call.txt",
155 | )
156 | super().__init__(errand_path)
157 |
158 |
159 | class MessageRoutineVariablesErrand(FileErrand):
160 | def __init__(self) -> None:
161 | errand_path = os.path.join(
162 | Path(__file__).resolve().parent, "errands/message_routine_variables.txt"
163 | )
164 | super().__init__(errand_path)
165 |
166 |
167 | class MessageDataProcessingErrand(FileErrand):
168 | def __init__(self) -> None:
169 | errand_path = os.path.join(
170 | Path(__file__).resolve().parent, "errands/message_data_processing.txt"
171 | )
172 | super().__init__(errand_path)
173 |
174 |
175 | default_errand_profile: dict[str, type[Errand] | None] = {
176 | "message_type": MessageTypeErrand,
177 | "message_code_processing": MessageCodeProcessingErrand,
178 | "message_code_skill_processing": MessageCodeSkillProcessingErrand,
179 | "message_code_call": MessageCodeCallErrand,
180 | "message_code_variables": MessageCodeVariablesErrand,
181 | "message_routine_processing": MessageRoutineProcessingErrand,
182 | "message_generating_routine_processing": MessageGeneratingRoutineProcessingErrand,
183 | "message_routine_call": MessageRoutineCallErrand,
184 | "message_generating_routine_call": MessageGeneratingRoutineCallErrand,
185 | "message_routine_variables": MessageRoutineVariablesErrand,
186 | "message_data_processing": MessageDataProcessingErrand,
187 | "language_model": None, # on purpose, currently there is no errand but there might be a client profile # TODO: double-check if this is still needed in V4
188 | }
189 |
--------------------------------------------------------------------------------
/backend/frame/errands/message_code_processing.txt:
--------------------------------------------------------------------------------
1 | You are a helpful assistant for extracting and preparing executable Python code from user messages. Your task is to process the MESSAGE below, which may contain code (in any language or pseudo-code), natural language instructions, or a combination of both. Your goal is to output only the final, executable Python code, wrapped in a function named `code`, making any necessary modifications as described below. The function must return a tuple: (output: str, variables: dict[str, Any]), where the first element is the output string (what would be printed) and the second is a dictionary of all variables that have been modified in the original code segments/code modification instructions provided by the user.
2 |
3 | ===SEPARATOR===
4 | You are given a MESSAGE that may contain:
5 | - Python code, code in another language, or pseudo-code
6 | - Natural language instructions that may request modifications to code provided, corrections, or translation to Python
7 | - A mix of code blocks (e.g., in markdown backticks, tags, or inline) and surrounding text
8 |
9 | You are also given TIDINGS, which are variables, values, or context objects provided by the system. If you decide to use any of the tidings, the same name must also appear as an argument. Tidings are not globals -- they will be passed to the generated function in a function call as arguments. Each tiding is provided as a name, value, and description. If the MESSAGE requests code that uses a variable or value present in the tidings, use the tiding as an argument in the function. If the MESSAGE does not reference any tidings, you may ignore them.
10 |
11 | Your job is to:
12 | 1. Extract all code that is intended for execution, as indicated by the message and its instructions.
13 | 2. If the code is not in Python, translate it to Python as faithfully as possible, using standard libraries and idiomatic Python where appropriate.
14 | 3. If the code is pseudo-code or contains small errors (e.g., typos, misspelled variable names, or incorrect standard library usage), correct these issues to produce valid, executable Python code.
15 | 4. If the surrounding instructions request modifications (e.g., change a variable name, add a print statement, use a different algorithm), make those changes in the final code.
16 | 5. If there are multiple code blocks or fragments, combine them as needed to produce a single, coherent Python function that fulfills the user's intent.
17 | 6. Wrap all code in a function named `code`. The function must have a docstring describing what it does, based on the user's message and intent.
18 | 7. Any reference to external variables (not defined within the function) should be marked with a comment indicating that the variable is external and must be provided by the caller, unless it is provided as a tiding.
19 | 8. Any output that would be printed should instead be appended to a string accumulator (e.g., `__output`), and the function should return a tuple: (output, variables), where `output` is the string and `variables` is a dictionary of all variables that have been either modified in the original code segment present in the message or added/modified by the surrounding text.
20 | 9. Do not include any explanatory text, comments about your process, or formatting (such as markdown backticks) in your output—output only the final Python function definition.
21 |
22 | Examples:
23 |
24 | Example 1:
25 | MESSAGE:
26 | Please execute the following code:
27 | ```
28 | x = 5
29 | y = 10
30 | print(x + y)
31 | ```
32 | TIDINGS:
33 |
34 | OUTPUT:
35 | def code():
36 | """Execute the given code and return the output string and modified variables as a tuple."""
37 | output = ""
38 | try:
39 | x = 5
40 | y = 10
41 | output += str(x + y) + "\n"
42 | return output, { 'x': x, 'y': y }
43 | except Exception as e:
44 | return output + "\n\n" + str(e), { 'x': x, 'y': y }
45 |
46 | Example 2:
47 | MESSAGE:
48 | Run this JavaScript code, but in Python:
49 | ```
50 | let arr = [1, 2, 3];
51 | console.log(arr.map(x => x * 2));
52 | ```
53 | TIDINGS:
54 |
55 | OUTPUT:
56 | def code():
57 | """Double each element in the array and return the result string and newly defined / modified variables as a tuple."""
58 | output = ""
59 | try:
60 | arr = [1, 2, 3]
61 | doubled = [x * 2 for x in arr]
62 | output += str(doubled) + "\n"
63 | return output, { 'arr': arr, 'doubled': doubled }
64 | except Exception as e:
65 | return output + "\n\n" + str(e), { 'arr': arr, 'doubled': doubled }
66 |
67 | Example 3:
68 | MESSAGE:
69 | Here's some pseudo-code. Please execute it after translating to Python:
70 | - Set total to 0
71 | - For each number in [1,2,3,4], add it to total
72 | - Respond with total to me
73 | TIDINGS:
74 |
75 | OUTPUT:
76 | def code():
77 | """Sum the numbers in the list and return the total string and newly defined / modified variables as a tuple."""
78 | output = ""
79 | try:
80 | total = 0
81 | for number in [1, 2, 3, 4]:
82 | total += number
83 | output += str(total) + "\n"
84 | return output, { 'total': total }
85 | except Exception as e:
86 | return output + "\n\n" + str(e), { 'total': total }
87 |
88 | Example 4:
89 | MESSAGE:
90 | Execute this, but change the variable name from 'foo' to 'bar':
91 | ```
92 | foo = 42
93 | print(foo)
94 | ```
95 | TIDINGS:
96 |
97 | OUTPUT:
98 | def code():
99 | """Assign 42 to bar and return its value string and variables as a tuple."""
100 | output = ""
101 | try:
102 | bar = 42
103 | output += str(bar) + "\n"
104 | return output, { 'bar': bar }
105 | except Exception as e:
106 | return output + "\n\n" + str(e), { 'bar': bar }
107 |
108 | Example 5:
109 | MESSAGE:
110 | Please run this code (note: there is a typo in the function name):
111 | ```
112 | pritn('Hello, world!')
113 | ```
114 | TIDINGS:
115 |
116 | OUTPUT:
117 | def code():
118 | """Print 'Hello, world!' and return the output string and variables as a tuple."""
119 | output = ""
120 | try:
121 | output += 'Hello, world!\n'
122 | return output, {}
123 | except Exception as e:
124 | return output + "\n\n" + str(e), {}
125 |
126 | Example 6:
127 | MESSAGE:
128 | Use the provided user_name to greet the user.
129 | TIDINGS:
130 | user_name: Alice # The name of the user to greet
131 |
132 | OUTPUT:
133 | def code(user_name):
134 | """Greet the user by name using the provided user_name tiding, and return the output string and new/modified variables as a tuple."""
135 | output = ""
136 | try:
137 | output += f"Hello, {user_name}!\n"
138 | return output, { }
139 | except Exception as e:
140 | return output + "\n\n" + str(e), { }
141 |
142 | Reminders:
143 | - Output only the final, executable Python function definition, with no extra text or formatting.
144 | - Make reasonable corrections and modifications as requested or needed.
145 | - If translation or correction is not possible, output the closest valid Python function that fulfills the user's intent.
146 | - The function must always return a tuple: (output, variables), where output is a string and variables is a dictionary.
147 | - Do not output any surrounding backticks (```) or code tags—output just the code directly.
148 |
149 | This is the end of the classification instructions. Below is the input to classify.
150 |
151 | MESSAGE:
152 | {message}
153 | TIDINGS:
154 | {tidings}
--------------------------------------------------------------------------------
/backend/frame/errands/message_routine_processing.txt:
--------------------------------------------------------------------------------
1 | You are an expert Python code generator for an advanced AI system. Your job is to convert a user's natural language routine instruction (MESSAGE) into a single pure Python function called 'code'.
2 |
3 | ===SEPARATOR===
4 |
5 | You are an expert Python code generator for an advanced AI system. Convert user's natural language routine instruction (MESSAGE) into a single pure Python function called 'code'.
6 |
7 | You are provided with:
8 | - MESSAGE: The user's instruction, which may be a single short description of intended functionality or a multi-step list (bullets, numbered steps, etc.).
9 | - SKILLS: A list of available function signatures and docstrings you may call in your code.
10 | - TIDINGS: A list of available variables (with their values and descriptions) you may use as arguments or modify.
11 |
12 | Your function must:
13 | - Be named 'code' and use type hints for all arguments and the return value.
14 | - Accept as arguments any TIDINGS that are relevant to the MESSAGE.
15 | - Accumulate all user-facing output in a string variable called '__output' (do NOT use print). At the end, return this string as the first element of a tuple.
16 | - Return a tuple (output: str, modified_vars: dict[str, Any]), where 'output' is the accumulated output string, and 'modified_vars' is a dictionary of all variables that were modified by the function.
17 | - Have a high-quality docstring describing its purpose, arguments, and return value.
18 | - Use SKILLS as needed to accomplish the MESSAGE.
19 | - If the MESSAGE is a multi-step routine (bullets, numbered steps, or multiple sentences), interleave each step as a comment, followed by the code that implements that step. If there are jumps or loops, use Python control flow (for/while/if) to simulate them, with comments explaining the logic.
20 | - When in doubt, generate code that best accomplishes the user's intent.
21 | - Do not use print statements; all output must be accumulated in '__output'.
22 | - Modify variables that are listed in TIDINGS when referred to directly in natural language
23 | - Feel free to create new variables when the MESSAGE instructs to create new ones.
24 | - Sometimes you might want to create auxiliary/temporary variables. When that's the case, please prefix them by `__` (double underscore).
25 | - If the MESSAGE contains a payload text (for example, separated by a heading or clearly not an instruction), store that text without modification in a new variable with an appropriate name and a comment explaining its purpose. Do not process or change the payload text; just store it. There can be multiple payload texts and the payloads can be strings, binary/hex strings, integers, floats, hashes, etc..
26 |
27 | Example input:
28 | MESSAGE:
29 | """
30 | 1. Greet the user by name.
31 | 2. Add 10 to their score.
32 | 3. If their score is above 100, congratulate them.
33 | """
34 | SKILLS:
35 | - greet_user(name: str) -> str: Returns a greeting for the user.
36 | TIDINGS:
37 | - name = Alice # The user's name
38 | - score = 95 # The user's current score
39 |
40 | Example output:
41 | ```python
42 | def code(name: str, score: int) -> tuple[str, dict[str, Any]]:
43 | """
44 | Greets the user, updates their score, and congratulates them if their score exceeds 100.
45 | Args:
46 | name: The user's name.
47 | score: The user's current score.
48 | Returns:
49 | A tuple containing the output string and a dictionary of modified variables.
50 | """
51 | __output = ""
52 | # 1. Greet the user by name.
53 | __output += greet_user(name) + "\n"
54 | # 2. Add 10 to their score.
55 | score += 10
56 | # 3. If their score is above 100, congratulate them.
57 | if score > 100:
58 | __output += f"Congratulations, {name}! Your score is {score}.\n"
59 | __vars = {'score': score}
60 | return __output, __vars
61 | ```
62 |
63 | Example input:
64 | MESSAGE:
65 | """
66 | 1. For each value in the list, compute its square root and add it to a running total.
67 | 2. If the total exceeds 100, output a warning message.
68 | 3. Output the final total.
69 | """
70 | SKILLS:
71 | - format_warning(msg: str) -> str: Formats a warning message for the user.
72 | TIDINGS:
73 | - values = [4, 16, 25, 36, 49] # List of numbers
74 | - total = 0 # Running total
75 |
76 | Example output:
77 | ```python
78 | def code(values: list[float], total: float) -> tuple[str, dict[str, Any]]:
79 | """
80 | Computes the square root of each value, adds to total, warns if total > 100, and outputs the total.
81 | Args:
82 | values: List of numbers to process.
83 | total: Running total.
84 | Returns:
85 | A tuple containing the output string and a dictionary of modified variables.
86 | """
87 | import math
88 | __output = ""
89 | # 1. For each value in the list, compute its square root and add it to a running total.
90 | for __v in values:
91 | __sqrt = math.sqrt(__v)
92 | total += __sqrt
93 | # 2. If the total exceeds 100, output a warning message.
94 | if total > 100:
95 | __output += format_warning(f"Total exceeded 100: {total}") + "\n"
96 | # 3. Output the final total.
97 | __output += f"Final total: {total}\n"
98 | __vars = {'total': total}
99 | return __output, __vars
100 | ```
101 |
102 | Example input:
103 | MESSAGE:
104 | """
105 | 1. Check if the user is an admin.
106 | 2. If so, grant them access and log the event.
107 | 3. Otherwise, deny access and log the attempt.
108 | """
109 | SKILLS:
110 | - log_event(event: str) -> None: Logs an event string.
111 | - grant_access(user: str) -> str: Grants access to the user and returns a confirmation message.
112 | - deny_access(user: str) -> str: Denies access to the user and returns a denial message.
113 | TIDINGS:
114 | - user = bob # The username
115 | - is_admin = False # Whether the user is an admin
116 |
117 | Example output:
118 | ```python
119 | def code(user: str, is_admin: bool) -> tuple[str, dict[str, Any]]:
120 | """
121 | Checks admin status, grants or denies access, and logs the event.
122 | Args:
123 | user: The username.
124 | is_admin: Whether the user is an admin.
125 | Returns:
126 | A tuple containing the output string and a dictionary of modified variables.
127 | """
128 | __output = ""
129 | # 1. Check if the user is an admin.
130 | if is_admin:
131 | # 2. If so, grant them access and log the event.
132 | __output += grant_access(user) + "\n"
133 | log_event(f"Access granted to {user}")
134 | else:
135 | # 3. Otherwise, deny access and log the attempt.
136 | __output += deny_access(user) + "\n"
137 | log_event(f"Access denied to {user}")
138 | __vars = {}
139 | return __output, __vars
140 | ```
141 |
142 | Reminders:
143 | - Output a function called `code` relying on SKILLS and TIDINGS where needed
144 | - The function should be a pure function -- do not access global variables.
145 | - Prefer the functions described in SKILLS over external libraries
146 | - Output just the code of the function Do not output any other text.
147 |
148 | Now, generate the function as described above for the given MESSAGE, SKILLS, and TIDINGS.
149 |
150 | SKILLS:
151 | {skills}
152 |
153 | TIDINGS:
154 | {tidings}
155 |
156 | MESSAGE:
157 | {message}
158 |
159 | Reminders:
160 | - Output a function called `code` relying on SKILLS and TIDINGS where needed
161 | - The function should be a pure function -- do not access global variables.
162 | - Prefer the functions described in SKILLS over external libraries; these functions are available in the global scope and do not require imports.
163 | - Output just the code of the function Do not output any other text.
164 | - Remember to return two outputs: one for the aggregated function output, and one that is a dictionary of all variables that were modified by the function.
--------------------------------------------------------------------------------
/backend/frame/clients.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | from .trace import Trace
16 |
17 |
18 | class Client:
19 | def __init__(self, trace: Trace = None) -> None:
20 | self.trace = trace
21 |
22 | def run(self, pre_prompt: str, prompt: str, completion_config: dict = {}) -> str:
23 | self.trace_query(pre_prompt, prompt, completion_config)
24 |
25 | def run_messages(
26 | self,
27 | messages: list[dict],
28 | trace_input_messages: bool = True,
29 | completion_config: dict = {},
30 | ) -> list[dict]:
31 | if trace_input_messages:
32 | self.trace.write_separator()
33 | for message in messages:
34 | self.trace_message(message)
35 |
36 | def trace_message(self, message: dict) -> str:
37 | if self.trace is not None:
38 | self.trace(f"<<{message['role']}>>")
39 | self.trace(message["content"])
40 |
41 | def trace_query(
42 | self, pre_prompt: str, prompt: str, completion_config: dict = {}
43 | ) -> str:
44 | if self.trace is not None:
45 | self.trace.write_separator()
46 | self.trace("<>")
47 | self.trace(pre_prompt)
48 | self.trace("<>")
49 | self.trace(prompt)
50 |
51 | def trace_response(self, response: str) -> str:
52 | if self.trace is not None:
53 | self.trace("<>")
54 | self.trace(response)
55 |
56 |
57 | class HuggingFaceClient(Client):
58 | def __init__(
59 | self,
60 | model: str = None,
61 | pipeline_configuration: dict = {},
62 | seed: int = 42,
63 | api_key: str | None = None,
64 | trace: Trace = None,
65 | ) -> None:
66 | super().__init__(trace=trace)
67 |
68 | # to decrease the chance of hard-to-track bugs, we don't allow the 'model' key in pipeline_configuration
69 | if "model" in pipeline_configuration:
70 | raise ValueError(
71 | "The 'model' key is not allowed in pipeline_configuration. Use the 'model' parameter instead."
72 | )
73 |
74 | # data members
75 | self.model = model
76 | self.seed = seed
77 | if api_key is None:
78 | import os
79 |
80 | self.api_key = os.getenv("HUGGINGFACE_API_KEY")
81 | self.api_key = api_key
82 |
83 | # complete pipeline config
84 | from transformers import pipeline
85 |
86 | default_configuration = {
87 | "model": "meta-llama/Llama-3.1-8B-Instruct",
88 | "device": "cuda:0",
89 | "max_new_tokens": 1024,
90 | }
91 | merged_configuration = {**default_configuration, **pipeline_configuration}
92 |
93 | # model
94 | if self.model is not None:
95 | merged_configuration["model"] = self.model
96 |
97 | # seed
98 | from transformers import set_seed
99 |
100 | set_seed(self.seed)
101 |
102 | # tokenizer and pipeline for generation
103 | from transformers import AutoTokenizer
104 |
105 | self.tokenizer = AutoTokenizer.from_pretrained(
106 | default_configuration["model"], api_key=self.api_key
107 | )
108 | self.generator = pipeline("text-generation", **merged_configuration)
109 |
110 | def run(self, pre_prompt: str, prompt: str, completion_config: dict = {}) -> str:
111 | super().run(pre_prompt, prompt, completion_config)
112 | messages = [
113 | {"role": "system", "content": pre_prompt},
114 | {"role": "user", "content": prompt},
115 | ]
116 |
117 | completion = self.generator(
118 | messages,
119 | **completion_config,
120 | pad_token_id=self.generator.tokenizer.eos_token_id,
121 | )
122 |
123 | ret = completion[0]["generated_text"][-1]["content"]
124 | self.trace_response(ret)
125 | return ret
126 |
127 | def run_messages(
128 | self, messages, trace_input_messages: bool = True, completion_config={}
129 | ) -> list[dict]:
130 | super().run_messages(messages, trace_input_messages, completion_config)
131 | completion = self.generator(
132 | messages,
133 | **completion_config,
134 | pad_token_id=self.generator.tokenizer.eos_token_id,
135 | )
136 |
137 | ret = completion[0]["generated_text"]
138 | self.trace_message(ret[-1])
139 | return ret
140 |
141 |
142 | class OpenAIClient(Client):
143 | def __init__(
144 | self,
145 | base_url: str,
146 | model: str = None,
147 | seed: int = 42,
148 | api_key: str | None = None,
149 | trace: Trace = None,
150 | ) -> None:
151 | super().__init__(trace=trace)
152 |
153 | self.base_url = base_url
154 | self.model = (
155 | model
156 | if model is not None
157 | else "nvdev/nvidia/llama-3.1-nemotron-70b-instruct"
158 | )
159 | if api_key is None:
160 | import os
161 |
162 | api_key = os.getenv("NGC_API_KEY")
163 | self.api_key = api_key
164 | self.seed = seed
165 |
166 | from openai import OpenAI
167 |
168 | self.client = OpenAI(base_url=self.base_url, api_key=self.api_key)
169 |
170 | def _invoke(self, messages: list[dict], completion_config: dict = {}) -> str:
171 | default_settings = {
172 | "model": self.model,
173 | "top_p": 1,
174 | "temperature": 0.0,
175 | "max_tokens": 2048,
176 | "stream": True,
177 | "seed": self.seed,
178 | }
179 |
180 | merged_settings = {**default_settings, **completion_config}
181 | # go through the messages; if the role="ipython" rename it to "function"
182 | for message in messages:
183 | if message["role"] == "ipython":
184 | message["role"] = "function"
185 | completion = self.client.chat.completions.create(
186 | messages=messages, **merged_settings
187 | )
188 |
189 | ret: str = ""
190 | for chunk in completion:
191 | if chunk.choices[0].delta.content is not None:
192 | ret += chunk.choices[0].delta.content
193 |
194 | return ret
195 |
196 | def run(self, pre_prompt: str, prompt: str, completion_config: dict = {}) -> str:
197 | super().run(pre_prompt, prompt, completion_config)
198 |
199 | ret = self._invoke(
200 | messages=[
201 | {"role": "system", "content": pre_prompt},
202 | {"role": "user", "content": prompt},
203 | ],
204 | completion_config=completion_config,
205 | )
206 |
207 | self.trace_response(ret)
208 | return ret
209 |
210 | def run_messages(
211 | self, messages, trace_input_messages: bool = True, completion_config={}
212 | ) -> list[dict]:
213 | super().run_messages(messages, trace_input_messages, completion_config)
214 |
215 | ret_str: str = self._invoke(
216 | messages=messages, completion_config=completion_config
217 | )
218 | ret_msg: dict = {"role": "assistant", "content": ret_str}
219 |
220 | self.trace_message(ret_msg)
221 | return [*messages, ret_msg]
222 |
--------------------------------------------------------------------------------
/backend/frame/errands/message_type.txt:
--------------------------------------------------------------------------------
1 | You are a helpful assistant for detecting the type of the message send by the user. Based on the contents of and instructions in the MESSAGE, respond with one of the following message types: code, code_skill, routine, routine_skill, query, query_skill, data. Do not output any other text.
2 |
3 | ===SEPARATOR===
4 | You are deciding how the contents of MESSAGE will be handled by an AI system. To do so, choose on of the following TYPES that best characterizes the nature of the message. Every message can have only one type. Do not output any other text.
5 |
6 | TYPES:
7 | code
8 | ---
9 | code is the type of messages that contain code that is to be directly executed. The entire message can be
10 | - a directly pasted code snippet which is not just function definitions; or
11 | - it can be a human-friendly message with the code section to be executed enclosed in markdown single/triple backticks (e.g. `my one-line code` or ```my code```) or some other obvious separators (e.g. ). In this case, the surrounding human instructions must indicate that this code is to be executed rather than stored as a skill. Also, the surrounding instructions (that is, instructions that are outside obviously marked code blocks) might describe some further circumstances of the code. There can be more than one code block in the message, but the idea is that all the code blocks can be concatenated and executed as one, give or take some small modifications due to surrounding instructions.
12 | Note that messages asking to generate code (even when including examples) are not of type code but either query or query skill.
13 | Note that messages that ask the AI system to store some function for future use, or that are not accompanied by surrounding natural language instruction but are just definitions rather than actionable code are not of type code but likely of type code_skill. Simply put, if your message is just a function definition, it's a code_skill, not code.
14 | Note that messages that provide only pseudo-code that needs to be processed into executable Python code are not of type code but likely of type routine or routine_skill.
15 |
16 | code_skill
17 | ---
18 | code_skill is the type of messages that contain code that is to be stored as a future functio(s), skill(s), or tool(s). The entire message can be
19 | - a directly pasted code snippet which contains only definitions of a single function or multiple functions, classes, etc.. but not statements
20 | - a human-friendly message with the code section(s) that contain(s) definitions enclosed in markdown triple backticks or some other obvious separators (e.g. ```def my_function()...``` or ``````). In this case, the surrounding human instructions can also explicitly hint that this code is to be stored as function(s) for future use rather than code that is to be directly executed.
21 | Note that messages asking to genrate code (even when including examples) are not of type code_skill but either query or query skill.
22 | Note that messages that ask the AI system to execute code directly to alter the application state are not of type code_skill but likely code.
23 | Note that messages that provide only pseudo-code or natural language instructions are not of type code_skill but likely of type routine or routine_skill.
24 |
25 | routine
26 | ---
27 | routine is the tupe of messages that contain natural language that instructs the model to perform some operation(s) that would be better converted into code and executed. The entire message can be either a single instructions that can be converted to code and executed for producing a provably correct result, or a list of instructions (loosely formatted, a sequence of sentences, or a markdown-formatted list).
28 | Note that while there might be some hints of code in the message, messages containing mostly code and only a few surrounding instructions are likely not of type routine but more likely code or code_skill.
29 | Note that messages that ask the AI system to store some skill for future use on some parameters to be given in the future are not of type routine but likely routine_skill.
30 | Note thet messages that sound like a request at a language model to perform some operation (i.e. they sound like a prompt oriented at some semantic operation or an operation that cannot be easily converted into code) are not of type skill byt likely of type quer or query_skill.
31 |
32 | routine_skill
33 | ---
34 | routine_skill is the type of messages that describe operations or workflows in natural language that are not to be immediately executed, but rather saved as reusable routines to be invoked later. The message often uses future-oriented or generalizing language (e.g., "In the future, when I say X, do Y" or "remember this for the future"), or describes a behavior the AI should learn or adopt for future use. The instructions may be procedural (i.e., can be translated into code) but the key distinction from routine is the intent to store or remember the behavior, not execute it right now. These routines might include steps that require parameters to be filled in during later invocations.
35 |
36 | Note that messages that describe natural language steps to perform a one-off operation (i.e. not to be stored) are not of type routine_skill but of type routine.
37 | Note that if the message contains mostly code and is about storing function definitions, it is not a routine_skill but more likely a code_skill.
38 | Note that vague, open-ended, or semantic tasks that are not clearly procedural are not of type routine_skill but more likely query_skill.
39 |
40 | query
41 | ---
42 | query is the type of messages that ask a one-off question, request, or task from the AI that does not require code execution or skill storage. These messages typically resemble a standard prompt to a language model and might request explanations, summaries, answers, creative writing, or similar outputs. They do not require storing skills for later or executing code—just generating a response.
43 |
44 | Note that queries may sometimes mention code, examples, or functions, but if the purpose is to ask for help, explanation, or generation (rather than execution or storage), the message is a query.
45 | Note that if the query is asking the AI to create a reusable behavior, it may be query_skill instead.
46 | Note that if the query is better expressed as a step-by-step procedural task to be executed, it may be routine instead.
47 |
48 | query_skill
49 | ---
50 | query_skill is the type of messages that instruct the AI to create a reusable capability or skill that handles a kind of query. These messages often generalize a task into a reusable behavior (e.g., “Whenever I ask you to summarize a paper, follow this method or use the following prompts verbatim”). The emphasis is on defining a way for the AI to handle a category of future inputs, rather than simply responding to one instance.
51 |
52 | Note that query_skill messages are not asking for execution right now but are defining a behavior to be remembered and reused.
53 | Note that if the message only asks a single question, or provides input for a one-time output, it is not a query_skill but likely a query.
54 | Note that if the instruction is better treated as code or procedural logic for execution, it is not a query_skill but more likely routine_skill or code_skill.
55 |
56 | data
57 | ---
58 | data is the type of messages that provide structured or semi-structured input (e.g., tables, lists, JSON, CSV, key-value pairs, raw text samples) meant to be stored, parsed, processed, or used as input by a previously defined function, routine, or skill. These messages typically do not contain executable instructions or requests, but are instead meant to supply information or parameters.
59 |
60 | The message may contain:
61 | - JSON objects or arrays;
62 | - CSV-formatted rows;
63 | - tables (e.g., markdown-style or plain text with consistent delimiters);
64 | - raw text samples or transcripts;
65 | - named parameters or inputs for a function;
66 | or any other structured information that can be interpreted as input data.
67 |
68 | Note that if the message includes a request or prompt alongside the data, it may be better classified as a query or routine.
69 | Note that if the message includes code or instructions for what to do with the data, it may be code, code_skill, routine, or routine_skill depending on the intent.
70 | Note that data does not include natural language instructions but only the content meant to be processed or consumed.
71 |
72 |
73 | This is the end of the classification instructions. Below is the input to classify.
74 |
75 | Reminders:
76 | - Output the type among TYPES that best characterizes the nature of the message given.
77 | - Output just one of the TYPES (code, code_skill, routine, routine_skill, query, query_skill, data).
78 | - Do not output any other text.
79 |
80 | MESSAGE:
81 | {message}
--------------------------------------------------------------------------------
/backend/frame/errands/message_generating_routine_processing.txt:
--------------------------------------------------------------------------------
1 | You are an expert Python code generator for an advanced AI system. Your job is to convert a user's natural language routine instruction (MESSAGE) into a single pure Python function called 'code' that returns a generator.
2 |
3 | ===SEPARATOR===
4 |
5 | You are an expert Python code generator for an advanced AI system. Convert user's natural language routine instruction (MESSAGE) into a single pure Python function called 'code' that returns a generator.
6 |
7 | You are provided with:
8 | - MESSAGE: The user's instruction, which may be a single short description of intended functionality or a multi-step list (bullets, numbered steps, etc.).
9 | - SKILLS: A list of available function signatures and docstrings you may call in your code.
10 | - TIDINGS: A list of available variables (with their values and descriptions) you may use as arguments or modify.
11 |
12 | Your function must:
13 | - Be named 'code' and use type hints for all arguments and the return value.
14 | - Accept as arguments any TIDINGS that are relevant to the MESSAGE.
15 | - Accumulate all user-facing output in a string variable called '__output' (do NOT use print). At the end, return this string as the first element of a tuple.
16 | - Return a generator that yields notification dictionaries, each of which has a "type" field of type string, and other fields that are up to any yield point of the code.
17 | - Return a final notification dictionary of shape `{ "type": "__final", "output": output, and "modified_vars": modified_vars }` (output: str, modified_vars: dict[str, Any]), where 'output' is the accumulated output string, and 'modified_vars' is a dictionary of all variables that were modified by the function.
18 | - Have a high-quality docstring describing its purpose, arguments, and return value.
19 | - Use SKILLS as needed to accomplish the MESSAGE.
20 | - If the MESSAGE is a multi-step routine (bullets, numbered steps, or multiple sentences), interleave each step as a comment, followed by the code that implements that step. If there are jumps or loops, use Python control flow (for/while/if) to simulate them, with comments explaining the logic.
21 | - When in doubt, generate code that best accomplishes the user's intent.
22 | - Do not use print statements; yield a notification when asked to send a message/notification or to notify the user. An instruction such as "print hello world" should be interpreted as `yield {"type": "__output", "content": "hello world" }`.
23 | - Modify variables that are listed in TIDINGS when referred to directly in natural language
24 | - Feel free to create new variables when the MESSAGE instructs to create new ones.
25 | - Sometimes you might want to create auxiliary/temporary variables -- i.e. variables that are not really mentioned by the user directly or indirectly. When that's the case, please prefix them by `__` (double underscore).
26 | - If the MESSAGE contains a payload text (for example, separated by a heading or clearly not an instruction), store that text without modification in a new variable with an appropriate name and a comment explaining its purpose. Do not process or change the payload text; just store it. There can be multiple payload texts and the payloads can be strings, binary/hex strings, integers, floats, hashes, etc..
27 |
28 | Example input:
29 | MESSAGE:
30 | """
31 | 1. Greet the user by name.
32 | 2. Add 10 to their score.
33 | 3. If their score is above 100, congratulate them.
34 | """
35 | SKILLS:
36 | - greet_user(name: str) -> str: Returns a greeting for the user.
37 | TIDINGS:
38 | - name = Alice # The user's name
39 | - score = 95 # The user's current score
40 |
41 | Example output:
42 | ```python
43 | def code(name: str, score: int) -> Generator[dict[str, Any], None, None]:
44 | """
45 | Greets the user, updates their score, and congratulates them if their score exceeds 100.
46 | Args:
47 | name: The user's name.
48 | score: The user's current score.
49 | Returns:
50 | A tuple containing the output string and a dictionary of modified variables.
51 | """
52 | # 1. Greet the user by name.
53 | yield { "type": "__output", "content": greet_user(name) }
54 | # 2. Add 10 to their score.
55 | score += 10
56 | # 3. If their score is above 100, congratulate them.
57 | if score > 100:
58 | yield { "type": "__output", "content": ff"Congratulations, {name}! Your score is {score}.\n" }
59 | __vars = {'score': score}
60 | yield { "type": "__final", "output": None, and "modified_vars": __vars }
61 | ```
62 |
63 | Example input:
64 | MESSAGE:
65 | """
66 | 1. For each value in the list, compute its square root and add it to a running total.
67 | 2. If the total exceeds 100, output a warning message.
68 | 3. Output the final total.
69 | """
70 | SKILLS:
71 | - format_warning(msg: str) -> str: Formats a warning message for the user.
72 | TIDINGS:
73 | - values = [4, 16, 25, 36, 49] # List of numbers
74 | - total = 0 # Running total
75 |
76 | Example output:
77 | ```python
78 | def code(values: list[float], total: float) -> Generator[dict[str, Any], None, None]:
79 | """
80 | Computes the square root of each value, adds to total, warns if total > 100, and outputs the total.
81 | Args:
82 | values: List of numbers to process.
83 | total: Running total.
84 | Returns:
85 | A tuple containing the output string and a dictionary of modified variables.
86 | """
87 | import math
88 | __output = ""
89 | # 1. For each value in the list, compute its square root and add it to a running total.
90 | for __v in values:
91 | __sqrt = math.sqrt(__v)
92 | total += __sqrt
93 | # 2. If the total exceeds 100, output a warning message.
94 | if total > 100:
95 | yield { "type": "__output", "content": format_warning(f"Total exceeded 100: {total}") + "\n" }
96 | # 3. Output the final total.
97 | yield { "type": "__output", "content": f"Final total: {total}\n" }
98 | __vars = { 'total': total }
99 | yield { "type": "__final", "output": None, and "modified_vars": __vars }
100 | ```
101 |
102 | Example input:
103 | MESSAGE:
104 | """
105 | 1. Check if the user is an admin.
106 | 2. If so, grant them access by sending a message of type "access_granted" and an accompanying description, and log the event.
107 | 3. Otherwise, deny access by sending a message of type "access_denied" and an accompanying description, and log the attempt.
108 | """
109 | SKILLS:
110 | - log_event(event: str) -> None: Logs an event string.
111 | - grant_access(user: str) -> str: Grants access to the user and returns a confirmation message.
112 | - deny_access(user: str) -> str: Denies access to the user and returns a denial message.
113 | TIDINGS:
114 | - user = bob # The username
115 | - is_admin = False # Whether the user is an admin
116 |
117 | Example output:
118 | ```python
119 | def code(user: str, is_admin: bool) -> Generator[dict[str, Any], None, None]:
120 | """
121 | Checks admin status, grants or denies access, and logs the event.
122 | Args:
123 | user: The username.
124 | is_admin: Whether the user is an admin.
125 | Returns:
126 | A tuple containing the output string and a dictionary of modified variables.
127 | """
128 | __output = ""
129 | # 1. Check if the user is an admin.
130 | if is_admin:
131 | # 2. If so, grant them access and log the event.
132 | yield { "type": "access_granted", "description": grant_access(user) + "\n" }
133 | log_event(f"Access granted to {user}")
134 | else:
135 | # 3. Otherwise, deny access and log the attempt.
136 | yield { "type": "access_denied", "description": deny_access(user) + "\n" }
137 | log_event(f"Access denied to {user}")
138 | __vars = {}
139 | yield { "type": "__final", "output": None, and "modified_vars": __vars }
140 | ```
141 |
142 | Reminders:
143 | - Output a function called `code` relying on SKILLS and TIDINGS where needed
144 | - The function should be a pure function -- do not access global variables.
145 | - Prefer the functions described in SKILLS over external libraries
146 | - Output just the code of the function. Do not output any other text.
147 |
148 | Now, generate the function as described above for the given MESSAGE, SKILLS, and TIDINGS.
149 |
150 | SKILLS:
151 | {skills}
152 |
153 | TIDINGS:
154 | {tidings}
155 |
156 | MESSAGE:
157 | {message}
158 |
159 | Reminders:
160 | - Output a function called `code` relying on SKILLS and TIDINGS where needed
161 | - The function should be a pure function -- do not access global variables.
162 | - Prefer the functions described in SKILLS over external libraries
163 | - Output just the code of the function. Do not output any other text.
164 | - Remember to return a generator of dictionaries, and to use yield to report user intermediate messages.
--------------------------------------------------------------------------------