conversation

package
v0.4.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2024 License: MIT Imports: 12 Imported by: 17

README

Conversation Package

The conversation package provides a flexible structure for managing complex conversation flows in LLM chatbots. It offers a tree-like structure for storing and traversing conversation messages.

Key Components

  1. Message: Represents individual messages with various content types.
  2. ConversationTree: Manages the tree structure of the conversation.
  3. Manager: Provides high-level conversation management operations.
  4. Context: Handles loading and saving conversations from/to files.

Features

  • Tree-based conversation representation
  • Support for different message content types (chat, tool use, tool results, images)
  • Flexible conversation traversal methods
  • JSON/YAML file persistence
  • High-level conversation management interface

Usage Examples

Creating and Manipulating a Conversation Tree

This example demonstrates how to create a conversation tree, add messages to it, and retrieve conversation threads. It showcases the basic operations for building and traversing the conversation structure.

tree := conversation.NewConversationTree()

message1 := conversation.NewChatMessage(conversation.RoleUser, "Hello!")
message2 := conversation.NewChatMessage(conversation.RoleAssistant, "Hi there!")

tree.InsertMessages(message1, message2)

thread := tree.GetConversationThread(message2.ID)
leftmostThread := tree.GetLeftMostThread(tree.RootID)
Using the Manager

The Manager provides a higher-level interface for managing conversations. This example shows how to create a manager with initial prompts, add messages, and retrieve the conversation. It's useful for more complex conversation handling scenarios.

manager, err := conversation.CreateManager(
    "System prompt",
    "User prompt",
    []*conversation.Message{},
    nil,
)
if err != nil {
    // Handle error
}

manager.AppendMessages(message1, message2)
conversation := manager.GetConversation()
Persistence

This example illustrates how to save and load conversation trees to/from JSON files. This feature is crucial for maintaining conversation state across sessions or for analysis purposes.

err := tree.SaveToFile("conversation.json")
if err != nil {
    // Handle error
}

loadedTree := conversation.NewConversationTree()
err = loadedTree.LoadFromFile("conversation.json")
if err != nil {
    // Handle error
}

Extending Message Content Types

The package allows for custom message content types by implementing the MessageContent interface. This flexibility enables the conversation package to handle various types of interactions beyond simple text messages.

