Skip to content

LivekitObserve.export() raises TypeError by awaiting a dict (sync function awaited) #4

Description

@0xNuru

Bug: LivekitObserve.export() raises TypeError: object dict can't be used in 'await' expression

Description

Calling await soundflare.export(session_id) during a LiveKit agent shutdown callback raises:

TypeError: object dict can't be used in 'await' expression

Root Cause

In soundflare/__init__.py (line ~815), the export() method is defined as async def and uses await on send_session_to_soundflare():

async def export(self, session_id, recording_url=""):
    extra_data = {}
    if hasattr(self, 'spans_data') and self.spans_data:
        extra_data['telemetry_spans'] = self.spans_data

    return await send_session_to_soundflare(  # <-- BUG: this is a sync function that returns a dict
        session_id,
        recording_url,
        apikey=self.apikey,
        api_url=self.host_url,
    )

However, send_session_to_soundflare() in soundflare/soundflare.py (line ~634) is a synchronous function that returns a dict. You cannot await a plain dict.

Suggested Fix

import asyncio

async def export(self, session_id, recording_url=""):
    """Export session data to Soundflare"""
    extra_data = {}
    if hasattr(self, 'spans_data') and self.spans_data:
        extra_data['telemetry_spans'] = self.spans_data

    return await asyncio.to_thread(
        send_session_to_soundflare,
        session_id,
        recording_url,
        apikey=self.apikey,
        api_url=self.host_url,
        **extra_data,  # <-- also forward the telemetry spans
    )

Using asyncio.to_thread() properly offloads the blocking HTTP call to a thread pool, keeping the event loop responsive during agent shutdown.

Reproduction

from soundflare import LivekitObserve

soundflare = LivekitObserve(
    agent_id="test-agent",
    apikey="your-key",
)

# In a LiveKit agent entrypoint:
session_id = soundflare.start_session(session=session)

async def on_shutdown():
    await soundflare.export(session_id)  # <-- TypeError here

ctx.add_shutdown_callback(on_shutdown)

Current Workaround

Bypassing export() and calling the underlying sync function directly:

from soundflare.soundflare import send_session_to_soundflare
async def export_soundflare_data():
    await asyncio.to_thread(
        send_session_to_soundflare,
        session_id,
        apikey=soundflare.apikey,
        api_url=soundflare.host_url,
    )

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions