diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 165c996..2f87f74 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,42 +14,32 @@ permissions:
jobs:
ci:
-
runs-on: ubuntu-latest
+
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
+
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
+ cache: "pip"
- name: Install dependencies
run: |
- python -m pip install --upgrade pip
- grep -vf req.filters.txt requirements.txt | pip install -r /dev/stdin
- ./code/tools/lint.sh deps
-
- - name: Check Python syntax with Pylint
- run: |
- ./code/tools/lint.sh pylint
-
- - name: Check Type Hints with MyPy
- run: |
- ./code/tools/lint.sh mypy
-
- - name: Check code formatting with Black
- run: |
- ./code/tools/lint.sh black
-
- - name: Ensure Notebooks have no output
- run: |
- ./code/tools/lint.sh ipynb
-
-
-
-
-
+ python -m pip install --upgrade pip wheel
+ pip install -r requirements.txt
+ pip install ruff mypy
+
+ - name: Check code formatting
+ run: make format
+ - name: Run code linters
+ run: make lint
+ - name: Check typehints
+ run: make typecheck
+ - name: Ensure notebooks have no output
+ run: make check-notebooks
diff --git a/.github/workflows/generate-documentation.yml b/.github/workflows/generate-documentation.yml
deleted file mode 100644
index ce140d1..0000000
--- a/.github/workflows/generate-documentation.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-name: Generate README
-
-on:
- push:
- paths:
- - 'docs/**' # Trigger when files under the docs folder change
- workflow_dispatch: # Also allows manual triggering from the GitHub Actions UI
-
-jobs:
- build-docs:
- runs-on: ubuntu-latest
-
- steps:
- # Step 1: Checkout the repository
- - name: Checkout repository
- uses: actions/checkout@v2
-
- # Step 2: Set up Python
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: '3.11'
-
- # Step 3: Install Dependencies (Pandoc, Make, etc.)
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y make wget
- PANDOC_VERSION=3.1.8
- wget https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-1-amd64.deb
- sudo dpkg -i pandoc-${PANDOC_VERSION}-1-amd64.deb
- python -m pip install --upgrade pip
- pip install -r .github/workflows/generate-documentation_requirements.txt
-
- # Step 4: Build Documentation
- - name: Build documentation
- working-directory: docs
- run: make all
-
- # Step 5: Upload README.md as an artifact for manual review
- #- name: Upload README.md as artifact
- # uses: actions/upload-artifact@v4
- # with:
- # name: generated-readme
- # path: README.md # Adjust the path if necessary
-
- # Step 6: Display the diff for review in logs
- - name: Show diff for README.md
- run: |
- echo "Comparing differences for the updated README"
- git diff README.md || echo "No differences to display"
-
- # Step 7: Commit changes automatically
- - name: Commit and push changes
- run: |
- git config --local user.name "GitHub Actions"
- git config --local user.email "actions@github.com"
- git add README.md .static
- git diff --cached --exit-code || git commit -m "Auto-generated: README.md and related content"
- git push
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Use GitHub token for authentication
\ No newline at end of file
diff --git a/.github/workflows/generate-documentation_requirements.txt b/.github/workflows/generate-documentation_requirements.txt
deleted file mode 100644
index d17b333..0000000
--- a/.github/workflows/generate-documentation_requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-confz==2.0.1
-Jinja2>=3.0.0
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1c7fc6b..e155146 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Ignore generated or temporary files managed by the Workbench
.project/*
+!.project/compose.yaml
!.project/spec.yaml
!.project/configpacks
@@ -56,6 +57,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
+.ruff_cache
# Workbench Project Layout
data/scratch/*
diff --git a/compose.yaml b/.project/compose.yaml
similarity index 100%
rename from compose.yaml
rename to .project/compose.yaml
diff --git a/.project/spec.yaml b/.project/spec.yaml
index e80cd4b..7c2d376 100644
--- a/.project/spec.yaml
+++ b/.project/spec.yaml
@@ -13,7 +13,7 @@ layout:
- path: code/
type: code
storage: git
- - path: docs/
+ - path: libs/
type: code
storage: git
- path: data/
@@ -25,8 +25,8 @@ layout:
environment:
base:
registry: nvcr.io
- image: nvidia/ai-workbench/python-cuda122:1.0.3
- build_timestamp: "20231214221614"
+ image: nvidia/ai-workbench/python-cuda122:1.0.6
+ build_timestamp: "20250205043304"
name: Python with CUDA 12.2
supported_architectures: []
cuda_version: "12.2"
@@ -53,8 +53,8 @@ environment:
url_command: jupyter lab list | head -n 2 | tail -n 1 | cut -f1 -d' ' | grep -v 'Currently'
programming_languages:
- python3
- icon_url: ""
- image_version: 1.0.3
+ icon_url: https://workbench.download.nvidia.com/static/img/ai-workbench-icon-rectangle.jpg
+ image_version: 1.0.6
os: linux
os_distro: ubuntu
os_distro_release: "22.04"
@@ -75,17 +75,14 @@ environment:
- python3-dev
- python3-pip
- vim
- - less
- - jq
- - ssh
- name: pip
- binary_path: /usr/local/bin/pip
+ binary_path: /usr/bin/pip
installed_packages:
- - jupyterlab==4.0.7
+ - jupyterlab==4.2.5
package_manager_environment:
name: ""
target: ""
- compose_file_path: ""
+ compose_file_path: .project/compose.yaml
execution:
apps:
- name: Visual Studio Code
diff --git a/.static/_static/generate_personal_key.png b/.static/_static/generate_personal_key.png
deleted file mode 100644
index 8786a22..0000000
Binary files a/.static/_static/generate_personal_key.png and /dev/null differ
diff --git a/.static/_static/mac_dmg_drag.png b/.static/_static/mac_dmg_drag.png
deleted file mode 100644
index ed4f494..0000000
Binary files a/.static/_static/mac_dmg_drag.png and /dev/null differ
diff --git a/.static/_static/na_frontend.png b/.static/_static/na_frontend.png
deleted file mode 100644
index bbf7dee..0000000
Binary files a/.static/_static/na_frontend.png and /dev/null differ
diff --git a/.static/_static/nim-anywhere.png b/.static/_static/nim-anywhere.png
deleted file mode 100644
index 77981e2..0000000
Binary files a/.static/_static/nim-anywhere.png and /dev/null differ
diff --git a/.static/_static/nvwb_clone.png b/.static/_static/nvwb_clone.png
deleted file mode 100644
index 29a8f2c..0000000
Binary files a/.static/_static/nvwb_clone.png and /dev/null differ
diff --git a/.static/_static/nvwb_left_menu.png b/.static/_static/nvwb_left_menu.png
deleted file mode 100644
index ecc5007..0000000
Binary files a/.static/_static/nvwb_left_menu.png and /dev/null differ
diff --git a/.static/_static/nvwb_locations.png b/.static/_static/nvwb_locations.png
deleted file mode 100644
index 957a3b4..0000000
Binary files a/.static/_static/nvwb_locations.png and /dev/null differ
diff --git a/.static/_static/nvwb_logs.png b/.static/_static/nvwb_logs.png
deleted file mode 100644
index ac033af..0000000
Binary files a/.static/_static/nvwb_logs.png and /dev/null differ
diff --git a/.static/_static/nvwb_projects.png b/.static/_static/nvwb_projects.png
deleted file mode 100644
index 217935d..0000000
Binary files a/.static/_static/nvwb_projects.png and /dev/null differ
diff --git a/.static/_static/personal_key.png b/.static/_static/personal_key.png
deleted file mode 100644
index 87802a2..0000000
Binary files a/.static/_static/personal_key.png and /dev/null differ
diff --git a/.static/_static/personal_key_form.png b/.static/_static/personal_key_form.png
deleted file mode 100644
index a0c6eb0..0000000
Binary files a/.static/_static/personal_key_form.png and /dev/null differ
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 2f9cfc6..785f2c8 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -2,11 +2,8 @@
"recommendations": [
"ms-python.vscode-pylance",
"ms-python.python",
- "ms-python.debugpy",
"ms-python.mypy-type-checker",
- "ms-python.black-formatter",
- "ms-python.isort",
- "ms-python.pylint",
- "ms-toolsai.jupyter"
+ "ms-toolsai.jupyter",
+ "charliermarsh.ruff"
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7233115..ecfc2db 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,9 +1,7 @@
{
// file explorer configuration
"files.exclude": {
- "**/.git": true,
- "**/.svn": true,
- "**/.hg": true,
+ ".git": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
@@ -11,27 +9,25 @@
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ipynb_checkpoints": true,
- "**/.terraform": true,
+ ".project": true,
+ ".vscode": true,
+ ".github": true,
+ "**/.ruff_cache": true,
},
-
// global editor settings
"files.eol": "\n",
"editor.tabSize": 4,
"editor.insertSpaces": true,
"files.insertFinalNewline": true,
- // remove this line to automatically forward ports
- // in general, workbench will manage this already
"remote.autoForwardPorts": false,
-
// bash scripting configuration
"[shellscript]": {
"editor.tabSize": 4,
- "editor.insertSpaces": false,
+ "editor.insertSpaces": false
},
-
// css style sheet configuration
"[css]": {
"editor.suggest.insertMode": "replace",
@@ -41,31 +37,29 @@
// js configuration
"[javascript]": {
"editor.maxTokenizationLineLength": 2500,
- "editor.tabSize": 2,
+ "editor.tabSize": 2
},
// Python environment configuration
"python.terminal.activateEnvironment": true,
"python.defaultInterpreterPath": "/usr/bin/python3",
- "isort.args":["--profile", "black"],
- "isort.check": true,
+
+ // Ruff as formatter
"[python]": {
- "editor.defaultFormatter": "ms-python.black-formatter",
+ "editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
- "source.organizeImports": "explicit"
+ "source.organizeImports": "explicit",
+ "source.fixAll": "explicit"
},
- // Comment out this settings to disable auto-formatting
"editor.formatOnSave": true
},
- "black-formatter.serverTransport": "stdio",
- "black-formatter.args": [
- "--line-length",
- "120"
+ "ruff.enable": true,
+ "ruff.exclude": [],
+ // tutorial apps use a different python environment
+ "python.analysis.exclude": [
+ "code/tutorial_app/**",
+ "libs/**"
],
- "pylint.severity": {
- "refactor": "Information",
- },
-
- "python.analysis.ignore": ["code/tutorial_app/**"]
+ "mypy-type-checker.ignorePatterns": ["**/answers/*"],
}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..001b310
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+## Help command
+default: help
+.PHONY: help
+help: # Show help for each of the Makefile recipes.
+ @echo "Available targets:"
+ @grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | sort | while read -r l; do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done
+
+## CI Pipeline logic
+.PHONY: ci lint format typecheck test check-notebooks
+ci: lint format typecheck check-notebooks test # Run the ci pipeline locally
+
+lint: # Examing the code with linters
+ ruff check .
+
+format: # Check the code formatting
+ ruff format --check .
+
+format-fix: # Autofix the code formatting where it is possible
+
+typecheck: # Check the type hints in the code
+ mypy .
+
+test: # Run unit tests
+ # pytest --quiet
+
+check-notebooks: # Ensure the jupyter notebooks have no saved
+ find . -name "*.ipynb" -exec jupyter nbconvert --clear-output --inplace {} +
+ git diff --exit-code || (echo "Notebooks have output. Clear it before committing." && exit 1)
diff --git a/README.md b/README.md
index 2583b49..59c6ef6 100644
--- a/README.md
+++ b/README.md
@@ -1,354 +1,30 @@
-# NVIDIA NIM Anywhere [](https://ngc.nvidia.com/open-ai-workbench/aHR0cHM6Ly9naXRodWIuY29tL05WSURJQS9uaW0tYW55d2hlcmUK)
-
-[](https://docs.nvidia.com/nim/#large-language-models)
-[](https://docs.nvidia.com/nim/#nemo-retriever)
-[](https://docs.nvidia.com/nim/#nemo-retriever)
-[](https://github.com/NVIDIA/nim-anywhere/actions/workflows/ci.yml?query=branch%3Amain)
-
-
-
-
-Please join \#cdd-nim-anywhere slack channel if you are a internal user,
-open an issue if you are external for any question and feedback.
-
-One of the primary benefit of using AI for Enterprises is their ability
-to work with and learn from their internal data. Retrieval-Augmented
-Generation
-([RAG](https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/))
-is one of the best ways to do so. NVIDIA has developed a set of
-micro-services called [NIM
-micro-service](https://docs.nvidia.com/nim/large-language-models/latest/introduction.html)
-to help our partners and customers build effective RAG pipeline with
-ease.
-
-NIM Anywhere contains all the tooling required to start integrating NIMs
-for RAG. It natively scales out to full-sized labs and up to production
-environments. This is great news for building a RAG architecture and
-easily adding NIMs as needed. If you're unfamiliar with RAG, it
-dynamically retrieves relevant external information during inference
-without modifying the model itself. Imagine you're the tech lead of a
-company with a local database containing confidential, up-to-date
-information. You donβt want OpenAI to access your data, but you need the
-model to understand it to answer questions accurately. The solution is
-to connect your language model to the database and feed them with the
-information.
-
-To learn more about why RAG is an excellent solution for boosting the
-accuracy and reliability of your generative AI models, [read this
-blog](https://developer.nvidia.com/blog/enhancing-rag-applications-with-nvidia-nim/).
-
-Get started with NIM Anywhere now with the [quick-start](#quick-start)
-instructions and build your first RAG application using NIMs!
-
-
-
-- [Quick-start](#quick-start)
- - [Generate your NGC Personal Key](#generate-your-ngc-personal-key)
- - [Authenticate with Docker](#authenticate-with-docker)
- - [Install AI Workbench](#install-ai-workbench)
- - [Download this project](#download-this-project)
- - [Configure this project](#configure-this-project)
- - [Start This Project](#start-this-project)
- - [Populating the Knowledge Base](#populating-the-knowledge-base)
-- [Developing Your Own Applications](#developing-your-own-applications)
-- [Application Configuration](#application-configuration)
- - [Config from a file](#config-from-a-file)
- - [Config from a custom file](#config-from-a-custom-file)
- - [Config from env vars](#config-from-env-vars)
- - [Chain Server config schema](#chain-server-config-schema)
- - [Chat Frontend config schema](#chat-frontend-config-schema)
-- [Contributing](#contributing)
- - [Code Style](#code-style)
- - [Updating the frontend](#updating-the-frontend)
- - [Updating documentation](#updating-documentation)
-- [Managing your Development
- Environment](#managing-your-development-environment)
- - [Environment Variables](#environment-variables)
- - [Python Environment Packages](#python-environment-packages)
- - [Operating System Configuration](#operating-system-configuration)
- - [Updating Dependencies](#updating-dependencies)
-
-# Quick-start
-
-## Generate your NGC Personal Key
-
-To allow AI Workbench to access NVIDIAβs cloud resources, youβll need to
-provide it with a Personal Key. These keys begin with `nvapi-`.
+# NVIDIA NIM Anywhere
-
-
-Expand this section for instructions for creating this key.
-
-
-1. Go to the [NGC Personal Key
- Manager](https://org.ngc.nvidia.com/setup/personal-keys). If you are
- prompted to, then register for a new account and sign in.
-
- > **HINT** You can find this tool by logging into
- > [ngc.nvidia.com](https://ngc.nvidia.com), expanding your profile
- > menu on the top right, selecting *Setup*, and then selecting
- > *Generate Personal Key*.
-
-2. Select *Generate Personal Key*.
-
- 
-
-3. Enter any value as the Key name, an expiration of 12 months is fine,
- and select all the services. Press *Generate Personal Key* when you
- are finished.
-
- 
-
-4. Save your personal key for later. Workbench will need it and there
- is no way to retrieve it later. If the key is lost, a new one must
- be created. Protect this key as if it were a password.
-
- 
-
-
-
-## Authenticate with Docker
-
-Workbench will use your system's Docker client to pull NVIDIA NIM
-containers, so before continuing, make sure to follow these steps to
-authenticate your Docker client with your NGC Personal Key.
-
-1. Run the following Docker login command
-
- ``` bash
- docker login nvcr.io
- ```
-
-2. When prompted for your credentials, use the following values:
-
- - Username: `$oauthtoken`
- - Password: Use your NGC Personal key beggining with `nv-api`
-
-## Install AI Workbench
-
-This project is designed to be used with [NVIDIA AI
-Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/).
-While this is not a requirement, running this demo without AI Workbench
-will require manual work as the pre-configured automation and
-integrations may not be available.
-
-This quick start guide will assume a remote lab machine is being used
-for development and the local machine is a thin-client for remotely
-accessing the development machine. This allows for compute resources to
-stay centrally located and for developers to be more portable. Note, the
-remote lab machine must run Ubuntu, but the local client can run
-Windows, MacOS, or Ubuntu. To install this project local only, simply
-skip the remote install.
-
-``` mermaid
-flowchart LR
- local
- subgraph lab environment
- remote-lab-machine
- end
-
- local <-.ssh.-> remote-lab-machine
-```
-
-### Client Machine Install
-
-Ubuntu is required if the local client will also be used for developent.
-When using a remote lab machine, this can be Windows, MacOS, or Ubuntu.
-
-
-
-Expand this section for a Windows install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User
-Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/windows.html).
-
-1. Install Prerequisite Software
-
- 1. If this machine has an NVIDIA GPU, ensure the GPU drivers are
- installed. It is recommended to use the [GeForce
- Experience](https://www.nvidia.com/en-us/geforce/geforce-experience/)
- tooling to manage the GPU drivers.
- 2. Install [Docker
- Desktop](https://www.docker.com/products/docker-desktop/) for
- local container support. Please be mindful of Docker Desktop's
- licensing for enterprise use. [Rancher
- Desktop](https://rancherdesktop.io/) may be a viable
- alternative.
- 3. *\[OPTIONAL\]* If Visual Studio Code integration is desired,
- install [Visual Studio Code](https://code.visualstudio.com/).
-
-2. Download the [NVIDIA AI
- Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/)
- installer and execute it. Authorize Windows to allow the installer
- to make changes.
+[](https://build.nvidia.com/open-ai-workbench/aHR0cHM6Ly9naXRodWIuY29tL05WSURJQS9uaW0tYW55d2hlcmU=)
+[](https://github.com/NVIDIA/nim-anywhere/fork)
-3. Follow the instructions in the installation wizard. If you need to
- install WSL2, authorize Windows to make the changes and reboot local
- machine when requested. When the system restarts, the NVIDIA AI
- Workbench installer should automatically resume.
+NIM Anywhere is a starting point into discovering enterprise AI. This branch is currently under heavy construction.
-4. Select Docker as your container runtime.
+
-5. Log into your GitHub Account by using the *Sign in through
- GitHub.com* option.
-
-6. Enter your git author information if requested.
-
-
-
-
-
-Expand this section for a MacOS install.
-
+# NEEDS TO BE UPDATED
-For full instructions, see the [NVIDIA AI Workbench User
-Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/macos.html).
-
-1. Install Prerequisite Software
-
- 1. Install [Docker
- Desktop](https://www.docker.com/products/docker-desktop/) for
- local container support. Please be mindful of Docker Desktop's
- licensing for enterprise use. [Rancher
- Desktop](https://rancherdesktop.io/) may be a viable
- alternative.
- 2. *\[OPTIONAL\]* If Visual Studio Code integration is desired,
- install [Visual Studio Code](https://code.visualstudio.com/).
- When using VSCode on a Mac, an a[dditional step must be
- performed](https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line)
- to install the VSCode CLI interface used by Workbench.
-
-2. Download the [NVIDIA AI
- Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/)
- disk image (*.dmg* file) and open it.
-
-3. Drag AI Workbench into the Applications folder and run *NVIDIA AI
- Workbench* from the application launcher. 
-
-4. Select Docker as your container runtime.
-
-5. Log into your GitHub Account by using the *Sign in through
- GitHub.com* option.
-
-6. Enter your git author information if requested.
-
-
-
-
-
-Expand this section for an Ubuntu install.
-
+## Get Started
-For full instructions, see the [NVIDIA AI Workbench User
-Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/ubuntu-local.html).
-Run this installation as the user who will be user Workbench. Do not run
-these steps as `root`.
+
-1. Install Prerequisite Software
+Hello world!
- 1. *\[OPTIONAL\]* If Visual Studio Code integration is desired,
- install [Visual Studio Code](https://code.visualstudio.com/).
-2. Download the [NVIDIA AI
- Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/)
- installer, make it executable, and then run it. You can make the
- file executable with the following command:
+### Prerequisites
- ``` bash
- chmod +x NVIDIA-AI-Workbench-*.AppImage
- ```
+### Generate your NGC Personal Key
-3. AI Workbench will install the NVIDIA drivers for you (if needed).
- You will need to reboot your local machine after the drivers are
- installed and then restart the AI Workbench installation by
- double-clicking the NVIDIA AI Workbench icon on your desktop.
+#### Install AI Workbench
-4. Select Docker as your container runtime.
+### Installing
-5. Log into your GitHub Account by using the *Sign in through
- GitHub.com* option.
-
-6. Enter your git author information if requested.
-
-
-
-### Remote Machine Install
-
-Only Ubuntu is supported for remote machines.
-
-
-
-Expand this section for a remote Ubuntu install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User
-Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/ubuntu-remote.html).
-Run this installation as the user who will be using Workbench. Do not
-run these steps as `root`.
-
-1. Ensure SSH Key based authentication is enabled from the local
- machine to the remote machine. If this is not currently enabled, the
- following commands will enable this is most situations. Change
- `REMOTE_USER` and `REMOTE-MACHINE` to reflect your remote address.
-
- - From a Windows local client, use the following PowerShell:
- ``` powershell
- ssh-keygen -f "C:\Users\local-user\.ssh\id_rsa" -t rsa -N '""'
- type $env:USERPROFILE\.ssh\id_rsa.pub | ssh REMOTE_USER@REMOTE-MACHINE "cat >> .ssh/authorized_keys"
- ```
- - From a MacOS or Linux local client, use the following shell:
- ``` bash
- if [ ! -e ~/.ssh/id_rsa ]; then ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ""; fi
- ssh-copy-id REMOTE_USER@REMOTE-MACHINE
- ```
-
-2. SSH into the remote host. Then, use the following commands to
- download and execute the NVIDIA AI Workbench Installer.
-
- ``` bash
- mkdir -p $HOME/.nvwb/bin && \
- curl -L https://workbench.download.nvidia.com/stable/workbench-cli/$(curl -L -s https://workbench.download.nvidia.com/stable/workbench-cli/LATEST)/nvwb-cli-$(uname)-$(uname -m) --output $HOME/.nvwb/bin/nvwb-cli && \
- chmod +x $HOME/.nvwb/bin/nvwb-cli && \
- sudo -E $HOME/.nvwb/bin/nvwb-cli install
- ```
-
-3. AI Workbench will install the NVIDIA drivers for you (if needed).
- You will need to reboot your remote machine after the drivers are
- installed and then restart the AI Workbench installation by
- re-running the commands in the previous step.
-
-4. Select Docker as your container runtime.
-
-5. Log into your GitHub Account by using the *Sign in through
- GitHub.com* option.
-
-6. Enter your git author information if requested.
-
-7. Once the remote installation is complete, the Remote Location can be
- added to the local AI Workbench instance. Open the AI Workbench
- application, click *Add Remote Location*, and then enter the
- required information. When finished, click *Add Location*.
-
- - \*Location Name: \* Any short name for this new location
- - \*Description: \* Any brief metadata for this location.
- - \*Hostname or IP Address: \* The hostname or address used to
- remotely SSH. If step 1 was followed, this should be the same as
- `REMOTE-MACHINE`.
- - \*SSH Port: \* Usually left blank. If a nonstandard SSH port is
- used, it can be configured here.
- - \*SSH Username: \* The username used for making an SSH connection.
- If step 1 was followed, this should be the same as `REMOTE_USER`.
- - \*SSH Key File: \* The path to the private key for making SSH
- connections. If step 1 was followed, this should be:
- `/home/USER/.ssh/id_rsa`.
- - \*Workbench Directory: \* Usually left blank. This is where
- Workbench will remotely save state.
-
-
-
-## Download this project
+#### Download this project
There are two ways to download this project for local use: Cloning and
Forking.
@@ -397,7 +73,7 @@ section.
-## Configure this project
+### Configure this project
The project must be configured to use your NGC personal key.
@@ -418,7 +94,7 @@ The project must be configured to use your NGC personal key.
-## Start This Project
+### Start This Project
Even the most basic of LLM Chains depend on a few additional
microservices. These can be ignored during development for in-memory
@@ -511,7 +187,7 @@ without GPUs.
-## Populating the Knowledge Base
+### Populating the Knowledge Base
To get started developing demos, a sample dataset is provided along with
a Jupyter Notebook showing how data is ingested into a Vector Database.
@@ -526,420 +202,23 @@ a Jupyter Notebook showing how data is ingested into a Vector Database.
3. If using a custom dataset, upload it to the `data/` directory in
Jupyter and modify the provided notebook as necessary.
-# Developing Your Own Applications
-
-This project contains applications for a few demo services as well as
-integrations with external services. These are all orchestrated by
-[NVIDIA AI
-Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/).
-
-The demo services are all in the `code` folder. The root level of the
-code folder has a few interactive notebooks meant for technical deep
-dives. The Chain Server is a sample application utilizing NIMs with
-LangChain. (Note that the Chain Server here gives you the option to
-experiment with and without RAG). The Chat Frontend folder contains an
-interactive UI server for exercising the chain server. Finally, sample
-notebooks are provided in the Evaluation directory to demonstrate
-retrieval scoring and validation.
-
-``` mermaid
-mindmap
- root((AI Workbench))
- Demo Services
- Chain Server LangChain + NIMs
- Frontend Interactive Demo UI
- Evaluation Validate the results
- Notebooks Advanced usage
-
- Integrations
- RedisConversation History
- MilvusVector Database
- LLM NIMOptimized LLMs
-```
-
-# Application Configuration
-
-The Chain Server can be configured with either a configuration file or
-environment variables.
-
-## Config from a file
-
-By default, the application will search for a configuration file in all
-of the following locations. If multiple configuration files are found,
-values from lower files in the list will take precedence.
-
-- ./config.yaml
-- ./config.yml
-- ./config.json
-- ~/app.yaml
-- ~/app.yml
-- ~/app.json
-- /etc/app.yaml
-- /etc/app.yml
-- /etc/app.json
-
-## Config from a custom file
-
-An additional config file path can be specified through an environment
-variable named `APP_CONFIG`. The value in this file will take precedence
-over all the default file locations.
-
-``` bash
-export APP_CONFIG=/etc/my_config.yaml
-```
-
-## Config from env vars
-
-Configuration can also be set using environment variables. The variable
-names will be in the form: `APP_FIELD__SUB_FIELD` Values specified as
-environment variables will take precedence over all values from files.
-
-## Chain Server config schema
-
-``` yaml
-# Your API key for authentication to AI Foundation.
-# ENV Variables: NGC_API_KEY, NVIDIA_API_KEY, APP_NVIDIA_API_KEY
-# Type: string, null
-nvidia_api_key: ~
-
-# The Data Source Name for your Redis DB.
-# ENV Variables: APP_REDIS_DSN
-# Type: string
-redis_dsn: redis://localhost:6379/0
-
-llm_model:
- # The name of the model to request.
- # ENV Variables: APP_LLM_MODEL__NAME
- # Type: string
- name: meta/llama3-8b-instruct
-
- # The URL to the model API.
- # ENV Variables: APP_LLM_MODEL__URL
- # Type: string
- url: https://integrate.api.nvidia.com/v1
-
-
-embedding_model:
- # The name of the model to request.
- # ENV Variables: APP_EMBEDDING_MODEL__NAME
- # Type: string
- name: nvidia/nv-embedqa-e5-v5
-
- # The URL to the model API.
- # ENV Variables: APP_EMBEDDING_MODEL__URL
- # Type: string
- url: https://integrate.api.nvidia.com/v1
-
-
-reranking_model:
- # The name of the model to request.
- # ENV Variables: APP_RERANKING_MODEL__NAME
- # Type: string
- name: nv-rerank-qa-mistral-4b:1
-
- # The URL to the model API.
- # ENV Variables: APP_RERANKING_MODEL__URL
- # Type: string
- url: https://integrate.api.nvidia.com/v1
-
-
-milvus:
- # The host machine running Milvus vector DB.
- # ENV Variables: APP_MILVUS__URL
- # Type: string
- url: http://localhost:19530
-
- # The name of the Milvus collection.
- # ENV Variables: APP_MILVUS__COLLECTION_NAME
- # Type: string
- collection_name: collection_1
-
-
-log_level:
-
-```
-
-## Chat Frontend config schema
-
-The chat frontend has a few configuration options as well. They can be
-set in the same manner as the chain server.
-
-``` yaml
-# The URL to the chain on the chain server.
-# ENV Variables: APP_CHAIN_URL
-# Type: string
-chain_url: http://localhost:3030/
-
-# The url prefix when this is running behind a proxy.
-# ENV Variables: PROXY_PREFIX, APP_PROXY_PREFIX
-# Type: string
-proxy_prefix: /
-
-# Path to the chain server's config.
-# ENV Variables: APP_CHAIN_CONFIG_FILE
-# Type: string
-chain_config_file: ./config.yaml
-
-log_level:
-
-```
-
# Contributing
-All feedback and contributions to this project are welcome. When making
-changes to this project, either for personal use or for contributing, it
-is recommended to work on a fork on this project. Once the changes have
-been completed on the fork, a Merge Request should be opened.
-
-## Code Style
-
-This project has been configured with Linters that have been tuned to
-help the code remain consistent while not being overly burdensome. We
-use the following Linters:
-
-- Bandit is used for security scanning
-- Pylint is used for Python Syntax Linting
-- MyPy is used for type hint linting
-- Black is configured for code styling
-- A custom check is run to ensure Jupyter Notebooks do not have any
- output
-- Another custom check is run to ensure the README.md file is up to date
-
-The embedded VSCode environment is configured to run the linting and
-checking in realtime.
-
-To manually run the linting that is done by the CI pipelines, execute
-`/project/code/tools/lint.sh`. Individual tests can be run be specifying
-them by name:
-`/project code/tools/lint.sh [deps|pylint|mypy|black|docs|fix]`. Running
-the lint tool in fix mode will automatically correct what it can by
-running Black, updating the README, and clearing the cell output on all
-Jupyter Notebooks.
-
-## Updating the frontend
-
-The frontend has been designed in an effort to minimize the required
-HTML and Javascript development. A branded and styled Application Shell
-is provided that has been created with vanilla HTML, Javascript, and
-CSS. It is designed to be easy to customize, but it should never be
-required. The interactive components of the frontend are all created in
-Gradio and mounted in the app shell using iframes.
-
-Along the top of the app shell is a menu listing the available views.
-Each view may have its own layout consisting of one or a few pages.
-
-### Creating a new page
-
-Pages contain the interactive components for a demo. The code for the
-pages is in the `code/frontend/pages` directory. To create a new page:
-
-1. Create a new folder in the pages directory
-2. Create an `__init__.py` file in the new directory that uses Gradio
- to define the UI. The Gradio Blocks layout should be defined in a
- variable called `page`.
-3. It is recommended that any CSS and JS files needed for this view be
- saved in the same directory. See the `chat` page for an example.
-4. Open the `code/frontend/pages/__init__.py` file, import the new
- page, and add the new page to the `__all__` list.
-
-> **NOTE:** Creating a new page will not add it to the frontend. It must
-> be added to a view to appear on the Frontend.
-
-### Adding a view
-
-View consist of one or a few pages and should function independently of
-each other. Views are all defined in the `code/frontend/server.py`
-module. All declared views will automatically be added to the Frontend's
-menu bar and made available in the UI.
-
-To define a new view, modify the list named `views`. This is a list of
-`View` objects. The order of the objects will define their order in the
-Frontend menu. The first defined view will be the default.
-
-View objects describe the view name and layout. They can be declared as
-follow:
-
-``` python
-my_view = frontend.view.View(
- name="My New View", # the name in the menu
- left=frontend.pages.sample_page, # the page to show on the left
- right=frontend.pages.another_page, # the page to show on the right
-)
-```
-
-All of the page declarations, `View.left` or `View.right`, are optional.
-If they are not declared, then the associated iframes in the web layout
-will be hidden. The other iframes will expand to fill the gaps. The
-following diagrams show the various layouts.
-
-- All pages are defined
-
-``` mermaid
-block-beta
- columns 1
- menu["menu bar"]
- block
- columns 2
- left right
- end
-```
-
-- Only left is defined
-
-``` mermaid
-block-beta
- columns 1
- menu["menu bar"]
- block
- columns 1
- left:1
- end
-```
-
-### Frontend branding
-
-The frontend contains a few branded assets that can be customized for
-different use cases.
-
-#### Logo
-
-The frontend contains a logo on the top left of the page. To modify the
-logo, an SVG of the desired logo is required. The app shell can then be
-easily modified to use the new SVG by modifying the
-`code/frontend/_assets/index.html` file. There is a single `div` with an
-ID of `logo`. This box contains a single SVG. Update this to the desired
-SVG definition.
-
-``` html
-
-
-
-```
-
-#### Color scheme
-
-The styling of the App Shell is defined in
-`code/frontend/_static/css/style.css`. The colors in this file may be
-safely modified.
-
-The styling of the various pages are defined in
-`code/frontend/pages/*/*.css`. These files may also require modification
-for custom color schemes.
-
-#### Gradio theme
-
-The Gradio theme is defined in the file
-`code/frontend/_assets/theme.json`. The colors in this file can safely
-be modified to the desired branding. Other styles in this file may also
-be changed, but may cause breaking changes to the frontend. The [Gradio
-documentation](https://www.gradio.app/guides/theming-guide) contains
-more information on Gradio theming.
-
-### Messaging between pages
-
-> **NOTE:** This is an advanced topic that most developers will never
-> require.
-
-Occasionally, it may be necessary to have multiple pages in a view that
-communicate with each other. For this purpose, Javascript's
-`postMessage` messaging framework is used. Any trusted message posted to
-the application shell will be forwarded to each iframe where the pages
-can handle the message as desired. The `control` page uses this feature
-to modify the configuration of the `chat` page.
-
-The following will post a message to the app shell (`window.top`). The
-message will contain a dictionary with the key `use_kb` and a value of
-true. Using Gradio, this Javascript can be executed by [any Gradio
-event](https://www.gradio.app/guides/custom-CSS-and-JS#adding-custom-java-script-to-your-demo).
-
-``` javascript
-window.top.postMessage({"use_kb": true}, '*');
-```
-
-This message will automatically be sent to all pages by the app shell.
-The following sample code will consume the message on another page. This
-code will run asynchronously when a `message` event is received. If the
-message is trusted, a Gradio component with the `elem_id` of `use_kb`
-will be updated to the value specified in the message. In this way, the
-value of a Gradio component can be duplicated across pages.
-
-``` javascript
-window.addEventListener(
- "message",
- (event) => {
- if (event.isTrusted) {
- use_kb = gradio_config.components.find((element) => element.props.elem_id == "use_kb");
- use_kb.props.value = event.data["use_kb"];
- };
- },
- false);
-```
-
-## Updating documentation
-
-The README is rendered automatically; direct edits will be overwritten.
-In order to modify the README you will need to edit the files for each
-section separately. All of these files will be combined and the README
-will be automatically generated. You can find all of the related files
-in the `docs` folder.
-
-Documentation is written in Github Flavored Markdown and then rendered
-to a final Markdown file by Pandoc. The details for this process are
-defined in the Makefile. The order of files generated are defined in
-`docs/_TOC.md`. The documentation can be previewed in the Workbench file
-browser window.
-
-### Header file
-
-The header file is the first file used to compile the documentation.
-This file can be found at `docs/_HEADER.md`. The contents of this file
-will be written verbatim, without any manipulation, to the README before
-anything else.
-
-### Summary file
+## Running the tests
-The summary file contains quick description and graphic that describe
-this project. The contents of this file will be added to the README
-immediately after the header and just before the table of contents. This
-file is processed by Pandoc to embed images before writing to the
-README.
-
-### Table of Contents file
-
-The most important file for the documentation is the table of contents
-file at `docs/_TOC.md`. This file defines a list of files that should be
-concatenated in order to generate the final README manual. Files must be
-on this list to be included.
-
-### Static Content
+- lint
+- ci
-Save all static content, including images, to the `_static` folder. This
-will help with organization.
+## Managing your Development Environment
-### Dynamic documentation
-
-It may be helpful to have documents that update and write themselves. To
-create a dynamic document, simply create an executable file that writes
-the Markdown formatted document to stdout. During build time, if an
-entry in the table of contents file is executable, it will be executed
-and its stdout will be used in its place.
-
-### Rendering documentation
-
-When a documentation related commit is pushed, a GitHub Action will
-render the documentation. Any changes to the README will be automatially
-committed.
-
-# Managing your Development Environment
-
-## Environment Variables
+### Environment Variables
Most of the configuration for the development environment happens with
Environment Variables. To make permanent changes to environment
variables, modify [`variables.env`](./variables.env) or use the
Workbench UI.
-## Python Environment Packages
+### Python Environment Packages
This project uses one Python environment at `/usr/bin/python3` and
dependencies are managed with `pip`. Because all development is done
@@ -947,7 +226,7 @@ inside a container, any changes to the Python environment will be
ephemeral. To permanently install a Python package, add it to the
[`requirements.txt`](./requirements.txt) file or use the Workbench UI.
-## Operating System Configuration
+### Operating System Configuration
The development environment is based on Ubuntu 22.04. The primary user
has password-less sudo access, but all changes to the system will be
@@ -990,3 +269,7 @@ update.
7. **Regression testing:** Run through the entire demo, from document
ingesting to the frontend, and ensure it is still functional and
that the GUI looks correct.
+
+# License
+
+This project is licensed under the Apache 2.0 License - see the [LICENSE.txt](LICENSE.txt) file for details.
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..d49e410
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,19 @@
+# NIM Anywhere Next
+
+## Basic Retrieval Lessons
+- [ ] Getting started with llms
+- [ ] singe doc rag
+- [ ] naive rag
+- [ ] advanced rag
+- [ ] enterprise rag -> point to the rag 2.0 blueprint
+
+## AI Agent Lessons
+- [x] Build an agent the hard way
+- [ ] Build your first agent
+
+## CI/CD
+- [ ] Look for dead links in tutorials and sidebar
+- [ ] run all labs against answers
+
+## Misc
+- [ ] Update readme
diff --git a/apt.txt b/apt.txt
index 76cd1b2..88c3d31 100644
--- a/apt.txt
+++ b/apt.txt
@@ -1,12 +1,9 @@
ca-certificates
curl
-gettext
jq
less
make
-pandoc
pipx
python3.10-venv
-skopeo
unzip
wget
diff --git a/code/chain_server/configuration.py b/code/chain_server/configuration.py
index e13fecf..bb88a59 100644
--- a/code/chain_server/configuration.py
+++ b/code/chain_server/configuration.py
@@ -48,12 +48,13 @@
Values specified as environment variables will take precedence over all values from files.
"""
+
import logging
import os
from enum import Enum
-from typing import Annotated, Any, Callable, Optional, cast
+from typing import Annotated, Any, Callable, ClassVar, Optional, cast
-from confz import BaseConfig, EnvSource, FileSource
+from confz import BaseConfig, ConfigSource, EnvSource, FileSource
from pydantic import (
BaseModel,
Field,
@@ -189,7 +190,7 @@ class Configuration(BaseConfig):
log_level: Annotated[LogLevels, Field(LogLevels.WARNING, description=LogLevels.__doc__)]
# sources where config is looked for
- CONFIG_SOURCES = [
+ CONFIG_SOURCES: ClassVar[list[ConfigSource]] = [
FileSource(file="./config.yaml", optional=True),
FileSource(file="./config.yml", optional=True),
FileSource(file="./config.json", optional=True),
diff --git a/code/frontend/configuration.py b/code/frontend/configuration.py
index 5d22c54..57249d5 100644
--- a/code/frontend/configuration.py
+++ b/code/frontend/configuration.py
@@ -18,9 +18,9 @@
import logging
import os
from enum import Enum
-from typing import Annotated
+from typing import Annotated, ClassVar
-from confz import BaseConfig, EnvSource, FileSource
+from confz import BaseConfig, ConfigSource, EnvSource, FileSource
from pydantic import Field, FilePath, HttpUrl, field_validator
_ENV_VAR_PREFIX = "APP_"
@@ -77,7 +77,7 @@ def _check_proxy_prefix(cls, val: str) -> str:
log_level: Annotated[LogLevels, Field(LogLevels.INFO, description=LogLevels.__doc__)]
# sources where config is looked for
- CONFIG_SOURCES = [
+ CONFIG_SOURCES: ClassVar[list[ConfigSource]] = [
FileSource(file="./frontend-config.yaml", optional=True),
FileSource(file="./frontend-config.yml", optional=True),
FileSource(file="./frontend-config.json", optional=True),
diff --git a/code/frontend/pages/__init__.py b/code/frontend/pages/__init__.py
index e6b358f..b0ad137 100644
--- a/code/frontend/pages/__init__.py
+++ b/code/frontend/pages/__init__.py
@@ -15,8 +15,6 @@
"""A collection of pages available to views."""
-import gradio as gr
-
from .chat import page as chat
from .control import page as control
diff --git a/code/frontend/pages/chat/__init__.py b/code/frontend/pages/chat/__init__.py
index 823637d..5049d9d 100644
--- a/code/frontend/pages/chat/__init__.py
+++ b/code/frontend/pages/chat/__init__.py
@@ -17,7 +17,7 @@
import uuid
from pathlib import Path
-from typing import AsyncGenerator, Any
+from typing import Any, AsyncGenerator
import gradio as gr
from httpx import ConnectError, HTTPStatusError
diff --git a/code/frontend/pages/control/__init__.py b/code/frontend/pages/control/__init__.py
index 20cb05b..0e76661 100644
--- a/code/frontend/pages/control/__init__.py
+++ b/code/frontend/pages/control/__init__.py
@@ -15,24 +15,19 @@
"""The control panel web app."""
-from pathlib import Path
-import shutil
-import glob
-from typing import List
+import os
import time
+from pathlib import Path
-import os
import gradio as gr
import jinja2
import yaml
-
-from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
-from langchain_milvus.vectorstores.milvus import Milvus
-from langchain_community.document_loaders import PyPDFLoader
-from langchain.text_splitter import RecursiveCharacterTextSplitter
-from langchain.schema import Document
from chain_server.configuration import Configuration as ChainConfiguration
from chain_server.configuration import config as chain_config
+from langchain.schema import Document
+from langchain_community.document_loaders import PyPDFLoader
+from langchain_milvus.vectorstores.milvus import Milvus
+from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
from ... import mermaid
from ...common import IMG_DIR, THEME, USE_KB_INITIAL, USE_RERANKER_INITIAL
@@ -101,10 +96,8 @@
# web ui definition
with gr.Blocks(theme=THEME, css=_CSS, head=mermaid.HEAD) as page:
-
# %% contrl panel tab
with gr.Tab("Control Panel", elem_id="cp-tab", elem_classes=["invert-bg"]):
-
# %% architecture control box
with gr.Accordion(label="Retrieval Configuration"):
with gr.Row(elem_id="kb-row"):
@@ -137,7 +130,6 @@
# %% knowledge base tab
with gr.Tab("Knowledge Base", elem_id="kb-tab", elem_classes=["invert-bg"]):
-
# upload file button
upload_btn = gr.UploadButton("Upload PDFs", icon=str(_UPLOAD_IMG), file_types=[".pdf"], file_count="multiple")
diff --git a/code/tools/audit.sh b/code/tools/audit.sh
deleted file mode 100755
index dc05baa..0000000
--- a/code/tools/audit.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-# auditing
-function _audit_venv() {
- echo "Auditing production python dependencies."
-
- # make a minimal python env
- venv_dir=$(mktemp -d)
- cd $venv_dir
- python3 -m venv "$venv_dir"
- "$venv_dir/bin/pip" install --upgrade pip
- # Install pipdeptree
- "$venv_dir/bin/pip" install pipdeptree
- grep -vf /project/req.filters.txt /project/requirements.txt | \
- "$venv_dir/bin/pip" install -r /dev/stdin
-
- # run the audit on the venv
- "$venv_dir/bin/python" /project/code/tools/audit_venv.py --ignore-healthy
- rm -rf "$venv_dir"
-}
-
-function main() {
- _audit_venv
-}
-
-main
diff --git a/code/tools/audit_venv.py b/code/tools/audit_venv.py
deleted file mode 100644
index a7a8772..0000000
--- a/code/tools/audit_venv.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
-# SPDX-License-Identifier: Apache-2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Check the version of all the dependencies."""
-
-# pylint: disable=bad-builtin,global-statement
-
-import argparse
-import json
-import subprocess
-import sys
-from collections import defaultdict
-from datetime import datetime
-from pprint import pprint
-from typing import Any, Dict, List, Tuple
-
-import requests
-from pip._vendor.packaging.specifiers import SpecifierSet
-from tqdm import tqdm
-
-NOW = datetime.now()
-VULN_MIRROR = "https://pyup.io/aws/safety/free/insecure_full.json"
-VULN_DB = requests.get(VULN_MIRROR, timeout=15).json()
-EXIT_CODE = 0
-
-
-def _parse_arguments() -> argparse.Namespace:
- """Parse the CLI arguments."""
- parser = argparse.ArgumentParser("Dependency Auditor")
- parser.add_argument(
- "--ignore-healthy",
- action="store_true",
- default=False,
- help="If set, only dependencies at yellow, red, or unknown are displayed.",
- )
- parser.add_argument(
- "--max-age",
- type=int,
- default=30,
- help="Maximum number of days for a dependency to be considered old.",
- )
- return parser.parse_args()
-
-
-def _read_dependencies() -> Tuple[List[Tuple[str, str]], Dict[str, List[str]]]:
- """Find all the dependencies of this project."""
- dep_tree = json.loads(
- subprocess.run(["pipdeptree", "--json", "--python", sys.executable], check=True, capture_output=True).stdout
- )
-
- all_deps = [(row["package"]["key"], row["package"]["installed_version"]) for row in dep_tree]
- dep_map: Dict[str, List[str]] = defaultdict(list)
- for row in dep_tree:
- for dep in row["dependencies"]:
- dep_map[dep["key"]].append(row["package"]["key"])
-
- return all_deps, dep_map
-
-
-def _latest_ver(package: str, report: Dict[str, Any]) -> None:
- """Lookup the latest version of a package."""
- # lookup package in pip
- req = requests.get(f"https://pypi.org/pypi/{package}/json", timeout=5)
- if req.status_code != 200:
- return
- metadata = req.json()
-
- # calculate age
- installed = report.get("installed", "0")
- installed_meta = metadata["releases"].get(installed)
- released = datetime.fromisoformat(installed_meta[0]["upload_time"])
- report["age_days"] = (NOW - released).days
-
- # find latest
- report["latest"] = metadata["info"]["version"]
-
-
-def _security(package: str, report: Dict[str, Any], max_age: int) -> None:
- """Lookup the safety of a package."""
- global EXIT_CODE
-
- installed = report.get("installed", "0")
- latest = report.get("latest", "")
- age_days = report.get("age_days", "-1")
-
- # check for cves
- active_cves: List[Tuple[str, str]] = []
- db_records: List[Dict[str, Any]] = VULN_DB.get(package, [])
- for cve in db_records:
- for vuln_spec in cve["specs"]:
- if SpecifierSet(vuln_spec).contains(installed):
- active_cves.append(
- (
- cve["cve"],
- f"https://nvd.nist.gov/vuln/detail/{cve['cve']}",
- )
- )
-
- # assume an out of date copy
- report["health"] = "π‘"
-
- # the installed version is comprimised
- if active_cves:
- report["health"] = "π΄"
- report["cves"] = active_cves
- EXIT_CODE = 1
-
- # package was not found in pypi
- if age_days == -1:
- report["health"] = "β"
-
- # the installed version is not comprimised and (latest or relatively new-ish)
- if latest == installed or age_days < max_age:
- report["health"] = "π’"
-
-
-def main() -> None:
- """Execute main routine."""
- # lookup package information
- args = _parse_arguments()
- full_report = {}
- print("Reading project dependencies...")
- all_packages, depdency_map = _read_dependencies()
-
- print("Processing and scoring dependencies...")
- for pkg, installed in tqdm(all_packages):
- pkg_report = {"installed": installed}
- if depdency_map.get(pkg):
- pkg_report["required_by"] = depdency_map[pkg]
- _latest_ver(pkg, pkg_report) # add age_days and latest
- _security(pkg, pkg_report, args.max_age) # add cves and health score
-
- if not args.ignore_healthy or pkg_report.get("health", "π’") != "π’":
- full_report[pkg] = pkg_report
-
- print("\nFinal Report:")
- pprint(full_report, sort_dicts=False)
-
- sys.exit(EXIT_CODE)
-
-
-if __name__ == "__main__":
- main()
diff --git a/code/tools/bump.sh b/code/tools/bump.sh
deleted file mode 100755
index 3a34258..0000000
--- a/code/tools/bump.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-set -e
-cd /project
-
-
-# helpers
-function _divider() {
- echo -e "\n\n\n\n\n"
-}
-
-# python deps
-function _bump_reqs() {
- echo "Updating Python Dependencies"
- pip install bumper --upgrade > /dev/null
- bump
-}
-
-# nims
-function _bump_nims() {
- echo "Updating NIMs"
- for app in apps/*.sh; do
- # get application image
- export IMAGE=$(env -i /bin/bash $app image 2> /dev/null)
- export TAG=$(env -i /bin/bash $app tag 2> /dev/null)
-
- # ensure this is a nim
- if [[ "$IMAGE" != "nvcr.io/nim/"* ]]; then
- continue
- fi
-
- # find the latest tag
- LATEST=$( \
- skopeo list-tags docker://$IMAGE | \
- jq '.Tags' | \
- jq -r '.[] | select(contains("sha256") | not)' | \
- grep -v 'latest' | \
- tail -n 1)
-
- # check if an update is required
- if [ "${LATEST}x" != "${TAG}x" ]; then
- echo "Updating: $app [ $TAG -> $LATEST ]"
- sed -i -r 's/(^TAG=.*)"'$TAG'"\)/\1"'$LATEST'")/g' "$app"
- git diff "$app"
- else
- echo "NO CHANGE"
- echo "$TAG == $LATEST"
- fi
- echo ""
- done
-}
-
-main() {
- _bump_reqs
- _divider
- _bump_nims
-}
-
-main
diff --git a/code/tools/lint.sh b/code/tools/lint.sh
deleted file mode 100755
index 3ebe474..0000000
--- a/code/tools/lint.sh
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-set -e
-
-cd $(dirname $0)/../..
-
-if [ -z "$1" ] || [ "$1" = "deps" ]; then
- echo "Installing dependencies..."
- which pylint || pip install pylint
- which mypy || pip install mypy
- which black || pip install black
- echo -e "\n\n\n"
-fi
-
-if [ -z "$1" ] || [ "$1" = "pylint" ]; then
- echo "Checking Python syntax with Pylint."
- pylint --rc-file pyproject.toml $(git ls-files 'code/*.py' | grep -v tutorial_app)
- echo -e "\n\n\n"
-fi
-
-if [ -z "$1" ] || [ "$1" = "mypy" ]; then
- echo "Checking Type Hints with MyPy"
- mypy --config-file pyproject.toml $(git ls-files 'code/*.py')
- echo -e "\n\n\n"
-fi
-
-if [ -z "$1" ] || [ "$1" = "black" ]; then
- echo "Checking code formatting with Black"
- black --check . --line-length 120 $(git ls-files 'code/*.py')
- echo -e "\n\n\n"
-fi
-
-if [ -z "$1" ] || [ "$1" = "ipynb" ]; then
- echo "Checking Notebooks for cells with output."
- fail_count=0
- for file in $(git ls-files 'code/*.ipynb'); do
- echo -en "$file\t"
- # filter the ipynb json to get only the cell output, remove empty values
- outputs=$(cat $file | jq '.cells[].outputs' | grep -Pv '(null|\[\])' | cat)
- if [ "$outputs" == "" ]; then
- echo "pass"
- else
- echo "fail"
- echo "$outputs"
- fail_count=$(expr $fail_count + 1)
- fi
- done
-
- if [ $fail_count > 0 ]; then
- exit $fail_count
- fi
- echo -e "\n\n\n"
-fi
-
-if [ -z "$1" ] || [ "$1" = "docs" ]; then
- echo "Checking if the Documentation is up to date."
- cd docs;
- if ! make -q; then
- make -qd | grep -v '^ ' | grep -v 'is older than'
- echo 'fail: in the docs folder run `make all` to update the readme' >&2
- exit 1
- fi
- echo "pass"
- cd ..
- echo -e "\n\n\n"
-fi
-
-if [ "$1" = "fix" ]; then
- echo "Fixing code formatting with Black"
- black . --line-length 120 $(git ls-files 'code/*.py')
- echo -e "\n\n\n"
-
- echo "Ensuring the README.md is up to date"
- cd docs
- make
- cd ..
-
- echo "Clearing Jupyter Notebook cell output."
- for ipynb in $(git ls-files 'code/*.ipynb'); do
- if cat "$ipynb" | jq '.cells[].outputs' | grep -Pv '(null|\[\])' > /dev/null ; then
- echo "$ipynb"
- jupyter nbconvert "$ipynb" --ClearOutputPreprocessor.enabled=True --to=notebook --inplace --log-level=ERROR
- fi
- done
-
- echo -e "\n\n\n"
-fi
-
-echo -e "Success."
-
diff --git a/code/tutorial_app/.vscode/settings.json b/code/tutorial_app/.vscode/settings.json
new file mode 100644
index 0000000..58d2322
--- /dev/null
+++ b/code/tutorial_app/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "git.openRepositoryInParentFolders": "always"
+}
diff --git a/code/tutorial_app/answers/agents_the_hard_way/caching.py b/code/tutorial_app/answers/agents_the_hard_way/caching.py
new file mode 100644
index 0000000..34aa44f
--- /dev/null
+++ b/code/tutorial_app/answers/agents_the_hard_way/caching.py
@@ -0,0 +1,43 @@
+"""Some helpers for caching LLM calls during development."""
+
+from collections import OrderedDict
+from typing import Any
+
+from cachier import cachier
+from openai import OpenAI
+
+CACHE_DIR = "/project/data/scratch"
+
+
+def _call_llm_cached_hash(_: Any, kwds: OrderedDict[str, Any]) -> int:
+ """Safely create a hash used for the cache."""
+ model_name = kwds.get("model_name", "---")
+ message_history = str(kwds.get("message_history"))
+ tool_list = str(kwds.get("tool_list"))
+ return hash(model_name + message_history + tool_list)
+
+
+@cachier(cache_dir=CACHE_DIR, allow_none=False, hash_func=_call_llm_cached_hash)
+def call_llm_cached(
+ model_client: OpenAI,
+ model_name: str,
+ message_history: list[dict[str, str]],
+ tool_list: None | list[dict[str, Any]] = None,
+) -> None | dict[str, Any]:
+ """Create OpenAI completions request."""
+ if tool_list:
+ response = model_client.chat.completions.create(
+ model=model_name,
+ messages=message_history,
+ tools=tool_list,
+ tool_choice="auto",
+ )
+ else:
+ response = model_client.chat.completions.create(
+ model=model_name,
+ messages=message_history,
+ )
+ if response.choices:
+ return response.choices[0].message.to_dict()
+ else:
+ return None
diff --git a/code/tutorial_app/answers/agents_the_hard_way/single_agent.py b/code/tutorial_app/answers/agents_the_hard_way/single_agent.py
new file mode 100644
index 0000000..d436249
--- /dev/null
+++ b/code/tutorial_app/answers/agents_the_hard_way/single_agent.py
@@ -0,0 +1,72 @@
+"""An example agent built from scratch."""
+# type: ignore
+
+import json
+import os
+
+from caching import call_llm_cached
+from openai import OpenAI
+
+API_KEY = os.environ.get("NGC_API_KEY", "---")
+MODEL_URL = "https://integrate.api.nvidia.com/v1"
+MODEL_NAME = "meta/llama-3.3-70b-instruct"
+
+
+# Connect to the model server
+client = OpenAI(base_url=MODEL_URL, api_key=API_KEY)
+
+
+# Create a tool for your agent
+def add(a, b):
+ return a + b
+
+
+# Create a list of all the available tools
+tools = [
+ {
+ "type": "function",
+ "function": {
+ "name": "add",
+ "description": "Add two integers.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "a": {"type": "integer", "description": "First integer"},
+ "b": {"type": "integer", "description": "Second integer"},
+ },
+ "required": ["a", "b"],
+ },
+ },
+ }
+]
+
+
+# Initilialize some short term memory
+messages = [{"role": "user", "content": "What is 3 plus 12?"}]
+
+
+# Prompt the model for a response to the question and update the memory
+llm_response = call_llm_cached(client, MODEL_NAME, messages, tools)
+messages.append(llm_response)
+
+
+# Extract tool request
+tool_call = messages[-1]["tool_calls"][0]
+tool_name = tool_call["function"]["name"]
+tool_args = json.loads(tool_call["function"]["arguments"])
+tool_id = tool_call["id"]
+
+
+# Run the tool
+if tool_name == "add":
+ tool_out = add(**tool_args)
+
+
+# Save the tool output into the memory
+tool_result = {"role": "tool", "tool_call_id": tool_id, "name": tool_name, "content": str(tool_out)}
+messages.append(tool_result)
+
+
+# Prompt the model again, this time with the tool output
+llm_response = call_llm_cached(client, messages, tools)
+messages.append(llm_response)
diff --git a/code/tutorial_app/new_page.sh b/code/tutorial_app/new_page.sh
deleted file mode 100755
index d59d7a3..0000000
--- a/code/tutorial_app/new_page.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-cd $(dirname $0)
-
-
-TEMPLATE_NAME="template"
-export PAGE_NAME=$1
-
-envsubst < pages_templates/${TEMPLATE_NAME}.py.envsub > pages/${PAGE_NAME}.py
-envsubst < pages_templates/${TEMPLATE_NAME}.en_US.yaml.envsub > pages/${PAGE_NAME}.en_US.yaml
-envsubst < pages_templates/${TEMPLATE_NAME}_tests.py.envsub > pages/${PAGE_NAME}_tests.py
-
-cat <> pages/sidebar.yaml
- - label: "$PAGE_NAME"
- target: $PAGE_NAME
-EOM
diff --git a/code/tutorial_app/pages/__init__.py b/code/tutorial_app/pages/__init__.py
index e69de29..a08b2c2 100644
--- a/code/tutorial_app/pages/__init__.py
+++ b/code/tutorial_app/pages/__init__.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/code/tutorial_app/pages/agents_the_hard_way.en_US.yaml b/code/tutorial_app/pages/agents_the_hard_way.en_US.yaml
new file mode 100644
index 0000000..e60e025
--- /dev/null
+++ b/code/tutorial_app/pages/agents_the_hard_way.en_US.yaml
@@ -0,0 +1,388 @@
+# header data
+title: Agents the Hard Way
+waiting_msg: Let me know when you are ready.
+testing_msg: Your turn! Edit the code in the editor.
+next: Let's go!
+
+welcome_msg: |
+ In this excercise, we will be building a simple AI agent from scratch.
+ Later, we will replace much of this logic with prebuilt tools, but building it ourselves helps
+ us understand the fundamentals.
+
+ 
+
+tasks:
+ - name: What is an agent?
+ msg: |
+ An agent is an application that uses an LLM to make decisions and interact with the outside world.
+
+ Agents have four key parts:
+ 1. **MODEL:** An LLM that decides which tools to use and how to respond
+ 1. **TOOLS:** Functions that let the LLM perform actions like math or database queries
+ 1. **MEMORY:** Information available to the LLM during and between conversations
+ 1. **ROUTING:** The LLM will make decisions about what to do next, we need to route messages accordingly
+
+ In this exercise, we'll build a basic agent by implementing these basic components from scratch.
+
+ - name: Setup the environment
+ msg: |
+ To get us started, I'll drop is some code and explain as I go.
+
+ Like most Python code, we start by importing the necessary modules.
+ - The `openai` module is maintained by OpenAI and is used for talking to models running remotely.
+ - The `caching` module helps us cache LLM responses and conserve tokens.
+
+ The caching module is custom code, but it makes use of the `cachier` Python library.
+ Feel free to check it out by opening Visual Studio Code or JupyterLab from the Workbench interface.
+ response: |
+ Let's keep going!
+ prep: prep_imports
+
+ - name: Load the configuration
+ msg: |
+ Now I'll load our configuration as contants:
+ - `API_KEY` loads our credentials from an environment variable
+ - `MODEL_URL` points the the server hosting your model
+ - `MODEL_NAME` is the model we are going to use
+
+ Why these values?
+
+ This `MODEL_URL` points to NVIDIA's hosted Model API Catalog.
+
+ Because we are starting with NVIDIA's hosted service, we have a lot of models to choose from.
+ Picking where to start can be difficult.
+
+ - Start with a newer open source model from a team you recognize.
+ - Start with a moderate sized model (~70b parameters). You'll work on optimizing to a smaller model later.
+ - If you need features like function calling, make sure the model supports it! (More on that later).
+ response: |
+ Let's keep going!
+ prep: prep_api_key
+
+ - name: Part 1 - The Model
+ msg: |
+ The first of four critical parts for an agent is the AI model.
+
+ We will talk to the AI models on build.nvidia.com using the OpenAI API.
+ This API is the *language* that most model providers use.
+ This means we can use the `OpenAI` class to connect to most model providers. Neat!
+
+ Using the `MODEL_URL` and `API_KEY` defined above, create a new model client named `client`.
+
+ #### Resources
+ - [Docs: OpenAI Quick Start](https://github.com/openai/openai-python?tab=readme-ov-file#usage)
+ - [Docs: OpenAI Custom base URL](https://github.com/openai/openai-python?tab=readme-ov-file#configuring-the-http-client)
+ - [Example: NVIDIA hosted models](https://build.nvidia.com/nvidia/llama-3_3-nemotron-super-49b-v1)
+
+
+ Need some help?
+
+ ```python
+ client = OpenAI(
+ base_url=MODEL_URL,
+ api_key=API_KEY
+ )
+ ```
+
+ response: |
+ Great! We are now connected to the models!
+ prep: prep_define_client
+ test: test_define_client
+
+ - name: Part 2 - Tools
+ msg: |
+ Every agent has access to some tools.
+ Tools are how the model is able to interact with the world.
+
+ Tools are created by developers to connect to external services.
+ The model cannot *do* anything that it doesn't have a tool for.
+
+ Tools are simply code that is executed at the LLM's request.
+ So lets write our first tool!
+
+ Create a function, called `add`, that adds two integers, called `a` and `b`.
+
+
+ Need some help?
+
+ Here is a simple Python function for adding two numbers.
+
+ ```python
+ def add(a, b):
+ return a + b
+ ```
+
+ We can use this function as a tool by returning it from our function.
+
+ response: ~
+ prep: prep_adding_tool
+ test: test_adding_tool
+
+ - name: Describe the tools
+ msg: |
+ Before moving on, we need create a description of the tools that the model can understand.
+ Think of this as the LLM's menu of possible helpers.
+
+ The syntax on this is tricky and picky, so I'll do this part.
+ The good news is this is usualy handled for you by agent frameworks.
+ This syntax is defined as part of the OpenAI API spec.
+
+ #### Resources
+ - [Docs: Defining functions format](https://platform.openai.com/docs/guides/function-calling?api-mode=responses&example=get-weather#defining-functions)
+ response: Your turn again!
+ prep: prep_tools_list
+
+ - name: Part 3 - Memory
+ msg: |
+ The topic of memory is complex, and we will only scratch the surface.
+ There are two types of memory, short term and long term.
+ For now, lets focus on short term memory.
+
+ Short term memory starts at the beginining of the conversation
+ and ends at the end of the conversation.
+ Put simply, short term memory is a log of the conversation.
+
+ For this, we will use a humble list.
+ Every line in our list will be a message in the conversation, stored in a dictionary.
+ The messages can come from the user, the assistant, or from tools.
+
+ Create an initial list called `memory`.
+ Initialize it with this message from the user:
+
+ ```python
+ {"role": "user", "content": "What is 3 plus 12?"}
+ ```
+
+ #### Reference
+ - [Docs: Chat completion docs](https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages)
+
+
+ Need some help?
+
+ ```python
+ messages = [
+ {"role": "user", "content": "What is 3 plus 12?"}
+ ]
+ ```
+
+ response: Looking good. Now let's have some fun!
+ prep: prep_messages
+ test: test_messages
+
+ - name: Run the agent
+ msg: |
+ We now have three of the four pieces required of an agent.
+ The last missing piece is the routing.
+ For the time being, we will forgo this piece and manually route this mesasge.
+
+ The agent starts by giving the memory and tool list to the model.
+ The model will reply by either requesting a tool or answering the question.
+
+ Based on the model's response, we will decide how to proceed.
+
+ Call the model using the `call_llm_cached` function.
+ The function takes four arguments:
+ - `model_client` - the OpenAI client
+ - `model_name` - the name of the model to use (check the constants from before)
+ - `message_history` - your short term memory
+ - `tool_list` - the menu of tools the model can access
+
+ Use this function to call the llm.
+ Save the result by appending it to the end of `messages`.
+
+
+ Need some help?
+
+ ```python
+ llm_response = call_llm_cached(
+ client,
+ MODEL_NAME,
+ messages,
+ tools
+ )
+ messages.append(llm_response)
+ ```
+
+ response: |
+ It's alive!
+
+ π This is what we got back. π
+
+ ```json
+ {{ result }}
+
+ | eval | tojson(2) }}
+ ```
+ prep: prep_run_agent
+ test: test_run_agent
+
+ - name: Part 4 - Routing
+ msg: |
+ Looks like the model chose to request a tool instead of answering.
+ We can tell because `content` is `null` and a function is defined under `tool_calls`.
+
+
+ Tools vs Function Calling?
+
+ These terms are often used interchangeably.
+ Technically, **Function Calling** is a feature of a model.
+ This feature allows the model to request that the developer run the **Tools**.
+
+ But most of the time, these terms are simply referring to an agents ability to run functions.
+
+
+ We can see that the model has requested that we run the `add` function with the arguments 3 and 12.
+
+ Write some code to extract the requested function's name, arguments, and id from `messages[-1]`.
+ Store those values in variables called `tool_name`, `tool_args`, and `tool_id` respectively.
+
+ :blue-badge[TIP] The value of `arguments` is a string. Use the `json` library to read it.
+
+ ```python
+ tool_args = json.loads(tool_args_str)
+ ```
+
+
+ Need some help?
+
+ ```python
+ tool_call = messages[-1]["tool_calls"][0]
+ tool_name = tool_call["function"]["name"]
+ tool_args = json.loads(tool_call["function"]["arguments"])
+ tool_id = tool_call["id"]
+ ```
+
+ prep: prep_extract_tool
+ test: test_extract_tool
+
+ - name: Tool Calling
+ msg: |
+ If you haven't noticed by now, even though the feature is called tool calling...
+ The model doesn't actually call the tool!
+
+ So let's write the code to run the tools as requested.
+
+ Check if the tool name is equal to `add`.
+ If it is, then run the add function with the requested arguments.
+
+ Save the output from the tool call to a variable called `tool_out`.
+
+
+ Need some help?
+
+ ```python
+ if tool_name == "add":
+ tool_out = add(**tool_args)
+ ```
+
+ response: |
+ Excellent! This is a portion of the heavy lifting usually handled by agent frameworks.
+ prep: prep_execute_tool
+ test: test_execute_tool
+
+ - name: Update the memory
+ msg: |
+ We just got `tool_out` back from the tool.
+ Now we can update the memory with the tool output.
+
+ Next time we call the model, it will see the prompt from the user,
+ it's own request for a tool call, and the result of that tool call.
+
+ This is another one where the syntax is tricky and picky.
+ I'll type this one out, but this is the standard message format for a tool call result.
+ This format is also defined by OpenAI.
+
+ #### Reference
+ - [Docs: Chat completion docs](https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages)
+ response: |
+ Our memory now looks like this:
+
+ ```json
+ [
+ {"role": "user", "content": "What is 3 plus 12?"},
+ {"role": "assistant", "tool_calls": ... },
+ {"role": "tool", "tool_call_id": "...", "name": "add", "content": "15"}
+ ]
+ ```
+ prep: prep_update_memory
+
+ - name: Loop back to the model
+ msg: |
+ Now, we call the model again and save the response in memory.
+
+ :blue-badge[HINT] It is the exact same code as last time.
+
+
+ Need some help?
+
+ ```python
+ llm_response = call_llm_cached(
+ client,
+ MODEL_NAME,
+ messages,
+ tools
+ )
+ messages.append(llm_response)
+ ```
+
+ response: |
+ You got it!
+
+ Here is what the model had to say:
+
+ ```json
+ {{ result | eval | tojson(2) }}
+ ```
+ prep: prep_call_model_again
+ test: test_call_model_again
+
+closing_header: You did it!
+closing_msg: |
+ Your very first agent! Congratulations.
+
+ This simple example demonstrates the basic components of an agent and how they work together.
+
+ Of course... we made a few *convenient assumptions* along the way.
+
+ For example. What if:
+ - the agent wants more than one tool call?
+ - the agent doesn't want any?
+ - we need additional feedback from the user?
+ - we want build complex multi-agent systems?
+
+ The list goes on. This is the value of agentic frameworks.
+ All of these worries are abstracted away and you focus on writing and wiring tools.
+ There are many agentic frameworks to choose from, and many people choose to make their own.
+
+ In the next example, we will be using [LangGraph](https://www.langchain.com/langgraph) as our framework.
+
+# Test time error messages
+info_test_nonzero_exit_code: "Uh oh! Looks like your code isn't running. Check the *Terminal Output* tab."
+info_no_client: "Create the variable named `client`."
+info_wrong_client_type: "`client` needs to be an instance of the OpenAI class."
+info_client_bad_request: |
+ We got a Bad Request error. This usually happens if you give the wrong args to the `chat.completions.create` method.
+info_client_bad_auth: |
+ You API Key was rejected.
+ Check the value you set in the workbench interface in the Project Container --> Variables configuration.
+info_test_bad_url: "No models were found at the provided URL. Make sure we are pointing to a working endpoint."
+info_no_add: "Create a function named `add`."
+info_add_not_fun: "`add` needs to be a funtion."
+info_bad_add_args: "Check the arguments to your `add` function."
+info_add_not_working: "The `add` function doesn't seem to be adding."
+info_no_messages: "Create a `messages` variable to be a list of messages."
+info_messages_not_correct: "Check the contents of your `messages` variable."
+info_messages_too_short: |
+ The chat history should have a langth of two. One message from the user,
+ and the reply from the model.
+info_bad_message_order: |
+ `messages` appears to be in the wrong order. The oldest messages should be first (lowest index).
+info_no_tool_out: |
+ Waiting to see the tool output in `tool_out`.
+info_tool_out_not_num: |
+ The output from your tool doesn't seem to be a number.
+info_no_tool_name: "`tool_name` should be set to equal the function name from the LLM response."
+info_no_tool_args: "`tool_args` should be set to equal the function arguments. Make sure to decode them with the `json` library."
+info_no_tool_id: "`tool_id` should be the tool call's id from the LLM response."
+info_messages_len_3: "Call the llm with `call_llm_cached` and append the response to `messages`."
+info_messages_all_wrong: "Make sure you append to the messages list."
diff --git a/code/tutorial_app/pages/editor_test.py b/code/tutorial_app/pages/agents_the_hard_way.py
similarity index 89%
rename from code/tutorial_app/pages/editor_test.py
rename to code/tutorial_app/pages/agents_the_hard_way.py
index cf44a1e..5070821 100644
--- a/code/tutorial_app/pages/editor_test.py
+++ b/code/tutorial_app/pages/agents_the_hard_way.py
@@ -19,19 +19,19 @@
import live_labs
import streamlit as st
-from pages import editor_test_tests as TESTS
+from pages import agents_the_hard_way_tests as TESTS
MESSAGES = live_labs.MessageCatalog.from_page(__file__)
NAME = Path(__file__).stem
EDITOR_DIR = Path("/project/code").joinpath(NAME)
-EDITOR_FILES = ["file1.py", "file2.py"]
+EDITOR_FILES = ["single_agent.py"]
+# name of the editor file
with live_labs.Worksheet(name=NAME, autorefresh=0).with_editor(EDITOR_DIR, EDITOR_FILES) as worksheet:
# Header
st.title(MESSAGES.get("title"))
st.write(MESSAGES.get("welcome_msg"))
- st.header(MESSAGES.get("header"), divider="gray")
# Print Tasks
worksheet.live_lab(MESSAGES, TESTS)
diff --git a/code/tutorial_app/pages/agents_the_hard_way_tests.py b/code/tutorial_app/pages/agents_the_hard_way_tests.py
new file mode 100644
index 0000000..871e461
--- /dev/null
+++ b/code/tutorial_app/pages/agents_the_hard_way_tests.py
@@ -0,0 +1,335 @@
+# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for auto continuing associated tasks."""
+
+import shutil
+from pathlib import Path
+
+from live_labs.editor import send_keys
+from live_labs.testing import isolate
+
+NAME = "agents_the_hard_way"
+ANSWER_DIR = Path(__file__).parent.parent.joinpath("answers", NAME)
+EDITOR_DIR = Path("/project/code").joinpath(NAME)
+PYTHON_EXE = "/usr/bin/python"
+
+
+## Setup the environment
+def prep_imports() -> None:
+ """Prepare the imports and api key."""
+ cache_answer = ANSWER_DIR.joinpath("caching.py")
+ cache_lib = EDITOR_DIR.joinpath("caching.py")
+ shutil.copy(cache_answer, cache_lib)
+
+ send_keys(
+ r'''
+ """An example agent built from scratch."""
+
+ import json
+ import os
+
+ from caching import call_llm_cached
+ from openai import OpenAI
+ '''
+ )
+
+
+## Load the configuration
+def prep_api_key() -> None:
+ """Prepare the imports and api key."""
+ send_keys(
+ r"""
+
+ API_KEY = os.environ.get("NGC_API_KEY", "---")
+ MODEL_URL = "https://integrate.api.nvidia.com/v1"
+ MODEL_NAME = "meta/llama-3.3-70b-instruct"
+ """
+ )
+
+
+## Part 1 - The Model
+def prep_define_client() -> None:
+ """Add some comments."""
+ send_keys(
+ r"""
+
+
+ # Connect to the model server
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_define_client():
+ """define the client"""
+ import caching # pyright: ignore[reportMissingImports]
+ import openai # pyright: ignore[reportMissingImports]
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ MODEL_NAME = "meta/llama-3.3-70b-instruct"
+
+ # look for the value
+ if not hasattr(single_agent, "client"):
+ print(":TestFail: info_no_client")
+ return
+
+ # ensure the correct type
+ if not isinstance(single_agent.client, openai.OpenAI):
+ print(":TestFail: info_wrong_client_type")
+ return
+
+ # ensure it works
+ messages = [{"role": "user", "content": "Hello!"}]
+ try:
+ _ = caching.call_llm_cached(single_agent.client, MODEL_NAME, messages)
+ except openai.BadRequestError:
+ print(":TestFail: info_client_bad_request")
+ except openai.AuthenticationError:
+ print(":TestFail: info_client_bad_auth")
+ except openai.NotFoundError:
+ print(":TestFail: info_test_bad_url")
+
+
+## Part 2 - Tools
+def prep_adding_tool() -> None:
+ """Add some comments."""
+ send_keys(
+ r"""
+
+
+ # Create a tool for your agents
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_adding_tool():
+ """make sure its an add function"""
+ import inspect
+
+ import single_agent # pyright: ignore[reportMissingImports]s
+
+ if not hasattr(single_agent, "add"):
+ print(":TestFail: info_no_add")
+ return
+
+ if not callable(single_agent.add):
+ print(":TestFail: info_add_not_fun")
+
+ signature = inspect.signature(single_agent.add)
+ args = list(signature.parameters.keys())
+ if args != ["a", "b"]:
+ print(":TestFail: info_bad_add_args")
+
+ if single_agent.add(7, 8) != 7 + 8:
+ print(":TestFail: info_add_not_working")
+ return
+
+
+## Describe the Tools
+def prep_tools_list():
+ """Write out the tools list."""
+ send_keys(
+ r"""
+
+
+ # A list of tools for the LLM
+ tools = [
+ {
+ "type": "function",
+ "function": {
+ "name": "add",
+ "description": "Add two integers.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "a": {"type": "integer", "description": "First integer"},
+ "b": {"type": "integer", "description": "Second integer"},
+ },
+ "required": ["a", "b"],
+ },
+ },
+ }
+ ]
+ """
+ )
+
+
+## Part 3 - Memory
+def prep_messages():
+ """Write a comment."""
+ send_keys(
+ r"""
+
+
+ # Initilialize some short term memory
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_messages():
+ """create a message list"""
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ if not hasattr(single_agent, "messages"):
+ print(":TestFail: info_no_messages")
+ return
+
+ if single_agent.messages != [{"role": "user", "content": "What is 3 plus 12?"}]:
+ print(":TestFail: info_messages_not_correct")
+ return
+
+
+## Run the agent
+def prep_run_agent():
+ """Write a comment."""
+ send_keys(
+ r"""
+
+
+ # Prompt the model for a response to the question and update the memory
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_run_agent():
+ """Wait for llm response."""
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ if not hasattr(single_agent, "messages"):
+ print(":TestFail: info_no_messages")
+ return
+
+ if len(single_agent.messages) < 2: # noqa
+ print(":TestFail: info_messages_too_short")
+ return
+
+ if single_agent.messages[1]["role"] != "assistant":
+ print(":TestFail: info_bad_message_order")
+ return
+
+ print(single_agent.messages[1])
+
+
+## Part 4 - Routing
+def prep_extract_tool():
+ """Write some comments."""
+ send_keys(
+ r"""
+
+
+ # Extract tool request
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_extract_tool():
+ """Wait for tool execution."""
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ # check for variable
+ for var, var_type in {"tool_name": str, "tool_args": dict, "tool_id": str}.items():
+ if not hasattr(single_agent, var):
+ print(f":TestFail: info_no_{var}")
+ return
+
+ var_val = getattr(single_agent, var)
+
+ if not isinstance(var_val, var_type):
+ print(f":TestFail: info_no_{var}")
+ return
+
+
+## Tool Calling
+def prep_execute_tool():
+ """Write some comments."""
+ send_keys(
+ r"""
+
+
+ # Run the requested tool
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_execute_tool():
+ """Wait for tool execution."""
+ import numbers
+
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ # check for variable
+ if not hasattr(single_agent, "tool_out"):
+ print(":TestFail: info_no_tool_out")
+ return
+
+ # check variable type
+ if not isinstance(single_agent.tool_out, numbers.Number):
+ print(":TestFail: info_tool_out_not_num")
+ return
+
+
+## Update the memory
+def prep_update_memory():
+ """Write some comments."""
+ send_keys(
+ r"""
+
+
+ # Save the tool output into the memory
+ tool_result = {"role": "tool", "tool_call_id": tool_id, "name": tool_name, "content": str(tool_out)}
+ messages.append(tool_result)
+ """
+ )
+
+
+## Loop back to the model
+def prep_call_model_again():
+ """Write some comments."""
+ send_keys(
+ r"""
+
+
+ # Call the model again with the tool output
+ """
+ )
+
+
+@isolate(EDITOR_DIR, PYTHON_EXE)
+def test_call_model_again():
+ """call the model again"""
+ import single_agent # pyright: ignore[reportMissingImports]
+
+ if not hasattr(single_agent, "messages"):
+ print(":TestFail: info_no_messages")
+ return
+
+ if not isinstance(single_agent.messages, list):
+ print(":TestFail: info_messages_all_wrong")
+ return
+
+ if len(single_agent.messages) == 3: # noqa
+ print(":TestFail: info_messages_len_3")
+ return
+
+ if len(single_agent.messages) != 4: # noqa
+ print(":TestFail: info_messages_all_wrong")
+ return
+
+ print(single_agent.messages[3])
diff --git a/code/tutorial_app/pages/editor_test.en_US.yaml b/code/tutorial_app/pages/editor_test.en_US.yaml
deleted file mode 100644
index 21a0e5b..0000000
--- a/code/tutorial_app/pages/editor_test.en_US.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-# header data
-title: Write some code
-welcome_msg: |
- In this example, we are going to write some code.
-header: |
- Sometimes, you have to write code.
-
- here, we will teach you how to do that.
-
-# general strings
-waiting_msg: Let me know when you are ready.
-testing_msg: Waiting for you to complete the task.
-next: Next
-
-# task script
-tasks:
- - name: Make a string
- msg: |
- In test1.py, define a string called `my_string` that equals `five`.
-
-
- Need a hint?
-
- Try this:
-
- ```python
- my_string = "five"
- ```
-
- response: |
- Nicely done! I checked and here is what I saw:
- {{ result | indent(4) }}
- test: test_my_string
-
-# footer data
-closing_msg: "Congratulations! You have completed this exercise."
-
-# testing messages
-# the helpers in the testing module may return one of these errors
-info_test_nonzero_exit_code: "Uh oh! I'm not able to run your code. Check the **Terminal Output** tab for details."
-info_test_timeout: "Your code seems to be taking too long to run."
-
-# custom testing messages
-# if you add manual tests, you can add your own messages here
-info_no_my_string: "`my_string` does not exist."
-info_my_string_not_five: "`my_string` exists, but isn't set to the correct value."
diff --git a/code/tutorial_app/pages/editor_test_tests.py b/code/tutorial_app/pages/editor_test_tests.py
deleted file mode 100644
index ccff212..0000000
--- a/code/tutorial_app/pages/editor_test_tests.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
-# SPDX-License-Identifier: Apache-2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Tests for auto continuing associated tasks."""
-
-import sys
-from pathlib import Path
-
-from live_labs.testing import TestFail, isolate
-
-NAME = "editor_test"
-EDITOR_DIR = Path("/project/code").joinpath(NAME)
-
-
-def sample_test():
- """Test a project using built in helpers."""
- # The testing module contains some test compatible helpers.
- # These helpers can either get an object or ensure the state of an object.
- # If there is an error in these helpers, they will raise TestFail automatically.
- raise TestFail("info_test_test")
-
-
-@isolate(EDITOR_DIR)
-def test_my_string():
- """Wait for my_string to be ready."""
- import file1 # pyright: ignore[reportMissingImports]
-
- print("Looking for my_string.")
-
- if not hasattr(file1, "my_string"):
- print(":TestFail: info_no_my_string")
- return
-
- print("Looking for five.")
-
- if file1.my_string != "five":
- print(":TestFail: info_my_string_not_five")
- return
-
- print("Looks good!")
-
-
-if __name__ == "__main__":
- sys.stdout.write("---------------\n")
- # you can use this space for testing while you are
- # developing your tests
- test_my_string()
diff --git a/code/tutorial_app/pages/overview.en_US.yaml b/code/tutorial_app/pages/overview.en_US.yaml
index 18ad1d5..3446145 100644
--- a/code/tutorial_app/pages/overview.en_US.yaml
+++ b/code/tutorial_app/pages/overview.en_US.yaml
@@ -1,128 +1,17 @@
# header data
-title: NVIDIA AI Workbench Live Labs
-waiting_msg: Click `Next` to continue.
-testing_msg: Go do the task!
-next: Next
-
-header: "Orientation"
+title: NIM Anywhere Live Labs
welcome_msg: |
- 
+ 
#### Welcome
- This is the AI Workbench Live Labs, an interactive guide to the features of NVIDIA AI Workbench.
+ This is the NIM Anywhere Live Labs, an interactive guide to using NVIDIA solutions.
It will help you learn by doing instead of only reading the docs.
- However, you should feel free to work through the [docs](https://docs.nvidia.com/ai-workbench/user-guide/latest/overview/introduction.html)
- on your own or with the assistance of an LLM.
-
It is organized into **exercises** that are divided into **tasks**.
- Completing each **task** unlocks content for the exercise and will give you feedback.
-
-
-
-tasks:
-
- - name: Guide Overview
- msg: |
- ###### Use the Sidebar to navigate this guide (click the :arrow_forward: icon in the top left to expand it)
- There are three sections of exercises, and each exercise is organized into tasks. Completing
- tasks unlocks more content in the exercise.
-
- 1. **Basic** explores core concepts and the Desktop App UI. (15 mins)
- 2. **Advanced** digs deeper to show example workflows. (20 mins - 1 hour)
- 3. **Sneak Peek** previews "in-early-development" features that will be stable soon.
-
- response: Ok. Let's get started. **Make sure the Desktop App is running with the view for this project visible.**
-
- - name: The AI Workbench Desktop App
- msg: |
- ##### AI Workbench is a free development platform that helps you:
- * **Connect** to and work on remote systems as if they were local.
- * **Develop** in containerized environments.
- * **Manage** development environments across local and remote systems.
-
- ##### The user experience is handled through views and tabs.
-
- - **Main Locations View**: For managing and accessing remote systems
- - **Single Location View**: For managing and accessing different projects on a single system
- - **Project Tab**: For managing and working with a single project
-
- 
- ##### There are three basics steps to get working:
- 1. You select a **location** to work in (*local* is default)
- 2. You create or select a **project** to work on
- 3. You start a **web app** or an **IDE** to do your work in the project
-
- ###### If you've gotten this far, you've already done all of these steps.
-
- response: |
- Let's get started. **Make sure the Desktop App is running with the view for this project visible.**
-
-
- - name: Starting JupyterLab
- msg: |
- ###### Let's begin with the most basic thing possible in a Workbench project, starting JupyterLab.
- - Go to the Desktop App and the Project View for this project
- - Find the large green button at the top right and click the drop down
- - Select JupyterLab
- - Wait for JupyterLab to open in your default browser.
-
- 
-
- response: Great. JupyterLab is running.
-
- - name: Stopping JupyterLab
- msg: |
- ###### Now let's do the second most basic thing possible, stopping JupyterLab.
- - Go to the project view for this project and click the **Project Dashboard**
- - Scroll down a little to see the **Project Container** section
- - You will see that both this Tutorial app and JupyterLab are running
- - Click the JupyterLab toggle to stop it.
-
- response: You stopped JupyterLab.
-
- - name: Finishing Up the Overview
- msg: |
- ###### Objective sources for help and validation are important, so don't be shy about asking for help.
-
- If something goes wrong or you are confused, work through the following steps:
-
- 1. Click the green :material/book_4: button to see our troubleshooting guide.
- 2. Click the green :material/help: button to see crowd sourced answers in the NVIDIA Developer Forum.
- 3. If you find a bug, click the :material/bug_report: button in the sidebar to create a report on GitHub.
- 4. If all else fails, email us at `aiworkbench-support@nvidia.com`.
-
- response: Awesome. Let's get started!
-
-
-
-
-# footer data
-closing_msg: |
- ##### Ok. Now you are ready to get started for real.
+ As you complete the requested tasks, the excercise will advance itself.
-# testing messages
-# the helpers in the testing module may return one of these errors
-info_wait_for_project: "Waiting for the project to exist."
-info_build_ready: ~
-info_build_needed: "It looks like your project needs you to start a new build. Please do that in the environment tab."
-info_build_running: "Your project's build is currently running."
-info_buid_error: "Uh oh! There was an error building your project. Please check the logs."
-info_container_not_running: ~
-info_container_running: ~
-info_container_paused: "The container has been manually paused."
-info_container_dead: "Uh oh! The container does not seem healthy. Please check the Workbench logs."
-info_wait_for_app: "Waiting for the application to exist."
-info_app_is_running: ~
-info_app_not_running: "Waiting for you to start the application."
-info_app_starting: "The application is starting up! Just a few more seconds."
-info_compose_is_running: "Docker Compose is running."
-info_compose_not_running: "Start Docker Compose from the environment tab."
-info_compose_starting: "Docker Compose is starting. This can take a while the first time."
-info_compose_error: "Uh oh! Docker Compose had an error. Please check the logs."
-info_wait_for_package: "Waiting for you to configure the necessary package."
-info_wait_for_file: "Waiting for you to create the requested file."
+ Let's make some AI!
diff --git a/code/tutorial_app/pages/overview.py b/code/tutorial_app/pages/overview.py
index d19c774..6e08d53 100644
--- a/code/tutorial_app/pages/overview.py
+++ b/code/tutorial_app/pages/overview.py
@@ -28,7 +28,3 @@
# Header
st.title(MESSAGES.get("title"))
st.write(MESSAGES.get("welcome_msg"))
- st.header(MESSAGES.get("header"), divider="gray")
-
- # Print Tasks
- worksheet.live_lab(MESSAGES, TESTS)
diff --git a/code/tutorial_app/pages/sidebar.yaml b/code/tutorial_app/pages/sidebar.yaml
index 612b35b..203e6b0 100644
--- a/code/tutorial_app/pages/sidebar.yaml
+++ b/code/tutorial_app/pages/sidebar.yaml
@@ -14,5 +14,7 @@ navbar:
- label: "π Overview"
target: "overview"
show_progress: False
- - label: "πEditor Test"
- target: editor_test
+ - label: Build Agents with NVIDIA
+ children:
+ - label: "πAgents the Hard Way"
+ target: agents_the_hard_way
diff --git a/code/tutorial_app/pages_templates/template.en_US.yaml.envsub b/code/tutorial_app/pages_templates/template.en_US.yaml.envsub
deleted file mode 100644
index f780efd..0000000
--- a/code/tutorial_app/pages_templates/template.en_US.yaml.envsub
+++ /dev/null
@@ -1,61 +0,0 @@
-# header data
-title: $PAGE_NAME
-welcome_msg: |
- An example welcome message.
-
- You **should** update this. GitHub Flavored Markdown is accepted with some additional extensions.
-
- Check out the [formatting docs here](https://docs.streamlit.io/develop/api-reference/text/st.markdown).
-header: |
- This is a new page!
- Edit the content to make your own tutorial.
-
-# general strings
-waiting_msg: Let me know when you are ready.
-testing_msg: Waiting for task to complete.
-next: Next
-
-# task script
-tasks:
- - name: The first task
- msg: |
- This message should explain the task to the user.
-
- Again, Markdown is accepted.
- response: |
- After the user completes their task, this message will be shown in a green bubble.
- test: ~
- # some tasks are able to check themselves and automatically continue when they are complete.
- # to do this, add a function to ${PAGE_NAME}_tests.py to test if the step has completed.
- # then add the function name to test.
- #
- # if this task does not have a test, you can omit this line or set it to the null value (~)
-
-# footer data
-closing_msg: "Congratulations! You have completed this exercise."
-
-# testing messages
-# the helpers in the testing module may return one of these errors
-info_wait_for_project: "Waiting for the project to exist."
-info_build_ready: ~
-info_build_needed: "It looks like your project needs you to start a new build. Please do that in the environment tab."
-info_build_running: "Your project's build is currently running."
-info_buid_error: "Uh oh! There was an error building your project. Please check the logs."
-info_container_not_running: ~
-info_container_running: ~
-info_container_paused: "The container has been manually paused."
-info_container_dead: "Uh oh! The container does not seem healthy. Please check the Workbench logs."
-info_wait_for_app: "Waiting for the application to exist."
-info_app_is_running: ~
-info_app_not_running: "Waiting for you to start the application."
-info_app_starting: "The application is starting up! Just a few more seconds."
-info_compose_is_running: "Docker Compose is running."
-info_compose_not_running: "Start Docker Compose from the environment tab."
-info_compose_starting: "Docker Compose is starting. This can take a while the first time."
-info_compose_error: "Uh oh! Docker Compose had an error. Please check the logs."
-info_wait_for_package: "Waiting for you to configure the necessary package."
-info_wait_for_file: "Waiting for you to create the requested file."
-
-# custom testing messages
-# if you add manual tests, you can add your own messages here
-info_test_test: "This is a test!"
diff --git a/code/tutorial_app/pages_templates/template_tests.py.envsub b/code/tutorial_app/pages_templates/template_tests.py.envsub
deleted file mode 100644
index 35afd8b..0000000
--- a/code/tutorial_app/pages_templates/template_tests.py.envsub
+++ /dev/null
@@ -1,45 +0,0 @@
-# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
-# SPDX-License-Identifier: Apache-2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Tests for auto continuing associated tasks."""
-
-import sys
-
-from .editor_test import EDITOR_DIR
-from live_labs.testing import TestFail, isolate
-
-
-def sample_test():
- """Test a project using built in helpers."""
- # The testing module contains some test compatible helpers.
- # These helpers can either get an object or ensure the state of an object.
- # If there is an error in these helpers, they will raise TestFail automatically.
- raise testing.TestFail("info_test_test")
-
-
-@isolate(EDITOR_DIR)
-def isolated_test():
- """Run test in another Python process."""
- import sys
-
- sys.stdout.write("info_isolated_test")
- sys.exit(1)
-
-
-if __name__ == "__main__":
- sys.stdout.write("---------------\n")
- # you can use this space for testing while you are
- # developing your tests
- sample_test()
- isolated_test()
diff --git a/code/tutorial_app/static/hero.png b/code/tutorial_app/static/hero.png
deleted file mode 100644
index 77981e2..0000000
Binary files a/code/tutorial_app/static/hero.png and /dev/null differ
diff --git a/code/tutorial_app/static/robots/assembly.png b/code/tutorial_app/static/robots/assembly.png
new file mode 100644
index 0000000..4f9081a
Binary files /dev/null and b/code/tutorial_app/static/robots/assembly.png differ
diff --git a/code/tutorial_app/static/robots/hero.png b/code/tutorial_app/static/robots/hero.png
new file mode 100644
index 0000000..a1546b5
Binary files /dev/null and b/code/tutorial_app/static/robots/hero.png differ
diff --git a/code/tutorial_app/static/robots/strong.png b/code/tutorial_app/static/robots/strong.png
new file mode 100644
index 0000000..03d3cd3
Binary files /dev/null and b/code/tutorial_app/static/robots/strong.png differ
diff --git a/code/tutorial_app/static/robots/wave.png b/code/tutorial_app/static/robots/wave.png
new file mode 100644
index 0000000..8ed22b5
Binary files /dev/null and b/code/tutorial_app/static/robots/wave.png differ
diff --git a/code/tutorial_app/tutorial_app.code-workspace b/code/tutorial_app/tutorial_app.code-workspace
index f00adf4..035d628 100644
--- a/code/tutorial_app/tutorial_app.code-workspace
+++ b/code/tutorial_app/tutorial_app.code-workspace
@@ -5,13 +5,9 @@
}
],
"settings": {
- "pylint.importStrategy": "useBundled",
- "python.analysis.extraPaths": ["/project/libs/live-labs"],
- // file explorer configuration
- "files.exclude": {
- "**/.git": true,
- "**/.svn": true,
- "**/.hg": true,
+ // file explorer configuration
+ "files.exclude": {
+ ".git": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
@@ -19,64 +15,70 @@
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ipynb_checkpoints": true,
- "**/.terraform": true,
- },
-
+ ".project": true,
+ ".vscode": true,
+ ".github": true,
+ "**/.ruff_cache": true,
+ },
- // global editor settings
- "files.eol": "\n",
- "editor.tabSize": 4,
- "editor.insertSpaces": true,
- "files.insertFinalNewline": true,
- // remove this line to automatically forward ports
- // in general, workbench will manage this already
- "remote.autoForwardPorts": false,
+ // global editor settings
+ "files.eol": "\n",
+ "editor.tabSize": 4,
+ "editor.insertSpaces": true,
+ "files.insertFinalNewline": true,
+ "remote.autoForwardPorts": false,
-
- // bash scripting configuration
- "[shellscript]": {
+ // bash scripting configuration
+ "[shellscript]": {
"editor.tabSize": 4,
- "editor.insertSpaces": false,
- },
-
+ "editor.insertSpaces": false
+ },
- // css style sheet configuration
- "[css]": {
+ // css style sheet configuration
+ "[css]": {
"editor.suggest.insertMode": "replace",
"editor.tabSize": 2
- },
+ },
- // js configuration
- "[javascript]": {
+ // js configuration
+ "[javascript]": {
"editor.maxTokenizationLineLength": 2500,
- "editor.tabSize": 2,
- },
+ "editor.tabSize": 2
+ },
+
+ // Python environment configuration
+ "python.terminal.activateEnvironment": true,
+ // "python.defaultInterpreterPath": "/usr/bin/python3",
- // Python environment configuration
- "python.terminal.activateEnvironment": true,
- "python.defaultInterpreterPath": ".venv/bin/python",
- "isort.args":["--profile", "black"],
- "isort.check": true,
- "[python]": {
- "editor.defaultFormatter": "ms-python.black-formatter",
+ // Ruff as formatter
+ "[python]": {
+ "editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
- "source.organizeImports": "explicit"
+ "source.organizeImports": "explicit",
+ "source.fixAll": "explicit"
},
- // Comment out this settings to disable auto-formatting
"editor.formatOnSave": true
- },
+ },
+ "ruff.enable": true,
+ "ruff.exclude": [],
- "black-formatter.args": [
- "--line-length",
- "120"
- ],
- "pylint.severity": {
- "refactor": "Information",
- },
- "explorer.fileNesting.enabled": true,
- "explorer.fileNesting.patterns": {
+ "mypy-type-checker.ignorePatterns": ["**/answers/*"],
+
+ // Virtual environment specific
+ "python.analysis.extraPaths": ["/project/libs/live-labs"],
+ "python.defaultInterpreterPath": "/opt/live-labs/bin/python",
+ "ruff.configuration": "../../pyproject.toml",
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.patterns": {
"*.py": "${capture}.*.yaml, ${capture}_tests.py"
- }
+ },
+ "python.analysis.exclude": [
+ "answers/**",
+ ],
+ "workbench.colorCustomizations": {
+ "titleBar.activeBackground": "#2fe274"
+ },
+ "window.title": "code/tutorial_app ${dirty}${activeEditorShort}${separator}${rootName}${separator}${profileName}${separator}${appName}"
}
}
diff --git a/code/upload-pdfs.ipynb b/code/upload-pdfs.ipynb
index 0c36ef1..5b6980c 100644
--- a/code/upload-pdfs.ipynb
+++ b/code/upload-pdfs.ipynb
@@ -41,9 +41,7 @@
"cell_type": "code",
"execution_count": null,
"id": "114728f1-6ee9-4851-9042-32248fa39318",
- "metadata": {
- "scrolled": true
- },
+ "metadata": {},
"outputs": [],
"source": [
"!unzip -n ../data/corp-comms-dataset.zip -d ../data/"
diff --git a/docs/.puppeteer.json b/docs/.puppeteer.json
deleted file mode 100644
index 2c60f5e..0000000
--- a/docs/.puppeteer.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "args": ["--no-sandbox"] }
diff --git a/docs/0_0_quick_start.md b/docs/0_0_quick_start.md
deleted file mode 100644
index 95ad18d..0000000
--- a/docs/0_0_quick_start.md
+++ /dev/null
@@ -1 +0,0 @@
-# Quick-start
diff --git a/docs/0_1_personal_key.md b/docs/0_1_personal_key.md
deleted file mode 100644
index b9e9706..0000000
--- a/docs/0_1_personal_key.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## Generate your NGC Personal Key
-
-To allow AI Workbench to access NVIDIAβs cloud resources, youβll need to provide it with a Personal Key. These keys begin with `nvapi-`.
-
-
-
-Expand this section for instructions for creating this key.
-
-
-1. Go to the [NGC Personal Key Manager](https://org.ngc.nvidia.com/setup/personal-keys). If you are prompted to, then register for a new account and sign in.
-
- > **HINT** You can find this tool by logging into [ngc.nvidia.com](https://ngc.nvidia.com), expanding your profile menu on the top right, selecting *Setup*, and then selecting *Generate Personal Key*.
-
-1. Select *Generate Personal Key*.
-
- 
-
-1. Enter any value as the Key name, an expiration of 12 months is fine, and select all the services. Press *Generate Personal Key* when you are finished.
-
- 
-
-1. Save your personal key for later. Workbench will need it and there is no way to retrieve it later. If the key is lost, a new one must be created. Protect this key as if it were a password.
-
- 
-
-
diff --git a/docs/0_2_docker_auth.md b/docs/0_2_docker_auth.md
deleted file mode 100644
index 89e8671..0000000
--- a/docs/0_2_docker_auth.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Authenticate with Docker
-
-Workbench will use your system's Docker client to pull NVIDIA NIM containers, so before continuing, make sure to follow these steps to authenticate your Docker client with your NGC Personal Key.
-
-1. Run the following Docker login command
-
- ```bash
- docker login nvcr.io
- ```
-
-1. When prompted for your credentials, use the following values:
-
- - Username: `$oauthtoken`
- - Password: Use your NGC Personal key beggining with `nv-api`
diff --git a/docs/0_3_0_install_nvwb.md b/docs/0_3_0_install_nvwb.md
deleted file mode 100644
index 7cf5cc0..0000000
--- a/docs/0_3_0_install_nvwb.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Install AI Workbench
-
-This project is designed to be used with [NVIDIA AI Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/). While this is not a requirement, running this demo without AI Workbench will require manual work as the pre-configured automation and integrations may not be available.
-
-This quick start guide will assume a remote lab machine is being used for development and the local machine is a thin-client for remotely accessing the development machine. This allows for compute resources to stay centrally located and for developers to be more portable. Note, the remote lab machine must run Ubuntu, but the local client can run Windows, MacOS, or Ubuntu. To install this project local only, simply skip the remote install.
-
-```mermaid
-flowchart LR
- local
- subgraph lab environment
- remote-lab-machine
- end
-
- local <-.ssh.-> remote-lab-machine
-```
-
-### Client Machine Install
-
-Ubuntu is required if the local client will also be used for developent. When using a remote lab machine, this can be Windows, MacOS, or Ubuntu.
diff --git a/docs/0_3_1_windows.md b/docs/0_3_1_windows.md
deleted file mode 100644
index bda2f55..0000000
--- a/docs/0_3_1_windows.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-Expand this section for a Windows install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/windows.html).
-
-1. Install Prerequisite Software
- 1. If this machine has an NVIDIA GPU, ensure the GPU drivers are installed. It is recommended to use the [GeForce Experience](https://www.nvidia.com/en-us/geforce/geforce-experience/) tooling to manage the GPU drivers.
- 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) for local container support. Please be mindful of Docker Desktop's licensing for enterprise use. [Rancher Desktop](https://rancherdesktop.io/) may be a viable alternative.
- 1. *[OPTIONAL]* If Visual Studio Code integration is desired, install [Visual Studio Code](https://code.visualstudio.com/).
-
-1. Download the [NVIDIA AI Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/) installer and execute it. Authorize Windows to allow the installer to make changes.
-
-1. Follow the instructions in the installation wizard. If you need to install WSL2, authorize Windows to make the changes and reboot local machine when requested. When the system restarts, the NVIDIA AI Workbench installer should automatically resume.
-
-1. Select Docker as your container runtime.
-
-1. Log into your GitHub Account by using the *Sign in through GitHub.com* option.
-
-1. Enter your git author information if requested.
-
-
diff --git a/docs/0_3_2_macos.md b/docs/0_3_2_macos.md
deleted file mode 100644
index 5f552cf..0000000
--- a/docs/0_3_2_macos.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-Expand this section for a MacOS install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/macos.html).
-
-1. Install Prerequisite Software
- 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) for local container support. Please be mindful of Docker Desktop's licensing for enterprise use. [Rancher Desktop](https://rancherdesktop.io/) may be a viable alternative.
- 1. *[OPTIONAL]* If Visual Studio Code integration is desired, install [Visual Studio Code](https://code.visualstudio.com/). When using VSCode on a Mac, an a[dditional step must be performed](https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line) to install the VSCode CLI interface used by Workbench.
-
-1. Download the [NVIDIA AI Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/) disk image (*.dmg* file) and open it.
-
-1. Drag AI Workbench into the Applications folder and run *NVIDIA AI Workbench* from the application launcher.
- 
-
-1. Select Docker as your container runtime.
-
-1. Log into your GitHub Account by using the *Sign in through GitHub.com* option.
-
-1. Enter your git author information if requested.
-
-
diff --git a/docs/0_3_3_ubuntu.md b/docs/0_3_3_ubuntu.md
deleted file mode 100644
index a7dc069..0000000
--- a/docs/0_3_3_ubuntu.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-Expand this section for an Ubuntu install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/ubuntu-local.html). Run this installation as the user who will be user Workbench. Do not run these steps as `root`.
-
-1. Install Prerequisite Software
- 1. *[OPTIONAL]* If Visual Studio Code integration is desired, install [Visual Studio Code](https://code.visualstudio.com/).
-
-1. Download the [NVIDIA AI Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/) installer, make it executable, and then run it. You can make the file executable with the following command:
-
- ```bash
- chmod +x NVIDIA-AI-Workbench-*.AppImage
- ```
-
-1. AI Workbench will install the NVIDIA drivers for you (if needed). You will need to reboot your local machine after the drivers are installed and then restart the AI Workbench installation by double-clicking the NVIDIA AI Workbench icon on your desktop.
-
-1. Select Docker as your container runtime.
-
-1. Log into your GitHub Account by using the *Sign in through GitHub.com* option.
-
-1. Enter your git author information if requested.
-
-
diff --git a/docs/0_3_4_remote_ubuntu.md b/docs/0_3_4_remote_ubuntu.md
deleted file mode 100644
index 4b687ad..0000000
--- a/docs/0_3_4_remote_ubuntu.md
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-### Remote Machine Install
-
-Only Ubuntu is supported for remote machines.
-
-
-
-Expand this section for a remote Ubuntu install.
-
-
-For full instructions, see the [NVIDIA AI Workbench User Guide](https://docs.nvidia.com/ai-workbench/user-guide/latest/installation/ubuntu-remote.html). Run this installation as the user who will be using Workbench. Do not run these steps as `root`.
-
-1. Ensure SSH Key based authentication is enabled from the local machine to the remote machine. If this is not currently enabled, the following commands will enable this is most situations. Change `REMOTE_USER` and `REMOTE-MACHINE` to reflect your remote address.
-
- - From a Windows local client, use the following PowerShell:
- ```powershell
- ssh-keygen -f "C:\Users\local-user\.ssh\id_rsa" -t rsa -N '""'
- type $env:USERPROFILE\.ssh\id_rsa.pub | ssh REMOTE_USER@REMOTE-MACHINE "cat >> .ssh/authorized_keys"
- ```
- - From a MacOS or Linux local client, use the following shell:
- ```bash
- if [ ! -e ~/.ssh/id_rsa ]; then ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ""; fi
- ssh-copy-id REMOTE_USER@REMOTE-MACHINE
- ```
-
-1. SSH into the remote host. Then, use the following commands to download and execute the NVIDIA AI Workbench Installer.
-
- ```bash
- mkdir -p $HOME/.nvwb/bin && \
- curl -L https://workbench.download.nvidia.com/stable/workbench-cli/$(curl -L -s https://workbench.download.nvidia.com/stable/workbench-cli/LATEST)/nvwb-cli-$(uname)-$(uname -m) --output $HOME/.nvwb/bin/nvwb-cli && \
- chmod +x $HOME/.nvwb/bin/nvwb-cli && \
- sudo -E $HOME/.nvwb/bin/nvwb-cli install
- ```
-
-1. AI Workbench will install the NVIDIA drivers for you (if needed). You will need to reboot your remote machine after the drivers are installed and then restart the AI Workbench installation by re-running the commands in the previous step.
-
-1. Select Docker as your container runtime.
-
-1. Log into your GitHub Account by using the *Sign in through GitHub.com* option.
-
-1. Enter your git author information if requested.
-
-1. Once the remote installation is complete, the Remote Location can be added to the local AI Workbench instance. Open the AI Workbench application, click *Add Remote Location*, and then enter the required information. When finished, click *Add Location*.
-
- - *Location Name: * Any short name for this new location
- - *Description: * Any brief metadata for this location.
- - *Hostname or IP Address: * The hostname or address used to remotely SSH. If step 1 was followed, this should be the same as `REMOTE-MACHINE`.
- - *SSH Port: * Usually left blank. If a nonstandard SSH port is used, it can be configured here.
- - *SSH Username: * The username used for making an SSH connection. If step 1 was followed, this should be the same as `REMOTE_USER`.
- - *SSH Key File: * The path to the private key for making SSH connections. If step 1 was followed, this should be: `/home/USER/.ssh/id_rsa`.
- - *Workbench Directory: * Usually left blank. This is where Workbench will remotely save state.
-
-
-
diff --git a/docs/0_4_0_download.md b/docs/0_4_0_download.md
deleted file mode 100644
index d93a7c4..0000000
--- a/docs/0_4_0_download.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Download this project
-
-There are two ways to download this project for local use: Cloning and Forking.
-
-Cloning this repository is the recommended way to start. This will not allow for local modifications, but is the fastest to get started. This also allows for the easiest way to pull updates.
-
-Forking this repository is recommended for development as changes will be able to be saved. However, to get updates, the fork maintainer will have to regularly pull from the upstream repo. To work from a fork, follow [GitHub's instructions](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and then reference the URL to your personal fork in the rest of this section.
-
-
-
-Expand this section for a details on downloading this project.
-
-
-1. Open the local NVIDIA AI Workbench window. From the list of locations displayed, select either the remote one you just set up, or local if you're going to work locally.
-
- 
-
-1. Once inside the location, select *Clone Project*.
-
- 
-
-1. In the 'Clone Project' pop up window, set the Repository URL to `https://github.com/NVIDIA/nim-anywhere.git`. You can leave the Path as the default of `/home/REMOTE_USER/nvidia-workbench/nim-anywhere.git`. Click *Clone*.`
-
- 
-
-1. You will be redirected to the new projectβs page. Workbench will automatically bootstrap the development environment. You can view real-time progress by expanding the Output from the bottom of the window.
-
- 
-
-
-
diff --git a/docs/0_4_1_configure.md b/docs/0_4_1_configure.md
deleted file mode 100644
index f7dba40..0000000
--- a/docs/0_4_1_configure.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Configure this project
-The project must be configured to use your NGC personal key.
-
-
-
-Expand this section for a details on configuring this project.
-
-
-1. Before running for the first time, your NGC personal key must be configured in Workbench. This is done using the *Environment* tab from the left-hand panel.
-
- 
-
-1. Scroll down to the **Secrets** section and find the *NGC_API_KEY* entry. Press *Configure* and provide the personal key for NGC that was generated earlier.
-
-
diff --git a/docs/0_5_start.md b/docs/0_5_start.md
deleted file mode 100644
index b52cc71..0000000
--- a/docs/0_5_start.md
+++ /dev/null
@@ -1,51 +0,0 @@
-## Start This Project
-
-Even the most basic of LLM Chains depend on a few additional microservices. These can be ignored during development for in-memory alternatives, but then code changes are required to go to production. Thankfully, Workbench manages those additional microservices for development environments.
-
-
-
-Expand this section for details on starting the demo application.
-
-
-> **HINT:** For each application, the debug output can be monitored in the UI by clicking the Output link in the lower left corner, selecting the dropdown menu, and choosing the application of interest (or **Compose** for applications started via compose).
-
-Since you can either pull NIMs and run them locally, or utilize the endpoints from *ai.nvidia.com* you can run this project with *or* without GPUs.
-
-1. The applications bundled in this workspace can be controlled by navigating to two tabs:
-
- - **Environment** > **Compose**
- - **Environment** > **Applications**
-
-1. First, navigate to the **Environment** > **Compose** tab. If you're not working in an environment with GPUs, you can just click **Start** to run the project using a lightweight deployment. This default configuration will run the following containers:
-
- - *Milvus Vector DB*: An unstructured knowledge base
-
- - *Redis*: Used to store conversation histories
-
-1. If you have access to GPU resources and want to run any NIMs locally, use the dropdown menu under **Compose** and select which set of NIMs you want to run locally. Note that you *must* have at least 1 available GPU per NIM you plan to run locally. Below is an outline of the available configurations:
-
- - Local LLM (min 1 GPU required)
- - The first time the LLM NIM is started, it will take some time to download the image and the optimized models.
- - During a long start, to confirm the LLM NIM is starting, the progress can be observed by viewing the logs by using the *Output* pane on the bottom left of the UI.
-
- - If the logs indicate an authentication error, that means the provided *NGC_API_KEY* does not have access to the NIMs. Please verify it was generated correctly and in an NGC organization that has NVIDIA AI Enterprise support or trial.
-
- - If the logs appear to be stuck on `..........: Pull complete`. `..........: Verifying complete`, or `..........: Download complete`; this is all normal output from Docker that the various layers of the container image have been downloaded.
-
- - Any other failures here need to be addressed.
- - Local LLM + Embedding (min 2 GPUs required)
-
- - Local LLM + Embedding + Reranking (min 3 GPUs required)
-
-
- > **NOTE:**
- > - Each profile will also run *Milvus Vector DB* and *Redis*
- > - Due to the nature of Docker Compose profiles, the UI will let you select multiple profiles at the same time. In the context of this project, selecting multiple profiles does not make sense. It will not cause any errors, however we recommend only selecting one profile at a time for simplicity.
-
-1. Once the compose services have been started, navigate to the **Environment** > **Applications** tab. Now, the *Chain Server* can safely be started. This contains the custom LangChain code for performing our reasoning chain. By default, it will use the local Milvus and Redis, but use *ai.nvidia.com* for LLM, Embedding, and Reranking model inferencing.
-
-1. Once the *Chain Server* is up, the *Chat Frontend* can be started. Starting the interface will automatically open it in a browser window. If you are running any local NIMs, you can edit the config to connect to them via the *Chat Frontend*
-
- 
-
-
diff --git a/docs/0_6_knowledgebase.md b/docs/0_6_knowledgebase.md
deleted file mode 100644
index 8b0edf9..0000000
--- a/docs/0_6_knowledgebase.md
+++ /dev/null
@@ -1,9 +0,0 @@
-## Populating the Knowledge Base
-
-To get started developing demos, a sample dataset is provided along with a Jupyter Notebook showing how data is ingested into a Vector Database.
-
- 1. To import PDF documentation into the vector Database, open Jupyter using the app launcher in AI Workbench.
-
- 1. Use the Jupyter Notebook at `code/upload-pdfs.ipynb` to ingest the default dataset. If using the default dataset, no changes are necessary.
-
- 1. If using a custom dataset, upload it to the `data/` directory in Jupyter and modify the provided notebook as necessary.
diff --git a/docs/1_develop.md b/docs/1_develop.md
deleted file mode 100644
index c21241e..0000000
--- a/docs/1_develop.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Developing Your Own Applications
-
-This project contains applications for a few demo services as well as integrations with external services. These are all orchestrated by [NVIDIA AI Workbench](https://www.nvidia.com/en-us/deep-learning-ai/solutions/data-science/workbench/).
-
-The demo services are all in the `code` folder. The root level of the code folder has a few interactive notebooks meant for technical deep dives. The Chain Server is a sample application utilizing NIMs with LangChain. (Note that the Chain Server here gives you the option to experiment with and without RAG). The Chat Frontend folder contains an interactive UI server for exercising the chain server. Finally, sample notebooks are provided in the Evaluation directory to demonstrate retrieval scoring and validation.
-
-``` mermaid
-mindmap
- root((AI Workbench))
- Demo Services
- Chain Server LangChain + NIMs
- Frontend Interactive Demo UI
- Evaluation Validate the results
- Notebooks Advanced usage
-
- Integrations
- RedisConversation History
- MilvusVector Database
- LLM NIMOptimized LLMs
-```
diff --git a/docs/2_configuration.md.py b/docs/2_configuration.md.py
deleted file mode 100755
index 40f26f7..0000000
--- a/docs/2_configuration.md.py
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python3
-
-import jinja2
-from chain_server import configuration
-from frontend import configuration as fe_configuration
-
-
-def resolve_child(schema, pinfo):
- ref = pinfo.get("$ref", "")
- if ref.startswith("#/$defs"):
- child = ref.split("/")[-1]
- return schema["$defs"][child]
- return None
-
-
-def to_yaml(schema, level=0, env_var_prefixes=[("APP_", "__")]):
- indent = " " * 4 * level
- out = ""
-
- if schema["type"] == "object":
- for prop_name, prop in schema["properties"].items():
- prop_type = prop.get("anyOf", [{"type": prop.get("type")}])
- prop_desc = prop.get("description", "")
- prop_child = resolve_child(schema, prop)
- prop_default = "" if prop_child else (prop.get("default") or "~")
-
- # print the property description
- if prop_desc:
- out += f"{indent}# {prop_desc}\n"
-
- # print the property environment variables
- env_vars = prop.get("extra_env_vars", []) + [
- f"{prefix[0]}{prop_name.upper()}" for prefix in env_var_prefixes
- ]
- if not prop_child and env_vars:
- out += f"{indent}# ENV Variables: "
- out += ", ".join(env_vars)
- out += "\n"
-
- # print variable type
- if prop_type[0].get("type"):
- out += f"{indent}# Type: "
- out += ", ".join([t["type"] for t in prop_type])
- out += "\n"
-
- # print out the property
- out += f"{indent}{prop_name}: {prop_default}\n"
-
- # if the property references a child, print the child
- if prop_child:
- new_env_var_prefixes = [
- (f"{prefix[0]}{prop_name.upper()}{prefix[1]}", prefix[1]) for prefix in env_var_prefixes
- ]
- out += to_yaml(prop_child, level=level + 1, env_var_prefixes=new_env_var_prefixes)
-
- out += "\n"
-
- return out
-
-
-environment = jinja2.Environment(loader=jinja2.BaseLoader, autoescape=True)
-environment.filters["to_yaml"] = to_yaml
-
-doc_page = environment.from_string(
- """
-
-# {{docstring}}
-
-## Chain Server config schema
-
-```yaml
-{{ cs_schema | to_yaml }}
-```
-
-## Chat Frontend config schema
-
-The chat frontend has a few configuration options as well. They can be set in the same manner as the chain server.
-
-```yaml
-{{ fe_schema | to_yaml }}
-```
-
-"""
-)
-
-env_var_prefixes = [
- (source.prefix, source.nested_separator)
- for source in configuration.config.CONFIG_SOURCES
- if hasattr(source, "prefix")
-]
-docs = doc_page.render(
- docstring=configuration.__doc__,
- cs_schema=configuration.config.model_json_schema(),
- fe_schema=fe_configuration.config.model_json_schema(),
-)
-print(docs)
diff --git a/docs/3.0_contributing.md b/docs/3.0_contributing.md
deleted file mode 100644
index 37ed02a..0000000
--- a/docs/3.0_contributing.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Contributing
-
-All feedback and contributions to this project are welcome. When making changes to this project, either for personal use or for contributing, it is recommended to work on a fork on this project. Once the changes have been completed on the fork, a Merge Request should be opened.
-
-## Code Style
-
-This project has been configured with Linters that have been tuned to help the code remain consistent while not being overly burdensome. We use the following Linters:
-
-- Bandit is used for security scanning
-- Pylint is used for Python Syntax Linting
-- MyPy is used for type hint linting
-- Black is configured for code styling
-- A custom check is run to ensure Jupyter Notebooks do not have any output
-- Another custom check is run to ensure the README.md file is up to date
-
-The embedded VSCode environment is configured to run the linting and checking in realtime.
-
-To manually run the linting that is done by the CI pipelines, execute `/project/code/tools/lint.sh`. Individual tests can be run be specifying them by name: `/project code/tools/lint.sh [deps|pylint|mypy|black|docs|fix]`. Running the lint tool in fix mode will automatically correct what it can by running Black, updating the README, and clearing the cell output on all Jupyter Notebooks.
diff --git a/docs/3.1_frontend.md b/docs/3.1_frontend.md
deleted file mode 100644
index 9d15d8a..0000000
--- a/docs/3.1_frontend.md
+++ /dev/null
@@ -1,107 +0,0 @@
-## Updating the frontend
-
-The frontend has been designed in an effort to minimize the required HTML and Javascript development. A branded and styled Application Shell is provided that has been created with vanilla HTML, Javascript, and CSS. It is designed to be easy to customize, but it should never be required. The interactive components of the frontend are all created in Gradio and mounted in the app shell using iframes.
-
-Along the top of the app shell is a menu listing the available views. Each view may have its own layout consisting of one or a few pages.
-
-### Creating a new page
-
-Pages contain the interactive components for a demo. The code for the pages is in the `code/frontend/pages` directory. To create a new page:
-
-1. Create a new folder in the pages directory
-1. Create an `__init__.py` file in the new directory that uses Gradio to define the UI. The Gradio Blocks layout should be defined in a variable called `page`.
-1. It is recommended that any CSS and JS files needed for this view be saved in the same directory. See the `chat` page for an example.
-1. Open the `code/frontend/pages/__init__.py` file, import the new page, and add the new page to the `__all__` list.
-
-> **NOTE:** Creating a new page will not add it to the frontend. It must be added to a view to appear on the Frontend.
-
-### Adding a view
-
-View consist of one or a few pages and should function independently of each other. Views are all defined in the `code/frontend/server.py` module. All declared views will automatically be added to the Frontend's menu bar and made available in the UI.
-
-To define a new view, modify the list named `views`. This is a list of `View` objects. The order of the objects will define their order in the Frontend menu. The first defined view will be the default.
-
-View objects describe the view name and layout. They can be declared as follow:
-```python
-my_view = frontend.view.View(
- name="My New View", # the name in the menu
- left=frontend.pages.sample_page, # the page to show on the left
- right=frontend.pages.another_page, # the page to show on the right
-)
-```
-
-All of the page declarations, `View.left` or `View.right`, are optional. If they are not declared, then the associated iframes in the web layout will be hidden. The other iframes will expand to fill the gaps. The following diagrams show the various layouts.
-
- - All pages are defined
-
-```mermaid
-block-beta
- columns 1
- menu["menu bar"]
- block
- columns 2
- left right
- end
-```
-
- - Only left is defined
-
-```mermaid
-block-beta
- columns 1
- menu["menu bar"]
- block
- columns 1
- left:1
- end
-```
-
-### Frontend branding
-
-The frontend contains a few branded assets that can be customized for different use cases.
-
-#### Logo
-
-The frontend contains a logo on the top left of the page. To modify the logo, an SVG of the desired logo is required. The app shell can then be easily modified to use the new SVG by modifying the `code/frontend/_assets/index.html` file. There is a single `div` with an ID of `logo`. This box contains a single SVG. Update this to the desired SVG definition.
-
-```html
-