type MessageContent interface {
    ContentType() ContentType
    String() string
    View() string
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ChatMessageContent

type ChatMessageContent struct {
	Role   Role            `json:"role"`
	Text   string          `json:"text"`
	Images []*ImageContent `json:"images"`
}

func (*ChatMessageContent) ContentType

func (c *ChatMessageContent) ContentType() ContentType

func (*ChatMessageContent) String

func (c *ChatMessageContent) String() string

func (*ChatMessageContent) View

func (c *ChatMessageContent) View() string

type ContentType

type ContentType string
const (
	ContentTypeChatMessage ContentType = "chat-message"
	// TODO(manuel, 2024-06-04) This needs to also handle tool call and tool response blocks (tool use block in claude API)
	// See also the comment to refactor this in openai/helpers.go, where tool use information is actually stored in the metadata of the message
	ContentTypeToolUse    ContentType = "tool-use"
	ContentTypeToolResult ContentType = "tool-result"
	ContentTypeImage      ContentType = "image"
)

TODO(manuel, 2024-07-04) Unify this with the events types that we added for the claude API

type Conversation

type Conversation []*Message

func (Conversation) GetSinglePrompt

func (messages Conversation) GetSinglePrompt() string

GetSinglePrompt concatenates all the messages together with a prompt in front. It just concatenates all the messages together with a prompt in front (if there are more than one message).

type ConversationTree

type ConversationTree struct {
	Nodes  map[NodeID]*Message
	RootID NodeID
	LastID NodeID
}

ConversationTree represents a tree-like structure for storing and managing conversation messages.

The tree consists of nodes (messages) connected by parent-child links. These relationships are done through the parent ID field in each message. The root node is the starting point of the conversation, and each node can have multiple children. The tree allows for traversing the conversation in various ways.

Node relationships are stored in the Message datastructure as `Children []*Message`.

Each node has a unique ID, and the tree keeps track of the root node ID and the last inserted node ID.

func NewConversationTree

func NewConversationTree() *ConversationTree

func (*ConversationTree) AppendMessages

func (ct *ConversationTree) AppendMessages(thread Conversation)

AppendMessages appends a conversation thread to the end of the tree. It attaches the thread to the last inserted node in the tree, making it the parent of the thread. The messages in the thread are inserted as nodes, extending the parent-child chain.

func (*ConversationTree) AttachThread

func (ct *ConversationTree) AttachThread(parentID NodeID, thread Conversation)

AttachThread attaches a conversation thread to a specified parent message. It updates the parent IDs of the messages in the thread to link them to the parent message. The last message in the thread becomes the new last inserted node ID.

func (*ConversationTree) FindChildren

func (ct *ConversationTree) FindChildren(id NodeID) []NodeID

FindChildren returns the IDs of all child messages for a given message ID.

func (*ConversationTree) FindSiblings

func (ct *ConversationTree) FindSiblings(id NodeID) []NodeID

FindSiblings returns the IDs of all sibling messages for a given message ID. Sibling messages are the nodes that share the same parent as the given message.

func (*ConversationTree) GetConversationThread

func (ct *ConversationTree) GetConversationThread(id NodeID) Conversation

GetConversationThread retrieves the linear conversation thread from root to the specified message.

func (*ConversationTree) GetLeftMostThread

func (ct *ConversationTree) GetLeftMostThread(id NodeID) Conversation

GetLeftMostThread returns the thread starting from a given message ID by always choosing the first child. It traverses the tree downwards, selecting the leftmost child at each level, until a leaf node is reached. The returned conversation is a linear sequence of messages from the given message to the leftmost leaf.

func (*ConversationTree) GetMessageByID

func (ct *ConversationTree) GetMessageByID(id NodeID) (*Message, bool)

func (*ConversationTree) InsertMessages

func (ct *ConversationTree) InsertMessages(msgs ...*Message)

InsertMessages adds new messages to the conversation tree. It updates the root ID if the tree is empty and sets the last inserted node ID. If a message has a parent ID that exists in the tree, it is added as a child of that parent node.

func (*ConversationTree) LoadFromFile

func (ct *ConversationTree) LoadFromFile(filename string) error

func (*ConversationTree) PrependThread

func (ct *ConversationTree) PrependThread(thread Conversation)

PrependThread prepends a conversation thread to the beginning of the tree. It updates the root ID to the first message in the thread and adjusts the parent-child relationships accordingly. The previous root node becomes a child of the new root node.

func (*ConversationTree) SaveToFile

func (ct *ConversationTree) SaveToFile(filename string) error

type ImageContent

type ImageContent struct {
	ImageURL     string      `json:"imageURL"`
	ImageContent []byte      `json:"imageContent"`
	ImageName    string      `json:"imageName"`
	MediaType    string      `json:"mediaType"`
	Detail       ImageDetail `json:"detail"`
}

func NewImageContentFromFile

func NewImageContentFromFile(path string) (*ImageContent, error)

func (*ImageContent) ContentType

func (i *ImageContent) ContentType() ContentType

func (*ImageContent) String

func (i *ImageContent) String() string

func (*ImageContent) View

func (i *ImageContent) View() string

type ImageDetail

type ImageDetail string
const (
	ImageDetailLow  ImageDetail = "low"
	ImageDetailHigh ImageDetail = "high"
	ImageDetailAuto ImageDetail = "auto"
)

type Manager

type Manager interface {
	GetConversation() Conversation
	AppendMessages(msgs ...*Message)
	AttachMessages(parentID NodeID, msgs ...*Message)
	GetMessage(ID NodeID) (*Message, bool)
	SaveToFile(filename string) error
}

Manager defines the interface for high-level conversation management operations.

type ManagerImpl

type ManagerImpl struct {
	Tree           *ConversationTree
	ConversationID uuid.UUID
}

func CreateManager

func CreateManager(
	systemPrompt string,
	prompt string,
	messages []*Message,
	params interface{},
	options ...ManagerOption,
) (*ManagerImpl, error)

CreateManager initializes a Manager implementation with system prompts, initial messages, and customizable options. It handles template rendering for prompts and messages.

NOTE(manuel, 2024-04-07) This currently seems to only be used by the codegen tests, while the main geppetto command uses NewManager. Unclear if this is just a legacy helper.

The systemPrompt and prompt templates are rendered using the params. Messages are also rendered using the params before being added to the manager.

ManagerOptions can be passed to further customize the manager on creation.

func NewManager

func NewManager(options ...ManagerOption) *ManagerImpl

func (*ManagerImpl) AppendMessages

func (c *ManagerImpl) AppendMessages(messages ...*Message)

func (*ManagerImpl) AttachMessages

func (c *ManagerImpl) AttachMessages(parentID NodeID, messages ...*Message)

func (*ManagerImpl) GetConversation

func (c *ManagerImpl) GetConversation() Conversation

func (*ManagerImpl) GetMessage

func (c *ManagerImpl) GetMessage(ID NodeID) (*Message, bool)

func (*ManagerImpl) PrependMessages

func (c *ManagerImpl) PrependMessages(messages ...*Message)

func (*ManagerImpl) SaveToFile

func (c *ManagerImpl) SaveToFile(s string) error

SaveToFile persists the current conversation state to a JSON file, enabling conversation continuity across sessions.

type ManagerOption

type ManagerOption func(*ManagerImpl)

func WithManagerConversationID

func WithManagerConversationID(conversationID uuid.UUID) ManagerOption

func WithMessages

func WithMessages(messages ...*Message) ManagerOption

type Message

type Message struct {
	ParentID   NodeID    `json:"parentID"`
	ID         NodeID    `json:"id"`
	Time       time.Time `json:"time"`
	LastUpdate time.Time `json:"lastUpdate"`

	Content  MessageContent         `json:"content"`
	Metadata map[string]interface{} `json:"metadata"` // Flexible metadata field

	// TODO(manuel, 2024-04-07) Add Parent and Sibling lists
	// omit in json
	Children []*Message `json:"-"`
}

Message represents a single message node in the conversation tree.

func LoadFromFile

func LoadFromFile(filename string) ([]*Message, error)

LoadFromFile reads messages from a JSON or YAML file, facilitating conversation initialization from saved states.

func NewChatMessage

func NewChatMessage(role Role, text string, options ...MessageOption) *Message

func NewMessage

func NewMessage(content MessageContent, options ...MessageOption) *Message

func (*Message) MarshalJSON

func (mn *Message) MarshalJSON() ([]byte, error)

func (*Message) UnmarshalJSON

func (mn *Message) UnmarshalJSON(data []byte) error

UnmarshalJSON custom unmarshaler for Message.

type MessageContent

type MessageContent interface {
	ContentType() ContentType
	String() string
	View() string
}

MessageContent is an interface for different types of node content.

type MessageOption

type MessageOption func(*Message)

func WithID

func WithID(id NodeID) MessageOption

func WithMetadata

func WithMetadata(metadata map[string]interface{}) MessageOption

func WithParentID

func WithParentID(parentID NodeID) MessageOption

func WithTime

func WithTime(time time.Time) MessageOption

type NodeID

type NodeID uuid.UUID
var NullNode NodeID = NodeID(uuid.Nil)

func NewNodeID

func NewNodeID() NodeID

func (NodeID) MarshalJSON

func (id NodeID) MarshalJSON() ([]byte, error)

func (NodeID) String

func (id NodeID) String() string

func (*NodeID) UnmarshalJSON

func (id *NodeID) UnmarshalJSON(data []byte) error

type Role

type Role string
const (
	RoleSystem    Role = "system"
	RoleAssistant Role = "assistant"
	RoleUser      Role = "user"
	RoleTool      Role = "tool"
)

type ToolResultContent

type ToolResultContent struct {
	ToolID string `json:"toolID"`
	Result string `json:"result"`
}

func (*ToolResultContent) ContentType

func (t *ToolResultContent) ContentType() ContentType

func (*ToolResultContent) String

func (t *ToolResultContent) String() string

func (*ToolResultContent) View

func (t *ToolResultContent) View() string

type ToolUseContent

type ToolUseContent struct {
	ToolID string          `json:"toolID"`
	Name   string          `json:"name"`
	Input  json.RawMessage `json:"input"`
	// used by openai currently (only function)
	Type string `json:"type"`
}

func (*ToolUseContent) ContentType

func (t *ToolUseContent) ContentType() ContentType

func (*ToolUseContent) String

func (t *ToolUseContent) String() string

func (*ToolUseContent) View

func (t *ToolUseContent) View() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL