From 911c5ed4169d6058f098c98b54cc57c602f09357 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 23 Jan 2026 18:53:59 +0530 Subject: [PATCH] fix: changes to update pipecat version to 0.0.100 (#122) * feat: add stt evals * add smart turn as provider * chore: remove deprecations * chore: format files * fix: remove deprecated UserIdleProcessor * fix: remove deprecated TranscriptProcessor * chore: update pipecat submodule * feat: add evals visualisation * fix: trigger llm generation on client connected and pipeline started * chore: update pipecat * chore: update pipecat submodule * Add tests * fix: slow loading of workflow page * chore: update pipecat submodule * Show version after release * Fixes #99 * fix: provider check for websocket connection * Fixes #107 * Fix #96 * chore: fix documentation * fix: cloudonix campaign call error --------- Co-authored-by: Sabiha Khan --- .dockerignore | 3 +- .github/workflows/docker-image.yml | 47 +- .gitignore | 1 + .../181475b2a1a1_add_public_access_token.py | 72 + api/constants.py | 18 +- api/db/models.py | 11 + api/db/workflow_client.py | 96 +- api/db/workflow_run_client.py | 54 + api/pyproject.toml | 5 + api/routes/main.py | 21 +- api/routes/public_download.py | 95 + api/routes/workflow.py | 82 +- api/services/campaign/call_dispatcher.py | 27 +- api/services/configuration/registry.py | 2 +- .../looptalk/core/pipeline_builder.py | 1 - api/services/looptalk/internal_serializer.py | 12 +- api/services/pipecat/event_handlers.py | 122 +- api/services/pipecat/pipeline_builder.py | 24 +- api/services/pipecat/run_pipeline.py | 127 +- api/services/pipecat/service_factory.py | 19 +- api/services/pipecat/transport_setup.py | 133 +- .../telephony/stasis_rtp_serializer.py | 10 +- api/services/workflow/pipecat_engine.py | 28 +- .../workflow/pipecat_engine_callbacks.py | 37 +- api/tasks/arq.py | 6 +- api/tasks/function_names.py | 3 +- api/tasks/run_integrations.py | 47 +- api/tasks/s3_upload.py | 219 +- api/tests/conftest.py | 107 +- .../test_pipecat_engine_context_update.py | 137 +- api/tests/test_pipeline_cancellation.py | 100 + api/tests/test_user_idle_handler.py | 51 +- docker-compose.yaml | 6 +- docs/deployment/custom-domain.mdx | 4 + evals/stt/README.md | 135 + evals/stt/__init__.py | 1 + evals/stt/audio/multi_speaker.m4a | Bin 0 -> 63136 bytes evals/stt/audio/nope.m4a | Bin 0 -> 27884 bytes evals/stt/audio/not_so_sure.m4a | Bin 0 -> 32543 bytes evals/stt/audio/vad.m4a | Bin 0 -> 111765 bytes evals/stt/audio/yes.m4a | Bin 0 -> 21134 bytes evals/stt/audio_streamer.py | 140 + evals/stt/benchmark.py | 247 + evals/stt/event_capture.py | 251 ++ evals/stt/providers/__init__.py | 16 + evals/stt/providers/base.py | 128 + evals/stt/providers/deepgram_flux_provider.py | 235 + evals/stt/providers/deepgram_provider.py | 236 + .../providers/local_smart_turn_provider.py | 287 ++ evals/stt/providers/speechmatics_provider.py | 258 ++ .../results/multi_speaker-deepgram-flux.json | 867 ++++ evals/stt/results/multi_speaker-deepgram.json | 637 +++ evals/stt/results/nope-deepgram-flux.json | 445 ++ .../results/not_so_sure-deepgram-flux.json | 678 +++ .../stt/results/not_so_sure-speechmatics.json | 936 ++++ evals/stt/results/vad-deepgram-flux.json | 2931 ++++++++++++ evals/stt/results/vad-deepgram.json | 1131 +++++ evals/stt/results/yes-deepgram-flux.json | 402 ++ evals/visualizer/.gitignore | 41 + evals/visualizer/README.md | 36 + evals/visualizer/eslint.config.mjs | 18 + evals/visualizer/next.config.ts | 7 + evals/visualizer/package.json | 26 + evals/visualizer/pnpm-lock.yaml | 4008 +++++++++++++++++ evals/visualizer/pnpm-workspace.yaml | 5 + evals/visualizer/postcss.config.mjs | 7 + evals/visualizer/public/file.svg | 1 + evals/visualizer/public/globe.svg | 1 + evals/visualizer/public/next.svg | 1 + evals/visualizer/public/vercel.svg | 1 + evals/visualizer/public/window.svg | 1 + .../src/app/api/audio/[filename]/route.ts | 42 + .../src/app/api/results/[id]/route.ts | 27 + evals/visualizer/src/app/api/results/route.ts | 47 + evals/visualizer/src/app/favicon.ico | Bin 0 -> 25931 bytes evals/visualizer/src/app/globals.css | 26 + evals/visualizer/src/app/layout.tsx | 34 + evals/visualizer/src/app/page.tsx | 129 + evals/visualizer/src/app/view/[id]/page.tsx | 158 + .../visualizer/src/components/AudioPlayer.tsx | 145 + evals/visualizer/src/components/EventList.tsx | 141 + .../src/components/EventTimeline.tsx | 119 + .../components/events/DeepgramEventItem.tsx | 98 + .../src/components/events/FluxEventItem.tsx | 115 + .../events/SpeechmaticsEventItem.tsx | 101 + .../visualizer/src/components/events/index.ts | 3 + evals/visualizer/src/types/index.ts | 24 + evals/visualizer/tsconfig.json | 34 + pipecat | 2 +- release-please-config.json | 5 + ui/src/app/after-sign-in/page.tsx | 13 +- ui/src/app/api/config/version/route.ts | 33 + ui/src/app/layout.tsx | 27 +- ui/src/app/page.tsx | 13 +- ui/src/app/usage/page.tsx | 52 +- ui/src/client/sdk.gen.ts | 51 +- ui/src/client/types.gen.ts | 101 +- ui/src/components/MediaPreviewDialog.tsx | 20 +- ui/src/components/ServiceConfiguration.tsx | 13 +- ui/src/components/flow/nodes/EndCall.tsx | 13 +- ui/src/components/flow/nodes/TriggerNode.tsx | 7 +- ui/src/components/layout/AppSidebar.tsx | 11 + ui/src/context/AppConfigContext.tsx | 58 + ui/src/lib/utils.ts | 13 +- 104 files changed, 16919 insertions(+), 597 deletions(-) create mode 100644 api/alembic/versions/181475b2a1a1_add_public_access_token.py create mode 100644 api/pyproject.toml create mode 100644 api/routes/public_download.py create mode 100644 api/tests/test_pipeline_cancellation.py create mode 100644 evals/stt/README.md create mode 100644 evals/stt/__init__.py create mode 100644 evals/stt/audio/multi_speaker.m4a create mode 100644 evals/stt/audio/nope.m4a create mode 100644 evals/stt/audio/not_so_sure.m4a create mode 100644 evals/stt/audio/vad.m4a create mode 100644 evals/stt/audio/yes.m4a create mode 100644 evals/stt/audio_streamer.py create mode 100644 evals/stt/benchmark.py create mode 100644 evals/stt/event_capture.py create mode 100644 evals/stt/providers/__init__.py create mode 100644 evals/stt/providers/base.py create mode 100644 evals/stt/providers/deepgram_flux_provider.py create mode 100644 evals/stt/providers/deepgram_provider.py create mode 100644 evals/stt/providers/local_smart_turn_provider.py create mode 100644 evals/stt/providers/speechmatics_provider.py create mode 100644 evals/stt/results/multi_speaker-deepgram-flux.json create mode 100644 evals/stt/results/multi_speaker-deepgram.json create mode 100644 evals/stt/results/nope-deepgram-flux.json create mode 100644 evals/stt/results/not_so_sure-deepgram-flux.json create mode 100644 evals/stt/results/not_so_sure-speechmatics.json create mode 100644 evals/stt/results/vad-deepgram-flux.json create mode 100644 evals/stt/results/vad-deepgram.json create mode 100644 evals/stt/results/yes-deepgram-flux.json create mode 100644 evals/visualizer/.gitignore create mode 100644 evals/visualizer/README.md create mode 100644 evals/visualizer/eslint.config.mjs create mode 100644 evals/visualizer/next.config.ts create mode 100644 evals/visualizer/package.json create mode 100644 evals/visualizer/pnpm-lock.yaml create mode 100644 evals/visualizer/pnpm-workspace.yaml create mode 100644 evals/visualizer/postcss.config.mjs create mode 100644 evals/visualizer/public/file.svg create mode 100644 evals/visualizer/public/globe.svg create mode 100644 evals/visualizer/public/next.svg create mode 100644 evals/visualizer/public/vercel.svg create mode 100644 evals/visualizer/public/window.svg create mode 100644 evals/visualizer/src/app/api/audio/[filename]/route.ts create mode 100644 evals/visualizer/src/app/api/results/[id]/route.ts create mode 100644 evals/visualizer/src/app/api/results/route.ts create mode 100644 evals/visualizer/src/app/favicon.ico create mode 100644 evals/visualizer/src/app/globals.css create mode 100644 evals/visualizer/src/app/layout.tsx create mode 100644 evals/visualizer/src/app/page.tsx create mode 100644 evals/visualizer/src/app/view/[id]/page.tsx create mode 100644 evals/visualizer/src/components/AudioPlayer.tsx create mode 100644 evals/visualizer/src/components/EventList.tsx create mode 100644 evals/visualizer/src/components/EventTimeline.tsx create mode 100644 evals/visualizer/src/components/events/DeepgramEventItem.tsx create mode 100644 evals/visualizer/src/components/events/FluxEventItem.tsx create mode 100644 evals/visualizer/src/components/events/SpeechmaticsEventItem.tsx create mode 100644 evals/visualizer/src/components/events/index.ts create mode 100644 evals/visualizer/src/types/index.ts create mode 100644 evals/visualizer/tsconfig.json create mode 100644 ui/src/app/api/config/version/route.ts create mode 100644 ui/src/context/AppConfigContext.tsx diff --git a/.dockerignore b/.dockerignore index 19aaf83..8439270 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -api/.env \ No newline at end of file +api/.env +evals/ diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 20d0de3..418b67f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,7 +4,7 @@ on: release: types: [published] -# Ensure only one workflow run per branch at a time; cancel any in-progress runs on new push +# Ensure only one workflow run per branch at a time; cancel any in-progress runs on new push concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -13,11 +13,11 @@ jobs: build: runs-on: ubuntu-latest env: - COMMIT_SHA: ${{ github.sha }} # Used to tag images with short commit SHA + COMMIT_SHA: ${{ github.sha }} strategy: matrix: - service: + service: - "dograh-api|api/Dockerfile|." - "dograh-ui|ui/Dockerfile|." @@ -25,14 +25,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - submodules: true # Only for version check, not used in build + submodules: true - # Pipecat version check removed - now using local submodule - - - name: Set up QEMU # Enables cross-platform builds (e.g., arm64) + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx # Enables multi-arch and advanced Docker builds + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to DockerHub @@ -51,48 +49,50 @@ jobs: - name: Set build variables id: build-vars run: | - # Parse matrix entry and set variables early (before build) SERVICE="${{ matrix.service }}" IMAGE_NAME=$(echo "$SERVICE" | cut -d '|' -f1) SHORT_SHA=${COMMIT_SHA::8} - - # Export for use in subsequent steps + + # Get version from release tag (removes 'dograh-' and 'v' prefixes if present) + VERSION="${{ github.event.release.tag_name }}" + VERSION="${VERSION#dograh-}" + VERSION="${VERSION#v}" + echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - echo "service=${SERVICE}" >> $GITHUB_OUTPUT - + echo "version=${VERSION}" >> $GITHUB_OUTPUT + - name: Build and Push ${{ matrix.service }} id: docker-build run: | - # Parse matrix entry into individual variables SERVICE="${{ matrix.service }}" IMAGE_NAME=$(echo "$SERVICE" | cut -d '|' -f1) DOCKERFILE=$(echo "$SERVICE" | cut -d '|' -f2) CONTEXT=$(echo "$SERVICE" | cut -d '|' -f3) SHORT_SHA=${COMMIT_SHA::8} + VERSION="${{ steps.build-vars.outputs.version }}" echo "Building and pushing image: $IMAGE_NAME" echo "Dockerfile: $DOCKERFILE" echo "Context: $CONTEXT" - echo "Commit SHA: $SHORT_SHA" - - # Export tags for Slack notification + echo "Version: $VERSION" + echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT echo "dockerhub_tag=${{ secrets.DOCKERHUB_USERNAME }}/${IMAGE_NAME}:${SHORT_SHA}" >> $GITHUB_OUTPUT echo "ghcr_tag=ghcr.io/${{ secrets.GHCR_USERNAME }}/${IMAGE_NAME}:${SHORT_SHA}" >> $GITHUB_OUTPUT echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - # Build and push multi-arch Docker image to DockerHub and GHCR docker buildx build \ -f "$DOCKERFILE" \ --platform linux/amd64,linux/arm64 \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:$VERSION \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:$SHORT_SHA \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/$IMAGE_NAME:latest \ + --tag ghcr.io/${{ secrets.GHCR_USERNAME }}/$IMAGE_NAME:$VERSION \ --tag ghcr.io/${{ secrets.GHCR_USERNAME }}/$IMAGE_NAME:$SHORT_SHA \ --tag ghcr.io/${{ secrets.GHCR_USERNAME }}/$IMAGE_NAME:latest \ --push "$CONTEXT" - - # Success notification + - name: Send Slack notification - Success if: success() uses: slackapi/slack-github-action@v1.26.0 @@ -101,10 +101,9 @@ jobs: with: payload: | { - "text": "✅ Docker Build Successful - ${{ steps.build-vars.outputs.image_name }} (${{ steps.build-vars.outputs.short_sha }}) on ${{ github.ref_name }} by ${{ github.actor }}" + "text": "✅ Docker Build Successful - ${{ steps.build-vars.outputs.image_name }} (${{ steps.build-vars.outputs.version }}) on ${{ github.ref_name }} by ${{ github.actor }}" } - - # Failure notification + - name: Send Slack notification - Failure if: failure() uses: slackapi/slack-github-action@v1.26.0 @@ -113,5 +112,5 @@ jobs: with: payload: | { - "text": "❌ Docker Build Failed - ${{ steps.build-vars.outputs.image_name }} (${{ steps.build-vars.outputs.short_sha }}) on ${{ github.ref_name }} by ${{ github.actor }} - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Logs>" + "text": "❌ Docker Build Failed - ${{ steps.build-vars.outputs.image_name }} (${{ steps.build-vars.outputs.version }}) on ${{ github.ref_name }} by ${{ github.actor }} - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Logs>" } diff --git a/.gitignore b/.gitignore index c8acf44..7f268eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ __pycache__ .DS_Store .env +.env.prod .env.test # logs and run directory on production diff --git a/api/alembic/versions/181475b2a1a1_add_public_access_token.py b/api/alembic/versions/181475b2a1a1_add_public_access_token.py new file mode 100644 index 0000000..a796285 --- /dev/null +++ b/api/alembic/versions/181475b2a1a1_add_public_access_token.py @@ -0,0 +1,72 @@ +"""add public_access_token + +Revision ID: 181475b2a1a1 +Revises: dc33eef8dabe +Create Date: 2026-01-23 17:37:54.449308 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "181475b2a1a1" +down_revision: Union[str, None] = "dc33eef8dabe" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_api_keys_key_hash"), table_name="api_keys") + op.create_index("ix_api_keys_key_hash", "api_keys", ["key_hash"], unique=False) + op.create_index( + "ix_kb_chunks_embedding_ivfflat", + "knowledge_base_chunks", + ["embedding"], + unique=False, + postgresql_using="ivfflat", + postgresql_with={"lists": 100}, + postgresql_ops={"embedding": "vector_cosine_ops"}, + ) + op.create_index( + "ix_kb_chunks_embedding_model", + "knowledge_base_chunks", + ["embedding_model"], + unique=False, + ) + op.add_column( + "workflow_runs", + sa.Column("public_access_token", sa.String(length=36), nullable=True), + ) + op.create_index( + "idx_workflow_runs_public_access_token", + "workflow_runs", + ["public_access_token"], + unique=True, + postgresql_where=sa.text("public_access_token IS NOT NULL"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index( + "idx_workflow_runs_public_access_token", + table_name="workflow_runs", + postgresql_where=sa.text("public_access_token IS NOT NULL"), + ) + op.drop_column("workflow_runs", "public_access_token") + op.drop_index("ix_kb_chunks_embedding_model", table_name="knowledge_base_chunks") + op.drop_index( + "ix_kb_chunks_embedding_ivfflat", + table_name="knowledge_base_chunks", + postgresql_using="ivfflat", + postgresql_with={"lists": 100}, + postgresql_ops={"embedding": "vector_cosine_ops"}, + ) + op.drop_index("ix_api_keys_key_hash", table_name="api_keys") + op.create_index(op.f("ix_api_keys_key_hash"), "api_keys", ["key_hash"], unique=True) + # ### end Alembic commands ### diff --git a/api/constants.py b/api/constants.py index 366a256..b92596a 100644 --- a/api/constants.py +++ b/api/constants.py @@ -14,7 +14,6 @@ FILLER_SOUND_PROBABILITY = 0.0 VOICEMAIL_RECORDING_DURATION = 5.0 # Configuration constants -ENABLE_SMART_TURN = os.getenv("ENABLE_SMART_TURN", "false").lower() == "true" ENABLE_TRACING = os.getenv("ENABLE_TRACING", "false").lower() == "true" ENABLE_RNNOISE = os.getenv("ENABLE_RNNOISE", "false").lower() == "true" @@ -52,6 +51,23 @@ ENABLE_ARI_STASIS = os.getenv("ENABLE_ARI_STASIS", "false").lower() == "true" SERIALIZE_LOG_OUTPUT = os.getenv("SERIALIZE_LOG_OUTPUT", "false").lower() == "true" ENABLE_TELEMETRY = os.getenv("ENABLE_TELEMETRY", "false").lower() == "true" + +def _get_version() -> str: + """Read version from pyproject.toml.""" + try: + import tomllib + + pyproject_path = APP_ROOT_DIR / "pyproject.toml" + with open(pyproject_path, "rb") as f: + pyproject = tomllib.load(f) + return pyproject.get("project", {}).get("version", "dev") + except Exception: + return "dev" + + +# Application version (read from pyproject.toml) +APP_VERSION = _get_version() + # Country code mapping: ISO country code -> international dialing prefix COUNTRY_CODES = { "US": "1", # United States diff --git a/api/db/models.py b/api/db/models.py index 55d0305..4f44636 100644 --- a/api/db/models.py +++ b/api/db/models.py @@ -360,6 +360,17 @@ class WorkflowRunModel(Base): campaign = relationship("CampaignModel") queued_run_id = Column(Integer, ForeignKey("queued_runs.id"), nullable=True) queued_run = relationship("QueuedRunModel", foreign_keys=[queued_run_id]) + public_access_token = Column(String(36), nullable=True) + + # Indexes + __table_args__ = ( + Index( + "idx_workflow_runs_public_access_token", + "public_access_token", + unique=True, + postgresql_where=text("public_access_token IS NOT NULL"), + ), + ) # LoopTalk Testing Models diff --git a/api/db/workflow_client.py b/api/db/workflow_client.py index 7ce4078..53eb00c 100644 --- a/api/db/workflow_client.py +++ b/api/db/workflow_client.py @@ -4,7 +4,7 @@ from typing import Optional from sqlalchemy import func from sqlalchemy.future import select -from sqlalchemy.orm import selectinload +from sqlalchemy.orm import load_only, selectinload from api.db.base_client import BaseDBClient from api.db.models import WorkflowDefinitionModel, WorkflowModel, WorkflowRunModel @@ -111,6 +111,70 @@ class WorkflowClient(BaseDBClient): result = await session.execute(query) return result.scalars().all() + async def get_all_workflows_for_listing( + self, organization_id: int = None, status: str = None + ) -> list[WorkflowModel]: + """Get workflows with only the columns needed for listing. + + This is an optimized version that excludes large JSON columns like + workflow_definition, template_context_variables, etc. + + Args: + organization_id: Filter by organization ID + status: Filter by status (active/archived) + + Returns: + List of WorkflowModel with only id, name, status, created_at loaded + """ + async with self.async_session() as session: + query = select(WorkflowModel).options( + load_only( + WorkflowModel.id, + WorkflowModel.name, + WorkflowModel.status, + WorkflowModel.created_at, + ) + ) + + if organization_id: + query = query.where(WorkflowModel.organization_id == organization_id) + + if status: + query = query.where(WorkflowModel.status == status) + + result = await session.execute(query) + return result.scalars().all() + + async def get_workflow_counts(self, organization_id: int = None) -> dict[str, int]: + """Get workflow counts by status. + + Args: + organization_id: Filter by organization ID + + Returns: + Dict with 'total', 'active', 'archived' counts + """ + async with self.async_session() as session: + query = select( + WorkflowModel.status, + func.count(WorkflowModel.id).label("count"), + ) + + if organization_id: + query = query.where(WorkflowModel.organization_id == organization_id) + + query = query.group_by(WorkflowModel.status) + + result = await session.execute(query) + rows = result.all() + + counts = {"total": 0, "active": 0, "archived": 0} + for status, count in rows: + counts[status] = count + counts["total"] += count + + return counts + async def get_workflow( self, workflow_id: int, user_id: int = None, organization_id: int = None ) -> WorkflowModel | None: @@ -310,3 +374,33 @@ class WorkflowClient(BaseDBClient): ) ) return result.scalar() or 0 + + async def get_workflow_run_counts(self, workflow_ids: list[int]) -> dict[int, int]: + """Get run counts for multiple workflows in a single query. + + Args: + workflow_ids: List of workflow IDs to get counts for + + Returns: + Dict mapping workflow_id to run count + """ + if not workflow_ids: + return {} + + async with self.async_session() as session: + result = await session.execute( + select( + WorkflowRunModel.workflow_id, + func.count(WorkflowRunModel.id).label("run_count"), + ) + .where(WorkflowRunModel.workflow_id.in_(workflow_ids)) + .group_by(WorkflowRunModel.workflow_id) + ) + rows = result.all() + + # Build dict with counts, defaulting to 0 for workflows with no runs + counts = {workflow_id: 0 for workflow_id in workflow_ids} + for workflow_id, run_count in rows: + counts[workflow_id] = run_count + + return counts diff --git a/api/db/workflow_run_client.py b/api/db/workflow_run_client.py index 27cffac..604a431 100644 --- a/api/db/workflow_run_client.py +++ b/api/db/workflow_run_client.py @@ -1,3 +1,4 @@ +import uuid from datetime import datetime, timezone from typing import Any, Dict, List, Optional, Tuple @@ -414,3 +415,56 @@ class WorkflowRunClient(BaseDBClient): organization_id = workflow_run.workflow.user.selected_organization_id return workflow_run, organization_id + + async def ensure_public_access_token(self, workflow_run_id: int) -> Optional[str]: + """Generate a public access token if not exists, return existing if present (idempotent). + + Args: + workflow_run_id: The ID of the workflow run + + Returns: + The public access token string, or None if workflow run not found + """ + async with self.async_session() as session: + result = await session.execute( + select(WorkflowRunModel).where(WorkflowRunModel.id == workflow_run_id) + ) + run = result.scalars().first() + if not run: + return None + + # Return existing token if present + if run.public_access_token: + return run.public_access_token + + # Generate and persist new token + token = str(uuid.uuid4()) + run.public_access_token = token + + try: + await session.commit() + except Exception as e: + await session.rollback() + raise e + await session.refresh(run) + + return run.public_access_token + + async def get_workflow_run_by_public_token( + self, token: str + ) -> Optional[WorkflowRunModel]: + """Lookup workflow run by public access token. + + Args: + token: The public access token + + Returns: + The WorkflowRunModel if found, None otherwise + """ + async with self.async_session() as session: + result = await session.execute( + select(WorkflowRunModel).where( + WorkflowRunModel.public_access_token == token + ) + ) + return result.scalars().first() diff --git a/api/pyproject.toml b/api/pyproject.toml new file mode 100644 index 0000000..7825d5a --- /dev/null +++ b/api/pyproject.toml @@ -0,0 +1,5 @@ +[project] +name = "dograh-api" +version = "1.10.0" +description = "Backend API for Dograh voice AI platform" +requires-python = ">=3.12" diff --git a/api/routes/main.py b/api/routes/main.py index 2141172..8086114 100644 --- a/api/routes/main.py +++ b/api/routes/main.py @@ -1,5 +1,6 @@ from fastapi import APIRouter from loguru import logger +from pydantic import BaseModel from api.routes.campaign import router as campaign_router from api.routes.credentials import router as credentials_router @@ -9,6 +10,7 @@ from api.routes.looptalk import router as looptalk_router from api.routes.organization import router as organization_router from api.routes.organization_usage import router as organization_usage_router from api.routes.public_agent import router as public_agent_router +from api.routes.public_download import router as public_download_router from api.routes.public_embed import router as public_embed_router from api.routes.reports import router as reports_router from api.routes.s3_signed_url import router as s3_router @@ -43,11 +45,24 @@ router.include_router(reports_router) router.include_router(webrtc_signaling_router) router.include_router(public_embed_router) router.include_router(public_agent_router) +router.include_router(public_download_router) router.include_router(workflow_embed_router) router.include_router(knowledge_base_router) -@router.get("/health") -async def health(): +class HealthResponse(BaseModel): + status: str + version: str + backend_api_endpoint: str + + +@router.get("/health", response_model=HealthResponse) +async def health() -> HealthResponse: + from api.constants import APP_VERSION, BACKEND_API_ENDPOINT + logger.debug("Health endpoint called") - return {"message": "OK"} + return HealthResponse( + status="ok", + version=APP_VERSION, + backend_api_endpoint=BACKEND_API_ENDPOINT, + ) diff --git a/api/routes/public_download.py b/api/routes/public_download.py new file mode 100644 index 0000000..38793d9 --- /dev/null +++ b/api/routes/public_download.py @@ -0,0 +1,95 @@ +"""Public download endpoints for workflow recordings and transcripts. + +These endpoints provide secure, token-based public access to workflow artifacts +without requiring authentication. Tokens are generated on-demand when webhooks +are executed and included in the webhook payload. +""" + +from typing import Literal + +from fastapi import APIRouter, HTTPException, Query +from fastapi.responses import RedirectResponse +from loguru import logger + +from api.db import db_client +from api.services.storage import get_storage_for_backend + +router = APIRouter(prefix="/public/download") + + +@router.get("/workflow/{token}/{artifact_type}") +async def download_workflow_artifact( + token: str, + artifact_type: Literal["recording", "transcript"], + inline: bool = Query( + default=False, description="Display inline in browser instead of download" + ), +): + """Download a workflow recording or transcript via public access token. + + This endpoint: + 1. Validates the public access token + 2. Looks up the corresponding workflow run + 3. Generates a signed URL for the requested artifact + 4. Redirects to the signed URL + + Args: + token: The public access token (UUID format) + artifact_type: Type of artifact - "recording" or "transcript" + inline: If true, sets Content-Disposition to inline for browser preview + + Returns: + RedirectResponse to the signed URL (302 redirect) + + Raises: + HTTPException 404: If token is invalid or artifact not found + """ + # 1. Lookup workflow run by token + workflow_run = await db_client.get_workflow_run_by_public_token(token) + if not workflow_run: + logger.warning(f"Invalid public access token: {token[:8]}...") + raise HTTPException(status_code=404, detail="Invalid or expired token") + + # 2. Get file path based on artifact type + if artifact_type == "recording": + file_path = workflow_run.recording_url + else: # transcript + file_path = workflow_run.transcript_url + + if not file_path: + logger.warning( + f"Artifact not found: type={artifact_type}, workflow_run_id={workflow_run.id}" + ) + raise HTTPException( + status_code=404, + detail=f"No {artifact_type} available for this workflow run", + ) + + # 3. Get storage backend for this workflow run + try: + storage = get_storage_for_backend(workflow_run.storage_backend) + except ValueError as e: + logger.error(f"Invalid storage backend: {workflow_run.storage_backend}") + raise HTTPException(status_code=500, detail="Storage configuration error") + + # 4. Generate signed URL (1 hour expiration) + try: + signed_url = await storage.aget_signed_url( + file_path=file_path, + expiration=3600, # 1 hour + force_inline=inline, + ) + except Exception as e: + logger.error(f"Failed to generate signed URL: {e}") + raise HTTPException(status_code=500, detail="Failed to generate download URL") + + if not signed_url: + logger.error(f"Storage returned None for signed URL: {file_path}") + raise HTTPException(status_code=500, detail="Failed to generate download URL") + + logger.info( + f"Generated signed URL for {artifact_type}: workflow_run_id={workflow_run.id}, token={token[:8]}..." + ) + + # 5. Redirect to signed URL + return RedirectResponse(url=signed_url, status_code=302) diff --git a/api/routes/workflow.py b/api/routes/workflow.py index f8e47eb..126314e 100644 --- a/api/routes/workflow.py +++ b/api/routes/workflow.py @@ -97,6 +97,24 @@ class WorkflowResponse(BaseModel): workflow_configurations: dict | None = None +class WorkflowListResponse(BaseModel): + """Lightweight response for workflow listings (excludes large fields).""" + + id: int + name: str + status: str + created_at: datetime + total_runs: int + + +class WorkflowCountResponse(BaseModel): + """Response for workflow count endpoint.""" + + total: int + active: int + archived: int + + class WorkflowTemplateResponse(BaseModel): id: int template_name: str @@ -359,6 +377,26 @@ class WorkflowSummaryResponse(BaseModel): name: str +@router.get("/count") +async def get_workflow_count( + user: UserModel = Depends(get_user), +) -> WorkflowCountResponse: + """Get workflow counts for the authenticated user's organization. + + This is a lightweight endpoint for checking if the user has workflows, + useful for redirect logic without fetching full workflow data. + """ + counts = await db_client.get_workflow_counts( + organization_id=user.selected_organization_id + ) + + return WorkflowCountResponse( + total=counts["total"], + active=counts["active"], + archived=counts["archived"], + ) + + @router.get("/fetch") async def get_workflows( user: UserModel = Depends(get_user), @@ -366,45 +404,43 @@ async def get_workflows( None, description="Filter by status - can be single value (active/archived) or comma-separated (active,archived)", ), -) -> List[WorkflowResponse]: - """Get all workflows for the authenticated user's organization""" +) -> List[WorkflowListResponse]: + """Get all workflows for the authenticated user's organization. + + Returns a lightweight response with only essential fields for listing. + Use GET /workflow/fetch/{workflow_id} to get full workflow details. + """ # Handle comma-separated status values if status and "," in status: # Split comma-separated values and fetch workflows for each status status_list = [s.strip() for s in status.split(",")] all_workflows = [] for status_value in status_list: - workflows = await db_client.get_all_workflows( + workflows = await db_client.get_all_workflows_for_listing( organization_id=user.selected_organization_id, status=status_value ) all_workflows.extend(workflows) workflows = all_workflows else: # Single status or no status filter - workflows = await db_client.get_all_workflows( + workflows = await db_client.get_all_workflows_for_listing( organization_id=user.selected_organization_id, status=status ) - # Get run counts for each workflow - workflow_responses = [] - for workflow in workflows: - run_count = await db_client.get_workflow_run_count(workflow.id) - workflow_responses.append( - { - "id": workflow.id, - "name": workflow.name, - "status": workflow.status, - "created_at": workflow.created_at, - "workflow_definition": workflow.workflow_definition_with_fallback, - "current_definition_id": workflow.current_definition_id, - "template_context_variables": workflow.template_context_variables, - "call_disposition_codes": workflow.call_disposition_codes, - "workflow_configurations": workflow.workflow_configurations, - "total_runs": run_count, - } - ) + # Get run counts for all workflows in a single query + workflow_ids = [workflow.id for workflow in workflows] + run_counts = await db_client.get_workflow_run_counts(workflow_ids) - return workflow_responses + return [ + WorkflowListResponse( + id=workflow.id, + name=workflow.name, + status=workflow.status, + created_at=workflow.created_at, + total_runs=run_counts.get(workflow.id, 0), + ) + for workflow in workflows + ] @router.get("/fetch/{workflow_id}") diff --git a/api/services/campaign/call_dispatcher.py b/api/services/campaign/call_dispatcher.py index d32bb32..353a42d 100644 --- a/api/services/campaign/call_dispatcher.py +++ b/api/services/campaign/call_dispatcher.py @@ -170,13 +170,6 @@ class CampaignCallDispatcher: ) raise ValueError(f"Workflow {campaign.workflow_id} not found") - # Merge context variables (queued_run context already includes retry info if applicable) - initial_context = { - **workflow.template_context_variables, - **queued_run.context_variables, - "campaign_id": campaign.id, - } - # Extract phone number phone_number = queued_run.context_variables.get("phone_number") if not phone_number: @@ -186,13 +179,25 @@ class CampaignCallDispatcher: ) raise ValueError(f"No phone number in queued run {queued_run.id}") - # Create workflow run with queued_run_id tracking - workflow_run_name = f"WR-CAMPAIGN-{campaign.id}-{queued_run.id}" - # Get provider first to determine the mode provider = await self.get_telephony_provider(campaign.organization_id) workflow_run_mode = provider.PROVIDER_NAME + + logger.info(f"Provider name: {provider.PROVIDER_NAME}") + logger.info(f"Queued run context: {queued_run.context_variables}") + # Merge context variables (queued_run context already includes retry info if applicable) + initial_context = { + **workflow.template_context_variables, + **queued_run.context_variables, + "campaign_id": campaign.id, + "provider": provider.PROVIDER_NAME, + } + + logger.info(f"Final initial_context: {initial_context}") + + # Create workflow run with queued_run_id tracking + workflow_run_name = f"WR-CAMPAIGN-{campaign.id}-{queued_run.id}" try: workflow_run = await db_client.create_workflow_run( name=workflow_run_name, @@ -243,6 +248,8 @@ class CampaignCallDispatcher: to_number=phone_number, webhook_url=webhook_url, workflow_run_id=workflow_run.id, + workflow_id=campaign.workflow_id, + user_id=campaign.created_by, ) # Store provider type and metadata in gathered_context diff --git a/api/services/configuration/registry.py b/api/services/configuration/registry.py index 8ea8409..f1d0b69 100644 --- a/api/services/configuration/registry.py +++ b/api/services/configuration/registry.py @@ -300,7 +300,7 @@ TTSConfig = Annotated[ ###################################################### STT ######################################################################## -DEEPGRAM_STT_MODELS = ["nova-2", "nova-3-general"] +DEEPGRAM_STT_MODELS = ["nova-2", "nova-3-general", "flux-general-en"] DEEPGRAM_LANGUAGES = [ "multi", "en", diff --git a/api/services/looptalk/core/pipeline_builder.py b/api/services/looptalk/core/pipeline_builder.py index 0de82fd..8e1ceb5 100644 --- a/api/services/looptalk/core/pipeline_builder.py +++ b/api/services/looptalk/core/pipeline_builder.py @@ -103,7 +103,6 @@ class LoopTalkPipelineBuilder: # Set the context and audio_buffer after creation engine.set_context(context) - engine.set_audio_buffer(audio_buffer) context_aggregator = LLMContextAggregatorPair(context) diff --git a/api/services/looptalk/internal_serializer.py b/api/services/looptalk/internal_serializer.py index 0f59b50..75d89ea 100644 --- a/api/services/looptalk/internal_serializer.py +++ b/api/services/looptalk/internal_serializer.py @@ -12,9 +12,8 @@ from pipecat.frames.frames import ( Frame, InputAudioRawFrame, OutputAudioRawFrame, - StartFrame, ) -from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType +from pipecat.serializers.base_serializer import FrameSerializer class InternalFrameSerializer(FrameSerializer): @@ -24,15 +23,6 @@ class InternalFrameSerializer(FrameSerializer): preventing control frames from creating infinite loops. """ - @property - def type(self) -> FrameSerializerType: - """Internal transport uses binary frames.""" - return FrameSerializerType.BINARY - - async def setup(self, frame: StartFrame): - """No setup required for internal transport.""" - pass - async def serialize(self, frame: Frame) -> bytes | None: """Only serialize audio frames for transmission between agents.""" # Only pass audio frames between agents diff --git a/api/services/pipecat/event_handlers.py b/api/services/pipecat/event_handlers.py index f96c36f..1e68109 100644 --- a/api/services/pipecat/event_handlers.py +++ b/api/services/pipecat/event_handlers.py @@ -22,16 +22,21 @@ from pipecat.pipeline.task import PipelineTask from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor -def register_transport_event_handlers( +def register_event_handlers( task: PipelineTask, transport, - workflow_run_id, + workflow_run_id: int, engine: PipecatEngine, audio_buffer: AudioBufferProcessor, + in_memory_logs_buffer: InMemoryLogsBuffer, + pipeline_metrics_aggregator: PipelineMetricsAggregator, audio_config=AudioConfig, ): - """Register event handlers for transport events""" + """Register all event handlers for transport and task events. + Returns: + Tuple of (in_memory_audio_buffer, in_memory_transcript_buffer) for use by other handlers. + """ # Initialize in-memory buffers with proper audio configuration sample_rate = audio_config.pipeline_sample_rate if audio_config else 16000 num_channels = 1 # Pipeline audio is always mono @@ -48,13 +53,35 @@ def register_transport_event_handlers( ) in_memory_transcript_buffer = InMemoryTranscriptBuffer(workflow_run_id) + # Track both events to ensure LLM is only triggered after both occur + ready_state = { + "pipeline_started": False, + "client_connected": False, + "llm_triggered": False, + } + + async def maybe_trigger_llm(): + """Trigger LLM only after both pipeline_started and client_connected events.""" + if ( + ready_state["pipeline_started"] + and ready_state["client_connected"] + and not ready_state["llm_triggered"] + ): + ready_state["llm_triggered"] = True + logger.debug( + "Both pipeline_started and client_connected received - triggering initial LLM generation" + ) + await engine.llm.queue_frame(LLMContextFrame(engine.context)) + @transport.event_handler("on_client_connected") - async def on_client_connected(transport, participant): - logger.debug("In on_client_connected callback handler - initializing workflow") + async def on_client_connected(_transport, _participant): + logger.debug("In on_client_connected callback handler") await audio_buffer.start_recording() + ready_state["client_connected"] = True + await maybe_trigger_llm() @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, participant): + async def on_client_disconnected(_transport, _participant): call_disposed = engine.is_call_disposed() logger.debug( @@ -69,33 +96,16 @@ def register_transport_event_handlers( if not call_disposed: await task.cancel() - # Return the buffers so they can be passed to other handlers - return in_memory_audio_buffer, in_memory_transcript_buffer - - -def register_task_event_handler( - workflow_run_id: int, - engine: PipecatEngine, - task: PipelineTask, - transport, - audio_buffer: AudioBufferProcessor, - in_memory_audio_buffer: InMemoryAudioBuffer, - in_memory_transcript_buffer: InMemoryTranscriptBuffer, - in_memory_logs_buffer: InMemoryLogsBuffer, - pipeline_metrics_aggregator: PipelineMetricsAggregator, -): @task.event_handler("on_pipeline_started") - async def on_pipeline_started(task: PipelineTask, frame: Frame): - logger.debug( - "In on_pipeline_started callback handler - triggering initial LLM generation" - ) - # Trigger initial LLM generation after pipeline has started - await engine.llm.queue_frame(LLMContextFrame(engine.context)) + async def on_pipeline_started(_task: PipelineTask, _frame: Frame): + logger.debug("In on_pipeline_started callback handler") + ready_state["pipeline_started"] = True + await maybe_trigger_llm() @task.event_handler("on_pipeline_finished") async def on_pipeline_finished( task: PipelineTask, - frame: Frame, + _frame: Frame, ): logger.debug(f"In on_pipeline_finished callback handler") @@ -207,14 +217,13 @@ def register_task_event_handler( if workflow_run and workflow_run.campaign_id: await campaign_call_dispatcher.release_call_slot(workflow_run_id) - # Write buffers to temp files and enqueue S3 upload + # Write buffers to temp files and enqueue combined processing task + audio_temp_path = None + transcript_temp_path = None + try: - # Only upload if buffers have content if not in_memory_audio_buffer.is_empty: audio_temp_path = await in_memory_audio_buffer.write_to_temp_file() - await enqueue_job( - FunctionNames.UPLOAD_AUDIO_TO_S3, workflow_run_id, audio_temp_path - ) else: logger.debug("Audio buffer is empty, skipping upload") @@ -222,11 +231,6 @@ def register_task_event_handler( transcript_temp_path = ( await in_memory_transcript_buffer.write_to_temp_file() ) - await enqueue_job( - FunctionNames.UPLOAD_TRANSCRIPT_TO_S3, - workflow_run_id, - transcript_temp_path, - ) else: logger.debug("Transcript buffer is empty, skipping upload") @@ -234,10 +238,18 @@ def register_task_event_handler( logger.error(f"Error preparing buffers for S3 upload: {e}", exc_info=True) await enqueue_job(FunctionNames.CALCULATE_WORKFLOW_RUN_COST, workflow_run_id) + + # Combined task: uploads artifacts then runs integrations sequentially await enqueue_job( - FunctionNames.RUN_INTEGRATIONS_POST_WORKFLOW_RUN, workflow_run_id + FunctionNames.PROCESS_WORKFLOW_COMPLETION, + workflow_run_id, + audio_temp_path, + transcript_temp_path, ) + # Return the buffers so they can be passed to other handlers + return in_memory_audio_buffer, in_memory_transcript_buffer + def register_audio_data_handler( audio_buffer: AudioBufferProcessor, @@ -260,18 +272,26 @@ def register_audio_data_handler( # Could implement overflow to disk here if needed -def register_transcript_handler( - transcript, workflow_run_id, in_memory_buffer: InMemoryTranscriptBuffer +def register_transcript_handlers( + user_aggregator, + assistant_aggregator, + workflow_run_id, + in_memory_buffer: InMemoryTranscriptBuffer, ): - """Register event handler for transcript updates""" + """Register event handlers for transcript updates on context aggregators. - @transcript.event_handler("on_transcript_update") - async def on_transcript_update(processor, frame): - transcript_text = "" - for msg in frame.messages: - timestamp = f"[{msg.timestamp}] " if msg.timestamp else "" - line = f"{timestamp}{msg.role}: {msg.content}\n" - transcript_text += line + Uses the on_user_turn_stopped and on_assistant_turn_stopped events to capture + transcripts as turns complete, following the event-based pattern. + """ - # Use in-memory buffer - await in_memory_buffer.append(transcript_text) + @user_aggregator.event_handler("on_user_turn_stopped") + async def on_user_turn_stopped(aggregator, strategy, message): + timestamp = f"[{message.timestamp}] " if message.timestamp else "" + line = f"{timestamp}user: {message.content}\n" + await in_memory_buffer.append(line) + + @assistant_aggregator.event_handler("on_assistant_turn_stopped") + async def on_assistant_turn_stopped(aggregator, message): + timestamp = f"[{message.timestamp}] " if message.timestamp else "" + line = f"{timestamp}assistant: {message.content}\n" + await in_memory_buffer.append(line) diff --git a/api/services/pipecat/pipeline_builder.py b/api/services/pipecat/pipeline_builder.py index 853f32d..0f6de5d 100644 --- a/api/services/pipecat/pipeline_builder.py +++ b/api/services/pipecat/pipeline_builder.py @@ -1,5 +1,4 @@ import os -from typing import TYPE_CHECKING from loguru import logger @@ -11,14 +10,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor -from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.utils.context import turn_var -if TYPE_CHECKING: - from api.services.workflow.pipecat_engine import PipecatEngine - -def create_pipeline_components(audio_config: AudioConfig, engine: "PipecatEngine"): +def create_pipeline_components(audio_config: AudioConfig): """Create and return the main pipeline components with proper audio configuration""" logger.info(f"Creating pipeline components with audio config: {audio_config}") @@ -28,28 +23,21 @@ def create_pipeline_components(audio_config: AudioConfig, engine: "PipecatEngine buffer_size=audio_config.buffer_size_bytes, ) - transcript = TranscriptProcessor( - assistant_correct_aggregation_callback=engine.create_aggregation_correction_callback() - ) - context = LLMContext() - return audio_buffer, transcript, context + return audio_buffer, context def build_pipeline( transport, stt, - transcript, audio_buffer, llm, tts, user_context_aggregator, assistant_context_aggregator, pipeline_engine_callback_processor, - stt_mute_filter, pipeline_metrics_aggregator, - user_idle_disconnect, voicemail_detector=None, ): """Build the main pipeline with all components. @@ -63,7 +51,7 @@ def build_pipeline( # Build processors list with optional voicemail detection processors = [ transport.input(), # Transport user input - stt, # STT (audio_passthrough=True by default, passes InputAudioRawFrame) + stt, ] # Insert voicemail detector after STT if enabled @@ -76,16 +64,12 @@ def build_pipeline( # Continue with the rest of the pipeline processors.extend( [ - stt_mute_filter, # STTMuteFilters don't let VAD related events pass through if muted - user_idle_disconnect, - transcript.user(), user_context_aggregator, llm, # LLM pipeline_engine_callback_processor, tts, # TTS transport.output(), # Transport bot output audio_buffer, # AudioBufferProcessor - records both input and output audio - transcript.assistant(), assistant_context_aggregator, # Assistant spoken responses pipeline_metrics_aggregator, ] @@ -98,7 +82,6 @@ def create_pipeline_task(pipeline, workflow_run_id, audio_config: AudioConfig = """Create a pipeline task with appropriate parameters""" # Set up pipeline params with audio configuration if provided pipeline_params = PipelineParams( - allow_interruptions=True, enable_metrics=True, enable_usage_metrics=True, send_initial_empty_metrics=False, @@ -119,6 +102,7 @@ def create_pipeline_task(pipeline, workflow_run_id, audio_config: AudioConfig = pipeline, params=pipeline_params, enable_tracing=ENABLE_TRACING, + enable_rtvi=False, conversation_id=f"{workflow_run_id}", ) diff --git a/api/services/pipecat/run_pipeline.py b/api/services/pipecat/run_pipeline.py index 11ae7cc..91b1e18 100644 --- a/api/services/pipecat/run_pipeline.py +++ b/api/services/pipecat/run_pipeline.py @@ -7,12 +7,12 @@ from loguru import logger from api.db import db_client from api.db.models import WorkflowModel from api.enums import WorkflowRunMode +from api.services.configuration.registry import ServiceProviders from api.services.pipecat.audio_config import AudioConfig, create_audio_config from api.services.pipecat.event_handlers import ( register_audio_data_handler, - register_task_event_handler, - register_transcript_handler, - register_transport_event_handlers, + register_event_handlers, + register_transcript_handlers, ) from api.services.pipecat.in_memory_buffers import InMemoryLogsBuffer from api.services.pipecat.pipeline_builder import ( @@ -46,20 +46,25 @@ from api.services.workflow.pipecat_engine import PipecatEngine from api.services.workflow.workflow import WorkflowGraph from pipecat.extensions.voicemail.voicemail_detector import VoicemailDetector from pipecat.pipeline.base_task import PipelineTaskParams -from pipecat.processors.aggregators.llm_response import ( +from pipecat.processors.aggregators.llm_response_universal import ( LLMAssistantAggregatorParams, + LLMContextAggregatorPair, LLMUserAggregatorParams, ) -from pipecat.processors.aggregators.llm_response_universal import ( - LLMContextAggregatorPair, -) -from pipecat.processors.filters.stt_mute_filter import ( - STTMuteConfig, - STTMuteFilter, - STTMuteStrategy, -) -from pipecat.processors.user_idle_processor import UserIdleProcessor from pipecat.transports.smallwebrtc.connection import SmallWebRTCConnection +from pipecat.turns.user_mute import MuteUntilFirstBotCompleteUserMuteStrategy +from pipecat.turns.user_start import ( + ExternalUserTurnStartStrategy, + TranscriptionUserTurnStartStrategy, +) +from pipecat.turns.user_start.vad_user_turn_start_strategy import ( + VADUserTurnStartStrategy, +) +from pipecat.turns.user_stop import ( + ExternalUserTurnStopStrategy, + TranscriptionUserTurnStopStrategy, +) +from pipecat.turns.user_turn_strategies import UserTurnStrategies from pipecat.utils.context import set_current_run_id from pipecat.utils.enums import EndTaskReason from pipecat.utils.tracing.context_registry import ContextProviderRegistry @@ -517,12 +522,11 @@ async def _run_pipeline( embeddings_model=embeddings_model, ) - # Create pipeline components with audio configuration and engine - audio_buffer, transcript, context = create_pipeline_components(audio_config, engine) + # Create pipeline components with audio configuration + audio_buffer, context = create_pipeline_components(audio_config) # Set the context and audio_buffer after creation engine.set_context(context) - engine.set_audio_buffer(audio_buffer) # Set Stasis connection for immediate transfers (if available) if stasis_connection: @@ -532,7 +536,31 @@ async def _run_pipeline( expect_stripped_words=True, correct_aggregation_callback=engine.create_aggregation_correction_callback(), ) - user_params = LLMUserAggregatorParams(enable_emulated_vad_interruptions=True) + + # Configure turn strategies based on STT provider and model + # Deepgram Flux uses external turn detection (VAD + External start/stop) + # Other models use transcription-based turn detection with smart turn analyzer + is_deepgram_flux = ( + user_config.stt.provider == ServiceProviders.DEEPGRAM.value + and user_config.stt.model == "flux-general-en" + ) + + if is_deepgram_flux: + user_turn_strategies = UserTurnStrategies( + start=[VADUserTurnStartStrategy(), ExternalUserTurnStartStrategy()], + stop=[ExternalUserTurnStopStrategy()], + ) + else: + user_turn_strategies = UserTurnStrategies( + start=[VADUserTurnStartStrategy(), TranscriptionUserTurnStartStrategy()], + stop=[TranscriptionUserTurnStopStrategy()], + ) + + user_params = LLMUserAggregatorParams( + user_turn_strategies=user_turn_strategies, + user_mute_strategies=[MuteUntilFirstBotCompleteUserMuteStrategy()], + user_idle_timeout=max_user_idle_timeout, + ) context_aggregator = LLMContextAggregatorPair( context, assistant_params=assistant_params, user_params=user_params ) @@ -547,25 +575,20 @@ async def _run_pipeline( pipeline_metrics_aggregator = PipelineMetricsAggregator() - # Create STT mute filter using the selected strategies and the engine's callback - stt_mute_filter = STTMuteFilter( - config=STTMuteConfig( - strategies={ - STTMuteStrategy.MUTE_UNTIL_FIRST_BOT_COMPLETE, - STTMuteStrategy.CUSTOM, - }, - should_mute_callback=engine.create_should_mute_callback(), - ) - ) - - # Use engine's user idle callback with configured timeout - user_idle_disconnect = UserIdleProcessor( - callback=engine.create_user_idle_callback(), timeout=max_user_idle_timeout - ) - user_context_aggregator = context_aggregator.user() assistant_context_aggregator = context_aggregator.assistant() + # Register user idle event handlers + user_idle_handler = engine.create_user_idle_handler() + + @user_context_aggregator.event_handler("on_user_turn_idle") + async def on_user_turn_idle(aggregator): + await user_idle_handler.handle_idle(aggregator) + + @user_context_aggregator.event_handler("on_user_turn_started") + async def on_user_turn_started(aggregator, strategy): + user_idle_handler.reset() + # Create voicemail detector if enabled in the workflow's start node voicemail_detector = None start_node = workflow_graph.nodes.get(workflow_graph.start_node_id) @@ -592,16 +615,13 @@ async def _run_pipeline( pipeline = build_pipeline( transport, stt, - transcript, audio_buffer, llm, tts, user_context_aggregator, assistant_context_aggregator, pipeline_engine_callback_processor, - stt_mute_filter, pipeline_metrics_aggregator, - user_idle_disconnect, voicemail_detector=voicemail_detector, ) @@ -614,18 +634,6 @@ async def _run_pipeline( # Initialize the engine to set the initial context await engine.initialize() - # Register event handlers - in_memory_audio_buffer, in_memory_transcript_buffer = ( - register_transport_event_handlers( - task, - transport, - workflow_run_id, - engine=engine, - audio_buffer=audio_buffer, - audio_config=audio_config, - ) - ) - # Add real-time feedback observer if WebSocket sender is available # Note: ws_sender was already fetched earlier for node_transition_callback if ws_sender: @@ -635,21 +643,24 @@ async def _run_pipeline( ) task.add_observer(feedback_observer) - register_task_event_handler( - workflow_run_id, - engine, + # Register event handlers + in_memory_audio_buffer, in_memory_transcript_buffer = register_event_handlers( task, transport, - audio_buffer, - in_memory_audio_buffer, - in_memory_transcript_buffer, - in_memory_logs_buffer, - pipeline_metrics_aggregator, + workflow_run_id, + engine=engine, + audio_buffer=audio_buffer, + in_memory_logs_buffer=in_memory_logs_buffer, + pipeline_metrics_aggregator=pipeline_metrics_aggregator, + audio_config=audio_config, ) register_audio_data_handler(audio_buffer, workflow_run_id, in_memory_audio_buffer) - register_transcript_handler( - transcript, workflow_run_id, in_memory_transcript_buffer + register_transcript_handlers( + user_context_aggregator, + assistant_context_aggregator, + workflow_run_id, + in_memory_transcript_buffer, ) try: diff --git a/api/services/pipecat/service_factory.py b/api/services/pipecat/service_factory.py index ab11f18..4fd1445 100644 --- a/api/services/pipecat/service_factory.py +++ b/api/services/pipecat/service_factory.py @@ -7,6 +7,7 @@ from api.constants import MPS_API_URL from api.services.configuration.registry import ServiceProviders from pipecat.services.azure.llm import AzureLLMService from pipecat.services.cartesia.stt import CartesiaSTTService +from pipecat.services.deepgram.flux.stt import DeepgramFluxSTTService from pipecat.services.deepgram.stt import DeepgramSTTService, LiveOptions from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.dograh.llm import DograhLLMService @@ -34,6 +35,20 @@ def create_stt_service(user_config): f"Creating STT service: provider={user_config.stt.provider}, model={user_config.stt.model}" ) if user_config.stt.provider == ServiceProviders.DEEPGRAM.value: + # Check if using Flux model (English-only, no language selection) + if user_config.stt.model == "flux-general-en": + logger.debug("Using DeepGram Flux Model") + return DeepgramFluxSTTService( + api_key=user_config.stt.api_key, + model=user_config.stt.model, + params=DeepgramFluxSTTService.InputParams( + eot_timeout_ms=3000, + eot_threshold=0.7, + ), + should_interrupt=False, # Let UserAggregator take care of sending InterruptionFrame + ) + + # Other models than flux # Use language from user config, defaulting to "multi" for multilingual support language = getattr(user_config.stt, "language", None) or "multi" live_options = LiveOptions( @@ -44,7 +59,9 @@ def create_stt_service(user_config): ) logger.debug(f"Using DeepGram Model - {user_config.stt.model}") return DeepgramSTTService( - live_options=live_options, api_key=user_config.stt.api_key + live_options=live_options, + api_key=user_config.stt.api_key, + should_interrupt=False, # Let UserAggregator take care of sending InterruptionFrame ) elif user_config.stt.provider == ServiceProviders.OPENAI.value: return OpenAISTTService( diff --git a/api/services/pipecat/transport_setup.py b/api/services/pipecat/transport_setup.py index 4798841..237829b 100644 --- a/api/services/pipecat/transport_setup.py +++ b/api/services/pipecat/transport_setup.py @@ -2,10 +2,9 @@ import os from fastapi import WebSocket -from api.constants import APP_ROOT_DIR, ENABLE_RNNOISE, ENABLE_SMART_TURN +from api.constants import APP_ROOT_DIR from api.db import db_client from api.enums import OrganizationConfigurationKey -from api.services.looptalk.internal_transport import InternalTransport from api.services.pipecat.audio_config import AudioConfig from api.services.telephony.stasis_rtp_connection import StasisRTPConnection from api.services.telephony.stasis_rtp_serializer import StasisRTPFrameSerializer @@ -13,11 +12,8 @@ from api.services.telephony.stasis_rtp_transport import ( StasisRTPTransport, StasisRTPTransportParams, ) -from pipecat.audio.filters.rnnoise_filter import RNNoiseFilter from pipecat.audio.mixers.silence_mixer import SilenceAudioMixer from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer -from pipecat.audio.turn.smart_turn.base_smart_turn import SmartTurnParams -from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 from pipecat.audio.vad.silero import SileroVADAnalyzer, VADParams from pipecat.serializers.twilio import TwilioFrameSerializer from pipecat.serializers.vobiz import VobizFrameSerializer @@ -35,19 +31,6 @@ librnnoise_path = os.path.normpath( ) -def create_turn_analyzer(workflow_run_id: int, audio_config: AudioConfig): - """Create a turn analyzer backed by the local Smart Turn HTTP service. - - Args: - workflow_run_id: ID of the workflow run for turn analyzer context - audio_config: Audio configuration containing pipeline sample rate - """ - if ENABLE_SMART_TURN: - return LocalSmartTurnAnalyzerV3(params=SmartTurnParams()) - - return None - - async def create_twilio_transport( websocket_client: WebSocket, stream_sid: str, @@ -78,8 +61,6 @@ async def create_twilio_transport( f"Incomplete Twilio configuration for organization {organization_id}" ) - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) - serializer = TwilioFrameSerializer( stream_sid=stream_sid, call_sid=call_sid, @@ -119,11 +100,7 @@ async def create_twilio_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, serializer=serializer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -158,8 +135,6 @@ async def create_cloudonix_transport( f"Required: bearer_token, domain_id" ) - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) - from pipecat.serializers.cloudonix import CloudonixFrameSerializer serializer = CloudonixFrameSerializer( @@ -202,11 +177,7 @@ async def create_cloudonix_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, serializer=serializer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -238,8 +209,6 @@ async def create_vonage_transport( f"Incomplete Vonage configuration for organization {organization_id}" ) - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) - serializer = VonageFrameSerializer( call_uuid=call_uuid, application_id=application_id, @@ -283,11 +252,7 @@ async def create_vonage_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, serializer=serializer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -337,8 +302,6 @@ async def create_vobiz_transport( f"from_numbers={len(config.get('from_numbers', []))} numbers" ) - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) - # Use VobizFrameSerializer for Vobiz WebSocket protocol serializer = VobizFrameSerializer( stream_id=stream_id, @@ -389,11 +352,7 @@ async def create_vobiz_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, serializer=serializer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -411,7 +370,6 @@ def create_webrtc_transport( ambient_noise_config: dict | None = None, ): """Create a transport for WebRTC connections""" - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) return SmallWebRTCTransport( webrtc_connection=webrtc_connection, @@ -445,10 +403,6 @@ def create_webrtc_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -461,7 +415,6 @@ def create_stasis_transport( ambient_noise_config: dict | None = None, ): """Create a transport for ARI connections""" - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) serializer = StasisRTPFrameSerializer( StasisRTPFrameSerializer.InputParams( @@ -502,11 +455,7 @@ def create_stasis_transport( if ambient_noise_config and ambient_noise_config.get("enabled", False) else SilenceAudioMixer() ), - turn_analyzer=turn_analyzer, serializer=serializer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, ), ) @@ -528,46 +477,44 @@ def create_internal_transport( Returns: InternalTransport instance configured with turn analyzer """ - turn_analyzer = create_turn_analyzer(workflow_run_id, audio_config) + pass + # Commented out because looptalk coming in the regular import flow + # was causing issue. May be move this to looptalk/orchestrator.py # Create and return the internal transport with latency - return InternalTransport( - params=TransportParams( - audio_out_enabled=True, - audio_out_sample_rate=audio_config.transport_out_sample_rate, - audio_out_channels=1, - audio_in_enabled=True, - audio_in_sample_rate=audio_config.transport_in_sample_rate, - audio_in_channels=1, - vad_analyzer=( - SileroVADAnalyzer( - params=VADParams( - confidence=vad_config.get("confidence", 0.7), - start_secs=vad_config.get("start_seconds", 0.4), - stop_secs=vad_config.get("stop_seconds", 0.8), - min_volume=vad_config.get("minimum_volume", 0.6), - ) - ) - if vad_config - else SileroVADAnalyzer() - ), - audio_out_mixer=( - SoundfileMixer( - sound_files={ - "office": APP_ROOT_DIR - / "assets" - / f"office-ambience-{audio_config.transport_out_sample_rate}-mono.wav" - }, - default_sound="office", - volume=ambient_noise_config.get("volume", 0.3), - ) - if ambient_noise_config and ambient_noise_config.get("enabled", False) - else SilenceAudioMixer() - ), - turn_analyzer=turn_analyzer, - audio_in_filter=RNNoiseFilter(library_path=librnnoise_path) - if ENABLE_RNNOISE - else None, - ), - latency_seconds=latency_seconds, - ) + # return InternalTransport( + # params=TransportParams( + # audio_out_enabled=True, + # audio_out_sample_rate=audio_config.transport_out_sample_rate, + # audio_out_channels=1, + # audio_in_enabled=True, + # audio_in_sample_rate=audio_config.transport_in_sample_rate, + # audio_in_channels=1, + # vad_analyzer=( + # SileroVADAnalyzer( + # params=VADParams( + # confidence=vad_config.get("confidence", 0.7), + # start_secs=vad_config.get("start_seconds", 0.4), + # stop_secs=vad_config.get("stop_seconds", 0.8), + # min_volume=vad_config.get("minimum_volume", 0.6), + # ) + # ) + # if vad_config + # else SileroVADAnalyzer() + # ), + # audio_out_mixer=( + # SoundfileMixer( + # sound_files={ + # "office": APP_ROOT_DIR + # / "assets" + # / f"office-ambience-{audio_config.transport_out_sample_rate}-mono.wav" + # }, + # default_sound="office", + # volume=ambient_noise_config.get("volume", 0.3), + # ) + # if ambient_noise_config and ambient_noise_config.get("enabled", False) + # else SilenceAudioMixer() + # ), + # ), + # latency_seconds=latency_seconds, + # ) diff --git a/api/services/telephony/stasis_rtp_serializer.py b/api/services/telephony/stasis_rtp_serializer.py index 751d68b..c4caf02 100644 --- a/api/services/telephony/stasis_rtp_serializer.py +++ b/api/services/telephony/stasis_rtp_serializer.py @@ -15,6 +15,8 @@ The serializer: from typing import Optional from loguru import logger +from pydantic import BaseModel + from pipecat.audio.utils import create_default_resampler, pcm_to_ulaw, ulaw_to_pcm from pipecat.frames.frames import ( AudioRawFrame, @@ -22,8 +24,7 @@ from pipecat.frames.frames import ( InputAudioRawFrame, StartFrame, ) -from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType -from pydantic import BaseModel +from pipecat.serializers.base_serializer import FrameSerializer class StasisRTPFrameSerializer(FrameSerializer): @@ -59,11 +60,6 @@ class StasisRTPFrameSerializer(FrameSerializer): # Resampler shared between encode / decode paths self._resampler = create_default_resampler() - @property - def type(self) -> FrameSerializerType: - """Stasis uses raw bytes → BINARY.""" - return FrameSerializerType.BINARY - async def setup(self, frame: StartFrame): """Remember pipeline configuration.""" self._sample_rate = self._params.sample_rate or frame.audio_in_sample_rate diff --git a/api/services/workflow/pipecat_engine.py b/api/services/workflow/pipecat_engine.py index 5cbbd5f..d3b3cc1 100644 --- a/api/services/workflow/pipecat_engine.py +++ b/api/services/workflow/pipecat_engine.py @@ -19,7 +19,6 @@ from pipecat.utils.enums import EndTaskReason if TYPE_CHECKING: from api.services.telephony.stasis_rtp_connection import StasisRTPConnection - from pipecat.processors.audio.audio_buffer_processor import AudioBuffer from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.openai.llm import OpenAILLMService @@ -64,7 +63,6 @@ class PipecatEngine: transport: Optional[BaseTransport] = None, workflow: WorkflowGraph, call_context_vars: dict, - audio_buffer: Optional["AudioBuffer"] = None, workflow_run_id: Optional[int] = None, node_transition_callback: Optional[ Callable[[str, Optional[str]], Awaitable[None]] @@ -78,7 +76,6 @@ class PipecatEngine: self.transport = transport self.workflow = workflow self._call_context_vars = call_context_vars - self._audio_buffer = audio_buffer self._workflow_run_id = workflow_run_id self._node_transition_callback = node_transition_callback self._initialized = False @@ -204,6 +201,7 @@ class PipecatEngine: logger.info(f"Arguments: {function_call_params.arguments}") await self.set_node(transition_to_node) try: + async def on_context_updated() -> None: """ pipecat framework will run this function after the function call result has been updated in the context. @@ -215,6 +213,12 @@ class PipecatEngine: self._current_node ) + # Queue EndFrame if we just transitioned to EndNode + if self._current_node.is_end: + await self.send_end_task_frame( + EndTaskReason.USER_QUALIFIED.value + ) + result = {"status": "done"} properties = FunctionCallResultProperties( @@ -478,8 +482,6 @@ class PipecatEngine: if node.extraction_enabled and node.extraction_variables: await self._perform_variable_extraction_if_needed(node) - await self.send_end_task_frame(EndTaskReason.USER_QUALIFIED.value) - async def _handle_agent_node(self, node: Node) -> None: """Handle agent node execution.""" if node.is_static: @@ -680,12 +682,12 @@ class PipecatEngine: """ return engine_callbacks.create_should_mute_callback(self) - def create_user_idle_callback(self): + def create_user_idle_handler(self): """ - This callback is called when the user is idle for a certain duration. - We use this to either play the static text or end the call + Returns a UserIdleHandler that manages user-idle timeouts with state. + The handler tracks retry count and handles escalating prompts. """ - return engine_callbacks.create_user_idle_callback(self) + return engine_callbacks.create_user_idle_handler(self) def create_max_duration_callback(self): """ @@ -721,14 +723,6 @@ class PipecatEngine: """ self.task = task - def set_audio_buffer(self, audio_buffer: "AudioBuffer") -> None: - """Set the audio buffer. - - This allows setting the audio buffer after the engine has been created, - which is useful when the audio buffer needs to be created after the engine. - """ - self._audio_buffer = audio_buffer - def set_stasis_connection( self, connection: Optional["StasisRTPConnection"] ) -> None: diff --git a/api/services/workflow/pipecat_engine_callbacks.py b/api/services/workflow/pipecat_engine_callbacks.py index 6449026..a6ac51c 100644 --- a/api/services/workflow/pipecat_engine_callbacks.py +++ b/api/services/workflow/pipecat_engine_callbacks.py @@ -23,7 +23,6 @@ from pipecat.utils.enums import EndTaskReason if TYPE_CHECKING: from api.services.workflow.pipecat_engine import PipecatEngine - from pipecat.processors.user_idle_processor import UserIdleProcessor # --------------------------------------------------------------------------- @@ -57,33 +56,43 @@ def create_should_mute_callback( # --------------------------------------------------------------------------- -def create_user_idle_callback(engine: "PipecatEngine"): - """Return a callback that handles user-idle timeouts.""" +class UserIdleHandler: + """Helper class to manage user idle retry logic with state.""" - async def handle_user_idle( - user_idle: "UserIdleProcessor", retry_count: int - ) -> bool: - logger.debug(f"Handling user_idle, attempt: {retry_count}") + def __init__(self, engine: "PipecatEngine"): + self._engine = engine + self._retry_count = 0 - if retry_count == 1: + def reset(self): + """Reset the retry count when user becomes active.""" + self._retry_count = 0 + + async def handle_idle(self, aggregator): + """Handle user idle event with escalating prompts.""" + self._retry_count += 1 + logger.debug(f"Handling user_idle, attempt: {self._retry_count}") + + if self._retry_count == 1: message = { "role": "system", "content": "The user has been quiet. Politely and briefly ask if they're still there in the language that the user has been speaking so far.", } - await user_idle.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) - return True + await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) + return message = { "role": "system", "content": "The user has been quiet. We will be disconnecting the call now. Wish them a good day in the language that the user has been speaking so far.", } - await user_idle.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) - await engine.send_end_task_frame( + await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True)) + await self._engine.send_end_task_frame( EndTaskReason.USER_IDLE_MAX_DURATION_EXCEEDED.value ) - return False - return handle_user_idle + +def create_user_idle_handler(engine: "PipecatEngine") -> UserIdleHandler: + """Return a UserIdleHandler that manages user-idle timeouts with state.""" + return UserIdleHandler(engine) # --------------------------------------------------------------------------- diff --git a/api/tasks/arq.py b/api/tasks/arq.py index 8e8f58c..102e94e 100644 --- a/api/tasks/arq.py +++ b/api/tasks/arq.py @@ -49,8 +49,7 @@ from api.tasks.campaign_tasks import ( from api.tasks.knowledge_base_processing import process_knowledge_base_document from api.tasks.run_integrations import run_integrations_post_workflow_run from api.tasks.s3_upload import ( - upload_audio_to_s3, - upload_transcript_to_s3, + process_workflow_completion, upload_voicemail_audio_to_s3, ) @@ -59,9 +58,8 @@ class WorkerSettings: functions = [ calculate_workflow_run_cost, run_integrations_post_workflow_run, - upload_audio_to_s3, - upload_transcript_to_s3, upload_voicemail_audio_to_s3, + process_workflow_completion, sync_campaign_source, process_campaign_batch, monitor_campaign_progress, diff --git a/api/tasks/function_names.py b/api/tasks/function_names.py index c3e7486..e3aeb54 100644 --- a/api/tasks/function_names.py +++ b/api/tasks/function_names.py @@ -1,8 +1,7 @@ class FunctionNames: CALCULATE_WORKFLOW_RUN_COST = "calculate_workflow_run_cost" RUN_INTEGRATIONS_POST_WORKFLOW_RUN = "run_integrations_post_workflow_run" - UPLOAD_AUDIO_TO_S3 = "upload_audio_to_s3" - UPLOAD_TRANSCRIPT_TO_S3 = "upload_transcript_to_s3" + PROCESS_WORKFLOW_COMPLETION = "process_workflow_completion" UPLOAD_VOICEMAIL_AUDIO_TO_S3 = "upload_voicemail_audio_to_s3" SYNC_CAMPAIGN_SOURCE = "sync_campaign_source" PROCESS_CAMPAIGN_BATCH = "process_campaign_batch" diff --git a/api/tasks/run_integrations.py b/api/tasks/run_integrations.py index 44e981e..8a3d77c 100644 --- a/api/tasks/run_integrations.py +++ b/api/tasks/run_integrations.py @@ -1,10 +1,11 @@ """Execute webhook integrations after workflow run completion.""" -from typing import Any, Dict +from typing import Any, Dict, Optional import httpx from loguru import logger +from api.constants import BACKEND_API_ENDPOINT from api.db import db_client from api.db.models import WorkflowRunModel from api.utils.credential_auth import build_auth_header @@ -54,10 +55,13 @@ async def run_integrations_post_workflow_run(_ctx, workflow_run_id: int): logger.info(f"Found {len(webhook_nodes)} webhook nodes to execute") - # Step 4: Build render context - render_context = _build_render_context(workflow_run) + # Step 4: Generate public access token (on-demand, only when webhooks exist) + public_token = await db_client.ensure_public_access_token(workflow_run_id) - # Step 5: Execute each webhook node + # Step 5: Build render context + render_context = _build_render_context(workflow_run, public_token) + + # Step 6: Execute each webhook node for node in webhook_nodes: webhook_data = node.get("data", {}) try: @@ -77,9 +81,19 @@ async def run_integrations_post_workflow_run(_ctx, workflow_run_id: int): raise -def _build_render_context(workflow_run: WorkflowRunModel) -> Dict[str, Any]: - """Build the context dict for template rendering.""" - return { +def _build_render_context( + workflow_run: WorkflowRunModel, public_token: Optional[str] = None +) -> Dict[str, Any]: + """Build the context dict for template rendering. + + Args: + workflow_run: The workflow run model + public_token: Optional public access token for download URLs + + Returns: + Dict containing all fields available for template rendering + """ + context = { # Top-level fields "workflow_run_id": workflow_run.id, "workflow_run_name": workflow_run.name, @@ -89,10 +103,25 @@ def _build_render_context(workflow_run: WorkflowRunModel) -> Dict[str, Any]: "initial_context": workflow_run.initial_context or {}, "gathered_context": workflow_run.gathered_context or {}, "cost_info": workflow_run.usage_info or {}, - "recording_url": getattr(workflow_run, "recording_url", None), - "transcript_url": getattr(workflow_run, "transcript_url", None), } + # Add public download URLs if token is available + if public_token: + base_url = ( + f"{BACKEND_API_ENDPOINT}/api/v1/public/download/workflow/{public_token}" + ) + context["recording_url"] = ( + f"{base_url}/recording" if workflow_run.recording_url else None + ) + context["transcript_url"] = ( + f"{base_url}/transcript" if workflow_run.transcript_url else None + ) + else: + context["recording_url"] = workflow_run.recording_url + context["transcript_url"] = workflow_run.transcript_url + + return context + async def _execute_webhook_node( webhook_data: Dict[str, Any], diff --git a/api/tasks/s3_upload.py b/api/tasks/s3_upload.py index 9faf432..d1c55a6 100644 --- a/api/tasks/s3_upload.py +++ b/api/tasks/s3_upload.py @@ -1,129 +1,27 @@ import os +from typing import Optional from loguru import logger -from pipecat.utils.context import set_current_run_id from api.db import db_client from api.services.storage import get_current_storage_backend, storage_fs - - -async def upload_audio_to_s3(ctx, workflow_run_id: int, temp_file_path: str): - """Upload audio file from temp path to S3.""" - run_id = str(workflow_run_id) - set_current_run_id(run_id) - - logger.info(f"Starting audio upload to S3 from {temp_file_path}") - - try: - # Verify temp file exists - if not os.path.exists(temp_file_path): - logger.error(f"Temp audio file not found: {temp_file_path}") - raise FileNotFoundError(f"Temp audio file not found: {temp_file_path}") - - file_size = os.path.getsize(temp_file_path) - logger.debug(f"Audio file size: {file_size} bytes") - - recording_url = f"recordings/{workflow_run_id}.wav" - storage_backend = get_current_storage_backend() - - logger.info( - f"UPLOAD: Using {storage_backend.name} (value: {storage_backend.value}) for audio upload - workflow_run_id: {workflow_run_id}" - ) - - await storage_fs.aupload_file(temp_file_path, recording_url) - - # Update DB with recording URL and storage backend - await db_client.update_workflow_run( - run_id=workflow_run_id, - recording_url=recording_url, - storage_backend=storage_backend.value, - ) - - logger.info( - f"Successfully uploaded audio to {storage_backend.name}: {recording_url} (stored backend: {storage_backend.name})" - ) - - except Exception as e: - logger.error(f"Error uploading audio to S3 for workflow {workflow_run_id}: {e}") - raise - finally: - # Clean up temp file - if os.path.exists(temp_file_path): - try: - os.remove(temp_file_path) - logger.debug(f"Cleaned up temp audio file: {temp_file_path}") - except Exception as e: - logger.warning( - f"Failed to clean up temp audio file {temp_file_path}: {e}" - ) - - -async def upload_transcript_to_s3(ctx, workflow_run_id: int, temp_file_path: str): - """Upload transcript file from temp path to S3.""" - run_id = str(workflow_run_id) - set_current_run_id(run_id) - - logger.info(f"Starting transcript upload to S3 from {temp_file_path}") - - try: - # Verify temp file exists - if not os.path.exists(temp_file_path): - logger.error(f"Temp transcript file not found: {temp_file_path}") - raise FileNotFoundError(f"Temp transcript file not found: {temp_file_path}") - - file_size = os.path.getsize(temp_file_path) - logger.debug(f"Transcript file size: {file_size} bytes") - - transcript_url = f"transcripts/{workflow_run_id}.txt" - storage_backend = get_current_storage_backend() - - logger.info( - f"UPLOAD: Using {storage_backend.name} (value: {storage_backend.value}) for transcript upload - workflow_run_id: {workflow_run_id}" - ) - - await storage_fs.aupload_file(temp_file_path, transcript_url) - - # Update DB with transcript URL and storage backend - await db_client.update_workflow_run( - run_id=workflow_run_id, - transcript_url=transcript_url, - storage_backend=storage_backend.value, - ) - - logger.info( - f"Successfully uploaded transcript to {storage_backend.name}: {transcript_url} (stored backend: {storage_backend.name})" - ) - - except Exception as e: - logger.error( - f"Error uploading transcript to S3 for workflow {workflow_run_id}: {e}" - ) - raise - finally: - # Clean up temp file - if os.path.exists(temp_file_path): - try: - os.remove(temp_file_path) - logger.debug(f"Cleaned up temp transcript file: {temp_file_path}") - except Exception as e: - logger.warning( - f"Failed to clean up temp transcript file {temp_file_path}: {e}" - ) +from api.tasks.run_integrations import run_integrations_post_workflow_run +from pipecat.utils.context import set_current_run_id async def upload_voicemail_audio_to_s3( - ctx, + _ctx, workflow_run_id: int, temp_file_path: str, s3_key: str, ): """Upload voicemail detection audio from temp file to S3. - This function is similar to upload_audio_to_s3 but handles voicemail-specific - paths and doesn't update the workflow run's recording_url field. + Handles voicemail-specific paths and doesn't update the workflow run's + recording_url field. Args: - ctx: ARQ context + _ctx: ARQ context (unused) workflow_run_id: The workflow run ID temp_file_path: Path to the temporary WAV file s3_key: The S3 key where the file should be uploaded @@ -161,7 +59,7 @@ async def upload_voicemail_audio_to_s3( ) raise finally: - # Clean up temp file (same pattern as upload_audio_to_s3) + # Clean up temp file if os.path.exists(temp_file_path): try: os.remove(temp_file_path) @@ -170,3 +68,104 @@ async def upload_voicemail_audio_to_s3( logger.warning( f"Failed to clean up temp voicemail audio file {temp_file_path}: {e}" ) + + +async def process_workflow_completion( + _ctx, + workflow_run_id: int, + audio_temp_path: Optional[str] = None, + transcript_temp_path: Optional[str] = None, +): + """Process workflow completion: upload artifacts and run integrations. + + This task combines audio upload, transcript upload, and webhook integrations + into a single sequential task to ensure integrations run after uploads complete. + + Args: + _ctx: ARQ context (unused) + workflow_run_id: The workflow run ID + audio_temp_path: Optional path to temp audio file + transcript_temp_path: Optional path to temp transcript file + """ + run_id = str(workflow_run_id) + set_current_run_id(run_id) + + logger.info(f"Processing workflow completion for run {workflow_run_id}") + + storage_backend = get_current_storage_backend() + + # Step 1: Upload audio if provided + if audio_temp_path: + try: + if os.path.exists(audio_temp_path): + file_size = os.path.getsize(audio_temp_path) + logger.debug(f"Audio file size: {file_size} bytes") + + recording_url = f"recordings/{workflow_run_id}.wav" + logger.info( + f"Uploading audio to {storage_backend.name} - workflow_run_id: {workflow_run_id}" + ) + + await storage_fs.aupload_file(audio_temp_path, recording_url) + await db_client.update_workflow_run( + run_id=workflow_run_id, + recording_url=recording_url, + storage_backend=storage_backend.value, + ) + logger.info(f"Successfully uploaded audio: {recording_url}") + else: + logger.warning(f"Audio temp file not found: {audio_temp_path}") + except Exception as e: + logger.error(f"Error uploading audio for workflow {workflow_run_id}: {e}") + finally: + if audio_temp_path and os.path.exists(audio_temp_path): + try: + os.remove(audio_temp_path) + logger.debug(f"Cleaned up temp audio file: {audio_temp_path}") + except Exception as e: + logger.warning(f"Failed to clean up temp audio file: {e}") + + # Step 2: Upload transcript if provided + if transcript_temp_path: + try: + if os.path.exists(transcript_temp_path): + file_size = os.path.getsize(transcript_temp_path) + logger.debug(f"Transcript file size: {file_size} bytes") + + transcript_url = f"transcripts/{workflow_run_id}.txt" + logger.info( + f"Uploading transcript to {storage_backend.name} - workflow_run_id: {workflow_run_id}" + ) + + await storage_fs.aupload_file(transcript_temp_path, transcript_url) + await db_client.update_workflow_run( + run_id=workflow_run_id, + transcript_url=transcript_url, + storage_backend=storage_backend.value, + ) + logger.info(f"Successfully uploaded transcript: {transcript_url}") + else: + logger.warning( + f"Transcript temp file not found: {transcript_temp_path}" + ) + except Exception as e: + logger.error( + f"Error uploading transcript for workflow {workflow_run_id}: {e}" + ) + finally: + if transcript_temp_path and os.path.exists(transcript_temp_path): + try: + os.remove(transcript_temp_path) + logger.debug( + f"Cleaned up temp transcript file: {transcript_temp_path}" + ) + except Exception as e: + logger.warning(f"Failed to clean up temp transcript file: {e}") + + # Step 3: Run webhook integrations (after uploads are complete) + try: + await run_integrations_post_workflow_run(_ctx, workflow_run_id) + except Exception as e: + logger.error(f"Error running integrations for workflow {workflow_run_id}: {e}") + + logger.info(f"Completed workflow completion processing for run {workflow_run_id}") diff --git a/api/tests/conftest.py b/api/tests/conftest.py index 9f97778..a7fe452 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass -from typing import Any, Dict +from dataclasses import dataclass, field +from typing import Any, Dict, Optional from unittest.mock import Mock import pytest @@ -28,6 +28,87 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor START_CALL_SYSTEM_PROMPT = "start_call_system_prompt" END_CALL_SYSTEM_PROMPT = "end_call_system_prompt" +# Default workflow definition for mocking database WorkflowModel +DEFAULT_WORKFLOW_DEFINITION = { + "nodes": [ + { + "id": "1", + "type": "startCall", + "position": {"x": 0, "y": 0}, + "data": { + "name": "Start", + "prompt": START_CALL_SYSTEM_PROMPT, + "is_start": True, + "allow_interrupt": False, + "add_global_prompt": False, + }, + }, + { + "id": "2", + "type": "endCall", + "position": {"x": 0, "y": 200}, + "data": { + "name": "End", + "prompt": END_CALL_SYSTEM_PROMPT, + "is_end": True, + "allow_interrupt": False, + "add_global_prompt": False, + }, + }, + ], + "edges": [ + { + "id": "1-2", + "source": "1", + "target": "2", + "data": {"label": "End", "condition": "End the call"}, + } + ], +} + + +@dataclass +class MockWorkflowModel: + """Mock database WorkflowModel for testing. + + This mimics the structure of the database WorkflowModel, not the parsed WorkflowGraph. + Use this when mocking db_client.get_workflow() responses. + """ + + workflow_id: int = 1 + organization_id: int = 1 + workflow_configurations: Dict[str, Any] = field(default_factory=dict) + workflow_definition_with_fallback: Dict[str, Any] = field(default_factory=dict) + + def __post_init__(self): + if not self.workflow_definition_with_fallback: + self.workflow_definition_with_fallback = DEFAULT_WORKFLOW_DEFINITION.copy() + + +@dataclass +class MockWorkflowRun: + """Mock database WorkflowRun for testing. + + Use this when mocking db_client.get_workflow_run() responses. + """ + + is_completed: bool = False + initial_context: Dict[str, Any] = field(default_factory=dict) + gathered_context: Dict[str, Any] = field(default_factory=dict) + + +@dataclass +class MockUserConfig: + """Mock user configuration for testing. + + Use this when mocking db_client.get_user_configurations() responses. + """ + + stt: Optional[Any] = None + tts: Optional[Any] = None + llm: Optional[Any] = None + embeddings: Optional[Any] = None + class MockTransportProcessor(FrameProcessor): """ @@ -41,7 +122,7 @@ class MockTransportProcessor(FrameProcessor): Args: emit_bot_speaking: If True, also emits BotSpeakingFrame on TTSAudioRawFrame - which is needed for UserIdleProcessor to start conversation tracking. Default True. + which is needed for user idle tracking to start conversation tracking. Default True. """ def __init__( @@ -63,7 +144,7 @@ class MockTransportProcessor(FrameProcessor): BotStartedSpeakingFrame(), direction=FrameDirection.UPSTREAM ) elif isinstance(frame, TTSAudioRawFrame): - # Emit BotSpeakingFrame - this is what triggers the UserIdleProcessor + # Emit BotSpeakingFrame - this is what triggers user idle tracking # to start conversation tracking if self._emit_bot_speaking: await self.push_frame(BotSpeakingFrame()) @@ -101,6 +182,24 @@ def mock_engine(): return engine +@pytest.fixture +def mock_workflow_model(): + """Create a mock WorkflowModel for testing database responses.""" + return MockWorkflowModel() + + +@pytest.fixture +def mock_workflow_run(): + """Create a mock WorkflowRun for testing database responses.""" + return MockWorkflowRun() + + +@pytest.fixture +def mock_user_config(): + """Create a mock user configuration for testing.""" + return MockUserConfig() + + @pytest.fixture def sample_tools(): """Create sample mock tools for testing.""" diff --git a/api/tests/test_pipecat_engine_context_update.py b/api/tests/test_pipecat_engine_context_update.py index e37db03..1a67665 100644 --- a/api/tests/test_pipecat_engine_context_update.py +++ b/api/tests/test_pipecat_engine_context_update.py @@ -42,7 +42,6 @@ from pipecat.processors.aggregators.llm_response_universal import ( ) from pipecat.tests import MockLLMService, MockTTSService - # Define prompts for test nodes START_NODE_PROMPT = "Start Node System Prompt" AGENT_NODE_PROMPT = "Agent Node System Prompt" @@ -143,14 +142,20 @@ class ContextCapturingMockLLM(MockLLMService): msg_copy = dict(msg) # Copy content to avoid reference issues if "content" in msg_copy: - msg_copy["content"] = str(msg_copy["content"]) if msg_copy["content"] else None + msg_copy["content"] = ( + str(msg_copy["content"]) if msg_copy["content"] else None + ) messages_snapshot.append(msg_copy) - self.captured_contexts.append({ - "step": self._current_step, - "messages": messages_snapshot, - "system_prompt": messages_snapshot[0]["content"] if messages_snapshot else None, - }) + self.captured_contexts.append( + { + "step": self._current_step, + "messages": messages_snapshot, + "system_prompt": messages_snapshot[0]["content"] + if messages_snapshot + else None, + } + ) # Call parent implementation to stream the mock chunks return await super()._stream_chat_completions_universal_context(context) @@ -306,14 +311,26 @@ class TestContextUpdateBeforeNextCompletion: transition completes. The test verifies the context is still correctly updated. """ # Step 0 (Start node): call collect_info to transition to agent - step_0_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "collect_info", "arguments": {}, "tool_call_id": "call_transition_1"}, - ]) + step_0_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "collect_info", + "arguments": {}, + "tool_call_id": "call_transition_1", + }, + ] + ) # Step 1 (Agent node): call end_call to transition to end - step_1_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "end_call", "arguments": {}, "tool_call_id": "call_transition_2"}, - ]) + step_1_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "end_call", + "arguments": {}, + "tool_call_id": "call_transition_2", + }, + ] + ) # Step 2 (End node): text response (end node has no outgoing edges) step_2_chunks = MockLLMService.create_text_chunks("Goodbye!") @@ -327,7 +344,7 @@ class TestContextUpdateBeforeNextCompletion: ) # Should have been called 3 times: start node, agent node, end node - assert llm.get_current_step() == 2, ( + assert llm.get_current_step() == 3, ( f"Expected 3 LLM generations (start, agent, end), got {llm.get_current_step()}" ) @@ -376,14 +393,26 @@ class TestContextUpdateBeforeNextCompletion: is handled correctly. """ # Step 0 (Start node): call collect_info to transition to agent - step_0_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "collect_info", "arguments": {}, "tool_call_id": "call_transition_1"}, - ]) + step_0_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "collect_info", + "arguments": {}, + "tool_call_id": "call_transition_1", + }, + ] + ) # Step 1 (Agent node): call end_call to transition to end - step_1_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "end_call", "arguments": {}, "tool_call_id": "call_transition_2"}, - ]) + step_1_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "end_call", + "arguments": {}, + "tool_call_id": "call_transition_2", + }, + ] + ) # Step 2 (End node): text response step_2_chunks = MockLLMService.create_text_chunks("Goodbye!") @@ -397,7 +426,7 @@ class TestContextUpdateBeforeNextCompletion: ) # Verify all three nodes were executed - assert llm.get_current_step() == 2, ( + assert llm.get_current_step() == 3, ( f"Expected 3 steps, got {llm.get_current_step()}" ) @@ -408,8 +437,7 @@ class TestContextUpdateBeforeNextCompletion: assert AGENT_NODE_PROMPT in llm.get_system_prompt_at_step(1) # Step 2: End node - should have end prompt - # FIXME - EndFrame is getting processed before LLMContextFrame - # assert END_NODE_PROMPT in llm.get_system_prompt_at_step(2) + assert END_NODE_PROMPT in llm.get_system_prompt_at_step(2) # Verify each subsequent step has the previous tool results step_1_ctx = llm.get_context_at_step(1) @@ -423,14 +451,14 @@ class TestContextUpdateBeforeNextCompletion: assert step_1_has_tool, "Agent node should see collect_info tool result" # Step 2 should have tool results from both transitions - # FIXME - EndFrame is getting processed before LLMContextFrame - # step_2_tool_messages = [ - # msg for msg in step_2_ctx["messages"] - # if msg.get("role") == "tool" or msg.get("tool_call_id") - # ] - # assert len(step_2_tool_messages) >= 2, ( - # f"End node should see at least 2 tool results, got {len(step_2_tool_messages)}" - # ) + step_2_tool_messages = [ + msg + for msg in step_2_ctx["messages"] + if msg.get("role") == "tool" or msg.get("tool_call_id") + ] + assert len(step_2_tool_messages) >= 2, ( + f"End node should see at least 2 tool results, got {len(step_2_tool_messages)}" + ) @pytest.mark.asyncio async def test_context_messages_preserve_conversation_history( @@ -444,14 +472,26 @@ class TestContextUpdateBeforeNextCompletion: - Tool call messages and results """ # Step 0 (Start node): call collect_info to transition to agent - step_0_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "collect_info", "arguments": {}, "tool_call_id": "call_transition_1"}, - ]) + step_0_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "collect_info", + "arguments": {}, + "tool_call_id": "call_transition_1", + }, + ] + ) # Step 1 (Agent node): call end_call to transition to end - step_1_chunks = MockLLMService.create_multiple_function_call_chunks([ - {"name": "end_call", "arguments": {}, "tool_call_id": "call_transition_2"}, - ]) + step_1_chunks = MockLLMService.create_multiple_function_call_chunks( + [ + { + "name": "end_call", + "arguments": {}, + "tool_call_id": "call_transition_2", + }, + ] + ) # Step 2 (End node): text response step_2_chunks = MockLLMService.create_text_chunks("Goodbye!") @@ -472,18 +512,15 @@ class TestContextUpdateBeforeNextCompletion: assert len(ctx_1["messages"]) > len(ctx_0["messages"]), ( "Context at step 1 should have more messages than step 0" ) - - # FIXME - # assert len(ctx_2["messages"]) > len(ctx_1["messages"]), ( - # "Context at step 2 should have more messages than step 1" - # ) + + assert len(ctx_2["messages"]) > len(ctx_1["messages"]), ( + "Context at step 2 should have more messages than step 1" + ) # Verify assistant messages are accumulated - # FIXME - # assistant_messages_at_step_2 = [ - # msg for msg in ctx_2["messages"] - # if msg.get("role") == "assistant" - # ] - # assert len(assistant_messages_at_step_2) >= 2, ( - # "Should have at least 2 assistant messages by step 2" - # ) + assistant_messages_at_step_2 = [ + msg for msg in ctx_2["messages"] if msg.get("role") == "assistant" + ] + assert len(assistant_messages_at_step_2) >= 2, ( + "Should have at least 2 assistant messages by step 2" + ) diff --git a/api/tests/test_pipeline_cancellation.py b/api/tests/test_pipeline_cancellation.py new file mode 100644 index 0000000..45f04c5 --- /dev/null +++ b/api/tests/test_pipeline_cancellation.py @@ -0,0 +1,100 @@ +import asyncio + +import pytest +from loguru import logger + +from pipecat.frames.frames import ( + EndTaskFrame, + Frame, + InterruptionTaskFrame, + LLMRunFrame, +) +from pipecat.pipeline.base_task import PipelineTaskParams +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.task import PipelineTask +from pipecat.processors.frame_processor import FrameDirection, FrameProcessor + + +class MockTransport(FrameProcessor): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + await self.push_frame(frame, direction) + + +class BusyWaitProcessor(FrameProcessor): + def __init__(self, wait_time=5.0, **kwargs): + super().__init__(**kwargs) + self._wait_time = wait_time + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + if isinstance(frame, LLMRunFrame): + # Simulate a delay, which can happen sometimes due to slow LLM Inferencing or + # other reasons + try: + logger.debug(f"{self} sleeping with frame: {frame}") + await asyncio.sleep(5) + logger.debug(f"{self} woke up with frame: {frame}") + except asyncio.CancelledError: + logger.debug(f"{self} was cancelled") + raise + await self.push_frame(frame, direction) + + +@pytest.mark.asyncio +async def test_interruption_with_blocked_end_frame(): + busy_wait_processor = BusyWaitProcessor(wait_time=5) + transport = MockTransport() + pipeline = Pipeline([transport, busy_wait_processor]) + + task = PipelineTask(pipeline) + + async def run_pipeline(): + loop = asyncio.get_running_loop() + params = PipelineTaskParams(loop=loop) + await task.run(params=params) + + async def queue_frame(): + await task.queue_frames([LLMRunFrame()]) + + # Send EndTaskFrame to simulate EndFrame + await asyncio.sleep(0.1) + await transport.queue_frame(EndTaskFrame(), direction=FrameDirection.UPSTREAM) + + # Simulate an Interruption, which can happen if the user + # has started to speak + await asyncio.sleep(0.1) + await transport.queue_frame( + InterruptionTaskFrame(), direction=FrameDirection.UPSTREAM + ) + + # Create tasks explicitly for better control + pipeline_task = asyncio.create_task(run_pipeline()) + queue_task = asyncio.create_task(queue_frame()) + + # Wait with timeout + done, pending = await asyncio.wait( + [pipeline_task, queue_task], + timeout=1.0, + return_when=asyncio.ALL_COMPLETED, + ) + + # If there are pending tasks, we timed out + if pending: + # Cancel all pending tasks + for t in pending: + t.cancel() + + # Give limited time for cleanup, then move on regardless + try: + await asyncio.wait_for( + asyncio.gather(*pending, return_exceptions=True), + timeout=1.0, + ) + except asyncio.TimeoutError: + pass # Cleanup took too long, continue anyway + + pytest.fail("Test timed out after 1 second") diff --git a/api/tests/test_user_idle_handler.py b/api/tests/test_user_idle_handler.py index 4d73abf..ac5252c 100644 --- a/api/tests/test_user_idle_handler.py +++ b/api/tests/test_user_idle_handler.py @@ -1,10 +1,10 @@ """ Simulates a user idle condition and tests the behaviour -of the user idle processor. +of the user idle handler. This module tests the behavior when the user becomes idle during a conversation, -ensuring the UserIdleProcessor properly triggers the callback and the engine -handles it correctly. +ensuring the user_idle_timeout in LLMUserAggregatorParams properly triggers +the on_user_turn_idle event and the engine handles it correctly. """ import asyncio @@ -23,8 +23,8 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response import LLMAssistantAggregatorParams from pipecat.processors.aggregators.llm_response_universal import ( LLMContextAggregatorPair, + LLMUserAggregatorParams, ) -from pipecat.processors.user_idle_processor import UserIdleProcessor from pipecat.tests import MockLLMService, MockTTSService @@ -32,8 +32,8 @@ async def run_pipeline_with_user_idle( workflow: WorkflowGraph, user_idle_timeout: float = 0.2, mock_steps: list | None = None, -) -> tuple[MockLLMService, LLMContext, UserIdleProcessor]: - """Run a pipeline with UserIdleProcessor and simulate user idle condition. +) -> tuple[MockLLMService, LLMContext]: + """Run a pipeline with user_idle_timeout and simulate user idle condition. Args: workflow: The workflow graph to use. @@ -42,7 +42,7 @@ async def run_pipeline_with_user_idle( defaults to a simple greeting followed by text responses. Returns: - Tuple of (MockLLMService, LLMContext, UserIdleProcessor) for assertions. + Tuple of (MockLLMService, LLMContext) for assertions. """ # Create mock responses - bot will speak first, then respond to idle prompts # Step 1: Initial greeting @@ -64,10 +64,11 @@ async def run_pipeline_with_user_idle( # Create LLM context context = LLMContext() - # Create context aggregator with both user and assistant aggregators + # Create context aggregator with user_idle_timeout in user_params assistant_params = LLMAssistantAggregatorParams(expect_stripped_words=True) + user_params = LLMUserAggregatorParams(user_idle_timeout=user_idle_timeout) context_aggregator = LLMContextAggregatorPair( - context, assistant_params=assistant_params + context, assistant_params=assistant_params, user_params=user_params ) user_context_aggregator = context_aggregator.user() assistant_context_aggregator = context_aggregator.assistant() @@ -81,18 +82,20 @@ async def run_pipeline_with_user_idle( workflow_run_id=1, ) - # Create UserIdleProcessor with engine's callback and a short timeout - user_idle_processor = UserIdleProcessor( - callback=engine.create_user_idle_callback(), - timeout=user_idle_timeout, - ) + # Register user idle event handlers + user_idle_handler = engine.create_user_idle_handler() - # Build the pipeline: - # llm -> mock_transport -> user_idle_processor -> assistant_context_aggregator - # The user_context_aggregator would normally be at the start for user input + @user_context_aggregator.event_handler("on_user_turn_idle") + async def on_user_turn_idle(aggregator): + await user_idle_handler.handle_idle(aggregator) + + @user_context_aggregator.event_handler("on_user_turn_started") + async def on_user_turn_started(aggregator, strategy): + user_idle_handler.reset() + + # Build the pipeline pipeline = Pipeline( [ - user_idle_processor, user_context_aggregator, llm, tts, @@ -154,11 +157,11 @@ async def run_pipeline_with_user_idle( return_exceptions=True, ) - return llm, context, user_idle_processor + return llm, context class TestUserIdleHandler: - """Test user idle handling through PipecatEngine and UserIdleProcessor.""" + """Test user idle handling through PipecatEngine and UserIdleHandler.""" @pytest.mark.asyncio async def test_user_idle_triggers_callback(self, simple_workflow: WorkflowGraph): @@ -167,13 +170,13 @@ class TestUserIdleHandler: This test verifies that when: 1. The bot starts speaking (triggers conversation tracking) 2. No user input is received for the timeout period - 3. The UserIdleProcessor triggers the idle callback + 3. The on_user_turn_idle event triggers the idle handler - The engine's user idle callback should: + The engine's user idle handler should: - First retry: Send a message asking if user is still there - Second retry: Send goodbye message and end the call """ - llm, context, user_idle_processor = await run_pipeline_with_user_idle( + llm, context = await run_pipeline_with_user_idle( workflow=simple_workflow, user_idle_timeout=0.2, # Short timeout for faster test ) @@ -220,7 +223,7 @@ class TestUserIdleHandler: MockLLMService.create_text_chunks("Response 3"), ] - llm, context, user_idle_processor = await run_pipeline_with_user_idle( + llm, context = await run_pipeline_with_user_idle( workflow=three_node_workflow, user_idle_timeout=0.2, mock_steps=mock_steps, diff --git a/docker-compose.yaml b/docker-compose.yaml index 26f2144..7dc661f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:17 + image: pgvector/pgvector:pg17 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -83,6 +83,10 @@ services: ENVIRONMENT: "local" LOG_LEVEL: "INFO" + # Replace this environment variable if you are using a custom + # domain to host the stack + BACKEND_API_ENDPOINT: "http://localhost:8000" + # Database configuration (using containerized postgres) DATABASE_URL: "postgresql+asyncpg://postgres:postgres@postgres:5432/postgres" diff --git a/docs/deployment/custom-domain.mdx b/docs/deployment/custom-domain.mdx index 073b080..e2bedbb 100644 --- a/docs/deployment/custom-domain.mdx +++ b/docs/deployment/custom-domain.mdx @@ -162,6 +162,10 @@ server { } ``` +### Add environment variable + +Replace `BACKEND_API_ENDPOINT` environment variable the `docker-compose.yaml` with your custom domain with the scheme. + ### Start Dograh Services Start Dograh with the updated configuration: diff --git a/evals/stt/README.md b/evals/stt/README.md new file mode 100644 index 0000000..6e2802b --- /dev/null +++ b/evals/stt/README.md @@ -0,0 +1,135 @@ +# STT Evaluation Benchmark + +Benchmark for comparing Speech-to-Text providers using **WebSocket streaming** with focus on: +- **Speaker diarization** - identifying who said what +- **Keyterm boosting** - improving recognition of specific terms (Deepgram) + +## Providers + +| Provider | Diarization | Keyterm Boost | Streaming | +|----------|-------------|---------------|-----------| +| Deepgram | Yes | Yes | WebSocket (v1/v2) | +| Speechmatics | Yes | Additional vocab | WebSocket RT | + +## Setup + +```bash +# Install dependencies +pip install websockets + +# Set API keys +export DEEPGRAM_API_KEY="your-key" +export SPEECHMATICS_API_KEY="your-key" +``` + +**Note:** Requires `ffmpeg` installed for audio conversion to PCM16. + +## Usage + +Run from the project root directory: + +```bash +# Test both providers with diarization +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize + +# Test only Deepgram +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --providers deepgram + +# Test with keyterm boosting (Deepgram) +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --keyterms "Dograh" "Pipecat" + +# Use different sample rate (default: 8000 Hz) +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --sample-rate 16000 + +# Show word-level timings +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --show-words + +# Save results to JSON +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --save +``` + +## CLI Options + +| Option | Description | +|--------|-------------| +| `audio_file` | Path to audio file (relative to evals/stt/ or absolute) | +| `--providers` | Providers to test: `deepgram`, `speechmatics` (default: both) | +| `--diarize` | Enable speaker diarization | +| `--keyterms` | Keywords to boost (Deepgram) / additional vocab (Speechmatics) | +| `--language` | Language code (default: en) | +| `--sample-rate` | Audio sample rate for streaming (default: 8000) | +| `--show-words` | Show individual word timings | +| `--save` | Save results to JSON in `results/` | + +## Directory Structure + +``` +evals/stt/ +├── audio/ # Audio test files +│ └── multi_speaker.m4a +├── results/ # Saved benchmark results (JSON) +├── providers/ # STT provider implementations +│ ├── base.py # Base classes +│ ├── deepgram_provider.py # WebSocket streaming +│ └── speechmatics_provider.py # WebSocket streaming +├── audio_streamer.py # PCM16 audio file streamer +├── benchmark.py # Main runner script +└── README.md +``` + +## How It Works + +1. **Audio Conversion**: The `AudioStreamer` converts any audio file to raw PCM16 using ffmpeg +2. **WebSocket Connection**: Providers connect to their respective WebSocket APIs +3. **Streaming**: Audio is sent in chunks (configurable sample rate, default 8kHz) +4. **Result Collection**: Transcripts and speaker info are collected from WebSocket responses +5. **Comparison**: Results are parsed into a common format for comparison + +## Output Example + +``` +Audio file: /path/to/audio/multi_speaker.m4a +Providers: ['deepgram', 'speechmatics'] +Diarization: True +Sample rate: 8000 Hz + +============================================================ +Provider: DEEPGRAM +============================================================ + +Duration: 45.32s +Speakers detected: 2 - ['0', '1'] + +Transcript: +Hello, welcome to the demo... + +--- Speaker Segments --- +[0.0s] Speaker 0: Hello, welcome to the demo. +[2.5s] Speaker 1: Thanks for having me. +... + +============================================================ +COMPARISON SUMMARY +============================================================ + +Provider Duration Speakers Words +--------------------------------------------- +deepgram 45.32 2 312 +speechmatics 45.32 2 308 +``` + +## Adding New Providers + +1. Create a new file in `providers/` (e.g., `whisper_provider.py`) +2. Implement the `STTProvider` abstract class with WebSocket streaming +3. Use `AudioStreamer` for PCM16 conversion +4. Add to `providers/__init__.py` +5. Add to `benchmark.py` provider choices + +## API Documentation + +- Deepgram Streaming: https://developers.deepgram.com/docs/live-streaming-audio +- Deepgram Diarization: https://developers.deepgram.com/docs/diarization +- Deepgram Keyterms: https://developers.deepgram.com/docs/keyterm +- Speechmatics RT API: https://docs.speechmatics.com/rt-api-ref +- Speechmatics Diarization: https://docs.speechmatics.com/features/diarization diff --git a/evals/stt/__init__.py b/evals/stt/__init__.py new file mode 100644 index 0000000..8c12ae8 --- /dev/null +++ b/evals/stt/__init__.py @@ -0,0 +1 @@ +# STT Evaluation Benchmark diff --git a/evals/stt/audio/multi_speaker.m4a b/evals/stt/audio/multi_speaker.m4a new file mode 100644 index 0000000000000000000000000000000000000000..d1ab1f5e5cb6a4aa7e9356cb4b1f4d11e6abf5bb GIT binary patch literal 63136 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 literal 0 HcmV?d00001 diff --git a/evals/stt/audio/nope.m4a b/evals/stt/audio/nope.m4a new file mode 100644 index 0000000000000000000000000000000000000000..f9c86c2dbade54415cf4cc38d30c5657fc930452 GIT binary patch literal 27884 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 literal 0 HcmV?d00001 diff --git a/evals/stt/audio/not_so_sure.m4a b/evals/stt/audio/not_so_sure.m4a new file mode 100644 index 0000000000000000000000000000000000000000..acaae9ad6f1dff0828c865e7b130862ee2a00e51 GIT binary patch literal 32543 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 literal 0 HcmV?d00001 diff --git a/evals/stt/audio/vad.m4a b/evals/stt/audio/vad.m4a new file mode 100644 index 0000000000000000000000000000000000000000..e0488c7ac8882fae38ba5f4725bc6ed1ce3cd3e5 GIT binary patch literal 111765 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 !Z6sT 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 K-v0j)bN&mU&{(ek literal 0 HcmV?d00001 diff --git a/evals/stt/audio/yes.m4a b/evals/stt/audio/yes.m4a new file mode 100644 index 0000000000000000000000000000000000000000..3c6e5a99a80ab0b02c15127593b6cf5162929ed0 GIT binary patch literal 21134 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 Wf1tL?`EZ%c-zx2K)cP+f{(l3CJ=p31 literal 0 HcmV?d00001 diff --git a/evals/stt/audio_streamer.py b/evals/stt/audio_streamer.py new file mode 100644 index 0000000..d9c98fc --- /dev/null +++ b/evals/stt/audio_streamer.py @@ -0,0 +1,140 @@ +"""Audio file streamer - converts audio files to PCM16 streams.""" + +import asyncio +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import AsyncIterator + + +@dataclass +class AudioConfig: + """Audio streaming configuration.""" + + sample_rate: int = 8000 + channels: int = 1 + sample_width: int = 2 # 16-bit = 2 bytes + chunk_duration_ms: int = 80 # Send chunks every 80ms + + @property + def chunk_size(self) -> int: + """Bytes per chunk based on duration.""" + samples_per_chunk = int(self.sample_rate * self.chunk_duration_ms / 1000) + return samples_per_chunk * self.channels * self.sample_width + + +class AudioStreamer: + """Streams audio files as PCM16 chunks. + + Converts any audio format to raw PCM16 using ffmpeg and streams + in real-time chunks to simulate live audio. + """ + + def __init__(self, config: AudioConfig | None = None): + self.config = config or AudioConfig() + + def convert_to_pcm16(self, audio_path: Path) -> bytes: + """Convert audio file to raw PCM16 bytes using ffmpeg. + + Args: + audio_path: Path to input audio file + + Returns: + Raw PCM16 audio bytes + """ + cmd = [ + "ffmpeg", + "-i", + str(audio_path), + "-f", + "s16le", # signed 16-bit little-endian + "-acodec", + "pcm_s16le", + "-ar", + str(self.config.sample_rate), + "-ac", + str(self.config.channels), + "-", # output to stdout + ] + + result = subprocess.run( + cmd, + capture_output=True, + check=True, + ) + return result.stdout + + async def stream_file( + self, + audio_path: Path, + realtime: bool = True, + trailing_silence_seconds: float = 0.0, + ) -> AsyncIterator[bytes]: + """Stream audio file as PCM16 chunks. + + Args: + audio_path: Path to audio file + realtime: If True, add delays to simulate real-time streaming + trailing_silence_seconds: Seconds of silence to append after audio ends. + Useful for capturing pending end-of-turn events from STT providers. + + Yields: + PCM16 audio chunks + """ + # Convert entire file to PCM16 + pcm_data = self.convert_to_pcm16(audio_path) + + chunk_size = self.config.chunk_size + delay = self.config.chunk_duration_ms / 1000.0 if realtime else 0 + + # Stream audio chunks + for i in range(0, len(pcm_data), chunk_size): + chunk = pcm_data[i : i + chunk_size] + if chunk: + yield chunk + if realtime and delay > 0: + await asyncio.sleep(delay) + + # Stream trailing silence if requested + if trailing_silence_seconds > 0: + silence_chunk = bytes(chunk_size) # Zero-filled bytes = silence + num_silence_chunks = int(trailing_silence_seconds / (self.config.chunk_duration_ms / 1000.0)) + + for _ in range(num_silence_chunks): + yield silence_chunk + if realtime and delay > 0: + await asyncio.sleep(delay) + + async def stream_file_fast(self, audio_path: Path) -> AsyncIterator[bytes]: + """Stream audio file as fast as possible (no real-time delay). + + Args: + audio_path: Path to audio file + + Yields: + PCM16 audio chunks + """ + async for chunk in self.stream_file(audio_path, realtime=False): + yield chunk + + def get_duration(self, audio_path: Path) -> float: + """Get audio file duration in seconds. + + Args: + audio_path: Path to audio file + + Returns: + Duration in seconds + """ + cmd = [ + "ffprobe", + "-v", + "error", + "-show_entries", + "format=duration", + "-of", + "default=noprint_wrappers=1:nokey=1", + str(audio_path), + ] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return float(result.stdout.strip()) diff --git a/evals/stt/benchmark.py b/evals/stt/benchmark.py new file mode 100644 index 0000000..7740cee --- /dev/null +++ b/evals/stt/benchmark.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +"""STT Benchmark Runner. + +Compare speech-to-text transcription across providers with focus on: +- Speaker diarization accuracy +- Keyword/keyterm recognition +- Transcription quality + +Usage: + python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize + python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --providers deepgram + python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --keyterms "Dograh" "Pipecat" +""" + +import argparse +import asyncio +import json +import sys +from datetime import datetime +from pathlib import Path +from typing import Any + +from evals.stt.providers import ( + DeepgramProvider, + DeepgramFluxProvider, + SpeechmaticsProvider, + LocalSmartTurnProvider, + STTProvider, + TranscriptionResult, +) + + +def get_provider(name: str) -> STTProvider: + """Get provider instance by name.""" + providers = { + "deepgram": DeepgramProvider, + "deepgram-flux": DeepgramFluxProvider, + "speechmatics": SpeechmaticsProvider, + "local-smart-turn": LocalSmartTurnProvider, + } + if name not in providers: + raise ValueError(f"Unknown provider: {name}. Available: {list(providers.keys())}") + return providers[name]() + + +async def run_transcription( + provider: STTProvider, + audio_path: Path, + diarize: bool = False, + keyterms: list[str] | None = None, + **kwargs: Any, +) -> TranscriptionResult: + """Run transcription with a provider.""" + print(f"\n{'='*60}") + print(f"Provider: {provider.name.upper()}") + print(f"{'='*60}") + + try: + result = await provider.transcribe( + audio_path, + diarize=diarize, + keyterms=keyterms, + **kwargs, + ) + return result + except Exception as e: + print(f"Error with {provider.name}: {e}") + raise + + +def print_result(result: TranscriptionResult, show_words: bool = False) -> None: + """Print transcription result.""" + print(f"\nDuration: {result.duration:.2f}s") + print(f"Speakers detected: {len(result.speakers)} - {result.speakers}") + print(f"\nTranscript:\n{result.transcript}") + + if result.speakers: + print(f"\n--- Speaker Segments ---") + for segment in result.get_speaker_segments(): + speaker = segment["speaker"] or "?" + text = segment["text"] + start = segment["start"] + print(f"[{start:.1f}s] Speaker {speaker}: {text}") + + if show_words: + print(f"\n--- Words ---") + for word in result.words[:50]: # First 50 words + speaker_info = f" (S{word.speaker})" if word.speaker else "" + print(f" {word.start:.2f}s: {word.word}{speaker_info} [{word.confidence:.2f}]") + if len(result.words) > 50: + print(f" ... and {len(result.words) - 50} more words") + + +def save_results( + results: list[TranscriptionResult], + output_dir: Path, + audio_name: str, +) -> Path: + """Save results to JSON file.""" + output_dir.mkdir(parents=True, exist_ok=True) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_file = output_dir / f"{audio_name}_{timestamp}.json" + + output_data = { + "timestamp": timestamp, + "audio_file": audio_name, + "results": [r.to_dict() for r in results], + } + + with open(output_file, "w") as f: + json.dump(output_data, f, indent=2) + + print(f"\nResults saved to: {output_file}") + return output_file + + +def compare_results(results: list[TranscriptionResult]) -> None: + """Compare results across providers.""" + if len(results) < 2: + return + + print(f"\n{'='*60}") + print("COMPARISON SUMMARY") + print(f"{'='*60}") + + print(f"\n{'Provider':<15} {'Duration':<10} {'Speakers':<10} {'Words':<10}") + print("-" * 45) + for r in results: + print(f"{r.provider:<15} {r.duration:<10.2f} {len(r.speakers):<10} {len(r.words):<10}") + + # Compare speaker counts + speaker_counts = {r.provider: len(r.speakers) for r in results} + if len(set(speaker_counts.values())) > 1: + print(f"\nNote: Providers detected different speaker counts: {speaker_counts}") + + +async def main() -> int: + parser = argparse.ArgumentParser( + description="STT Benchmark - Compare transcription providers", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize + python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --providers deepgram + python -m evals.stt.benchmark audio/multi_speaker.m4a --keyterms "Dograh" "API" + """, + ) + parser.add_argument( + "audio_file", + type=str, + help="Path to audio file (relative to evals/stt/ or absolute)", + ) + parser.add_argument( + "--providers", + nargs="+", + default=["deepgram", "speechmatics"], + choices=["deepgram", "deepgram-flux", "speechmatics", "local-smart-turn"], + help="Providers to test (default: all)", + ) + parser.add_argument( + "--diarize", + action="store_true", + help="Enable speaker diarization", + ) + parser.add_argument( + "--keyterms", + nargs="+", + help="Keywords to boost (Deepgram only)", + ) + parser.add_argument( + "--language", + default="en", + help="Language code (default: en)", + ) + parser.add_argument( + "--sample-rate", + type=int, + default=8000, + help="Audio sample rate for streaming (default: 8000)", + ) + parser.add_argument( + "--show-words", + action="store_true", + help="Show individual word timings", + ) + parser.add_argument( + "--save", + action="store_true", + help="Save results to JSON file", + ) + parser.add_argument( + "--output-dir", + type=str, + default="results", + help="Output directory for results (default: results)", + ) + + args = parser.parse_args() + + # Resolve audio path + script_dir = Path(__file__).parent + audio_path = Path(args.audio_file) + if not audio_path.is_absolute(): + audio_path = script_dir / audio_path + + if not audio_path.exists(): + print(f"Error: Audio file not found: {audio_path}") + return 1 + + print(f"Audio file: {audio_path}") + print(f"Providers: {args.providers}") + print(f"Diarization: {args.diarize}") + print(f"Sample rate: {args.sample_rate} Hz") + if args.keyterms: + print(f"Keyterms: {args.keyterms}") + + results: list[TranscriptionResult] = [] + + for provider_name in args.providers: + try: + provider = get_provider(provider_name) + result = await run_transcription( + provider, + audio_path, + diarize=args.diarize, + keyterms=args.keyterms, + language=args.language, + sample_rate=args.sample_rate, + ) + print_result(result, show_words=args.show_words) + results.append(result) + except Exception as e: + print(f"\nFailed to run {provider_name}: {e}") + continue + + if len(results) > 1: + compare_results(results) + + if args.save and results: + output_dir = script_dir / args.output_dir + save_results(results, output_dir, audio_path.stem) + + return 0 + + +if __name__ == "__main__": + sys.exit(asyncio.run(main())) diff --git a/evals/stt/event_capture.py b/evals/stt/event_capture.py new file mode 100644 index 0000000..7370dfb --- /dev/null +++ b/evals/stt/event_capture.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +"""STT Event Capture Runner. + +Streams audio to STT providers and captures raw WebSocket events with timestamps +for visualization in the web UI. + +Usage: + python -m evals.stt.event_capture audio/multi_speaker.m4a --provider deepgram + python -m evals.stt.event_capture audio/multi_speaker.m4a --provider speechmatics +""" + +import argparse +import asyncio +import json +import sys +from dataclasses import asdict, dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Any, Callable + +from evals.stt.audio_streamer import AudioStreamer +from evals.stt.providers import ( + DeepgramFluxProvider, + DeepgramProvider, + SpeechmaticsProvider, + STTProvider, +) + + +@dataclass +class CapturedEvent: + """A captured WebSocket event with timestamp.""" + + timestamp: float # Time since stream start (seconds) + event_type: str # e.g., "Results", "TurnInfo", "AddTranscript" + data: dict[str, Any] # Raw event payload + + def to_dict(self) -> dict[str, Any]: + return { + "timestamp": self.timestamp, + "event_type": self.event_type, + "data": self.data, + } + + +@dataclass +class EventCaptureResult: + """Result from event capture session.""" + + audio_file: str + audio_path: str # Relative path to audio from results dir + provider: str + duration: float + created_at: str + events: list[CapturedEvent] = field(default_factory=list) + transcript: str = "" # Final transcript for reference + + def to_dict(self) -> dict[str, Any]: + return { + "audio_file": self.audio_file, + "audio_path": self.audio_path, + "provider": self.provider, + "duration": self.duration, + "created_at": self.created_at, + "events": [e.to_dict() for e in self.events], + "transcript": self.transcript, + } + + +EventCallback = Callable[[str, dict[str, Any]], None] + + +def get_provider(name: str) -> STTProvider: + """Get provider instance by name.""" + providers = { + "deepgram": DeepgramProvider, + "deepgram-flux": DeepgramFluxProvider, + "speechmatics": SpeechmaticsProvider, + } + if name not in providers: + raise ValueError(f"Unknown provider: {name}. Available: {list(providers.keys())}") + return providers[name]() + + +async def capture_events( + provider: STTProvider, + audio_path: Path, + sample_rate: int = 8000, + **kwargs: Any, +) -> EventCaptureResult: + """Capture WebSocket events from a provider. + + Args: + provider: The STT provider to use + audio_path: Path to the audio file + sample_rate: Audio sample rate + **kwargs: Additional provider parameters + + Returns: + EventCaptureResult with all captured events + """ + # Get audio duration + streamer = AudioStreamer() + duration = streamer.get_duration(audio_path) + + # Event list and start time + events: list[CapturedEvent] = [] + start_time: float | None = None + + def on_event(event_type: str, data: dict[str, Any]) -> None: + """Callback for capturing events.""" + nonlocal start_time + if start_time is None: + start_time = asyncio.get_event_loop().time() + + timestamp = asyncio.get_event_loop().time() - start_time + events.append(CapturedEvent(timestamp=timestamp, event_type=event_type, data=data)) + + # Run transcription with event callback + result = await provider.transcribe( + audio_path, + sample_rate=sample_rate, + on_event=on_event, + **kwargs, + ) + + return EventCaptureResult( + audio_file=audio_path.name, + audio_path=f"../audio/{audio_path.name}", + provider=provider.name, + duration=duration, + created_at=datetime.now().isoformat(), + events=events, + transcript=result.transcript, + ) + + +def save_result(result: EventCaptureResult, output_dir: Path) -> Path: + """Save capture result to JSON file. + + Args: + result: The capture result to save + output_dir: Directory to save results + + Returns: + Path to the saved file + """ + output_dir.mkdir(parents=True, exist_ok=True) + + # Format: {audio_name}-{provider}.json + audio_name = Path(result.audio_file).stem + output_file = output_dir / f"{audio_name}-{result.provider}.json" + + with open(output_file, "w") as f: + json.dump(result.to_dict(), f, indent=2) + + return output_file + + +async def main() -> int: + parser = argparse.ArgumentParser( + description="STT Event Capture - Capture WebSocket events for visualization", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python -m evals.stt.event_capture audio/multi_speaker.m4a --provider deepgram + python -m evals.stt.event_capture audio/multi_speaker.m4a --provider speechmatics --diarize + """, + ) + parser.add_argument( + "audio_file", + type=str, + help="Path to audio file (relative to evals/stt/ or absolute)", + ) + parser.add_argument( + "--provider", + required=True, + choices=["deepgram", "deepgram-flux", "speechmatics"], + help="STT provider to use", + ) + parser.add_argument( + "--sample-rate", + type=int, + default=8000, + help="Audio sample rate for streaming (default: 8000)", + ) + parser.add_argument( + "--diarize", + action="store_true", + help="Enable speaker diarization", + ) + parser.add_argument( + "--output-dir", + type=str, + default="results", + help="Output directory for results (default: results)", + ) + + args = parser.parse_args() + + # Resolve audio path + script_dir = Path(__file__).parent + audio_path = Path(args.audio_file) + if not audio_path.is_absolute(): + audio_path = script_dir / audio_path + + if not audio_path.exists(): + print(f"Error: Audio file not found: {audio_path}") + return 1 + + print(f"Audio file: {audio_path}") + print(f"Provider: {args.provider}") + print(f"Sample rate: {args.sample_rate} Hz") + print(f"Diarization: {args.diarize}") + + try: + provider = get_provider(args.provider) + print(f"\nCapturing events from {provider.name}...") + + result = await capture_events( + provider, + audio_path, + sample_rate=args.sample_rate, + diarize=args.diarize, + ) + + output_dir = script_dir / args.output_dir + output_file = save_result(result, output_dir) + + print(f"\nCapture complete!") + print(f" Duration: {result.duration:.2f}s") + print(f" Events: {len(result.events)}") + print(f" Saved to: {output_file}") + + # Show first few events + print(f"\nFirst 5 events:") + for event in result.events[:5]: + print(f" [{event.timestamp:.2f}s] {event.event_type}") + + return 0 + + except Exception as e: + print(f"\nError: {e}") + import traceback + + traceback.print_exc() + return 1 + + +if __name__ == "__main__": + sys.exit(asyncio.run(main())) diff --git a/evals/stt/providers/__init__.py b/evals/stt/providers/__init__.py new file mode 100644 index 0000000..5df045c --- /dev/null +++ b/evals/stt/providers/__init__.py @@ -0,0 +1,16 @@ +from .base import EventCallback, STTProvider, TranscriptionResult, Word +from .deepgram_provider import DeepgramProvider +from .deepgram_flux_provider import DeepgramFluxProvider +from .speechmatics_provider import SpeechmaticsProvider +from .local_smart_turn_provider import LocalSmartTurnProvider + +__all__ = [ + "EventCallback", + "STTProvider", + "TranscriptionResult", + "Word", + "DeepgramProvider", + "DeepgramFluxProvider", + "SpeechmaticsProvider", + "LocalSmartTurnProvider", +] diff --git a/evals/stt/providers/base.py b/evals/stt/providers/base.py new file mode 100644 index 0000000..de41265 --- /dev/null +++ b/evals/stt/providers/base.py @@ -0,0 +1,128 @@ +"""Base classes for STT providers.""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Callable + +# Event callback type: (event_type, data) -> None +EventCallback = Callable[[str, dict[str, Any]], None] + + +@dataclass +class Word: + """Represents a transcribed word with metadata.""" + + word: str + start: float + end: float + confidence: float + speaker: str | None = None + speaker_confidence: float | None = None + + def to_dict(self) -> dict[str, Any]: + return { + "word": self.word, + "start": self.start, + "end": self.end, + "confidence": self.confidence, + "speaker": self.speaker, + "speaker_confidence": self.speaker_confidence, + } + + +@dataclass +class TranscriptionResult: + """Result from STT transcription.""" + + provider: str + transcript: str + words: list[Word] + speakers: list[str] + duration: float + raw_response: dict[str, Any] = field(default_factory=dict) + params: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + return { + "provider": self.provider, + "transcript": self.transcript, + "words": [w.to_dict() for w in self.words], + "speakers": self.speakers, + "duration": self.duration, + "params": self.params, + } + + def get_speaker_segments(self) -> list[dict[str, Any]]: + """Get transcript segmented by speaker.""" + if not self.words: + return [] + + segments = [] + current_speaker = None + current_text = [] + segment_start = 0.0 + + for word in self.words: + if word.speaker != current_speaker: + if current_text: + segments.append( + { + "speaker": current_speaker, + "text": " ".join(current_text), + "start": segment_start, + "end": self.words[len(segments) - 1].end + if segments + else word.start, + } + ) + current_speaker = word.speaker + current_text = [word.word] + segment_start = word.start + else: + current_text.append(word.word) + + if current_text: + segments.append( + { + "speaker": current_speaker, + "text": " ".join(current_text), + "start": segment_start, + "end": self.words[-1].end if self.words else 0.0, + } + ) + + return segments + + +class STTProvider(ABC): + """Abstract base class for STT providers.""" + + @property + @abstractmethod + def name(self) -> str: + """Provider name.""" + pass + + @abstractmethod + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, + keyterms: list[str] | None = None, + on_event: EventCallback | None = None, + **kwargs: Any, + ) -> TranscriptionResult: + """Transcribe audio file. + + Args: + audio_path: Path to the audio file + diarize: Enable speaker diarization + keyterms: List of keywords to boost (if supported) + on_event: Optional callback for raw WebSocket events (event_type, data) + **kwargs: Provider-specific parameters + + Returns: + TranscriptionResult with transcript and metadata + """ + pass diff --git a/evals/stt/providers/deepgram_flux_provider.py b/evals/stt/providers/deepgram_flux_provider.py new file mode 100644 index 0000000..860dbd2 --- /dev/null +++ b/evals/stt/providers/deepgram_flux_provider.py @@ -0,0 +1,235 @@ +"""Deepgram Flux STT provider with WebSocket streaming. + +Flux is Deepgram's conversational AI model with built-in turn detection. +It has a different API than Nova models - no language/punctuate/diarize params. +""" + +import asyncio +import json +import os +from pathlib import Path +from typing import Any +from urllib.parse import urlencode + +from loguru import logger + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import EventCallback, STTProvider, TranscriptionResult, Word + +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") + + +class DeepgramFluxProvider(STTProvider): + """Deepgram Flux Speech-to-Text provider with WebSocket streaming. + + Flux is optimized for conversational AI with built-in turn detection. + + Key differences from Nova: + - Uses v2 API endpoint + - Only supports English (flux-general-en) + - No punctuate, diarize, or language params + - Has turn detection events (StartOfTurn, EndOfTurn, EagerEndOfTurn) + - Supports keyterm boosting + + API Docs: https://developers.deepgram.com/docs/ + """ + + WS_URL = "wss://api.deepgram.com/v2/listen" + + def __init__(self, api_key: str | None = None): + self.api_key = api_key or os.getenv("DEEPGRAM_API_KEY") + if not self.api_key: + raise ValueError( + "Deepgram API key required. Set DEEPGRAM_API_KEY env var or pass api_key." + ) + + @property + def name(self) -> str: + return "deepgram-flux" + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, # Ignored - Flux doesn't support diarization + keyterms: list[str] | None = None, + on_event: EventCallback | None = None, + model: str = "flux-general-en", + sample_rate: int = 16000, + eot_threshold: float | None = 0.70, + eot_timeout_ms: int | None = 3000, + eager_eot_threshold: float | None = None, + trailing_silence_seconds: float = 3.0, + **kwargs: Any, + ) -> TranscriptionResult: + """Transcribe audio using Deepgram Flux WebSocket streaming. + + Args: + audio_path: Path to audio file + diarize: IGNORED - Flux does not support diarization + keyterms: List of keywords to boost recognition + on_event: Optional callback for raw WebSocket events + model: Flux model (default: flux-general-en) + sample_rate: Audio sample rate (default: 16000 for Flux) + eot_threshold: End-of-turn confidence threshold (0-1, default 0.7) + eot_timeout_ms: Timeout in ms to force end of turn (default 5000) + eager_eot_threshold: Threshold for eager end-of-turn events + trailing_silence_seconds: Seconds of silence after audio to capture pending events + **kwargs: Additional Flux parameters + + Returns: + TranscriptionResult with transcript (no speaker info - Flux doesn't support diarization) + """ + if diarize: + logger.warning("Flux does not support diarization - ignoring diarize=True") + + # Build query params - Flux only supports specific params + params: dict[str, Any] = { + "model": model, + "encoding": "linear16", + "sample_rate": sample_rate, + } + + # Flux-specific turn detection params + if eot_threshold is not None: + params["eot_threshold"] = eot_threshold + if eot_timeout_ms is not None: + params["eot_timeout_ms"] = eot_timeout_ms + if eager_eot_threshold is not None: + params["eager_eot_threshold"] = eager_eot_threshold + + # Build URL with params + url_parts = [f"{k}={v}" for k, v in params.items()] + + # Add keyterms (repeated params) + if keyterms: + for term in keyterms: + url_parts.append(urlencode({"keyterm": term})) + + ws_url = f"{self.WS_URL}?{'&'.join(url_parts)}" + logger.debug(f"Flux WebSocket URL: {ws_url}") + + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Collect results + all_transcripts: list[dict[str, Any]] = [] + final_transcript = "" + duration = 0.0 + connected = asyncio.Event() + + async with websocket_connect( + ws_url, + additional_headers={"Authorization": f"Token {self.api_key}"}, + ) as ws: + + async def send_audio(): + """Send audio chunks to Deepgram Flux.""" + await connected.wait() + + chunk_no = 0 + async for chunk in streamer.stream_file( + audio_path, trailing_silence_seconds=trailing_silence_seconds + ): + logger.trace(f"[deepgram-flux] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 + + async def receive_messages(): + """Receive and collect Flux messages.""" + nonlocal all_transcripts, final_transcript, duration + + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("type") + logger.debug(f"[deepgram-flux] Received {msg_type}: {data}") + + # Emit event via callback if provided + if on_event and msg_type: + on_event(msg_type, data) + + if msg_type == "Connected": + logger.info("[deepgram-flux] Connected") + connected.set() + + elif msg_type == "TurnInfo": + event = data.get("event") + transcript = data.get("transcript", "") + words = data.get("words", []) + + if event == "EndOfTurn": + if transcript: + final_transcript += transcript + " " + if words: + all_transcripts.append({ + "transcript": transcript, + "words": words, + }) + # Get duration from last word + if words: + last_word = words[-1] + duration = max(duration, last_word.get("end", 0)) + + elif event == "TurnResumed": + logger.debug("TurnResumed") + + elif msg_type == "Error": + raise Exception(f"Deepgram Flux error: {data}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_messages()) + + await send_task + + logger.debug("[deepgram-flux] Send task done") + try: + await asyncio.wait_for(receive_task, timeout=10.0) + except asyncio.TimeoutError: + pass + + return self._parse_results( + all_transcripts, final_transcript.strip(), duration, params, keyterms + ) + + def _parse_results( + self, + transcripts: list[dict[str, Any]], + final_transcript: str, + duration: float, + params: dict[str, Any], + keyterms: list[str] | None, + ) -> TranscriptionResult: + """Parse collected Flux results into TranscriptionResult.""" + words = [] + + for turn in transcripts: + for w in turn.get("words", []): + words.append( + Word( + word=w.get("word", ""), + start=w.get("start", 0.0), + end=w.get("end", 0.0), + confidence=w.get("confidence", 0.0), + speaker=None, # Flux doesn't support diarization + speaker_confidence=None, + ) + ) + + stored_params = dict(params) + if keyterms: + stored_params["keyterms"] = keyterms + + return TranscriptionResult( + provider=self.name, + transcript=final_transcript, + words=words, + speakers=[], # Flux doesn't support diarization + duration=duration, + raw_response={"transcripts": transcripts}, + params=stored_params, + ) diff --git a/evals/stt/providers/deepgram_provider.py b/evals/stt/providers/deepgram_provider.py new file mode 100644 index 0000000..14311a4 --- /dev/null +++ b/evals/stt/providers/deepgram_provider.py @@ -0,0 +1,236 @@ +"""Deepgram STT provider with WebSocket streaming.""" + +import asyncio +import json +import os +from pathlib import Path +from typing import Any +from urllib.parse import urlencode + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import EventCallback, STTProvider, TranscriptionResult, Word +from loguru import logger + +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") + + +class DeepgramProvider(STTProvider): + """Deepgram Nova Speech-to-Text provider with WebSocket streaming. + + API Docs: https://developers.deepgram.com/docs/ + + Supports: + - Speaker diarization via `diarize=true` + - Keyterm boosting via `keyterm` parameter + - Real-time streaming via WebSocket + - Multiple languages + - Punctuation + + For Flux models, use DeepgramFluxProvider instead. + """ + + WS_URL = "wss://api.deepgram.com/v1/listen" + + def __init__(self, api_key: str | None = None): + self.api_key = api_key or os.getenv("DEEPGRAM_API_KEY") + if not self.api_key: + raise ValueError( + "Deepgram API key required. Set DEEPGRAM_API_KEY env var or pass api_key." + ) + + @property + def name(self) -> str: + return "deepgram" + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, + keyterms: list[str] | None = None, + on_event: EventCallback | None = None, + model: str = "nova-3-general", + language: str = "en", + sample_rate: int = 8000, + punctuate: bool = True, + trailing_silence_seconds: float = 3.0, + **kwargs: Any, + ) -> TranscriptionResult: + """Transcribe audio using Deepgram Nova WebSocket streaming. + + Args: + audio_path: Path to audio file + diarize: Enable speaker diarization + keyterms: List of keywords to boost recognition + on_event: Optional callback for raw WebSocket events + model: Deepgram Nova model (nova-3, nova-2, etc.) + language: Language code + sample_rate: Audio sample rate for streaming + punctuate: Add punctuation + trailing_silence_seconds: Seconds of silence after audio to capture pending events + **kwargs: Additional Deepgram parameters + + Returns: + TranscriptionResult with transcript and speaker info + """ + # Build query params + params: dict[str, Any] = { + "model": model, + "language": language, + "punctuate": str(punctuate).lower(), + "encoding": "linear16", + "sample_rate": sample_rate, + "channels": 1, + "interim_results": "true", + "smart_format": "true", + "profanity_filter": "true", + "vad_events": "true", + "utterance_end_ms": "1000" + } + + if diarize: + params["diarize"] = "true" + + # Build URL with params + url_parts = [f"{k}={v}" for k, v in params.items()] + + # Add keyterms (repeated params) + if keyterms: + for term in keyterms: + url_parts.append(urlencode({"keyterm": term})) + + # Add extra kwargs + for k, v in kwargs.items(): + url_parts.append(f"{k}={v}") + + ws_url = f"{self.WS_URL}?{'&'.join(url_parts)}" + logger.debug(f"Deepgram WebSocket URL: {ws_url}") + + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Collect results + all_words: list[dict[str, Any]] = [] + final_transcript = "" + duration = 0.0 + + try: + async with websocket_connect( + ws_url, + additional_headers={"Authorization": f"Token {self.api_key}"}, + ) as ws: + # Create tasks for sending and receiving + send_complete = asyncio.Event() + + async def send_audio(): + """Send audio chunks to Deepgram.""" + chunk_no = 0 + async for chunk in streamer.stream_file( + audio_path, trailing_silence_seconds=trailing_silence_seconds + ): + logger.trace(f"[deepgram] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 + # Send close message + logger.debug(f"[deepgram] Sending CloseStream after {chunk_no} chunks") + await ws.send(json.dumps({"type": "CloseStream"})) + send_complete.set() + + async def receive_transcripts(): + """Receive and collect transcription results.""" + nonlocal all_words, final_transcript, duration + + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("type") + logger.debug(f"[deepgram] Received {msg_type}: {data}") + + # Emit event via callback if provided + if on_event and msg_type: + on_event(msg_type, data) + + if msg_type == "Results": + # Nova-style response + channel = data.get("channel", {}) + alternatives = channel.get("alternatives", []) + if alternatives: + alt = alternatives[0] + words = alt.get("words", []) + all_words.extend(words) + + # Check if final + if data.get("is_final"): + final_transcript += alt.get("transcript", "") + " " + duration = max( + duration, data.get("duration", 0) + data.get("start", 0) + ) + + elif msg_type == "Metadata": + # Get duration from metadata + duration = data.get("duration", duration) + + elif msg_type == "Error": + raise Exception(f"Deepgram error: {data}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_transcripts()) + + # Wait for send to complete, then wait a bit for final results + await send_task + try: + await asyncio.wait_for(receive_task, timeout=5.0) + except asyncio.TimeoutError: + pass # Normal - websocket closes after final results + except Exception as e: + logger.exception(e) + + return self._parse_results( + all_words, final_transcript.strip(), duration, params, keyterms + ) + + def _parse_results( + self, + raw_words: list[dict[str, Any]], + transcript: str, + duration: float, + params: dict[str, Any], + keyterms: list[str] | None, + ) -> TranscriptionResult: + """Parse collected results into TranscriptionResult.""" + words = [] + speakers_set: set[str] = set() + + for w in raw_words: + speaker = str(w.get("speaker", "")) if "speaker" in w else None + if speaker: + speakers_set.add(speaker) + + words.append( + Word( + word=w.get("word", ""), + start=w.get("start", 0.0), + end=w.get("end", 0.0), + confidence=w.get("confidence", 0.0), + speaker=speaker, + speaker_confidence=w.get("speaker_confidence"), + ) + ) + + stored_params = dict(params) + if keyterms: + stored_params["keyterms"] = keyterms + + return TranscriptionResult( + provider=self.name, + transcript=transcript, + words=words, + speakers=sorted(speakers_set), + duration=duration, + raw_response={"words": raw_words}, + params=stored_params, + ) diff --git a/evals/stt/providers/local_smart_turn_provider.py b/evals/stt/providers/local_smart_turn_provider.py new file mode 100644 index 0000000..2133ef1 --- /dev/null +++ b/evals/stt/providers/local_smart_turn_provider.py @@ -0,0 +1,287 @@ +"""Local Smart Turn provider for benchmarking end-of-turn detection. + +Uses the pipecat smart-turn-v3 ONNX model for local ML-based turn detection. +This is NOT an STT provider - it only detects when a speaker has finished talking. +""" + +import os +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import numpy as np +from loguru import logger + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import EventCallback, STTProvider, TranscriptionResult, Word + +try: + import onnxruntime as ort + from transformers import WhisperFeatureExtractor +except ImportError: + raise ImportError( + "onnxruntime and transformers required: pip install onnxruntime transformers" + ) + + +@dataclass +class TurnEvent: + """Represents a detected turn event.""" + timestamp: float # Time in audio when turn was detected + probability: float # Model confidence + prediction: int # 1=complete, 0=incomplete + inference_time_ms: float + + +class LocalSmartTurnProvider(STTProvider): + """Local Smart Turn provider for end-of-turn detection benchmarking. + + Uses the smart-turn-v3 ONNX model to detect when speakers finish talking. + This is useful for comparing turn detection accuracy against cloud services + like Deepgram Flux's built-in turn detection. + + NOTE: This provider does NOT produce transcripts - only turn detection events. + """ + + # Smart turn model requires 16kHz audio + REQUIRED_SAMPLE_RATE = 16000 + # Model analyzes 8 seconds of audio + WINDOW_SECONDS = 8 + + def __init__( + self, + model_path: str | None = None, + cpu_count: int = 1, + ): + """Initialize the local smart turn provider. + + Args: + model_path: Path to ONNX model file. If None, uses bundled model. + cpu_count: Number of CPUs for inference (default: 1) + """ + self.model_path = model_path + self.cpu_count = cpu_count + self._session = None + self._feature_extractor = None + + def _load_model(self): + """Lazy load the ONNX model.""" + if self._session is not None: + return + + model_path = self.model_path + + if not model_path: + # Try to load bundled model from pipecat + model_name = "smart-turn-v3.1-cpu.onnx" + package_path = "pipecat.audio.turn.smart_turn.data" + + try: + import importlib_resources as impresources + model_path = str(impresources.files(package_path).joinpath(model_name)) + except Exception: + from importlib import resources as impresources + try: + with impresources.path(package_path, model_name) as f: + model_path = str(f) + except Exception: + model_path = str(impresources.files(package_path).joinpath(model_name)) + + logger.info(f"[local-smart-turn] Loading model from {model_path}") + + # Configure ONNX runtime + so = ort.SessionOptions() + so.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + so.inter_op_num_threads = 1 + so.intra_op_num_threads = self.cpu_count + so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + + self._feature_extractor = WhisperFeatureExtractor(chunk_length=8) + self._session = ort.InferenceSession(model_path, sess_options=so) + + logger.info("[local-smart-turn] Model loaded") + + @property + def name(self) -> str: + return "local-smart-turn" + + def _predict_endpoint(self, audio_array: np.ndarray) -> dict[str, Any]: + """Predict end-of-turn using the ONNX model. + + Args: + audio_array: Audio samples as float32 numpy array (16kHz) + + Returns: + Dict with prediction (0/1) and probability + """ + # Truncate to last 8 seconds or pad to 8 seconds + max_samples = self.WINDOW_SECONDS * self.REQUIRED_SAMPLE_RATE + if len(audio_array) > max_samples: + audio_array = audio_array[-max_samples:] + elif len(audio_array) < max_samples: + padding = max_samples - len(audio_array) + audio_array = np.pad(audio_array, (padding, 0), mode="constant", constant_values=0) + + # Process using Whisper's feature extractor + inputs = self._feature_extractor( + audio_array, + sampling_rate=self.REQUIRED_SAMPLE_RATE, + return_tensors="np", + padding="max_length", + max_length=self.WINDOW_SECONDS * self.REQUIRED_SAMPLE_RATE, + truncation=True, + do_normalize=True, + ) + + # Extract features for ONNX + input_features = inputs.input_features.squeeze(0).astype(np.float32) + input_features = np.expand_dims(input_features, axis=0) + + # Run inference + start_time = time.perf_counter() + outputs = self._session.run(None, {"input_features": input_features}) + inference_time = (time.perf_counter() - start_time) * 1000 + + # Extract probability (model returns sigmoid probabilities) + probability = outputs[0][0].item() + prediction = 1 if probability > 0.5 else 0 + + return { + "prediction": prediction, + "probability": probability, + "inference_time_ms": inference_time, + } + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, # Ignored - not applicable + keyterms: list[str] | None = None, # Ignored - not applicable + on_event: EventCallback | None = None, # Ignored - not applicable + sample_rate: int = 16000, # Must be 16kHz for smart turn + analysis_interval_ms: int = 500, # How often to check for turn completion + **kwargs: Any, + ) -> TranscriptionResult: + """Analyze audio for turn detection events. + + NOTE: This does NOT produce transcripts. It detects when speakers + finish talking using ML-based turn detection. + + Args: + audio_path: Path to audio file + diarize: Ignored (not applicable for turn detection) + keyterms: Ignored (not applicable for turn detection) + on_event: Ignored (not applicable for turn detection) + sample_rate: Must be 16000 Hz for smart turn model + analysis_interval_ms: How often to run turn detection (ms) + **kwargs: Additional parameters (ignored) + + Returns: + TranscriptionResult with turn detection events in raw_response + """ + if sample_rate != self.REQUIRED_SAMPLE_RATE: + logger.warning( + f"[local-smart-turn] Sample rate must be {self.REQUIRED_SAMPLE_RATE}Hz, " + f"overriding {sample_rate}Hz" + ) + sample_rate = self.REQUIRED_SAMPLE_RATE + + # Load model if not already loaded + self._load_model() + + # Setup audio streamer at 16kHz + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Get audio duration + duration = streamer.get_duration(audio_path) + logger.info(f"[local-smart-turn] Processing {audio_path} ({duration:.2f}s)") + + # Collect all audio first (smart turn needs to analyze segments) + pcm_data = streamer.convert_to_pcm16(audio_path) + + # Convert to float32 for model + audio_int16 = np.frombuffer(pcm_data, dtype=np.int16) + audio_float32 = audio_int16.astype(np.float32) / 32768.0 + + # Analyze at intervals + turn_events: list[TurnEvent] = [] + samples_per_interval = int(sample_rate * analysis_interval_ms / 1000) + window_samples = self.WINDOW_SECONDS * sample_rate + + chunk_no = 0 + for end_sample in range(samples_per_interval, len(audio_float32), samples_per_interval): + # Get window of audio ending at current position + start_sample = max(0, end_sample - window_samples) + audio_window = audio_float32[start_sample:end_sample] + + current_time = end_sample / sample_rate + logger.debug(f"[local-smart-turn] Analyzing chunk {chunk_no} at {current_time:.2f}s") + + result = self._predict_endpoint(audio_window) + + turn_events.append(TurnEvent( + timestamp=current_time, + probability=result["probability"], + prediction=result["prediction"], + inference_time_ms=result["inference_time_ms"], + )) + + if result["prediction"] == 1: + logger.info( + f"[local-smart-turn] Turn complete at {current_time:.2f}s " + f"(prob={result['probability']:.3f})" + f"(inf time ms={result["inference_time_ms"]})" + ) + + chunk_no += 1 + + # Create result + # Convert turn events to word-like format for compatibility + words = [] + for event in turn_events: + if event.prediction == 1: + words.append(Word( + word=f"[END_OF_TURN prob={event.probability:.2f}]", + start=event.timestamp - 0.1, + end=event.timestamp, + confidence=event.probability, + speaker=None, + speaker_confidence=None, + )) + + # Count completed turns + completed_turns = sum(1 for e in turn_events if e.prediction == 1) + + params = { + "sample_rate": sample_rate, + "analysis_interval_ms": analysis_interval_ms, + "window_seconds": self.WINDOW_SECONDS, + } + + return TranscriptionResult( + provider=self.name, + transcript=f"[Turn detection only - {completed_turns} turns detected]", + words=words, + speakers=[], # Not applicable + duration=duration, + raw_response={ + "turn_events": [ + { + "timestamp": e.timestamp, + "probability": e.probability, + "prediction": e.prediction, + "inference_time_ms": e.inference_time_ms, + } + for e in turn_events + ], + "completed_turns": completed_turns, + "total_analyses": len(turn_events), + "avg_inference_time_ms": ( + sum(e.inference_time_ms for e in turn_events) / len(turn_events) + if turn_events else 0 + ), + }, + params=params, + ) diff --git a/evals/stt/providers/speechmatics_provider.py b/evals/stt/providers/speechmatics_provider.py new file mode 100644 index 0000000..1efd51e --- /dev/null +++ b/evals/stt/providers/speechmatics_provider.py @@ -0,0 +1,258 @@ +"""Speechmatics STT provider with WebSocket streaming.""" + +import asyncio +import json +import os +from pathlib import Path +from typing import Any + +from loguru import logger + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import EventCallback, STTProvider, TranscriptionResult, Word + +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") + + +class SpeechmaticsProvider(STTProvider): + """Speechmatics Speech-to-Text provider with WebSocket streaming. + + API Docs: https://docs.speechmatics.com/ + + Supports: + - Speaker diarization via `diarization: "speaker"` config + - Speaker sensitivity tuning + - Real-time streaming via WebSocket + """ + + def __init__(self, api_key: str | None = None, region: str = "eu2"): + self.api_key = api_key or os.getenv("SPEECHMATICS_API_KEY") + if not self.api_key: + raise ValueError( + "Speechmatics API key required. Set SPEECHMATICS_API_KEY env var or pass api_key." + ) + # Set region-specific endpoint + self.ws_url = f"wss://{region}.rt.speechmatics.com/v2" + + @property + def name(self) -> str: + return "speechmatics" + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, + keyterms: list[str] | None = None, + on_event: EventCallback | None = None, + language: str = "en", + operating_point: str = "enhanced", + sample_rate: int = 8000, + speaker_sensitivity: float | None = None, + max_speakers: int | None = None, + trailing_silence_seconds: float = 3.0, + **kwargs: Any, + ) -> TranscriptionResult: + """Transcribe audio using Speechmatics WebSocket streaming. + + Args: + audio_path: Path to audio file + diarize: Enable speaker diarization + keyterms: Additional vocabulary (limited support) + on_event: Optional callback for raw WebSocket events + language: Language code + operating_point: "standard" or "enhanced" + sample_rate: Audio sample rate for streaming + speaker_sensitivity: 0.0-1.0, higher = more speakers detected + max_speakers: Maximum number of speakers to detect + trailing_silence_seconds: Seconds of silence after audio to capture pending events + **kwargs: Additional config parameters + + Returns: + TranscriptionResult with transcript and speaker info + """ + # Build transcription config for StartRecognition message + transcription_config: dict[str, Any] = { + "language": language, + "operating_point": operating_point, + "enable_partials": False, + } + + if diarize: + transcription_config["diarization"] = "speaker" + if speaker_sensitivity is not None: + transcription_config["speaker_diarization_config"] = { + "speaker_sensitivity": speaker_sensitivity + } + if max_speakers is not None: + if "speaker_diarization_config" not in transcription_config: + transcription_config["speaker_diarization_config"] = {} + transcription_config["speaker_diarization_config"]["max_speakers"] = max_speakers + + # Add additional vocabulary if provided + if keyterms: + transcription_config["additional_vocab"] = [{"content": term} for term in keyterms] + + # Audio format config + audio_format = { + "type": "raw", + "encoding": "pcm_s16le", + "sample_rate": sample_rate, + } + + # Store params for result + params = { + "diarize": diarize, + "language": language, + "operating_point": operating_point, + "sample_rate": sample_rate, + "speaker_sensitivity": speaker_sensitivity, + "max_speakers": max_speakers, + } + + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Collect results + all_results: list[dict[str, Any]] = [] + recognition_started = asyncio.Event() + transcription_complete = asyncio.Event() + + async with websocket_connect( + self.ws_url, + additional_headers={"Authorization": f"Bearer {self.api_key}"}, + ) as ws: + # Send StartRecognition message + start_msg = { + "message": "StartRecognition", + "transcription_config": transcription_config, + "audio_format": audio_format, + } + await ws.send(json.dumps(start_msg)) + + async def send_audio(): + """Send audio chunks after recognition starts.""" + await recognition_started.wait() + + chunk_no = 0 + async for chunk in streamer.stream_file( + audio_path, trailing_silence_seconds=trailing_silence_seconds + ): + logger.debug(f"[speechmatics] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 + + # Signal end of audio with last sequence number + logger.debug(f"[speechmatics] Sending EndOfStream after {chunk_no} chunks") + await ws.send(json.dumps({"message": "EndOfStream", "last_seq_no": chunk_no})) + + async def receive_messages(): + """Receive and process messages.""" + nonlocal all_results + + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("message") + logger.debug(f"[speechmatics] Received {msg_type}: {data}") + + # Emit event via callback if provided + if on_event and msg_type: + on_event(msg_type, data) + + if msg_type == "RecognitionStarted": + logger.info("[speechmatics] Connected") + recognition_started.set() + + elif msg_type == "AddTranscript": + # Final transcript segment + results = data.get("results", []) + all_results.extend(results) + + elif msg_type == "EndOfTranscript": + transcription_complete.set() + return + + elif msg_type == "Error": + raise Exception(f"Speechmatics error: {data}") + + elif msg_type == "Warning": + logger.warning(f"[speechmatics] Warning: {data.get('reason')}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_messages()) + + # Wait for completion + await send_task + try: + await asyncio.wait_for(transcription_complete.wait(), timeout=30.0) + except asyncio.TimeoutError: + pass + + receive_task.cancel() + try: + await receive_task + except asyncio.CancelledError: + pass + + return self._parse_results(all_results, params) + + def _parse_results( + self, + results: list[dict[str, Any]], + params: dict[str, Any], + ) -> TranscriptionResult: + """Parse Speechmatics results.""" + words = [] + speakers_set: set[str] = set() + transcript_parts = [] + duration = 0.0 + + for item in results: + item_type = item.get("type") + alternatives = item.get("alternatives", []) + + if not alternatives: + continue + + alt = alternatives[0] + content = alt.get("content", "") + speaker = alt.get("speaker") + + if speaker: + speakers_set.add(speaker) + + end_time = item.get("end_time", 0.0) + duration = max(duration, end_time) + + if item_type == "word": + words.append( + Word( + word=content, + start=item.get("start_time", 0.0), + end=end_time, + confidence=alt.get("confidence", 0.0), + speaker=speaker, + speaker_confidence=None, + ) + ) + transcript_parts.append(content) + elif item_type == "punctuation": + if transcript_parts: + transcript_parts[-1] += content + + transcript = " ".join(transcript_parts) + + return TranscriptionResult( + provider=self.name, + transcript=transcript, + words=words, + speakers=sorted(speakers_set), + duration=duration, + raw_response={"results": results}, + params=params, + ) diff --git a/evals/stt/results/multi_speaker-deepgram-flux.json b/evals/stt/results/multi_speaker-deepgram-flux.json new file mode 100644 index 0000000..7754af7 --- /dev/null +++ b/evals/stt/results/multi_speaker-deepgram-flux.json @@ -0,0 +1,867 @@ +{ + "audio_file": "multi_speaker.m4a", + "audio_path": "../audio/multi_speaker.m4a", + "provider": "deepgram-flux", + "duration": 7.987664, + "created_at": "2026-01-20T12:21:59.183902", + "events": [ + { + "timestamp": 3.1916191801428795e-05, + "event_type": "Connected", + "data": { + "type": "Connected", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "sequence_id": 0 + } + }, + { + "timestamp": 0.6468284581787884, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2195, + "sequence_id": 1 + } + }, + { + "timestamp": 0.8891876661218703, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.167, + "sequence_id": 2 + } + }, + { + "timestamp": 1.0987569580320269, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1045, + "sequence_id": 3 + } + }, + { + "timestamp": 1.356455208035186, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3054, + "sequence_id": 4 + } + }, + { + "timestamp": 1.6076077912002802, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2996, + "sequence_id": 5 + } + }, + { + "timestamp": 1.831926790997386, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1659, + "sequence_id": 6 + } + }, + { + "timestamp": 2.0988957500085235, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.6800001, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0922, + "sequence_id": 7 + } + }, + { + "timestamp": 2.320036916062236, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.9200001, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1154, + "sequence_id": 8 + } + }, + { + "timestamp": 2.5783222501631826, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0789, + "sequence_id": 9 + } + }, + { + "timestamp": 2.805098250042647, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.028, + "sequence_id": 10 + } + }, + { + "timestamp": 3.0677467910572886, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.6399999, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0544, + "sequence_id": 11 + } + }, + { + "timestamp": 3.3053550410550088, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.88, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0221, + "sequence_id": 12 + } + }, + { + "timestamp": 3.5730851250700653, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.12, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0896, + "sequence_id": 13 + } + }, + { + "timestamp": 3.7986690001562238, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.3600001, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0837, + "sequence_id": 14 + } + }, + { + "timestamp": 4.056284500053152, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.6, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0217, + "sequence_id": 15 + } + }, + { + "timestamp": 4.2824959580320865, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.84, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0277, + "sequence_id": 16 + } + }, + { + "timestamp": 4.541013500187546, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.08, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0636, + "sequence_id": 17 + } + }, + { + "timestamp": 4.7826515410561115, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.32, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.092, + "sequence_id": 18 + } + }, + { + "timestamp": 5.044063208159059, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.56, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1632, + "sequence_id": 19 + } + }, + { + "timestamp": 5.277323708171025, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.8, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1748, + "sequence_id": 20 + } + }, + { + "timestamp": 5.519584750058129, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.04, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1267, + "sequence_id": 21 + } + }, + { + "timestamp": 5.761642290977761, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.28, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.085, + "sequence_id": 22 + } + }, + { + "timestamp": 5.985961250029504, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0726, + "sequence_id": 23 + } + }, + { + "timestamp": 6.235282083041966, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1489, + "sequence_id": 24 + } + }, + { + "timestamp": 6.479744625044987, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1815, + "sequence_id": 25 + } + }, + { + "timestamp": 6.722758750198409, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1548, + "sequence_id": 26 + } + }, + { + "timestamp": 7.02101350016892, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1779, + "sequence_id": 27 + } + }, + { + "timestamp": 7.2554090830963105, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.7200003, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1924, + "sequence_id": 28 + } + }, + { + "timestamp": 7.495738583151251, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0734, + "sequence_id": 29 + } + }, + { + "timestamp": 7.695259500062093, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0621, + "sequence_id": 30 + } + }, + { + "timestamp": 7.9374284581281245, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0523, + "sequence_id": 31 + } + }, + { + "timestamp": 8.201127333100885, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.68, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0868, + "sequence_id": 32 + } + }, + { + "timestamp": 8.452570000197738, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.92, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1788, + "sequence_id": 33 + } + }, + { + "timestamp": 8.6957666662056, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3462, + "sequence_id": 34 + } + }, + { + "timestamp": 8.937032666057348, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3477, + "sequence_id": 35 + } + }, + { + "timestamp": 9.179693832993507, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.64, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2825, + "sequence_id": 36 + } + }, + { + "timestamp": 9.439219749998301, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.88, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1785, + "sequence_id": 37 + } + }, + { + "timestamp": 9.65257745818235, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.12, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.119, + "sequence_id": 38 + } + }, + { + "timestamp": 9.894739540992305, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.36, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0948, + "sequence_id": 39 + } + }, + { + "timestamp": 10.137037916108966, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.6, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0836, + "sequence_id": 40 + } + }, + { + "timestamp": 10.37885733298026, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.84, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0648, + "sequence_id": 41 + } + }, + { + "timestamp": 10.640081625198945, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.08, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0426, + "sequence_id": 42 + } + }, + { + "timestamp": 10.882513708202168, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.32, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0297, + "sequence_id": 43 + } + }, + { + "timestamp": 11.11375533300452, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.56, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0247, + "sequence_id": 44 + } + }, + { + "timestamp": 11.356210750062019, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.8, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0134, + "sequence_id": 45 + } + }, + { + "timestamp": 11.60117325000465, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.04, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0102, + "sequence_id": 46 + } + }, + { + "timestamp": 11.859979416010901, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.28, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0089, + "sequence_id": 47 + } + }, + { + "timestamp": 12.093679000157863, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0074, + "sequence_id": 48 + } + }, + { + "timestamp": 12.334945333190262, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.007, + "sequence_id": 49 + } + }, + { + "timestamp": 12.588809041073546, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0067, + "sequence_id": 50 + } + }, + { + "timestamp": 12.83585675014183, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0042, + "sequence_id": 51 + } + }, + { + "timestamp": 13.075434750178829, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0047, + "sequence_id": 52 + } + }, + { + "timestamp": 13.31491966615431, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "63038896-d7d9-4186-995f-16056c3306d5", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0036, + "sequence_id": 53 + } + } + ], + "transcript": "" +} \ No newline at end of file diff --git a/evals/stt/results/multi_speaker-deepgram.json b/evals/stt/results/multi_speaker-deepgram.json new file mode 100644 index 0000000..24f174a --- /dev/null +++ b/evals/stt/results/multi_speaker-deepgram.json @@ -0,0 +1,637 @@ +{ + "audio_file": "multi_speaker.m4a", + "audio_path": "../audio/multi_speaker.m4a", + "provider": "deepgram", + "duration": 7.987664, + "created_at": "2026-01-20T12:15:06.097292", + "events": [ + { + "timestamp": 2.50060111284256e-07, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 0.13 + } + }, + { + "timestamp": 0.9085824999492615, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0399375, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "Biggest pleasure", + "confidence": 0.7919922, + "words": [ + { + "word": "biggest", + "start": 0.0, + "end": 0.39999998, + "confidence": 0.7919922, + "punctuated_word": "Biggest" + }, + { + "word": "pleasure", + "start": 0.39999998, + "end": 0.79999995, + "confidence": 0.77734375, + "punctuated_word": "pleasure" + } + ] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 1.9669485830236226, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0799375, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 3.0349432919174433, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 3.1199374, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "Please give a text that I am just trying to", + "confidence": 0.4921875, + "words": [ + { + "word": "please", + "start": 0.48, + "end": 0.79999995, + "confidence": 0.19970703, + "punctuated_word": "Please" + }, + { + "word": "give", + "start": 0.79999995, + "end": 1.04, + "confidence": 0.2849121, + "punctuated_word": "give" + }, + { + "word": "a", + "start": 0.96, + "end": 1.1999999, + "confidence": 0.4921875, + "punctuated_word": "a" + }, + { + "word": "text", + "start": 1.1999999, + "end": 1.5999999, + "confidence": 0.4482422, + "punctuated_word": "text" + }, + { + "word": "that", + "start": 1.5999999, + "end": 2.1599998, + "confidence": 0.5317383, + "punctuated_word": "that" + }, + { + "word": "i", + "start": 2.1599998, + "end": 2.32, + "confidence": 0.984375, + "punctuated_word": "I" + }, + { + "word": "am", + "start": 2.32, + "end": 2.48, + "confidence": 0.5024414, + "punctuated_word": "am" + }, + { + "word": "just", + "start": 2.48, + "end": 2.6399999, + "confidence": 0.27416992, + "punctuated_word": "just" + }, + { + "word": "trying", + "start": 2.6399999, + "end": 2.96, + "confidence": 0.19909668, + "punctuated_word": "trying" + }, + { + "word": "to", + "start": 2.96, + "end": 3.04, + "confidence": 0.7060547, + "punctuated_word": "to" + } + ] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 4.100316457916051, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 4.1599374, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "Is the test that I am just trying do so. Multiple", + "confidence": 0.7207031, + "words": [ + { + "word": "is", + "start": 0.24, + "end": 0.79999995, + "confidence": 0.83251953, + "punctuated_word": "Is" + }, + { + "word": "the", + "start": 0.88, + "end": 1.12, + "confidence": 0.14794922, + "punctuated_word": "the" + }, + { + "word": "test", + "start": 1.12, + "end": 1.52, + "confidence": 0.7207031, + "punctuated_word": "test" + }, + { + "word": "that", + "start": 1.52, + "end": 2.1599998, + "confidence": 0.40307617, + "punctuated_word": "that" + }, + { + "word": "i", + "start": 2.1599998, + "end": 2.3999999, + "confidence": 0.99316406, + "punctuated_word": "I" + }, + { + "word": "am", + "start": 2.3999999, + "end": 2.48, + "confidence": 0.52783203, + "punctuated_word": "am" + }, + { + "word": "just", + "start": 2.48, + "end": 2.72, + "confidence": 0.27270508, + "punctuated_word": "just" + }, + { + "word": "trying", + "start": 2.72, + "end": 3.12, + "confidence": 0.81591797, + "punctuated_word": "trying" + }, + { + "word": "do", + "start": 3.12, + "end": 3.28, + "confidence": 0.9116211, + "punctuated_word": "do" + }, + { + "word": "so", + "start": 3.28, + "end": 3.4399998, + "confidence": 0.37774658, + "punctuated_word": "so." + }, + { + "word": "multiple", + "start": 3.6, + "end": 3.84, + "confidence": 0.74072266, + "punctuated_word": "Multiple" + } + ] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 4.506603500107303, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 4.53, + "start": 0.0, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "Is the test that I am testing multiple speaker", + "confidence": 0.65966797, + "words": [ + { + "word": "is", + "start": 0.24, + "end": 0.39999998, + "confidence": 0.83984375, + "punctuated_word": "Is" + }, + { + "word": "the", + "start": 0.39999998, + "end": 0.79999995, + "confidence": 0.15722656, + "punctuated_word": "the" + }, + { + "word": "test", + "start": 1.12, + "end": 1.52, + "confidence": 0.8588867, + "punctuated_word": "test" + }, + { + "word": "that", + "start": 1.52, + "end": 2.1599998, + "confidence": 0.35107422, + "punctuated_word": "that" + }, + { + "word": "i", + "start": 2.1599998, + "end": 2.32, + "confidence": 0.99121094, + "punctuated_word": "I" + }, + { + "word": "am", + "start": 2.32, + "end": 2.48, + "confidence": 0.6010742, + "punctuated_word": "am" + }, + { + "word": "testing", + "start": 2.48, + "end": 3.12, + "confidence": 0.9526367, + "punctuated_word": "testing" + }, + { + "word": "multiple", + "start": 3.4399998, + "end": 3.84, + "confidence": 0.65966797, + "punctuated_word": "multiple" + }, + { + "word": "speaker", + "start": 3.84, + "end": 4.3199997, + "confidence": 0.20446777, + "punctuated_word": "speaker" + } + ] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 4.648572708014399, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 4.63 + } + }, + { + "timestamp": 5.556989792035893, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0699372, + "start": 4.53, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 6.615257542114705, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.08, + "start": 4.53, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 6.769657667027786, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 6.72 + } + }, + { + "timestamp": 7.672739624977112, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0099998, + "start": 6.61, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 8.081677624955773, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 0.3676877, + "start": 7.62, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 8.083154707914218, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 0.0, + "start": 7.9876876, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 8.083194707985967, + "event_type": "Metadata", + "data": { + "type": "Metadata", + "transaction_key": "deprecated", + "request_id": "39481f46-cd5f-40b1-9a55-a6635d8c06d9", + "sha256": "a6f954deb3fb3bf7a3c420061d5dd968251ba401d6304e6cd2fc9f396c12da77", + "created": "2026-01-20T06:44:57.522Z", + "duration": 7.9876876, + "channels": 1, + "models": [ + "40bd3654-e622-47c4-a111-63a61b23bfe8" + ], + "model_info": { + "40bd3654-e622-47c4-a111-63a61b23bfe8": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + } + } + } + } + ], + "transcript": "Is the test that I am testing multiple speaker" +} \ No newline at end of file diff --git a/evals/stt/results/nope-deepgram-flux.json b/evals/stt/results/nope-deepgram-flux.json new file mode 100644 index 0000000..a26cf69 --- /dev/null +++ b/evals/stt/results/nope-deepgram-flux.json @@ -0,0 +1,445 @@ +{ + "audio_file": "nope.m4a", + "audio_path": "../audio/nope.m4a", + "provider": "deepgram-flux", + "duration": 3.390113, + "created_at": "2026-01-20T13:34:04.075559", + "events": [ + { + "timestamp": 3.3294782042503357e-07, + "event_type": "Connected", + "data": { + "type": "Connected", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "sequence_id": 0 + } + }, + { + "timestamp": 0.6400237919297069, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1726, + "sequence_id": 1 + } + }, + { + "timestamp": 0.850623874925077, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0643, + "sequence_id": 2 + } + }, + { + "timestamp": 1.0877662498969585, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0343, + "sequence_id": 3 + } + }, + { + "timestamp": 1.3602930000051856, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.023, + "sequence_id": 4 + } + }, + { + "timestamp": 1.5734205420594662, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "StartOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.2, + "transcript": "No.", + "words": [ + { + "word": "No.", + "confidence": 0.9956 + } + ], + "end_of_turn_confidence": 0.1445, + "sequence_id": 5 + } + }, + { + "timestamp": 1.7732612078543752, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "EndOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.36, + "transcript": "No.", + "words": [ + { + "word": "No.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.7266, + "sequence_id": 6 + } + }, + { + "timestamp": 2.0032672078814358, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 1.6, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2114, + "sequence_id": 7 + } + }, + { + "timestamp": 2.272528207860887, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 1.8399999, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2886, + "sequence_id": 8 + } + }, + { + "timestamp": 2.4770477078855038, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 2.08, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1366, + "sequence_id": 9 + } + }, + { + "timestamp": 2.7586996669415385, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 2.32, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0687, + "sequence_id": 10 + } + }, + { + "timestamp": 2.9688463748898357, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 2.56, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0571, + "sequence_id": 11 + } + }, + { + "timestamp": 3.2333728750236332, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 2.8, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0284, + "sequence_id": 12 + } + }, + { + "timestamp": 3.4381651668809354, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 3.04, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0352, + "sequence_id": 13 + } + }, + { + "timestamp": 3.7163160829804838, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 3.28, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0211, + "sequence_id": 14 + } + }, + { + "timestamp": 3.936306041898206, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 3.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0123, + "sequence_id": 15 + } + }, + { + "timestamp": 4.212840874912217, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 3.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0399, + "sequence_id": 16 + } + }, + { + "timestamp": 4.417071416974068, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 4.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0503, + "sequence_id": 17 + } + }, + { + "timestamp": 4.685962416930124, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 4.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0443, + "sequence_id": 18 + } + }, + { + "timestamp": 4.898042541928589, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 4.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0367, + "sequence_id": 19 + } + }, + { + "timestamp": 5.167347207898274, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 4.7200003, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0221, + "sequence_id": 20 + } + }, + { + "timestamp": 5.415992958005518, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 4.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1116, + "sequence_id": 21 + } + }, + { + "timestamp": 5.703707166947424, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 5.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0883, + "sequence_id": 22 + } + }, + { + "timestamp": 5.923421707935631, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 5.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0663, + "sequence_id": 23 + } + }, + { + "timestamp": 6.128664416959509, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 5.68, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0324, + "sequence_id": 24 + } + }, + { + "timestamp": 6.382756792008877, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 5.92, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0138, + "sequence_id": 25 + } + }, + { + "timestamp": 6.629080249927938, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "b42d9771-4a63-4c7f-aa89-33370cd70d23", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.36, + "audio_window_end": 6.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0064, + "sequence_id": 26 + } + } + ], + "transcript": "No." +} \ No newline at end of file diff --git a/evals/stt/results/not_so_sure-deepgram-flux.json b/evals/stt/results/not_so_sure-deepgram-flux.json new file mode 100644 index 0000000..540ba0e --- /dev/null +++ b/evals/stt/results/not_so_sure-deepgram-flux.json @@ -0,0 +1,678 @@ +{ + "audio_file": "not_so_sure.m4a", + "audio_path": "../audio/not_so_sure.m4a", + "provider": "deepgram-flux", + "duration": 3.784853, + "created_at": "2026-01-20T13:34:30.619814", + "events": [ + { + "timestamp": 4.1606836020946503e-07, + "event_type": "Connected", + "data": { + "type": "Connected", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "sequence_id": 0 + } + }, + { + "timestamp": 0.6479636249132454, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2837, + "sequence_id": 1 + } + }, + { + "timestamp": 0.8711565409321338, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1409, + "sequence_id": 2 + } + }, + { + "timestamp": 1.0940386659931391, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.103, + "sequence_id": 3 + } + }, + { + "timestamp": 1.3378053328488022, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "StartOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.96, + "transcript": "I don", + "words": [ + { + "word": "I", + "confidence": 0.8521 + }, + { + "word": "don", + "confidence": 0.9858 + } + ], + "end_of_turn_confidence": 0.1526, + "sequence_id": 4 + } + }, + { + "timestamp": 1.575752625009045, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.2, + "transcript": "I don't know", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know", + "confidence": 0.9956 + } + ], + "end_of_turn_confidence": 0.0815, + "sequence_id": 5 + } + }, + { + "timestamp": 1.809568207943812, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.44, + "transcript": "I don't know. I", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know.", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 0.9995 + } + ], + "end_of_turn_confidence": 0.0533, + "sequence_id": 6 + } + }, + { + "timestamp": 2.0778977079316974, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.6800001, + "transcript": "I don't know. I'm not", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know.", + "confidence": 1.0 + }, + { + "word": "I'm", + "confidence": 1.0 + }, + { + "word": "not", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0296, + "sequence_id": 7 + } + }, + { + "timestamp": 2.3323032909538597, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.9200001, + "transcript": "I don't know. I'm not sure she", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know.", + "confidence": 1.0 + }, + { + "word": "I'm", + "confidence": 1.0 + }, + { + "word": "not", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 0.9692 + }, + { + "word": "she", + "confidence": 0.6968 + } + ], + "end_of_turn_confidence": 0.1591, + "sequence_id": 8 + } + }, + { + "timestamp": 2.563972583040595, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.16, + "transcript": "I don't know. I'm not so sure.", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know.", + "confidence": 1.0 + }, + { + "word": "I'm", + "confidence": 1.0 + }, + { + "word": "not", + "confidence": 1.0 + }, + { + "word": "so", + "confidence": 0.9971 + }, + { + "word": "sure.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.5312, + "sequence_id": 9 + } + }, + { + "timestamp": 2.766235665883869, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "EndOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.32, + "transcript": "I don't know. I'm not so sure.", + "words": [ + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "don't", + "confidence": 1.0 + }, + { + "word": "know.", + "confidence": 1.0 + }, + { + "word": "I'm", + "confidence": 1.0 + }, + { + "word": "not", + "confidence": 1.0 + }, + { + "word": "so", + "confidence": 0.9971 + }, + { + "word": "sure.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.7129, + "sequence_id": 10 + } + }, + { + "timestamp": 2.980985000031069, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 2.56, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6235, + "sequence_id": 11 + } + }, + { + "timestamp": 3.040183125063777, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 2.6399999, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.7163, + "sequence_id": 12 + } + }, + { + "timestamp": 3.134053166024387, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 2.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.7603, + "sequence_id": 13 + } + }, + { + "timestamp": 3.200523457955569, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 2.8, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.8013, + "sequence_id": 14 + } + }, + { + "timestamp": 3.3396010829601437, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 2.88, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.8052, + "sequence_id": 15 + } + }, + { + "timestamp": 3.462065916042775, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 3.04, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6968, + "sequence_id": 16 + } + }, + { + "timestamp": 3.532107833074406, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 3.12, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.7026, + "sequence_id": 17 + } + }, + { + "timestamp": 3.6854247499722987, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 3.28, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6123, + "sequence_id": 18 + } + }, + { + "timestamp": 3.9346718329470605, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 3.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.4551, + "sequence_id": 19 + } + }, + { + "timestamp": 4.174561291001737, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 3.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.293, + "sequence_id": 20 + } + }, + { + "timestamp": 4.423174874857068, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 4.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1186, + "sequence_id": 21 + } + }, + { + "timestamp": 4.661856249906123, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 4.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1186, + "sequence_id": 22 + } + }, + { + "timestamp": 4.934342915890738, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 4.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0629, + "sequence_id": 23 + } + }, + { + "timestamp": 5.1988217500038445, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 4.7200003, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0302, + "sequence_id": 24 + } + }, + { + "timestamp": 5.868438957957551, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 4.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0104, + "sequence_id": 25 + } + }, + { + "timestamp": 5.924830165924504, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 5.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0039, + "sequence_id": 26 + } + }, + { + "timestamp": 6.008775374852121, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 5.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.003, + "sequence_id": 27 + } + }, + { + "timestamp": 6.224981207866222, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 5.68, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0027, + "sequence_id": 28 + } + }, + { + "timestamp": 6.400387583067641, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 5.92, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0944, + "sequence_id": 29 + } + }, + { + "timestamp": 6.6102081660646945, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 6.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.083, + "sequence_id": 30 + } + }, + { + "timestamp": 6.853603166062385, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 6.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0674, + "sequence_id": 31 + } + }, + { + "timestamp": 7.1176844160072505, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "badd4484-3b22-42c5-bd5f-13fd2014021b", + "event": "Update", + "turn_index": 1, + "audio_window_start": 2.32, + "audio_window_end": 6.64, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0348, + "sequence_id": 32 + } + } + ], + "transcript": "I don't know. I'm not so sure." +} \ No newline at end of file diff --git a/evals/stt/results/not_so_sure-speechmatics.json b/evals/stt/results/not_so_sure-speechmatics.json new file mode 100644 index 0000000..3893801 --- /dev/null +++ b/evals/stt/results/not_so_sure-speechmatics.json @@ -0,0 +1,936 @@ +{ + "audio_file": "not_so_sure.m4a", + "audio_path": "../audio/not_so_sure.m4a", + "provider": "speechmatics", + "duration": 3.784853, + "created_at": "2026-01-20T13:38:01.957263", + "events": [ + { + "timestamp": 2.50060111284256e-07, + "event_type": "Info", + "data": { + "message": "Info", + "type": "concurrent_session_usage", + "reason": "1 concurrent sessions active out of quota 2", + "usage": 1, + "quota": 2, + "last_updated": "2026-01-20T08:07:53Z" + } + }, + { + "timestamp": 0.17636274988763034, + "event_type": "RecognitionStarted", + "data": { + "message": "RecognitionStarted", + "orchestrator_version": "2026.01.09+e449221ca0+14.12.0", + "id": "ff50bcc6-03cc-4609-b52b-c61492be97b0", + "language_pack_info": { + "adapted": false, + "itn": true, + "language_description": "English", + "word_delimiter": " ", + "writing_direction": "left-to-right" + } + } + }, + { + "timestamp": 0.1765422080643475, + "event_type": "Info", + "data": { + "message": "Info", + "type": "recognition_quality", + "reason": "Running recognition using a broadcast model quality.", + "quality": "broadcast" + } + }, + { + "timestamp": 0.44156987499445677, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 1 + } + }, + { + "timestamp": 0.5090052080340683, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 2 + } + }, + { + "timestamp": 0.5927771248389035, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 3 + } + }, + { + "timestamp": 0.6792412919458002, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 4 + } + }, + { + "timestamp": 0.7540834578685462, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 5 + } + }, + { + "timestamp": 0.8363401249516755, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 6 + } + }, + { + "timestamp": 0.916276125004515, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 7 + } + }, + { + "timestamp": 1.0025545828975737, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 8 + } + }, + { + "timestamp": 1.0930295418947935, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 9 + } + }, + { + "timestamp": 1.1681176249403507, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 10 + } + }, + { + "timestamp": 1.2440201670397073, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 11 + } + }, + { + "timestamp": 1.3254928330425173, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 12 + } + }, + { + "timestamp": 1.411379124969244, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 13 + } + }, + { + "timestamp": 1.4989973329938948, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 14 + } + }, + { + "timestamp": 1.569762917002663, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 15 + } + }, + { + "timestamp": 1.6669557499699295, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 16 + } + }, + { + "timestamp": 1.7321407499257475, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 17 + } + }, + { + "timestamp": 1.8123597078956664, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 18 + } + }, + { + "timestamp": 1.89311487483792, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 19 + } + }, + { + "timestamp": 1.99575070803985, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 20 + } + }, + { + "timestamp": 2.0635348330251873, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 21 + } + }, + { + "timestamp": 2.136281125014648, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 22 + } + }, + { + "timestamp": 2.2212352079804987, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 23 + } + }, + { + "timestamp": 2.300102249952033, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 24 + } + }, + { + "timestamp": 2.3838018749374896, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 25 + } + }, + { + "timestamp": 2.4612751249223948, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 26 + } + }, + { + "timestamp": 2.5520844168495387, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 27 + } + }, + { + "timestamp": 2.6254100420046598, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 28 + } + }, + { + "timestamp": 2.7110170419327915, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 29 + } + }, + { + "timestamp": 2.793728666845709, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 30 + } + }, + { + "timestamp": 2.8698849170468748, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 31 + } + }, + { + "timestamp": 2.9517348748631775, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 32 + } + }, + { + "timestamp": 3.034996416885406, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 33 + } + }, + { + "timestamp": 3.1222795830108225, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 34 + } + }, + { + "timestamp": 3.2133053748402745, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 35 + } + }, + { + "timestamp": 3.2794892080128193, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 36 + } + }, + { + "timestamp": 3.360972832888365, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 37 + } + }, + { + "timestamp": 3.480351625010371, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 38 + } + }, + { + "timestamp": 3.527200457872823, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 39 + } + }, + { + "timestamp": 3.614834832958877, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 40 + } + }, + { + "timestamp": 3.7000621668994427, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 41 + } + }, + { + "timestamp": 3.7709098330233246, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 42 + } + }, + { + "timestamp": 3.870571249863133, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 43 + } + }, + { + "timestamp": 3.9319135828409344, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 44 + } + }, + { + "timestamp": 4.0240056668408215, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 45 + } + }, + { + "timestamp": 4.1135993748903275, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 46 + } + }, + { + "timestamp": 4.178906166926026, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 47 + } + }, + { + "timestamp": 4.262735291849822, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 48 + } + }, + { + "timestamp": 4.3524885000661016, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 49 + } + }, + { + "timestamp": 4.42170758289285, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 50 + } + }, + { + "timestamp": 4.503200083039701, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 51 + } + }, + { + "timestamp": 4.588893749983981, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 52 + } + }, + { + "timestamp": 4.6728779170662165, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 53 + } + }, + { + "timestamp": 4.749415792059153, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 54 + } + }, + { + "timestamp": 4.834314750041813, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 55 + } + }, + { + "timestamp": 4.934304124908522, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 56 + } + }, + { + "timestamp": 5.015187042066827, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 57 + } + }, + { + "timestamp": 5.083739625057206, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 58 + } + }, + { + "timestamp": 5.15739579196088, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 59 + } + }, + { + "timestamp": 5.254215708002448, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 60 + } + }, + { + "timestamp": 5.319055167026818, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 61 + } + }, + { + "timestamp": 5.422228208044544, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 62 + } + }, + { + "timestamp": 5.493815457914025, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 63 + } + }, + { + "timestamp": 5.562712874962017, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 64 + } + }, + { + "timestamp": 5.677756666904315, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 65 + } + }, + { + "timestamp": 5.728489124914631, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 66 + } + }, + { + "timestamp": 5.73234708304517, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [ + { + "alternatives": [ + { + "confidence": 1.0, + "content": "I", + "language": "en" + } + ], + "end_time": 0.8, + "start_time": 0.64, + "type": "word" + } + ], + "metadata": { + "end_time": 0.8, + "start_time": 0.0, + "transcript": "I " + } + } + }, + { + "timestamp": 5.831468666903675, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 67 + } + }, + { + "timestamp": 5.9311752079520375, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 68 + } + }, + { + "timestamp": 5.970860542031005, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 69 + } + }, + { + "timestamp": 6.0573643748648465, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 70 + } + }, + { + "timestamp": 6.071638958062977, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [ + { + "alternatives": [ + { + "confidence": 1.0, + "content": "don't", + "language": "en" + } + ], + "end_time": 1.08, + "start_time": 0.84, + "type": "word" + }, + { + "alternatives": [ + { + "confidence": 1.0, + "content": "know", + "language": "en" + } + ], + "end_time": 1.2, + "start_time": 1.08, + "type": "word" + }, + { + "alternatives": [ + { + "confidence": 1.0, + "content": ".", + "language": "en" + } + ], + "attaches_to": "previous", + "end_time": 1.2, + "is_eos": true, + "start_time": 1.2, + "type": "punctuation" + } + ], + "metadata": { + "end_time": 1.2, + "start_time": 0.8, + "transcript": "don't know. " + } + } + }, + { + "timestamp": 6.143923291936517, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 71 + } + }, + { + "timestamp": 6.229828458046541, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 72 + } + }, + { + "timestamp": 6.297467292053625, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 73 + } + }, + { + "timestamp": 6.388417499838397, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 74 + } + }, + { + "timestamp": 6.46747541683726, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [ + { + "alternatives": [ + { + "confidence": 1.0, + "content": "I'm", + "language": "en" + } + ], + "end_time": 1.4, + "start_time": 1.2, + "type": "word" + }, + { + "alternatives": [ + { + "confidence": 1.0, + "content": "not", + "language": "en" + } + ], + "end_time": 1.56, + "start_time": 1.4, + "type": "word" + } + ], + "metadata": { + "end_time": 1.56, + "start_time": 1.2, + "transcript": "I'm not " + } + } + }, + { + "timestamp": 6.467542249942198, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 75 + } + }, + { + "timestamp": 6.571689167059958, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 76 + } + }, + { + "timestamp": 6.633496082853526, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 77 + } + }, + { + "timestamp": 6.705628624884412, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 78 + } + }, + { + "timestamp": 6.791943500051275, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 79 + } + }, + { + "timestamp": 6.8231504168361425, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [ + { + "alternatives": [ + { + "confidence": 1.0, + "content": "so", + "language": "en" + } + ], + "end_time": 1.72, + "start_time": 1.56, + "type": "word" + } + ], + "metadata": { + "end_time": 1.72, + "start_time": 1.56, + "transcript": "so " + } + } + }, + { + "timestamp": 6.889297208050266, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 80 + } + }, + { + "timestamp": 6.96820458304137, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 81 + } + }, + { + "timestamp": 7.030788874952123, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 82 + } + }, + { + "timestamp": 7.114988874876872, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 83 + } + }, + { + "timestamp": 7.1660370419267565, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [ + { + "alternatives": [ + { + "confidence": 1.0, + "content": "sure", + "language": "en" + } + ], + "end_time": 2.2, + "start_time": 1.76, + "type": "word" + }, + { + "alternatives": [ + { + "confidence": 1.0, + "content": ".", + "language": "en" + } + ], + "attaches_to": "previous", + "end_time": 2.2, + "is_eos": true, + "start_time": 2.2, + "type": "punctuation" + } + ], + "metadata": { + "end_time": 2.2, + "start_time": 1.72, + "transcript": "sure. " + } + } + }, + { + "timestamp": 7.197767958045006, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 84 + } + }, + { + "timestamp": 7.281636083032936, + "event_type": "AudioAdded", + "data": { + "message": "AudioAdded", + "seq_no": 85 + } + }, + { + "timestamp": 7.966639708029106, + "event_type": "AddTranscript", + "data": { + "message": "AddTranscript", + "format": "2.9", + "results": [], + "metadata": { + "end_time": 6.72, + "start_time": 2.28, + "transcript": "" + } + } + }, + { + "timestamp": 7.966674832860008, + "event_type": "EndOfTranscript", + "data": { + "message": "EndOfTranscript" + } + } + ], + "transcript": "I don't know. I'm not so sure." +} \ No newline at end of file diff --git a/evals/stt/results/vad-deepgram-flux.json b/evals/stt/results/vad-deepgram-flux.json new file mode 100644 index 0000000..a93db46 --- /dev/null +++ b/evals/stt/results/vad-deepgram-flux.json @@ -0,0 +1,2931 @@ +{ + "audio_file": "vad.m4a", + "audio_path": "../audio/vad.m4a", + "provider": "deepgram-flux", + "duration": 13.606893, + "created_at": "2026-01-20T13:06:16.523794", + "events": [ + { + "timestamp": 2.0791776478290558e-07, + "event_type": "Connected", + "data": { + "type": "Connected", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "sequence_id": 0 + } + }, + { + "timestamp": 0.5991181668359786, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0739, + "sequence_id": 1 + } + }, + { + "timestamp": 0.8387298330198973, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0226, + "sequence_id": 2 + } + }, + { + "timestamp": 1.0813759579323232, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0106, + "sequence_id": 3 + } + }, + { + "timestamp": 1.329924332909286, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0058, + "sequence_id": 4 + } + }, + { + "timestamp": 1.5782449168618768, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0292, + "sequence_id": 5 + } + }, + { + "timestamp": 1.8236550828441978, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0247, + "sequence_id": 6 + } + }, + { + "timestamp": 2.073379916837439, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.6800001, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.009, + "sequence_id": 7 + } + }, + { + "timestamp": 2.3028037920594215, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.9200001, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0031, + "sequence_id": 8 + } + }, + { + "timestamp": 2.5454757078550756, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0017, + "sequence_id": 9 + } + }, + { + "timestamp": 2.8026564170140773, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0024, + "sequence_id": 10 + } + }, + { + "timestamp": 3.034466916928068, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.6399999, + "transcript": "The", + "words": [ + { + "word": "The", + "confidence": 0.5684 + } + ], + "end_of_turn_confidence": 0.0023, + "sequence_id": 11 + } + }, + { + "timestamp": 3.280960999894887, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "StartOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 2.88, + "transcript": "This is", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0304, + "sequence_id": 12 + } + }, + { + "timestamp": 3.507918791845441, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.12, + "transcript": "This is", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0345, + "sequence_id": 13 + } + }, + { + "timestamp": 3.767754166852683, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.3600001, + "transcript": "This is", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0382, + "sequence_id": 14 + } + }, + { + "timestamp": 4.002102291909978, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.6, + "transcript": "This is some", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "some", + "confidence": 0.5229 + } + ], + "end_of_turn_confidence": 0.002, + "sequence_id": 15 + } + }, + { + "timestamp": 4.240494291996583, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 3.84, + "transcript": "This is something", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 0.9692 + } + ], + "end_of_turn_confidence": 0.01, + "sequence_id": 16 + } + }, + { + "timestamp": 4.523886707844213, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.08, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 0.9941 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0206, + "sequence_id": 17 + } + }, + { + "timestamp": 4.734341792063788, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.32, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 0.9985 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0133, + "sequence_id": 18 + } + }, + { + "timestamp": 5.001597333000973, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.56, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 0.9995 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0151, + "sequence_id": 19 + } + }, + { + "timestamp": 5.227020208025351, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 4.8, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0108, + "sequence_id": 20 + } + }, + { + "timestamp": 5.476216417038813, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.04, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.017, + "sequence_id": 21 + } + }, + { + "timestamp": 5.706772041972727, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.28, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0259, + "sequence_id": 22 + } + }, + { + "timestamp": 5.9626319168601185, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.52, + "transcript": "This is something that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0107, + "sequence_id": 23 + } + }, + { + "timestamp": 6.2007496249862015, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 5.76, + "transcript": "This is something that I am", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9551 + } + ], + "end_of_turn_confidence": 0.006, + "sequence_id": 24 + } + }, + { + "timestamp": 6.439131374936551, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.0, + "transcript": "This is something that I am just", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9868 + }, + { + "word": "just", + "confidence": 0.7397 + } + ], + "end_of_turn_confidence": 0.0036, + "sequence_id": 25 + } + }, + { + "timestamp": 6.685360916890204, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.24, + "transcript": "This is something that I am testing", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.999 + }, + { + "word": "testing", + "confidence": 0.9897 + } + ], + "end_of_turn_confidence": 0.0223, + "sequence_id": 26 + } + }, + { + "timestamp": 6.922765875002369, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.48, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9995 + }, + { + "word": "to", + "confidence": 0.9907 + } + ], + "end_of_turn_confidence": 0.0156, + "sequence_id": 27 + } + }, + { + "timestamp": 7.1796076248865575, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.7200003, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.033, + "sequence_id": 28 + } + }, + { + "timestamp": 7.420775583013892, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 6.96, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0189, + "sequence_id": 29 + } + }, + { + "timestamp": 7.662735000019893, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.2, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9985 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0316, + "sequence_id": 30 + } + }, + { + "timestamp": 7.901641249889508, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.44, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9985 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0467, + "sequence_id": 31 + } + }, + { + "timestamp": 8.193162374896929, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.68, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.998 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0161, + "sequence_id": 32 + } + }, + { + "timestamp": 8.390170000027865, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 7.92, + "transcript": "This is something that I am testing to", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9976 + }, + { + "word": "to", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0323, + "sequence_id": 33 + } + }, + { + "timestamp": 8.670882124919444, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.16, + "transcript": "This is something that I am testing to make", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9976 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 0.9697 + } + ], + "end_of_turn_confidence": 0.0029, + "sequence_id": 34 + } + }, + { + "timestamp": 8.921790292020887, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.4, + "transcript": "This is something that I am testing to make sure", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.9985 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 0.9995 + }, + { + "word": "sure", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0081, + "sequence_id": 35 + } + }, + { + "timestamp": 9.150760042015463, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.64, + "transcript": "This is something that I am testing to make sure that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.002, + "sequence_id": 36 + } + }, + { + "timestamp": 9.389485124964267, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 8.88, + "transcript": "This is something that I am testing to make sure that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0078, + "sequence_id": 37 + } + }, + { + "timestamp": 10.12114379205741, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.12, + "transcript": "This is something that I am testing to make sure that", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0092, + "sequence_id": 38 + } + }, + { + "timestamp": 10.164927457924932, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.36, + "transcript": "This is something that I am testing to make sure that the web", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.6372 + }, + { + "word": "web", + "confidence": 0.2715 + } + ], + "end_of_turn_confidence": 0.0168, + "sequence_id": 39 + } + }, + { + "timestamp": 10.287556666880846, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.6, + "transcript": "This is something that I am testing to make sure that the bag", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.7988 + }, + { + "word": "bag", + "confidence": 0.4683 + } + ], + "end_of_turn_confidence": 0.0435, + "sequence_id": 40 + } + }, + { + "timestamp": 10.371591208036989, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 9.84, + "transcript": "This is something that I am testing to make sure that bad", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "bad", + "confidence": 0.5386 + } + ], + "end_of_turn_confidence": 0.0258, + "sequence_id": 41 + } + }, + { + "timestamp": 10.592813499970362, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.08, + "transcript": "This is something that I am testing to make sure that bad", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "bad", + "confidence": 0.7607 + } + ], + "end_of_turn_confidence": 0.0147, + "sequence_id": 42 + } + }, + { + "timestamp": 10.8256475829985, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.32, + "transcript": "This is something that I am testing to make sure that bad is", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "bad", + "confidence": 0.6484 + }, + { + "word": "is", + "confidence": 0.9995 + } + ], + "end_of_turn_confidence": 0.0019, + "sequence_id": 43 + } + }, + { + "timestamp": 11.079555582953617, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.56, + "transcript": "This is something that I am testing to make sure that the VAT is", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.2808 + }, + { + "word": "VAT", + "confidence": 0.3472 + }, + { + "word": "is", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0118, + "sequence_id": 44 + } + }, + { + "timestamp": 11.314390541985631, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 10.8, + "transcript": "This is something that I am testing to make sure that that is what", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 0.2949 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "what", + "confidence": 0.6787 + } + ], + "end_of_turn_confidence": 0.0087, + "sequence_id": 45 + } + }, + { + "timestamp": 11.60575837502256, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.04, + "transcript": "This is something that I am testing to make sure that that is working", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 0.2474 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 0.998 + } + ], + "end_of_turn_confidence": 0.0225, + "sequence_id": 46 + } + }, + { + "timestamp": 11.842209457885474, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.28, + "transcript": "This is something that I am testing to make sure that VAD is working properly", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "VAD", + "confidence": 0.999 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 0.9995 + }, + { + "word": "properly", + "confidence": 0.8716 + } + ], + "end_of_turn_confidence": 0.0368, + "sequence_id": 47 + } + }, + { + "timestamp": 12.073602374875918, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.52, + "transcript": "This is something that I am testing to make sure that the rad is working properly", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.3406 + }, + { + "word": "rad", + "confidence": 0.1747 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly", + "confidence": 0.998 + } + ], + "end_of_turn_confidence": 0.0746, + "sequence_id": 48 + } + }, + { + "timestamp": 12.294721042038873, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 11.76, + "transcript": "This is something that I am testing to make sure that the VAT is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "VAT", + "confidence": 0.205 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.2715, + "sequence_id": 49 + } + }, + { + "timestamp": 12.542759666917846, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.0, + "transcript": "This is something that I am testing to make sure that the VAT is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "VAT", + "confidence": 0.1903 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.0775, + "sequence_id": 50 + } + }, + { + "timestamp": 12.783193832961842, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.24, + "transcript": "This is something that I am testing to make sure that the rad is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "rad", + "confidence": 0.1826 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.1837, + "sequence_id": 51 + } + }, + { + "timestamp": 13.045388832921162, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.48, + "transcript": "This is something that I am testing to make sure that the rad is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "rad", + "confidence": 0.1855 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.4099, + "sequence_id": 52 + } + }, + { + "timestamp": 13.27779766684398, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.72, + "transcript": "This is something that I am testing to make sure that the rad is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "rad", + "confidence": 0.1855 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.7061, + "sequence_id": 53 + } + }, + { + "timestamp": 13.36575054191053, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "EndOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 12.8, + "transcript": "This is something that I am testing to make sure that the rad is working properly.", + "words": [ + { + "word": "This", + "confidence": 1.0 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "something", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "I", + "confidence": 1.0 + }, + { + "word": "am", + "confidence": 0.9995 + }, + { + "word": "testing", + "confidence": 0.999 + }, + { + "word": "to", + "confidence": 1.0 + }, + { + "word": "make", + "confidence": 1.0 + }, + { + "word": "sure", + "confidence": 1.0 + }, + { + "word": "that", + "confidence": 1.0 + }, + { + "word": "the", + "confidence": 0.5513 + }, + { + "word": "rad", + "confidence": 0.1859 + }, + { + "word": "is", + "confidence": 1.0 + }, + { + "word": "working", + "confidence": 1.0 + }, + { + "word": "properly.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.791, + "sequence_id": 54 + } + }, + { + "timestamp": 13.604034666903317, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 13.04, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.5913, + "sequence_id": 55 + } + }, + { + "timestamp": 13.757088999962434, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 13.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.7568, + "sequence_id": 56 + } + }, + { + "timestamp": 13.844318666961044, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 13.28, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.8135, + "sequence_id": 57 + } + }, + { + "timestamp": 14.096143374918029, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 13.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6445, + "sequence_id": 58 + } + }, + { + "timestamp": 14.32369429199025, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 13.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6685, + "sequence_id": 59 + } + }, + { + "timestamp": 14.57952274987474, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 14.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.6743, + "sequence_id": 60 + } + }, + { + "timestamp": 14.821225458057597, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 14.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3857, + "sequence_id": 61 + } + }, + { + "timestamp": 15.078599707921967, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 14.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1913, + "sequence_id": 62 + } + }, + { + "timestamp": 15.8081968750339, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 14.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1469, + "sequence_id": 63 + } + }, + { + "timestamp": 15.853020707843825, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 14.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0952, + "sequence_id": 64 + } + }, + { + "timestamp": 15.972035791957751, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 15.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0655, + "sequence_id": 65 + } + }, + { + "timestamp": 16.056484082946554, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 15.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0452, + "sequence_id": 66 + } + }, + { + "timestamp": 16.30366349988617, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 15.68, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0339, + "sequence_id": 67 + } + }, + { + "timestamp": 16.55612133303657, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 15.92, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0199, + "sequence_id": 68 + } + }, + { + "timestamp": 16.809007041854784, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 16.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0143, + "sequence_id": 69 + } + }, + { + "timestamp": 17.008537916932255, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 16.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3384, + "sequence_id": 70 + } + }, + { + "timestamp": 17.2497119999025, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 16.64, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3823, + "sequence_id": 71 + } + }, + { + "timestamp": 17.51993320789188, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 16.88, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3843, + "sequence_id": 72 + } + }, + { + "timestamp": 17.765261041931808, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 17.12, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2246, + "sequence_id": 73 + } + }, + { + "timestamp": 18.005616874899715, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 17.36, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1076, + "sequence_id": 74 + } + }, + { + "timestamp": 18.270230083027855, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 17.6, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0357, + "sequence_id": 75 + } + }, + { + "timestamp": 18.490703457966447, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 17.84, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0112, + "sequence_id": 76 + } + }, + { + "timestamp": 18.75762120797299, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 18.08, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0053, + "sequence_id": 77 + } + }, + { + "timestamp": 18.954981041839346, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 18.32, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0018, + "sequence_id": 78 + } + }, + { + "timestamp": 19.206135916989297, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "d7bb925b-1ba7-4fff-a36e-5077b6c580f7", + "event": "Update", + "turn_index": 1, + "audio_window_start": 12.8, + "audio_window_end": 18.56, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0008, + "sequence_id": 79 + } + } + ], + "transcript": "This is something that I am testing to make sure that the rad is working properly." +} \ No newline at end of file diff --git a/evals/stt/results/vad-deepgram.json b/evals/stt/results/vad-deepgram.json new file mode 100644 index 0000000..e2f6666 --- /dev/null +++ b/evals/stt/results/vad-deepgram.json @@ -0,0 +1,1131 @@ +{ + "audio_file": "vad.m4a", + "audio_path": "../audio/vad.m4a", + "provider": "deepgram", + "duration": 13.606893, + "created_at": "2026-01-20T13:23:22.720144", + "events": [ + { + "timestamp": 2.1248124539852142e-06, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0399375, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 0.5033409169409424, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 1.1 + } + }, + { + "timestamp": 1.0483719168696553, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0799375, + "start": 0.0, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 1.2717794578056782, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.23, + "start": 0.0, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 1.3922397918067873, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 2.51 + } + }, + { + "timestamp": 2.141541542019695, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0499375, + "start": 2.23, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "This is", + "confidence": 0.99902344, + "words": [ + { + "word": "this", + "start": 2.31, + "end": 2.71, + "confidence": 0.8803711, + "punctuated_word": "This" + }, + { + "word": "is", + "start": 2.71, + "end": 2.87, + "confidence": 0.99902344, + "punctuated_word": "is" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 3.198163916822523, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0899377, + "start": 2.23, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "This is something that", + "confidence": 0.9970703, + "words": [ + { + "word": "this", + "start": 2.31, + "end": 2.71, + "confidence": 0.9223633, + "punctuated_word": "This" + }, + { + "word": "is", + "start": 2.71, + "end": 2.95, + "confidence": 1.0, + "punctuated_word": "is" + }, + { + "word": "something", + "start": 2.95, + "end": 3.67, + "confidence": 0.93408203, + "punctuated_word": "something" + }, + { + "word": "that", + "start": 3.67, + "end": 3.99, + "confidence": 0.9970703, + "punctuated_word": "that" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 3.610837416956201, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.4299998, + "start": 2.23, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "This is something that", + "confidence": 0.9980469, + "words": [ + { + "word": "this", + "start": 2.31, + "end": 2.71, + "confidence": 0.9448242, + "punctuated_word": "This" + }, + { + "word": "is", + "start": 2.71, + "end": 2.95, + "confidence": 1.0, + "punctuated_word": "is" + }, + { + "word": "something", + "start": 2.95, + "end": 3.75, + "confidence": 0.9291992, + "punctuated_word": "something" + }, + { + "word": "that", + "start": 3.75, + "end": 4.0699997, + "confidence": 0.9980469, + "punctuated_word": "that" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 4.484807749977335, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 5.55 + } + }, + { + "timestamp": 4.5869907499291, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0199375, + "start": 4.66, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "I", + "confidence": 0.9301758, + "words": [ + { + "word": "i", + "start": 5.2999997, + "end": 5.54, + "confidence": 0.9301758, + "punctuated_word": "I" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 4.587112749926746, + "event_type": "UtteranceEnd", + "data": { + "type": "UtteranceEnd", + "channel": [ + 0, + 1 + ], + "last_word_end": 4.0699997 + } + }, + { + "timestamp": 5.644599041901529, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0599375, + "start": 4.66, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "I am testing to", + "confidence": 0.9980469, + "words": [ + { + "word": "i", + "start": 5.3799996, + "end": 5.62, + "confidence": 0.9980469, + "punctuated_word": "I" + }, + { + "word": "am", + "start": 5.62, + "end": 5.8599997, + "confidence": 0.96240234, + "punctuated_word": "am" + }, + { + "word": "testing", + "start": 5.8599997, + "end": 6.2599998, + "confidence": 1.0, + "punctuated_word": "testing" + }, + { + "word": "to", + "start": 6.2599998, + "end": 6.5, + "confidence": 0.9892578, + "punctuated_word": "to" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 5.970383666921407, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.31, + "start": 4.66, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "I am testing to", + "confidence": 0.99902344, + "words": [ + { + "word": "i", + "start": 5.3799996, + "end": 5.62, + "confidence": 0.99902344, + "punctuated_word": "I" + }, + { + "word": "am", + "start": 5.62, + "end": 5.8599997, + "confidence": 0.95410156, + "punctuated_word": "am" + }, + { + "word": "testing", + "start": 5.8599997, + "end": 6.2599998, + "confidence": 1.0, + "punctuated_word": "testing" + }, + { + "word": "to", + "start": 6.2599998, + "end": 6.58, + "confidence": 0.99316406, + "punctuated_word": "to" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 6.601957832928747, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 7.62 + } + }, + { + "timestamp": 6.9396405830048025, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0299377, + "start": 6.97, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 6.939695374807343, + "event_type": "UtteranceEnd", + "data": { + "type": "UtteranceEnd", + "channel": [ + 0, + 1 + ], + "last_word_end": 6.58 + } + }, + { + "timestamp": 8.006767624989152, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0699382, + "start": 6.97, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "make sure that", + "confidence": 0.99902344, + "words": [ + { + "word": "make", + "start": 7.5299997, + "end": 8.17, + "confidence": 0.99902344, + "punctuated_word": "make" + }, + { + "word": "sure", + "start": 8.17, + "end": 8.41, + "confidence": 1.0, + "punctuated_word": "sure" + }, + { + "word": "that", + "start": 8.41, + "end": 8.65, + "confidence": 0.9980469, + "punctuated_word": "that" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 8.733146582962945, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.7199998, + "start": 6.97, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "make sure that that", + "confidence": 0.99902344, + "words": [ + { + "word": "make", + "start": 7.5299997, + "end": 8.25, + "confidence": 0.99902344, + "punctuated_word": "make" + }, + { + "word": "sure", + "start": 8.25, + "end": 8.41, + "confidence": 1.0, + "punctuated_word": "sure" + }, + { + "word": "that", + "start": 8.41, + "end": 8.969999, + "confidence": 0.9980469, + "punctuated_word": "that" + }, + { + "word": "that", + "start": 8.969999, + "end": 9.37, + "confidence": 0.34033203, + "punctuated_word": "that" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 8.792208916973323, + "event_type": "SpeechStarted", + "data": { + "type": "SpeechStarted", + "channel": [ + 0, + 1 + ], + "timestamp": 9.79 + } + }, + { + "timestamp": 9.710732833016664, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0299377, + "start": 9.69, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "is", + "confidence": 0.99316406, + "words": [ + { + "word": "is", + "start": 10.089999, + "end": 10.33, + "confidence": 0.99316406, + "punctuated_word": "is" + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 10.779752457980067, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.9800005, + "start": 9.69, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "is working properly.", + "confidence": 0.93603516, + "words": [ + { + "word": "is", + "start": 10.089999, + "end": 10.41, + "confidence": 0.93603516, + "punctuated_word": "is" + }, + { + "word": "working", + "start": 10.41, + "end": 11.049999, + "confidence": 0.99121094, + "punctuated_word": "working" + }, + { + "word": "properly", + "start": 11.049999, + "end": 11.45, + "confidence": 0.7998047, + "punctuated_word": "properly." + } + ] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 11.75167812500149, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 1.0499372, + "start": 11.67, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 11.75173195800744, + "event_type": "UtteranceEnd", + "data": { + "type": "UtteranceEnd", + "channel": [ + 0, + 1 + ], + "last_word_end": 11.45 + } + }, + { + "timestamp": 13.053302166983485, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0968122, + "start": 11.67, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 13.95449158293195, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 3.1368122, + "start": 11.67, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 15.400284249801189, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 4.176812, + "start": 11.67, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 15.893622082890943, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 3.0199995, + "start": 11.67, + "is_final": true, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 15.894181541865692, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 2.0368128, + "start": 14.69, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 16.9369917078875, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 3.0768137, + "start": 14.69, + "is_final": false, + "speech_final": false, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 18.010773707879707, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 3.876813, + "start": 14.69, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 18.01088787498884, + "event_type": "Results", + "data": { + "type": "Results", + "channel_index": [ + 0, + 1 + ], + "duration": 0.0, + "start": 18.566813, + "is_final": true, + "speech_final": true, + "channel": { + "alternatives": [ + { + "transcript": "", + "confidence": 0.0, + "words": [] + } + ] + }, + "metadata": { + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "model_info": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + }, + "model_uuid": "40bd3654-e622-47c4-a111-63a61b23bfe8" + }, + "from_finalize": false + } + }, + { + "timestamp": 18.01097366702743, + "event_type": "Metadata", + "data": { + "type": "Metadata", + "transaction_key": "deprecated", + "request_id": "02f8e10c-b08e-4ffd-952e-3cde2adcbf2f", + "sha256": "8dd2361b38ded42a65ec3b79bd9f64742e3bec916a0eb8783f2204e55cb663fc", + "created": "2026-01-20T07:53:03.141Z", + "duration": 18.566813, + "channels": 1, + "models": [ + "40bd3654-e622-47c4-a111-63a61b23bfe8" + ], + "model_info": { + "40bd3654-e622-47c4-a111-63a61b23bfe8": { + "name": "general-nova-3", + "version": "2025-04-17.21547", + "arch": "nova-3" + } + } + } + } + ], + "transcript": "This is something that I am testing to make sure that that is working properly." +} \ No newline at end of file diff --git a/evals/stt/results/yes-deepgram-flux.json b/evals/stt/results/yes-deepgram-flux.json new file mode 100644 index 0000000..7f70551 --- /dev/null +++ b/evals/stt/results/yes-deepgram-flux.json @@ -0,0 +1,402 @@ +{ + "audio_file": "yes.m4a", + "audio_path": "../audio/yes.m4a", + "provider": "deepgram-flux", + "duration": 2.507755, + "created_at": "2026-01-20T13:33:37.737569", + "events": [ + { + "timestamp": 2.0791776478290558e-07, + "event_type": "Connected", + "data": { + "type": "Connected", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "sequence_id": 0 + } + }, + { + "timestamp": 0.6149860408622772, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2494, + "sequence_id": 1 + } + }, + { + "timestamp": 0.8699209159240127, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1246, + "sequence_id": 2 + } + }, + { + "timestamp": 1.0665327080059797, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0557, + "sequence_id": 3 + } + }, + { + "timestamp": 1.319559457944706, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "StartOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 0.96, + "transcript": "Yes.", + "words": [ + { + "word": "Yes.", + "confidence": 0.9761 + } + ], + "end_of_turn_confidence": 0.0793, + "sequence_id": 4 + } + }, + { + "timestamp": 1.5604322908911854, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.2, + "transcript": "Yes.", + "words": [ + { + "word": "Yes.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.5703, + "sequence_id": 5 + } + }, + { + "timestamp": 1.6325784579385072, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "EndOfTurn", + "turn_index": 0, + "audio_window_start": 0.0, + "audio_window_end": 1.28, + "transcript": "Yes.", + "words": [ + { + "word": "Yes.", + "confidence": 1.0 + } + ], + "end_of_turn_confidence": 0.7026, + "sequence_id": 6 + } + }, + { + "timestamp": 1.897370790829882, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 1.52, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.4883, + "sequence_id": 7 + } + }, + { + "timestamp": 2.117000916041434, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 1.76, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.3801, + "sequence_id": 8 + } + }, + { + "timestamp": 2.3733394159935415, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 2.0, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.2346, + "sequence_id": 9 + } + }, + { + "timestamp": 2.6072654998861253, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 2.24, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1049, + "sequence_id": 10 + } + }, + { + "timestamp": 2.85038537485525, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 2.48, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.075, + "sequence_id": 11 + } + }, + { + "timestamp": 3.091235165949911, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 2.72, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0218, + "sequence_id": 12 + } + }, + { + "timestamp": 3.3325049998238683, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 2.96, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.03, + "sequence_id": 13 + } + }, + { + "timestamp": 3.577521916013211, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 3.2, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0189, + "sequence_id": 14 + } + }, + { + "timestamp": 3.8645569998770952, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 3.44, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0118, + "sequence_id": 15 + } + }, + { + "timestamp": 4.106258499901742, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 3.68, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0089, + "sequence_id": 16 + } + }, + { + "timestamp": 4.346511875046417, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 3.92, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0073, + "sequence_id": 17 + } + }, + { + "timestamp": 4.589668208034709, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 4.16, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0053, + "sequence_id": 18 + } + }, + { + "timestamp": 4.826804416021332, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 4.4, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0034, + "sequence_id": 19 + } + }, + { + "timestamp": 5.060472874902189, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 4.64, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0024, + "sequence_id": 20 + } + }, + { + "timestamp": 5.304136332822964, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 4.88, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1091, + "sequence_id": 21 + } + }, + { + "timestamp": 5.544230999890715, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 5.12, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.1007, + "sequence_id": 22 + } + }, + { + "timestamp": 5.779906540876254, + "event_type": "TurnInfo", + "data": { + "type": "TurnInfo", + "request_id": "277cf8d3-27b0-439b-a04e-707598e13489", + "event": "Update", + "turn_index": 1, + "audio_window_start": 1.28, + "audio_window_end": 5.36, + "transcript": "", + "words": [], + "end_of_turn_confidence": 0.0565, + "sequence_id": 23 + } + } + ], + "transcript": "Yes." +} \ No newline at end of file diff --git a/evals/visualizer/.gitignore b/evals/visualizer/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/evals/visualizer/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/evals/visualizer/README.md b/evals/visualizer/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/evals/visualizer/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/evals/visualizer/eslint.config.mjs b/evals/visualizer/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/evals/visualizer/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/evals/visualizer/next.config.ts b/evals/visualizer/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/evals/visualizer/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/evals/visualizer/package.json b/evals/visualizer/package.json new file mode 100644 index 0000000..5d44c50 --- /dev/null +++ b/evals/visualizer/package.json @@ -0,0 +1,26 @@ +{ + "name": "visualizer", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "next": "16.1.4", + "react": "19.2.3", + "react-dom": "19.2.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.1.4", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/evals/visualizer/pnpm-lock.yaml b/evals/visualizer/pnpm-lock.yaml new file mode 100644 index 0000000..11a9e03 --- /dev/null +++ b/evals/visualizer/pnpm-lock.yaml @@ -0,0 +1,4008 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + next: + specifier: 16.1.4 + version: 16.1.4(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.18 + '@types/node': + specifier: ^20 + version: 20.19.30 + '@types/react': + specifier: ^19 + version: 19.2.8 + '@types/react-dom': + specifier: ^19 + version: 19.2.3(@types/react@19.2.8) + eslint: + specifier: ^9 + version: 9.39.2(jiti@2.6.1) + eslint-config-next: + specifier: 16.1.4 + version: 16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + tailwindcss: + specifier: ^4 + version: 4.1.18 + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.28.6': + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.6': + resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.6': + resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.6': + resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.6': + resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.6': + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@next/env@16.1.4': + resolution: {integrity: sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A==} + + '@next/eslint-plugin-next@16.1.4': + resolution: {integrity: sha512-38WMjGP8y+1MN4bcZFs+GTcBe0iem5GGTzFE5GWW/dWdRKde7LOXH3lQT2QuoquVWyfl2S0fQRchGmeacGZ4Wg==} + + '@next/swc-darwin-arm64@16.1.4': + resolution: {integrity: sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.1.4': + resolution: {integrity: sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.1.4': + resolution: {integrity: sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@16.1.4': + resolution: {integrity: sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@16.1.4': + resolution: {integrity: sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@16.1.4': + resolution: {integrity: sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@16.1.4': + resolution: {integrity: sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.1.4': + resolution: {integrity: sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.18': + resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.19.30': + resolution: {integrity: sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.8': + resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} + + '@typescript-eslint/eslint-plugin@8.53.1': + resolution: {integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.53.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.53.1': + resolution: {integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.53.1': + resolution: {integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.53.1': + resolution: {integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.53.1': + resolution: {integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.53.1': + resolution: {integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.53.1': + resolution: {integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.1': + resolution: {integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.1': + resolution: {integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.53.1': + resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.9.15: + resolution: {integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==} + hasBin: true + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001765: + resolution: {integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + engines: {node: '>=10.13.0'} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@16.1.4: + resolution: {integrity: sha512-iCrrNolUPpn/ythx0HcyNRfUBgTkaNBXByisKUbusPGCl8DMkDXXAu7exlSTSLGTIsH9lFE/c4s/3Qiyv2qwdA==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@16.1.4: + resolution: {integrity: sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.53.1: + resolution: {integrity: sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.5: + resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.28.6': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.6': {} + + '@babel/core@7.28.6': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.28.6 + '@babel/template': 7.28.6 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.6': + dependencies: + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.28.6 + + '@babel/parser@7.28.6': + dependencies: + '@babel/types': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + + '@babel/traverse@7.28.6': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.6 + '@babel/template': 7.28.6 + '@babel/types': 7.28.6 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@next/env@16.1.4': {} + + '@next/eslint-plugin-next@16.1.4': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@16.1.4': + optional: true + + '@next/swc-darwin-x64@16.1.4': + optional: true + + '@next/swc-linux-arm64-gnu@16.1.4': + optional: true + + '@next/swc-linux-arm64-musl@16.1.4': + optional: true + + '@next/swc-linux-x64-gnu@16.1.4': + optional: true + + '@next/swc-linux-x64-musl@16.1.4': + optional: true + + '@next/swc-win32-arm64-msvc@16.1.4': + optional: true + + '@next/swc-win32-x64-msvc@16.1.4': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@rtsao/scc@1.1.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.4 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/postcss@4.1.18': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + postcss: 8.5.6 + tailwindcss: 4.1.18 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.19.30': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.8)': + dependencies: + '@types/react': 19.2.8 + + '@types/react@19.2.8': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/type-utils': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.1 + eslint: 9.39.2(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.1 + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) + '@typescript-eslint/types': 8.53.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.1': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + + '@typescript-eslint/tsconfig-utils@8.53.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.1': {} + + '@typescript-eslint/typescript-estree@8.53.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.53.1': + dependencies: + '@typescript-eslint/types': 8.53.1 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.1: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.9.15: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.15 + caniuse-lite: 1.0.30001765 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001765: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + client-only@0.0.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.267: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.4: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.2: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.1.4(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.1.4 + eslint: 9.39.2(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) + globals: 16.4.0 + typescript-eslint: 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + get-tsconfig: 4.13.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.2(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.2(jiti@2.6.1) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)): + dependencies: + '@babel/core': 7.28.6 + '@babel/parser': 7.28.6 + eslint: 9.39.2(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 4.3.5 + zod-validation-error: 4.0.2(zod@4.3.5) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.2 + eslint: 9.39.2(jiti@2.6.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.3 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next@16.1.4(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@next/env': 16.1.4 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.9.15 + caniuse-lite: 1.0.30001765 + postcss: 8.4.31 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styled-jsx: 5.1.6(@babel/core@7.28.6)(react@19.2.3) + optionalDependencies: + '@next/swc-darwin-arm64': 16.1.4 + '@next/swc-darwin-x64': 16.1.4 + '@next/swc-linux-arm64-gnu': 16.1.4 + '@next/swc-linux-arm64-musl': 16.1.4 + '@next/swc-linux-x64-gnu': 16.1.4 + '@next/swc-linux-x64-musl': 16.1.4 + '@next/swc-win32-arm64-msvc': 16.1.4 + '@next/swc-win32-x64-msvc': 16.1.4 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-releases@2.0.27: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react@19.2.3: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(@babel/core@7.28.6)(react@19.2.3): + dependencies: + client-only: 0.0.1 + react: 19.2.3 + optionalDependencies: + '@babel/core': 7.28.6 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.3.5): + dependencies: + zod: 4.3.5 + + zod@4.3.5: {} diff --git a/evals/visualizer/pnpm-workspace.yaml b/evals/visualizer/pnpm-workspace.yaml new file mode 100644 index 0000000..4851e0c --- /dev/null +++ b/evals/visualizer/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - . +ignoredBuiltDependencies: + - sharp + - unrs-resolver diff --git a/evals/visualizer/postcss.config.mjs b/evals/visualizer/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/evals/visualizer/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/evals/visualizer/public/file.svg b/evals/visualizer/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/evals/visualizer/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/evals/visualizer/public/globe.svg b/evals/visualizer/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/evals/visualizer/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/evals/visualizer/public/next.svg b/evals/visualizer/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/evals/visualizer/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/evals/visualizer/public/vercel.svg b/evals/visualizer/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/evals/visualizer/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/evals/visualizer/public/window.svg b/evals/visualizer/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/evals/visualizer/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/evals/visualizer/src/app/api/audio/[filename]/route.ts b/evals/visualizer/src/app/api/audio/[filename]/route.ts new file mode 100644 index 0000000..e7dba65 --- /dev/null +++ b/evals/visualizer/src/app/api/audio/[filename]/route.ts @@ -0,0 +1,42 @@ +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; +import path from "path"; + +const AUDIO_DIR = path.join(process.cwd(), "..", "stt", "audio"); + +const MIME_TYPES: Record = { + ".mp3": "audio/mpeg", + ".wav": "audio/wav", + ".m4a": "audio/mp4", + ".ogg": "audio/ogg", + ".webm": "audio/webm", +}; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ filename: string }> } +) { + try { + const { filename } = await params; + const filePath = path.join(AUDIO_DIR, filename); + + if (!fs.existsSync(filePath)) { + return NextResponse.json({ error: "Audio file not found" }, { status: 404 }); + } + + const ext = path.extname(filename).toLowerCase(); + const contentType = MIME_TYPES[ext] || "application/octet-stream"; + + const fileBuffer = fs.readFileSync(filePath); + + return new NextResponse(fileBuffer, { + headers: { + "Content-Type": contentType, + "Content-Length": fileBuffer.length.toString(), + }, + }); + } catch (error) { + console.error("Error serving audio:", error); + return NextResponse.json({ error: "Failed to serve audio" }, { status: 500 }); + } +} diff --git a/evals/visualizer/src/app/api/results/[id]/route.ts b/evals/visualizer/src/app/api/results/[id]/route.ts new file mode 100644 index 0000000..08711f4 --- /dev/null +++ b/evals/visualizer/src/app/api/results/[id]/route.ts @@ -0,0 +1,27 @@ +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; +import path from "path"; + +const RESULTS_DIR = path.join(process.cwd(), "..", "stt", "results"); + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { id } = await params; + const filePath = path.join(RESULTS_DIR, `${id}.json`); + + if (!fs.existsSync(filePath)) { + return NextResponse.json({ error: "Result not found" }, { status: 404 }); + } + + const content = fs.readFileSync(filePath, "utf-8"); + const data = JSON.parse(content); + + return NextResponse.json(data); + } catch (error) { + console.error("Error reading result:", error); + return NextResponse.json({ error: "Failed to read result" }, { status: 500 }); + } +} diff --git a/evals/visualizer/src/app/api/results/route.ts b/evals/visualizer/src/app/api/results/route.ts new file mode 100644 index 0000000..08b2095 --- /dev/null +++ b/evals/visualizer/src/app/api/results/route.ts @@ -0,0 +1,47 @@ +import { NextResponse } from "next/server"; +import fs from "fs"; +import path from "path"; +import { ResultSummary, EventCaptureResult } from "@/types"; + +const RESULTS_DIR = path.join(process.cwd(), "..", "stt", "results"); + +export async function GET() { + try { + if (!fs.existsSync(RESULTS_DIR)) { + return NextResponse.json([]); + } + + const files = fs.readdirSync(RESULTS_DIR).filter((f) => f.endsWith(".json")); + const results: ResultSummary[] = []; + + for (const file of files) { + try { + const filePath = path.join(RESULTS_DIR, file); + const content = fs.readFileSync(filePath, "utf-8"); + const data: EventCaptureResult = JSON.parse(content); + + results.push({ + id: file.replace(".json", ""), + audio_file: data.audio_file, + provider: data.provider, + duration: data.duration, + created_at: data.created_at, + event_count: data.events.length, + }); + } catch { + console.error(`Failed to parse ${file}`); + } + } + + // Sort by created_at descending + results.sort( + (a, b) => + new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + ); + + return NextResponse.json(results); + } catch (error) { + console.error("Error reading results:", error); + return NextResponse.json({ error: "Failed to read results" }, { status: 500 }); + } +} diff --git a/evals/visualizer/src/app/favicon.ico b/evals/visualizer/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/evals/visualizer/src/app/globals.css b/evals/visualizer/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/evals/visualizer/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/evals/visualizer/src/app/layout.tsx b/evals/visualizer/src/app/layout.tsx new file mode 100644 index 0000000..fc37a19 --- /dev/null +++ b/evals/visualizer/src/app/layout.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "STT Event Visualizer", + description: "Visualize WebSocket events from STT providers", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/evals/visualizer/src/app/page.tsx b/evals/visualizer/src/app/page.tsx new file mode 100644 index 0000000..422c61d --- /dev/null +++ b/evals/visualizer/src/app/page.tsx @@ -0,0 +1,129 @@ +"use client"; + +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { ResultSummary } from "@/types"; + +function formatDuration(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, "0")}`; +} + +function formatDate(isoString: string): string { + const date = new Date(isoString); + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); +} + +const PROVIDER_COLORS: Record = { + deepgram: "bg-blue-500/20 text-blue-300", + "deepgram-flux": "bg-green-500/20 text-green-300", + speechmatics: "bg-purple-500/20 text-purple-300", +}; + +export default function Home() { + const [results, setResults] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + async function fetchResults() { + try { + const response = await fetch("/api/results"); + if (!response.ok) { + throw new Error("Failed to fetch results"); + } + const data = await response.json(); + setResults(data); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + } + } + + fetchResults(); + }, []); + + return ( +
+
+
+

STT Event Visualizer

+

+ Visualize captured WebSocket events from STT providers +

+
+ + {loading && ( +
+
+
+ )} + + {error && ( +
+ {error} +
+ )} + + {!loading && !error && results.length === 0 && ( +
+

No results found

+

+ Run the event capture script to generate results: +

+ + python -m evals.stt.event_capture audio/multi_speaker.m4a --provider deepgram + +
+ )} + + {!loading && !error && results.length > 0 && ( +
+ {results.map((result) => ( + +
+
+
+ {result.audio_file} + + {result.provider} + +
+
+ {formatDate(result.created_at)} +
+
+
+
+ {formatDuration(result.duration)} +
+
+ {result.event_count} events +
+
+
+ + ))} +
+ )} +
+
+ ); +} diff --git a/evals/visualizer/src/app/view/[id]/page.tsx b/evals/visualizer/src/app/view/[id]/page.tsx new file mode 100644 index 0000000..a217312 --- /dev/null +++ b/evals/visualizer/src/app/view/[id]/page.tsx @@ -0,0 +1,158 @@ +"use client"; + +import { useEffect, useState, useCallback } from "react"; +import { useParams } from "next/navigation"; +import Link from "next/link"; +import { EventCaptureResult } from "@/types"; +import AudioPlayer from "@/components/AudioPlayer"; +import EventTimeline from "@/components/EventTimeline"; +import EventList from "@/components/EventList"; + +const PROVIDER_COLORS: Record = { + deepgram: "bg-blue-500/20 text-blue-300", + "deepgram-flux": "bg-green-500/20 text-green-300", + speechmatics: "bg-purple-500/20 text-purple-300", +}; + +export default function ViewPage() { + const params = useParams(); + const id = params.id as string; + + const [result, setResult] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [currentTime, setCurrentTime] = useState(0); + const [isPlaying, setIsPlaying] = useState(false); + + useEffect(() => { + async function fetchResult() { + try { + const response = await fetch(`/api/results/${id}`); + if (!response.ok) { + if (response.status === 404) { + throw new Error("Result not found"); + } + throw new Error("Failed to fetch result"); + } + const data = await response.json(); + setResult(data); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + } + } + + if (id) { + fetchResult(); + } + }, [id]); + + const handleTimeUpdate = useCallback((time: number) => { + setCurrentTime(time); + }, []); + + const handlePlayingChange = useCallback((playing: boolean) => { + setIsPlaying(playing); + }, []); + + const handleSeek = useCallback((time: number) => { + setCurrentTime(time); + }, []); + + if (loading) { + return ( +
+
+
+ ); + } + + if (error) { + return ( +
+
+ + ← Back to results + +
{error}
+
+
+ ); + } + + if (!result) { + return null; + } + + const audioUrl = `/api/audio/${result.audio_file}`; + + return ( +
+
+ {/* Header */} +
+ + ← Back to results + +
+

{result.audio_file}

+ + {result.provider} + +
+ {result.transcript && ( +

+ {result.transcript} +

+ )} +
+ + {/* Main content */} +
+ {/* Left column: Audio player and timeline */} +
+ + + + + {/* Transcript section */} + {result.transcript && ( +
+
+ Final Transcript +
+

{result.transcript}

+
+ )} +
+ + {/* Right column: Event list */} +
+ +
+
+
+
+ ); +} diff --git a/evals/visualizer/src/components/AudioPlayer.tsx b/evals/visualizer/src/components/AudioPlayer.tsx new file mode 100644 index 0000000..e5795e5 --- /dev/null +++ b/evals/visualizer/src/components/AudioPlayer.tsx @@ -0,0 +1,145 @@ +"use client"; + +import { useRef, useEffect, useState, useCallback } from "react"; + +interface AudioPlayerProps { + audioUrl: string; + duration: number; + currentTime: number; + onTimeUpdate: (time: number) => void; + onPlayingChange: (playing: boolean) => void; +} + +function formatTime(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, "0")}`; +} + +export default function AudioPlayer({ + audioUrl, + duration, + currentTime, + onTimeUpdate, + onPlayingChange, +}: AudioPlayerProps) { + const audioRef = useRef(null); + const [isPlaying, setIsPlaying] = useState(false); + const [internalTime, setInternalTime] = useState(0); + + useEffect(() => { + const audio = audioRef.current; + if (!audio) return; + + const handleTimeUpdate = () => { + setInternalTime(audio.currentTime); + onTimeUpdate(audio.currentTime); + }; + + const handlePlay = () => { + setIsPlaying(true); + onPlayingChange(true); + }; + + const handlePause = () => { + setIsPlaying(false); + onPlayingChange(false); + }; + + const handleEnded = () => { + setIsPlaying(false); + onPlayingChange(false); + }; + + audio.addEventListener("timeupdate", handleTimeUpdate); + audio.addEventListener("play", handlePlay); + audio.addEventListener("pause", handlePause); + audio.addEventListener("ended", handleEnded); + + return () => { + audio.removeEventListener("timeupdate", handleTimeUpdate); + audio.removeEventListener("play", handlePlay); + audio.removeEventListener("pause", handlePause); + audio.removeEventListener("ended", handleEnded); + }; + }, [onTimeUpdate, onPlayingChange]); + + // Seek to currentTime when it changes externally + useEffect(() => { + const audio = audioRef.current; + if (!audio) return; + + // Only seek if the difference is significant (user clicked timeline) + if (Math.abs(audio.currentTime - currentTime) > 0.5) { + audio.currentTime = currentTime; + } + }, [currentTime]); + + const togglePlay = useCallback(() => { + const audio = audioRef.current; + if (!audio) return; + + if (isPlaying) { + audio.pause(); + } else { + audio.play(); + } + }, [isPlaying]); + + const handleSeek = useCallback((e: React.ChangeEvent) => { + const audio = audioRef.current; + if (!audio) return; + + const newTime = parseFloat(e.target.value); + audio.currentTime = newTime; + setInternalTime(newTime); + onTimeUpdate(newTime); + }, [onTimeUpdate]); + + return ( +
+
+ ); +} diff --git a/evals/visualizer/src/components/EventList.tsx b/evals/visualizer/src/components/EventList.tsx new file mode 100644 index 0000000..02a3f9e --- /dev/null +++ b/evals/visualizer/src/components/EventList.tsx @@ -0,0 +1,141 @@ +"use client"; + +import { useEffect, useRef, useMemo, useState } from "react"; +import { CapturedEvent } from "@/types"; +import { DeepgramEventItem, FluxEventItem, SpeechmaticsEventItem } from "./events"; + +interface EventListProps { + events: CapturedEvent[]; + currentTime: number; + onSeek: (time: number) => void; + provider: string; +} + +function formatTime(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + const ms = Math.floor((seconds % 1) * 100); + return `${mins}:${secs.toString().padStart(2, "0")}.${ms.toString().padStart(2, "0")}`; +} + +function getEventItemComponent(provider: string) { + if (provider === "deepgram-flux") { + return FluxEventItem; + } + if (provider === "speechmatics") { + return SpeechmaticsEventItem; + } + // Default to Deepgram Nova + return DeepgramEventItem; +} + +export default function EventList({ + events, + currentTime, + onSeek, + provider, +}: EventListProps) { + const containerRef = useRef(null); + const [expandedEvents, setExpandedEvents] = useState>(new Set()); + const [autoScroll, setAutoScroll] = useState(true); + + const EventItemComponent = getEventItemComponent(provider); + + // Find the current event index based on time + const currentEventIndex = useMemo(() => { + for (let i = events.length - 1; i >= 0; i--) { + if (events[i].timestamp <= currentTime) { + return i; + } + } + return -1; + }, [events, currentTime]); + + // Auto-scroll to current event + useEffect(() => { + if (!autoScroll || currentEventIndex < 0) return; + + const container = containerRef.current; + if (!container) return; + + const eventElement = container.querySelector(`[data-index="${currentEventIndex}"]`); + if (eventElement) { + eventElement.scrollIntoView({ behavior: "smooth", block: "center" }); + } + }, [currentEventIndex, autoScroll]); + + const toggleExpand = (index: number) => { + setExpandedEvents((prev) => { + const next = new Set(prev); + if (next.has(index)) { + next.delete(index); + } else { + next.add(index); + } + return next; + }); + }; + + return ( +
+
+
+ Events ({events.length}) +
+ +
+ +
+ {events.map((event, index) => { + const isCurrent = index === currentEventIndex; + const isExpanded = expandedEvents.has(index); + + return ( +
onSeek(event.timestamp)} + > +
+ {/* Current indicator */} +
+ {isCurrent ? ( +
+ ) : ( +
+ )} +
+ + {/* Timestamp */} + + {formatTime(event.timestamp)} + + + {/* Provider-specific event item */} + toggleExpand(index)} + /> +
+
+ ); + })} +
+
+ ); +} diff --git a/evals/visualizer/src/components/EventTimeline.tsx b/evals/visualizer/src/components/EventTimeline.tsx new file mode 100644 index 0000000..2efa8f2 --- /dev/null +++ b/evals/visualizer/src/components/EventTimeline.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { useMemo } from "react"; +import { CapturedEvent } from "@/types"; + +interface EventTimelineProps { + events: CapturedEvent[]; + duration: number; + currentTime: number; + onSeek: (time: number) => void; +} + +const EVENT_COLORS: Record = { + Results: "bg-blue-500", + TurnInfo: "bg-green-500", + AddTranscript: "bg-purple-500", + Connected: "bg-yellow-500", + RecognitionStarted: "bg-yellow-500", + EndOfTranscript: "bg-red-500", + Metadata: "bg-gray-500", + Error: "bg-red-600", + default: "bg-zinc-400", +}; + +function formatTime(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, "0")}`; +} + +export default function EventTimeline({ + events, + duration, + currentTime, + onSeek, +}: EventTimelineProps) { + const timeMarkers = useMemo(() => { + const markers: number[] = []; + const interval = Math.ceil(duration / 6); + for (let i = 0; i <= duration; i += interval) { + markers.push(i); + } + if (markers[markers.length - 1] !== Math.floor(duration)) { + markers.push(Math.floor(duration)); + } + return markers; + }, [duration]); + + const handleClick = (e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const x = e.clientX - rect.left; + const percent = x / rect.width; + const time = percent * duration; + onSeek(Math.max(0, Math.min(time, duration))); + }; + + const progressPercent = (currentTime / duration) * 100; + + return ( +
+
Event Timeline
+ +
+ {/* Progress indicator */} +
+ + {/* Current time indicator */} +
+ + {/* Event markers */} +
+ {events.map((event, index) => { + const leftPercent = Math.min((event.timestamp / duration) * 100, 100); + const colorClass = + EVENT_COLORS[event.event_type] || EVENT_COLORS.default; + + return ( +
+ ); + })} +
+
+ + {/* Time markers */} +
+ {timeMarkers.map((time, index) => ( + {formatTime(time)} + ))} +
+ + {/* Legend */} +
+ {Object.entries(EVENT_COLORS) + .filter(([key]) => key !== "default") + .slice(0, 6) + .map(([eventType, colorClass]) => ( +
+
+ {eventType} +
+ ))} +
+
+ ); +} diff --git a/evals/visualizer/src/components/events/DeepgramEventItem.tsx b/evals/visualizer/src/components/events/DeepgramEventItem.tsx new file mode 100644 index 0000000..7419658 --- /dev/null +++ b/evals/visualizer/src/components/events/DeepgramEventItem.tsx @@ -0,0 +1,98 @@ +"use client"; + +import { CapturedEvent } from "@/types"; + +interface DeepgramEventItemProps { + event: CapturedEvent; + isExpanded: boolean; + onToggleExpand: () => void; +} + +const EVENT_COLORS: Record = { + Results: "text-blue-400 bg-blue-500/10", + SpeechStarted: "text-yellow-400 bg-yellow-500/10", + Metadata: "text-gray-400 bg-gray-500/10", + UtteranceEnd: "text-red-500 bg-red-600/10", + default: "text-zinc-400 bg-zinc-500/10", +}; + +function getTranscript(event: CapturedEvent): string { + const data = event.data; + const channel = data.channel as Record | undefined; + if (channel) { + const alternatives = channel.alternatives as Array<{ transcript?: string }> | undefined; + if (alternatives?.[0]?.transcript) { + return alternatives[0].transcript; + } + } + return ""; +} + +export default function DeepgramEventItem({ + event, + isExpanded, + onToggleExpand, +}: DeepgramEventItemProps) { + const colorClass = EVENT_COLORS[event.event_type] || EVENT_COLORS.default; + const data = event.data; + + const transcript = getTranscript(event); + const isFinal = data.is_final as boolean | undefined; + const speechFinal = data.speech_final as boolean | undefined; + + // For non-Results events + const isConnection = event.event_type === "Connected"; + const isMetadata = event.event_type === "Metadata"; + + return ( +
+
+ + {event.event_type} + + + {/* Final/Partial indicator for Results */} + {isFinal !== undefined && ( + + {isFinal ? "Final" : "Partial"} + + )} + + {/* Speech Final indicator */} + {speechFinal && ( + + Speech Final + + )} +
+ + {/* Transcript or status message */} +
+ {transcript} +
+ + {/* Expand/collapse button */} + + + {/* Expanded JSON view */} + {isExpanded && ( +
+          {JSON.stringify(event.data, null, 2)}
+        
+ )} +
+ ); +} diff --git a/evals/visualizer/src/components/events/FluxEventItem.tsx b/evals/visualizer/src/components/events/FluxEventItem.tsx new file mode 100644 index 0000000..6e85e5f --- /dev/null +++ b/evals/visualizer/src/components/events/FluxEventItem.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { CapturedEvent } from "@/types"; + +interface FluxEventItemProps { + event: CapturedEvent; + isExpanded: boolean; + onToggleExpand: () => void; +} + +const EVENT_COLORS: Record = { + TurnInfo: "text-green-400 bg-green-500/10", + Connected: "text-yellow-400 bg-yellow-500/10", + Error: "text-red-500 bg-red-600/10", + default: "text-zinc-400 bg-zinc-500/10", +}; + +const FLUX_EVENT_COLORS: Record = { + Update: "text-amber-300 bg-amber-500/20", + EndOfTurn: "text-emerald-300 bg-emerald-500/20", + EagerEndOfTurn: "text-cyan-300 bg-cyan-500/20", + StartOfTurn: "text-blue-300 bg-blue-500/20", + TurnResumed: "text-purple-300 bg-purple-500/20", + default: "text-zinc-300 bg-zinc-500/20", +}; + +export default function FluxEventItem({ + event, + isExpanded, + onToggleExpand, +}: FluxEventItemProps) { + const colorClass = EVENT_COLORS[event.event_type] || EVENT_COLORS.default; + const data = event.data; + + // Flux TurnInfo fields + const fluxEvent = data.event as string | undefined; + const transcript = data.transcript as string | undefined; + const endOfTurnConfidence = data.end_of_turn_confidence as number | undefined; + const turnIndex = data.turn_index as number | undefined; + + const isFinal = fluxEvent === "EndOfTurn"; + const fluxEventColor = fluxEvent + ? FLUX_EVENT_COLORS[fluxEvent] || FLUX_EVENT_COLORS.default + : ""; + + // For non-TurnInfo events + const isConnection = event.event_type === "Connected"; + + return ( +
+
+ + {event.event_type} + + + {/* Flux sub-event type */} + {fluxEvent && ( + + {fluxEvent} + + )} + + {/* Final/Partial indicator */} + {fluxEvent && ( + + {isFinal ? "Final" : "Partial"} + + )} + + {/* Turn index */} + {turnIndex !== undefined && ( + + Turn {turnIndex} + + )} + + {/* EOT confidence */} + {endOfTurnConfidence !== undefined && ( + + EOT: {(endOfTurnConfidence * 100).toFixed(1)}% + + )} +
+ + {/* Transcript or status message */} +
+ {transcript || (isConnection ? "[Connected]" : `[${fluxEvent || event.event_type}]`)} +
+ + {/* Expand/collapse button */} + + + {/* Expanded JSON view */} + {isExpanded && ( +
+          {JSON.stringify(event.data, null, 2)}
+        
+ )} +
+ ); +} diff --git a/evals/visualizer/src/components/events/SpeechmaticsEventItem.tsx b/evals/visualizer/src/components/events/SpeechmaticsEventItem.tsx new file mode 100644 index 0000000..e6411d3 --- /dev/null +++ b/evals/visualizer/src/components/events/SpeechmaticsEventItem.tsx @@ -0,0 +1,101 @@ +"use client"; + +import { CapturedEvent } from "@/types"; + +interface SpeechmaticsEventItemProps { + event: CapturedEvent; + isExpanded: boolean; + onToggleExpand: () => void; +} + +const EVENT_COLORS: Record = { + AddTranscript: "text-purple-400 bg-purple-500/10", + RecognitionStarted: "text-yellow-400 bg-yellow-500/10", + EndOfTranscript: "text-red-400 bg-red-500/10", + Warning: "text-orange-400 bg-orange-500/10", + Error: "text-red-500 bg-red-600/10", + default: "text-zinc-400 bg-zinc-500/10", +}; + +function getTranscript(event: CapturedEvent): string { + const data = event.data; + const results = data.results as Array<{ + type?: string; + alternatives?: Array<{ content?: string }>; + }> | undefined; + + if (results) { + const words = results + .filter((r) => r.type === "word" && r.alternatives?.[0]?.content) + .map((r) => r.alternatives![0].content) + .join(" "); + return words; + } + return ""; +} + +export default function SpeechmaticsEventItem({ + event, + isExpanded, + onToggleExpand, +}: SpeechmaticsEventItemProps) { + const colorClass = EVENT_COLORS[event.event_type] || EVENT_COLORS.default; + const data = event.data; + + const transcript = getTranscript(event); + + // Status events + const isRecognitionStarted = event.event_type === "RecognitionStarted"; + const isEndOfTranscript = event.event_type === "EndOfTranscript"; + const isWarning = event.event_type === "Warning"; + + // Warning reason + const warningReason = isWarning ? (data.reason as string | undefined) : undefined; + + return ( +
+
+ + {event.event_type} + + + {/* AddTranscript is always final in Speechmatics */} + {event.event_type === "AddTranscript" && ( + + Final + + )} +
+ + {/* Transcript or status message */} +
+ {transcript || + (isRecognitionStarted + ? "[Recognition Started]" + : isEndOfTranscript + ? "[End of Transcript]" + : isWarning + ? `[Warning: ${warningReason || "unknown"}]` + : `[${event.event_type}]`)} +
+ + {/* Expand/collapse button */} + + + {/* Expanded JSON view */} + {isExpanded && ( +
+          {JSON.stringify(event.data, null, 2)}
+        
+ )} +
+ ); +} diff --git a/evals/visualizer/src/components/events/index.ts b/evals/visualizer/src/components/events/index.ts new file mode 100644 index 0000000..e436198 --- /dev/null +++ b/evals/visualizer/src/components/events/index.ts @@ -0,0 +1,3 @@ +export { default as DeepgramEventItem } from "./DeepgramEventItem"; +export { default as FluxEventItem } from "./FluxEventItem"; +export { default as SpeechmaticsEventItem } from "./SpeechmaticsEventItem"; diff --git a/evals/visualizer/src/types/index.ts b/evals/visualizer/src/types/index.ts new file mode 100644 index 0000000..787e89d --- /dev/null +++ b/evals/visualizer/src/types/index.ts @@ -0,0 +1,24 @@ +export interface CapturedEvent { + timestamp: number; + event_type: string; + data: Record; +} + +export interface EventCaptureResult { + audio_file: string; + audio_path: string; + provider: string; + duration: number; + created_at: string; + events: CapturedEvent[]; + transcript: string; +} + +export interface ResultSummary { + id: string; + audio_file: string; + provider: string; + duration: number; + created_at: string; + event_count: number; +} diff --git a/evals/visualizer/tsconfig.json b/evals/visualizer/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/evals/visualizer/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/pipecat b/pipecat index a1d3062..f11fad8 160000 --- a/pipecat +++ b/pipecat @@ -1 +1 @@ -Subproject commit a1d3062446240b6b27ebc787d28578e4561e7441 +Subproject commit f11fad8f3e90e06b1625b9dc49c13e26f3c9e716 diff --git a/release-please-config.json b/release-please-config.json index 1e07b35..83acde6 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -16,6 +16,11 @@ "type": "json", "path": "ui/package.json", "jsonpath": "$.version" + }, + { + "type": "toml", + "path": "api/pyproject.toml", + "key": "project.version" } ] } diff --git a/ui/src/app/after-sign-in/page.tsx b/ui/src/app/after-sign-in/page.tsx index 21f0ea8..39796e9 100644 --- a/ui/src/app/after-sign-in/page.tsx +++ b/ui/src/app/after-sign-in/page.tsx @@ -1,6 +1,6 @@ import { redirect } from "next/navigation"; -import { getWorkflowsApiV1WorkflowFetchGet } from "@/client/sdk.gen"; +import { getWorkflowCountApiV1WorkflowCountGet } from "@/client/sdk.gen"; import { getServerAccessToken,getServerAuthProvider, getServerUser } from "@/lib/auth/server"; import logger from '@/lib/logger'; import { getRedirectUrl } from "@/lib/utils"; @@ -34,21 +34,18 @@ export default async function AfterSignInPage() { try { const accessToken = await getServerAccessToken(); if (accessToken) { - const workflowsResponse = await getWorkflowsApiV1WorkflowFetchGet({ + const countResponse = await getWorkflowCountApiV1WorkflowCountGet({ headers: { Authorization: `Bearer ${accessToken}`, }, }); - const workflows = workflowsResponse.data ? (Array.isArray(workflowsResponse.data) ? workflowsResponse.data : [workflowsResponse.data]) : []; - const activeWorkflows = workflows.filter(w => w.status === 'active'); - logger.debug('[AfterSignInPage] Found workflows:', { - total: workflows.length, - active: activeWorkflows.length + total: countResponse.data?.total, + active: countResponse.data?.active }); - if (activeWorkflows.length > 0) { + if (countResponse.data && countResponse.data.active > 0) { logger.debug('[AfterSignInPage] Redirecting to /workflow - user has workflows'); redirect('/workflow'); } else { diff --git a/ui/src/app/api/config/version/route.ts b/ui/src/app/api/config/version/route.ts new file mode 100644 index 0000000..241085f --- /dev/null +++ b/ui/src/app/api/config/version/route.ts @@ -0,0 +1,33 @@ +import { NextResponse } from "next/server"; + +import { healthApiV1HealthGet } from "@/client/sdk.gen"; +import type { HealthResponse } from "@/client/types.gen"; + +// Import version from package.json at build time +import packageJson from "../../../../../package.json"; + +export async function GET() { + const uiVersion = packageJson.version || "dev"; + + // Fetch backend version and config from health endpoint + let apiVersion = "unknown"; + let backendApiEndpoint: string | null = null; + + try { + const response = await healthApiV1HealthGet(); + if (response.data) { + const data = response.data as HealthResponse; + apiVersion = data.version; + backendApiEndpoint = data.backend_api_endpoint; + } + } catch { + // Backend might not be reachable during build or in some deployments + apiVersion = "unavailable"; + } + + return NextResponse.json({ + ui: uiVersion, + api: apiVersion, + backendApiEndpoint, + }); +} diff --git a/ui/src/app/layout.tsx b/ui/src/app/layout.tsx index b0a4ced..060fdd3 100644 --- a/ui/src/app/layout.tsx +++ b/ui/src/app/layout.tsx @@ -9,6 +9,7 @@ import AppLayout from "@/components/layout/AppLayout"; import PostHogIdentify from "@/components/PostHogIdentify"; import SpinLoader from "@/components/SpinLoader"; import { Toaster } from "@/components/ui/sonner"; +import { AppConfigProvider } from "@/context/AppConfigContext"; import { OnboardingProvider } from "@/context/OnboardingContext"; import { UserConfigProvider } from "@/context/UserConfigContext"; import { AuthProvider } from "@/lib/auth"; @@ -59,18 +60,20 @@ export default function RootLayout({ - }> - - - - - {children} - - - - - - + + }> + + + + + {children} + + + + + + + diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index ae6e151..60a175f 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -1,7 +1,7 @@ import { isNextRouterError } from "next/dist/client/components/is-next-router-error"; import { redirect } from "next/navigation"; -import { getWorkflowsApiV1WorkflowFetchGet } from "@/client/sdk.gen"; +import { getWorkflowCountApiV1WorkflowCountGet } from "@/client/sdk.gen"; import SignInClient from "@/components/SignInClient"; import { getServerAccessToken,getServerAuthProvider,getServerUser } from "@/lib/auth/server"; import logger from '@/lib/logger'; @@ -21,21 +21,18 @@ export default async function Home() { try { const accessToken = await getServerAccessToken(); if (accessToken) { - const workflowsResponse = await getWorkflowsApiV1WorkflowFetchGet({ + const countResponse = await getWorkflowCountApiV1WorkflowCountGet({ headers: { Authorization: `Bearer ${accessToken}`, }, }); - const workflows = workflowsResponse.data ? (Array.isArray(workflowsResponse.data) ? workflowsResponse.data : [workflowsResponse.data]) : []; - const activeWorkflows = workflows.filter(w => w.status === 'active'); - logger.debug('[HomePage] Found workflows for local provider:', { - total: workflows.length, - active: activeWorkflows.length + total: countResponse.data?.total, + active: countResponse.data?.active }); - if (activeWorkflows.length > 0) { + if (countResponse.data && countResponse.data.active > 0) { logger.debug('[HomePage] Redirecting to /workflow - user has workflows'); redirect('/workflow'); } else { diff --git a/ui/src/app/usage/page.tsx b/ui/src/app/usage/page.tsx index 688b47b..d38b7ce 100644 --- a/ui/src/app/usage/page.tsx +++ b/ui/src/app/usage/page.tsx @@ -326,14 +326,64 @@ export default function UsagePage() { isDisabled={savingTimezone || userConfigLoading} placeholder={userConfigLoading ? "Loading..." : "Select timezone"} styles={{ - control: (base) => ({ + control: (base, state) => ({ ...base, minHeight: '36px', fontSize: '14px', + backgroundColor: 'var(--background)', + borderColor: state.isFocused ? 'var(--ring)' : 'var(--border)', + boxShadow: state.isFocused ? '0 0 0 2px color-mix(in srgb, var(--ring) 20%, transparent)' : 'none', + '&:hover': { + borderColor: 'var(--border)', + }, }), menu: (base) => ({ ...base, zIndex: 9999, + backgroundColor: 'var(--popover)', + border: '1px solid var(--border)', + boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', + }), + menuList: (base) => ({ + ...base, + backgroundColor: 'var(--popover)', + padding: 0, + }), + option: (base, state) => ({ + ...base, + backgroundColor: state.isSelected + ? 'var(--accent)' + : state.isFocused + ? 'var(--accent)' + : 'var(--popover)', + color: 'var(--foreground)', + cursor: 'pointer', + '&:active': { + backgroundColor: 'var(--accent)', + }, + }), + singleValue: (base) => ({ + ...base, + color: 'var(--foreground)', + }), + input: (base) => ({ + ...base, + color: 'var(--foreground)', + }), + placeholder: (base) => ({ + ...base, + color: 'var(--muted-foreground)', + }), + indicatorSeparator: (base) => ({ + ...base, + backgroundColor: 'var(--border)', + }), + dropdownIndicator: (base) => ({ + ...base, + color: 'var(--muted-foreground)', + '&:hover': { + color: 'var(--foreground)', + }, }), }} /> diff --git a/ui/src/client/sdk.gen.ts b/ui/src/client/sdk.gen.ts index c925c76..57a9786 100644 --- a/ui/src/client/sdk.gen.ts +++ b/ui/src/client/sdk.gen.ts @@ -3,7 +3,7 @@ import type { Client,Options as ClientOptions, TDataShape } from '@hey-api/client-fetch'; import { client as _heyApiClient } from './client.gen'; -import type { ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteData, ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteError, ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteResponse, ArchiveServiceKeyApiV1UserServiceKeysServiceKeyIdDeleteData, ArchiveServiceKeyApiV1UserServiceKeysServiceKeyIdDeleteError, CreateApiKeyApiV1UserApiKeysPostData, CreateApiKeyApiV1UserApiKeysPostError, CreateApiKeyApiV1UserApiKeysPostResponse, CreateCampaignApiV1CampaignCreatePostData, CreateCampaignApiV1CampaignCreatePostError, CreateCampaignApiV1CampaignCreatePostResponse, CreateCredentialApiV1CredentialsPostData, CreateCredentialApiV1CredentialsPostError, CreateCredentialApiV1CredentialsPostResponse, CreateLoadTestApiV1LooptalkLoadTestsPostData, CreateLoadTestApiV1LooptalkLoadTestsPostError, CreateLoadTestApiV1LooptalkLoadTestsPostResponse, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostData, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostError, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostResponse, CreateServiceKeyApiV1UserServiceKeysPostData, CreateServiceKeyApiV1UserServiceKeysPostError, CreateServiceKeyApiV1UserServiceKeysPostResponse, CreateSessionApiV1IntegrationSessionPostData, CreateSessionApiV1IntegrationSessionPostError, CreateSessionApiV1IntegrationSessionPostResponse, CreateTestSessionApiV1LooptalkTestSessionsPostData, CreateTestSessionApiV1LooptalkTestSessionsPostError, CreateTestSessionApiV1LooptalkTestSessionsPostResponse, CreateToolApiV1ToolsPostData, CreateToolApiV1ToolsPostError, CreateToolApiV1ToolsPostResponse, CreateWorkflowApiV1WorkflowCreateDefinitionPostData, CreateWorkflowApiV1WorkflowCreateDefinitionPostError, CreateWorkflowApiV1WorkflowCreateDefinitionPostResponse, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostData, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostError, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponse, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostData, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostError, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostResponse, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteData, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteError, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteResponse, DeleteCredentialApiV1CredentialsCredentialUuidDeleteData, DeleteCredentialApiV1CredentialsCredentialUuidDeleteError, DeleteCredentialApiV1CredentialsCredentialUuidDeleteResponse, DeleteDocumentApiV1KnowledgeBaseDocumentsDocumentUuidDeleteData, DeleteDocumentApiV1KnowledgeBaseDocumentsDocumentUuidDeleteError, DeleteToolApiV1ToolsToolUuidDeleteData, DeleteToolApiV1ToolsToolUuidDeleteError, DeleteToolApiV1ToolsToolUuidDeleteResponse, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostData, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostError, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostResponse, GetActiveTestsApiV1LooptalkActiveTestsGetData, GetActiveTestsApiV1LooptalkActiveTestsGetError, GetApiKeysApiV1UserApiKeysGetData, GetApiKeysApiV1UserApiKeysGetError, GetApiKeysApiV1UserApiKeysGetResponse, GetAuthUserApiV1UserAuthUserGetData, GetAuthUserApiV1UserAuthUserGetError, GetAuthUserApiV1UserAuthUserGetResponse, GetCampaignApiV1CampaignCampaignIdGetData, GetCampaignApiV1CampaignCampaignIdGetError, GetCampaignApiV1CampaignCampaignIdGetResponse, GetCampaignProgressApiV1CampaignCampaignIdProgressGetData, GetCampaignProgressApiV1CampaignCampaignIdProgressGetError, GetCampaignProgressApiV1CampaignCampaignIdProgressGetResponse, GetCampaignRunsApiV1CampaignCampaignIdRunsGetData, GetCampaignRunsApiV1CampaignCampaignIdRunsGetError, GetCampaignRunsApiV1CampaignCampaignIdRunsGetResponse, GetCampaignsApiV1CampaignGetData, GetCampaignsApiV1CampaignGetError, GetCampaignsApiV1CampaignGetResponse, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetData, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetError, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetResponse, GetCredentialApiV1CredentialsCredentialUuidGetData, GetCredentialApiV1CredentialsCredentialUuidGetError, GetCredentialApiV1CredentialsCredentialUuidGetResponse, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetData, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetError, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetResponse, GetDailyReportApiV1OrganizationsReportsDailyGetData, GetDailyReportApiV1OrganizationsReportsDailyGetError, GetDailyReportApiV1OrganizationsReportsDailyGetResponse, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetData, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetError, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetResponse, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetData, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetError, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetResponse, GetDefaultConfigurationsApiV1UserConfigurationsDefaultsGetData, GetDefaultConfigurationsApiV1UserConfigurationsDefaultsGetResponse, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetData, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetError, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetResponse, GetEmbedConfigApiV1PublicEmbedConfigTokenGetData, GetEmbedConfigApiV1PublicEmbedConfigTokenGetError, GetEmbedConfigApiV1PublicEmbedConfigTokenGetResponse, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetData, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetError, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetResponse, GetFileMetadataApiV1S3FileMetadataGetData, GetFileMetadataApiV1S3FileMetadataGetError, GetFileMetadataApiV1S3FileMetadataGetResponse, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetData, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetError, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetResponse, GetIntegrationsApiV1IntegrationGetData, GetIntegrationsApiV1IntegrationGetError, GetIntegrationsApiV1IntegrationGetResponse, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetData, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetError, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetResponse, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostData, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostError, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostResponse, GetServiceKeysApiV1UserServiceKeysGetData, GetServiceKeysApiV1UserServiceKeysGetError, GetServiceKeysApiV1UserServiceKeysGetResponse, GetSignedUrlApiV1S3SignedUrlGetData, GetSignedUrlApiV1S3SignedUrlGetError, GetSignedUrlApiV1S3SignedUrlGetResponse, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetData, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetError, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetResponse, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetData, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetError, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetResponse, GetTestSessionConversationApiV1LooptalkTestSessionsTestSessionIdConversationGetData, GetTestSessionConversationApiV1LooptalkTestSessionsTestSessionIdConversationGetError, GetToolApiV1ToolsToolUuidGetData, GetToolApiV1ToolsToolUuidGetError, GetToolApiV1ToolsToolUuidGetResponse, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostData, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostError, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostResponse, GetUsageHistoryApiV1OrganizationsUsageRunsGetData, GetUsageHistoryApiV1OrganizationsUsageRunsGetError, GetUsageHistoryApiV1OrganizationsUsageRunsGetResponse, GetUserConfigurationsApiV1UserConfigurationsUserGetData, GetUserConfigurationsApiV1UserConfigurationsUserGetError, GetUserConfigurationsApiV1UserConfigurationsUserGetResponse, GetVoicesApiV1UserConfigurationsVoicesProviderGetData, GetVoicesApiV1UserConfigurationsVoicesProviderGetError, GetVoicesApiV1UserConfigurationsVoicesProviderGetResponse, GetWorkflowApiV1WorkflowFetchWorkflowIdGetData, GetWorkflowApiV1WorkflowFetchWorkflowIdGetError, GetWorkflowApiV1WorkflowFetchWorkflowIdGetResponse, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetData, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetError, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetResponse, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetData, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetError, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetResponse, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetData, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetError, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponse, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetData, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetError, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetResponse, GetWorkflowsApiV1WorkflowFetchGetData, GetWorkflowsApiV1WorkflowFetchGetError, GetWorkflowsApiV1WorkflowFetchGetResponse, GetWorkflowsSummaryApiV1WorkflowSummaryGetData, GetWorkflowsSummaryApiV1WorkflowSummaryGetError, GetWorkflowsSummaryApiV1WorkflowSummaryGetResponse, GetWorkflowTemplatesApiV1WorkflowTemplatesGetData, GetWorkflowTemplatesApiV1WorkflowTemplatesGetResponse, HandleCloudonixStatusCallbackApiV1TelephonyCloudonixStatusCallbackWorkflowRunIdPostData, HandleCloudonixStatusCallbackApiV1TelephonyCloudonixStatusCallbackWorkflowRunIdPostError, HandleInboundFallbackApiV1TelephonyInboundFallbackPostData, HandleInboundTelephonyApiV1TelephonyInboundWorkflowIdPostData, HandleInboundTelephonyApiV1TelephonyInboundWorkflowIdPostError, HandleTwilioStatusCallbackApiV1TelephonyTwilioStatusCallbackWorkflowRunIdPostData, HandleTwilioStatusCallbackApiV1TelephonyTwilioStatusCallbackWorkflowRunIdPostError, HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostData, HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostError, HandleVobizHangupCallbackByWorkflowApiV1TelephonyVobizHangupCallbackWorkflowWorkflowIdPostData, HandleVobizHangupCallbackByWorkflowApiV1TelephonyVobizHangupCallbackWorkflowWorkflowIdPostError, HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostData, HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostError, HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostData, HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostError, HealthApiV1HealthGetData,ImpersonateApiV1SuperuserImpersonatePostData, ImpersonateApiV1SuperuserImpersonatePostError, ImpersonateApiV1SuperuserImpersonatePostResponse, InitializeEmbedSessionApiV1PublicEmbedInitPostData, InitializeEmbedSessionApiV1PublicEmbedInitPostError, InitializeEmbedSessionApiV1PublicEmbedInitPostResponse, InitiateCallApiV1PublicAgentUuidPostData, InitiateCallApiV1PublicAgentUuidPostError, InitiateCallApiV1PublicAgentUuidPostResponse, InitiateCallApiV1TelephonyInitiateCallPostData, InitiateCallApiV1TelephonyInitiateCallPostError, ListCredentialsApiV1CredentialsGetData, ListCredentialsApiV1CredentialsGetError, ListCredentialsApiV1CredentialsGetResponse, ListDocumentsApiV1KnowledgeBaseDocumentsGetData, ListDocumentsApiV1KnowledgeBaseDocumentsGetError, ListDocumentsApiV1KnowledgeBaseDocumentsGetResponse, ListTestSessionsApiV1LooptalkTestSessionsGetData, ListTestSessionsApiV1LooptalkTestSessionsGetError, ListTestSessionsApiV1LooptalkTestSessionsGetResponse, ListToolsApiV1ToolsGetData, ListToolsApiV1ToolsGetError, ListToolsApiV1ToolsGetResponse, OptionsConfigApiV1PublicEmbedConfigTokenOptionsData, OptionsConfigApiV1PublicEmbedConfigTokenOptionsError, OptionsInitApiV1PublicEmbedInitOptionsData, PauseCampaignApiV1CampaignCampaignIdPausePostData, PauseCampaignApiV1CampaignCampaignIdPausePostError, PauseCampaignApiV1CampaignCampaignIdPausePostResponse, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostData, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostError, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostResponse, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutData, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutError, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutResponse, ReactivateServiceKeyApiV1UserServiceKeysServiceKeyIdReactivatePutData, ReactivateServiceKeyApiV1UserServiceKeysServiceKeyIdReactivatePutError, ResumeCampaignApiV1CampaignCampaignIdResumePostData, ResumeCampaignApiV1CampaignCampaignIdResumePostError, ResumeCampaignApiV1CampaignCampaignIdResumePostResponse, SaveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPostData, SaveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPostError, SearchChunksApiV1KnowledgeBaseSearchPostData, SearchChunksApiV1KnowledgeBaseSearchPostError, SearchChunksApiV1KnowledgeBaseSearchPostResponse, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostData, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostError, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponse, StartCampaignApiV1CampaignCampaignIdStartPostData, StartCampaignApiV1CampaignCampaignIdStartPostError, StartCampaignApiV1CampaignCampaignIdStartPostResponse, StartTestSessionApiV1LooptalkTestSessionsTestSessionIdStartPostData, StartTestSessionApiV1LooptalkTestSessionsTestSessionIdStartPostError, StopTestSessionApiV1LooptalkTestSessionsTestSessionIdStopPostData, StopTestSessionApiV1LooptalkTestSessionsTestSessionIdStopPostError, UnarchiveToolApiV1ToolsToolUuidUnarchivePostData, UnarchiveToolApiV1ToolsToolUuidUnarchivePostError, UnarchiveToolApiV1ToolsToolUuidUnarchivePostResponse, UpdateCredentialApiV1CredentialsCredentialUuidPutData, UpdateCredentialApiV1CredentialsCredentialUuidPutError, UpdateCredentialApiV1CredentialsCredentialUuidPutResponse, UpdateIntegrationApiV1IntegrationIntegrationIdPutData, UpdateIntegrationApiV1IntegrationIntegrationIdPutError, UpdateIntegrationApiV1IntegrationIntegrationIdPutResponse, UpdateToolApiV1ToolsToolUuidPutData, UpdateToolApiV1ToolsToolUuidPutError, UpdateToolApiV1ToolsToolUuidPutResponse, UpdateUserConfigurationsApiV1UserConfigurationsUserPutData, UpdateUserConfigurationsApiV1UserConfigurationsUserPutError, UpdateUserConfigurationsApiV1UserConfigurationsUserPutResponse, UpdateWorkflowApiV1WorkflowWorkflowIdPutData, UpdateWorkflowApiV1WorkflowWorkflowIdPutError, UpdateWorkflowApiV1WorkflowWorkflowIdPutResponse, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutData, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutError, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutResponse, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetData, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetError, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetResponse, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostData, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostError, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostResponse } from './types.gen'; +import type { ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteData, ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteError, ArchiveApiKeyApiV1UserApiKeysApiKeyIdDeleteResponse, ArchiveServiceKeyApiV1UserServiceKeysServiceKeyIdDeleteData, ArchiveServiceKeyApiV1UserServiceKeysServiceKeyIdDeleteError, CreateApiKeyApiV1UserApiKeysPostData, CreateApiKeyApiV1UserApiKeysPostError, CreateApiKeyApiV1UserApiKeysPostResponse, CreateCampaignApiV1CampaignCreatePostData, CreateCampaignApiV1CampaignCreatePostError, CreateCampaignApiV1CampaignCreatePostResponse, CreateCredentialApiV1CredentialsPostData, CreateCredentialApiV1CredentialsPostError, CreateCredentialApiV1CredentialsPostResponse, CreateLoadTestApiV1LooptalkLoadTestsPostData, CreateLoadTestApiV1LooptalkLoadTestsPostError, CreateLoadTestApiV1LooptalkLoadTestsPostResponse, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostData, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostError, CreateOrUpdateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenPostResponse, CreateServiceKeyApiV1UserServiceKeysPostData, CreateServiceKeyApiV1UserServiceKeysPostError, CreateServiceKeyApiV1UserServiceKeysPostResponse, CreateSessionApiV1IntegrationSessionPostData, CreateSessionApiV1IntegrationSessionPostError, CreateSessionApiV1IntegrationSessionPostResponse, CreateTestSessionApiV1LooptalkTestSessionsPostData, CreateTestSessionApiV1LooptalkTestSessionsPostError, CreateTestSessionApiV1LooptalkTestSessionsPostResponse, CreateToolApiV1ToolsPostData, CreateToolApiV1ToolsPostError, CreateToolApiV1ToolsPostResponse, CreateWorkflowApiV1WorkflowCreateDefinitionPostData, CreateWorkflowApiV1WorkflowCreateDefinitionPostError, CreateWorkflowApiV1WorkflowCreateDefinitionPostResponse, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostData, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostError, CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponse, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostData, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostError, CreateWorkflowRunApiV1WorkflowWorkflowIdRunsPostResponse, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteData, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteError, DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteResponse, DeleteCredentialApiV1CredentialsCredentialUuidDeleteData, DeleteCredentialApiV1CredentialsCredentialUuidDeleteError, DeleteCredentialApiV1CredentialsCredentialUuidDeleteResponse, DeleteDocumentApiV1KnowledgeBaseDocumentsDocumentUuidDeleteData, DeleteDocumentApiV1KnowledgeBaseDocumentsDocumentUuidDeleteError, DeleteToolApiV1ToolsToolUuidDeleteData, DeleteToolApiV1ToolsToolUuidDeleteError, DeleteToolApiV1ToolsToolUuidDeleteResponse, DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetData, DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetError, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostData, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostError, DuplicateWorkflowTemplateApiV1WorkflowTemplatesDuplicatePostResponse, GetActiveTestsApiV1LooptalkActiveTestsGetData, GetActiveTestsApiV1LooptalkActiveTestsGetError, GetApiKeysApiV1UserApiKeysGetData, GetApiKeysApiV1UserApiKeysGetError, GetApiKeysApiV1UserApiKeysGetResponse, GetAuthUserApiV1UserAuthUserGetData, GetAuthUserApiV1UserAuthUserGetError, GetAuthUserApiV1UserAuthUserGetResponse, GetCampaignApiV1CampaignCampaignIdGetData, GetCampaignApiV1CampaignCampaignIdGetError, GetCampaignApiV1CampaignCampaignIdGetResponse, GetCampaignProgressApiV1CampaignCampaignIdProgressGetData, GetCampaignProgressApiV1CampaignCampaignIdProgressGetError, GetCampaignProgressApiV1CampaignCampaignIdProgressGetResponse, GetCampaignRunsApiV1CampaignCampaignIdRunsGetData, GetCampaignRunsApiV1CampaignCampaignIdRunsGetError, GetCampaignRunsApiV1CampaignCampaignIdRunsGetResponse, GetCampaignsApiV1CampaignGetData, GetCampaignsApiV1CampaignGetError, GetCampaignsApiV1CampaignGetResponse, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetData, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetError, GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetResponse, GetCredentialApiV1CredentialsCredentialUuidGetData, GetCredentialApiV1CredentialsCredentialUuidGetError, GetCredentialApiV1CredentialsCredentialUuidGetResponse, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetData, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetError, GetCurrentPeriodUsageApiV1OrganizationsUsageCurrentPeriodGetResponse, GetDailyReportApiV1OrganizationsReportsDailyGetData, GetDailyReportApiV1OrganizationsReportsDailyGetError, GetDailyReportApiV1OrganizationsReportsDailyGetResponse, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetData, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetError, GetDailyRunsDetailApiV1OrganizationsReportsDailyRunsGetResponse, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetData, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetError, GetDailyUsageBreakdownApiV1OrganizationsUsageDailyBreakdownGetResponse, GetDefaultConfigurationsApiV1UserConfigurationsDefaultsGetData, GetDefaultConfigurationsApiV1UserConfigurationsDefaultsGetResponse, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetData, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetError, GetDocumentApiV1KnowledgeBaseDocumentsDocumentUuidGetResponse, GetEmbedConfigApiV1PublicEmbedConfigTokenGetData, GetEmbedConfigApiV1PublicEmbedConfigTokenGetError, GetEmbedConfigApiV1PublicEmbedConfigTokenGetResponse, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetData, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetError, GetEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenGetResponse, GetFileMetadataApiV1S3FileMetadataGetData, GetFileMetadataApiV1S3FileMetadataGetError, GetFileMetadataApiV1S3FileMetadataGetResponse, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetData, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetError, GetIntegrationAccessTokenApiV1IntegrationIntegrationIdAccessTokenGetResponse, GetIntegrationsApiV1IntegrationGetData, GetIntegrationsApiV1IntegrationGetError, GetIntegrationsApiV1IntegrationGetResponse, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetData, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetError, GetLoadTestStatsApiV1LooptalkLoadTestsLoadTestGroupIdStatsGetResponse, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostData, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostError, GetPresignedUploadUrlApiV1S3PresignedUploadUrlPostResponse, GetServiceKeysApiV1UserServiceKeysGetData, GetServiceKeysApiV1UserServiceKeysGetError, GetServiceKeysApiV1UserServiceKeysGetResponse, GetSignedUrlApiV1S3SignedUrlGetData, GetSignedUrlApiV1S3SignedUrlGetError, GetSignedUrlApiV1S3SignedUrlGetResponse, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetData, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetError, GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetResponse, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetData, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetError, GetTestSessionApiV1LooptalkTestSessionsTestSessionIdGetResponse, GetTestSessionConversationApiV1LooptalkTestSessionsTestSessionIdConversationGetData, GetTestSessionConversationApiV1LooptalkTestSessionsTestSessionIdConversationGetError, GetToolApiV1ToolsToolUuidGetData, GetToolApiV1ToolsToolUuidGetError, GetToolApiV1ToolsToolUuidGetResponse, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostData, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostError, GetUploadUrlApiV1KnowledgeBaseUploadUrlPostResponse, GetUsageHistoryApiV1OrganizationsUsageRunsGetData, GetUsageHistoryApiV1OrganizationsUsageRunsGetError, GetUsageHistoryApiV1OrganizationsUsageRunsGetResponse, GetUserConfigurationsApiV1UserConfigurationsUserGetData, GetUserConfigurationsApiV1UserConfigurationsUserGetError, GetUserConfigurationsApiV1UserConfigurationsUserGetResponse, GetVoicesApiV1UserConfigurationsVoicesProviderGetData, GetVoicesApiV1UserConfigurationsVoicesProviderGetError, GetVoicesApiV1UserConfigurationsVoicesProviderGetResponse, GetWorkflowApiV1WorkflowFetchWorkflowIdGetData, GetWorkflowApiV1WorkflowFetchWorkflowIdGetError, GetWorkflowApiV1WorkflowFetchWorkflowIdGetResponse, GetWorkflowCountApiV1WorkflowCountGetData, GetWorkflowCountApiV1WorkflowCountGetError, GetWorkflowCountApiV1WorkflowCountGetResponse, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetData, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetError, GetWorkflowOptionsApiV1OrganizationsReportsWorkflowsGetResponse, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetData, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetError, GetWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGetResponse, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetData, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetError, GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponse, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetData, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetError, GetWorkflowRunsApiV1WorkflowWorkflowIdRunsGetResponse, GetWorkflowsApiV1WorkflowFetchGetData, GetWorkflowsApiV1WorkflowFetchGetError, GetWorkflowsApiV1WorkflowFetchGetResponse, GetWorkflowsSummaryApiV1WorkflowSummaryGetData, GetWorkflowsSummaryApiV1WorkflowSummaryGetError, GetWorkflowsSummaryApiV1WorkflowSummaryGetResponse, GetWorkflowTemplatesApiV1WorkflowTemplatesGetData, GetWorkflowTemplatesApiV1WorkflowTemplatesGetResponse, HandleCloudonixStatusCallbackApiV1TelephonyCloudonixStatusCallbackWorkflowRunIdPostData, HandleCloudonixStatusCallbackApiV1TelephonyCloudonixStatusCallbackWorkflowRunIdPostError, HandleInboundFallbackApiV1TelephonyInboundFallbackPostData, HandleInboundTelephonyApiV1TelephonyInboundWorkflowIdPostData, HandleInboundTelephonyApiV1TelephonyInboundWorkflowIdPostError, HandleTwilioStatusCallbackApiV1TelephonyTwilioStatusCallbackWorkflowRunIdPostData, HandleTwilioStatusCallbackApiV1TelephonyTwilioStatusCallbackWorkflowRunIdPostError, HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostData, HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostError, HandleVobizHangupCallbackByWorkflowApiV1TelephonyVobizHangupCallbackWorkflowWorkflowIdPostData, HandleVobizHangupCallbackByWorkflowApiV1TelephonyVobizHangupCallbackWorkflowWorkflowIdPostError, HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostData, HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostError, HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostData, HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostError, HealthApiV1HealthGetData, HealthApiV1HealthGetResponse,ImpersonateApiV1SuperuserImpersonatePostData, ImpersonateApiV1SuperuserImpersonatePostError, ImpersonateApiV1SuperuserImpersonatePostResponse, InitializeEmbedSessionApiV1PublicEmbedInitPostData, InitializeEmbedSessionApiV1PublicEmbedInitPostError, InitializeEmbedSessionApiV1PublicEmbedInitPostResponse, InitiateCallApiV1PublicAgentUuidPostData, InitiateCallApiV1PublicAgentUuidPostError, InitiateCallApiV1PublicAgentUuidPostResponse, InitiateCallApiV1TelephonyInitiateCallPostData, InitiateCallApiV1TelephonyInitiateCallPostError, ListCredentialsApiV1CredentialsGetData, ListCredentialsApiV1CredentialsGetError, ListCredentialsApiV1CredentialsGetResponse, ListDocumentsApiV1KnowledgeBaseDocumentsGetData, ListDocumentsApiV1KnowledgeBaseDocumentsGetError, ListDocumentsApiV1KnowledgeBaseDocumentsGetResponse, ListTestSessionsApiV1LooptalkTestSessionsGetData, ListTestSessionsApiV1LooptalkTestSessionsGetError, ListTestSessionsApiV1LooptalkTestSessionsGetResponse, ListToolsApiV1ToolsGetData, ListToolsApiV1ToolsGetError, ListToolsApiV1ToolsGetResponse, OptionsConfigApiV1PublicEmbedConfigTokenOptionsData, OptionsConfigApiV1PublicEmbedConfigTokenOptionsError, OptionsInitApiV1PublicEmbedInitOptionsData, PauseCampaignApiV1CampaignCampaignIdPausePostData, PauseCampaignApiV1CampaignCampaignIdPausePostError, PauseCampaignApiV1CampaignCampaignIdPausePostResponse, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostData, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostError, ProcessDocumentApiV1KnowledgeBaseProcessDocumentPostResponse, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutData, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutError, ReactivateApiKeyApiV1UserApiKeysApiKeyIdReactivatePutResponse, ReactivateServiceKeyApiV1UserServiceKeysServiceKeyIdReactivatePutData, ReactivateServiceKeyApiV1UserServiceKeysServiceKeyIdReactivatePutError, ResumeCampaignApiV1CampaignCampaignIdResumePostData, ResumeCampaignApiV1CampaignCampaignIdResumePostError, ResumeCampaignApiV1CampaignCampaignIdResumePostResponse, SaveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPostData, SaveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPostError, SearchChunksApiV1KnowledgeBaseSearchPostData, SearchChunksApiV1KnowledgeBaseSearchPostError, SearchChunksApiV1KnowledgeBaseSearchPostResponse, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostData, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostError, SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponse, StartCampaignApiV1CampaignCampaignIdStartPostData, StartCampaignApiV1CampaignCampaignIdStartPostError, StartCampaignApiV1CampaignCampaignIdStartPostResponse, StartTestSessionApiV1LooptalkTestSessionsTestSessionIdStartPostData, StartTestSessionApiV1LooptalkTestSessionsTestSessionIdStartPostError, StopTestSessionApiV1LooptalkTestSessionsTestSessionIdStopPostData, StopTestSessionApiV1LooptalkTestSessionsTestSessionIdStopPostError, UnarchiveToolApiV1ToolsToolUuidUnarchivePostData, UnarchiveToolApiV1ToolsToolUuidUnarchivePostError, UnarchiveToolApiV1ToolsToolUuidUnarchivePostResponse, UpdateCredentialApiV1CredentialsCredentialUuidPutData, UpdateCredentialApiV1CredentialsCredentialUuidPutError, UpdateCredentialApiV1CredentialsCredentialUuidPutResponse, UpdateIntegrationApiV1IntegrationIntegrationIdPutData, UpdateIntegrationApiV1IntegrationIntegrationIdPutError, UpdateIntegrationApiV1IntegrationIntegrationIdPutResponse, UpdateToolApiV1ToolsToolUuidPutData, UpdateToolApiV1ToolsToolUuidPutError, UpdateToolApiV1ToolsToolUuidPutResponse, UpdateUserConfigurationsApiV1UserConfigurationsUserPutData, UpdateUserConfigurationsApiV1UserConfigurationsUserPutError, UpdateUserConfigurationsApiV1UserConfigurationsUserPutResponse, UpdateWorkflowApiV1WorkflowWorkflowIdPutData, UpdateWorkflowApiV1WorkflowWorkflowIdPutError, UpdateWorkflowApiV1WorkflowWorkflowIdPutResponse, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutData, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutError, UpdateWorkflowStatusApiV1WorkflowWorkflowIdStatusPutResponse, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetData, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetError, ValidateUserConfigurationsApiV1UserConfigurationsUserValidateGetResponse, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostData, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostError, ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostResponse } from './types.gen'; export type Options = ClientOptions & { /** @@ -251,9 +251,26 @@ export const createWorkflowFromTemplateApiV1WorkflowCreateTemplatePost = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + url: '/api/v1/workflow/count', + ...options + }); +}; + /** * Get Workflows - * Get all workflows for the authenticated user's organization + * Get all workflows for the authenticated user's organization. + * + * Returns a lightweight response with only essential fields for listing. + * Use GET /workflow/fetch/{workflow_id} to get full workflow details. */ export const getWorkflowsApiV1WorkflowFetchGet = (options?: Options) => { return (options?.client ?? _heyApiClient).get({ @@ -1300,6 +1317,34 @@ export const initiateCallApiV1PublicAgentUuidPost = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + url: '/api/v1/public/download/workflow/{token}/{artifact_type}', + ...options + }); +}; + /** * Deactivate Embed Token * Deactivate the embed token for a workflow. @@ -1461,7 +1506,7 @@ export const searchChunksApiV1KnowledgeBaseSearchPost = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? _heyApiClient).get({ url: '/api/v1/health', ...options }); diff --git a/ui/src/client/types.gen.ts b/ui/src/client/types.gen.ts index 9e1a028..cd1eeb9 100644 --- a/ui/src/client/types.gen.ts +++ b/ui/src/client/types.gen.ts @@ -524,6 +524,12 @@ export type HttpValidationError = { detail?: Array; }; +export type HealthResponse = { + status: string; + version: string; + backend_api_endpoint: string; +}; + /** * Configuration for HTTP API tools. */ @@ -1042,6 +1048,15 @@ export type VonageConfigurationResponse = { */ export type WebhookCredentialType = 'none' | 'api_key' | 'bearer_token' | 'basic_auth' | 'custom_header'; +/** + * Response for workflow count endpoint. + */ +export type WorkflowCountResponse = { + total: number; + active: number; + archived: number; +}; + export type WorkflowError = { kind: ItemKind; id: string | null; @@ -1049,6 +1064,17 @@ export type WorkflowError = { message: string; }; +/** + * Lightweight response for workflow listings (excludes large fields). + */ +export type WorkflowListResponse = { + id: number; + name: string; + status: string; + created_at: string; + total_runs: number; +}; + export type WorkflowOption = { id: number; name: string; @@ -1391,6 +1417,7 @@ export type HandleInboundTelephonyApiV1TelephonyInboundWorkflowIdPostData = { 'x-twilio-signature'?: string | null; 'x-vobiz-signature'?: string | null; 'x-vobiz-timestamp'?: string | null; + 'x-cx-apikey'?: string | null; }; path: { workflow_id: number; @@ -1655,6 +1682,39 @@ export type CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponses = export type CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponse = CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponses[keyof CreateWorkflowFromTemplateApiV1WorkflowCreateTemplatePostResponses]; +export type GetWorkflowCountApiV1WorkflowCountGetData = { + body?: never; + headers?: { + authorization?: string | null; + 'X-API-Key'?: string | null; + }; + path?: never; + query?: never; + url: '/api/v1/workflow/count'; +}; + +export type GetWorkflowCountApiV1WorkflowCountGetErrors = { + /** + * Not found + */ + 404: unknown; + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetWorkflowCountApiV1WorkflowCountGetError = GetWorkflowCountApiV1WorkflowCountGetErrors[keyof GetWorkflowCountApiV1WorkflowCountGetErrors]; + +export type GetWorkflowCountApiV1WorkflowCountGetResponses = { + /** + * Successful Response + */ + 200: WorkflowCountResponse; +}; + +export type GetWorkflowCountApiV1WorkflowCountGetResponse = GetWorkflowCountApiV1WorkflowCountGetResponses[keyof GetWorkflowCountApiV1WorkflowCountGetResponses]; + export type GetWorkflowsApiV1WorkflowFetchGetData = { body?: never; headers?: { @@ -1688,7 +1748,7 @@ export type GetWorkflowsApiV1WorkflowFetchGetResponses = { /** * Successful Response */ - 200: Array; + 200: Array; }; export type GetWorkflowsApiV1WorkflowFetchGetResponse = GetWorkflowsApiV1WorkflowFetchGetResponses[keyof GetWorkflowsApiV1WorkflowFetchGetResponses]; @@ -4168,6 +4228,41 @@ export type InitiateCallApiV1PublicAgentUuidPostResponses = { export type InitiateCallApiV1PublicAgentUuidPostResponse = InitiateCallApiV1PublicAgentUuidPostResponses[keyof InitiateCallApiV1PublicAgentUuidPostResponses]; +export type DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetData = { + body?: never; + path: { + token: string; + artifact_type: 'recording' | 'transcript'; + }; + query?: { + /** + * Display inline in browser instead of download + */ + inline?: boolean; + }; + url: '/api/v1/public/download/workflow/{token}/{artifact_type}'; +}; + +export type DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetErrors = { + /** + * Not found + */ + 404: unknown; + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetError = DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetErrors[keyof DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetErrors]; + +export type DownloadWorkflowArtifactApiV1PublicDownloadWorkflowTokenArtifactTypeGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + export type DeactivateEmbedTokenApiV1WorkflowWorkflowIdEmbedTokenDeleteData = { body?: never; headers?: { @@ -4500,9 +4595,11 @@ export type HealthApiV1HealthGetResponses = { /** * Successful Response */ - 200: unknown; + 200: HealthResponse; }; +export type HealthApiV1HealthGetResponse = HealthApiV1HealthGetResponses[keyof HealthApiV1HealthGetResponses]; + export type ClientOptions = { baseUrl: 'http://127.0.0.1:8000' | (string & {}); }; diff --git a/ui/src/components/MediaPreviewDialog.tsx b/ui/src/components/MediaPreviewDialog.tsx index a463d48..fd406f2 100644 --- a/ui/src/components/MediaPreviewDialog.tsx +++ b/ui/src/components/MediaPreviewDialog.tsx @@ -22,6 +22,7 @@ export function MediaPreviewDialog({ accessToken }: MediaPreviewDialogProps) { const [isOpen, setIsOpen] = useState(false); const [mediaType, setMediaType] = useState<'audio' | 'transcript' | null>(null); const [mediaSignedUrl, setMediaSignedUrl] = useState(null); + const [transcriptContent, setTranscriptContent] = useState(null); const [selectedRunId, setSelectedRunId] = useState(null); const [mediaDownloadKey, setMediaDownloadKey] = useState(null); const [mediaLoading, setMediaLoading] = useState(false); @@ -47,6 +48,7 @@ export function MediaPreviewDialog({ accessToken }: MediaPreviewDialogProps) { async (fileKey: string | null, runId: number) => { if (!fileKey || !accessToken) return; setMediaLoading(true); + setTranscriptContent(null); const signed = await getSignedUrl(fileKey, accessToken, true); if (signed) { setMediaType('transcript'); @@ -54,6 +56,14 @@ export function MediaPreviewDialog({ accessToken }: MediaPreviewDialogProps) { setMediaDownloadKey(fileKey); setSelectedRunId(runId); setIsOpen(true); + // Fetch transcript content with proper UTF-8 encoding + try { + const response = await fetch(signed); + const text = await response.text(); + setTranscriptContent(text); + } catch (error) { + console.error('Error fetching transcript:', error); + } } setMediaLoading(false); }, @@ -84,12 +94,10 @@ export function MediaPreviewDialog({ accessToken }: MediaPreviewDialogProps) {