Feat: STT & TTS Gemini, Creds Gemini Cloud Speech, Generate Buffer Audio, Testing
Browse filesticket:https://bukittechnology.atlassian.net/browse/KM-500
- develop feat STT using gemini
- debugging gemini speech to text and text to speech credential using
personal account
- testing functionality STT and TTS
- generate 3 buffer audio for buffering response
- .env.example +11 -6
- .gitignore +2 -1
- README.md +4 -1
- main.py +20 -21
- pyproject.toml +9 -0
- src/chatbot/__init__.py +0 -0
- src/chatbot/client.py +0 -60
- src/config.py +5 -3
- src/pipeline.py +22 -61
- src/stt/chirp3_client.py +119 -0
- src/stt/gemini_stt.py +43 -84
- src/tts/gemini_client.py +29 -29
- uv.lock +411 -0
.env.example
CHANGED
|
@@ -15,19 +15,24 @@ GEMINI_TTS_MODEL=gemini-2.5-flash-preview-tts
|
|
| 15 |
GEMINI_TTS_VOICE=Autonoe
|
| 16 |
GEMINI_TTS_LANGUAGE=id-ID
|
| 17 |
|
| 18 |
-
# Chatbot agent service
|
| 19 |
-
CHATBOT_BASE_URL=http://localhost:8000/chatbot-knowledgemanagement
|
| 20 |
-
CHATBOT_TIMEOUT=30
|
| 21 |
|
| 22 |
-
# Google Cloud Project
|
| 23 |
GOOGLE_PROJECT_NAME=
|
|
|
|
| 24 |
GOOGLE_PROJECT_NUMBER=
|
|
|
|
| 25 |
|
| 26 |
# Gemini STT
|
| 27 |
GEMINI_STT_MODEL=gemini-2.0-flash
|
| 28 |
GEMINI_LIVE_MODEL=gemini-live-2.5-flash-preview
|
| 29 |
GEMINI_STT_LANGUAGE=id-ID
|
| 30 |
|
| 31 |
-
#
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
TTS_PROVIDER=gemini
|
|
|
|
| 15 |
GEMINI_TTS_VOICE=Autonoe
|
| 16 |
GEMINI_TTS_LANGUAGE=id-ID
|
| 17 |
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
# Google Cloud Project
|
| 20 |
GOOGLE_PROJECT_NAME=
|
| 21 |
+
GOOGLE_PROJECT_ID=
|
| 22 |
GOOGLE_PROJECT_NUMBER=
|
| 23 |
+
GOOGLE_CLOUD_LOCATION=
|
| 24 |
|
| 25 |
# Gemini STT
|
| 26 |
GEMINI_STT_MODEL=gemini-2.0-flash
|
| 27 |
GEMINI_LIVE_MODEL=gemini-live-2.5-flash-preview
|
| 28 |
GEMINI_STT_LANGUAGE=id-ID
|
| 29 |
|
| 30 |
+
# Chirp 3 STT (Google Cloud Speech-to-Text V2)
|
| 31 |
+
# Membutuhkan GOOGLE_PROJECT_NAME dan Application Default Credentials (ADC)
|
| 32 |
+
# Set GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
|
| 33 |
+
CHIRP3_REGION=us
|
| 34 |
+
CHIRP3_LANGUAGE=id-ID
|
| 35 |
+
|
| 36 |
+
# Provider default untuk WebSocket /ws/voice: "deepgram", "gemini", atau "chirp3"
|
| 37 |
+
STT_PROVIDER=chirp3
|
| 38 |
TTS_PROVIDER=gemini
|
.gitignore
CHANGED
|
@@ -23,4 +23,5 @@ test_client.py
|
|
| 23 |
convert_audio.py
|
| 24 |
|
| 25 |
API_CONTRACT_CHATBOT.md
|
| 26 |
-
API_CONTRACT_VOICE.md
|
|
|
|
|
|
| 23 |
convert_audio.py
|
| 24 |
|
| 25 |
API_CONTRACT_CHATBOT.md
|
| 26 |
+
API_CONTRACT_VOICE.md
|
| 27 |
+
HIGHLIGHT_VOICE.md
|
README.md
CHANGED
|
@@ -52,7 +52,10 @@ WAKE_WORD=Hai EMA # Default: "Hai EMA"
|
|
| 52 |
## Run
|
| 53 |
|
| 54 |
```bash
|
| 55 |
-
uv run uvicorn main:app --host 0.0.0.0 --port 7861
|
|
|
|
|
|
|
|
|
|
| 56 |
```
|
| 57 |
|
| 58 |
Server akan berjalan di `http://localhost:7861`.
|
|
|
|
| 52 |
## Run
|
| 53 |
|
| 54 |
```bash
|
| 55 |
+
`uv run uvicorn main:app --host 0.0.0.0 --port 7861`
|
| 56 |
+
or
|
| 57 |
+
`uv run uvicorn main:app --host 0.0.0.0 --port 7861 --reload`
|
| 58 |
+
|
| 59 |
```
|
| 60 |
|
| 61 |
Server akan berjalan di `http://localhost:7861`.
|
main.py
CHANGED
|
@@ -8,7 +8,7 @@ from src.pipeline import VoicePipeline
|
|
| 8 |
from typing import Literal
|
| 9 |
from src.config import (
|
| 10 |
DEEPGRAM_API_KEY, CARTESIA_API_KEY, CARTESIA_VOICE_ID,
|
| 11 |
-
SAMPLE_RATE, GOOGLE_API_KEY,
|
| 12 |
)
|
| 13 |
from src.stt.deepgram_rest import transcribe_audio as deepgram_transcribe
|
| 14 |
from src.stt.gemini_stt import transcribe_audio as gemini_stt_transcribe
|
|
@@ -23,13 +23,20 @@ VERSION = "1.2.0"
|
|
| 23 |
app = FastAPI(title="Voice Agent Service", version=VERSION)
|
| 24 |
|
| 25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
@app.get("/health")
|
| 27 |
async def health() -> JSONResponse:
|
| 28 |
stt_ready = bool(DEEPGRAM_API_KEY)
|
| 29 |
tts_ready = bool(CARTESIA_API_KEY and CARTESIA_VOICE_ID)
|
| 30 |
gemini_ready = bool(GOOGLE_API_KEY)
|
| 31 |
-
|
| 32 |
-
all_ready = stt_ready and tts_ready and chatbot_ready
|
| 33 |
|
| 34 |
body: dict = {
|
| 35 |
"status": "ok" if all_ready else "degraded",
|
|
@@ -38,7 +45,6 @@ async def health() -> JSONResponse:
|
|
| 38 |
"tts_ready": tts_ready,
|
| 39 |
"gemini_tts_ready": gemini_ready,
|
| 40 |
"gemini_stt_ready": gemini_ready,
|
| 41 |
-
"chatbot_ready": chatbot_ready,
|
| 42 |
}
|
| 43 |
if not all_ready:
|
| 44 |
body["message"] = "One or more required configurations are missing. Check your .env file."
|
|
@@ -99,21 +105,14 @@ async def text_to_speech(req: TTSRequest) -> StreamingResponse:
|
|
| 99 |
@app.websocket("/ws/voice")
|
| 100 |
async def voice_ws(
|
| 101 |
ws: WebSocket,
|
| 102 |
-
user_id: str = Query(default="anonymous"),
|
| 103 |
-
fullname: str = Query(default=""),
|
| 104 |
-
company: str = Query(default=""),
|
| 105 |
-
function: str = Query(default=""),
|
| 106 |
-
site: str = Query(default="HO"),
|
| 107 |
-
role: str = Query(default="engineer"),
|
| 108 |
-
agent: str = Query(default="analysis"),
|
| 109 |
stt_provider: str = Query(default=STT_PROVIDER),
|
| 110 |
tts_provider: str = Query(default=TTS_PROVIDER),
|
| 111 |
wake_word_enabled: bool = Query(default=WAKE_WORD_ENABLED),
|
| 112 |
) -> None:
|
| 113 |
await ws.accept()
|
| 114 |
logger.info(
|
| 115 |
-
"Client connected: %s (
|
| 116 |
-
ws.client,
|
| 117 |
)
|
| 118 |
|
| 119 |
async def send_audio(chunk: bytes) -> None:
|
|
@@ -135,13 +134,6 @@ async def voice_ws(
|
|
| 135 |
pipeline = VoicePipeline(
|
| 136 |
send_audio=send_audio,
|
| 137 |
send_event=send_event,
|
| 138 |
-
user_id=user_id,
|
| 139 |
-
fullname=fullname,
|
| 140 |
-
company=company,
|
| 141 |
-
function=function,
|
| 142 |
-
site=site,
|
| 143 |
-
role=role,
|
| 144 |
-
agent=agent,
|
| 145 |
stt_provider=stt_provider,
|
| 146 |
tts_provider=tts_provider,
|
| 147 |
wake_word_enabled=wake_word_enabled,
|
|
@@ -163,12 +155,19 @@ async def voice_ws(
|
|
| 163 |
await ws.send_text(json.dumps({"event": "pong"}))
|
| 164 |
elif action == "interrupt":
|
| 165 |
await pipeline.interrupt()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
except json.JSONDecodeError:
|
| 167 |
pass
|
| 168 |
except WebSocketDisconnect:
|
| 169 |
logger.info("Client disconnected: %s", ws.client)
|
| 170 |
finally:
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
|
| 174 |
if __name__ == "__main__":
|
|
|
|
| 8 |
from typing import Literal
|
| 9 |
from src.config import (
|
| 10 |
DEEPGRAM_API_KEY, CARTESIA_API_KEY, CARTESIA_VOICE_ID,
|
| 11 |
+
SAMPLE_RATE, GOOGLE_API_KEY, STT_PROVIDER, TTS_PROVIDER, WAKE_WORD_ENABLED,
|
| 12 |
)
|
| 13 |
from src.stt.deepgram_rest import transcribe_audio as deepgram_transcribe
|
| 14 |
from src.stt.gemini_stt import transcribe_audio as gemini_stt_transcribe
|
|
|
|
| 23 |
app = FastAPI(title="Voice Agent Service", version=VERSION)
|
| 24 |
|
| 25 |
|
| 26 |
+
@app.get("/")
|
| 27 |
+
async def health() -> JSONResponse:
|
| 28 |
+
body: dict = {
|
| 29 |
+
"message": "Welcome to the Voice Agent Service! Please use the /health endpoint to check service status.",
|
| 30 |
+
"version": VERSION,
|
| 31 |
+
}
|
| 32 |
+
return JSONResponse(status_code=200, content=body)
|
| 33 |
+
|
| 34 |
@app.get("/health")
|
| 35 |
async def health() -> JSONResponse:
|
| 36 |
stt_ready = bool(DEEPGRAM_API_KEY)
|
| 37 |
tts_ready = bool(CARTESIA_API_KEY and CARTESIA_VOICE_ID)
|
| 38 |
gemini_ready = bool(GOOGLE_API_KEY)
|
| 39 |
+
all_ready = stt_ready and tts_ready
|
|
|
|
| 40 |
|
| 41 |
body: dict = {
|
| 42 |
"status": "ok" if all_ready else "degraded",
|
|
|
|
| 45 |
"tts_ready": tts_ready,
|
| 46 |
"gemini_tts_ready": gemini_ready,
|
| 47 |
"gemini_stt_ready": gemini_ready,
|
|
|
|
| 48 |
}
|
| 49 |
if not all_ready:
|
| 50 |
body["message"] = "One or more required configurations are missing. Check your .env file."
|
|
|
|
| 105 |
@app.websocket("/ws/voice")
|
| 106 |
async def voice_ws(
|
| 107 |
ws: WebSocket,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
stt_provider: str = Query(default=STT_PROVIDER),
|
| 109 |
tts_provider: str = Query(default=TTS_PROVIDER),
|
| 110 |
wake_word_enabled: bool = Query(default=WAKE_WORD_ENABLED),
|
| 111 |
) -> None:
|
| 112 |
await ws.accept()
|
| 113 |
logger.info(
|
| 114 |
+
"Client connected: %s (stt=%s, tts=%s)",
|
| 115 |
+
ws.client, stt_provider, tts_provider,
|
| 116 |
)
|
| 117 |
|
| 118 |
async def send_audio(chunk: bytes) -> None:
|
|
|
|
| 134 |
pipeline = VoicePipeline(
|
| 135 |
send_audio=send_audio,
|
| 136 |
send_event=send_event,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
stt_provider=stt_provider,
|
| 138 |
tts_provider=tts_provider,
|
| 139 |
wake_word_enabled=wake_word_enabled,
|
|
|
|
| 155 |
await ws.send_text(json.dumps({"event": "pong"}))
|
| 156 |
elif action == "interrupt":
|
| 157 |
await pipeline.interrupt()
|
| 158 |
+
elif action == "speak":
|
| 159 |
+
text = msg.get("text", "").strip()
|
| 160 |
+
if text:
|
| 161 |
+
await pipeline.speak(text)
|
| 162 |
except json.JSONDecodeError:
|
| 163 |
pass
|
| 164 |
except WebSocketDisconnect:
|
| 165 |
logger.info("Client disconnected: %s", ws.client)
|
| 166 |
finally:
|
| 167 |
+
try:
|
| 168 |
+
await pipeline.stop_async()
|
| 169 |
+
except Exception:
|
| 170 |
+
logger.exception("Error during pipeline stop")
|
| 171 |
|
| 172 |
|
| 173 |
if __name__ == "__main__":
|
pyproject.toml
CHANGED
|
@@ -13,6 +13,10 @@ dependencies = [
|
|
| 13 |
"deepgram-sdk>=6.1.1",
|
| 14 |
"python-multipart>=0.0.26",
|
| 15 |
"google-genai>=1.0.0",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
]
|
| 17 |
|
| 18 |
[project.optional-dependencies]
|
|
@@ -26,3 +30,8 @@ build-backend = "hatchling.build"
|
|
| 26 |
|
| 27 |
[tool.hatch.build.targets.wheel]
|
| 28 |
packages = ["src"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
"deepgram-sdk>=6.1.1",
|
| 14 |
"python-multipart>=0.0.26",
|
| 15 |
"google-genai>=1.0.0",
|
| 16 |
+
"google-generativeai>=0.8.6",
|
| 17 |
+
"google-cloud-texttospeech>=2.29.0",
|
| 18 |
+
"google-cloud-speech>=2.0.0",
|
| 19 |
+
"numpy>=1.26.0",
|
| 20 |
]
|
| 21 |
|
| 22 |
[project.optional-dependencies]
|
|
|
|
| 30 |
|
| 31 |
[tool.hatch.build.targets.wheel]
|
| 32 |
packages = ["src"]
|
| 33 |
+
|
| 34 |
+
[dependency-groups]
|
| 35 |
+
dev = [
|
| 36 |
+
"sounddevice>=0.5.5",
|
| 37 |
+
]
|
src/chatbot/__init__.py
DELETED
|
File without changes
|
src/chatbot/client.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
import uuid
|
| 2 |
-
import logging
|
| 3 |
-
import httpx
|
| 4 |
-
from src.config import CHATBOT_BASE_URL, CHATBOT_TIMEOUT
|
| 5 |
-
|
| 6 |
-
logger = logging.getLogger(__name__)
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
async def call_agent(
|
| 10 |
-
question: str,
|
| 11 |
-
chat_history: list[dict],
|
| 12 |
-
user_id: str = "anonymous",
|
| 13 |
-
fullname: str = "",
|
| 14 |
-
company: str = "",
|
| 15 |
-
function: str = "",
|
| 16 |
-
site: str = "HO",
|
| 17 |
-
role: str = "engineer",
|
| 18 |
-
agent: str = "analysis",
|
| 19 |
-
room_id: str | None = None,
|
| 20 |
-
chat_id: str | None = None,
|
| 21 |
-
) -> str:
|
| 22 |
-
"""
|
| 23 |
-
Call POST /api/agents/call and return audio_text (fallback to content).
|
| 24 |
-
|
| 25 |
-
Raises RuntimeError if CHATBOT_BASE_URL is not configured or the agent
|
| 26 |
-
returns a failed status.
|
| 27 |
-
"""
|
| 28 |
-
if not CHATBOT_BASE_URL:
|
| 29 |
-
raise RuntimeError("CHATBOT_BASE_URL is not configured.")
|
| 30 |
-
|
| 31 |
-
history = chat_history + [{"role": "human", "content": question}]
|
| 32 |
-
|
| 33 |
-
payload = {
|
| 34 |
-
"user_id": user_id,
|
| 35 |
-
"fullname": fullname,
|
| 36 |
-
"company": company,
|
| 37 |
-
"function": function,
|
| 38 |
-
"site": site,
|
| 39 |
-
"role": role,
|
| 40 |
-
"agent": agent,
|
| 41 |
-
"room_id": room_id,
|
| 42 |
-
"chat_id": chat_id or str(uuid.uuid4()),
|
| 43 |
-
"req_plot_object": False,
|
| 44 |
-
"chat_history": history,
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
async with httpx.AsyncClient(timeout=CHATBOT_TIMEOUT) as client:
|
| 48 |
-
resp = await client.post(f"{CHATBOT_BASE_URL}/api/agents/call", json=payload)
|
| 49 |
-
resp.raise_for_status()
|
| 50 |
-
data = resp.json()
|
| 51 |
-
|
| 52 |
-
status = data.get("status") or {}
|
| 53 |
-
if status.get("status") == "failed":
|
| 54 |
-
raise RuntimeError(status.get("message", "Agent call failed"))
|
| 55 |
-
|
| 56 |
-
answer = data.get("answer") or {}
|
| 57 |
-
audio_text: str | None = answer.get("audio_text")
|
| 58 |
-
content: str = answer.get("content", "")
|
| 59 |
-
|
| 60 |
-
return audio_text or content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/config.py
CHANGED
|
@@ -14,9 +14,11 @@ CARTESIA_MODEL: str = os.getenv("CARTESIA_MODEL", "sonic-3")
|
|
| 14 |
|
| 15 |
GOOGLE_API_KEY: str = os.getenv("GOOGLE_API_KEY", "")
|
| 16 |
GOOGLE_PROJECT_NAME: str = os.getenv("GOOGLE_PROJECT_NAME", "")
|
|
|
|
| 17 |
GOOGLE_PROJECT_NUMBER: str = os.getenv("GOOGLE_PROJECT_NUMBER", "")
|
|
|
|
| 18 |
|
| 19 |
-
GEMINI_TTS_MODEL: str = os.getenv("GEMINI_TTS_MODEL", "gemini-2.5-flash-
|
| 20 |
GEMINI_TTS_VOICE: str = os.getenv("GEMINI_TTS_VOICE", "Autonoe")
|
| 21 |
GEMINI_TTS_LANGUAGE: str = os.getenv("GEMINI_TTS_LANGUAGE", "id-ID")
|
| 22 |
|
|
@@ -29,5 +31,5 @@ DEEPGRAM_LANGUAGE: str = os.getenv("DEEPGRAM_LANGUAGE", "id")
|
|
| 29 |
DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
|
| 30 |
DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
|
|
|
| 14 |
|
| 15 |
GOOGLE_API_KEY: str = os.getenv("GOOGLE_API_KEY", "")
|
| 16 |
GOOGLE_PROJECT_NAME: str = os.getenv("GOOGLE_PROJECT_NAME", "")
|
| 17 |
+
GOOGLE_PROJECT_ID: str = os.getenv("GOOGLE_PROJECT_ID", "")
|
| 18 |
GOOGLE_PROJECT_NUMBER: str = os.getenv("GOOGLE_PROJECT_NUMBER", "")
|
| 19 |
+
GOOGLE_CLOUD_LOCATION: str = os.getenv("GOOGLE_CLOUD_LOCATION", "us-central1")
|
| 20 |
|
| 21 |
+
GEMINI_TTS_MODEL: str = os.getenv("GEMINI_TTS_MODEL", "gemini-2.5-flash-tts")
|
| 22 |
GEMINI_TTS_VOICE: str = os.getenv("GEMINI_TTS_VOICE", "Autonoe")
|
| 23 |
GEMINI_TTS_LANGUAGE: str = os.getenv("GEMINI_TTS_LANGUAGE", "id-ID")
|
| 24 |
|
|
|
|
| 31 |
DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
|
| 32 |
DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
|
| 33 |
|
| 34 |
+
CHIRP3_REGION: str = os.getenv("CHIRP3_REGION", "us")
|
| 35 |
+
CHIRP3_LANGUAGE: str = os.getenv("CHIRP3_LANGUAGE", "id-ID")
|
src/pipeline.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
import uuid
|
| 2 |
import asyncio
|
| 3 |
import logging
|
| 4 |
from typing import Callable, Awaitable
|
|
@@ -6,7 +5,6 @@ from src.config import WAKE_WORDS, WAKE_WORD_ENABLED
|
|
| 6 |
from src.stt.deepgram_client import DeepgramStreamer
|
| 7 |
from src.tts.cartesia_client import synthesize_stream as cartesia_synthesize_stream
|
| 8 |
from src.tts.gemini_client import synthesize_stream as gemini_synthesize_stream, GEMINI_SAMPLE_RATE
|
| 9 |
-
from src.chatbot.client import call_agent
|
| 10 |
|
| 11 |
logger = logging.getLogger(__name__)
|
| 12 |
|
|
@@ -27,16 +25,10 @@ class VoicePipeline:
|
|
| 27 |
self,
|
| 28 |
send_audio: SendAudioCallback,
|
| 29 |
send_event: SendEventCallback,
|
| 30 |
-
user_id: str = "anonymous",
|
| 31 |
-
fullname: str = "",
|
| 32 |
-
company: str = "",
|
| 33 |
-
function: str = "",
|
| 34 |
-
site: str = "HO",
|
| 35 |
-
role: str = "engineer",
|
| 36 |
-
agent: str = "analysis",
|
| 37 |
stt_provider: str = "deepgram",
|
| 38 |
tts_provider: str = "cartesia",
|
| 39 |
wake_word_enabled: bool = WAKE_WORD_ENABLED,
|
|
|
|
| 40 |
):
|
| 41 |
self._send_audio = send_audio
|
| 42 |
self._send_event = send_event
|
|
@@ -44,27 +36,18 @@ class VoicePipeline:
|
|
| 44 |
self._tts_provider = tts_provider
|
| 45 |
self._wake_word_enabled = wake_word_enabled
|
| 46 |
|
| 47 |
-
self._user_id = user_id
|
| 48 |
-
self._fullname = fullname
|
| 49 |
-
self._company = company
|
| 50 |
-
self._function = function
|
| 51 |
-
self._site = site
|
| 52 |
-
self._role = role
|
| 53 |
-
self._agent = agent
|
| 54 |
-
|
| 55 |
-
# Stable identifiers for this session
|
| 56 |
-
self._room_id = str(uuid.uuid4())
|
| 57 |
-
self._chat_id = str(uuid.uuid4())
|
| 58 |
-
|
| 59 |
-
# Running chat history for multi-turn context
|
| 60 |
-
self._chat_history: list[dict] = []
|
| 61 |
-
|
| 62 |
if stt_provider == "gemini":
|
| 63 |
from src.stt.gemini_stt import GeminiSTTStreamer
|
| 64 |
self._stt = GeminiSTTStreamer(
|
| 65 |
on_final_transcript=self._on_final_transcript,
|
| 66 |
loop=self._loop,
|
| 67 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
else:
|
| 69 |
self._stt = DeepgramStreamer(
|
| 70 |
on_final_transcript=self._on_final_transcript,
|
|
@@ -80,21 +63,18 @@ class VoicePipeline:
|
|
| 80 |
self._stt.send_audio(chunk)
|
| 81 |
|
| 82 |
async def _on_final_transcript(self, text: str) -> None:
|
| 83 |
-
await self._send_event({"event": "transcript", "text": text})
|
| 84 |
-
|
| 85 |
if self._wake_word_enabled:
|
| 86 |
question = self._extract_question(text)
|
| 87 |
if question is None:
|
| 88 |
logger.debug("No wake word detected, ignoring: %s", text)
|
| 89 |
return
|
| 90 |
logger.info("Wake word detected, question: %s", question)
|
|
|
|
| 91 |
else:
|
| 92 |
-
|
| 93 |
-
if not question:
|
| 94 |
return
|
| 95 |
-
logger.info("
|
| 96 |
-
|
| 97 |
-
self._tts_task = asyncio.create_task(self._answer_and_speak(question))
|
| 98 |
|
| 99 |
def _extract_question(self, transcript: str) -> str | None:
|
| 100 |
lower = transcript.lower()
|
|
@@ -109,36 +89,9 @@ class VoicePipeline:
|
|
| 109 |
question = transcript[idx:].strip(" ,.")
|
| 110 |
return question if question else None
|
| 111 |
|
| 112 |
-
async def
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
question=question,
|
| 116 |
-
chat_history=self._chat_history,
|
| 117 |
-
user_id=self._user_id,
|
| 118 |
-
fullname=self._fullname,
|
| 119 |
-
company=self._company,
|
| 120 |
-
function=self._function,
|
| 121 |
-
site=self._site,
|
| 122 |
-
role=self._role,
|
| 123 |
-
agent=self._agent,
|
| 124 |
-
room_id=self._room_id,
|
| 125 |
-
chat_id=self._chat_id,
|
| 126 |
-
)
|
| 127 |
-
except Exception:
|
| 128 |
-
logger.exception("Chatbot call failed for question: %s", question)
|
| 129 |
-
await self._send_event({
|
| 130 |
-
"event": "error",
|
| 131 |
-
"code": "CHATBOT_ERROR",
|
| 132 |
-
"message": "Failed to get response from chatbot agent.",
|
| 133 |
-
})
|
| 134 |
-
return
|
| 135 |
-
|
| 136 |
-
# Update history for multi-turn context
|
| 137 |
-
self._chat_history.append({"role": "human", "content": question})
|
| 138 |
-
self._chat_history.append({"role": "ai", "content": reply_text})
|
| 139 |
-
|
| 140 |
-
await self._send_event({"event": "reply", "text": reply_text})
|
| 141 |
-
await self._speak(reply_text)
|
| 142 |
|
| 143 |
async def _speak(self, text: str) -> None:
|
| 144 |
async with self._tts_lock:
|
|
@@ -176,6 +129,14 @@ class VoicePipeline:
|
|
| 176 |
self._tts_task.cancel()
|
| 177 |
self._stt.stop()
|
| 178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
# Backward-compatible alias
|
| 181 |
EchoPipeline = VoicePipeline
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import logging
|
| 3 |
from typing import Callable, Awaitable
|
|
|
|
| 5 |
from src.stt.deepgram_client import DeepgramStreamer
|
| 6 |
from src.tts.cartesia_client import synthesize_stream as cartesia_synthesize_stream
|
| 7 |
from src.tts.gemini_client import synthesize_stream as gemini_synthesize_stream, GEMINI_SAMPLE_RATE
|
|
|
|
| 8 |
|
| 9 |
logger = logging.getLogger(__name__)
|
| 10 |
|
|
|
|
| 25 |
self,
|
| 26 |
send_audio: SendAudioCallback,
|
| 27 |
send_event: SendEventCallback,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
stt_provider: str = "deepgram",
|
| 29 |
tts_provider: str = "cartesia",
|
| 30 |
wake_word_enabled: bool = WAKE_WORD_ENABLED,
|
| 31 |
+
**kwargs,
|
| 32 |
):
|
| 33 |
self._send_audio = send_audio
|
| 34 |
self._send_event = send_event
|
|
|
|
| 36 |
self._tts_provider = tts_provider
|
| 37 |
self._wake_word_enabled = wake_word_enabled
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
if stt_provider == "gemini":
|
| 40 |
from src.stt.gemini_stt import GeminiSTTStreamer
|
| 41 |
self._stt = GeminiSTTStreamer(
|
| 42 |
on_final_transcript=self._on_final_transcript,
|
| 43 |
loop=self._loop,
|
| 44 |
)
|
| 45 |
+
elif stt_provider == "chirp3":
|
| 46 |
+
from src.stt.chirp3_client import Chirp3STTStreamer
|
| 47 |
+
self._stt = Chirp3STTStreamer(
|
| 48 |
+
on_final_transcript=self._on_final_transcript,
|
| 49 |
+
loop=self._loop,
|
| 50 |
+
)
|
| 51 |
else:
|
| 52 |
self._stt = DeepgramStreamer(
|
| 53 |
on_final_transcript=self._on_final_transcript,
|
|
|
|
| 63 |
self._stt.send_audio(chunk)
|
| 64 |
|
| 65 |
async def _on_final_transcript(self, text: str) -> None:
|
|
|
|
|
|
|
| 66 |
if self._wake_word_enabled:
|
| 67 |
question = self._extract_question(text)
|
| 68 |
if question is None:
|
| 69 |
logger.debug("No wake word detected, ignoring: %s", text)
|
| 70 |
return
|
| 71 |
logger.info("Wake word detected, question: %s", question)
|
| 72 |
+
await self._send_event({"event": "transcript", "text": question})
|
| 73 |
else:
|
| 74 |
+
if not text.strip():
|
|
|
|
| 75 |
return
|
| 76 |
+
logger.info("Transcript: %s", text)
|
| 77 |
+
await self._send_event({"event": "transcript", "text": text})
|
|
|
|
| 78 |
|
| 79 |
def _extract_question(self, transcript: str) -> str | None:
|
| 80 |
lower = transcript.lower()
|
|
|
|
| 89 |
question = transcript[idx:].strip(" ,.")
|
| 90 |
return question if question else None
|
| 91 |
|
| 92 |
+
async def speak(self, text: str) -> None:
|
| 93 |
+
"""Called by the WebSocket handler when the FE sends a speak action."""
|
| 94 |
+
self._tts_task = asyncio.create_task(self._speak(text))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
async def _speak(self, text: str) -> None:
|
| 97 |
async with self._tts_lock:
|
|
|
|
| 129 |
self._tts_task.cancel()
|
| 130 |
self._stt.stop()
|
| 131 |
|
| 132 |
+
async def stop_async(self) -> None:
|
| 133 |
+
if self._tts_task and not self._tts_task.done():
|
| 134 |
+
self._tts_task.cancel()
|
| 135 |
+
if hasattr(self._stt, "stop_and_transcribe"):
|
| 136 |
+
await self._stt.stop_and_transcribe()
|
| 137 |
+
else:
|
| 138 |
+
self._stt.stop()
|
| 139 |
+
|
| 140 |
|
| 141 |
# Backward-compatible alias
|
| 142 |
EchoPipeline = VoicePipeline
|
src/stt/chirp3_client.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
import wave
|
| 3 |
+
import asyncio
|
| 4 |
+
import logging
|
| 5 |
+
from typing import Callable, Awaitable
|
| 6 |
+
|
| 7 |
+
from google.cloud.speech_v2 import SpeechClient
|
| 8 |
+
from google.cloud.speech_v2.types import cloud_speech as cloud_speech_types
|
| 9 |
+
from google.api_core.client_options import ClientOptions
|
| 10 |
+
|
| 11 |
+
from src.config import GOOGLE_PROJECT_ID, CHIRP3_REGION, CHIRP3_LANGUAGE
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
OnTranscriptCallback = Callable[[str], Awaitable[None]]
|
| 16 |
+
|
| 17 |
+
_CHUNK_SIZE = 24 * 1024 # stay under the 25 KB gRPC streaming limit
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Chirp3STTStreamer:
|
| 21 |
+
"""
|
| 22 |
+
Push-to-talk STT using Google Cloud Speech-to-Text V2 streaming with Chirp 3 model.
|
| 23 |
+
|
| 24 |
+
Interface matches GeminiSTTStreamer: start(), send_audio(chunk), stop(), stop_and_transcribe().
|
| 25 |
+
Audio chunks (PCM Linear16 16kHz mono) are buffered, then streamed to Chirp 3 in 24 KB
|
| 26 |
+
pieces via streaming_recognize() on stop_and_transcribe().
|
| 27 |
+
|
| 28 |
+
Authentication: Application Default Credentials (ADC).
|
| 29 |
+
Set GOOGLE_APPLICATION_CREDENTIALS or run: gcloud auth application-default login
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
def __init__(self, on_final_transcript: OnTranscriptCallback, loop: asyncio.AbstractEventLoop):
|
| 33 |
+
self._on_final_transcript = on_final_transcript
|
| 34 |
+
self._loop = loop
|
| 35 |
+
self._audio_chunks: list[bytes] = []
|
| 36 |
+
|
| 37 |
+
def start(self) -> None:
|
| 38 |
+
self._audio_chunks = []
|
| 39 |
+
logger.info("Chirp 3 STT ready (region=%s, language=%s)", CHIRP3_REGION, CHIRP3_LANGUAGE)
|
| 40 |
+
|
| 41 |
+
def send_audio(self, chunk: bytes) -> None:
|
| 42 |
+
self._audio_chunks.append(chunk)
|
| 43 |
+
|
| 44 |
+
def stop(self) -> None:
|
| 45 |
+
logger.info("Chirp 3 STT stopped")
|
| 46 |
+
|
| 47 |
+
async def stop_and_transcribe(self) -> None:
|
| 48 |
+
audio_data = b"".join(self._audio_chunks)
|
| 49 |
+
self._audio_chunks = []
|
| 50 |
+
|
| 51 |
+
if not audio_data:
|
| 52 |
+
logger.info("Chirp 3 STT: no audio received, skipping transcription")
|
| 53 |
+
return
|
| 54 |
+
|
| 55 |
+
logger.info("Chirp 3 STT transcribing %d bytes of audio...", len(audio_data))
|
| 56 |
+
wav_data = self._pcm_to_wav(audio_data)
|
| 57 |
+
|
| 58 |
+
try:
|
| 59 |
+
text = await asyncio.get_event_loop().run_in_executor(
|
| 60 |
+
None, self._recognize, wav_data
|
| 61 |
+
)
|
| 62 |
+
if text:
|
| 63 |
+
await self._on_final_transcript(text)
|
| 64 |
+
except Exception:
|
| 65 |
+
logger.exception("Chirp 3 STT transcription failed")
|
| 66 |
+
|
| 67 |
+
def _recognize(self, wav_data: bytes) -> str:
|
| 68 |
+
client = SpeechClient(
|
| 69 |
+
client_options=ClientOptions(
|
| 70 |
+
api_endpoint=f"{CHIRP3_REGION}-speech.googleapis.com"
|
| 71 |
+
)
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
chunks = [
|
| 75 |
+
wav_data[i : i + _CHUNK_SIZE]
|
| 76 |
+
for i in range(0, len(wav_data), _CHUNK_SIZE)
|
| 77 |
+
]
|
| 78 |
+
audio_requests = (
|
| 79 |
+
cloud_speech_types.StreamingRecognizeRequest(audio=chunk)
|
| 80 |
+
for chunk in chunks
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
recognition_config = cloud_speech_types.RecognitionConfig(
|
| 84 |
+
auto_decoding_config=cloud_speech_types.AutoDetectDecodingConfig(),
|
| 85 |
+
language_codes=[CHIRP3_LANGUAGE],
|
| 86 |
+
model="chirp_3",
|
| 87 |
+
)
|
| 88 |
+
streaming_config = cloud_speech_types.StreamingRecognitionConfig(
|
| 89 |
+
config=recognition_config
|
| 90 |
+
)
|
| 91 |
+
config_request = cloud_speech_types.StreamingRecognizeRequest(
|
| 92 |
+
recognizer=f"projects/{GOOGLE_PROJECT_ID}/locations/{CHIRP3_REGION}/recognizers/_",
|
| 93 |
+
streaming_config=streaming_config,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
def requests():
|
| 97 |
+
yield config_request
|
| 98 |
+
yield from audio_requests
|
| 99 |
+
|
| 100 |
+
transcripts: list[str] = []
|
| 101 |
+
for response in client.streaming_recognize(requests=requests()):
|
| 102 |
+
for result in response.results:
|
| 103 |
+
if result.alternatives:
|
| 104 |
+
transcripts.append(result.alternatives[0].transcript)
|
| 105 |
+
|
| 106 |
+
transcript = " ".join(transcripts).strip()
|
| 107 |
+
if transcript:
|
| 108 |
+
logger.info("Chirp 3 STT transcript: %s", transcript[:80])
|
| 109 |
+
return transcript
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
def _pcm_to_wav(pcm_data: bytes) -> bytes:
|
| 113 |
+
buf = io.BytesIO()
|
| 114 |
+
with wave.open(buf, "wb") as wf:
|
| 115 |
+
wf.setnchannels(1)
|
| 116 |
+
wf.setsampwidth(2)
|
| 117 |
+
wf.setframerate(16000)
|
| 118 |
+
wf.writeframes(pcm_data)
|
| 119 |
+
return buf.getvalue()
|
src/stt/gemini_stt.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import logging
|
| 3 |
from typing import Callable, Awaitable
|
|
@@ -7,10 +9,10 @@ from google.genai import types
|
|
| 7 |
|
| 8 |
from src.config import (
|
| 9 |
GOOGLE_API_KEY,
|
|
|
|
|
|
|
| 10 |
GEMINI_STT_MODEL,
|
| 11 |
-
GEMINI_LIVE_MODEL,
|
| 12 |
GEMINI_STT_LANGUAGE,
|
| 13 |
-
DEEPGRAM_UTTERANCE_END_MS,
|
| 14 |
)
|
| 15 |
|
| 16 |
logger = logging.getLogger(__name__)
|
|
@@ -23,7 +25,10 @@ async def transcribe_audio(data: bytes, mimetype: str = "audio/wav") -> dict:
|
|
| 23 |
Transcribes a full audio file using Gemini multimodal API.
|
| 24 |
Returns dict with 'text', 'language', and 'duration'.
|
| 25 |
"""
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
| 27 |
response = await client.aio.models.generate_content(
|
| 28 |
model=GEMINI_STT_MODEL,
|
| 29 |
contents=[
|
|
@@ -39,99 +44,53 @@ async def transcribe_audio(data: bytes, mimetype: str = "audio/wav") -> dict:
|
|
| 39 |
|
| 40 |
class GeminiSTTStreamer:
|
| 41 |
"""
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
Interface matches DeepgramStreamer: start(), send_audio(chunk), stop().
|
| 45 |
-
Audio chunks must be PCM Linear16 16kHz mono bytes.
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
|
|
|
| 49 |
"""
|
| 50 |
|
| 51 |
def __init__(self, on_final_transcript: OnTranscriptCallback, loop: asyncio.AbstractEventLoop):
|
| 52 |
self._on_final_transcript = on_final_transcript
|
| 53 |
self._loop = loop
|
| 54 |
-
self.
|
| 55 |
-
self._session_task: asyncio.Task | None = None
|
| 56 |
-
self._transcript_buffer: list[str] = []
|
| 57 |
-
self._flush_handle: asyncio.TimerHandle | None = None
|
| 58 |
|
| 59 |
def start(self) -> None:
|
| 60 |
-
self.
|
| 61 |
-
|
| 62 |
-
logger.info("Gemini Live STT starting...")
|
| 63 |
|
| 64 |
def send_audio(self, chunk: bytes) -> None:
|
| 65 |
-
|
| 66 |
-
self._audio_queue.put_nowait(chunk)
|
| 67 |
|
| 68 |
def stop(self) -> None:
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
config = types.LiveConnectConfig(
|
| 82 |
-
response_modalities=["TEXT"],
|
| 83 |
-
input_audio_transcription=types.AudioTranscriptionConfig(),
|
| 84 |
-
)
|
| 85 |
-
try:
|
| 86 |
-
async with client.aio.live.connect(model=GEMINI_LIVE_MODEL, config=config) as session:
|
| 87 |
-
logger.info("Gemini Live STT connected")
|
| 88 |
-
await asyncio.gather(
|
| 89 |
-
self._send_loop(session),
|
| 90 |
-
self._receive_loop(session),
|
| 91 |
-
)
|
| 92 |
-
except asyncio.CancelledError:
|
| 93 |
-
pass
|
| 94 |
-
except Exception:
|
| 95 |
-
logger.exception("Gemini Live session error")
|
| 96 |
|
| 97 |
-
async def _send_loop(self, session) -> None:
|
| 98 |
-
try:
|
| 99 |
-
while True:
|
| 100 |
-
chunk = await self._audio_queue.get()
|
| 101 |
-
if chunk is None:
|
| 102 |
-
break
|
| 103 |
-
await session.send_realtime_input(
|
| 104 |
-
audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000")
|
| 105 |
-
)
|
| 106 |
-
except asyncio.CancelledError:
|
| 107 |
-
raise
|
| 108 |
-
|
| 109 |
-
async def _receive_loop(self, session) -> None:
|
| 110 |
try:
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
if transcription and transcription.text:
|
| 116 |
-
self._buffer_transcript(transcription.text)
|
| 117 |
-
except asyncio.CancelledError:
|
| 118 |
-
raise
|
| 119 |
except Exception:
|
| 120 |
-
logger.exception("Gemini
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
if not self._transcript_buffer:
|
| 132 |
-
return
|
| 133 |
-
full_text = " ".join(self._transcript_buffer)
|
| 134 |
-
self._transcript_buffer.clear()
|
| 135 |
-
self._flush_handle = None
|
| 136 |
-
logger.info("Final transcript: %s", full_text)
|
| 137 |
-
asyncio.ensure_future(self._on_final_transcript(full_text), loop=self._loop)
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
import wave
|
| 3 |
import asyncio
|
| 4 |
import logging
|
| 5 |
from typing import Callable, Awaitable
|
|
|
|
| 9 |
|
| 10 |
from src.config import (
|
| 11 |
GOOGLE_API_KEY,
|
| 12 |
+
GOOGLE_PROJECT_NAME,
|
| 13 |
+
GOOGLE_CLOUD_LOCATION,
|
| 14 |
GEMINI_STT_MODEL,
|
|
|
|
| 15 |
GEMINI_STT_LANGUAGE,
|
|
|
|
| 16 |
)
|
| 17 |
|
| 18 |
logger = logging.getLogger(__name__)
|
|
|
|
| 25 |
Transcribes a full audio file using Gemini multimodal API.
|
| 26 |
Returns dict with 'text', 'language', and 'duration'.
|
| 27 |
"""
|
| 28 |
+
if GOOGLE_API_KEY:
|
| 29 |
+
client = genai.Client(api_key=GOOGLE_API_KEY)
|
| 30 |
+
else:
|
| 31 |
+
client = genai.Client(vertexai=True, project=GOOGLE_PROJECT_NAME, location=GOOGLE_CLOUD_LOCATION)
|
| 32 |
response = await client.aio.models.generate_content(
|
| 33 |
model=GEMINI_STT_MODEL,
|
| 34 |
contents=[
|
|
|
|
| 44 |
|
| 45 |
class GeminiSTTStreamer:
|
| 46 |
"""
|
| 47 |
+
Push-to-talk STT using Gemini batch transcription.
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
Interface matches DeepgramStreamer: start(), send_audio(chunk), stop_and_transcribe().
|
| 50 |
+
Audio chunks (PCM Linear16 16kHz mono) are buffered during the session.
|
| 51 |
+
On stop_and_transcribe(), all buffered audio is sent to Gemini as a single WAV request.
|
| 52 |
"""
|
| 53 |
|
| 54 |
def __init__(self, on_final_transcript: OnTranscriptCallback, loop: asyncio.AbstractEventLoop):
|
| 55 |
self._on_final_transcript = on_final_transcript
|
| 56 |
self._loop = loop
|
| 57 |
+
self._audio_chunks: list[bytes] = []
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
def start(self) -> None:
|
| 60 |
+
self._audio_chunks = []
|
| 61 |
+
logger.info("Gemini STT ready")
|
|
|
|
| 62 |
|
| 63 |
def send_audio(self, chunk: bytes) -> None:
|
| 64 |
+
self._audio_chunks.append(chunk)
|
|
|
|
| 65 |
|
| 66 |
def stop(self) -> None:
|
| 67 |
+
logger.info("Gemini STT stopped")
|
| 68 |
+
|
| 69 |
+
async def stop_and_transcribe(self) -> None:
|
| 70 |
+
audio_data = b"".join(self._audio_chunks)
|
| 71 |
+
self._audio_chunks = []
|
| 72 |
+
|
| 73 |
+
if not audio_data:
|
| 74 |
+
logger.info("Gemini STT: no audio received, skipping transcription")
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
+
logger.info("Gemini STT transcribing %d bytes of audio...", len(audio_data))
|
| 78 |
+
wav_data = self._pcm_to_wav(audio_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
try:
|
| 81 |
+
result = await transcribe_audio(wav_data, mimetype="audio/wav")
|
| 82 |
+
text = result.get("text", "").strip()
|
| 83 |
+
if text:
|
| 84 |
+
await self._on_final_transcript(text)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
except Exception:
|
| 86 |
+
logger.exception("Gemini STT batch transcription failed")
|
| 87 |
+
|
| 88 |
+
@staticmethod
|
| 89 |
+
def _pcm_to_wav(pcm_data: bytes) -> bytes:
|
| 90 |
+
buf = io.BytesIO()
|
| 91 |
+
with wave.open(buf, "wb") as wf:
|
| 92 |
+
wf.setnchannels(1)
|
| 93 |
+
wf.setsampwidth(2)
|
| 94 |
+
wf.setframerate(16000)
|
| 95 |
+
wf.writeframes(pcm_data)
|
| 96 |
+
return buf.getvalue()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/tts/gemini_client.py
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
|
|
| 1 |
import logging
|
| 2 |
from typing import AsyncIterator
|
| 3 |
-
|
| 4 |
-
from google.
|
| 5 |
-
|
|
|
|
| 6 |
|
| 7 |
logger = logging.getLogger(__name__)
|
| 8 |
|
| 9 |
-
GEMINI_SAMPLE_RATE = 24000 # Gemini outputs PCM Linear16 at 24kHz
|
| 10 |
|
| 11 |
|
| 12 |
async def synthesize_stream(text: str) -> AsyncIterator[bytes]:
|
| 13 |
-
"""Calls
|
| 14 |
-
client = genai.Client(api_key=GOOGLE_API_KEY)
|
| 15 |
logger.info("Gemini TTS: synthesizing '%s'", text[:60])
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
yield chunk.candidates[0].content.parts[0].inline_data.data
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
import logging
|
| 3 |
from typing import AsyncIterator
|
| 4 |
+
|
| 5 |
+
from google.cloud import texttospeech
|
| 6 |
+
|
| 7 |
+
from src.config import GEMINI_TTS_MODEL, GEMINI_TTS_VOICE, GEMINI_TTS_LANGUAGE
|
| 8 |
|
| 9 |
logger = logging.getLogger(__name__)
|
| 10 |
|
| 11 |
+
GEMINI_SAMPLE_RATE = 24000 # Cloud TTS Gemini model outputs PCM Linear16 at 24kHz
|
| 12 |
|
| 13 |
|
| 14 |
async def synthesize_stream(text: str) -> AsyncIterator[bytes]:
|
| 15 |
+
"""Calls Cloud TTS Gemini model and yields raw PCM Linear16 chunks at 24kHz."""
|
|
|
|
| 16 |
logger.info("Gemini TTS: synthesizing '%s'", text[:60])
|
| 17 |
|
| 18 |
+
def _collect() -> list[bytes]:
|
| 19 |
+
client = texttospeech.TextToSpeechClient()
|
| 20 |
+
config_req = texttospeech.StreamingSynthesizeRequest(
|
| 21 |
+
streaming_config=texttospeech.StreamingSynthesizeConfig(
|
| 22 |
+
voice=texttospeech.VoiceSelectionParams(
|
| 23 |
+
name=GEMINI_TTS_VOICE,
|
| 24 |
+
language_code=GEMINI_TTS_LANGUAGE,
|
| 25 |
+
model_name=GEMINI_TTS_MODEL,
|
| 26 |
+
)
|
| 27 |
+
)
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
def _gen():
|
| 31 |
+
yield config_req
|
| 32 |
+
yield texttospeech.StreamingSynthesizeRequest(
|
| 33 |
+
input=texttospeech.StreamingSynthesisInput(text=text)
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
return [r.audio_content for r in client.streaming_synthesize(_gen())]
|
| 37 |
+
|
| 38 |
+
for chunk in await asyncio.to_thread(_collect):
|
| 39 |
+
yield chunk
|
|
|
uv.lock
CHANGED
|
@@ -1,6 +1,11 @@
|
|
| 1 |
version = 1
|
| 2 |
revision = 2
|
| 3 |
requires-python = ">=3.11"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
[[package]]
|
| 6 |
name = "aiohappyeyeballs"
|
|
@@ -571,6 +576,90 @@ wheels = [
|
|
| 571 |
{ url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
|
| 572 |
]
|
| 573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
[[package]]
|
| 575 |
name = "google-auth"
|
| 576 |
version = "2.49.2"
|
|
@@ -584,6 +673,53 @@ wheels = [
|
|
| 584 |
{ url = "https://files.pythonhosted.org/packages/73/76/d241a5c927433420507215df6cac1b1fa4ac0ba7a794df42a84326c68da8/google_auth-2.49.2-py3-none-any.whl", hash = "sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5", size = 240638, upload-time = "2026-04-10T00:41:14.501Z" },
|
| 585 |
]
|
| 586 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 587 |
[[package]]
|
| 588 |
name = "google-genai"
|
| 589 |
version = "1.2.0"
|
|
@@ -599,6 +735,102 @@ wheels = [
|
|
| 599 |
{ url = "https://files.pythonhosted.org/packages/0d/ed/985f2d2e2b5fbd912ab0fdb11d6dc48c22553a6c4edffabb8146d53b974a/google_genai-1.2.0-py3-none-any.whl", hash = "sha256:609d61bee73f1a6ae5b47e9c7dd4b469d50318f050c5ceacf835b0f80f79d2d9", size = 130744, upload-time = "2025-02-12T16:40:03.601Z" },
|
| 600 |
]
|
| 601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 602 |
[[package]]
|
| 603 |
name = "h11"
|
| 604 |
version = "0.16.0"
|
|
@@ -621,6 +853,18 @@ wheels = [
|
|
| 621 |
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
| 622 |
]
|
| 623 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
[[package]]
|
| 625 |
name = "httptools"
|
| 626 |
version = "0.7.1"
|
|
@@ -808,6 +1052,85 @@ wheels = [
|
|
| 808 |
{ url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" },
|
| 809 |
]
|
| 810 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 |
[[package]]
|
| 812 |
name = "propcache"
|
| 813 |
version = "0.4.1"
|
|
@@ -907,6 +1230,32 @@ wheels = [
|
|
| 907 |
{ url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
|
| 908 |
]
|
| 909 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 |
[[package]]
|
| 911 |
name = "pyasn1"
|
| 912 |
version = "0.6.3"
|
|
@@ -1054,6 +1403,15 @@ wheels = [
|
|
| 1054 |
{ url = "https://files.pythonhosted.org/packages/ef/d2/d2b0025246481aa2ce6db8ba196e29b92063343ac76e675b3a1fa478ed4d/pydantic_core-2.46.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:375cfdd2a1049910c82ba2ff24f948e93599a529e0fdb066d747975ca31fc663", size = 2190970, upload-time = "2026-04-15T14:49:33.111Z" },
|
| 1055 |
]
|
| 1056 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1057 |
[[package]]
|
| 1058 |
name = "python-dotenv"
|
| 1059 |
version = "1.0.1"
|
|
@@ -1151,6 +1509,22 @@ wheels = [
|
|
| 1151 |
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
| 1152 |
]
|
| 1153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1154 |
[[package]]
|
| 1155 |
name = "starlette"
|
| 1156 |
version = "0.38.6"
|
|
@@ -1163,6 +1537,18 @@ wheels = [
|
|
| 1163 |
{ url = "https://files.pythonhosted.org/packages/b7/9c/93f7bc03ff03199074e81974cc148908ead60dcf189f68ba1761a0ee35cf/starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05", size = 71451, upload-time = "2024-09-22T17:01:43.076Z" },
|
| 1164 |
]
|
| 1165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1166 |
[[package]]
|
| 1167 |
name = "typing-extensions"
|
| 1168 |
version = "4.15.0"
|
|
@@ -1184,6 +1570,15 @@ wheels = [
|
|
| 1184 |
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
| 1185 |
]
|
| 1186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1187 |
[[package]]
|
| 1188 |
name = "urllib3"
|
| 1189 |
version = "2.6.3"
|
|
@@ -1264,8 +1659,12 @@ dependencies = [
|
|
| 1264 |
{ name = "cartesia" },
|
| 1265 |
{ name = "deepgram-sdk" },
|
| 1266 |
{ name = "fastapi" },
|
|
|
|
|
|
|
| 1267 |
{ name = "google-genai" },
|
|
|
|
| 1268 |
{ name = "httpx" },
|
|
|
|
| 1269 |
{ name = "python-dotenv" },
|
| 1270 |
{ name = "python-multipart" },
|
| 1271 |
{ name = "uvicorn", extra = ["standard"] },
|
|
@@ -1277,14 +1676,23 @@ dev = [
|
|
| 1277 |
{ name = "websockets" },
|
| 1278 |
]
|
| 1279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1280 |
[package.metadata]
|
| 1281 |
requires-dist = [
|
| 1282 |
{ name = "assemblyai", specifier = "==0.33.0" },
|
| 1283 |
{ name = "cartesia", specifier = "==1.3.1" },
|
| 1284 |
{ name = "deepgram-sdk", specifier = ">=6.1.1" },
|
| 1285 |
{ name = "fastapi", specifier = "==0.115.0" },
|
|
|
|
|
|
|
| 1286 |
{ name = "google-genai", specifier = ">=1.0.0" },
|
|
|
|
| 1287 |
{ name = "httpx", specifier = "==0.27.2" },
|
|
|
|
| 1288 |
{ name = "python-dotenv", specifier = "==1.0.1" },
|
| 1289 |
{ name = "python-multipart", specifier = ">=0.0.26" },
|
| 1290 |
{ name = "uvicorn", extras = ["standard"], specifier = "==0.30.6" },
|
|
@@ -1293,6 +1701,9 @@ requires-dist = [
|
|
| 1293 |
]
|
| 1294 |
provides-extras = ["dev"]
|
| 1295 |
|
|
|
|
|
|
|
|
|
|
| 1296 |
[[package]]
|
| 1297 |
name = "watchfiles"
|
| 1298 |
version = "1.1.1"
|
|
|
|
| 1 |
version = 1
|
| 2 |
revision = 2
|
| 3 |
requires-python = ">=3.11"
|
| 4 |
+
resolution-markers = [
|
| 5 |
+
"python_full_version >= '3.14'",
|
| 6 |
+
"python_full_version == '3.13.*'",
|
| 7 |
+
"python_full_version < '3.13'",
|
| 8 |
+
]
|
| 9 |
|
| 10 |
[[package]]
|
| 11 |
name = "aiohappyeyeballs"
|
|
|
|
| 576 |
{ url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
|
| 577 |
]
|
| 578 |
|
| 579 |
+
[[package]]
|
| 580 |
+
name = "google-ai-generativelanguage"
|
| 581 |
+
version = "0.6.15"
|
| 582 |
+
source = { registry = "https://pypi.org/simple" }
|
| 583 |
+
dependencies = [
|
| 584 |
+
{ name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version >= '3.14'" },
|
| 585 |
+
{ name = "google-api-core", version = "2.30.3", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version < '3.14'" },
|
| 586 |
+
{ name = "google-auth" },
|
| 587 |
+
{ name = "proto-plus" },
|
| 588 |
+
{ name = "protobuf" },
|
| 589 |
+
]
|
| 590 |
+
sdist = { url = "https://files.pythonhosted.org/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3", size = 1375443, upload-time = "2025-01-13T21:50:47.459Z" }
|
| 591 |
+
wheels = [
|
| 592 |
+
{ url = "https://files.pythonhosted.org/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c", size = 1327356, upload-time = "2025-01-13T21:50:44.174Z" },
|
| 593 |
+
]
|
| 594 |
+
|
| 595 |
+
[[package]]
|
| 596 |
+
name = "google-api-core"
|
| 597 |
+
version = "2.25.2"
|
| 598 |
+
source = { registry = "https://pypi.org/simple" }
|
| 599 |
+
resolution-markers = [
|
| 600 |
+
"python_full_version >= '3.14'",
|
| 601 |
+
]
|
| 602 |
+
dependencies = [
|
| 603 |
+
{ name = "google-auth", marker = "python_full_version >= '3.14'" },
|
| 604 |
+
{ name = "googleapis-common-protos", marker = "python_full_version >= '3.14'" },
|
| 605 |
+
{ name = "proto-plus", marker = "python_full_version >= '3.14'" },
|
| 606 |
+
{ name = "protobuf", marker = "python_full_version >= '3.14'" },
|
| 607 |
+
{ name = "requests", marker = "python_full_version >= '3.14'" },
|
| 608 |
+
]
|
| 609 |
+
sdist = { url = "https://files.pythonhosted.org/packages/09/cd/63f1557235c2440fe0577acdbc32577c5c002684c58c7f4d770a92366a24/google_api_core-2.25.2.tar.gz", hash = "sha256:1c63aa6af0d0d5e37966f157a77f9396d820fba59f9e43e9415bc3dc5baff300", size = 166266, upload-time = "2025-10-03T00:07:34.778Z" }
|
| 610 |
+
wheels = [
|
| 611 |
+
{ url = "https://files.pythonhosted.org/packages/c8/d8/894716a5423933f5c8d2d5f04b16f052a515f78e815dab0c2c6f1fd105dc/google_api_core-2.25.2-py3-none-any.whl", hash = "sha256:e9a8f62d363dc8424a8497f4c2a47d6bcda6c16514c935629c257ab5d10210e7", size = 162489, upload-time = "2025-10-03T00:07:32.924Z" },
|
| 612 |
+
]
|
| 613 |
+
|
| 614 |
+
[package.optional-dependencies]
|
| 615 |
+
grpc = [
|
| 616 |
+
{ name = "grpcio", marker = "python_full_version >= '3.14'" },
|
| 617 |
+
{ name = "grpcio-status", marker = "python_full_version >= '3.14'" },
|
| 618 |
+
]
|
| 619 |
+
|
| 620 |
+
[[package]]
|
| 621 |
+
name = "google-api-core"
|
| 622 |
+
version = "2.30.3"
|
| 623 |
+
source = { registry = "https://pypi.org/simple" }
|
| 624 |
+
resolution-markers = [
|
| 625 |
+
"python_full_version == '3.13.*'",
|
| 626 |
+
"python_full_version < '3.13'",
|
| 627 |
+
]
|
| 628 |
+
dependencies = [
|
| 629 |
+
{ name = "google-auth", marker = "python_full_version < '3.14'" },
|
| 630 |
+
{ name = "googleapis-common-protos", marker = "python_full_version < '3.14'" },
|
| 631 |
+
{ name = "proto-plus", marker = "python_full_version < '3.14'" },
|
| 632 |
+
{ name = "protobuf", marker = "python_full_version < '3.14'" },
|
| 633 |
+
{ name = "requests", marker = "python_full_version < '3.14'" },
|
| 634 |
+
]
|
| 635 |
+
sdist = { url = "https://files.pythonhosted.org/packages/16/ce/502a57fb0ec752026d24df1280b162294b22a0afb98a326084f9a979138b/google_api_core-2.30.3.tar.gz", hash = "sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b", size = 177001, upload-time = "2026-04-10T00:41:28.035Z" }
|
| 636 |
+
wheels = [
|
| 637 |
+
{ url = "https://files.pythonhosted.org/packages/03/15/e56f351cf6ef1cfea58e6ac226a7318ed1deb2218c4b3cc9bd9e4b786c5a/google_api_core-2.30.3-py3-none-any.whl", hash = "sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8", size = 173274, upload-time = "2026-04-09T22:57:16.198Z" },
|
| 638 |
+
]
|
| 639 |
+
|
| 640 |
+
[package.optional-dependencies]
|
| 641 |
+
grpc = [
|
| 642 |
+
{ name = "grpcio", marker = "python_full_version < '3.14'" },
|
| 643 |
+
{ name = "grpcio-status", marker = "python_full_version < '3.14'" },
|
| 644 |
+
]
|
| 645 |
+
|
| 646 |
+
[[package]]
|
| 647 |
+
name = "google-api-python-client"
|
| 648 |
+
version = "2.194.0"
|
| 649 |
+
source = { registry = "https://pypi.org/simple" }
|
| 650 |
+
dependencies = [
|
| 651 |
+
{ name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
| 652 |
+
{ name = "google-api-core", version = "2.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
| 653 |
+
{ name = "google-auth" },
|
| 654 |
+
{ name = "google-auth-httplib2" },
|
| 655 |
+
{ name = "httplib2" },
|
| 656 |
+
{ name = "uritemplate" },
|
| 657 |
+
]
|
| 658 |
+
sdist = { url = "https://files.pythonhosted.org/packages/60/ab/e83af0eb043e4ccc49571ca7a6a49984e9d00f4e9e6e6f1238d60bc84dce/google_api_python_client-2.194.0.tar.gz", hash = "sha256:db92647bd1a90f40b79c9618461553c2b20b6a43ce7395fa6de07132dc14f023", size = 14443469, upload-time = "2026-04-08T23:07:35.757Z" }
|
| 659 |
+
wheels = [
|
| 660 |
+
{ url = "https://files.pythonhosted.org/packages/b0/34/5a624e49f179aa5b0cb87b2ce8093960299030ff40423bfbde09360eb908/google_api_python_client-2.194.0-py3-none-any.whl", hash = "sha256:61eaaac3b8fc8fdf11c08af87abc3d1342d1b37319cc1b57405f86ef7697e717", size = 15016514, upload-time = "2026-04-08T23:07:33.093Z" },
|
| 661 |
+
]
|
| 662 |
+
|
| 663 |
[[package]]
|
| 664 |
name = "google-auth"
|
| 665 |
version = "2.49.2"
|
|
|
|
| 673 |
{ url = "https://files.pythonhosted.org/packages/73/76/d241a5c927433420507215df6cac1b1fa4ac0ba7a794df42a84326c68da8/google_auth-2.49.2-py3-none-any.whl", hash = "sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5", size = 240638, upload-time = "2026-04-10T00:41:14.501Z" },
|
| 674 |
]
|
| 675 |
|
| 676 |
+
[[package]]
|
| 677 |
+
name = "google-auth-httplib2"
|
| 678 |
+
version = "0.3.1"
|
| 679 |
+
source = { registry = "https://pypi.org/simple" }
|
| 680 |
+
dependencies = [
|
| 681 |
+
{ name = "google-auth" },
|
| 682 |
+
{ name = "httplib2" },
|
| 683 |
+
]
|
| 684 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ed/99/107612bef8d24b298bb5a7c8466f908ecda791d43f9466f5c3978f5b24c1/google_auth_httplib2-0.3.1.tar.gz", hash = "sha256:0af542e815784cb64159b4469aa5d71dd41069ba93effa006e1916b1dcd88e55", size = 11152, upload-time = "2026-03-30T22:50:26.766Z" }
|
| 685 |
+
wheels = [
|
| 686 |
+
{ url = "https://files.pythonhosted.org/packages/97/e9/93afb14d23a949acaa3f4e7cc51a0024671174e116e35f42850764b99634/google_auth_httplib2-0.3.1-py3-none-any.whl", hash = "sha256:682356a90ef4ba3d06548c37e9112eea6fc00395a11b0303a644c1a86abc275c", size = 9534, upload-time = "2026-03-30T22:49:03.384Z" },
|
| 687 |
+
]
|
| 688 |
+
|
| 689 |
+
[[package]]
|
| 690 |
+
name = "google-cloud-speech"
|
| 691 |
+
version = "2.38.0"
|
| 692 |
+
source = { registry = "https://pypi.org/simple" }
|
| 693 |
+
dependencies = [
|
| 694 |
+
{ name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version >= '3.14'" },
|
| 695 |
+
{ name = "google-api-core", version = "2.30.3", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version < '3.14'" },
|
| 696 |
+
{ name = "google-auth" },
|
| 697 |
+
{ name = "grpcio" },
|
| 698 |
+
{ name = "proto-plus" },
|
| 699 |
+
{ name = "protobuf" },
|
| 700 |
+
]
|
| 701 |
+
sdist = { url = "https://files.pythonhosted.org/packages/21/1f/d0122ad8af8c0608fb3168bd5030e62ce0a1fcc09c730487bc8be541874a/google_cloud_speech-2.38.0.tar.gz", hash = "sha256:1854b51cbb7957273b6ba61f4a6cf49dec8d09ec450991587897e50267eaca51", size = 406015, upload-time = "2026-03-26T22:18:54.434Z" }
|
| 702 |
+
wheels = [
|
| 703 |
+
{ url = "https://files.pythonhosted.org/packages/01/96/008365cddc78720d65475091be929466fb16c62b47283546f8eab5ff4445/google_cloud_speech-2.38.0-py3-none-any.whl", hash = "sha256:dbccb340a750a409b0e70c48c16c8d7d5d48a87c70cce2add50f3d571f5375a0", size = 346013, upload-time = "2026-03-26T22:13:50.88Z" },
|
| 704 |
+
]
|
| 705 |
+
|
| 706 |
+
[[package]]
|
| 707 |
+
name = "google-cloud-texttospeech"
|
| 708 |
+
version = "2.36.0"
|
| 709 |
+
source = { registry = "https://pypi.org/simple" }
|
| 710 |
+
dependencies = [
|
| 711 |
+
{ name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version >= '3.14'" },
|
| 712 |
+
{ name = "google-api-core", version = "2.30.3", source = { registry = "https://pypi.org/simple" }, extra = ["grpc"], marker = "python_full_version < '3.14'" },
|
| 713 |
+
{ name = "google-auth" },
|
| 714 |
+
{ name = "grpcio" },
|
| 715 |
+
{ name = "proto-plus" },
|
| 716 |
+
{ name = "protobuf" },
|
| 717 |
+
]
|
| 718 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1a/fe/670610dce6852c8fbdd723101a2ffb497d196bc242cb04a1312339f49d67/google_cloud_texttospeech-2.36.0.tar.gz", hash = "sha256:6c605af7e4774c1bac99fcaaf4538f152b10bba7738a23f42184557f444dc6b7", size = 196558, upload-time = "2026-04-02T21:23:42.236Z" }
|
| 719 |
+
wheels = [
|
| 720 |
+
{ url = "https://files.pythonhosted.org/packages/76/1a/0317eaa1ccd6e3645d570851574bb7c1a1cfa3504bb114b7211db59b154d/google_cloud_texttospeech-2.36.0-py3-none-any.whl", hash = "sha256:03f76162543e9d77ecbab823c1cc3728c42ef40547353bcfdbd9ac0e71cb8121", size = 200086, upload-time = "2026-04-02T21:23:13.444Z" },
|
| 721 |
+
]
|
| 722 |
+
|
| 723 |
[[package]]
|
| 724 |
name = "google-genai"
|
| 725 |
version = "1.2.0"
|
|
|
|
| 735 |
{ url = "https://files.pythonhosted.org/packages/0d/ed/985f2d2e2b5fbd912ab0fdb11d6dc48c22553a6c4edffabb8146d53b974a/google_genai-1.2.0-py3-none-any.whl", hash = "sha256:609d61bee73f1a6ae5b47e9c7dd4b469d50318f050c5ceacf835b0f80f79d2d9", size = 130744, upload-time = "2025-02-12T16:40:03.601Z" },
|
| 736 |
]
|
| 737 |
|
| 738 |
+
[[package]]
|
| 739 |
+
name = "google-generativeai"
|
| 740 |
+
version = "0.8.6"
|
| 741 |
+
source = { registry = "https://pypi.org/simple" }
|
| 742 |
+
dependencies = [
|
| 743 |
+
{ name = "google-ai-generativelanguage" },
|
| 744 |
+
{ name = "google-api-core", version = "2.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
| 745 |
+
{ name = "google-api-core", version = "2.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
| 746 |
+
{ name = "google-api-python-client" },
|
| 747 |
+
{ name = "google-auth" },
|
| 748 |
+
{ name = "protobuf" },
|
| 749 |
+
{ name = "pydantic" },
|
| 750 |
+
{ name = "tqdm" },
|
| 751 |
+
{ name = "typing-extensions" },
|
| 752 |
+
]
|
| 753 |
+
wheels = [
|
| 754 |
+
{ url = "https://files.pythonhosted.org/packages/97/0f/ef33b5bb71437966590c6297104c81051feae95d54b11ece08533ef937d3/google_generativeai-0.8.6-py3-none-any.whl", hash = "sha256:37a0eaaa95e5bbf888828e20a4a1b2c196cc9527d194706e58a68ff388aeb0fa", size = 155098, upload-time = "2025-12-16T17:53:58.61Z" },
|
| 755 |
+
]
|
| 756 |
+
|
| 757 |
+
[[package]]
|
| 758 |
+
name = "googleapis-common-protos"
|
| 759 |
+
version = "1.74.0"
|
| 760 |
+
source = { registry = "https://pypi.org/simple" }
|
| 761 |
+
dependencies = [
|
| 762 |
+
{ name = "protobuf" },
|
| 763 |
+
]
|
| 764 |
+
sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" }
|
| 765 |
+
wheels = [
|
| 766 |
+
{ url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" },
|
| 767 |
+
]
|
| 768 |
+
|
| 769 |
+
[[package]]
|
| 770 |
+
name = "grpcio"
|
| 771 |
+
version = "1.80.0"
|
| 772 |
+
source = { registry = "https://pypi.org/simple" }
|
| 773 |
+
dependencies = [
|
| 774 |
+
{ name = "typing-extensions" },
|
| 775 |
+
]
|
| 776 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
|
| 777 |
+
wheels = [
|
| 778 |
+
{ url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" },
|
| 779 |
+
{ url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" },
|
| 780 |
+
{ url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" },
|
| 781 |
+
{ url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" },
|
| 782 |
+
{ url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" },
|
| 783 |
+
{ url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" },
|
| 784 |
+
{ url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" },
|
| 785 |
+
{ url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" },
|
| 786 |
+
{ url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" },
|
| 787 |
+
{ url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" },
|
| 788 |
+
{ url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" },
|
| 789 |
+
{ url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" },
|
| 790 |
+
{ url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" },
|
| 791 |
+
{ url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" },
|
| 792 |
+
{ url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" },
|
| 793 |
+
{ url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" },
|
| 794 |
+
{ url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" },
|
| 795 |
+
{ url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" },
|
| 796 |
+
{ url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" },
|
| 797 |
+
{ url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" },
|
| 798 |
+
{ url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" },
|
| 799 |
+
{ url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" },
|
| 800 |
+
{ url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" },
|
| 801 |
+
{ url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" },
|
| 802 |
+
{ url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" },
|
| 803 |
+
{ url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" },
|
| 804 |
+
{ url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" },
|
| 805 |
+
{ url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" },
|
| 806 |
+
{ url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" },
|
| 807 |
+
{ url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" },
|
| 808 |
+
{ url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" },
|
| 809 |
+
{ url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" },
|
| 810 |
+
{ url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" },
|
| 811 |
+
{ url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" },
|
| 812 |
+
{ url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" },
|
| 813 |
+
{ url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" },
|
| 814 |
+
{ url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" },
|
| 815 |
+
{ url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" },
|
| 816 |
+
{ url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" },
|
| 817 |
+
{ url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" },
|
| 818 |
+
]
|
| 819 |
+
|
| 820 |
+
[[package]]
|
| 821 |
+
name = "grpcio-status"
|
| 822 |
+
version = "1.71.2"
|
| 823 |
+
source = { registry = "https://pypi.org/simple" }
|
| 824 |
+
dependencies = [
|
| 825 |
+
{ name = "googleapis-common-protos" },
|
| 826 |
+
{ name = "grpcio" },
|
| 827 |
+
{ name = "protobuf" },
|
| 828 |
+
]
|
| 829 |
+
sdist = { url = "https://files.pythonhosted.org/packages/fd/d1/b6e9877fedae3add1afdeae1f89d1927d296da9cf977eca0eb08fb8a460e/grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50", size = 13677, upload-time = "2025-06-28T04:24:05.426Z" }
|
| 830 |
+
wheels = [
|
| 831 |
+
{ url = "https://files.pythonhosted.org/packages/67/58/317b0134129b556a93a3b0afe00ee675b5657f0155509e22fcb853bafe2d/grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3", size = 14424, upload-time = "2025-06-28T04:23:42.136Z" },
|
| 832 |
+
]
|
| 833 |
+
|
| 834 |
[[package]]
|
| 835 |
name = "h11"
|
| 836 |
version = "0.16.0"
|
|
|
|
| 853 |
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
| 854 |
]
|
| 855 |
|
| 856 |
+
[[package]]
|
| 857 |
+
name = "httplib2"
|
| 858 |
+
version = "0.31.2"
|
| 859 |
+
source = { registry = "https://pypi.org/simple" }
|
| 860 |
+
dependencies = [
|
| 861 |
+
{ name = "pyparsing" },
|
| 862 |
+
]
|
| 863 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" }
|
| 864 |
+
wheels = [
|
| 865 |
+
{ url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" },
|
| 866 |
+
]
|
| 867 |
+
|
| 868 |
[[package]]
|
| 869 |
name = "httptools"
|
| 870 |
version = "0.7.1"
|
|
|
|
| 1052 |
{ url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" },
|
| 1053 |
]
|
| 1054 |
|
| 1055 |
+
[[package]]
|
| 1056 |
+
name = "numpy"
|
| 1057 |
+
version = "2.4.4"
|
| 1058 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1059 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" }
|
| 1060 |
+
wheels = [
|
| 1061 |
+
{ url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" },
|
| 1062 |
+
{ url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" },
|
| 1063 |
+
{ url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" },
|
| 1064 |
+
{ url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" },
|
| 1065 |
+
{ url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" },
|
| 1066 |
+
{ url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" },
|
| 1067 |
+
{ url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" },
|
| 1068 |
+
{ url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" },
|
| 1069 |
+
{ url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" },
|
| 1070 |
+
{ url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" },
|
| 1071 |
+
{ url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" },
|
| 1072 |
+
{ url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" },
|
| 1073 |
+
{ url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" },
|
| 1074 |
+
{ url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" },
|
| 1075 |
+
{ url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" },
|
| 1076 |
+
{ url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" },
|
| 1077 |
+
{ url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" },
|
| 1078 |
+
{ url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" },
|
| 1079 |
+
{ url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" },
|
| 1080 |
+
{ url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" },
|
| 1081 |
+
{ url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" },
|
| 1082 |
+
{ url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" },
|
| 1083 |
+
{ url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" },
|
| 1084 |
+
{ url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" },
|
| 1085 |
+
{ url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" },
|
| 1086 |
+
{ url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" },
|
| 1087 |
+
{ url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" },
|
| 1088 |
+
{ url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" },
|
| 1089 |
+
{ url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" },
|
| 1090 |
+
{ url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" },
|
| 1091 |
+
{ url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" },
|
| 1092 |
+
{ url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" },
|
| 1093 |
+
{ url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" },
|
| 1094 |
+
{ url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" },
|
| 1095 |
+
{ url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" },
|
| 1096 |
+
{ url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" },
|
| 1097 |
+
{ url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" },
|
| 1098 |
+
{ url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" },
|
| 1099 |
+
{ url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" },
|
| 1100 |
+
{ url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" },
|
| 1101 |
+
{ url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" },
|
| 1102 |
+
{ url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" },
|
| 1103 |
+
{ url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" },
|
| 1104 |
+
{ url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" },
|
| 1105 |
+
{ url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" },
|
| 1106 |
+
{ url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" },
|
| 1107 |
+
{ url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" },
|
| 1108 |
+
{ url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" },
|
| 1109 |
+
{ url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" },
|
| 1110 |
+
{ url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" },
|
| 1111 |
+
{ url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" },
|
| 1112 |
+
{ url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" },
|
| 1113 |
+
{ url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" },
|
| 1114 |
+
{ url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" },
|
| 1115 |
+
{ url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" },
|
| 1116 |
+
{ url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" },
|
| 1117 |
+
{ url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" },
|
| 1118 |
+
{ url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" },
|
| 1119 |
+
{ url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" },
|
| 1120 |
+
{ url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" },
|
| 1121 |
+
{ url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" },
|
| 1122 |
+
{ url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" },
|
| 1123 |
+
{ url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" },
|
| 1124 |
+
{ url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" },
|
| 1125 |
+
{ url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" },
|
| 1126 |
+
{ url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" },
|
| 1127 |
+
{ url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" },
|
| 1128 |
+
{ url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" },
|
| 1129 |
+
{ url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" },
|
| 1130 |
+
{ url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" },
|
| 1131 |
+
{ url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" },
|
| 1132 |
+
]
|
| 1133 |
+
|
| 1134 |
[[package]]
|
| 1135 |
name = "propcache"
|
| 1136 |
version = "0.4.1"
|
|
|
|
| 1230 |
{ url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
|
| 1231 |
]
|
| 1232 |
|
| 1233 |
+
[[package]]
|
| 1234 |
+
name = "proto-plus"
|
| 1235 |
+
version = "1.27.2"
|
| 1236 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1237 |
+
dependencies = [
|
| 1238 |
+
{ name = "protobuf" },
|
| 1239 |
+
]
|
| 1240 |
+
sdist = { url = "https://files.pythonhosted.org/packages/81/0d/94dfe80193e79d55258345901acd2917523d56e8381bc4dee7fd38e3868a/proto_plus-1.27.2.tar.gz", hash = "sha256:b2adde53adadf75737c44d3dcb0104fde65250dfc83ad59168b4aa3e574b6a24", size = 57204, upload-time = "2026-03-26T22:18:57.174Z" }
|
| 1241 |
+
wheels = [
|
| 1242 |
+
{ url = "https://files.pythonhosted.org/packages/84/f3/1fba73eeffafc998a25d59703b63f8be4fe8a5cb12eaff7386a0ba0f7125/proto_plus-1.27.2-py3-none-any.whl", hash = "sha256:6432f75893d3b9e70b9c412f1d2f03f65b11fb164b793d14ae2ca01821d22718", size = 50450, upload-time = "2026-03-26T22:13:42.927Z" },
|
| 1243 |
+
]
|
| 1244 |
+
|
| 1245 |
+
[[package]]
|
| 1246 |
+
name = "protobuf"
|
| 1247 |
+
version = "5.29.6"
|
| 1248 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1249 |
+
sdist = { url = "https://files.pythonhosted.org/packages/7e/57/394a763c103e0edf87f0938dafcd918d53b4c011dfc5c8ae80f3b0452dbb/protobuf-5.29.6.tar.gz", hash = "sha256:da9ee6a5424b6b30fd5e45c5ea663aef540ca95f9ad99d1e887e819cdf9b8723", size = 425623, upload-time = "2026-02-04T22:54:40.584Z" }
|
| 1250 |
+
wheels = [
|
| 1251 |
+
{ url = "https://files.pythonhosted.org/packages/d4/88/9ee58ff7863c479d6f8346686d4636dd4c415b0cbeed7a6a7d0617639c2a/protobuf-5.29.6-cp310-abi3-win32.whl", hash = "sha256:62e8a3114992c7c647bce37dcc93647575fc52d50e48de30c6fcb28a6a291eb1", size = 423357, upload-time = "2026-02-04T22:54:25.805Z" },
|
| 1252 |
+
{ url = "https://files.pythonhosted.org/packages/1c/66/2dc736a4d576847134fb6d80bd995c569b13cdc7b815d669050bf0ce2d2c/protobuf-5.29.6-cp310-abi3-win_amd64.whl", hash = "sha256:7e6ad413275be172f67fdee0f43484b6de5a904cc1c3ea9804cb6fe2ff366eda", size = 435175, upload-time = "2026-02-04T22:54:28.592Z" },
|
| 1253 |
+
{ url = "https://files.pythonhosted.org/packages/06/db/49b05966fd208ae3f44dcd33837b6243b4915c57561d730a43f881f24dea/protobuf-5.29.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5a169e664b4057183a34bdc424540e86eea47560f3c123a0d64de4e137f9269", size = 418619, upload-time = "2026-02-04T22:54:30.266Z" },
|
| 1254 |
+
{ url = "https://files.pythonhosted.org/packages/b7/d7/48cbf6b0c3c39761e47a99cb483405f0fde2be22cf00d71ef316ce52b458/protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:a8866b2cff111f0f863c1b3b9e7572dc7eaea23a7fae27f6fc613304046483e6", size = 320284, upload-time = "2026-02-04T22:54:31.782Z" },
|
| 1255 |
+
{ url = "https://files.pythonhosted.org/packages/e3/dd/cadd6ec43069247d91f6345fa7a0d2858bef6af366dbd7ba8f05d2c77d3b/protobuf-5.29.6-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:e3387f44798ac1106af0233c04fb8abf543772ff241169946f698b3a9a3d3ab9", size = 320478, upload-time = "2026-02-04T22:54:32.909Z" },
|
| 1256 |
+
{ url = "https://files.pythonhosted.org/packages/5a/cb/e3065b447186cb70aa65acc70c86baf482d82bf75625bf5a2c4f6919c6a3/protobuf-5.29.6-py3-none-any.whl", hash = "sha256:6b9edb641441b2da9fa8f428760fc136a49cf97a52076010cf22a2ff73438a86", size = 173126, upload-time = "2026-02-04T22:54:39.462Z" },
|
| 1257 |
+
]
|
| 1258 |
+
|
| 1259 |
[[package]]
|
| 1260 |
name = "pyasn1"
|
| 1261 |
version = "0.6.3"
|
|
|
|
| 1403 |
{ url = "https://files.pythonhosted.org/packages/ef/d2/d2b0025246481aa2ce6db8ba196e29b92063343ac76e675b3a1fa478ed4d/pydantic_core-2.46.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:375cfdd2a1049910c82ba2ff24f948e93599a529e0fdb066d747975ca31fc663", size = 2190970, upload-time = "2026-04-15T14:49:33.111Z" },
|
| 1404 |
]
|
| 1405 |
|
| 1406 |
+
[[package]]
|
| 1407 |
+
name = "pyparsing"
|
| 1408 |
+
version = "3.3.2"
|
| 1409 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1410 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
|
| 1411 |
+
wheels = [
|
| 1412 |
+
{ url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
|
| 1413 |
+
]
|
| 1414 |
+
|
| 1415 |
[[package]]
|
| 1416 |
name = "python-dotenv"
|
| 1417 |
version = "1.0.1"
|
|
|
|
| 1509 |
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
| 1510 |
]
|
| 1511 |
|
| 1512 |
+
[[package]]
|
| 1513 |
+
name = "sounddevice"
|
| 1514 |
+
version = "0.5.5"
|
| 1515 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1516 |
+
dependencies = [
|
| 1517 |
+
{ name = "cffi" },
|
| 1518 |
+
]
|
| 1519 |
+
sdist = { url = "https://files.pythonhosted.org/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191, upload-time = "2026-01-23T18:36:43.529Z" }
|
| 1520 |
+
wheels = [
|
| 1521 |
+
{ url = "https://files.pythonhosted.org/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807, upload-time = "2026-01-23T18:36:35.649Z" },
|
| 1522 |
+
{ url = "https://files.pythonhosted.org/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557, upload-time = "2026-01-23T18:36:37.41Z" },
|
| 1523 |
+
{ url = "https://files.pythonhosted.org/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765, upload-time = "2026-01-23T18:36:39.047Z" },
|
| 1524 |
+
{ url = "https://files.pythonhosted.org/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324, upload-time = "2026-01-23T18:36:40.496Z" },
|
| 1525 |
+
{ url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" },
|
| 1526 |
+
]
|
| 1527 |
+
|
| 1528 |
[[package]]
|
| 1529 |
name = "starlette"
|
| 1530 |
version = "0.38.6"
|
|
|
|
| 1537 |
{ url = "https://files.pythonhosted.org/packages/b7/9c/93f7bc03ff03199074e81974cc148908ead60dcf189f68ba1761a0ee35cf/starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05", size = 71451, upload-time = "2024-09-22T17:01:43.076Z" },
|
| 1538 |
]
|
| 1539 |
|
| 1540 |
+
[[package]]
|
| 1541 |
+
name = "tqdm"
|
| 1542 |
+
version = "4.67.3"
|
| 1543 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1544 |
+
dependencies = [
|
| 1545 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
| 1546 |
+
]
|
| 1547 |
+
sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
|
| 1548 |
+
wheels = [
|
| 1549 |
+
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
|
| 1550 |
+
]
|
| 1551 |
+
|
| 1552 |
[[package]]
|
| 1553 |
name = "typing-extensions"
|
| 1554 |
version = "4.15.0"
|
|
|
|
| 1570 |
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
| 1571 |
]
|
| 1572 |
|
| 1573 |
+
[[package]]
|
| 1574 |
+
name = "uritemplate"
|
| 1575 |
+
version = "4.2.0"
|
| 1576 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1577 |
+
sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" }
|
| 1578 |
+
wheels = [
|
| 1579 |
+
{ url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" },
|
| 1580 |
+
]
|
| 1581 |
+
|
| 1582 |
[[package]]
|
| 1583 |
name = "urllib3"
|
| 1584 |
version = "2.6.3"
|
|
|
|
| 1659 |
{ name = "cartesia" },
|
| 1660 |
{ name = "deepgram-sdk" },
|
| 1661 |
{ name = "fastapi" },
|
| 1662 |
+
{ name = "google-cloud-speech" },
|
| 1663 |
+
{ name = "google-cloud-texttospeech" },
|
| 1664 |
{ name = "google-genai" },
|
| 1665 |
+
{ name = "google-generativeai" },
|
| 1666 |
{ name = "httpx" },
|
| 1667 |
+
{ name = "numpy" },
|
| 1668 |
{ name = "python-dotenv" },
|
| 1669 |
{ name = "python-multipart" },
|
| 1670 |
{ name = "uvicorn", extra = ["standard"] },
|
|
|
|
| 1676 |
{ name = "websockets" },
|
| 1677 |
]
|
| 1678 |
|
| 1679 |
+
[package.dev-dependencies]
|
| 1680 |
+
dev = [
|
| 1681 |
+
{ name = "sounddevice" },
|
| 1682 |
+
]
|
| 1683 |
+
|
| 1684 |
[package.metadata]
|
| 1685 |
requires-dist = [
|
| 1686 |
{ name = "assemblyai", specifier = "==0.33.0" },
|
| 1687 |
{ name = "cartesia", specifier = "==1.3.1" },
|
| 1688 |
{ name = "deepgram-sdk", specifier = ">=6.1.1" },
|
| 1689 |
{ name = "fastapi", specifier = "==0.115.0" },
|
| 1690 |
+
{ name = "google-cloud-speech", specifier = ">=2.0.0" },
|
| 1691 |
+
{ name = "google-cloud-texttospeech", specifier = ">=2.29.0" },
|
| 1692 |
{ name = "google-genai", specifier = ">=1.0.0" },
|
| 1693 |
+
{ name = "google-generativeai", specifier = ">=0.8.6" },
|
| 1694 |
{ name = "httpx", specifier = "==0.27.2" },
|
| 1695 |
+
{ name = "numpy", specifier = ">=1.26.0" },
|
| 1696 |
{ name = "python-dotenv", specifier = "==1.0.1" },
|
| 1697 |
{ name = "python-multipart", specifier = ">=0.0.26" },
|
| 1698 |
{ name = "uvicorn", extras = ["standard"], specifier = "==0.30.6" },
|
|
|
|
| 1701 |
]
|
| 1702 |
provides-extras = ["dev"]
|
| 1703 |
|
| 1704 |
+
[package.metadata.requires-dev]
|
| 1705 |
+
dev = [{ name = "sounddevice", specifier = ">=0.5.5" }]
|
| 1706 |
+
|
| 1707 |
[[package]]
|
| 1708 |
name = "watchfiles"
|
| 1709 |
version = "1.1.1"
|