-
Notifications
You must be signed in to change notification settings - Fork 262
feat: migrate to Agents SDK (useSession hook) #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: migrate to Agents SDK (useSession hook) #298
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
5087f3f to
71fac98
Compare
71fac98 to
2b9eeb9
Compare
2b9eeb9 to
7f77240
Compare
b595bdf to
b6ab0a3
Compare
b6ab0a3 to
abca768
Compare
abca768 to
37e53d8
Compare
37e53d8 to
4d1da91
Compare
4d1da91 to
33eba9e
Compare
33eba9e to
2a528dc
Compare
2a528dc to
554bbba
Compare
| const session = useSession(tokenSource, sessionOptions); | ||
|
|
||
| const value = useMemo( | ||
| () => ({ | ||
| isSessionActive, | ||
| startSession: (startSession = true) => { | ||
| setIsSessionActive(true); | ||
| if (startSession) { | ||
| session.start(); | ||
| } | ||
| }, | ||
| endSession: (endSession = true) => { | ||
| setIsSessionActive(false); | ||
| if (endSession) { | ||
| session.end(); | ||
| } | ||
| }, | ||
| }), | ||
| // session object is not a stable reference | ||
| // TODO: add session object to dependencies | ||
| /* eslint-disable-next-line react-hooks/exhaustive-deps */ | ||
| [isSessionActive] | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love to get your thoughts on this,
I want control over the enter and exit transitions, delaying the ending of the session until the transition has complete, to prevent the video avatar disappearing before the exit transition has completed.
Also,
I'm noticing session isn't a stable reference (even if I pass in stable references) so to prevent it triggering context re-renders I've left it out of the dependency array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want control over the enter and exit transitions, delaying the ending of the session until the transition has complete, to prevent the video avatar disappearing before the exit transition has completed.
I'm not sure that I understand where this is being used - can you point me to the call sites where startSession / endSession is being called with that boolean parameter? I did see this which looks like it might be related to the problem you are mentioning but it doesn't seem to be using this app session abstraction.
I'm noticing session isn't a stable reference (even if I pass in stable references) so to prevent it triggering context re-renders I've left it out of the dependency array
Huh, that is definitely not intentional - it should be. I'll look into this, thanks for surfacing it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thomasyuill-livekit FYI that I think I figured out the useSession return stability issue. I've got a pull request fixing it here: livekit/components-js#1230
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you point me to the call sites where startSession / endSession
@1egoman
it's being handled here
https://github.com/livekit-examples/agent-starter-react/pull/298/files#diff-b4576c1322a558b6186c64fd05018928d9815d04717dff4ecc33d36a96d56aefR38-R48
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That link you posted above links me to a section of the code where I see startSession is being defined (const { isSessionActive, startSession } = useAppSession();) but not where it is used.
Scanning through the diff, I see it is used here, where it is being passed as onStartCall into MotionWelcomeView, but onStartCall isn't being called with parameters (from here) so I'm not sure how this boolean parameter is being used to conditionally call session.start() / session.end().
Am I missing something? Sorry if so, just want to make sure I'm properly able to map this out in my head.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the piece you're missing (onDisconnect) is found here
By default, startSession and endSession manage controlling the app state isSessionActive and the session state session.start()/session.end()
if you pass false into startSession or endSession, you are opting to control session state manually.
In session-view.tsx we create an handleDisconnect callback that opts out of the default behaviour, requiring the manual calling of session.end() that happens in the ViewContoller, after the exit transition here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some additional comments to the useAppSession context value to document this behaviour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see! Thank you for that additional context.
I will rephrase my understanding of the problem, just to make sure I have it right - clicking disconnect should update the application's state so that an animation can occur, and once the animation completes then you want to call session.end() to actually end the session. This is important so that the livekit generated video streams that are active stay active throughout the duration of the animation.
Assuming I have that right - my initial take is this is going to be inevitably fairly nuanced given there's a series of steps that the system must progress through asynchronously. It's possible that the agent sdk could do more to help with this, and I'll throw a few ideas below but I'm not sure if the agent sdk should really have to care about this given it's a problem fairly outside of its responsibility. I could be convinced though, because I could imagine this would be a fairly common rough edge.
A few potential options that come to mind in the collapsed section below:
1. Continue to push handling this down to downstream client applications
IMO if this is what is decided, I think the thing that makes what you currently have fairly confusing to me at least is that startSession / endSession currently do multiple things. I think breaking this up into two methods, making it clear one should be called "when animation starts" and one called "when animation ends" would help a lot.
2. Some sort of "keep the session running while this is rendered" component
Update the agents sdk to add some sort of component that can be rendered to keep the session "stuck" open while the animation is running. Then when react-motion or a similar tool finished the animation and unmounts the dom, the component cleans up some effect, which causes the session to really end.
Rough example:
const session = useSession(/* ... */);
// Somewhere one or multiple of these could be rendered (ie, alongside the avatar):
<KeepSessionConnected session={session} />
// Then later on:
const promise = session.end();
// session.status is now "disconnecting"
// ( ... animation occurs ... )
// session.status is STILL "disconnecting"
// Finally, the `KeepSessionConnected` component is unmounted
// session.status is STILL "disconnected"
// `promise` resolves
Important considerations:
- There probably would need to be a new
disconnecting state or something similar that can be used to make it clear that the disconnect is now async. That being said, this probably should have existed already since room.disconnect is inherently async.
- How does this interact with the
failed / other future non-disconnect terminal states? ie, would disconnecting after a session has failed also go to disconnecting or would this behavior only apply for happy path scenarios? IMO, I think it should always go through disconnecting (and maybe that is just not a great name for what this actually is).
3. Some sort of "before end" type event
Update the agents sdk to add some sort of async callback to session.end, which could return a promise that could allow you to wait for the animation to complete before continuing. So something like:
const session = useSession(/* ... */);
// Later on:
// session.state is "connected"
session.end(async () => {
// session.state is "disconnecting"
// wait for animation and resolve once complete
});
// session.state is "disconnected"
Important considerations: Same as 2
1. Continue to push handling this down to downstream client applications
IMO if this is what is decided, I think the thing that makes what you currently have fairly confusing to me at least is that startSession / endSession currently do multiple things. I think breaking this up into two methods, making it clear one should be called "when animation starts" and one called "when animation ends" would help a lot.
2. Some sort of "keep the session running while this is rendered" component
Update the agents sdk to add some sort of component that can be rendered to keep the session "stuck" open while the animation is running. Then when react-motion or a similar tool finished the animation and unmounts the dom, the component cleans up some effect, which causes the session to really end.
Rough example:
const session = useSession(/* ... */);
// Somewhere one or multiple of these could be rendered (ie, alongside the avatar):
<KeepSessionConnected session={session} />
// Then later on:
const promise = session.end();
// session.status is now "disconnecting"
// ( ... animation occurs ... )
// session.status is STILL "disconnecting"
// Finally, the `KeepSessionConnected` component is unmounted
// session.status is STILL "disconnected"
// `promise` resolvesImportant considerations:
- There probably would need to be a new
disconnectingstate or something similar that can be used to make it clear that the disconnect is now async. That being said, this probably should have existed already sinceroom.disconnectis inherently async. - How does this interact with the
failed/ other future non-disconnectterminal states? ie, would disconnecting after a session hasfailedalso go todisconnectingor would this behavior only apply for happy path scenarios? IMO, I think it should always go throughdisconnecting(and maybe that is just not a great name for what this actually is).
3. Some sort of "before end" type event
Update the agents sdk to add some sort of async callback to session.end, which could return a promise that could allow you to wait for the animation to complete before continuing. So something like:
const session = useSession(/* ... */);
// Later on:
// session.state is "connected"
session.end(async () => {
// session.state is "disconnecting"
// wait for animation and resolve once complete
});
// session.state is "disconnected"Important considerations: Same as 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed this with @lukasIO out of band - it sounds like he's also of a similar mind that this isn't a responsibility that the agents sdk should take on.
I think (at least to me) what makes the current abstractions hard to reason about is that the animation state is so coupled with the session state. Is there anything you can do to decouple this from useSession's state?
Also, I think the name AppSession is sufficiently close to Session to also make things confusing. If you need to keep this context around, maybe there's something else you could call it to make it clear that it's not necessarily directly related to the existing useSession hook / SessionProvider component.
This function is needed for use in `components-js` so I can fix an issue reported by Thom [here](livekit-examples/agent-starter-react#298 (comment)) related to the useSession return not being a stable reference due to fetch options changing every render.
554bbba to
70d9de0
Compare
7d0f187 to
5061173
Compare
| messageOrigin={messageOrigin} | ||
| hasBeenEdited={hasBeenEdited} | ||
| {...MESSAGE_MOTION_PROPS} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: is it worth continuing to pass through this hasBeenEdited prop when the ReceivedMessage is the subtype of ReceivedChatMessage?
| key="welcome" | ||
| {...VIEW_MOTION_PROPS} | ||
| startButtonText={appConfig.startButtonText} | ||
| startButtonText={appConfig?.startButtonText ?? ''} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: is appConfig ever able to be unset? It looks like in the props for the component appConfig should always be passed so I think this is redundant can can be just made into a regular property access?
| startButtonText={appConfig?.startButtonText ?? ''} | |
| startButtonText={appConfig.startButtonText} |
| }); | ||
| } | ||
|
|
||
| return TokenSource.endpoint('/api/connection-details'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I'm glad you were able to make /api/connection-details work with the endpoint abstraction!
| const sessionOptions = useMemo( | ||
| () => (appConfig.agentName ? { agentName: appConfig.agentName } : undefined), | ||
| [appConfig] | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Just a FYI that this useMemo shouldn't be required once this is merged, which I plan on doing shortly. I'll post an update here once it is merged.
Migrate to LiveKit React Agent SDK
This PR integrates the LiveKit React Agent SDK (@livekit/components-react@0.0.0-20251021151641), replacing custom implementations with SDK-provided hooks and providers.
Key Changes
Session Management
Hooks & State
Type Updates
Component Updates
Other