refactor(extraction-service): provider-agnostic transcription — OpenAI + Azure Speech + Mock
- TranscriptionProvider interface with transcribe() + isConfigured()
- OpenAITranscriptionProvider: Whisper API (existing behavior)
- AzureTranscriptionProvider: Azure Speech REST API for short audio
- MockTranscriptionProvider: deterministic results for testing
- Factory: getSTT() singleton with env-driven auto-detection
- STT_PROVIDER=openai|azure|mock (explicit)
- Auto-detect: AZURE_SPEECH_KEY → azure, OPENAI_API_KEY → openai, else mock
- Config: add STT_PROVIDER, AZURE_SPEECH_KEY, AZURE_SPEECH_REGION env vars
- Route refactored: audio download (common) → provider.transcribe() (swappable)
- deriveFilename() extracted to types.ts (shared by route + providers)
- 35 transcription tests (was 12), 171 total passing
- Follows same pattern as @bytelyst/llm provider abstraction