Skip to content

Commit 2af07e1

Browse files
committed
Add persistent session storage
1 parent b33241c commit 2af07e1

2 files changed

Lines changed: 49 additions & 10 deletions

File tree

genkit-angular-story-generator/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ node_modules/
107107
# dataconnect generated files
108108
.dataconnect
109109

110+
# session data
111+
session-stores/
112+

genkit-angular-story-generator/src/flows.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.dev/license
77
*/
8-
import { Chat, genkit, Session } from "genkit/beta";
8+
import { Chat, genkit, Session, SessionData, SessionStore } from "genkit/beta";
99
import { z } from "zod";
1010
import { imagen3Fast, vertexAI, gemini15Pro } from '@genkit-ai/vertexai';
1111
import { googleAI, gemini20Flash } from "@genkit-ai/googleai";
1212
import { beginStoryPrompt, createImgPrompt, continuePrompt, descriptionPrompt, preamblePrompt } from './prompts';
1313
import { parse } from 'partial-json';
14+
import { readFile, writeFile } from "node:fs/promises";
1415

1516
// Defined twice to easily swap models
1617
const model = gemini20Flash;
@@ -28,8 +29,7 @@ interface MyState {
2829
primaryObjective?: string;
2930
milestones?: string[];
3031
currentMilestone?: string;
31-
}
32-
let session: Session;
32+
}
3333

3434
const DescriptionOutput = z.object({
3535
storyPremise: z.string(),
@@ -49,11 +49,15 @@ export const descriptionFlow = ai.defineFlow(
4949
},
5050
async ({ userInput, sessionId, clearSession }) => {
5151
let chat: Chat;
52-
if (clearSession || !session) {
53-
session = ai.createSession<MyState>({
54-
sessionId,
55-
initialState: {},
56-
});
52+
53+
let session = ai.createSession<MyState>({
54+
store: new JsonSessionStore(),
55+
sessionId,
56+
initialState: {}
57+
});
58+
59+
if (clearSession) {
60+
session.updateState({});
5761
chat = session.chat(preamble, { sessionId, model });
5862
await session.updateMessages(sessionId, []);
5963
} else {
@@ -96,7 +100,7 @@ export const beginStoryFlow = ai.defineFlow(
96100
name: 'beginStoryFlow',
97101
inputSchema: z.object({
98102
userInput: z.string(),
99-
sessionId: z.string()
103+
sessionId: z.string(),
100104
}),
101105
outputSchema: StoryOutput
102106
},
@@ -105,6 +109,9 @@ export const beginStoryFlow = ai.defineFlow(
105109
let options: string[] = [];
106110
let primaryObjective = '';
107111
try {
112+
const session = await ai.loadSession(sessionId, {
113+
store: new JsonSessionStore(),
114+
});
108115
const chat = session.chat({ sessionId, model });
109116
const { text } = await chat.send(beginStoryPrompt(userInput));
110117
let storyDetail: z.infer<typeof StoryDetail>;
@@ -148,6 +155,9 @@ export const continueStoryFlow = ai.defineFlow(
148155
outputSchema: StoryOutput.extend({ rating: z.string() })
149156
},
150157
async ({ userInput, sessionId}) => {
158+
const session = await ai.loadSession(sessionId, {
159+
store: new JsonSessionStore(),
160+
});
151161
const chat = session.chat({ sessionId, model });
152162

153163
let storyParts: string[] = [];
@@ -190,6 +200,9 @@ async function handleProgress(
190200
storyParts: string[],
191201
achievedMilestone: boolean,
192202
sessionId: string): Promise<{ storyParts: string[], progress: number }> {
203+
const session = await ai.loadSession(sessionId, {
204+
store: new JsonSessionStore(),
205+
});
193206
let currentMilestone = session.state.currentMilestone;
194207
const milestones = session.state.milestones;
195208
const finalMilestone = milestones[milestones.length - 1];
@@ -215,6 +228,9 @@ const endStoryFlow = ai.defineFlow(
215228
},
216229
async (sessionId) => {
217230
try {
231+
const session = await ai.loadSession(sessionId, {
232+
store: new JsonSessionStore(),
233+
});
218234
const chat = session.chat({ sessionId, model });
219235
const { text } = await chat.send(`
220236
The characters have achieved their primary objective.
@@ -245,6 +261,9 @@ export const genImgFlow = ai.defineFlow(
245261
);
246262

247263
async function genImgBlob(story: string, sessionId: string): Promise<string> {
264+
const session = await ai.loadSession(sessionId, {
265+
store: new JsonSessionStore(),
266+
});
248267
const chat = session.chat({ sessionId, model });
249268
const { text: storyImgDescr } = await chat.send(`
250269
Describe an image that the captures the essence of this story: ${story}.
@@ -282,4 +301,21 @@ function maybeStripMarkdown(withMarkdown: string) {
282301
return withMarkdown;
283302
}
284303
return mdMatch[2];
285-
}
304+
}
305+
306+
class JsonSessionStore<S = any> implements SessionStore<S> {
307+
async get(sessionId: string): Promise<SessionData<S> | undefined> {
308+
try {
309+
const s = await readFile(`session-stores/${sessionId}.json`, { encoding: 'utf8'});
310+
const data = JSON.parse(s);
311+
return data;
312+
} catch {
313+
return undefined;
314+
}
315+
}
316+
317+
async save(sessionId: string, sessionData: SessionData<S>): Promise<void> {
318+
const s = JSON.stringify(sessionData);
319+
await writeFile(`session-stores/${sessionId}.json`, s, { encoding: 'utf8' });
320+
}
321+
}

0 commit comments

Comments
 (0)