adk-go icon indicating copy to clipboard operation
adk-go copied to clipboard

Session.State().Get/Set do not appear to work

Open verdverm opened this issue 1 month ago • 4 comments

I tried the following basic get/set with both in-memory and database, neither work as expected and have the same output, notably they both show the state value just after being set, but it never seems to retain after the function return

[!CAUTION] none of the state prefixes appear to work

Not sure if I'm doing something wrong, or there is a copy in the implementation which means this never makes it back to storage...?

func sessionGetState(r *runtime.Runtime, c *runtime.Client, m *runtime.Message) {

	var s StatePayload
	if err := json.Unmarshal(m.Payload, &s); err != nil {
		log.Printf("Error unmarshaling 'session.getState' payload: %v", err)
		return
	}

	// lookup session
	resp, err := r.S.Get(r.Ctx, &session.GetRequest{
		AppName:   r.AppName,
		UserID:    c.User,
		SessionID: s.Sid,
	})
	if err != nil {
		...
	}

	v, err := resp.Session.State().Get(s.Key)
	if err != nil {
		...
	}
	s.Val = v

	// fmt.Println("mailing sessions", payload)
	c.Mail("session.getState.resp", s)
}

func sessionPutState(r *runtime.Runtime, c *runtime.Client, m *runtime.Message) {
	var s StatePayload
	if err := json.Unmarshal(m.Payload, &s); err != nil {
		log.Printf("Error unmarshaling 'session.getState' payload: %v", err)
		return
	}

	// lookup session
	resp, err := r.S.Get(r.Ctx, &session.GetRequest{
		AppName:   r.AppName,
		UserID:    c.User,
		SessionID: s.Sid,
	})
	if err != nil {
		...
	}

	err = resp.Session.State().Set(s.Key, s.Val)
	if err != nil {
		...
	}

	fmt.Println("session.state", maps.Collect(resp.Session.State().All()))
}
Received message type: session.state.put
sessionPutState {28b3c4f4-d140-4409-ad0c-56336ad4971c title testing}
State Set 28b3c4f4-d140-4409-ad0c-56336ad4971c title testing
session.state map[title:testing]
Received message type: session.get
sessionGet {"id":"28b3c4f4-d140-4409-ad0c-56336ad4971c"}
Received message type: session.state.put
sessionPutState {28b3c4f4-d140-4409-ad0c-56336ad4971c foo bar}
State Set 28b3c4f4-d140-4409-ad0c-56336ad4971c foo bar
session.state map[foo:bar]
Received message type: session.state.get
Error: session.getState.getState: state key does not exist
Received message type: session.state.put
sessionPutState {28b3c4f4-d140-4409-ad0c-56336ad4971c user:name verdverm}
State Set 28b3c4f4-d140-4409-ad0c-56336ad4971c user:name verdverm
session.state map[user:name:verdverm]
Received message type: session.state.get
Error: session.getState.getState: state key does not exist

verdverm avatar Nov 20 '25 04:11 verdverm

@verdverm Seems that you're updating the state in the copied memory session. Guess you should use the Create API to update the updated session back to the session service.

git-hulk avatar Nov 20 '25 05:11 git-hulk

Create would be a bad name for something that updates a session, Put or Set would be more appropriate, like the State interface. Either way, it doesn't seem to work or is overwriting it again before I can inspect

025/11/19 22:10:52 /Users/tony/go/pkg/mod/google.golang.org/[email protected]/session/database/service.go:124 UNIQUE constraint failed: sessions.app_name, sessions.user_id, sessions.id
[0.191ms] [rows:0] INSERT INTO `sessions` (`app_name`,`user_id`,`id`,`state`,`create_time`,`update_time`) VALUES ("veg","tony","2f0e9fe1-68b1-496b-bdf7-b3958dded320","{""hello"":""world""}","2025-11-19 22:10:52.819","2025-11-19 22:10:52.819")
State Set 2f0e9fe1-68b1-496b-bdf7-b3958dded320 hello world
session.state map[hello:world]
^Csignal: interrupt
FAIL

tony@hydrogen: ~/hof/hof _next!
$ sqlite3 veg.db                                                                                                             [22:10:58]
SQLite version 3.43.2 2023-10-10 13:08:14
Enter ".help" for usage hints.
sqlite> select * from sessions;
veg|tony|5937ffcc-17db-4fbd-909e-e73919cc5cff|{}|2025-11-19 02:41:25.009195-08:00|2025-11-19 05:25:01.075054-08:00
veg|tony|8be7815a-1188-4732-ad42-fb600cc2360e|{}|2025-11-19 21:42:56.80102-08:00|2025-11-19 21:44:43.776355-08:00
veg|tony|2f0e9fe1-68b1-496b-bdf7-b3958dded320|{}|2025-11-19 22:07:14.963185-08:00|2025-11-19 22:07:14.963185-08:00
sqlite> 

[!IMPORTANT] manually running the same SQL uncovers the error

sqlite> INSERT INTO `sessions` (`app_name`,`user_id`,`id`,`state`,`create_time`,`update_time`) VALUES ("veg","tony","2f0e9fe1-68b1-496b-bdf7-b3958dded320","{""hello"":""world""}","2025-11-19 22:10:52.819","2025-11-19 22:10:52.819");
Runtime error: UNIQUE constraint failed: sessions.app_name, sessions.user_id, sessions.id (19)

This needs to be an UPDATE query

verdverm avatar Nov 20 '25 06:11 verdverm

Making the following change resolves the issue, not sure if it's really the desired behavior, but using Save instead of Create matches the two database calls above it (for app_state and user_state)

https://github.com/google/adk-go/blob/main/session/database/service.go#L124

diff --git a/session/database/service.go b/session/database/service.go
index 56e808a..0c2f6c7 100644
--- a/session/database/service.go
+++ b/session/database/service.go
@@ -121,7 +121,7 @@ func (s *databaseService) Create(ctx context.Context, req *session.CreateRequest
                }
                createdSession.State = sessionState
 
-               if err := tx.Create(createdSession).Error; err != nil {
+               if err := tx.Save(createdSession).Error; err != nil {
                        return fmt.Errorf("error creating session on database: %w", err)
                }

verdverm avatar Nov 20 '25 06:11 verdverm

From my personal perspective, it would be nice to have the Update API to update the session. But not sure if the API needs to align with adk-python. It would be great if maintainers could suggest on this. @dpasiukevich

git-hulk avatar Nov 20 '25 07:11 git-hulk