Spaces:
Sleeping
Sleeping
[NOTTICKET] add gemini client
Browse files- .env.example +10 -0
- .gitignore +3 -1
- README.md +1 -1
- main.py +51 -12
- pyproject.toml +1 -0
- src/{llm → chatbot}/__init__.py +0 -0
- src/chatbot/client.py +60 -0
- src/config.py +8 -0
- src/llm/answerer.py +0 -5
- src/pipeline.py +77 -16
- src/tts/gemini_client.py +39 -0
- uv.lock +189 -0
.env.example
CHANGED
|
@@ -7,3 +7,13 @@ DEEPGRAM_UTTERANCE_END_MS=
|
|
| 7 |
|
| 8 |
SAMPLE_RATE=
|
| 9 |
WAKE_WORD="Hai <agent name>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
SAMPLE_RATE=
|
| 9 |
WAKE_WORD="Hai <agent name>"
|
| 10 |
+
|
| 11 |
+
# Gemini TTS (optional — hanya dibutuhkan jika provider=gemini)
|
| 12 |
+
GOOGLE_API_KEY=
|
| 13 |
+
GEMINI_TTS_MODEL=gemini-2.5-flash-preview-tts
|
| 14 |
+
GEMINI_TTS_VOICE=Autonoe
|
| 15 |
+
GEMINI_TTS_LANGUAGE=id-ID
|
| 16 |
+
|
| 17 |
+
# Chatbot agent service
|
| 18 |
+
CHATBOT_BASE_URL=http://localhost:8000/chatbot-knowledgemanagement
|
| 19 |
+
CHATBOT_TIMEOUT=30
|
.gitignore
CHANGED
|
@@ -21,4 +21,6 @@ output.pcm
|
|
| 21 |
test_client.py
|
| 22 |
/playground
|
| 23 |
convert_audio.py
|
| 24 |
-
|
|
|
|
|
|
|
|
|
| 21 |
test_client.py
|
| 22 |
/playground
|
| 23 |
convert_audio.py
|
| 24 |
+
|
| 25 |
+
API_CONTRACT_CHATBOT.md
|
| 26 |
+
API_CONTRACT_VOICE.md
|
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: 🌍
|
| 4 |
colorFrom: pink
|
| 5 |
colorTo: pink
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Demo Voice Agent Data Eyond
|
| 3 |
emoji: 🌍
|
| 4 |
colorFrom: pink
|
| 5 |
colorTo: pink
|
main.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
| 1 |
import json
|
| 2 |
import logging
|
| 3 |
import uvicorn
|
| 4 |
-
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, File, UploadFile, HTTPException
|
| 5 |
from fastapi.responses import JSONResponse, StreamingResponse
|
| 6 |
from pydantic import BaseModel
|
| 7 |
-
from src.pipeline import
|
| 8 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
from src.stt.deepgram_rest import transcribe_audio
|
| 10 |
-
from src.tts.cartesia_client import synthesize_stream
|
|
|
|
| 11 |
|
| 12 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
-
VERSION = "1.
|
| 16 |
|
| 17 |
app = FastAPI(title="Voice Agent Service", version=VERSION)
|
| 18 |
|
|
@@ -21,22 +26,27 @@ app = FastAPI(title="Voice Agent Service", version=VERSION)
|
|
| 21 |
async def health() -> JSONResponse:
|
| 22 |
stt_ready = bool(DEEPGRAM_API_KEY)
|
| 23 |
tts_ready = bool(CARTESIA_API_KEY and CARTESIA_VOICE_ID)
|
| 24 |
-
|
|
|
|
|
|
|
| 25 |
|
| 26 |
body: dict = {
|
| 27 |
"status": "ok" if all_ready else "degraded",
|
| 28 |
"version": VERSION,
|
| 29 |
"stt_ready": stt_ready,
|
| 30 |
"tts_ready": tts_ready,
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
if not all_ready:
|
| 33 |
-
body["message"] = "One or more
|
| 34 |
|
| 35 |
return JSONResponse(status_code=200 if all_ready else 503, content=body)
|
| 36 |
|
| 37 |
|
| 38 |
class TTSRequest(BaseModel):
|
| 39 |
text: str
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
@app.post("/stt")
|
|
@@ -53,11 +63,21 @@ async def speech_to_text(audio: UploadFile = File(...)) -> JSONResponse:
|
|
| 53 |
async def text_to_speech(req: TTSRequest) -> StreamingResponse:
|
| 54 |
if not req.text.strip():
|
| 55 |
raise HTTPException(status_code=400, detail="text must not be empty.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
return StreamingResponse(
|
| 57 |
-
|
| 58 |
media_type="audio/pcm",
|
| 59 |
headers={
|
| 60 |
-
"X-Sample-Rate": str(
|
| 61 |
"X-Encoding": "pcm_s16le",
|
| 62 |
"X-Channels": "1",
|
| 63 |
},
|
|
@@ -65,9 +85,18 @@ async def text_to_speech(req: TTSRequest) -> StreamingResponse:
|
|
| 65 |
|
| 66 |
|
| 67 |
@app.websocket("/ws/voice")
|
| 68 |
-
async def voice_ws(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
await ws.accept()
|
| 70 |
-
logger.info("Client connected: %s", ws.client)
|
| 71 |
|
| 72 |
async def send_audio(chunk: bytes) -> None:
|
| 73 |
await ws.send_bytes(chunk)
|
|
@@ -75,7 +104,17 @@ async def voice_ws(ws: WebSocket) -> None:
|
|
| 75 |
async def send_event(event: dict) -> None:
|
| 76 |
await ws.send_text(json.dumps(event))
|
| 77 |
|
| 78 |
-
pipeline =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
pipeline.start()
|
| 80 |
|
| 81 |
try:
|
|
|
|
| 1 |
import json
|
| 2 |
import logging
|
| 3 |
import uvicorn
|
| 4 |
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, File, UploadFile, HTTPException, Query
|
| 5 |
from fastapi.responses import JSONResponse, StreamingResponse
|
| 6 |
from pydantic import BaseModel
|
| 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, CHATBOT_BASE_URL,
|
| 12 |
+
)
|
| 13 |
from src.stt.deepgram_rest import transcribe_audio
|
| 14 |
+
from src.tts.cartesia_client import synthesize_stream as cartesia_synthesize
|
| 15 |
+
from src.tts.gemini_client import synthesize_stream as gemini_synthesize, GEMINI_SAMPLE_RATE
|
| 16 |
|
| 17 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
|
| 18 |
logger = logging.getLogger(__name__)
|
| 19 |
|
| 20 |
+
VERSION = "1.2.0"
|
| 21 |
|
| 22 |
app = FastAPI(title="Voice Agent Service", version=VERSION)
|
| 23 |
|
|
|
|
| 26 |
async def health() -> JSONResponse:
|
| 27 |
stt_ready = bool(DEEPGRAM_API_KEY)
|
| 28 |
tts_ready = bool(CARTESIA_API_KEY and CARTESIA_VOICE_ID)
|
| 29 |
+
gemini_ready = bool(GOOGLE_API_KEY)
|
| 30 |
+
chatbot_ready = bool(CHATBOT_BASE_URL)
|
| 31 |
+
all_ready = stt_ready and tts_ready and chatbot_ready
|
| 32 |
|
| 33 |
body: dict = {
|
| 34 |
"status": "ok" if all_ready else "degraded",
|
| 35 |
"version": VERSION,
|
| 36 |
"stt_ready": stt_ready,
|
| 37 |
"tts_ready": tts_ready,
|
| 38 |
+
"gemini_tts_ready": gemini_ready,
|
| 39 |
+
"chatbot_ready": chatbot_ready,
|
| 40 |
}
|
| 41 |
if not all_ready:
|
| 42 |
+
body["message"] = "One or more required configurations are missing. Check your .env file."
|
| 43 |
|
| 44 |
return JSONResponse(status_code=200 if all_ready else 503, content=body)
|
| 45 |
|
| 46 |
|
| 47 |
class TTSRequest(BaseModel):
|
| 48 |
text: str
|
| 49 |
+
provider: Literal["cartesia", "gemini"] = "cartesia"
|
| 50 |
|
| 51 |
|
| 52 |
@app.post("/stt")
|
|
|
|
| 63 |
async def text_to_speech(req: TTSRequest) -> StreamingResponse:
|
| 64 |
if not req.text.strip():
|
| 65 |
raise HTTPException(status_code=400, detail="text must not be empty.")
|
| 66 |
+
|
| 67 |
+
if req.provider == "gemini":
|
| 68 |
+
if not GOOGLE_API_KEY:
|
| 69 |
+
raise HTTPException(status_code=503, detail="Gemini TTS not configured.")
|
| 70 |
+
stream = gemini_synthesize(req.text)
|
| 71 |
+
sample_rate = GEMINI_SAMPLE_RATE
|
| 72 |
+
else:
|
| 73 |
+
stream = cartesia_synthesize(req.text)
|
| 74 |
+
sample_rate = SAMPLE_RATE
|
| 75 |
+
|
| 76 |
return StreamingResponse(
|
| 77 |
+
stream,
|
| 78 |
media_type="audio/pcm",
|
| 79 |
headers={
|
| 80 |
+
"X-Sample-Rate": str(sample_rate),
|
| 81 |
"X-Encoding": "pcm_s16le",
|
| 82 |
"X-Channels": "1",
|
| 83 |
},
|
|
|
|
| 85 |
|
| 86 |
|
| 87 |
@app.websocket("/ws/voice")
|
| 88 |
+
async def voice_ws(
|
| 89 |
+
ws: WebSocket,
|
| 90 |
+
user_id: str = Query(default="anonymous"),
|
| 91 |
+
fullname: str = Query(default=""),
|
| 92 |
+
company: str = Query(default=""),
|
| 93 |
+
function: str = Query(default=""),
|
| 94 |
+
site: str = Query(default="HO"),
|
| 95 |
+
role: str = Query(default="engineer"),
|
| 96 |
+
agent: str = Query(default="analysis"),
|
| 97 |
+
) -> None:
|
| 98 |
await ws.accept()
|
| 99 |
+
logger.info("Client connected: %s (user_id=%s)", ws.client, user_id)
|
| 100 |
|
| 101 |
async def send_audio(chunk: bytes) -> None:
|
| 102 |
await ws.send_bytes(chunk)
|
|
|
|
| 104 |
async def send_event(event: dict) -> None:
|
| 105 |
await ws.send_text(json.dumps(event))
|
| 106 |
|
| 107 |
+
pipeline = VoicePipeline(
|
| 108 |
+
send_audio=send_audio,
|
| 109 |
+
send_event=send_event,
|
| 110 |
+
user_id=user_id,
|
| 111 |
+
fullname=fullname,
|
| 112 |
+
company=company,
|
| 113 |
+
function=function,
|
| 114 |
+
site=site,
|
| 115 |
+
role=role,
|
| 116 |
+
agent=agent,
|
| 117 |
+
)
|
| 118 |
pipeline.start()
|
| 119 |
|
| 120 |
try:
|
pyproject.toml
CHANGED
|
@@ -12,6 +12,7 @@ dependencies = [
|
|
| 12 |
"cartesia==1.3.1",
|
| 13 |
"deepgram-sdk>=6.1.1",
|
| 14 |
"python-multipart>=0.0.26",
|
|
|
|
| 15 |
]
|
| 16 |
|
| 17 |
[project.optional-dependencies]
|
|
|
|
| 12 |
"cartesia==1.3.1",
|
| 13 |
"deepgram-sdk>=6.1.1",
|
| 14 |
"python-multipart>=0.0.26",
|
| 15 |
+
"google-genai>=1.0.0",
|
| 16 |
]
|
| 17 |
|
| 18 |
[project.optional-dependencies]
|
src/{llm → chatbot}/__init__.py
RENAMED
|
File without changes
|
src/chatbot/client.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
@@ -10,6 +10,14 @@ CARTESIA_VOICE_ID: str = os.environ["CARTESIA_VOICE_ID"]
|
|
| 10 |
SAMPLE_RATE: int = int(os.getenv("SAMPLE_RATE", "16000"))
|
| 11 |
WAKE_WORDS: list[str] = [w.strip().lower() for w in os.getenv("WAKE_WORD", "Hai EMA").split(",") if w.strip()]
|
| 12 |
CARTESIA_MODEL: str = os.getenv("CARTESIA_MODEL", "sonic-3")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
DEEPGRAM_LANGUAGE: str = os.getenv("DEEPGRAM_LANGUAGE", "id")
|
| 14 |
DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
|
| 15 |
DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
SAMPLE_RATE: int = int(os.getenv("SAMPLE_RATE", "16000"))
|
| 11 |
WAKE_WORDS: list[str] = [w.strip().lower() for w in os.getenv("WAKE_WORD", "Hai EMA").split(",") if w.strip()]
|
| 12 |
CARTESIA_MODEL: str = os.getenv("CARTESIA_MODEL", "sonic-3")
|
| 13 |
+
|
| 14 |
+
GOOGLE_API_KEY: str = os.getenv("GOOGLE_API_KEY", "")
|
| 15 |
+
GEMINI_TTS_MODEL: str = os.getenv("GEMINI_TTS_MODEL", "gemini-2.5-flash-preview-tts")
|
| 16 |
+
GEMINI_TTS_VOICE: str = os.getenv("GEMINI_TTS_VOICE", "Autonoe")
|
| 17 |
+
GEMINI_TTS_LANGUAGE: str = os.getenv("GEMINI_TTS_LANGUAGE", "id-ID")
|
| 18 |
DEEPGRAM_LANGUAGE: str = os.getenv("DEEPGRAM_LANGUAGE", "id")
|
| 19 |
DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
|
| 20 |
DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
|
| 21 |
+
|
| 22 |
+
CHATBOT_BASE_URL: str = os.getenv("CHATBOT_BASE_URL", "")
|
| 23 |
+
CHATBOT_TIMEOUT: int = int(os.getenv("CHATBOT_TIMEOUT", "30"))
|
src/llm/answerer.py
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
# Phase 2 placeholder: LLM answer generation
|
| 2 |
-
# Will use: Anthropic Claude API with retrieved PDF context
|
| 3 |
-
#
|
| 4 |
-
# Interface (to be implemented):
|
| 5 |
-
# async def answer(question: str, context_chunks: list[str]) -> str: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/pipeline.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import logging
|
| 3 |
from typing import Callable, Awaitable
|
| 4 |
from src.config import WAKE_WORDS
|
| 5 |
from src.stt.deepgram_client import DeepgramStreamer
|
| 6 |
from src.tts.cartesia_client import synthesize_stream
|
|
|
|
| 7 |
|
| 8 |
logger = logging.getLogger(__name__)
|
| 9 |
|
|
@@ -11,18 +13,46 @@ SendAudioCallback = Callable[[bytes], Awaitable[None]]
|
|
| 11 |
SendEventCallback = Callable[[dict], Awaitable[None]]
|
| 12 |
|
| 13 |
|
| 14 |
-
class
|
| 15 |
"""
|
| 16 |
-
|
| 17 |
-
Audio in → STT → wake word check → TTS (echo) → audio out.
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
| 20 |
"""
|
| 21 |
|
| 22 |
-
def __init__(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
self._send_audio = send_audio
|
| 24 |
self._send_event = send_event
|
| 25 |
self._loop = asyncio.get_event_loop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
self._stt = DeepgramStreamer(
|
| 27 |
on_final_transcript=self._on_final_transcript,
|
| 28 |
loop=self._loop,
|
|
@@ -39,18 +69,15 @@ class EchoPipeline:
|
|
| 39 |
async def _on_final_transcript(self, text: str) -> None:
|
| 40 |
await self._send_event({"event": "transcript", "text": text})
|
| 41 |
|
| 42 |
-
|
| 43 |
-
if
|
| 44 |
logger.debug("No wake word detected, ignoring: %s", text)
|
| 45 |
return
|
| 46 |
|
| 47 |
-
logger.info("Wake word detected,
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
# Run as a Task so it can be cancelled via interrupt()
|
| 51 |
-
self._tts_task = asyncio.create_task(self._speak(reply_text))
|
| 52 |
|
| 53 |
-
def
|
| 54 |
lower = transcript.lower()
|
| 55 |
match = min(
|
| 56 |
((lower.index(w), w) for w in WAKE_WORDS if w in lower),
|
|
@@ -60,8 +87,39 @@ class EchoPipeline:
|
|
| 60 |
if match is None:
|
| 61 |
return None
|
| 62 |
idx = match[0] + len(match[1])
|
| 63 |
-
|
| 64 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
async def _speak(self, text: str) -> None:
|
| 67 |
async with self._tts_lock:
|
|
@@ -70,7 +128,6 @@ class EchoPipeline:
|
|
| 70 |
await self._send_audio(audio_chunk)
|
| 71 |
await self._send_event({"event": "tts_end"})
|
| 72 |
except asyncio.CancelledError:
|
| 73 |
-
# Interrupted mid-stream — do not send tts_end
|
| 74 |
raise
|
| 75 |
except Exception:
|
| 76 |
logger.exception("TTS error for text: %s", text)
|
|
@@ -94,3 +151,7 @@ class EchoPipeline:
|
|
| 94 |
if self._tts_task and not self._tts_task.done():
|
| 95 |
self._tts_task.cancel()
|
| 96 |
self._stt.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
import asyncio
|
| 3 |
import logging
|
| 4 |
from typing import Callable, Awaitable
|
| 5 |
from src.config import WAKE_WORDS
|
| 6 |
from src.stt.deepgram_client import DeepgramStreamer
|
| 7 |
from src.tts.cartesia_client import synthesize_stream
|
| 8 |
+
from src.chatbot.client import call_agent
|
| 9 |
|
| 10 |
logger = logging.getLogger(__name__)
|
| 11 |
|
|
|
|
| 13 |
SendEventCallback = Callable[[dict], Awaitable[None]]
|
| 14 |
|
| 15 |
|
| 16 |
+
class VoicePipeline:
|
| 17 |
"""
|
| 18 |
+
Voice pipeline: Audio in → STT → wake word check → chatbot agent → TTS → audio out.
|
|
|
|
| 19 |
|
| 20 |
+
After wake word detection, the text following the wake word is sent to the
|
| 21 |
+
chatbot service (POST /api/agents/call). The response field answer.audio_text
|
| 22 |
+
is used for TTS; falls back to answer.content if audio_text is null.
|
| 23 |
"""
|
| 24 |
|
| 25 |
+
def __init__(
|
| 26 |
+
self,
|
| 27 |
+
send_audio: SendAudioCallback,
|
| 28 |
+
send_event: SendEventCallback,
|
| 29 |
+
user_id: str = "anonymous",
|
| 30 |
+
fullname: str = "",
|
| 31 |
+
company: str = "",
|
| 32 |
+
function: str = "",
|
| 33 |
+
site: str = "HO",
|
| 34 |
+
role: str = "engineer",
|
| 35 |
+
agent: str = "analysis",
|
| 36 |
+
):
|
| 37 |
self._send_audio = send_audio
|
| 38 |
self._send_event = send_event
|
| 39 |
self._loop = asyncio.get_event_loop()
|
| 40 |
+
|
| 41 |
+
self._user_id = user_id
|
| 42 |
+
self._fullname = fullname
|
| 43 |
+
self._company = company
|
| 44 |
+
self._function = function
|
| 45 |
+
self._site = site
|
| 46 |
+
self._role = role
|
| 47 |
+
self._agent = agent
|
| 48 |
+
|
| 49 |
+
# Stable identifiers for this session
|
| 50 |
+
self._room_id = str(uuid.uuid4())
|
| 51 |
+
self._chat_id = str(uuid.uuid4())
|
| 52 |
+
|
| 53 |
+
# Running chat history for multi-turn context
|
| 54 |
+
self._chat_history: list[dict] = []
|
| 55 |
+
|
| 56 |
self._stt = DeepgramStreamer(
|
| 57 |
on_final_transcript=self._on_final_transcript,
|
| 58 |
loop=self._loop,
|
|
|
|
| 69 |
async def _on_final_transcript(self, text: str) -> None:
|
| 70 |
await self._send_event({"event": "transcript", "text": text})
|
| 71 |
|
| 72 |
+
question = self._extract_question(text)
|
| 73 |
+
if question is None:
|
| 74 |
logger.debug("No wake word detected, ignoring: %s", text)
|
| 75 |
return
|
| 76 |
|
| 77 |
+
logger.info("Wake word detected, question: %s", question)
|
| 78 |
+
self._tts_task = asyncio.create_task(self._answer_and_speak(question))
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
+
def _extract_question(self, transcript: str) -> str | None:
|
| 81 |
lower = transcript.lower()
|
| 82 |
match = min(
|
| 83 |
((lower.index(w), w) for w in WAKE_WORDS if w in lower),
|
|
|
|
| 87 |
if match is None:
|
| 88 |
return None
|
| 89 |
idx = match[0] + len(match[1])
|
| 90 |
+
question = transcript[idx:].strip(" ,.")
|
| 91 |
+
return question if question else None
|
| 92 |
+
|
| 93 |
+
async def _answer_and_speak(self, question: str) -> None:
|
| 94 |
+
try:
|
| 95 |
+
reply_text = await call_agent(
|
| 96 |
+
question=question,
|
| 97 |
+
chat_history=self._chat_history,
|
| 98 |
+
user_id=self._user_id,
|
| 99 |
+
fullname=self._fullname,
|
| 100 |
+
company=self._company,
|
| 101 |
+
function=self._function,
|
| 102 |
+
site=self._site,
|
| 103 |
+
role=self._role,
|
| 104 |
+
agent=self._agent,
|
| 105 |
+
room_id=self._room_id,
|
| 106 |
+
chat_id=self._chat_id,
|
| 107 |
+
)
|
| 108 |
+
except Exception:
|
| 109 |
+
logger.exception("Chatbot call failed for question: %s", question)
|
| 110 |
+
await self._send_event({
|
| 111 |
+
"event": "error",
|
| 112 |
+
"code": "CHATBOT_ERROR",
|
| 113 |
+
"message": "Failed to get response from chatbot agent.",
|
| 114 |
+
})
|
| 115 |
+
return
|
| 116 |
+
|
| 117 |
+
# Update history for multi-turn context
|
| 118 |
+
self._chat_history.append({"role": "human", "content": question})
|
| 119 |
+
self._chat_history.append({"role": "ai", "content": reply_text})
|
| 120 |
+
|
| 121 |
+
await self._send_event({"event": "reply", "text": reply_text})
|
| 122 |
+
await self._speak(reply_text)
|
| 123 |
|
| 124 |
async def _speak(self, text: str) -> None:
|
| 125 |
async with self._tts_lock:
|
|
|
|
| 128 |
await self._send_audio(audio_chunk)
|
| 129 |
await self._send_event({"event": "tts_end"})
|
| 130 |
except asyncio.CancelledError:
|
|
|
|
| 131 |
raise
|
| 132 |
except Exception:
|
| 133 |
logger.exception("TTS error for text: %s", text)
|
|
|
|
| 151 |
if self._tts_task and not self._tts_task.done():
|
| 152 |
self._tts_task.cancel()
|
| 153 |
self._stt.stop()
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
# Backward-compatible alias
|
| 157 |
+
EchoPipeline = VoicePipeline
|
src/tts/gemini_client.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from typing import AsyncIterator
|
| 3 |
+
from google import genai
|
| 4 |
+
from google.genai import types
|
| 5 |
+
from src.config import GOOGLE_API_KEY, GEMINI_TTS_MODEL, GEMINI_TTS_VOICE, GEMINI_TTS_LANGUAGE
|
| 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 Gemini TTS and yields raw PCM Linear16 chunks at 24kHz."""
|
| 14 |
+
client = genai.Client(api_key=GOOGLE_API_KEY)
|
| 15 |
+
logger.info("Gemini TTS: synthesizing '%s'", text[:60])
|
| 16 |
+
|
| 17 |
+
response_iter = await client.aio.models.generate_content_stream(
|
| 18 |
+
model=GEMINI_TTS_MODEL,
|
| 19 |
+
contents=text,
|
| 20 |
+
config=types.GenerateContentConfig(
|
| 21 |
+
response_modalities=["AUDIO"],
|
| 22 |
+
speech_config=types.SpeechConfig(
|
| 23 |
+
language_code=GEMINI_TTS_LANGUAGE,
|
| 24 |
+
voice_config=types.VoiceConfig(
|
| 25 |
+
prebuilt_voice_config=types.PrebuiltVoiceConfig(
|
| 26 |
+
voice_name=GEMINI_TTS_VOICE,
|
| 27 |
+
)
|
| 28 |
+
),
|
| 29 |
+
),
|
| 30 |
+
),
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
async for chunk in response_iter:
|
| 34 |
+
if (
|
| 35 |
+
chunk.candidates
|
| 36 |
+
and chunk.candidates[0].content.parts
|
| 37 |
+
and chunk.candidates[0].content.parts[0].inline_data
|
| 38 |
+
):
|
| 39 |
+
yield chunk.candidates[0].content.parts[0].inline_data.data
|
uv.lock
CHANGED
|
@@ -197,6 +197,76 @@ wheels = [
|
|
| 197 |
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
|
| 198 |
]
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
[[package]]
|
| 201 |
name = "charset-normalizer"
|
| 202 |
version = "3.4.7"
|
|
@@ -307,6 +377,65 @@ wheels = [
|
|
| 307 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
| 308 |
]
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
[[package]]
|
| 311 |
name = "deepgram-sdk"
|
| 312 |
version = "6.1.1"
|
|
@@ -442,6 +571,34 @@ wheels = [
|
|
| 442 |
{ 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" },
|
| 443 |
]
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
[[package]]
|
| 446 |
name = "h11"
|
| 447 |
version = "0.16.0"
|
|
@@ -750,6 +907,36 @@ wheels = [
|
|
| 750 |
{ 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" },
|
| 751 |
]
|
| 752 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 753 |
[[package]]
|
| 754 |
name = "pydantic"
|
| 755 |
version = "2.13.1"
|
|
@@ -1077,6 +1264,7 @@ dependencies = [
|
|
| 1077 |
{ name = "cartesia" },
|
| 1078 |
{ name = "deepgram-sdk" },
|
| 1079 |
{ name = "fastapi" },
|
|
|
|
| 1080 |
{ name = "httpx" },
|
| 1081 |
{ name = "python-dotenv" },
|
| 1082 |
{ name = "python-multipart" },
|
|
@@ -1095,6 +1283,7 @@ requires-dist = [
|
|
| 1095 |
{ name = "cartesia", specifier = "==1.3.1" },
|
| 1096 |
{ name = "deepgram-sdk", specifier = ">=6.1.1" },
|
| 1097 |
{ name = "fastapi", specifier = "==0.115.0" },
|
|
|
|
| 1098 |
{ name = "httpx", specifier = "==0.27.2" },
|
| 1099 |
{ name = "python-dotenv", specifier = "==1.0.1" },
|
| 1100 |
{ name = "python-multipart", specifier = ">=0.0.26" },
|
|
|
|
| 197 |
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
|
| 198 |
]
|
| 199 |
|
| 200 |
+
[[package]]
|
| 201 |
+
name = "cffi"
|
| 202 |
+
version = "2.0.0"
|
| 203 |
+
source = { registry = "https://pypi.org/simple" }
|
| 204 |
+
dependencies = [
|
| 205 |
+
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
| 206 |
+
]
|
| 207 |
+
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
| 208 |
+
wheels = [
|
| 209 |
+
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
|
| 210 |
+
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
|
| 211 |
+
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
|
| 212 |
+
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
|
| 213 |
+
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
|
| 214 |
+
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
|
| 215 |
+
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
|
| 216 |
+
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
|
| 217 |
+
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
|
| 218 |
+
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
|
| 219 |
+
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
|
| 220 |
+
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
|
| 221 |
+
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
|
| 222 |
+
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
|
| 223 |
+
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
|
| 224 |
+
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
|
| 225 |
+
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
|
| 226 |
+
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
|
| 227 |
+
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
|
| 228 |
+
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
|
| 229 |
+
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
|
| 230 |
+
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
|
| 231 |
+
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
|
| 232 |
+
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
|
| 233 |
+
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
|
| 234 |
+
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
|
| 235 |
+
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
|
| 236 |
+
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
|
| 237 |
+
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
|
| 238 |
+
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
|
| 239 |
+
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
|
| 240 |
+
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
|
| 241 |
+
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
|
| 242 |
+
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
|
| 243 |
+
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
|
| 244 |
+
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
|
| 245 |
+
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
|
| 246 |
+
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
|
| 247 |
+
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
|
| 248 |
+
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
| 249 |
+
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
| 250 |
+
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
| 251 |
+
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
| 252 |
+
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
| 253 |
+
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
| 254 |
+
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
|
| 255 |
+
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
|
| 256 |
+
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
|
| 257 |
+
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
|
| 258 |
+
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
|
| 259 |
+
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
| 260 |
+
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
| 261 |
+
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
| 262 |
+
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
| 263 |
+
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
| 264 |
+
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
| 265 |
+
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
|
| 266 |
+
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
|
| 267 |
+
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
| 268 |
+
]
|
| 269 |
+
|
| 270 |
[[package]]
|
| 271 |
name = "charset-normalizer"
|
| 272 |
version = "3.4.7"
|
|
|
|
| 377 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
| 378 |
]
|
| 379 |
|
| 380 |
+
[[package]]
|
| 381 |
+
name = "cryptography"
|
| 382 |
+
version = "46.0.7"
|
| 383 |
+
source = { registry = "https://pypi.org/simple" }
|
| 384 |
+
dependencies = [
|
| 385 |
+
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
| 386 |
+
]
|
| 387 |
+
sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" }
|
| 388 |
+
wheels = [
|
| 389 |
+
{ url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" },
|
| 390 |
+
{ url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" },
|
| 391 |
+
{ url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" },
|
| 392 |
+
{ url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" },
|
| 393 |
+
{ url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" },
|
| 394 |
+
{ url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" },
|
| 395 |
+
{ url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" },
|
| 396 |
+
{ url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" },
|
| 397 |
+
{ url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" },
|
| 398 |
+
{ url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" },
|
| 399 |
+
{ url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" },
|
| 400 |
+
{ url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" },
|
| 401 |
+
{ url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" },
|
| 402 |
+
{ url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" },
|
| 403 |
+
{ url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" },
|
| 404 |
+
{ url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" },
|
| 405 |
+
{ url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" },
|
| 406 |
+
{ url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" },
|
| 407 |
+
{ url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" },
|
| 408 |
+
{ url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" },
|
| 409 |
+
{ url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" },
|
| 410 |
+
{ url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" },
|
| 411 |
+
{ url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" },
|
| 412 |
+
{ url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" },
|
| 413 |
+
{ url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" },
|
| 414 |
+
{ url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" },
|
| 415 |
+
{ url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" },
|
| 416 |
+
{ url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" },
|
| 417 |
+
{ url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" },
|
| 418 |
+
{ url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" },
|
| 419 |
+
{ url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" },
|
| 420 |
+
{ url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" },
|
| 421 |
+
{ url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" },
|
| 422 |
+
{ url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" },
|
| 423 |
+
{ url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" },
|
| 424 |
+
{ url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" },
|
| 425 |
+
{ url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" },
|
| 426 |
+
{ url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" },
|
| 427 |
+
{ url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" },
|
| 428 |
+
{ url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" },
|
| 429 |
+
{ url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" },
|
| 430 |
+
{ url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" },
|
| 431 |
+
{ url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" },
|
| 432 |
+
{ url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" },
|
| 433 |
+
{ url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" },
|
| 434 |
+
{ url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" },
|
| 435 |
+
{ url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" },
|
| 436 |
+
{ url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" },
|
| 437 |
+
]
|
| 438 |
+
|
| 439 |
[[package]]
|
| 440 |
name = "deepgram-sdk"
|
| 441 |
version = "6.1.1"
|
|
|
|
| 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"
|
| 577 |
+
source = { registry = "https://pypi.org/simple" }
|
| 578 |
+
dependencies = [
|
| 579 |
+
{ name = "cryptography" },
|
| 580 |
+
{ name = "pyasn1-modules" },
|
| 581 |
+
]
|
| 582 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c6/fc/e925290a1ad95c975c459e2df070fac2b90954e13a0370ac505dff78cb99/google_auth-2.49.2.tar.gz", hash = "sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409", size = 333958, upload-time = "2026-04-10T00:41:21.888Z" }
|
| 583 |
+
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"
|
| 590 |
+
source = { registry = "https://pypi.org/simple" }
|
| 591 |
+
dependencies = [
|
| 592 |
+
{ name = "google-auth" },
|
| 593 |
+
{ name = "pydantic" },
|
| 594 |
+
{ name = "requests" },
|
| 595 |
+
{ name = "typing-extensions" },
|
| 596 |
+
{ name = "websockets" },
|
| 597 |
+
]
|
| 598 |
+
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"
|
|
|
|
| 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"
|
| 913 |
+
source = { registry = "https://pypi.org/simple" }
|
| 914 |
+
sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
|
| 915 |
+
wheels = [
|
| 916 |
+
{ url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
|
| 917 |
+
]
|
| 918 |
+
|
| 919 |
+
[[package]]
|
| 920 |
+
name = "pyasn1-modules"
|
| 921 |
+
version = "0.4.2"
|
| 922 |
+
source = { registry = "https://pypi.org/simple" }
|
| 923 |
+
dependencies = [
|
| 924 |
+
{ name = "pyasn1" },
|
| 925 |
+
]
|
| 926 |
+
sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
|
| 927 |
+
wheels = [
|
| 928 |
+
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
|
| 929 |
+
]
|
| 930 |
+
|
| 931 |
+
[[package]]
|
| 932 |
+
name = "pycparser"
|
| 933 |
+
version = "3.0"
|
| 934 |
+
source = { registry = "https://pypi.org/simple" }
|
| 935 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
|
| 936 |
+
wheels = [
|
| 937 |
+
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
|
| 938 |
+
]
|
| 939 |
+
|
| 940 |
[[package]]
|
| 941 |
name = "pydantic"
|
| 942 |
version = "2.13.1"
|
|
|
|
| 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" },
|
|
|
|
| 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" },
|