A Python-based containerized service that exposes an API for interacting with Google's Gemini Live API with tool use support.
- FastAPI-based REST and WebSocket endpoints for real-time streaming
- Tool use support with manual tool declarations
- WebSocket streaming for real-time responses
- Docker containerization for easy deployment
- Built-in tool management with registration and storage
- Health checks and logging
- Frontend API Documentation - Complete guide for frontend developers
- Interactive Demo - Open in browser to test the API
- JavaScript SDK - Ready-to-use client library
.
├── main.py # FastAPI application with endpoints
├── gemini_service.py # Gemini Live API integration
├── tool_manager.py # Tool declaration management
├── models.py # Pydantic models for requests/responses
├── config.py # Configuration management
├── live.py # Original example from Gemini docs
├── requirements.txt # Python dependencies
├── Dockerfile # Container definition
├── docker-compose.yml # Docker Compose configuration
├── .env.example # Environment variables template
├── demo.html # Interactive browser demo
├── gemini-client.js # JavaScript SDK
├── gemini-client.d.ts # TypeScript type definitions
├── API_FRONTEND.md # Frontend API documentation
└── README.md # This file
- Docker and Docker Compose (for containerized deployment)
- Python 3.11+ (for local development)
- Google API Key with Gemini API access
Get your API key from Google AI Studio
Option A: Dynamic Injection (Easiest)
Start the service first, then inject your key via API:
# Start service
docker-compose up -d
# Set key dynamically (no restart needed!)
curl -X POST http://localhost:8000/config/api-key \
-H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY_HERE"}'See QUICK_API_KEY_SETUP.md for details.
Option B: Environment File (Traditional)
Copy the example environment file and add your API key:
cp .env.example .envEdit .env and add your API key:
GOOGLE_API_KEY=your_actual_api_key_here
# Build and start the service
docker-compose up --build
# Or run in detached mode
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the service
docker-compose downThe service will be available at http://localhost:8000
# Install dependencies
pip install -r requirements.txt
# Run the service
python main.py
# Or use uvicorn directly
uvicorn main:app --reload --host 0.0.0.0 --port 8000GET /healthCheck if the service is running:
curl http://localhost:8000/healthPOST /toolsRegister tool declarations:
curl -X POST http://localhost:8000/tools \
-H "Content-Type: application/json" \
-d '[
{
"function_declarations": [
{
"name": "turn_on_the_lights",
"description": "Turn on the lights in a room",
"parameters": {
"type": "object",
"properties": {
"room": {
"type": "string",
"description": "The room to turn lights on in"
}
},
"required": ["room"]
}
}
]
}
]'GET /toolsRetrieve current tool declarations:
curl http://localhost:8000/toolsDELETE /toolsRemove all tool declarations:
curl -X DELETE http://localhost:8000/toolsConnect to the WebSocket endpoint for real-time streaming:
ws://localhost:8000/ws/chatJavaScript Example:
const ws = new WebSocket('ws://localhost:8000/ws/chat');
ws.onopen = () => {
// Send a message
ws.send(JSON.stringify({
message: "Turn on the lights in the living room",
tools: [ // Optional: specify tools for this session
{
"function_declarations": [
{
"name": "turn_on_the_lights",
"description": "Turn on the lights"
}
]
}
]
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'text') {
console.log('Text:', data.text);
} else if (data.type === 'tool_call') {
console.log('Tool calls:', data.tool_calls);
} else if (data.type === 'error') {
console.error('Error:', data.error);
} else if (data.type === 'done') {
console.log('Response complete');
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};Python Example:
import asyncio
import websockets
import json
async def chat():
uri = "ws://localhost:8000/ws/chat"
async with websockets.connect(uri) as websocket:
# Send message
await websocket.send(json.dumps({
"message": "Turn on the lights in the bedroom"
}))
# Receive responses
while True:
response = await websocket.recv()
data = json.loads(response)
if data.get('type') == 'done':
break
elif data.get('type') == 'text':
print(f"Text: {data['text']}")
elif data.get('type') == 'tool_call':
print(f"Tool calls: {data['tool_calls']}")
elif data.get('type') == 'error':
print(f"Error: {data['error']}")
asyncio.run(chat())POST /chatNon-streaming endpoint that returns the complete response:
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{
"message": "Turn on the lights in the kitchen",
"tools": [
{
"function_declarations": [
{
"name": "turn_on_the_lights",
"description": "Turn on the lights"
}
]
}
]
}'Tools follow the Gemini API function calling format:
[
{
"function_declarations": [
{
"name": "function_name",
"description": "What the function does",
"parameters": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "Parameter description"
}
},
"required": ["param1"]
}
}
]
}
]For tools without parameters:
[
{
"function_declarations": [
{
"name": "turn_on_the_lights"
}
]
}
]# Install dev dependencies
pip install pytest pytest-asyncio httpx
# Run tests (you'll need to create tests)
pytestmain.py: FastAPI application with all endpointsgemini_service.py: Handles Gemini Live API connections and streamingtool_manager.py: Tool registration and management utilitiesmodels.py: Pydantic models for request/response validationconfig.py: Configuration loaded from environment variables
You can implement custom tool handlers by modifying gemini_service.py:
async def my_tool_handler(tool_name: str, tool_args: dict):
if tool_name == "turn_on_the_lights":
# Your custom logic here
return {"result": "Lights turned on", "status": "success"}
return {"result": "ok"}docker build -t gemini-live-api .docker run -d \
-p 8000:8000 \
-e GOOGLE_API_KEY=your_api_key \
--name gemini-live-api \
gemini-live-apiThe docker-compose.yml includes:
- Automatic restarts
- Health checks
- Volume mounting for development
- Environment variable configuration
- Logging configuration
| Variable | Description | Default |
|---|---|---|
GOOGLE_API_KEY |
Google API key (required) | None |
GEMINI_MODEL |
Model to use | gemini-live-2.5-flash-preview |
RESPONSE_MODALITIES |
Response types | ["TEXT"] |
HOST |
Server host | 0.0.0.0 |
PORT |
Server port | 8000 |
If you get authentication errors:
- Verify your API key is correct in
.env - Check that your API key has Gemini API access enabled
- Ensure the
.envfile is being loaded (check logs)
If WebSocket connections fail:
- Check that port 8000 is not blocked by firewall
- Verify the service is running:
curl http://localhost:8000/health - Check logs:
docker-compose logs -f
If tools aren't being called:
- Verify tool declarations are registered:
curl http://localhost:8000/tools - Check that tool descriptions are clear and relevant to your prompt
- Review logs for any errors during tool execution
{
"function_declarations": [
{
"name": "control_device",
"description": "Control smart home devices",
"parameters": {
"type": "object",
"properties": {
"device": {"type": "string"},
"action": {"type": "string"},
"value": {"type": "number"}
}
}
}
]
}{
"function_declarations": [
{
"name": "get_weather",
"description": "Get current weather",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
}
}
}
]
}This project is provided as-is for educational and development purposes.
Feel free to submit issues and enhancement requests!