Skip to content

Buzz Provider

BuzzProvider

BuzzProvider(source)

Bases: BuzzRelayProvider

Outbound Buzz delivery via a shared :class:BuzzRelaySource client.

The provider delegates every send to the buzzkit.BuzzClient owned by the paired source. It does not manage the connection lifecycle — that stays with the source. Sends use the HTTP bridge, so they succeed even when the inbound WebSocket is mid-reconnect.

send async

send(event, to)

Post event to Buzz channel to (a relay channel UUID).

channel_data.thread_id (the thread-root Nostr event id, as set by the inbound parser) threads the message as a NIP-10 reply — the same provider-native contract Discord and Teams use.

send_reaction async

send_reaction(target_event_id, emoji)

React to a relay event (kind 7) through the shared client.

remove_reaction async

remove_reaction(reaction_event_id)

Retract our own reaction (kind 5) through the shared client.

BuzzConfig

Bases: BaseModel

Buzz relay agent configuration.

private_key is the agent's Nostr secret (nsec… or hex); it signs the agent's events and authenticates it to the relay (NIP-42/98).

MockBuzzProvider

MockBuzzProvider()

Bases: BuzzRelayProvider

Records sent messages for verification in tests.

Carries no buzzkit dependency and no relay client, so it can drive the delivery path without a live connection.

BuzzRelaySource

BuzzRelaySource(config, channel_id='buzz', *, relay_channel_id, parser=None, kinds=None, on_event=None)

Bases: BaseSourceProvider

Persistent Buzz relay connection emitting one channel's messages.

Owns the :class:buzzkit.BuzzClient and exposes it via :attr:client so the paired provider can send through the same identity. Subscribes to a single relay channel (relay_channel_id); register one source per Buzz channel and bind each to its RoomKit room.

kinds selects the Nostr event kinds to subscribe to (default: chat messages, kind 9). Pass other kinds — e.g. huddle announcements, kind 48100 — together with a parser that knows how to convert them; the default parser only understands text messages.

on_event surfaces reaction lifecycle events (kind 7 add, kind 5 remove) as normalised dicts, outside the message pipeline — matching how Discord and WhatsApp-personal handle reactions. Providing it widens the default subscription to kinds 9, 7 and 5; requires a relay that scopes reactions to their target's channel (buzzkit>=0.2.0).

client property

client

Expose the underlying BuzzClient for outbound use.

stop async

stop()

Stop receiving and close the relay connection.

parse_buzz_event

parse_buzz_event(event, channel_id, *, own_pubkey=None, ignore_own=True)

Convert a Nostr event dict into an :class:InboundMessage.

Duck-typed on a plain dict so it can be unit-tested without a relay. Returns None to skip the agent's own events (echo guard) and events with no text content.

huddle_announcement_parser

huddle_announcement_parser(channel_id, *, started_after=None)

Parser for huddle announcements (kind 48100).

Emits one :class:InboundMessage per announcement with the ephemeral huddle id in metadata["ephemeral_channel_id"]. Subscribe the source with kinds=[KIND_HUDDLE_STARTED].

started_after (unix seconds) drops announcements replayed from relay history — the subscription replays recent events before EOSE, and a restarted agent must not chase long-dead huddles.

Voice (huddles)

BuzzHuddleBackend

BuzzHuddleBackend(*, silence_fill=True, provider_input_rate=16000, provider_output_rate=24000, end_when_alone=True, empty_huddle_grace=90.0)

Bases: VoiceBackend

Realtime voice transport backed by a Buzz huddle.

The connection given to :meth:accept must be a connected buzzkit.HuddleClient. The backend owns it from that point on: it runs the client's event loop, and :meth:disconnect leaves the huddle and closes the socket.

Outbound pacing (one Opus frame per 20 ms) and the wire protocol live in buzzkit; this class only moves PCM bytes and session state.

silence_fill (default on) streams silence frames to the pipeline whenever no huddle audio is arriving — huddle senders go quiet between utterances (Opus DTX), but a realtime provider's server VAD needs to hear the post-speech silence to close the user's turn, exactly as it would from a continuously open microphone.

provider_input_rate / provider_output_rate are the realtime provider's PCM rates (defaults match Gemini Live: 16 kHz in, 24 kHz out). The backend resamples huddle audio (48 kHz) to/from those rates internally — leave the channel's transport_sample_rate unset.

end_when_alone (default on) ends the session when the last remote peer leaves the huddle. The relay keeps a huddle alive while ANY member is connected — this agent included — so without it the huddle and the provider session run forever. empty_huddle_grace is how long to wait for a first peer in a huddle that is empty at join time (the announcement can precede the creator's audio socket).

accept async

accept(session, connection)

Bind a connected buzzkit.HuddleClient to this session.

send_audio async

send_audio(session, audio)

Resample provider PCM to 48 kHz and hand it to the outbound pacer.

interrupt

interrupt(session)

Drop queued + in-flight outbound audio (barge-in).

end_of_response

end_of_response(session)

Signal the pacer that the current response is fully delivered.

BuzzHuddleWatcher

BuzzHuddleWatcher(kit, *, voice_channel, config, parent_channel_id, room_id, participant_id='buzz-agent', events_channel_id='buzz-huddle-events', join_attempts=3, client_factory=None)

Bridge every huddle announced on a Buzz channel to a voice channel.

Owns the whole announcement→call lifecycle so an application only builds its voice channel::

voice = RealtimeVoiceChannel("buzz-voice", provider=..., transport=BuzzHuddleBackend())
kit.register_channel(voice)
await kit.create_room(room_id="huddles")
await kit.attach_channel("huddles", "buzz-voice")

watcher = BuzzHuddleWatcher(
    kit,
    voice_channel=voice,
    config=BuzzConfig(relay_url=..., private_key=...),
    parent_channel_id=parent_uuid,
    room_id="huddles",
)
await watcher.start()

start() subscribes to huddle announcements (kind 48100) on the parent channel through a :class:~roomkit.sources.buzz.BuzzRelaySource attached with auto_restart=True — relay reconnection is the framework's job — and dials every announced huddle. The transport ends each call on its own (end_when_alone, or the relay dropping the socket); the watcher reads session.metadata["buzz_end_reason"] to choose between rejoining the same huddle (connection loss) and waiting for the next one (call over).

bridge(huddle_id) dials one known huddle directly (no watching). Calls are bridged one at a time; announcements that arrive mid-call are ignored. client_factory is injectable for tests.

start async

start()

Register the announcement channel + hook and attach the source.

bridge async

bridge(huddle_id)

Dial one huddle and keep the call bridged until it is over.