You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We need a public way to add a user-role ChatMessage to a running AgentSession and have it behave like a normal conversation item, including emitting conversation_item_added.
Right now, the only way I can find to do this correctly is to mirror what LiveKit does internally:
So if an application only appends to agent._chat_ctx, the LLM context may contain the message, but the session context doesn't, and neither do any conversation_item_added listeners.
Subscribing to conversation_item_added is public API. There just doesn't seem to be a corresponding public API for adding an item and causing that event to fire.
Use cases
We're running outbound SIP agents, where legitimate user input can sometimes arrive outside the normal VAD/STT turn pipeline.
DTMF-derived input. We collect things like dates and account numbers via keypad. In some cases, that input should appear in conversation history as user input.
Out-of-band text. For example, SIP INFO or caller data pushed in by the backend that should become part of the conversation record.
What we're doing today
We currently have two defensive injection sites using the private APIs above.
Both use two layers of deduplication because there's a race where LiveKit may have inserted the message itself before our fallback runs:
This works, but it makes upgrading livekit-agents unnecessarily risky. We pin the package exactly and have tripwire tests around _conversation_item_added, since a private API change here could otherwise show up as a mid-call production failure.
It also means we can get blocked from upgrading for unrelated fixes because we're depending on an implementation detail.
Proposed API
I'd be happy with either of these shapes, and happy to submit a PR for whichever fits the project better.
Minimal API
Expose the existing behavior publicly, something like:
session.add_conversation_item(message)
This would add the item to the appropriate context(s) and emit conversation_item_added, ideally idempotently by message ID.
This seems like the smallest change and maps pretty directly to what the framework already does internally.
Higher-level API
Alternatively:
session.add_user_message(text, ...)
This could construct the ChatMessage, add it to the active agent's context, update the session context, dedupe, and emit the event.
It's more convenient, but also a little more opinionated.
Alternatives we've looked at
session.say(..., add_to_chat_ctx=True) solves the equivalent problem for agent speech, but there doesn't appear to be a user-role equivalent.
Agent.update_chat_ctx() replaces the whole context and updates the realtime session, but it doesn't emit conversation_item_added, so listeners doing things like transcript capture won't see the inserted message.
Feature Type
Would make my life easier
Feature Description
Summary
We need a public way to add a user-role
ChatMessageto a runningAgentSessionand have it behave like a normal conversation item, including emittingconversation_item_added.Right now, the only way I can find to do this correctly is to mirror what LiveKit does internally:
This is also exactly what LiveKit does internally. For example: the session-closing path in
agent_activity.py:2454–2455.The problem is that both of these are private API.
Why appending to
agent._chat_ctxisn't enoughAgentSession._conversation_item_addeddoes more than emit the event. It also inserts the item into the session-level_chat_ctxand logs it.So if an application only appends to
agent._chat_ctx, the LLM context may contain the message, but the session context doesn't, and neither do anyconversation_item_addedlisteners.Subscribing to
conversation_item_addedis public API. There just doesn't seem to be a corresponding public API for adding an item and causing that event to fire.Use cases
We're running outbound SIP agents, where legitimate user input can sometimes arrive outside the normal VAD/STT turn pipeline.
Late STT finals. A final transcript can arrive after the turn has already committed. Turn commits with end_of_turn_probability: null when STT transcript arrives after turn flush, splitting one utterance into two turns #6504 appears to cover part of this race upstream. When this happens, we need to insert the user text ourselves so the LLM context and transcript capture stay consistent.
DTMF-derived input. We collect things like dates and account numbers via keypad. In some cases, that input should appear in conversation history as user input.
Out-of-band text. For example, SIP INFO or caller data pushed in by the backend that should become part of the conversation record.
What we're doing today
We currently have two defensive injection sites using the private APIs above.
Both use two layers of deduplication because there's a race where LiveKit may have inserted the message itself before our fallback runs:
This works, but it makes upgrading
livekit-agentsunnecessarily risky. We pin the package exactly and have tripwire tests around_conversation_item_added, since a private API change here could otherwise show up as a mid-call production failure.It also means we can get blocked from upgrading for unrelated fixes because we're depending on an implementation detail.
Proposed API
I'd be happy with either of these shapes, and happy to submit a PR for whichever fits the project better.
Minimal API
Expose the existing behavior publicly, something like:
This would add the item to the appropriate context(s) and emit
conversation_item_added, ideally idempotently by message ID.This seems like the smallest change and maps pretty directly to what the framework already does internally.
Higher-level API
Alternatively:
This could construct the
ChatMessage, add it to the active agent's context, update the session context, dedupe, and emit the event.It's more convenient, but also a little more opinionated.
Alternatives we've looked at
session.say(..., add_to_chat_ctx=True)solves the equivalent problem for agent speech, but there doesn't appear to be a user-role equivalent.Agent.update_chat_ctx()replaces the whole context and updates the realtime session, but it doesn't emitconversation_item_added, so listeners doing things like transcript capture won't see the inserted message.That leaves the private:
pair as the only option we've found.
Environment
livekit-agents==1.7.1livekit-agents@1.7.1commit (6e3af31)Happy to open a PR for either API shape.
Workarounds / Alternatives
No response
Additional Context
No response