Skip to content
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

feat(go/genkit): added persistent chat session and agent support #1592

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

dysrama
Copy link
Contributor

@dysrama dysrama commented Jan 10, 2025

func SimpleChat() {
	ctx := context.Background()
	g, err := genkit.New(nil)
	if err != nil {
		log.Fatal(err)
	}

	if err := vertexai.Init(ctx, g, nil); err != nil {
		log.Fatal(err)
	}

	m := vertexai.Model(g, "gemini-1.5-flash")

	chat, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're a pirate first mate. Address the user as Captain and assist them however you can."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1.3}),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := chat.Send(ctx, "Hello")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())
}

func StatefulChat() {
	ctx := context.Background()
	g, err := genkit.New(nil)
	if err != nil {
		log.Fatal(err)
	}

	if err := vertexai.Init(ctx, g, nil); err != nil {
		log.Fatal(err)
	}

	m := vertexai.Model(g, "gemini-1.5-pro")

	// To include state in a session, you need to instantiate a session explicitly
	session, err := genkit.NewSession(ctx,
		genkit.WithSessionData(genkit.SessionData{
			State: map[string]any{
				"username": "Michael",
			},
		},
		),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Modify the state using tools
	chatTool := genkit.DefineTool(
		g,
		"updateName",
		"use this tool to update the name of the user",
		func(ctx context.Context, input struct {
			Name string
		}) (string, error) {
			// Set name in state
			session, err := genkit.SessionFromContext(ctx)
			if err != nil {
				return "", err
			}

			err = session.UpdateState(map[string]any{
				"username": input.Name,
			})
			if err != nil {
				return "", err
			}

			return "changed username to " + input.Name, nil
		},
	)

	chat, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're Kitt from Knight Rider. Address the user as Kitt would and always introduce yourself."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1}),
		genkit.WithSession(session),
		genkit.WithTools(chatTool),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := chat.Send(ctx, "Hello, my name is Earl")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())
	fmt.Print(session.SessionData.State["username"])
}

func MultiThreadChat() {
	ctx := context.Background()
	g, err := genkit.New(nil)
	if err != nil {
		log.Fatal(err)
	}

	if err := vertexai.Init(ctx, g, nil); err != nil {
		log.Fatal(err)
	}

	m := vertexai.Model(g, "gemini-1.5-flash")

	pirateChat, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're a pirate first mate. Address the user as Captain and assist them however you can."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1.3}),
		genkit.WithThreadName("pirate"),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := pirateChat.Send(ctx, "Hello")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())

	lawyerChat, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're a lawyer. Give unsolicited advice no matter what is asked."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1.3}),
		genkit.WithThreadName("lawyer"),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err = lawyerChat.Send(ctx, "Hello")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())
}

func PersistentStorageChat() {
	ctx := context.Background()
	g, err := genkit.New(nil)
	if err != nil {
		log.Fatal(err)
	}

	if err := vertexai.Init(ctx, g, nil); err != nil {
		log.Fatal(err)
	}

	m := vertexai.Model(g, "gemini-1.5-pro")

	// To override default in-mem session storage
	store := &MyOwnSessionStore{
		SessionData: make(map[string]genkit.SessionData),
	}
	session, err := genkit.NewSession(ctx,
		genkit.WithSessionStore(store),
	)
	if err != nil {
		log.Fatal(err)
	}

	chat, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're a helpful chatbox. Help the user."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1}),
		genkit.WithSession(session),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := chat.Send(ctx, "Hello, my name is Earl")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())

	// Load and use existing session
	session2, err := genkit.LoadSession(ctx, "mySessionID", store)
	if err != nil {
		log.Fatal(err)
	}

	chat2, err := genkit.NewChat(
		ctx,
		g,
		genkit.WithModel(m),
		genkit.WithSystemText("You're a helpful chatbox. Help the user."),
		genkit.WithConfig(ai.GenerationCommonConfig{Temperature: 1}),
		genkit.WithSession(session2),
	)
	if err != nil {
		log.Fatal(err)
	}

	resp, err = chat2.Send(ctx, "What's my name?")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Print(resp.Text())
}

type MyOwnSessionStore struct {
	SessionData map[string]genkit.SessionData
}

func (s *MyOwnSessionStore) Get(sessionId string) (data genkit.SessionData, err error) {
	d, err := os.ReadFile("/tmp/" + sessionId)
	if err != nil {
		return data, err
	}
	err = json.Unmarshal(d, &data)
	if err != nil {
		return data, err
	}

	if data.Threads == nil {
		data.Threads = make(map[string][]*ai.Message)
	}

	s.SessionData[sessionId] = data
	return s.SessionData[sessionId], nil
}

func (s *MyOwnSessionStore) Save(sessionId string, data genkit.SessionData) error {
	s.SessionData[sessionId] = data
	d, err := json.Marshal(data)
	if err != nil {
		return err
	}
	err = os.WriteFile("/tmp/"+sessionId, []byte(d), 0644)
	if err != nil {
		return err
	}
	return nil
}

@dysrama dysrama linked an issue Jan 28, 2025 that may be closed by this pull request
@apascal07 apascal07 self-requested a review January 30, 2025 17:56
Copy link
Collaborator

@apascal07 apascal07 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Louise, could you please a sample that uses this new API? We'll use it as the canonical one for playing around with this in Dev UI.

@github-actions github-actions bot added the go label Feb 7, 2025
@dysrama
Copy link
Contributor Author

dysrama commented Feb 7, 2025

Louise, could you please a sample that uses this new API? We'll use it as the canonical one for playing around with this in Dev UI.

Added in description, went with the same examples we have in node

@apascal07
Copy link
Collaborator

Can you actually add

Louise, could you please a sample that uses this new API? We'll use it as the canonical one for playing around with this in Dev UI.

Added in description, went with the same examples we have in node

Can you actually commit this to the samples directory please? We want to have a canonical example that we can run anytime.

@dysrama
Copy link
Contributor Author

dysrama commented Feb 12, 2025

Can you actually commit this to the samples directory please? We want to have a canonical example that we can run anytime.

Yep, done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

[Go] Add persistent chat session and agent support
2 participants