Skip to content
Open
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
1 change: 1 addition & 0 deletions flask_app/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEMINI_API_KEY="YOUR_API_KEY_HERE_IN_FLASK_APP_DOT_ENV"
51 changes: 51 additions & 0 deletions flask_app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from flask import Flask, render_template, send_from_directory # Added send_from_directory
import datetime # Ensure datetime is imported
from flask_app.mock_data import mock_logs # Import mock_logs
import os # Added os import
from dotenv import load_dotenv # Added dotenv import

load_dotenv() # Load environment variables from .env file

app = Flask(__name__)

# Load Gemini API Key
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
print(f"Gemini API Key Loaded: {'Yes' if GEMINI_API_KEY and GEMINI_API_KEY != 'YOUR_API_KEY_HERE_IN_FLASK_APP_DOT_ENV' else 'No - Please set in flask_app/.env'}")
if GEMINI_API_KEY == "YOUR_API_KEY_HERE_IN_FLASK_APP_DOT_ENV":
print("WARNING: Using placeholder API Key. Please replace it in flask_app/.env")


@app.route('/robots.txt')
def robots_txt():
return send_from_directory(app.static_folder, 'robots.txt')

@app.route('/')
def index():
initial_context = {
"logs": mock_logs,
"initial_connected": False,
"initial_muted": False,
"initial_supports_video": True,
"initial_enable_settings": True, # This will enable the settings button and dialog
"initial_webcam_streaming": False,
"initial_screen_streaming": False,

# Settings Dialog specific context
"initial_system_instruction": "You are a helpful AI assistant.",
"initial_function_declarations": [
{'name': 'get_weather', 'args': ['city', 'date'], 'description': 'Provides the weather forecast for a given city and date.'},
{'name': 'send_email', 'args': ['recipient', 'subject', 'body'], 'description': 'Sends an email.'}
],
"initial_response_modalities": ["audio", "text", "audio_and_text"],
"initial_voices": ["Puck", "BayMax", "GLaDOS", "HAL9000"],
"initial_selected_modality": "audio",
"initial_selected_voice": "Puck",

# Side Panel specific context
"initial_side_panel_open": True,
"current_log_filter": "none"
}
return render_template('index.html', **initial_context)

if __name__ == '__main__':
app.run(debug=True)
122 changes: 122 additions & 0 deletions flask_app/mock_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import datetime

# Helper functions to mimic the TypeScript behavior
def sound_logs(n):
return [
{"date": datetime.datetime.now(), "type": "server.audio", "message": f"buffer (11250)", "count": 1} # Added count for consistency, though not in original TS for this helper
for _ in range(n)
]

def realtime_logs(n):
return [
{"date": datetime.datetime.now(), "type": "client.realtimeInput", "message": "audio"}
for _ in range(n)
]

mock_logs = [
{
"date": datetime.datetime.now(),
"type": "client.open",
"message": "connected",
},
{"date": datetime.datetime.now(), "type": "receive", "message": "setupComplete"},
*realtime_logs(10),
*sound_logs(10), # In the TS, soundLogs don't have a count, but the example output in logger.html shows one. I'll add a count of 1 to each generated sound log for now.
{
"date": datetime.datetime.now(),
"type": "receive.content",
"message": {
"serverContent": {
"interrupted": True,
},
},
},
{
"date": datetime.datetime.now(),
"type": "receive.content",
"message": {
"serverContent": {
"turnComplete": True,
},
},
},
*realtime_logs(10),
*sound_logs(20), # Added count of 1 here as well
{
"date": datetime.datetime.now(),
"type": "receive.content",
"message": {
"serverContent": {
"modelTurn": {
"parts": [{"text": "Hey its text"}, {"text": "more"}],
},
},
},
},
{
"date": datetime.datetime.now(),
"type": "client.send",
"message": {
"turns": [
{
"text": "How much wood could a woodchuck chuck if a woodchuck could chuck wood",
},
{
"text": "more text",
},
],
"turnComplete": False,
},
},
{
"date": datetime.datetime.now(),
"type": "server.toolCall",
"message": {
"toolCall": {
"functionCalls": [
{
"id": "akadjlasdfla-askls",
"name": "take_photo",
"args": {},
},
{
"id": "akldjsjskldsj-102",
"name": "move_camera",
"args": {"x": 20, "y": 4},
},
],
},
},
},
{
"date": datetime.datetime.now(),
"type": "server.toolCallCancellation",
"message": {
"toolCallCancellation": {
"ids": ["akladfjadslfk", "adkafsdljfsdk"],
},
},
},
{
"date": datetime.datetime.now(),
"type": "client.toolResponse",
"message": {
"functionResponses": [
{
"response": {"success": True},
"id": "akslaj-10102",
},
],
},
},
{
"date": datetime.datetime.now(),
"type": "receive.serverContent",
"message": "interrupted",
},
{
"date": datetime.datetime.now(),
"type": "receive.serverContent",
"message": "turnComplete",
},
]
6 changes: 6 additions & 0 deletions flask_app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Flask
python-dotenv
pytest
flake8
pylint
black
48 changes: 48 additions & 0 deletions flask_app/static/css/audio-pulse.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.audioPulse {
display: flex;
width: 24px;
justify-content: space-evenly;
align-items: center;
transition: all 0.5s;
height: 4px; /* Moved from below as it's a property of .audioPulse */
transition: opacity 0.333s; /* This will be overridden by 'all 0.5s' but kept for fidelity to original SCSS structure */
}

.audioPulse > div {
background-color: var(--Neutral-30);
border-radius: 1000px;
width: 4px;
min-height: 4px;
/* border-radius: 1000px; Duplicate, removed */
transition: height 0.1s;
}

.audioPulse.hover > div {
animation: hover 1.4s infinite alternate ease-in-out;
}

.audioPulse.active {
opacity: 1;
}

.audioPulse.active > div {
background-color: var(--Neutral-80);
}

@keyframes hover {
from {
transform: translateY(0);
}
to {
transform: translateY(-3.5px);
}
}

@keyframes pulse {
from {
scale: 1 1;
}
to {
scale: 1.2 1.2;
}
}
Loading