This document explains why and how we use Azure (Durable) Functions to handle AI transcription and classification, offloading heavy processing from the Next.js frontend.
Processing large audio or video files (transcription + classification) can take anywhere from 20 seconds to several minutes depending on the file size.
- Vercel Limits: Standard Vercel Serverless Functions have a 10–60 second maximum execution time (depending on plan). Long media files will often trigger a timeout.
- Client Experience: Moving the processing to a background job allows the frontend to return a "Successfully Queued" message immediately, keeping the UI responsive.
- Reliability: Background jobs can be retried and monitored independently of the frontend lifecycle.
The background processing system is located in the azure-functions/ directory.
HttpBlobTrigger: A Node.js v4 function that acts as a Webhook endpoint. It listens forblob.createdevents (simulated or real) from Vercel Blob storage.jobQueue: Currently implemented as an in-memory sequential queue within the function to ensure one-by-one processing, preventing API rate limits on Gemini/Whisper.- Durable Logic (Planned): The
durable-functionsdependency is installed to support multi-step orchestrations (e.g., separate activities for Transcription and Classification) in future iterations.
- azure-functions/src/functions/transcriptionFlow.js: Core logic for transcription and classification.
- app/actions.js (
enqueueTranscriptionAction): Triggers the Azure Function from the Next.js frontend. - azure-functions/local.settings.json: Environment variables for local execution.
- Prerequisites: Install Azure Functions Core Tools and run Azurite (local storage emulator).
- Setup:
cd azure-functions npm install # Ensure Azurite is running in the background npm start
- Webhook Tunnelling: Use
ngrokto expose your local function to the internet:ngrok http 7071 - Configuration: Set the
AZURE_FUNCTION_URLin your Next.js.envto your ngrok URL (e.g.,https://xyz.ngrok-free.app/api/HttpBlobTrigger).
- Deployment: Deploy the code in
azure-functions/to an Azure Function App (Linux/Node.js). - Environment Variables: Mirror the keys from
azure-functions/local.settings.jsonto the Azure portal (Application Settings). - Blob Webhook: Configure the Vercel Blob store to trigger the Azure Function URL on every new upload.
When moving to production on Vercel:
- Update
AZURE_FUNCTION_URLin the Vercel Dashboard to point to your live Azure Function. - Ensure
VERCEL_BLOB_BASE_URLis correctly set so the Azure Function can retrieve the uploaded files. - The
maxDurationinvercel.jsonis less critical when using Azure Functions, as the frontend only sends a trigger request.
Below is a detailed analysis of the core background processing logic in transcriptionFlow.js.
Lines: 288 – 319
- What: This is the
HttpBlobTriggerfunction that listens forPOSTrequests. - Why: It captures the Vercel Blob webhook event. If the event type is
blob.created, it extracts theurland model IDs. - Expected Result: Returns a
202 Acceptedstatus to the caller (Next.js/Vercel) along with the current queue position. It triggers thejobQueuewithout blocking the HTTP response.
Lines: 125 – 130 & 277 – 285
- What: A simple
jobQueuearray and arunQueueloop. - Why: Since Azure Functions can be triggered concurrently by multiple uploads, we use this queue to ensure sequential processing. This prevents hitting rate limits on Google Vertex AI or DeepInfra APIs by only running one
processJobat a time. - Expected Result: Jobs are processed one-by-one in the order they were received (FIFO).
Lines: 142 – 181
- What: Logic inside
processJobthat fetches the media file and sends it to either DeepInfra (Whisper) or Replicate. - Why: Converts the raw audio/video file into text segments with timestamps.
- Expected Result: A
transcriptionResultobject containing the fulltextand an array of timestampedsegments.
Lines: 187 – 240
- What: Initializes
VertexAI, selects the correct regional endpoint (globalvsasia-south1), and sends the transcription text to Gemini. - Why: Uses AI to categorize the conversation based on the strict hierarchy defined in
GEMINI_PROMPT. - Expected Result: A structured JSON object (
geminiResult) containing categories, sub-types, sentiment, and key points.
Lines: 242 – 271
- What: Downloads the existing
results.jsonfrom Vercel Blob, appends the new classification data (including timing metadata), and re-uploads it. - Why: To keep a persistent history of all processed files that the Dashboard can display.
- Expected Result:
history/results.jsonis updated with the latest processing details.
Tip
Monitoring: You can check the health of the background processor via the GET /api/health endpoint (Lines 321–335), which returns the current queue length and processing status.