Spaces:
Build error
Build error
Deploying TechHub Prototype
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .dockerignore +7 -0
- .gitattributes +50 -0
- Dockerfile +33 -0
- referenceDocs/Get_started_LiveAPI.py +275 -0
- techhubprototypeucbackend/.env +1 -0
- techhubprototypeucbackend/.env.example +5 -0
- techhubprototypeucbackend/.pytest_cache/.gitignore +2 -0
- techhubprototypeucbackend/.pytest_cache/CACHEDIR.TAG +4 -0
- techhubprototypeucbackend/.pytest_cache/README.md +8 -0
- techhubprototypeucbackend/.pytest_cache/v/cache/lastfailed +1 -0
- techhubprototypeucbackend/.pytest_cache/v/cache/nodeids +4 -0
- techhubprototypeucbackend/__pycache__/main.cpython-312.pyc +0 -0
- techhubprototypeucbackend/main.py +190 -0
- techhubprototypeucbackend/requirements.txt +12 -0
- techhubprototypeucbackend/tests/__pycache__/test_main.cpython-312-pytest-8.4.2.pyc +0 -0
- techhubprototypeucbackend/tests/test_main.py +46 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/INSTALLER +1 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/LICENSE.txt +20 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/METADATA +72 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/RECORD +10 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/REQUESTED +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/WHEEL +5 -0
- techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/top_level.txt +1 -0
- techhubprototypeucbackend/venv/Lib/site-packages/__pycache__/py.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-312.pyc +3 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__init__.py +13 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc +0 -0
- techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc +0 -0
.dockerignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git
|
| 2 |
+
.env
|
| 3 |
+
__pycache__
|
| 4 |
+
node_modules
|
| 5 |
+
dist
|
| 6 |
+
venv
|
| 7 |
+
*.md
|
.gitattributes
CHANGED
|
@@ -33,3 +33,53 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
techhubprototypeucbackend/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
techhubprototypeucbackend/venv/Lib/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
techhubprototypeucbackend/venv/Lib/site-packages/charset_normalizer/md__mypyc.cp312-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
techhubprototypeucbackend/venv/Lib/site-packages/click/__pycache__/core.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
techhubprototypeucbackend/venv/Lib/site-packages/google/_upb/_message.pyd filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
techhubprototypeucbackend/venv/Lib/site-packages/google/genai/__pycache__/models.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
techhubprototypeucbackend/venv/Lib/site-packages/google/genai/__pycache__/types.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
techhubprototypeucbackend/venv/Lib/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
techhubprototypeucbackend/venv/Lib/site-packages/idna/__pycache__/idnadata.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
techhubprototypeucbackend/venv/Lib/site-packages/idna/__pycache__/uts46data.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/distlib/t64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pyasn1/type/__pycache__/univ.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pyaudio/_portaudio.cp312-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pydantic/__pycache__/json_schema.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pydantic/_internal/__pycache__/_generate_schema.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pydantic_core/__pycache__/core_schema.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pydantic_core/_pydantic_core.cp312-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pydantic_settings/__pycache__/sources.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
techhubprototypeucbackend/venv/Lib/site-packages/pygments/lexers/__pycache__/lisp.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
techhubprototypeucbackend/venv/Scripts/dotenv.exe filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
techhubprototypeucbackend/venv/Scripts/fastapi.exe filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
techhubprototypeucbackend/venv/Scripts/httpx.exe filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
techhubprototypeucbackend/venv/Scripts/normalizer.exe filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
techhubprototypeucbackend/venv/Scripts/pip.exe filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
techhubprototypeucbackend/venv/Scripts/pip3.12.exe filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
techhubprototypeucbackend/venv/Scripts/pip3.exe filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
techhubprototypeucbackend/venv/Scripts/py.test.exe filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
techhubprototypeucbackend/venv/Scripts/pygmentize.exe filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-decrypt.exe filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-encrypt.exe filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-keygen.exe filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-priv2pub.exe filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-sign.exe filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
techhubprototypeucbackend/venv/Scripts/pyrsa-verify.exe filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
techhubprototypeucbackend/venv/Scripts/pytest.exe filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
techhubprototypeucbackend/venv/Scripts/python.exe filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
techhubprototypeucbackend/venv/Scripts/pythonw.exe filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
techhubprototypeucbackend/venv/Scripts/uvicorn.exe filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
techhubprototypeucbackend/venv/Scripts/websockets.exe filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
techhubprototypeucfrontend/node_modules/@esbuild/win32-x64/esbuild.exe filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
techhubprototypeucfrontend/node_modules/@rollup/rollup-win32-x64-gnu/rollup.win32-x64-gnu.node filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
techhubprototypeucfrontend/node_modules/@rollup/rollup-win32-x64-msvc/rollup.win32-x64-msvc.node filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stage 1: Build Frontend
|
| 2 |
+
FROM node:20 as frontend-builder
|
| 3 |
+
|
| 4 |
+
WORKDIR /app/frontend
|
| 5 |
+
COPY techhubprototypeucfrontend/package*.json ./
|
| 6 |
+
RUN npm install
|
| 7 |
+
COPY techhubprototypeucfrontend/ .
|
| 8 |
+
RUN npm run build
|
| 9 |
+
|
| 10 |
+
# Stage 2: Backend
|
| 11 |
+
FROM python:3.11-slim
|
| 12 |
+
|
| 13 |
+
WORKDIR /app
|
| 14 |
+
|
| 15 |
+
# Install system dependencies if needed
|
| 16 |
+
# RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/*
|
| 17 |
+
|
| 18 |
+
COPY techhubprototypeucbackend/requirements.txt .
|
| 19 |
+
# Remove pyaudio from requirements if it's there, as it requires system dependencies and we removed usage
|
| 20 |
+
RUN sed -i '/pyaudio/d' requirements.txt
|
| 21 |
+
|
| 22 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 23 |
+
|
| 24 |
+
COPY techhubprototypeucbackend/ .
|
| 25 |
+
|
| 26 |
+
# Copy frontend build to static folder
|
| 27 |
+
COPY --from=frontend-builder /app/frontend/dist ./static
|
| 28 |
+
|
| 29 |
+
# Expose port 7860 (Hugging Face Spaces default)
|
| 30 |
+
EXPOSE 7860
|
| 31 |
+
|
| 32 |
+
# Run the app
|
| 33 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
referenceDocs/Get_started_LiveAPI.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
# Copyright 2025 Google LLC
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
## Setup
|
| 18 |
+
|
| 19 |
+
To install the dependencies for this script, run:
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
pip install google-genai opencv-python pyaudio pillow mss
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Before running this script, ensure the `GOOGLE_API_KEY` environment
|
| 26 |
+
variable is set to the api-key you obtained from Google AI Studio.
|
| 27 |
+
|
| 28 |
+
Important: **Use headphones**. This script uses the system default audio
|
| 29 |
+
input and output, which often won't include echo cancellation. So to prevent
|
| 30 |
+
the model from interrupting itself it is important that you use headphones.
|
| 31 |
+
|
| 32 |
+
## Run
|
| 33 |
+
|
| 34 |
+
To run the script:
|
| 35 |
+
|
| 36 |
+
```
|
| 37 |
+
python Get_started_LiveAPI.py
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
The script takes a video-mode flag `--mode`, this can be "camera", "screen", or "none".
|
| 41 |
+
The default is "camera". To share your screen run:
|
| 42 |
+
|
| 43 |
+
```
|
| 44 |
+
python Get_started_LiveAPI.py --mode screen
|
| 45 |
+
```
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
import asyncio
|
| 49 |
+
import base64
|
| 50 |
+
import io
|
| 51 |
+
import os
|
| 52 |
+
import sys
|
| 53 |
+
import traceback
|
| 54 |
+
|
| 55 |
+
import cv2
|
| 56 |
+
import pyaudio
|
| 57 |
+
import PIL.Image
|
| 58 |
+
import mss
|
| 59 |
+
|
| 60 |
+
import argparse
|
| 61 |
+
|
| 62 |
+
from google import genai
|
| 63 |
+
|
| 64 |
+
if sys.version_info < (3, 11, 0):
|
| 65 |
+
import taskgroup, exceptiongroup
|
| 66 |
+
|
| 67 |
+
asyncio.TaskGroup = taskgroup.TaskGroup
|
| 68 |
+
asyncio.ExceptionGroup = exceptiongroup.ExceptionGroup
|
| 69 |
+
|
| 70 |
+
FORMAT = pyaudio.paInt16
|
| 71 |
+
CHANNELS = 1
|
| 72 |
+
SEND_SAMPLE_RATE = 16000
|
| 73 |
+
RECEIVE_SAMPLE_RATE = 24000
|
| 74 |
+
CHUNK_SIZE = 1024
|
| 75 |
+
|
| 76 |
+
MODEL = "models/gemini-2.0-flash-live-001"
|
| 77 |
+
|
| 78 |
+
DEFAULT_MODE = "camera"
|
| 79 |
+
|
| 80 |
+
client = genai.Client(http_options={"api_version": "v1beta"})
|
| 81 |
+
|
| 82 |
+
CONFIG = {"response_modalities": ["AUDIO"]}
|
| 83 |
+
|
| 84 |
+
pya = pyaudio.PyAudio()
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class AudioLoop:
|
| 88 |
+
def __init__(self, video_mode=DEFAULT_MODE):
|
| 89 |
+
self.video_mode = video_mode
|
| 90 |
+
|
| 91 |
+
self.audio_in_queue = None
|
| 92 |
+
self.out_queue = None
|
| 93 |
+
|
| 94 |
+
self.session = None
|
| 95 |
+
|
| 96 |
+
self.send_text_task = None
|
| 97 |
+
self.receive_audio_task = None
|
| 98 |
+
self.play_audio_task = None
|
| 99 |
+
|
| 100 |
+
async def send_text(self):
|
| 101 |
+
while True:
|
| 102 |
+
text = await asyncio.to_thread(
|
| 103 |
+
input,
|
| 104 |
+
"message > ",
|
| 105 |
+
)
|
| 106 |
+
if text.lower() == "q":
|
| 107 |
+
break
|
| 108 |
+
await self.session.send(input=text or ".", end_of_turn=True)
|
| 109 |
+
|
| 110 |
+
def _get_frame(self, cap):
|
| 111 |
+
# Read the frameq
|
| 112 |
+
ret, frame = cap.read()
|
| 113 |
+
# Check if the frame was read successfully
|
| 114 |
+
if not ret:
|
| 115 |
+
return None
|
| 116 |
+
# Fix: Convert BGR to RGB color space
|
| 117 |
+
# OpenCV captures in BGR but PIL expects RGB format
|
| 118 |
+
# This prevents the blue tint in the video feed
|
| 119 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 120 |
+
img = PIL.Image.fromarray(frame_rgb) # Now using RGB frame
|
| 121 |
+
img.thumbnail([1024, 1024])
|
| 122 |
+
|
| 123 |
+
image_io = io.BytesIO()
|
| 124 |
+
img.save(image_io, format="jpeg")
|
| 125 |
+
image_io.seek(0)
|
| 126 |
+
|
| 127 |
+
mime_type = "image/jpeg"
|
| 128 |
+
image_bytes = image_io.read()
|
| 129 |
+
return {"mime_type": mime_type, "data": base64.b64encode(image_bytes).decode()}
|
| 130 |
+
|
| 131 |
+
async def get_frames(self):
|
| 132 |
+
# This takes about a second, and will block the whole program
|
| 133 |
+
# causing the audio pipeline to overflow if you don't to_thread it.
|
| 134 |
+
cap = await asyncio.to_thread(
|
| 135 |
+
cv2.VideoCapture, 0
|
| 136 |
+
) # 0 represents the default camera
|
| 137 |
+
|
| 138 |
+
while True:
|
| 139 |
+
frame = await asyncio.to_thread(self._get_frame, cap)
|
| 140 |
+
if frame is None:
|
| 141 |
+
break
|
| 142 |
+
|
| 143 |
+
await asyncio.sleep(1.0)
|
| 144 |
+
|
| 145 |
+
await self.out_queue.put(frame)
|
| 146 |
+
|
| 147 |
+
# Release the VideoCapture object
|
| 148 |
+
cap.release()
|
| 149 |
+
|
| 150 |
+
def _get_screen(self):
|
| 151 |
+
sct = mss.mss()
|
| 152 |
+
monitor = sct.monitors[0]
|
| 153 |
+
|
| 154 |
+
i = sct.grab(monitor)
|
| 155 |
+
|
| 156 |
+
mime_type = "image/jpeg"
|
| 157 |
+
image_bytes = mss.tools.to_png(i.rgb, i.size)
|
| 158 |
+
img = PIL.Image.open(io.BytesIO(image_bytes))
|
| 159 |
+
|
| 160 |
+
image_io = io.BytesIO()
|
| 161 |
+
img.save(image_io, format="jpeg")
|
| 162 |
+
image_io.seek(0)
|
| 163 |
+
|
| 164 |
+
image_bytes = image_io.read()
|
| 165 |
+
return {"mime_type": mime_type, "data": base64.b64encode(image_bytes).decode()}
|
| 166 |
+
|
| 167 |
+
async def get_screen(self):
|
| 168 |
+
|
| 169 |
+
while True:
|
| 170 |
+
frame = await asyncio.to_thread(self._get_screen)
|
| 171 |
+
if frame is None:
|
| 172 |
+
break
|
| 173 |
+
|
| 174 |
+
await asyncio.sleep(1.0)
|
| 175 |
+
|
| 176 |
+
await self.out_queue.put(frame)
|
| 177 |
+
|
| 178 |
+
async def send_realtime(self):
|
| 179 |
+
while True:
|
| 180 |
+
msg = await self.out_queue.get()
|
| 181 |
+
await self.session.send(input=msg)
|
| 182 |
+
|
| 183 |
+
async def listen_audio(self):
|
| 184 |
+
mic_info = pya.get_default_input_device_info()
|
| 185 |
+
self.audio_stream = await asyncio.to_thread(
|
| 186 |
+
pya.open,
|
| 187 |
+
format=FORMAT,
|
| 188 |
+
channels=CHANNELS,
|
| 189 |
+
rate=SEND_SAMPLE_RATE,
|
| 190 |
+
input=True,
|
| 191 |
+
input_device_index=mic_info["index"],
|
| 192 |
+
frames_per_buffer=CHUNK_SIZE,
|
| 193 |
+
)
|
| 194 |
+
if __debug__:
|
| 195 |
+
kwargs = {"exception_on_overflow": False}
|
| 196 |
+
else:
|
| 197 |
+
kwargs = {}
|
| 198 |
+
while True:
|
| 199 |
+
data = await asyncio.to_thread(self.audio_stream.read, CHUNK_SIZE, **kwargs)
|
| 200 |
+
await self.out_queue.put({"data": data, "mime_type": "audio/pcm"})
|
| 201 |
+
|
| 202 |
+
async def receive_audio(self):
|
| 203 |
+
"Background task to reads from the websocket and write pcm chunks to the output queue"
|
| 204 |
+
while True:
|
| 205 |
+
turn = self.session.receive()
|
| 206 |
+
async for response in turn:
|
| 207 |
+
if data := response.data:
|
| 208 |
+
self.audio_in_queue.put_nowait(data)
|
| 209 |
+
continue
|
| 210 |
+
if text := response.text:
|
| 211 |
+
print(text, end="")
|
| 212 |
+
|
| 213 |
+
# If you interrupt the model, it sends a turn_complete.
|
| 214 |
+
# For interruptions to work, we need to stop playback.
|
| 215 |
+
# So empty out the audio queue because it may have loaded
|
| 216 |
+
# much more audio than has played yet.
|
| 217 |
+
while not self.audio_in_queue.empty():
|
| 218 |
+
self.audio_in_queue.get_nowait()
|
| 219 |
+
|
| 220 |
+
async def play_audio(self):
|
| 221 |
+
stream = await asyncio.to_thread(
|
| 222 |
+
pya.open,
|
| 223 |
+
format=FORMAT,
|
| 224 |
+
channels=CHANNELS,
|
| 225 |
+
rate=RECEIVE_SAMPLE_RATE,
|
| 226 |
+
output=True,
|
| 227 |
+
)
|
| 228 |
+
while True:
|
| 229 |
+
bytestream = await self.audio_in_queue.get()
|
| 230 |
+
await asyncio.to_thread(stream.write, bytestream)
|
| 231 |
+
|
| 232 |
+
async def run(self):
|
| 233 |
+
try:
|
| 234 |
+
async with (
|
| 235 |
+
client.aio.live.connect(model=MODEL, config=CONFIG) as session,
|
| 236 |
+
asyncio.TaskGroup() as tg,
|
| 237 |
+
):
|
| 238 |
+
self.session = session
|
| 239 |
+
|
| 240 |
+
self.audio_in_queue = asyncio.Queue()
|
| 241 |
+
self.out_queue = asyncio.Queue(maxsize=5)
|
| 242 |
+
|
| 243 |
+
send_text_task = tg.create_task(self.send_text())
|
| 244 |
+
tg.create_task(self.send_realtime())
|
| 245 |
+
tg.create_task(self.listen_audio())
|
| 246 |
+
if self.video_mode == "camera":
|
| 247 |
+
tg.create_task(self.get_frames())
|
| 248 |
+
elif self.video_mode == "screen":
|
| 249 |
+
tg.create_task(self.get_screen())
|
| 250 |
+
|
| 251 |
+
tg.create_task(self.receive_audio())
|
| 252 |
+
tg.create_task(self.play_audio())
|
| 253 |
+
|
| 254 |
+
await send_text_task
|
| 255 |
+
raise asyncio.CancelledError("User requested exit")
|
| 256 |
+
|
| 257 |
+
except asyncio.CancelledError:
|
| 258 |
+
pass
|
| 259 |
+
except ExceptionGroup as EG:
|
| 260 |
+
self.audio_stream.close()
|
| 261 |
+
traceback.print_exception(EG)
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
if __name__ == "__main__":
|
| 265 |
+
parser = argparse.ArgumentParser()
|
| 266 |
+
parser.add_argument(
|
| 267 |
+
"--mode",
|
| 268 |
+
type=str,
|
| 269 |
+
default=DEFAULT_MODE,
|
| 270 |
+
help="pixels to stream from",
|
| 271 |
+
choices=["camera", "screen", "none"],
|
| 272 |
+
)
|
| 273 |
+
args = parser.parse_args()
|
| 274 |
+
main = AudioLoop(video_mode=args.mode)
|
| 275 |
+
asyncio.run(main.run())
|
techhubprototypeucbackend/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
GOOGLE_API_KEY="AIzaSyCdGSqk7pc3yPuftVsP1EKzh4VUeQddya4"
|
techhubprototypeucbackend/.env.example
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Google Generative AI API Key
|
| 2 |
+
GOOGLE_API_KEY="YOUR_API_KEY_HERE"
|
| 3 |
+
|
| 4 |
+
# Gemini Model to use
|
| 5 |
+
GEMINI_MODEL="models/gemini-2.0-flash-live-001"
|
techhubprototypeucbackend/.pytest_cache/.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Created by pytest automatically.
|
| 2 |
+
*
|
techhubprototypeucbackend/.pytest_cache/CACHEDIR.TAG
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Signature: 8a477f597d28d172789f06886806bc55
|
| 2 |
+
# This file is a cache directory tag created by pytest.
|
| 3 |
+
# For information about cache directory tags, see:
|
| 4 |
+
# https://bford.info/cachedir/spec.html
|
techhubprototypeucbackend/.pytest_cache/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# pytest cache directory #
|
| 2 |
+
|
| 3 |
+
This directory contains data from the pytest's cache plugin,
|
| 4 |
+
which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
|
| 5 |
+
|
| 6 |
+
**Do not** commit this to version control.
|
| 7 |
+
|
| 8 |
+
See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
|
techhubprototypeucbackend/.pytest_cache/v/cache/lastfailed
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{}
|
techhubprototypeucbackend/.pytest_cache/v/cache/nodeids
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"tests/test_main.py::test_read_main",
|
| 3 |
+
"tests/test_main.py::test_websocket_connection"
|
| 4 |
+
]
|
techhubprototypeucbackend/__pycache__/main.cpython-312.pyc
ADDED
|
Binary file (10.5 kB). View file
|
|
|
techhubprototypeucbackend/main.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
import traceback
|
| 6 |
+
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
| 9 |
+
from fastapi.staticfiles import StaticFiles
|
| 10 |
+
from fastapi.responses import FileResponse
|
| 11 |
+
from google import genai
|
| 12 |
+
from google.api_core import exceptions as google_exceptions
|
| 13 |
+
|
| 14 |
+
# Configure logging
|
| 15 |
+
logging.basicConfig(level=logging.INFO)
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
load_dotenv()
|
| 19 |
+
|
| 20 |
+
if sys.version_info < (3, 11, 0):
|
| 21 |
+
import taskgroup, exceptiongroup
|
| 22 |
+
|
| 23 |
+
asyncio.TaskGroup = taskgroup.TaskGroup
|
| 24 |
+
asyncio.ExceptionGroup = exceptiongroup.ExceptionGroup
|
| 25 |
+
|
| 26 |
+
# Audio settings
|
| 27 |
+
# FORMAT = pyaudio.paInt16 # Removed pyaudio dependency
|
| 28 |
+
# CHANNELS = 1
|
| 29 |
+
# SEND_SAMPLE_RATE = 16000
|
| 30 |
+
# RECEIVE_SAMPLE_RATE = 24000
|
| 31 |
+
# CHUNK_SIZE = 1024
|
| 32 |
+
|
| 33 |
+
# Load configuration from environment variables
|
| 34 |
+
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
|
| 35 |
+
MODEL = os.environ.get("GEMINI_MODEL", "models/gemini-2.0-flash-live-001")
|
| 36 |
+
|
| 37 |
+
# Configure the client with the API key
|
| 38 |
+
try:
|
| 39 |
+
if not GOOGLE_API_KEY or GOOGLE_API_KEY == "YOUR_API_KEY_HERE":
|
| 40 |
+
# In HF Spaces, we might set this via secrets, so we warn but don't exit immediately if it's missing during build
|
| 41 |
+
logger.warning("GOOGLE_API_KEY environment variable not set or is a placeholder.")
|
| 42 |
+
client = genai.Client(api_key=GOOGLE_API_KEY)
|
| 43 |
+
except (KeyError, ValueError) as e:
|
| 44 |
+
logger.critical(f"Error: {e}. Please set the GOOGLE_API_KEY environment variable.")
|
| 45 |
+
# sys.exit(1) # Don't exit, let it fail at runtime if key is missing, to allow build to pass
|
| 46 |
+
|
| 47 |
+
CONFIG = {
|
| 48 |
+
"response_modalities": ["AUDIO"],
|
| 49 |
+
"output_audio_transcription": {},
|
| 50 |
+
"generation_config": {
|
| 51 |
+
"temperature": 1.0,
|
| 52 |
+
},
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
# pya = pyaudio.PyAudio() # Removed pyaudio dependency
|
| 56 |
+
|
| 57 |
+
app = FastAPI()
|
| 58 |
+
|
| 59 |
+
# Mount static files
|
| 60 |
+
# We assume the frontend build will be copied to 'static' directory in the container
|
| 61 |
+
if os.path.exists("static"):
|
| 62 |
+
app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")
|
| 63 |
+
|
| 64 |
+
@app.get("/")
|
| 65 |
+
async def get():
|
| 66 |
+
# Serve the index.html from the static directory
|
| 67 |
+
if os.path.exists("static/index.html"):
|
| 68 |
+
return FileResponse("static/index.html")
|
| 69 |
+
return HTMLResponse("<h1>Frontend not found. Please build the frontend.</h1>")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class AudioLoop:
|
| 73 |
+
def __init__(self, websocket: WebSocket):
|
| 74 |
+
self.websocket = websocket
|
| 75 |
+
self.session = None
|
| 76 |
+
|
| 77 |
+
async def run(self):
|
| 78 |
+
try:
|
| 79 |
+
async with client.aio.live.connect(model=MODEL, config=CONFIG) as session:
|
| 80 |
+
self.session = session
|
| 81 |
+
logger.info("Gemini Live API session started.")
|
| 82 |
+
|
| 83 |
+
async with asyncio.TaskGroup() as tg:
|
| 84 |
+
tg.create_task(self.receive_from_gemini())
|
| 85 |
+
tg.create_task(self.send_to_gemini())
|
| 86 |
+
|
| 87 |
+
except asyncio.CancelledError:
|
| 88 |
+
logger.info("Audio loop cancelled.")
|
| 89 |
+
except google_exceptions.GoogleAPICallError as e:
|
| 90 |
+
logger.error(f"Google API call error in audio loop: {e}")
|
| 91 |
+
await self.websocket.close(code=1011, reason=f"Google API Error: {e}")
|
| 92 |
+
except Exception as e:
|
| 93 |
+
logger.error(f"An error occurred in the audio loop: {e}")
|
| 94 |
+
traceback.print_exc()
|
| 95 |
+
await self.websocket.close(code=1011, reason="Internal Server Error")
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
async def send_to_gemini(self):
|
| 99 |
+
"""Receives audio from the WebSocket and sends it to the Gemini API."""
|
| 100 |
+
while True:
|
| 101 |
+
try:
|
| 102 |
+
data = await self.websocket.receive_bytes()
|
| 103 |
+
if self.session:
|
| 104 |
+
await self.session.send(
|
| 105 |
+
input={"data": data, "mime_type": "audio/pcm"}
|
| 106 |
+
)
|
| 107 |
+
except WebSocketDisconnect:
|
| 108 |
+
logger.info("Client disconnected from WebSocket.")
|
| 109 |
+
break
|
| 110 |
+
except Exception as e:
|
| 111 |
+
logger.error(f"Error receiving from websocket or sending to Gemini: {e}")
|
| 112 |
+
break
|
| 113 |
+
|
| 114 |
+
async def receive_from_gemini(self):
|
| 115 |
+
"""Receives audio and text from the Gemini API and forwards it to the WebSocket."""
|
| 116 |
+
while True:
|
| 117 |
+
try:
|
| 118 |
+
if self.session:
|
| 119 |
+
turn = self.session.receive()
|
| 120 |
+
async for response in turn:
|
| 121 |
+
# Handle audio data directly from response.data
|
| 122 |
+
if data := response.data:
|
| 123 |
+
await self.websocket.send_bytes(data)
|
| 124 |
+
continue
|
| 125 |
+
|
| 126 |
+
# Handle text/transcript and potentially nested audio data
|
| 127 |
+
candidate_texts = []
|
| 128 |
+
server_content = (
|
| 129 |
+
response.server_content.model_turn.parts
|
| 130 |
+
if response.server_content
|
| 131 |
+
and response.server_content.model_turn
|
| 132 |
+
and response.server_content.model_turn.parts
|
| 133 |
+
else []
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
for part in server_content:
|
| 137 |
+
# Check for nested audio data
|
| 138 |
+
if inline_data := getattr(part, "inline_data", None):
|
| 139 |
+
if data := getattr(inline_data, "data", None):
|
| 140 |
+
await self.websocket.send_bytes(data)
|
| 141 |
+
# Check for text
|
| 142 |
+
if part_text := getattr(part, "text", None):
|
| 143 |
+
candidate_texts.append(part_text)
|
| 144 |
+
|
| 145 |
+
server_content_obj = getattr(response, "server_content", None)
|
| 146 |
+
if server_content_obj:
|
| 147 |
+
if output_transcription := getattr(server_content_obj, "output_transcription", None):
|
| 148 |
+
if trans_text := getattr(output_transcription, "text", None):
|
| 149 |
+
candidate_texts.append(trans_text)
|
| 150 |
+
if input_transcription := getattr(server_content_obj, "input_transcription", None):
|
| 151 |
+
if trans_text := getattr(input_transcription, "text", None):
|
| 152 |
+
candidate_texts.append(trans_text)
|
| 153 |
+
|
| 154 |
+
if output_text := getattr(response, "output_text", None):
|
| 155 |
+
if isinstance(output_text, (list, tuple)):
|
| 156 |
+
candidate_texts.extend(output_text)
|
| 157 |
+
else:
|
| 158 |
+
candidate_texts.append(output_text)
|
| 159 |
+
|
| 160 |
+
if response_text := getattr(response, "text", None):
|
| 161 |
+
candidate_texts.append(response_text)
|
| 162 |
+
|
| 163 |
+
for text_chunk in candidate_texts:
|
| 164 |
+
if not text_chunk:
|
| 165 |
+
continue
|
| 166 |
+
normalized = text_chunk.replace("\r", "").replace("\n", " ")
|
| 167 |
+
if normalized and normalized.strip():
|
| 168 |
+
logger.info(f"Received text: {normalized.strip()}")
|
| 169 |
+
await self.websocket.send_text(normalized)
|
| 170 |
+
except WebSocketDisconnect:
|
| 171 |
+
logger.info("Client disconnected. Stopping receive loop.")
|
| 172 |
+
break
|
| 173 |
+
except Exception as e:
|
| 174 |
+
logger.error(f"Error receiving from Gemini or sending to websocket: {e}")
|
| 175 |
+
break
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
@app.websocket("/ws")
|
| 179 |
+
async def websocket_endpoint(websocket: WebSocket):
|
| 180 |
+
await websocket.accept()
|
| 181 |
+
logger.info("WebSocket connection accepted.")
|
| 182 |
+
audio_loop = AudioLoop(websocket)
|
| 183 |
+
try:
|
| 184 |
+
await audio_loop.run()
|
| 185 |
+
except WebSocketDisconnect:
|
| 186 |
+
logger.info("Client disconnected.")
|
| 187 |
+
except Exception as e:
|
| 188 |
+
logger.error(f"Error in websocket endpoint: {e}")
|
| 189 |
+
finally:
|
| 190 |
+
logger.info("WebSocket connection closed.")
|
techhubprototypeucbackend/requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
uvicorn>=0.30.5
|
| 2 |
+
google-genai==1.46.0
|
| 3 |
+
google-auth==2.41.1
|
| 4 |
+
google-api-core==2.19.2
|
| 5 |
+
pydantic==2.9.0
|
| 6 |
+
pydantic-settings==2.7.0
|
| 7 |
+
pytest>=8.3
|
| 8 |
+
pytest-asyncio>=0.23
|
| 9 |
+
fastapi
|
| 10 |
+
python-dotenv
|
| 11 |
+
websockets
|
| 12 |
+
pyaudio
|
techhubprototypeucbackend/tests/__pycache__/test_main.cpython-312-pytest-8.4.2.pyc
ADDED
|
Binary file (4.9 kB). View file
|
|
|
techhubprototypeucbackend/tests/test_main.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
from fastapi.testclient import TestClient
|
| 3 |
+
from unittest.mock import patch, MagicMock, AsyncMock
|
| 4 |
+
from main import app
|
| 5 |
+
|
| 6 |
+
client = TestClient(app)
|
| 7 |
+
|
| 8 |
+
def test_read_main():
|
| 9 |
+
response = client.get("/")
|
| 10 |
+
assert response.status_code == 200
|
| 11 |
+
assert "<h1>Gemini Live API</h1>" in response.text
|
| 12 |
+
|
| 13 |
+
@pytest.mark.asyncio
|
| 14 |
+
@patch('main.client')
|
| 15 |
+
async def test_websocket_connection(mock_client):
|
| 16 |
+
# Mock the Gemini client and session
|
| 17 |
+
mock_session = MagicMock()
|
| 18 |
+
|
| 19 |
+
# Make the mock session an async iterator for the receive method
|
| 20 |
+
mock_session.receive.return_value = mock_session
|
| 21 |
+
async def async_iterator():
|
| 22 |
+
yield MagicMock(text="test", data=b"")
|
| 23 |
+
mock_session.__aiter__.return_value = async_iterator()
|
| 24 |
+
mock_session.send = AsyncMock()
|
| 25 |
+
|
| 26 |
+
# Create an async context manager for the mock
|
| 27 |
+
async def async_context_manager(*args, **kwargs):
|
| 28 |
+
class AsyncContextManager:
|
| 29 |
+
async def __aenter__(self):
|
| 30 |
+
return mock_session
|
| 31 |
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
| 32 |
+
pass
|
| 33 |
+
return AsyncContextManager()
|
| 34 |
+
|
| 35 |
+
mock_client.aio.live.connect.side_effect = async_context_manager
|
| 36 |
+
|
| 37 |
+
with client.websocket_connect("/ws") as websocket:
|
| 38 |
+
# Test connection and initial message
|
| 39 |
+
assert websocket.scope['path'] == '/ws'
|
| 40 |
+
|
| 41 |
+
# Simulate sending audio data
|
| 42 |
+
audio_data = b'\x01\x02\x03'
|
| 43 |
+
websocket.send_bytes(audio_data)
|
| 44 |
+
|
| 45 |
+
# Close the connection
|
| 46 |
+
websocket.close()
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2006 Hubert Pham
|
| 2 |
+
|
| 3 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 4 |
+
a copy of this software and associated documentation files (the
|
| 5 |
+
"Software"), to deal in the Software without restriction, including
|
| 6 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
| 7 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
| 8 |
+
permit persons to whom the Software is furnished to do so, subject to
|
| 9 |
+
the following conditions:
|
| 10 |
+
|
| 11 |
+
The above copyright notice and this permission notice shall be
|
| 12 |
+
included in all copies or substantial portions of the Software.
|
| 13 |
+
|
| 14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 15 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 16 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| 17 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| 18 |
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| 19 |
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| 20 |
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/METADATA
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: PyAudio
|
| 3 |
+
Version: 0.2.14
|
| 4 |
+
Summary: Cross-platform audio I/O with PortAudio
|
| 5 |
+
Home-page: https://people.csail.mit.edu/hubert/pyaudio/
|
| 6 |
+
Author: Hubert Pham
|
| 7 |
+
License: MIT
|
| 8 |
+
Classifier: Development Status :: 4 - Beta
|
| 9 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 10 |
+
Classifier: Programming Language :: Python :: 3
|
| 11 |
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
| 12 |
+
Description-Content-Type: text/markdown
|
| 13 |
+
License-File: LICENSE.txt
|
| 14 |
+
Provides-Extra: test
|
| 15 |
+
Requires-Dist: numpy ; extra == 'test'
|
| 16 |
+
|
| 17 |
+
<img align="right" width="200" style="margin-left: 3px" src="https://people.csail.mit.edu/hubert/pyaudio/images/snake-300.png">
|
| 18 |
+
|
| 19 |
+
# PyAudio
|
| 20 |
+
|
| 21 |
+
PyAudio provides Python bindings for PortAudio v19, the cross-platform audio I/O library. With PyAudio, you can easily use Python to play and record audio on a variety of platforms, such as GNU/Linux, Microsoft Windows, and Apple macOS.
|
| 22 |
+
|
| 23 |
+
PyAudio is distributed under the MIT License.
|
| 24 |
+
|
| 25 |
+
* [Homepage](https://people.csail.mit.edu/hubert/pyaudio/)
|
| 26 |
+
* [API Documentation](https://people.csail.mit.edu/hubert/pyaudio/docs/)
|
| 27 |
+
* [PyPi](https://pypi.python.org/pypi/PyAudio)
|
| 28 |
+
|
| 29 |
+
## Installation
|
| 30 |
+
|
| 31 |
+
See the INSTALLATION file in the source distribution for details. In summary, install PyAudio using `pip` on most platforms.
|
| 32 |
+
|
| 33 |
+
### Windows
|
| 34 |
+
|
| 35 |
+
```sh
|
| 36 |
+
python -m pip install pyaudio
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
This installs the precompiled PyAudio library with PortAudio v19 19.7.0 included. The library is compiled with support for Windows MME API, DirectSound, WASAPI, and WDM-KS. It does not include support for ASIO. If you require support for APIs not included, you will need to compile PortAudio and PyAudio.
|
| 40 |
+
|
| 41 |
+
### macOS
|
| 42 |
+
|
| 43 |
+
Use [Homebrew](https://brew.sh) to install the prerequisite [portaudio](http://portaudio.com) library, then install PyAudio using `pip`:
|
| 44 |
+
|
| 45 |
+
```sh
|
| 46 |
+
brew install portaudio
|
| 47 |
+
pip install pyaudio
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
### GNU/Linux
|
| 51 |
+
|
| 52 |
+
Use the package manager to install PyAudio. For example, on Debian-based systems:
|
| 53 |
+
|
| 54 |
+
```sh
|
| 55 |
+
sudo apt install python3-pyaudio
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Alternatively, if the latest version of PyAudio is not available, install it using `pip`. Be sure to first install development libraries for `portaudio19` and `python3`.
|
| 59 |
+
|
| 60 |
+
### Building from source
|
| 61 |
+
|
| 62 |
+
See the INSTALLATION file.
|
| 63 |
+
|
| 64 |
+
## Documentation & Usage Examples
|
| 65 |
+
|
| 66 |
+
* Read the [API Documentation](https://people.csail.mit.edu/hubert/pyaudio/docs/), or generate it from the source using [`sphinx`](https://www.sphinx-doc.org/).
|
| 67 |
+
|
| 68 |
+
* Usage examples are in the `examples` directory of the source distribution, or see the [project homepage](https://people.csail.mit.edu/hubert/pyaudio/).
|
| 69 |
+
|
| 70 |
+
## License
|
| 71 |
+
|
| 72 |
+
PyAudio is distributed under the MIT License. See LICENSE.txt.
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/RECORD
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
PyAudio-0.2.14.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 2 |
+
PyAudio-0.2.14.dist-info/LICENSE.txt,sha256=fntVlrMb-hzPD2Nug0TmemhiZeCRwN02DxG2bcgqvFQ,1055
|
| 3 |
+
PyAudio-0.2.14.dist-info/METADATA,sha256=qNNR9Cphk7asLfQ1hbLo3VibawpPaCR2HyufgWYdrwI,2683
|
| 4 |
+
PyAudio-0.2.14.dist-info/RECORD,,
|
| 5 |
+
PyAudio-0.2.14.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 6 |
+
PyAudio-0.2.14.dist-info/WHEEL,sha256=7M9omTteV1AA1lqimSm66FA5mPjaJNBD3zI5eMwtk7s,102
|
| 7 |
+
PyAudio-0.2.14.dist-info/top_level.txt,sha256=jp8bgmWZh435lFm-DsWc00D0k4xj4iAEmWd1L_TpKk8,8
|
| 8 |
+
pyaudio/__init__.py,sha256=7V6ZjNJra5wjKcIoklmSU84UmFMrFm23DT1dtWtxXGU,36914
|
| 9 |
+
pyaudio/__pycache__/__init__.cpython-312.pyc,,
|
| 10 |
+
pyaudio/_portaudio.cp312-win_amd64.pyd,sha256=aKCiqugJeqIL9-m0A6N4KdufANn-sN1ea33C5_vNeBk,301568
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/REQUESTED
ADDED
|
File without changes
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: bdist_wheel (0.41.3)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp312-cp312-win_amd64
|
| 5 |
+
|
techhubprototypeucbackend/venv/Lib/site-packages/PyAudio-0.2.14.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pyaudio
|
techhubprototypeucbackend/venv/Lib/site-packages/__pycache__/py.cpython-312.pyc
ADDED
|
Binary file (549 Bytes). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-312.pyc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ff5616ddeb9ff271cbcba20d5201ea1643c2899ba707d028dbb6e58ae95f2eaf
|
| 3 |
+
size 163697
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
__all__ = ["__version__", "version_tuple"]
|
| 5 |
+
|
| 6 |
+
try:
|
| 7 |
+
from ._version import version as __version__
|
| 8 |
+
from ._version import version_tuple
|
| 9 |
+
except ImportError: # pragma: no cover
|
| 10 |
+
# broken installation, we don't even try
|
| 11 |
+
# unknown only works because we do poor mans version compare
|
| 12 |
+
__version__ = "unknown"
|
| 13 |
+
version_tuple = (0, 0, "unknown")
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (537 Bytes). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc
ADDED
|
Binary file (4.88 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-312.pyc
ADDED
|
Binary file (846 Bytes). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc
ADDED
|
Binary file (30.9 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-312.pyc
ADDED
|
Binary file (55.8 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-312.pyc
ADDED
|
Binary file (12.5 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc
ADDED
|
Binary file (18.3 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc
ADDED
|
Binary file (2.62 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc
ADDED
|
Binary file (33.4 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc
ADDED
|
Binary file (4.49 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc
ADDED
|
Binary file (81.9 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc
ADDED
|
Binary file (1.88 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc
ADDED
|
Binary file (12.6 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc
ADDED
|
Binary file (45.7 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc
ADDED
|
Binary file (33.2 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc
ADDED
|
Binary file (24.2 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-312.pyc
ADDED
|
Binary file (45.9 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-312.pyc
ADDED
|
Binary file (44.1 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc
ADDED
|
Binary file (16.6 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc
ADDED
|
Binary file (30.9 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc
ADDED
|
Binary file (11.8 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc
ADDED
|
Binary file (5.92 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc
ADDED
|
Binary file (41.3 kB). View file
|
|
|
techhubprototypeucbackend/venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc
ADDED
|
Binary file (84.5 kB). View file
|
|
|