zoobzio December 13, 2025 Edit this page

Session

Session manages conversation context across synapse calls.

Constructor

func NewSession() *Session

Create a new empty session.

session := zyn.NewSession()

Read Methods

Messages

func (s *Session) Messages() []Message

Get all messages in the session.

messages := session.Messages()
for _, msg := range messages {
    fmt.Printf("%s: %s\n", msg.Role, msg.Content)
}

Len

func (s *Session) Len() int

Get the number of messages.

count := session.Len()

At

func (s *Session) At(index int) (Message, error)

Get message at index. Returns error if out of bounds.

msg, err := session.At(0)
if err != nil {
    // Index out of bounds
}

LastUsage

func (s *Session) LastUsage() *TokenUsage

Get token usage from the last successful call. Returns nil if no usage recorded.

if usage := session.LastUsage(); usage != nil {
    fmt.Printf("Tokens: %d\n", usage.Total)
}

Write Methods

Append

func (s *Session) Append(role Role, content string)

Add a message to the end.

session.Append(zyn.RoleUser, "Hello")
session.Append(zyn.RoleAssistant, "Hi there!")

Clear

func (s *Session) Clear()

Remove all messages.

session.Clear()

Remove

func (s *Session) Remove(index int) error

Remove message at index. Returns error if out of bounds.

err := session.Remove(0)

Replace

func (s *Session) Replace(index int, msg Message) error

Replace message at index. Returns error if out of bounds.

err := session.Replace(0, zyn.Message{
    Role:    zyn.RoleUser,
    Content: "Updated message",
})

Insert

func (s *Session) Insert(index int, msg Message) error

Insert message at index. Returns error if out of bounds.

err := session.Insert(0, zyn.Message{
    Role:    zyn.RoleSystem,
    Content: "You are a helpful assistant",
})

Bulk Methods

Prune

func (s *Session) Prune(n int) error

Remove the last n message pairs (user + assistant). Each pair is 2 messages, so n=1 removes 2 messages. Returns error if n is negative.

err := session.Prune(2)  // Remove last 2 exchanges (4 messages)

Truncate

func (s *Session) Truncate(keepFirst, keepLast int) error

Keep first keepFirst and last keepLast messages, remove middle.

err := session.Truncate(2, 2)  // Keep first 2 and last 2

SetMessages

func (s *Session) SetMessages(messages []Message)

Replace entire message history.

session.SetMessages([]zyn.Message{
    {Role: zyn.RoleUser, Content: "Summary of previous conversation"},
    {Role: zyn.RoleAssistant, Content: "Acknowledged"},
})

SetUsage

func (s *Session) SetUsage(usage *TokenUsage)

Set token usage (typically called internally by synapses).

session.SetUsage(&zyn.TokenUsage{
    Prompt:     100,
    Completion: 50,
    Total:      150,
})

Types

Message

type Message struct {
    Role    string // RoleUser, RoleAssistant, or RoleSystem
    Content string
}

Role Constants

const (
    RoleUser      = "user"
    RoleAssistant = "assistant"
    RoleSystem    = "system"
)

TokenUsage

type TokenUsage struct {
    Prompt     int
    Completion int
    Total      int
}

Behavior

Transactional Updates

Synapses update sessions atomically:

session := zyn.NewSession()
initialLen := session.Len()  // 0

_, err := synapse.Fire(ctx, session, "input")
if err != nil {
    // Session unchanged on error
    assert(session.Len() == initialLen)
} else {
    // Session has user message + assistant response
    assert(session.Len() == initialLen + 2)
}

Thread Safety

Sessions are safe for concurrent use by multiple goroutines. All methods are protected by a read-write mutex.

// ✅ OK: Concurrent access
go func() { synapse.Fire(ctx, session, "input1") }()
go func() { synapse.Fire(ctx, session, "input2") }()

// ✅ Also OK: Sequential access
synapse.Fire(ctx, session, "input1")
synapse.Fire(ctx, session, "input2")

// ✅ Also OK: Separate sessions for independent conversations
go func() { synapse.Fire(ctx, zyn.NewSession(), "input1") }()
go func() { synapse.Fire(ctx, zyn.NewSession(), "input2") }()

Note: While concurrent access is safe, the message ordering in concurrent scenarios depends on which goroutine completes first. For deterministic conversation flow, sequential access is recommended.