first commit

This commit is contained in:
Astra Clelia Bertelli
2025-07-28 15:38:34 +02:00
commit ab8d7540ac
16 changed files with 1776 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
name: Linting
on:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Set up Python
run: uv python install 3.12
- name: Run linter
shell: bash
run: uv run -- pre-commit run -a
+21
View File
@@ -0,0 +1,21 @@
name: CI Tests
on:
pull_request:
jobs:
testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Set up Python
run: uv python install
- name: Run Tests
run: uv run -- pytest tests/test_*.py
+22
View File
@@ -0,0 +1,22 @@
name: Typecheck
on:
pull_request:
jobs:
core-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Set up Python
run: uv python install
- name: Run Mypy
working-directory: src
run: uv run -- mypy gemini_live_demo
+9
View File
@@ -0,0 +1,9 @@
# python generated files
.venv
__pycache__/
.*_cache/
lib/
build/
# env variables
.env
+84
View File
@@ -0,0 +1,84 @@
---
default_language_version:
python: python3
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.11.8
hooks:
- id: ruff
args: [--exit-non-zero-on-fix, --fix]
- id: ruff-format
exclude: ".*poetry.lock|.*_static|.*uv.lock|.*ipynb|.*docs.*"
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.0.1
hooks:
- id: mypy
additional_dependencies:
[
"types-requests",
"types-Deprecated",
"types-redis",
"types-setuptools",
"types-PyYAML",
"types-protobuf==4.24.0.4",
]
args:
[
--namespace-packages,
--explicit-package-bases,
--disallow-untyped-defs,
--ignore-missing-imports,
--python-version=3.9,
]
entry: bash -c "export MYPYPATH=src/gemini_live_demo"
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.10.1
hooks:
- id: black-jupyter
name: black-docs-py
alias: black
files: ^(README.md|CONTRIBUTING.md)
# Using PEP 8's line length in docs prevents excess left/right scrolling
args: [--line-length=79]
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
- id: blacken-docs
name: black-docs-text
alias: black
types_or: [rst, markdown, tex]
additional_dependencies: [black==23.10.1]
# Using PEP 8's line length in docs prevents excess left/right scrolling
args: [--line-length=79]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
hooks:
- id: prettier
- repo: https://github.com/srstevenson/nb-clean
rev: 3.1.0
hooks:
- id: nb-clean
args: [--preserve-cell-outputs, --remove-empty-cells]
- repo: https://github.com/pappasam/toml-sort
rev: v0.23.1
hooks:
- id: toml-sort-fix
+1
View File
@@ -0,0 +1 @@
3.13
+48
View File
@@ -0,0 +1,48 @@
# Contributing to `gemini-live-demo`
Do you want to contribute to this project? Make sure to read this guidelines first :)
## Issue
**When to do it**:
- You found bugs but you don't know how to solve them or don't have time/will to do the solve
- You want new features but you don't know how to implement them or don't have time/will to do the implementation
> ⚠️ _Always check open and closed issues before you submit yours to avoid duplicates_
**How to do it**:
- Open an issue
- Give the issue a meaningful title (short but effective problem/feature request description)
- Describe the problem/feature request
## Traditional contribution
**When to do it**:
- You found bugs and corrected them
- You optimized/improved the code
- You added new features that you think could be useful to others
**How to do it**:
1. Fork this repository
2. Install `pre-commit` and make sure to have it within the Git Hooks for your fork:
```bash
pip install pre-commit
pre-commit install
```
3. Change the things you want, and make sure tests still pass or add new ones:
```bash
pytest tests/test_*.py
```
3. Commit your changes
4. Make sure your changes pass the pre-commit linting/type checking, if not modify them so that they pass
5. Submit pull request (make sure to provide a thorough description of the changes)
### Thanks for contributing!
+21
View File
@@ -0,0 +1,21 @@
The MIT License
Copyright (c) Jerry Liu
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.
+43
View File
@@ -0,0 +1,43 @@
# Gemini-Live Demo
This is a demo repository showcasing Gemini Live x LlamaIndex integration.
## Install and Launch
> [!IMPORTANT]
>
> _This is a [uv](https://docs.astral.sh/uv/) project, so make sure to have uv [installed](https://docs.astral.sh/uv/getting-started/installation/)_
Clone this repository locally:
```bash
git clone https://github.com/run-llama/gemini-live-demo
cd gemini-live-demo
```
And install the needed dependencies:
```bash
uv sync
```
Now create a `.env` file and add your `GOOGLE_API_KEY` there:
```bash
touch .env
echo GOOGLE_API_KEY=*** > .env
```
Launch the application with
```bash
uv run src/gemini_live_demo/main.py
```
## Contributing
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
## License
This project is licensed under the [MIT License](./LICENSE).
+23
View File
@@ -0,0 +1,23 @@
[project]
name = "gemini-demo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"llama-index-voice-agents-gemini-live",
"mypy>=1.17.0",
"pre-commit>=4.2.0",
"pytest>=8.4.1",
"python-dotenv>=1.1.1",
"ruff>=0.12.5"
]
[tool.mypy]
disable_error_code = ["import-not-found", "import-untyped"]
[tool.pytest.ini_options]
pythonpath = ["src"]
[tool.uv.sources]
llama-index-voice-agents-gemini-live = {git = "https://github.com/run-llama/llama_index", subdirectory = "llama-index-integrations/voice_agents/llama-index-voice-agents-gemini-live", branch = "main"}
View File
+31
View File
@@ -0,0 +1,31 @@
from llama_index.voice_agents.gemini_live import GeminiLiveVoiceAgent # type: ignore
from llama_index.core.tools import FunctionTool
from utils import get_weather, filter_events, filter_messages # type: ignore
from dotenv import load_dotenv
load_dotenv()
weather_tool = FunctionTool.from_defaults(
fn=get_weather,
name="get_weather",
description="Get the weather at a given location",
)
async def main():
conversation = GeminiLiveVoiceAgent(tools=[weather_tool])
await conversation.start()
if conversation._quitflag:
print("Events")
print(conversation.export_events(filter=filter_events))
print()
print("Messages")
print(conversation.export_messages(filter=filter_messages))
if __name__ == "__main__":
import asyncio
asyncio.run(main())
+42
View File
@@ -0,0 +1,42 @@
from llama_index.core.voice_agents import BaseVoiceAgentEvent
from llama_index.core.llms import ChatMessage, TextBlock
from typing import List
import random
import json
# use filter functions to export messages and events without your terminal being swamped by base64-encoded audio bytes :)
def filter_events(
events: List[BaseVoiceAgentEvent],
) -> List[BaseVoiceAgentEvent]:
evs = []
for event in events:
if "audio" not in event.type_t:
evs.append(event)
return evs
def filter_messages(messages: List[ChatMessage]) -> List[ChatMessage]:
msgs = []
for message in messages:
msg = ChatMessage(role=message.role, blocks=[])
for b in message.blocks:
if isinstance(b, TextBlock):
msg.blocks.append(b)
if len(msg.blocks) > 0:
msgs.append(msg)
return msgs
def get_weather(location: str) -> str:
"""Fetch weather data for a given location."""
return json.dumps(
{
"location": location,
"temperature_c": round(random.uniform(15, 30), 1),
"humidity_percent": random.randint(40, 90),
"wind_speed_kmh": round(random.uniform(5, 25), 1),
"precipitation_probability_percent": random.randint(0, 100),
},
indent=4,
)
View File
+69
View File
@@ -0,0 +1,69 @@
import pytest
import json
from typing import List, Optional
from src.gemini_live_demo.utils import get_weather, filter_events, filter_messages # type: ignore
from llama_index.core.llms import ChatMessage, AudioBlock, TextBlock
from llama_index.core.voice_agents import BaseVoiceAgentEvent
@pytest.fixture()
def messages() -> List[ChatMessage]:
return [
ChatMessage(role="user", blocks=[AudioBlock(audio=b"Hello")]),
ChatMessage(
role="assistant",
blocks=[AudioBlock(audio=b"Hello back"), TextBlock(text="hello back")],
),
ChatMessage(role="user", content="now what?"),
]
class Audio(BaseVoiceAgentEvent):
pass
class Text(BaseVoiceAgentEvent):
pass
@pytest.fixture()
def events() -> List[BaseVoiceAgentEvent]:
return [
Audio(type_t="audio"),
Text(type_t="text"),
Text(type_t="text"),
]
def is_serializable(s: str) -> tuple[bool, Optional[dict]]:
try:
d = json.loads(s)
return True, d
except json.JSONDecodeError:
return False, None
def test_weather_tool() -> None:
a = get_weather("San Francisco")
assert isinstance(a, str)
serializable, serialized = is_serializable(a)
assert serializable
assert serialized is not None
assert serialized["location"] == "San Francisco"
def test_filter_messages(messages: List[ChatMessage]):
filtered = filter_messages(messages)
assert isinstance(filtered, list)
assert isinstance(filtered[0], ChatMessage)
assert len(filtered) == 2
assert filtered[1].content == "now what?"
def test_filter_events(events: List[BaseVoiceAgentEvent]):
filtered = filter_events(events)
assert isinstance(filtered, list)
assert isinstance(filtered[0], BaseVoiceAgentEvent)
assert len(filtered) == 2
assert all(ev.type_t == "text" for ev in filtered)
Generated
+1342
View File
File diff suppressed because it is too large Load Diff