{"id":51027199,"url":"https://github.com/deepgram/deepgram-audiocodes-bridge","last_synced_at":"2026-06-21T20:30:48.277Z","repository":{"id":356173775,"uuid":"1217612734","full_name":"deepgram/deepgram-audiocodes-bridge","owner":"deepgram","description":"Deepgram Voice Agent API + AudioCodes bridge SDK","archived":false,"fork":false,"pushed_at":"2026-05-06T22:35:28.000Z","size":118,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-07T00:27:41.032Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deepgram.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T03:54:54.000Z","updated_at":"2026-05-06T22:35:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/deepgram/deepgram-audiocodes-bridge","commit_stats":null,"previous_names":["deepgram/deepgram-audiocodes-bridge"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/deepgram/deepgram-audiocodes-bridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepgram%2Fdeepgram-audiocodes-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepgram%2Fdeepgram-audiocodes-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepgram%2Fdeepgram-audiocodes-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepgram%2Fdeepgram-audiocodes-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deepgram","download_url":"https://codeload.github.com/deepgram/deepgram-audiocodes-bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepgram%2Fdeepgram-audiocodes-bridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34625624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-21T20:30:47.329Z","updated_at":"2026-06-21T20:30:48.271Z","avatar_url":"https://github.com/deepgram.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# deepgram-audiocodes-bridge\n\nPython SDK that bridges the AudioCodes Bot API to the Deepgram Voice Agent API.\n\n## Overview\n\nRuns a WebSocket server implementing the AudioCodes Bot API protocol, opens and manages a Deepgram Voice Agent API connection per call, routes audio bidirectionally in real time, and emits typed higher-level events to application code.\n\n## Quick Start\n\n```python\nimport asyncio\nimport logging\n\nfrom deepgram_audiocodes_bridge import (\n    DeepgramBridge,\n    Session,\n    BridgeConfig,\n    SessionStartEvent,\n)\nfrom deepgram_audiocodes_bridge.types import (\n    DeepgramAgent,\n    DeepgramAgentConfig,\n    DeepgramListen,\n    DeepgramSpeak,\n    DeepgramSpeakProviderDeepgram,\n    DeepgramThink,\n    DeepgramThinkProviderOpenAI,\n    CartesiaSpeakVoice,\n    DeepgramSpeakProviderCartesia,\n    DeepgramThinkProviderAnthropic,\n    BridgeErrorEvent,\n    WarningEvent\n)\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(asctime)s [%(levelname)s] %(name)s: %(message)s\",\n)\n\ndeepgram_config: DeepgramAgentConfig = DeepgramAgentConfig(\n    # You don't need to configure or set the Audio input/output settings.\n    # It will be pulled dynamically from AudioCodes Bot API.\n\n    agent=DeepgramAgent(\n        listen=DeepgramListen(\n            provider={\"type\": \"deepgram\", \"model\": \"flux-general-en\"},\n        ),\n\n    # It's highly recommended to configure multiple LLM providers so you have a fallback mechanism\n    # https://developers.deepgram.com/docs/voice-agent-llm-models#using-multiple-llm-providers\n        think=[\n            DeepgramThink(\n                provider=DeepgramThinkProviderOpenAI(\n                    type=\"open_ai\",\n                    model=\"gpt-5.4-mini\",\n                ),\n                prompt=\"You are a helpful assistant.\",\n            ),\n            DeepgramThink(\n                provider=DeepgramThinkProviderAnthropic(\n                    type=\"anthropic\",\n                    model=\"claude-sonnet-4-6\"\n                )\n            )\n        ],\n\n    # It's also highly recommended to configure multiple providers for TTS, for the same reasons.\n    # https://developers.deepgram.com/docs/voice-agent-tts-models#using-multiple-tts-providers\n        speak=[\n            DeepgramSpeak(\n                provider=DeepgramSpeakProviderDeepgram(\n                    type=\"deepgram\",\n                    model=\"aura-2-helena-en\"\n                )\n            ),\n            DeepgramSpeak(\n                provider=DeepgramSpeakProviderCartesia(\n                    type=\"cartesia\",\n                    model_id=\"sonic-2\",\n                    voice=CartesiaSpeakVoice(\n                        mode=\"id\",\n                        id=\"\u003cyour-desired-voice-id\u003e\"\n                    # Sign up for a Cartesia account and then see the Voice Library for the Voice ID values.\n                    # https://play.cartesia.ai/voices\n                    )\n                )\n            )\n        ],\n        greeting=\"Hello from the otter slide!\"\n    ),\n)\n\n# The Deepgram___* config classes are helper types (autocomplete, etc.)\n# You can also just pass a generic JSON object if you prefer, for example:\n\"\"\"\ndeepgram_config = {\n  \"agent\": {\n    \"listen\": {\n        \"provider\": {\n            \"type\": \"deepgram\",\n            \"model\": \"flux-general-en\"\n        }\n    },\n    \"think\" {\n    ...[etc.]...\n    }\n  }\n}\n\"\"\"\n\n\n### This is how you initiate your bridge.\nbridge = DeepgramBridge(BridgeConfig(\n    deepgram_api_key=\"your-api-key\",\n    deepgram_config=deepgram_config,\n    ac_token=\"your-audiocodes-token\",  # Validates the Header Authentication configured in LiveHub / VAIC Bot Connection\n    port=8000,  # default is port 8081. You can change it here.\n))\n\n@bridge.on(\"session_start\")\nasync def on_start(session: Session, event: SessionStartEvent) -\u003e None:\n    print(f\"Call started: {session.session_id}\")\n    print(event)\n\n@bridge.on(\"error\")\nasync def on_error(session: Session, event: BridgeErrorEvent) -\u003e None:\n    print(event)\n\n@bridge.on(\"warning\")\nasync def on_warning(session: Session, event: WarningEvent) -\u003e None:\n    print(event)\n\n## Register your other bridge event handlers here!\n\nasyncio.run(bridge.run())\n```\n\n## Concurrency and the `Session` object\n\nThe bridge handles multiple concurrent calls out of the box — `bridge.run()` spawns a new asyncio task per inbound WebSocket; you don't need threads.\n\nEach call is represented by a `Session` object, passed as the first argument to every event handler. It carries the per-call state and exposes all the control methods (`session.send_agent_message(...)`, `session.transfer(...)`, `session.end_session()`, `session.play_url(...)`, `session.get_transcript()`, and so on). Concurrent calls each get their own `Session`, so there is no global state to collide.\n\n```python\n@bridge.on(\"conversation_text\")\nasync def on_text(session: Session, event: ConversationTextEvent) -\u003e None:\n    if \"transfer me\" in event.content.lower():\n        await session.transfer(\"sip:queue@example.com\")\n```\n\n### Embedding the bridge in an existing server\n\nIf you already run a WebSocket server (FastAPI, Starlette, or any framework built on the `websockets` library) and want the bridge to live as one route alongside health checks, admin APIs, and the rest of your app, use `Session.serve(socket, bridge)` directly. Construct the `DeepgramBridge` once at startup — it holds config and event handlers — and call `Session.serve` from your WebSocket handler after you've accepted the upgrade and performed your own authentication.\n\n```python\nbridge = DeepgramBridge(BridgeConfig(...))\n\n@bridge.on(\"session_start\")\nasync def on_start(session: Session, event: SessionStartEvent) -\u003e None:\n    ...\n\n# inside your framework's WebSocket route:\n# await Session.serve(websocket, bridge)\n```\n\n## Events\n\nRegister handlers with `@bridge.on(\"\u003cname\u003e\")`. The full set of events emitted by the bridge:\n\n| Event name                | Payload type               |\n| ------------------------- | -------------------------- |\n| `\"session_start\"`         | `SessionStartEvent`        |\n| `\"session_end\"`           | `SessionEndEvent`          |\n| `\"conversation_text\"`     | `ConversationTextEvent`    |\n| `\"user_started_speaking\"` | `UserStartedSpeakingEvent` |\n| `\"agent_thinking\"`        | `AgentThinkingEvent`       |\n| `\"agent_audio_done\"`      | `AgentAudioDoneEvent`      |\n| `\"function_call_request\"` | `FunctionCallRequestEvent` |\n| `\"prompt_updated\"`        | `PromptUpdatedEvent`       |\n| `\"speak_updated\"`         | `SpeakUpdatedEvent`        |\n| `\"think_updated\"`         | `ThinkUpdatedEvent`        |\n| `\"warning\"`               | `WarningEvent`             |\n| `\"activity\"`              | `InboundActivityEvent`     |\n| `\"error\"`                 | `BridgeErrorEvent`         |\n\n### Handling inbound AudioCodes activities\n\nEverything AudioCodes sends inside an `activities` envelope arrives on the `\"activity\"` event as an `InboundActivityEvent`. The raw activity dict is on `event.activity` — branch on `activity[\"name\"]` to handle specific events like DTMF digits or silence-timeout notifications.\n\nThe most common inbound event activities are:\n\n| Activity `name` | When it fires                                                        | Where the data lives                                           |\n| --------------- | -------------------------------------------------------------------- | -------------------------------------------------------------- |\n| `\"DTMF\"`        | Caller pressed digits on their phone keypad                          | `activity[\"value\"]` — a string like `\"1\"`, `\"42#\"`, `\"*\"`      |\n| `\"noUserInput\"` | VAIC's silence timer expired without caller speech (see note below)  | `activity[\"value\"]` — int count of how many times it has fired |\n| `\"start\"`       | VAIC sends this once per session; already handled by `session_start` | —                                                              |\n| `\"hangup\"`      | VAIC sends this at call end; already handled by `session_end`        | `activity[\"activityParams\"][\"hangupReason\"]`                   |\n\n\u003e **`noUserInput` requires VAIC-side configuration.** The AudioCodes gateway only forwards it to the bot when `sendEventsToBot` is set to include `noUserInput` on the VAIC bot connection. If it isn't configured, you will never see this activity — this can be helpful because the Deepgram Voice Agent API has no context as to silence and does not keep track of it natively.\n\n```python\nfrom deepgram_audiocodes_bridge import InboundActivityEvent\n\n@bridge.on(\"activity\")\nasync def on_activity(event: InboundActivityEvent) -\u003e None:\n    name = event.activity.get(\"name\")\n\n    if name == \"DTMF\":\n        digits = str(event.activity.get(\"value\", \"\"))\n        print(f\"Caller pressed: {digits}\")\n        # e.g. route to a menu handler, append to an account-number buffer, etc.\n\n    elif name == \"noUserInput\":\n        count = event.activity.get(\"value\", 0)\n        print(f\"Silence timeout fired (#{count})\")\n        # e.g. reprompt the caller, or hang up after N timeouts.\n\n    else:\n        # Unknown / future event — log it so you notice new activity types.\n        print(f\"unhandled activity: {name} {event.activity}\")\n```\n\nFor the complete list of activities VAIC can send, see the \"Receiving notifications\" section under the [\"Bot integration\" AudioCodes docs](https://techdocs.audiocodes.com/voice-ai-connect/#VAIG_Combined/bot-integration.htm?TocPath=Bot%2520integration%257C_____0).\n\n## Methods\n\nEvery method below is called on the per-call `Session` object. Inside an event handler the session arrives as the first argument:\n\n```python\n@bridge.on(\"conversation_text\")\nasync def on_text(session: Session, event: ConversationTextEvent) -\u003e None:\n    if \"agent\" in event.content.lower():\n        await session.send_agent_message(\"I'm here, how can I help?\")\n```\n\nAll control methods are coroutines — `await` them. Calling one after the session has ended raises `RuntimeError`; gate on `session.is_active` if you've handed the session off to a background task.\n\n### Deepgram Voice Agent control\n\nMethods that drive the agent itself — inject turns, swap providers, return function-call results. Each one wraps a [Voice Agent client message](https://developers.deepgram.com/docs/voice-agent-inputs).\n\n| Method                                        | What it does                                                   | Wire message           |\n| --------------------------------------------- | -------------------------------------------------------------- | ---------------------- |\n| `send_agent_message(content)`                 | Force the agent to immediately speak `content`.                | `InjectAgentMessage`   |\n| `send_user_message(content)`                  | Inject text as if the user had spoken it.                      | `InjectUserMessage`    |\n| `update_prompt(prompt)`                       | Append to the agent's system prompt mid-conversation.          | `UpdatePrompt`         |\n| `update_speak(speak)`                         | Swap the TTS provider/model mid-conversation.                  | `UpdateSpeak`          |\n| `update_think(think)`                         | Replace the entire Think (LLM) configuration mid-conversation. | `UpdateThink`          |\n| `respond_to_function_call(id, name, content)` | Return the result of a client-side function call to Deepgram.  | `FunctionCallResponse` |\n\n```python\n# Push the agent to speak something specific (e.g. after a long DB lookup).\nawait session.send_agent_message(\"Thanks for waiting — I found your account.\")\n\n# Swap to a different LLM mid-call (e.g. on user request, or as a fallback).\nawait session.update_think({\n    \"provider\": {\"type\": \"anthropic\", \"model\": \"claude-sonnet-4-6\"},\n    \"prompt\": \"You are a billing specialist.\"\n})\n\n# Handle a function call from the agent.\n@bridge.on(\"function_call_request\")\nasync def on_function_call(session: Session, event: FunctionCallRequestEvent) -\u003e None:\n    for fc in event.functions:\n        if fc.name == \"get_order_status\":\n            result = await lookup_order(fc.arguments)\n            await session.respond_to_function_call(fc.id, fc.name, result)\n```\n\n### AudioCodes Bot API control\n\nMethods that drive the telephony layer — transfers, hangups, DTMF, audio prompts. Each one wraps an [AudioCodes outbound activity](https://techdocs.audiocodes.com/voice-ai-connect/#VAIG_Combined/sending-activities.htm?TocPath=Bot%2520integration%257CControlling%2520the%2520call%257C_____1).\n\n| Method                                                                      | What it does                                                                                                                                                                           |\n| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `transfer(destination, *, handover_reason=None, transfer_sip_headers=None)` | SIP REFER transfer to `destination`. Emits `session_end` with reason `'transfer'`.                                                                                                     |\n| `end_session(reason=None)`                                                  | Send `hangup` and close both sockets. Emits `session_end` with reason `'ended'`.                                                                                                       |\n| `play_url(url, *, options=None)`                                            | Play a pre-recorded audio file via VAIC's own audio engine.                                                                                                                            |\n| `send_dtmf(digits, *, options=None)`                                        | Send DTMF digits downstream via VAIC.                                                                                                                                                  |\n| `send_meta_data(data)`                                                      | Push arbitrary metadata to VAIC.                                                                                                                                                       |\n| `abort_prompts()`                                                           | Cancel any VAIC-managed prompts currently playing.                                                                                                                                     |\n| `expect_another_bot_message()`                                              | Tell VAIC to keep the turn open — another bot utterance is coming.                                                                                                                     |\n| `apply_config(params)`                                                      | Dynamically change session-level configuration mid-call.                                                                                                                               |\n| `send_activity(activity)`                                                   | Generic escape hatch — accepts a single activity dict or a list and wraps it in an `activities` envelope. Use this for any AudioCodes event the SDK doesn't expose a typed helper for. |\n\n```python\n# Warm transfer to a queue.\nawait session.transfer(\n    \"sip:queue@example.com\",\n    handover_reason=\"caller asked for a human\",\n)\n\n# End the call from the bot side.\nawait session.end_session()\n\n# Play a pre-recorded prompt instead of TTS.\nawait session.play_url(\"https://cdn.example.com/hold-music.wav\")\n\n# Send DTMF (e.g. navigating an IVR after transfer).\nawait session.send_dtmf(\"1234#\")\n\n# Generic activity for anything the SDK doesn't wrap.\nawait session.send_activity({\n    \"type\": \"event\",\n    \"name\": \"customEvent\",\n    \"activityParams\": {\"foo\": \"bar\"},\n})\n```\n\n### Call recording\n\nRecording is handled by VAIC / the SBC, not by this SDK. These methods just toggle it on and off.\n\n| Method                              | What it does               |\n| ----------------------------------- | -------------------------- |\n| `start_call_recording(params=None)` | Start recording.           |\n| `stop_call_recording()`             | Stop recording.            |\n| `pause_call_recording()`            | Pause recording.           |\n| `resume_call_recording()`           | Resume a paused recording. |\n\n```python\n@bridge.on(\"session_start\")\nasync def on_start(session: Session, event: SessionStartEvent) -\u003e None:\n    await session.start_call_recording({\"recordingName\": event.session_id})\n```\n\n### Read-only state\n\nProperties and accessors on `Session`. Safe to read at any time, including inside a `session_end` handler.\n\n| Accessor           | Returns                                                                                       |\n| ------------------ | --------------------------------------------------------------------------------------------- |\n| `session_id`       | `str` — unique ID assigned by the bridge for this call.                                       |\n| `conversation_id`  | `str \\| None` — AudioCodes conversation ID from `session.initiate`.                           |\n| `media_format`     | `AudioCodesMediaFormat \\| None` — format negotiated in `session.accepted`.                    |\n| `is_active`        | `bool` — `True` while the session is in the `Active` state.                                   |\n| `get_transcript()` | `list[ConversationTextEvent]` — every `conversation_text` event accumulated so far, in order. |\n\n```python\nasync def archive_call(session_id, conversation_id, transcript):\n    # Do something here\n    # For example, POST to external CRM\n    pass\n\n@bridge.on(\"session_end\")\nasync def on_end(session: Session, event: SessionEndEvent) -\u003e None:\n    transcript = session.get_transcript()\n    await archive_call(session.session_id, session.conversation_id, transcript)\n```\n\n## Authentication\n\nAudioCodes LiveHub / VAIC Bot Connections support three authentication modes on the upgrade request to your bridge: **No Auth**, **Permanent Token** (shared secret), and **OAuth 2.0** (or any custom callback). Pick one on the LiveHub side and match it on the bridge side via `BridgeConfig`.\n\nSee [`examples/03_auth`](./examples/03_auth) for a full walkthrough of all three modes, when to use each, and copy-pasteable bridge code.\n\n## A note on Barge-In\n\nThe Deepgram Voice Agent API has support for barge in and interruptions, and this SDK handles that natively (when a UserStartedSpeaking event is received from Deepgram, the SDK tells VAIC to stop playing the TTS audio). However, VAIC has an option to disable it if you want (it will ignore the stop message).\n\nIn VAIC / LiveHub, simply toggle on or off the \"Barge-in\" setting in the Bot Connection. Note, the default when creating a Bot Connection is for this setting to be off. For LiveHub, to toggle this setting on, and allow the user to interrupt the agent, see here - [Edit your Bot Connection](https://techdocs.audiocodes.com/livehub/#LiveHub/Editing%20your%20bot.htm?TocPath=Bot%2520connectivity%257C_____6)\n\n## Local Development / Testing\n\n```bash\ngit clone \u003cssh-or-https-url-for-this-repo\u003e\ncd audiocodes-deepgram-bridge\npython -m venv .venv\nsource .venv/bin/activate\npip install -e .\n```\n\n## Installation\n\n```bash\npip install git+https://github.com/deepgram/deepgram-audiocodes-bridge.git\n```\n\n## Environment Variables\n\n`.env.example` shows some example environment variables.\n\n- `DEEPGRAM_API_KEY` is the only requirement for using this SDK.\n- `AC_TOKEN` is optional and only needed if you are using Permanent Token authentication in the LiveHub / VAIC bot connection. It must match the value configured in LiveHub / VAIC.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeepgram%2Fdeepgram-audiocodes-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeepgram%2Fdeepgram-audiocodes-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeepgram%2Fdeepgram-audiocodes-bridge/lists"}