openspeechapi 0.2.6__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/PKG-INFO +1 -1
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/__init__.py +1 -1
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/models.py +19 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/iflytek.py +214 -77
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/pyproject.toml +1 -1
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.dockerignore +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.env.example +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.github/workflows/ci.yml +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.gitignore +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en.aiff +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_16k.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_16k_pad6.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_long.aiff +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_long_16k.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_mid.aiff +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/en_mid_16k.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/zh.aiff +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/audio/zh_16k.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/openspeech-8600.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/openspeech-serve.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/webui-server.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/webui-server.pid +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/wlk12101.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/wlk12101.pid +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/wlk12102.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/.tmp/wlk12102.pid +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/AGENTS.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/CLAUDE.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/Dockerfile +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/README.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/aibox-script/aibox-1.0.0-SNAPSHOT-stdout.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/aibox-script/aibox.2026-04-02.log +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/aibox-script/com.user.restart-jar.plist +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/aibox-script/restart-jar.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/aibox-script.tar.gz +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docker-compose.yml +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/local-engine-manager.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/logging-spec.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/stt-engineering-optimization-guide.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/stt-streaming-spec.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/webui-phase-a.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/engines/fish-speech-docker.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/engines/fish-speech-native.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/engines/stt-native-models.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/plans/2026-04-01-phase1-implementation.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/plans/2026-04-11-macos-native-tts-stt.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-01-openspeech-api-design.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-03-hot-lazy-loading.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-03-phase2-protocol-layer.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-03-phase3-production.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-11-macos-native-tts-stt-design.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-12-cloud-providers-webui-design.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-15-streaming-tts-stt-fixes-display-names.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-16-provider-management-engines-rename.md +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/examples/client_stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/examples/client_tts.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/examples/stt_simple.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/examples/tts_simple.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/__main__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/cli.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/client/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/client/client.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/config.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/enums.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/registry.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/core/settings.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/demo.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/context.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/dispatcher.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/in_process.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/remote.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/subprocess_exec.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/fanout.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/filters.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/lifecycle.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/watcher.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/engine_catalog.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/engine_registry.yaml +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/exceptions.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/factory.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/aim_resolver.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/backends/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/backends/docker_backend.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/backends/native_backend.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/faster_whisper.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/fish_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/sherpa_onnx.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/whisper.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/engines/whisperlivekit.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/manager.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/models.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/progress.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/registry.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/task_store.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/local_engines/tasks.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/logging_config.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/debug.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/latency.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/metrics.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/tracing.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/observe/usage.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/_template.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/alibaba.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/assemblyai.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/azure_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/baidu.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/deepgram.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/elevenlabs.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/faster_whisper.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/google_cloud.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/macos_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/openai.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/sherpa_onnx.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/tencent.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/volcengine.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/whisper.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/whisperlivekit.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/stt/windows_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/alibaba.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/azure_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/baidu.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/coqui.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/cosyvoice.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/deepgram.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/elevenlabs.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/fish_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/google_cloud.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/iflytek.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/macos_say.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/minimax.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/openai.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/piper.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/tencent.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/volcengine.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/providers/tts/windows_sapi.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/app.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/auth.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/middleware.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/routes/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/routes/management.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/routes/stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/routes/tts.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/routes/webui.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/webui/app.js +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/webui/index.html +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/webui/styles.css +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/ws/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/ws/stt_stream.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/server/ws/tts_stream.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/telemetry/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/telemetry/perf.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/utils/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/utils/audio_converter.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/utils/audio_playback.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/vendor_registry.yaml +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/output/output.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/output.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/providers.example.yaml +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/cloud/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/faster-whisper/native/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/fish-speech/native/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/macos-stt/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/macos-stt/macos_stt.swift +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/macos-stt/request_auth.swift +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/sherpa-onnx/native/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/sherpa-onnx/native/run_streaming_server.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/whisper/native/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/scripts/engines/whisperlivekit/native/install.sh +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/conftest.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/conftest.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/test_fanout_e2e.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/test_faster_whisper_e2e.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/test_openai_e2e.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/e2e/test_webui_e2e.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/fixtures/hello.wav +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/integration/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/integration/test_fanout_integration.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/integration/test_in_process_integration.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/integration/test_server_client.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_aim_resolver.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_audio_converter.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_audio_playback.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_cli.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_cli_engine.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_client.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_config.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_context.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_debug_observer.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_dispatcher.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_docker_backend_progress.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_engine_registry.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_enums.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_executor_base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_fanout.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_filters.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_hot_reload.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_in_process.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_latency_observer.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_lifecycle.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_local_engine_task_store.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_local_engines_manager.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_logging.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_metrics_observer.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_models.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_native_backend.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_observer_base.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_plugin_mechanism.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_cloud_providers.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_elevenlabs_stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_macos_say.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_macos_speech.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_openai_base_url.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_openai_stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_openai_tts.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_sherpa_onnx_stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_stt_stubs.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_tts_stubs.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_providers/test_whisperlivekit_stt.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_registry.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_remote.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_server/__init__.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_server/test_auth.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_server/test_config_api.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_server/test_routes.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_server/test_websocket.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_subprocess.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_usage_observer.py +0 -0
- {openspeechapi-0.2.6 → openspeechapi-0.2.8}/tests/unit/test_watcher.py +0 -0
|
@@ -60,6 +60,25 @@ class STTOptions:
|
|
|
60
60
|
# voice assistant. Providers that don't support VAD finalization
|
|
61
61
|
# (Whisper, Faster-Whisper) silently ignore this field.
|
|
62
62
|
vad_eos: int | None = None
|
|
63
|
+
# ── iFlytek IAT pass-through (matches Java AsrServiceImpl) ───────
|
|
64
|
+
# Wallex's Java AsrService forwards the client-supplied
|
|
65
|
+
# ``audio.common`` / ``audio.business`` / extra ``audio.data``
|
|
66
|
+
# fields verbatim to iFlytek's WS, treating the panel as the
|
|
67
|
+
# source of truth for ASR parameters. The Python pipeline now
|
|
68
|
+
# mirrors that contract: when these fields are non-None, the
|
|
69
|
+
# iFlytek provider uses them as the basis for the WS first frame
|
|
70
|
+
# (with ``setdefault`` fallback to its own settings for any keys
|
|
71
|
+
# the client omitted) instead of building the blocks purely from
|
|
72
|
+
# ``speech_providers.yaml``. ``None`` preserves the existing
|
|
73
|
+
# yaml-driven behaviour. Other STT providers ignore these fields.
|
|
74
|
+
iflytek_common: dict | None = None
|
|
75
|
+
iflytek_business: dict | None = None
|
|
76
|
+
# Extra fields to merge into the iFlytek ``data`` block beyond the
|
|
77
|
+
# canonical ``status``/``format``/``encoding``/``audio`` quadruple
|
|
78
|
+
# (e.g. panel-supplied ``data_type``). Keys that collide with the
|
|
79
|
+
# canonical set are preserved (the provider's defaults still win,
|
|
80
|
+
# since the canonical set is required by the IAT spec).
|
|
81
|
+
iflytek_data_extras: dict | None = None
|
|
63
82
|
|
|
64
83
|
|
|
65
84
|
@dataclass
|
|
@@ -72,6 +72,34 @@ class IflytekSTTSettings(BaseSettings):
|
|
|
72
72
|
# that. Lower if the network has aggressive proxies, higher only
|
|
73
73
|
# if the iFlytek endpoint is consistently slow to handshake.
|
|
74
74
|
timeout_secs: int = 15
|
|
75
|
+
# ── Java AsrConfig parity (used as setdefault fallbacks) ────────
|
|
76
|
+
# When a wallex client (panel) supplies ``audio.business``/
|
|
77
|
+
# ``audio.common`` per-frame, those values flow through via
|
|
78
|
+
# ``STTOptions.iflytek_business``/``STTOptions.iflytek_common`` and
|
|
79
|
+
# become the WS first frame body. The settings below act as
|
|
80
|
+
# ``setdefault`` fallbacks for keys the client omits, mirroring
|
|
81
|
+
# Java ``AsrConfig``'s field set so the two implementations
|
|
82
|
+
# produce identical wire frames given the same panel payload.
|
|
83
|
+
#
|
|
84
|
+
# ``domain`` — iFlytek IAT domain. Java default ``iat``; a few
|
|
85
|
+
# vertical models (``medical`` / ``tv``) exist but most
|
|
86
|
+
# deployments stay on the general one.
|
|
87
|
+
domain: str = "iat"
|
|
88
|
+
# ``accent`` — only meaningful when ``language=="zh_cn"`` (selects
|
|
89
|
+
# mandarin vs. cantonese etc.). Java sends ``mandarin`` blindly;
|
|
90
|
+
# we keep the same default so the WS frame matches Java byte-for-
|
|
91
|
+
# byte when the panel omits ``business.accent``. iFlytek treats
|
|
92
|
+
# it as a no-op for non-Chinese language codes.
|
|
93
|
+
accent: str = "mandarin"
|
|
94
|
+
# ``dwa`` — dynamic word adjustment / wpgs (实时纠错). Java's
|
|
95
|
+
# default ``wpgs`` is the realtime-correction mode panels rely on
|
|
96
|
+
# for the partial-result protocol described in
|
|
97
|
+
# ``stt-streaming-spec.md``. Empty disables it.
|
|
98
|
+
dwa: str = "wpgs"
|
|
99
|
+
# ``sample_rate`` — required by the IAT directed-domain endpoint
|
|
100
|
+
# (``ws-api-dx.xfyun.cn``) which expects it in ``business``. Java
|
|
101
|
+
# AsrConfig.sampleRate=16000.
|
|
102
|
+
sample_rate: int = 16000
|
|
75
103
|
|
|
76
104
|
|
|
77
105
|
# iFlytek expects the full locale tag; common ISO short codes need to
|
|
@@ -188,6 +216,117 @@ class IflytekSTT(STTProvider):
|
|
|
188
216
|
)
|
|
189
217
|
return f"wss://{host}{path}?{params}"
|
|
190
218
|
|
|
219
|
+
def _build_first_frame_blocks(
|
|
220
|
+
self,
|
|
221
|
+
opts: STTOptions | None,
|
|
222
|
+
*,
|
|
223
|
+
include_dwa: bool,
|
|
224
|
+
) -> tuple[dict, dict]:
|
|
225
|
+
"""Build the ``common`` / ``business`` blocks for the WS first frame.
|
|
226
|
+
|
|
227
|
+
Mirrors Java ``AsrServiceImpl.sendToAsr`` semantics: when
|
|
228
|
+
``opts.iflytek_common``/``opts.iflytek_business`` is provided
|
|
229
|
+
(typically by wallex relaying the panel's per-frame
|
|
230
|
+
``audio.common`` / ``audio.business``), those dicts are the
|
|
231
|
+
source of truth. We only ``setdefault`` keys the client omitted,
|
|
232
|
+
falling back to ``self.settings`` so a panel that misses a
|
|
233
|
+
single field doesn't get a malformed frame.
|
|
234
|
+
|
|
235
|
+
``include_dwa`` differs between ``transcribe()`` (batch — no
|
|
236
|
+
wpgs because there's no streaming protocol) and
|
|
237
|
+
``transcribe_stream()`` (always wpgs).
|
|
238
|
+
"""
|
|
239
|
+
canon = _canonical_language(self.settings.language)
|
|
240
|
+
eos = (opts.vad_eos
|
|
241
|
+
if opts is not None and opts.vad_eos is not None
|
|
242
|
+
else self.settings.vad_eos)
|
|
243
|
+
|
|
244
|
+
# ── business block ─────────────────────────────────────────
|
|
245
|
+
if opts is not None and opts.iflytek_business:
|
|
246
|
+
# Panel-supplied is authoritative; copy then fill missing
|
|
247
|
+
# keys from yaml so we never send a partial frame.
|
|
248
|
+
business = dict(opts.iflytek_business)
|
|
249
|
+
else:
|
|
250
|
+
business = {}
|
|
251
|
+
|
|
252
|
+
business.setdefault("language", canon)
|
|
253
|
+
business.setdefault("domain", self.settings.domain)
|
|
254
|
+
business.setdefault("vad_eos", eos)
|
|
255
|
+
business.setdefault("ltc", self.settings.ltc)
|
|
256
|
+
if include_dwa:
|
|
257
|
+
business.setdefault("dwa", self.settings.dwa)
|
|
258
|
+
# ``accent`` is only meaningful for the Chinese model. Java
|
|
259
|
+
# sends ``mandarin`` blindly; we keep that for byte-for-byte
|
|
260
|
+
# parity when the panel omits it AND language is zh_cn. For
|
|
261
|
+
# other languages we leave it out entirely (sending it is a
|
|
262
|
+
# no-op on iFlytek's side but confuses log readers).
|
|
263
|
+
if "accent" not in business and canon == "zh_cn":
|
|
264
|
+
business["accent"] = self.settings.accent
|
|
265
|
+
|
|
266
|
+
# ── common block ──────────────────────────────────────────
|
|
267
|
+
if opts is not None and opts.iflytek_common:
|
|
268
|
+
common = dict(opts.iflytek_common)
|
|
269
|
+
else:
|
|
270
|
+
common = {}
|
|
271
|
+
common.setdefault("app_id", self.settings.app_id)
|
|
272
|
+
|
|
273
|
+
return common, business
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _build_data_block(
|
|
277
|
+
*, status: int, audio_b64: str, opts: STTOptions | None,
|
|
278
|
+
) -> dict:
|
|
279
|
+
"""Assemble the ``data`` block, merging panel-supplied extras.
|
|
280
|
+
|
|
281
|
+
Canonical keys (``status``/``format``/``encoding``/``audio``)
|
|
282
|
+
always win over ``iflytek_data_extras`` because the IAT spec
|
|
283
|
+
requires them in a specific shape; extras like the panel's
|
|
284
|
+
``data_type`` flow through.
|
|
285
|
+
"""
|
|
286
|
+
if opts is not None and opts.iflytek_data_extras:
|
|
287
|
+
data = dict(opts.iflytek_data_extras)
|
|
288
|
+
else:
|
|
289
|
+
data = {}
|
|
290
|
+
data["status"] = status
|
|
291
|
+
data["format"] = "audio/L16;rate=16000"
|
|
292
|
+
data["encoding"] = "raw"
|
|
293
|
+
data["audio"] = audio_b64
|
|
294
|
+
return data
|
|
295
|
+
|
|
296
|
+
async def _connect_with_retry(self) -> "websockets.ClientConnection":
|
|
297
|
+
"""Connect to iFlytek IAT WS with backoff, mirroring Java parity.
|
|
298
|
+
|
|
299
|
+
Java ``AsrServiceImpl.connectWithRetry`` does 4 attempts with
|
|
300
|
+
300/600/1200ms backoff before giving up. The previous Python
|
|
301
|
+
path was one-shot: a single TCP/handshake hiccup surfaced as a
|
|
302
|
+
hard ASR failure. Aligning the retry budget keeps wallex's
|
|
303
|
+
Python and Java front-ends behaviourally interchangeable on
|
|
304
|
+
flaky links.
|
|
305
|
+
"""
|
|
306
|
+
backoffs = (0.3, 0.6, 1.2) # delays AFTER attempts 1, 2, 3
|
|
307
|
+
last_exc: Exception | None = None
|
|
308
|
+
for attempt in range(4):
|
|
309
|
+
try:
|
|
310
|
+
url = self._build_auth_url()
|
|
311
|
+
ws = await websockets.connect(url)
|
|
312
|
+
if attempt > 0:
|
|
313
|
+
logger.info(
|
|
314
|
+
"{}: WS connected on attempt {}/4",
|
|
315
|
+
self.name, attempt + 1,
|
|
316
|
+
)
|
|
317
|
+
return ws
|
|
318
|
+
except Exception as e: # noqa: BLE001 — retry boundary
|
|
319
|
+
last_exc = e
|
|
320
|
+
logger.warning(
|
|
321
|
+
"{}: WS connect failed (attempt {}/4): {}",
|
|
322
|
+
self.name, attempt + 1, e,
|
|
323
|
+
)
|
|
324
|
+
if attempt < len(backoffs):
|
|
325
|
+
await asyncio.sleep(backoffs[attempt])
|
|
326
|
+
raise RuntimeError(
|
|
327
|
+
f"iFlytek STT connect failed after 4 attempts: {last_exc}"
|
|
328
|
+
)
|
|
329
|
+
|
|
191
330
|
async def transcribe(
|
|
192
331
|
self, audio: AudioData, opts: STTOptions | None = None
|
|
193
332
|
) -> Transcription:
|
|
@@ -196,7 +335,6 @@ class IflytekSTT(STTProvider):
|
|
|
196
335
|
logger.info("{}: request received, audio={} bytes", self.name, len(audio.data))
|
|
197
336
|
_t0 = time.perf_counter()
|
|
198
337
|
|
|
199
|
-
url = self._build_auth_url()
|
|
200
338
|
audio_bytes = audio.data
|
|
201
339
|
# iFlytek recommends ~40ms per frame at 16kHz 16bit mono = 1280 bytes.
|
|
202
340
|
# Use larger frames (8000 bytes = ~250ms) with pacing to avoid server
|
|
@@ -209,7 +347,16 @@ class IflytekSTT(STTProvider):
|
|
|
209
347
|
|
|
210
348
|
result_texts: list[str] = []
|
|
211
349
|
|
|
212
|
-
|
|
350
|
+
# ``ws = await websockets.connect(...)`` returns the connection
|
|
351
|
+
# object directly. Re-using it as ``async with ws:`` worked on
|
|
352
|
+
# older websockets releases that exposed ``__aenter__`` /
|
|
353
|
+
# ``__aexit__`` on ``WebSocketClientProtocol``, but recent
|
|
354
|
+
# versions removed that re-entry alias and the only supported
|
|
355
|
+
# context-manager pattern is ``async with websockets.connect(...)``.
|
|
356
|
+
# Use try/finally with an explicit close so this stays correct
|
|
357
|
+
# across both old and new websockets.
|
|
358
|
+
ws = await self._connect_with_retry()
|
|
359
|
+
try:
|
|
213
360
|
# Send audio in chunks with interleaved receive
|
|
214
361
|
total = len(audio_bytes)
|
|
215
362
|
offset = 0
|
|
@@ -228,47 +375,36 @@ class IflytekSTT(STTProvider):
|
|
|
228
375
|
frame_data = base64.b64encode(chunk).decode("utf-8")
|
|
229
376
|
|
|
230
377
|
if status == 0:
|
|
231
|
-
# First frame
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# ``parameter.iat.eos`` through here so a kiosk
|
|
241
|
-
# can ship a tighter or looser silence threshold
|
|
242
|
-
# than the deployment yaml.
|
|
243
|
-
eos = (opts.vad_eos
|
|
244
|
-
if opts is not None and opts.vad_eos is not None
|
|
245
|
-
else self.settings.vad_eos)
|
|
246
|
-
business = {
|
|
247
|
-
"language": canon,
|
|
248
|
-
"domain": "iat",
|
|
249
|
-
"vad_eos": eos,
|
|
250
|
-
"ltc": self.settings.ltc,
|
|
251
|
-
}
|
|
252
|
-
if canon == "zh_cn":
|
|
253
|
-
business["accent"] = "mandarin"
|
|
378
|
+
# First frame: panel-supplied common/business win;
|
|
379
|
+
# batch path doesn't carry wpgs (no streaming
|
|
380
|
+
# protocol) so include_dwa=False.
|
|
381
|
+
common, business = self._build_first_frame_blocks(
|
|
382
|
+
opts, include_dwa=False,
|
|
383
|
+
)
|
|
384
|
+
data_block = self._build_data_block(
|
|
385
|
+
status=0, audio_b64=frame_data, opts=opts,
|
|
386
|
+
)
|
|
254
387
|
msg = {
|
|
255
|
-
"common":
|
|
388
|
+
"common": common,
|
|
256
389
|
"business": business,
|
|
257
|
-
"data":
|
|
258
|
-
"status": 0,
|
|
259
|
-
"format": "audio/L16;rate=16000",
|
|
260
|
-
"encoding": "raw",
|
|
261
|
-
"audio": frame_data,
|
|
262
|
-
},
|
|
390
|
+
"data": data_block,
|
|
263
391
|
}
|
|
392
|
+
# Java parity: log the exact blocks we're about to
|
|
393
|
+
# ship to iFlytek. Debugging "wrong language /
|
|
394
|
+
# wrong endpoint" reports needs to see this from
|
|
395
|
+
# the log alone — Java's AsrServiceImpl prints the
|
|
396
|
+
# equivalent line at INFO.
|
|
397
|
+
logger.info(
|
|
398
|
+
"{}: ASR first frame business={}, common={}",
|
|
399
|
+
self.name,
|
|
400
|
+
json.dumps(business, ensure_ascii=False),
|
|
401
|
+
json.dumps(common, ensure_ascii=False),
|
|
402
|
+
)
|
|
264
403
|
else:
|
|
265
404
|
msg = {
|
|
266
|
-
"data":
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
"encoding": "raw",
|
|
270
|
-
"audio": frame_data,
|
|
271
|
-
}
|
|
405
|
+
"data": self._build_data_block(
|
|
406
|
+
status=status, audio_b64=frame_data, opts=opts,
|
|
407
|
+
)
|
|
272
408
|
}
|
|
273
409
|
|
|
274
410
|
await ws.send(json.dumps(msg))
|
|
@@ -301,6 +437,10 @@ class IflytekSTT(STTProvider):
|
|
|
301
437
|
|
|
302
438
|
if data.get("status") == 2:
|
|
303
439
|
break
|
|
440
|
+
finally:
|
|
441
|
+
# Idempotent on websockets ≥ 11; safe to call even if the
|
|
442
|
+
# server already closed the socket.
|
|
443
|
+
await ws.close()
|
|
304
444
|
|
|
305
445
|
result = Transcription(text="".join(result_texts))
|
|
306
446
|
logger.info("{}: completed in {:.0f}ms, result={} chars", self.name, (time.perf_counter() - _t0) * 1000, len(result.text))
|
|
@@ -340,7 +480,6 @@ class IflytekSTT(STTProvider):
|
|
|
340
480
|
if self._client is None:
|
|
341
481
|
raise RuntimeError("Provider not started — call start() first")
|
|
342
482
|
|
|
343
|
-
url = self._build_auth_url()
|
|
344
483
|
results: asyncio.Queue[Transcription | None] = asyncio.Queue()
|
|
345
484
|
_t0 = time.perf_counter()
|
|
346
485
|
_frames_sent = 0
|
|
@@ -352,7 +491,10 @@ class IflytekSTT(STTProvider):
|
|
|
352
491
|
_sender_stop = asyncio.Event()
|
|
353
492
|
|
|
354
493
|
logger.debug("{}: connecting to iFlytek WebSocket...", self.name)
|
|
355
|
-
|
|
494
|
+
# See ``transcribe`` for why this is try/finally + ``await
|
|
495
|
+
# ws.close()`` rather than ``async with ws``.
|
|
496
|
+
ws = await self._connect_with_retry()
|
|
497
|
+
try:
|
|
356
498
|
_t_connected = time.perf_counter()
|
|
357
499
|
logger.info("{}: WS connected in {:.0f}ms", self.name,
|
|
358
500
|
(_t_connected - _t0) * 1000)
|
|
@@ -370,43 +512,38 @@ class IflytekSTT(STTProvider):
|
|
|
370
512
|
break
|
|
371
513
|
frame_data = base64.b64encode(chunk).decode("utf-8")
|
|
372
514
|
if is_first:
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
"language": canon,
|
|
384
|
-
"domain": "iat",
|
|
385
|
-
"dwa": "wpgs",
|
|
386
|
-
"vad_eos": eos,
|
|
387
|
-
"ltc": self.settings.ltc,
|
|
388
|
-
}
|
|
389
|
-
if canon == "zh_cn":
|
|
390
|
-
business["accent"] = "mandarin"
|
|
515
|
+
# First frame: panel-supplied common/business win;
|
|
516
|
+
# streaming path always carries wpgs (see
|
|
517
|
+
# stt-streaming-spec.md realtime-correction
|
|
518
|
+
# protocol) so include_dwa=True.
|
|
519
|
+
common, business = self._build_first_frame_blocks(
|
|
520
|
+
opts, include_dwa=True,
|
|
521
|
+
)
|
|
522
|
+
data_block = self._build_data_block(
|
|
523
|
+
status=0, audio_b64=frame_data, opts=opts,
|
|
524
|
+
)
|
|
391
525
|
msg = {
|
|
392
|
-
"common":
|
|
526
|
+
"common": common,
|
|
393
527
|
"business": business,
|
|
394
|
-
"data":
|
|
395
|
-
"status": 0,
|
|
396
|
-
"format": "audio/L16;rate=16000",
|
|
397
|
-
"encoding": "raw",
|
|
398
|
-
"audio": frame_data,
|
|
399
|
-
},
|
|
528
|
+
"data": data_block,
|
|
400
529
|
}
|
|
530
|
+
# Java parity (AsrServiceImpl line 221): log
|
|
531
|
+
# the first-frame business + common at INFO so
|
|
532
|
+
# operators can verify which language/eos/dwa
|
|
533
|
+
# the panel actually requested without
|
|
534
|
+
# rebuilding the call from yaml + STTOptions.
|
|
535
|
+
logger.info(
|
|
536
|
+
"{}: ASR first frame business={}, common={}",
|
|
537
|
+
self.name,
|
|
538
|
+
json.dumps(business, ensure_ascii=False),
|
|
539
|
+
json.dumps(common, ensure_ascii=False),
|
|
540
|
+
)
|
|
401
541
|
is_first = False
|
|
402
542
|
else:
|
|
403
543
|
msg = {
|
|
404
|
-
"data":
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
"encoding": "raw",
|
|
408
|
-
"audio": frame_data,
|
|
409
|
-
}
|
|
544
|
+
"data": self._build_data_block(
|
|
545
|
+
status=1, audio_b64=frame_data, opts=opts,
|
|
546
|
+
)
|
|
410
547
|
}
|
|
411
548
|
await ws.send(json.dumps(msg))
|
|
412
549
|
_frames_sent += 1
|
|
@@ -417,12 +554,9 @@ class IflytekSTT(STTProvider):
|
|
|
417
554
|
# Send empty last frame to signal end (only if WS still open)
|
|
418
555
|
if not _sender_stop.is_set():
|
|
419
556
|
last_msg = {
|
|
420
|
-
"data":
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
"encoding": "raw",
|
|
424
|
-
"audio": "",
|
|
425
|
-
}
|
|
557
|
+
"data": self._build_data_block(
|
|
558
|
+
status=2, audio_b64="", opts=opts,
|
|
559
|
+
)
|
|
426
560
|
}
|
|
427
561
|
await ws.send(json.dumps(last_msg))
|
|
428
562
|
except websockets.exceptions.ConnectionClosed:
|
|
@@ -544,3 +678,6 @@ class IflytekSTT(STTProvider):
|
|
|
544
678
|
await task
|
|
545
679
|
except websockets.exceptions.ConnectionClosed:
|
|
546
680
|
pass
|
|
681
|
+
finally:
|
|
682
|
+
# Idempotent on websockets ≥ 11.
|
|
683
|
+
await ws.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/architecture/stt-engineering-optimization-guide.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-03-hot-lazy-loading.md
RENAMED
|
File without changes
|
|
File without changes
|
{openspeechapi-0.2.6 → openspeechapi-0.2.8}/docs/superpowers/specs/2026-04-03-phase3-production.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openspeechapi-0.2.6 → openspeechapi-0.2.8}/openspeechapi/dispatch/executors/subprocess_exec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|