Merge pull request #4348 from pipecat-ai/mb/pyright-scripts-docs
Fix type errors in scripts and add to pyright checked set
This commit is contained in:
2
.github/workflows/format.yaml
vendored
2
.github/workflows/format.yaml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
run: uv python install 3.12
|
||||
|
||||
- name: Install development dependencies
|
||||
run: uv sync --group dev
|
||||
run: uv sync --group dev --extra daily --extra tracing
|
||||
|
||||
- name: Ruff formatter
|
||||
id: ruff-format
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
"pythonVersion": "3.11",
|
||||
"pythonPlatform": "All",
|
||||
"include": [
|
||||
"scripts",
|
||||
"src/pipecat/clocks",
|
||||
"src/pipecat/metrics",
|
||||
"src/pipecat/transcriptions",
|
||||
"src/pipecat/frames",
|
||||
"src/pipecat/observers",
|
||||
"src/pipecat/extensions",
|
||||
"src/pipecat/turns",
|
||||
"src/pipecat/frames",
|
||||
"src/pipecat/metrics",
|
||||
"src/pipecat/observers",
|
||||
"src/pipecat/pipeline",
|
||||
"src/pipecat/runner"
|
||||
"src/pipecat/runner",
|
||||
"src/pipecat/tests",
|
||||
"src/pipecat/transcriptions",
|
||||
"src/pipecat/turns",
|
||||
"src/pipecat/utils"
|
||||
],
|
||||
"exclude": ["**/*_pb2.py", "**/__pycache__"],
|
||||
"ignore": [
|
||||
@@ -20,13 +23,8 @@
|
||||
"src/pipecat/processors",
|
||||
"src/pipecat/serializers",
|
||||
"src/pipecat/services",
|
||||
"src/pipecat/sync",
|
||||
"src/pipecat/tests",
|
||||
"src/pipecat/transports",
|
||||
"src/pipecat/utils",
|
||||
"tests",
|
||||
"scripts",
|
||||
"docs"
|
||||
"tests"
|
||||
],
|
||||
"reportMissingImports": false
|
||||
}
|
||||
|
||||
@@ -2,7 +2,14 @@ import asyncio
|
||||
import os
|
||||
import signal
|
||||
|
||||
from daily import *
|
||||
from daily import (
|
||||
AudioData,
|
||||
CallClient,
|
||||
CustomAudioSource,
|
||||
CustomAudioTrack,
|
||||
Daily,
|
||||
EventHandler,
|
||||
)
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
|
||||
@@ -33,8 +40,8 @@ class DailyProxyApp(EventHandler):
|
||||
def __init__(self, sample_rate: int):
|
||||
super().__init__()
|
||||
self._sample_rate = sample_rate
|
||||
self._loop = None
|
||||
self._audio_queue: asyncio.Queue | None = None
|
||||
self._loop = asyncio.new_event_loop()
|
||||
self._audio_queue: asyncio.Queue = asyncio.Queue()
|
||||
self._audio_task: asyncio.Task | None = None
|
||||
|
||||
self._client: CallClient = CallClient(event_handler=self)
|
||||
@@ -52,7 +59,6 @@ class DailyProxyApp(EventHandler):
|
||||
self._loop.call_soon_threadsafe(self._loop.stop)
|
||||
|
||||
def run(self, meeting_url: str):
|
||||
self._loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self._loop)
|
||||
self._create_audio_task()
|
||||
|
||||
@@ -101,7 +107,6 @@ class DailyProxyApp(EventHandler):
|
||||
|
||||
def _create_audio_task(self):
|
||||
if not self._audio_task:
|
||||
self._audio_queue = asyncio.Queue()
|
||||
self._audio_task = self._loop.create_task(self._audio_task_handler())
|
||||
|
||||
async def _cancel_audio_task(self):
|
||||
@@ -113,7 +118,6 @@ class DailyProxyApp(EventHandler):
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
self._audio_task = None
|
||||
self._audio_queue = None
|
||||
|
||||
async def capture_participant_audio(self, participant_id: str):
|
||||
logger.info(f"Capturing participant audio: {participant_id}")
|
||||
@@ -168,7 +172,7 @@ class DailyProxyApp(EventHandler):
|
||||
|
||||
def main():
|
||||
Daily.init()
|
||||
room_url = os.getenv("TAVUS_SAMPLE_ROOM_URL")
|
||||
room_url = os.environ["TAVUS_SAMPLE_ROOM_URL"]
|
||||
app = DailyProxyApp(sample_rate=24000)
|
||||
app.run(room_url)
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ class EvalRunner:
|
||||
|
||||
|
||||
async def run_example_pipeline(script_path: Path, eval_config: EvalConfig):
|
||||
room_url = os.getenv("DAILY_ROOM_URL")
|
||||
room_url = os.environ["DAILY_ROOM_URL"]
|
||||
|
||||
module = load_module_from_path(script_path)
|
||||
|
||||
@@ -227,7 +227,7 @@ async def run_eval_pipeline(
|
||||
):
|
||||
logger.info(f"Starting eval bot")
|
||||
|
||||
room_url = os.getenv("DAILY_ROOM_URL")
|
||||
room_url = os.environ["DAILY_ROOM_URL"]
|
||||
|
||||
transport = DailyTransport(
|
||||
room_url,
|
||||
@@ -243,7 +243,7 @@ async def run_eval_pipeline(
|
||||
# We disable smart formatting because some times if the user says "3 + 2 is
|
||||
# 5" (in audio) this can be converted to "32 is 5".
|
||||
stt = DeepgramSTTService(
|
||||
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
||||
api_key=os.environ["DEEPGRAM_API_KEY"],
|
||||
settings=DeepgramSTTService.Settings(
|
||||
language="multi",
|
||||
smart_format=False,
|
||||
@@ -251,7 +251,7 @@ async def run_eval_pipeline(
|
||||
)
|
||||
|
||||
tts = CartesiaTTSService(
|
||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||
api_key=os.environ["CARTESIA_API_KEY"],
|
||||
settings=CartesiaTTSService.Settings(
|
||||
voice="97f4b8fb-f2fe-444b-bb9a-c109783a857a", # Nathan
|
||||
),
|
||||
@@ -375,7 +375,7 @@ async def run_eval_pipeline(
|
||||
@task.event_handler("on_pipeline_finished")
|
||||
async def on_pipeline_finished(task, frame):
|
||||
if isinstance(frame, EndFrame):
|
||||
await eval_runner.assert_eval(frame.reason)
|
||||
await eval_runner.assert_eval(bool(frame.reason))
|
||||
elif isinstance(frame, CancelFrame):
|
||||
await eval_runner.assert_eval(False)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from eval import EvalRunner
|
||||
from eval import EvalConfig, EvalRunner
|
||||
from loguru import logger
|
||||
from utils import check_env_variables
|
||||
|
||||
@@ -33,7 +33,8 @@ async def main(args: argparse.Namespace):
|
||||
|
||||
runner = EvalRunner(examples_dir=script_path, record_audio=args.audio, log_level=log_level)
|
||||
|
||||
await runner.run_eval(script_file, args.prompt, args.eval)
|
||||
eval_config = EvalConfig(prompt=args.prompt, eval=args.eval)
|
||||
await runner.run_eval(script_file, eval_config)
|
||||
|
||||
runner.print_results()
|
||||
|
||||
|
||||
@@ -79,6 +79,8 @@ def load_module_from_path(path: str | Path):
|
||||
module_name = path.stem
|
||||
|
||||
spec = importlib.util.spec_from_file_location(module_name, str(path))
|
||||
if spec is None or spec.loader is None:
|
||||
raise ImportError(f"Could not load module spec from {path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
@@ -71,7 +71,7 @@ async def analyze_audio_file(
|
||||
frame_duration_ms: int = 20,
|
||||
chunk_duration_ms: int = 20,
|
||||
verbose: bool = False,
|
||||
output_file: str = None,
|
||||
output_file: str | None = None,
|
||||
) -> None:
|
||||
"""Analyze an audio file for turn detection using Krisp VIVA turn analyzer.
|
||||
|
||||
|
||||
@@ -20,7 +20,11 @@ if TYPE_CHECKING:
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from pipecat.processors.aggregators.llm_context import LLMContext, LLMSpecificMessage
|
||||
from pipecat.processors.aggregators.llm_context import (
|
||||
LLMContext,
|
||||
LLMContextMessage,
|
||||
LLMSpecificMessage,
|
||||
)
|
||||
|
||||
# Fallback timeout (seconds) used when summarization_timeout is None.
|
||||
DEFAULT_SUMMARIZATION_TIMEOUT = 120.0
|
||||
@@ -269,7 +273,7 @@ class LLMMessagesToSummarize:
|
||||
last_summarized_index: Index of the last message being summarized
|
||||
"""
|
||||
|
||||
messages: list[dict]
|
||||
messages: list[LLMContextMessage]
|
||||
last_summarized_index: int
|
||||
|
||||
|
||||
@@ -415,7 +419,7 @@ class LLMContextSummarizationUtil:
|
||||
|
||||
@staticmethod
|
||||
def _get_earliest_function_call_not_resolved_in_range(
|
||||
messages: list[dict], start_idx: int, summary_end: int
|
||||
messages: list[LLMContextMessage], start_idx: int, summary_end: int
|
||||
) -> int:
|
||||
"""Find the earliest message index with incomplete function calls.
|
||||
|
||||
@@ -470,9 +474,10 @@ class LLMContextSummarizationUtil:
|
||||
if role == "tool":
|
||||
tool_call_id = msg.get("tool_call_id")
|
||||
if tool_call_id and tool_call_id in pending_tool_calls:
|
||||
if not LLMContextSummarizationUtil._is_tool_message_pending(
|
||||
msg.get("content", "")
|
||||
):
|
||||
content = msg.get("content", "")
|
||||
if not isinstance(content, str):
|
||||
content = ""
|
||||
if not LLMContextSummarizationUtil._is_tool_message_pending(content):
|
||||
pending_tool_calls.pop(tool_call_id)
|
||||
|
||||
# Check for async tool completion — a developer message with
|
||||
@@ -480,7 +485,10 @@ class LLMContextSummarizationUtil:
|
||||
# async result has arrived and the call is now resolved.
|
||||
if role == "developer":
|
||||
try:
|
||||
parsed = json.loads(msg.get("content", ""))
|
||||
content = msg.get("content", "")
|
||||
if not isinstance(content, str):
|
||||
continue
|
||||
parsed = json.loads(content)
|
||||
if (
|
||||
isinstance(parsed, dict)
|
||||
and parsed.get("type") == "async_tool"
|
||||
|
||||
@@ -58,7 +58,7 @@ class FrameQueue(asyncio.Queue):
|
||||
Returns:
|
||||
True if at least one enqueued frame is an instance of ``frame_type``.
|
||||
"""
|
||||
for item in self._queue:
|
||||
for item in self._queue: # pyright: ignore[reportAttributeAccessIssue]
|
||||
if isinstance(self._frame_getter(item), frame_type):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -234,7 +234,7 @@ class TextPartForConcatenation:
|
||||
includes_inter_part_spaces: bool
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}(text: [{self.text}], includes_inter_part_spaces: {self.includes_inter_part_spaces})"
|
||||
return f"{type(self).__name__}(text: [{self.text}], includes_inter_part_spaces: {self.includes_inter_part_spaces})"
|
||||
|
||||
|
||||
def concatenate_aggregated_text(text_parts: list[TextPartForConcatenation]) -> str:
|
||||
|
||||
@@ -125,7 +125,7 @@ class BaseTextAggregator(ABC):
|
||||
"""
|
||||
pass
|
||||
# Make this a generator to satisfy type checker
|
||||
yield # pragma: no cover
|
||||
yield # pyright: ignore[reportReturnType] # pragma: no cover
|
||||
|
||||
@abstractmethod
|
||||
async def flush(self) -> Aggregation | None:
|
||||
|
||||
@@ -273,7 +273,7 @@ class PatternPairAggregator(SimpleTextAggregator):
|
||||
# Which is why we base the return on the first found.
|
||||
if start_count > end_count:
|
||||
start_index = text.find(start)
|
||||
return [start_index, pattern_info]
|
||||
return (start_index, pattern_info)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@ def add_openai_realtime_span_attributes(
|
||||
if isinstance(tool, dict) and "name" in tool:
|
||||
tool_names.append(tool["name"])
|
||||
elif hasattr(tool, "name"):
|
||||
tool_names.append(tool.name)
|
||||
tool_names.append(getattr(tool, "name"))
|
||||
elif isinstance(tool, dict) and "function" in tool and "name" in tool["function"]:
|
||||
tool_names.append(tool["function"]["name"])
|
||||
|
||||
@@ -455,7 +455,7 @@ def add_openai_realtime_span_attributes(
|
||||
if function_calls:
|
||||
call = function_calls[0]
|
||||
if hasattr(call, "name"):
|
||||
span.set_attribute("function_calls.first_name", call.name)
|
||||
span.set_attribute("function_calls.first_name", getattr(call, "name"))
|
||||
elif isinstance(call, dict) and "name" in call:
|
||||
span.set_attribute("function_calls.first_name", call["name"])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user