Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions components/livekit/examples/grocery_assistant/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# IDF
**/sdkconfig
**/sdkconfig.old
**/build
**/managed_components
**/dependencies.lock
**/dist
6 changes: 6 additions & 0 deletions components/livekit/examples/grocery_assistant/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(COMPONENTS main) # Trim build
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(grocery_assistant)
4 changes: 4 additions & 0 deletions components/livekit/examples/grocery_assistant/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Grocery Assistant

Assistant for checking prices and answering product questions powered by Gemini Live. For products sold by weight, the
agent can read the weight on the scale attached to the client (ESP32) via RPC.
48 changes: 48 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Python bytecode and artifacts
__pycache__/
*.py[cod]
*.pyo
*.pyd
*.egg-info/
dist/
build/

# Virtual environments
.venv/
venv/

# Caches and test output
.cache/
.pytest_cache/
.ruff_cache/
coverage/

# Logs and temp files
*.log
*.gz
*.tgz
.tmp
.cache

# Environment variables
.env
.env.*

# VCS, editor, OS
.git
.gitignore
.gitattributes
.github/
.idea/
.vscode/
.DS_Store

# Project docs and misc
README.md
LICENSE

# Project tests
test/
tests/
eval/
evals/
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LIVEKIT_URL=
LIVEKIT_API_KEY=
LIVEKIT_API_SECRET=

GOOGLE_API_KEY=
12 changes: 12 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.env
.env.*
!.env.example
.DS_Store
__pycache__
.idea
KMS
.venv
.vscode
*.egg-info
.pytest_cache
.ruff_cache
69 changes: 69 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# syntax=docker/dockerfile:1

# Use the official UV Python base image with Python 3.13 on Debian Bookworm
# UV is a fast Python package manager that provides better performance than pip
# We use the slim variant to keep the image size smaller while still having essential tools
ARG PYTHON_VERSION=3.13
FROM ghcr.io/astral-sh/uv:python${PYTHON_VERSION}-bookworm-slim AS base

# Keeps Python from buffering stdout and stderr to avoid situations where
# the application crashes without emitting any logs due to buffering.
ENV PYTHONUNBUFFERED=1

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/app" \
--shell "/sbin/nologin" \
--uid "${UID}" \
appuser

# Install build dependencies required for Python packages with native extensions
# gcc: C compiler needed for building Python packages with C extensions
# python3-dev: Python development headers needed for compilation
# We clean up the apt cache after installation to keep the image size down
RUN apt-get update && apt-get install -y \
gcc \
g++ \
python3-dev \
&& rm -rf /var/lib/apt/lists/*

# Create a new directory for our application code
# And set it as the working directory
WORKDIR /app

# Copy just the dependency files first, for more efficient layer caching
COPY pyproject.toml uv.lock ./
RUN mkdir -p src

# Install Python dependencies using UV's lock file
# --locked ensures we use exact versions from uv.lock for reproducible builds
# This creates a virtual environment and installs all dependencies
# Ensure your uv.lock file is checked in for consistency across environments
RUN uv sync --locked

# Copy all remaining application files into the container
# This includes source code, configuration files, and dependency specifications
# (Excludes files specified in .dockerignore)
COPY . .

# Change ownership of all app files to the non-privileged user
# This ensures the application can read/write files as needed
RUN chown -R appuser:appuser /app

# Switch to the non-privileged user for all subsequent operations
# This improves security by not running as root
USER appuser

# Pre-download any ML models or files the agent needs
# This ensures the container is ready to run immediately without downloading
# dependencies at runtime, which improves startup time and reliability
RUN uv run src/agent.py download-files

# Run the application using UV
# UV will activate the virtual environment and run the agent.
# The "start" command tells the worker to connect to LiveKit and begin waiting for jobs.
CMD ["uv", "run", "src/agent.py", "start"]
21 changes: 21 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 LiveKit, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
75 changes: 75 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Grocery Assistant

Assistant for checking prices and answering product questions powered by Gemini Live. For products sold by weight, the
agent can read the weight on the scale attached to the client (ESP32) via RPC.

## Product Database

Known products the agent is allowed to answers questions about are defined in [_database.py_](./src/database.py).
This is provided for demonstration purposes; in a production agent, you would likely access products from a database.

## Dev Setup

Clone the repository and install dependencies to a virtual environment:

```console
cd agent-starter-python
uv sync
```

Sign up for [LiveKit Cloud](https://cloud.livekit.io/) then set up the environment by copying `.env.example` to `.env.local` and filling in the required keys:

- `LIVEKIT_URL`
- `LIVEKIT_API_KEY`
- `LIVEKIT_API_SECRET`
- `GOOGLE_API_KEY`

You can load the LiveKit environment automatically using the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup):

```bash
lk cloud auth
lk app env -w -d .env.local
```

## Run the agent

Before your first run, you must download certain models such as [Silero VAD](https://docs.livekit.io/agents/build/turns/vad/) and the [LiveKit turn detector](https://docs.livekit.io/agents/build/turns/turn-detector/):

```console
uv run python src/agent.py download-files
```

Next, run this command to speak to your agent directly in your terminal:

```console
uv run python src/agent.py console
```

To run the agent for use with a frontend or telephony, use the `dev` command:

```console
uv run python src/agent.py dev
```

In production, use the `start` command:

```console
uv run python src/agent.py start
```

## Frontend & Telephony

This agent is intended for use with the _grocery_assistant_ example for the ESP32 SDK since the agent makes an RCP call to read the attached scale. However,
it can be easily modified to work with another [frontend](https://docs.livekit.io/agents/start/frontend/) and/or [telephony](https://docs.livekit.io/agents/start/telephony/).

## Deploying to production

This project is production-ready and includes a working `Dockerfile`. To deploy it to LiveKit Cloud or another environment, see the [deploying to production](https://docs.livekit.io/agents/ops/deployment/) guide.

## Self-hosted LiveKit

You can also self-host LiveKit instead of using LiveKit Cloud. See the [self-hosting](https://docs.livekit.io/home/self-hosting/) guide for more information. If you choose to self-host, you'll need to also use [model plugins](https://docs.livekit.io/agents/models/#plugins) instead of LiveKit Inference and will need to remove the [LiveKit Cloud noise cancellation](https://docs.livekit.io/home/cloud/noise-cancellation/) plugin.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
37 changes: 37 additions & 0 deletions components/livekit/examples/grocery_assistant/agent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "grocery-assistant-agent"
version = "0.1.0"
description = "Assistant for checking prices and answering product questions."
requires-python = ">=3.9"

dependencies = [
"livekit-agents[google,silero,images]~=1.3.5",
"livekit-plugins-noise-cancellation~=0.2",
"pydantic>=2.11.10",
"python-dotenv",
]

[dependency-groups]
dev = ["ruff"]

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-dir]
"" = "src"

[tool.ruff]
line-length = 88
target-version = "py39"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "B", "A", "C4", "UP", "SIM", "RUF"]
ignore = ["E501"] # Line too long (handled by formatter)

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file makes the src directory a Python package
Loading