Skip to content

Commit

Permalink
fix(js/ai): make updateState patch state instead of replace it
Browse files Browse the repository at this point in the history
  • Loading branch information
cabljac committed Dec 23, 2024
1 parent e52a0d7 commit 7185cb3
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
14 changes: 10 additions & 4 deletions js/ai/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,22 @@ export class Session<S = any> {
}

/**
* Update session state data.
* Update session state data by patching the existing state.
* @param data Partial state update that will be merged with existing state
*/
async updateState(data: S): Promise<void> {
async updateState(data: Partial<S>): Promise<void> {
let sessionData = this.sessionData;
if (!sessionData) {
sessionData = {} as SessionData<S>;
}
sessionData.state = data;
this.sessionData = sessionData;

// Merge the new data with existing state
sessionData.state = {
...sessionData.state,
...data,
} as S;

this.sessionData = sessionData;
await this.store.save(this.id, sessionData);
}

Expand Down
65 changes: 65 additions & 0 deletions js/genkit/tests/chat_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,3 +660,68 @@ describe('preamble', () => {
]);
});
});

describe('session state', () => {
let ai: Genkit;

beforeEach(() => {
ai = genkit({
model: 'echoModel',
});
defineEchoModel(ai);
});

it('properly patches partial state updates', async () => {
const session = ai.createSession({
initialState: { userName: 'John', color: 'Blue', age: 25 },
});

// Update single property
await session.updateState({ color: 'Green' });
assert.deepStrictEqual(
session.state,
{ userName: 'John', color: 'Green', age: 25 },
'should preserve existing properties when updating a single field'
);

// Update multiple properties
await session.updateState({ userName: 'Jane', color: 'red', age: 26 });
assert.deepStrictEqual(
session.state,
{ userName: 'Jane', color: 'Green', age: 26 },
'should preserve non-updated properties when updating multiple fields'
);

// Update with undefined state
const emptySession = ai.createSession();
await emptySession.updateState({ newProp: 'value' });
assert.deepStrictEqual(
emptySession.state,
{ newProp: 'value' },
'should handle updates when initial state is undefined'
);
});

it('maintains state across chat messages', async () => {
const session = ai.createSession({
initialState: { userName: 'Pavel' },
});

const chat = session.chat();
await chat.send('hi');

await session.updateState({ userName: 'Jacob' });
assert.deepStrictEqual(
session.state,
{ userName: 'John', status: 'active' },
'should preserve state between chat messages'
);

await chat.send('bye');
assert.deepStrictEqual(
session.state,
{ userName: 'John', status: 'active' },
'state should remain stable after chat messages'
);
});
});

0 comments on commit 7185cb3

Please sign in to comment.