Building the Session Manager in Go

Welcome back! In the previous lesson, we explored the importance of a robust system prompt and how it guides the behavior of our personal tutor. Now, we will delve into the next step of our journey: building the Session Manager. This lesson will focus on the model layer of the MVC (Model-View-Controller) architecture, which is crucial for organizing and managing data in a structured way. The SessionManager will be the core component of our model layer, responsible for managing tutoring session data effectively. By the end of this lesson, you will understand how to create, manage, and retrieve session data using Go structs and methods.

Initializing the SessionManager

In Go, we use structs to define complex data types. The SessionManager struct will handle the storage and management of tutoring session data. We'll also define supporting types for session data and messages.

Let's start by defining the necessary types and a constructor-like function to initialize the SessionManager:

type Message struct {
    Role    string
    Content string
}

type Session struct {
    SystemPrompt string
    Messages     []Message
}

// SessionManager manages all sessions for all students.
type SessionManager struct {
    sessions map[string]map[string]*Session // studentID -> sessionID -> Session
}

// NewSessionManager creates and returns a new SessionManager.
func NewSessionManager() *SessionManager {
    return &SessionManager{
        sessions: make(map[string]map[string]*Session),
    }
}

Here, SessionManager uses a nested map to store sessions for each student. The first key is the studentID, and the second key is the sessionID. This structure allows us to efficiently manage multiple tutoring sessions for different students.

Creating a New Session

To create a new session, we'll add a method to SessionManager called CreateSession. This method takes a studentID, sessionID, and systemPrompt as parameters and initializes a new session.

func (sm *SessionManager) CreateSession(studentID, sessionID, systemPrompt string) {
    if _, ok := sm.sessions[studentID]; !ok {
        sm.sessions[studentID] = make(map[string]*Session)
    }
    sm.sessions[studentID][sessionID] = &Session{
        SystemPrompt: systemPrompt,
        Messages:     []Message{},
    }
}

This method checks if the studentID exists in the sessions map. If not, it creates a new entry. Then, it initializes the session with the provided systemPrompt and an empty slice for messages.

Retrieving a Session

To access a specific tutoring session, we need a method to retrieve it safely. In Go, we use the comma-ok idiom to check if a key exists in a map.

func (sm *SessionManager) GetSession(studentID, sessionID string) (*Session, bool) {
    studentSessions, ok := sm.sessions[studentID]
    if !ok {
        return nil, false
    }
    session, ok := studentSessions[sessionID]
    return session, ok
}

This method returns the session and a boolean indicating whether the session was found.

Adding Messages to a Session

Now, let's add a method to append messages to a session. This method requires the studentID, sessionID, role, and content of the message.

func (sm *SessionManager) AddMessage(studentID, sessionID, role, content string) {
    if session, ok := sm.GetSession(studentID, sessionID); ok {
        session.Messages = append(session.Messages, Message{Role: role, Content: content})
    }
}

The AddMessage method first retrieves the session using GetSession. If the session exists, it appends the message to the session's message slice.

Retrieving the Full Conversation

To retrieve the entire conversation, including the system prompt, we'll implement a method that returns a slice of messages, starting with the system prompt.

func (sm *SessionManager) GetConversation(studentID, sessionID string) []Message {
    if session, ok := sm.GetSession(studentID, sessionID); ok {
        conversation := []Message{
            {Role: "system", Content: session.SystemPrompt},
        }
        conversation = append(conversation, session.Messages...)
        return conversation
    }
    return []Message{}
}

This method constructs a slice starting with the system prompt, followed by the session messages. If the session does not exist, it returns an empty slice.

Creating and Managing Sessions

To create a new tutoring session, we use the CreateSession method. This method takes three parameters: studentID, sessionID, and systemPrompt. It initializes a new session entry in the sessions map. If the studentID does not already exist in the map, a new entry is created. The session entry includes the system prompt and an empty slice for messages.

Here's an example of how to create a new tutoring session in Go:

func loadSystemPrompt(filePath string) string {
    data, err := os.ReadFile(filePath)
    if err != nil {
        fmt.Printf("Error loading system prompt: %v\n", err)
        return "You are a helpful tutor."
    }
    return string(data)
}

func main() {
    // Load the system prompt
    systemPrompt := loadSystemPrompt("app/data/system_prompt.txt")

    // Initialize the session manager
    manager := NewSessionManager()

    // Create a new tutoring session
    studentID := "student_test"
    sessionID := "session_test"
    manager.CreateSession(studentID, sessionID, systemPrompt)

    // ... (see next sections for adding and retrieving messages)
}
Adding and Retrieving Messages

Once a session is created, we can add messages to it using the AddMessage method. This method requires the studentID, sessionID, role, and content of the message. The role indicates whether the message is from the student or the tutor assistant. The message is then appended to the session's message slice.

Here's how you can add messages to a tutoring session in Go:

    // Add some messages to the session
    manager.AddMessage(studentID, sessionID, "user", "Hello, I need help with geometry.")
    manager.AddMessage(studentID, sessionID, "assistant", "Hi there! What specific geometry topic are you struggling with?")

In this example, we add a student message and an assistant response to the session. The AddMessage method ensures that messages are stored in the correct session entry.

Retrieving Conversation

To retrieve the entire conversation, including the system prompt, we use the GetConversation method. This method returns a slice of messages, starting with the system prompt followed by the session messages.

Here's how you can retrieve and display the conversation in Go:

    // Retrieve the full conversation
    conversation := manager.GetConversation(studentID, sessionID)

    // Display the session details
    fmt.Printf("Session ID: %s\n\n", sessionID)
    for _, message := range conversation {
        fmt.Printf("Role: %s\n", message.Role)
        fmt.Printf("Message:\n%s\n\n", message.Content)
    }

The GetConversation method compiles the session history, allowing us to access the full context of the conversation. This is particularly useful when we need to provide the full conversation history to the DeepSeek model to ensure contextual responses.

Summary and Preparation for Practice

In this lesson, we explored the SessionManager struct and its role in managing tutoring session data within the model layer of our application. We learned how to create and manage sessions, add messages, and retrieve conversation histories using Go's data structures and idioms. The SessionManager is a crucial component for organizing session data, ensuring that our personal tutor can handle multiple tutoring sessions efficiently.

As you move on to the practice exercises, take the opportunity to experiment with modifying and extending the SessionManager functionality. This hands-on practice will reinforce the concepts covered in this lesson and prepare you for the next steps in our course. Keep up the great work, and I look forward to seeing your progress!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal