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" ;
99import { z } from "zod" ;
1010import { imagen3Fast , vertexAI , gemini15Pro } from '@genkit-ai/vertexai' ;
1111import { googleAI , gemini20Flash } from "@genkit-ai/googleai" ;
1212import { beginStoryPrompt , createImgPrompt , continuePrompt , descriptionPrompt , preamblePrompt } from './prompts' ;
1313import { parse } from 'partial-json' ;
14+ import { readFile , writeFile } from "node:fs/promises" ;
1415
1516// Defined twice to easily swap models
1617const model = gemini20Flash ;
@@ -28,8 +29,7 @@ interface MyState {
2829 primaryObjective ?: string ;
2930 milestones ?: string [ ] ;
3031 currentMilestone ?: string ;
31- }
32- let session : Session ;
32+ }
3333
3434const 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
247263async 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