diff options
| author | adamdottv <[email protected]> | 2025-05-12 08:43:34 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-05-12 08:43:34 -0500 |
| commit | ed9fba99c9e230094ed5d468c88f81469d60c911 (patch) | |
| tree | 0cebc5f7610e91c58c0289d3aa12d1f4c57eab8e /internal/session/session.go | |
| parent | f1007771997bd0401516eda87a7e0ac92f269680 (diff) | |
| download | opencode-ed9fba99c9e230094ed5d468c88f81469d60c911.tar.gz opencode-ed9fba99c9e230094ed5d468c88f81469d60c911.zip | |
wip: refactoring
Diffstat (limited to 'internal/session/session.go')
| -rw-r--r-- | internal/session/session.go | 229 |
1 files changed, 155 insertions, 74 deletions
diff --git a/internal/session/session.go b/internal/session/session.go index be395bb87..933df8117 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -3,12 +3,16 @@ package session import ( "context" "database/sql" + "fmt" + "sync" + "time" "github.com/google/uuid" "github.com/opencode-ai/opencode/internal/db" "github.com/opencode-ai/opencode/internal/pubsub" ) +// Session represents a conversation session. type Session struct { ID string ParentSessionID string @@ -23,128 +27,185 @@ type Session struct { UpdatedAt int64 } +// --- Events --- + +const ( + EventSessionCreated pubsub.EventType = "session_created" + EventSessionUpdated pubsub.EventType = "session_updated" + EventSessionDeleted pubsub.EventType = "session_deleted" +) + +// --- Service Definition --- + type Service interface { - pubsub.Suscriber[Session] + pubsub.Subscriber[Session] + Create(ctx context.Context, title string) (Session, error) - CreateTitleSession(ctx context.Context, parentSessionID string) (Session, error) CreateTaskSession(ctx context.Context, toolCallID, parentSessionID, title string) (Session, error) Get(ctx context.Context, id string) (Session, error) List(ctx context.Context) ([]Session, error) - Save(ctx context.Context, session Session) (Session, error) + Update(ctx context.Context, session Session) (Session, error) Delete(ctx context.Context, id string) error } type service struct { - *pubsub.Broker[Session] - q db.Querier + db *db.Queries + broker *pubsub.Broker[Session] + mu sync.RWMutex +} + +var globalSessionService *service + +func InitService(dbConn *sql.DB) error { + if globalSessionService != nil { + return fmt.Errorf("session service already initialized") + } + queries := db.New(dbConn) + broker := pubsub.NewBroker[Session]() + + globalSessionService = &service{ + db: queries, + broker: broker, + } + return nil } +func GetService() Service { + if globalSessionService == nil { + panic("session service not initialized. Call session.InitService() first.") + } + return globalSessionService +} + +// --- Service Methods --- + func (s *service) Create(ctx context.Context, title string) (Session, error) { - dbSession, err := s.q.CreateSession(ctx, db.CreateSessionParams{ + s.mu.Lock() + defer s.mu.Unlock() + + if title == "" { + title = "New Session - " + time.Now().Format("2006-01-02 15:04:05") + } + + dbSessParams := db.CreateSessionParams{ ID: uuid.New().String(), Title: title, - }) + } + dbSession, err := s.db.CreateSession(ctx, dbSessParams) if err != nil { - return Session{}, err + return Session{}, fmt.Errorf("db.CreateSession: %w", err) } + session := s.fromDBItem(dbSession) - s.Publish(pubsub.CreatedEvent, session) + s.broker.Publish(EventSessionCreated, session) return session, nil } func (s *service) CreateTaskSession(ctx context.Context, toolCallID, parentSessionID, title string) (Session, error) { - dbSession, err := s.q.CreateSession(ctx, db.CreateSessionParams{ + s.mu.Lock() + defer s.mu.Unlock() + + if title == "" { + title = "Task Session - " + time.Now().Format("2006-01-02 15:04:05") + } + if toolCallID == "" { + toolCallID = uuid.New().String() + } + + dbSessParams := db.CreateSessionParams{ ID: toolCallID, - ParentSessionID: sql.NullString{String: parentSessionID, Valid: true}, + ParentSessionID: sql.NullString{String: parentSessionID, Valid: parentSessionID != ""}, Title: title, - }) - if err != nil { - return Session{}, err } - session := s.fromDBItem(dbSession) - s.Publish(pubsub.CreatedEvent, session) - return session, nil -} - -func (s *service) CreateTitleSession(ctx context.Context, parentSessionID string) (Session, error) { - dbSession, err := s.q.CreateSession(ctx, db.CreateSessionParams{ - ID: "title-" + parentSessionID, - ParentSessionID: sql.NullString{String: parentSessionID, Valid: true}, - Title: "Generate a title", - }) + dbSession, err := s.db.CreateSession(ctx, dbSessParams) if err != nil { - return Session{}, err + return Session{}, fmt.Errorf("db.CreateTaskSession: %w", err) } session := s.fromDBItem(dbSession) - s.Publish(pubsub.CreatedEvent, session) + s.broker.Publish(EventSessionCreated, session) return session, nil } -func (s *service) Delete(ctx context.Context, id string) error { - session, err := s.Get(ctx, id) - if err != nil { - return err - } - err = s.q.DeleteSession(ctx, session.ID) +func (s *service) Get(ctx context.Context, id string) (Session, error) { + s.mu.RLock() + defer s.mu.RUnlock() + dbSession, err := s.db.GetSessionByID(ctx, id) if err != nil { - return err + if err == sql.ErrNoRows { + return Session{}, fmt.Errorf("session ID '%s' not found", id) + } + return Session{}, fmt.Errorf("db.GetSessionByID: %w", err) } - s.Publish(pubsub.DeletedEvent, session) - return nil + return s.fromDBItem(dbSession), nil } -func (s *service) Get(ctx context.Context, id string) (Session, error) { - dbSession, err := s.q.GetSessionByID(ctx, id) +func (s *service) List(ctx context.Context) ([]Session, error) { + s.mu.RLock() + defer s.mu.RUnlock() + dbSessions, err := s.db.ListSessions(ctx) if err != nil { - return Session{}, err + return nil, fmt.Errorf("db.ListSessions: %w", err) } - return s.fromDBItem(dbSession), nil + sessions := make([]Session, len(dbSessions)) + for i, dbSess := range dbSessions { + sessions[i] = s.fromDBItem(dbSess) + } + return sessions, nil } -func (s *service) Save(ctx context.Context, session Session) (Session, error) { - summary := sql.NullString{} - if session.Summary != "" { - summary.String = session.Summary - summary.Valid = true - } +func (s *service) Update(ctx context.Context, session Session) (Session, error) { + s.mu.Lock() + defer s.mu.Unlock() - summarizedAt := sql.NullInt64{} - if session.SummarizedAt != 0 { - summarizedAt.Int64 = session.SummarizedAt - summarizedAt.Valid = true + if session.ID == "" { + return Session{}, fmt.Errorf("cannot update session with empty ID") } - - dbSession, err := s.q.UpdateSession(ctx, db.UpdateSessionParams{ + params := db.UpdateSessionParams{ ID: session.ID, Title: session.Title, PromptTokens: session.PromptTokens, CompletionTokens: session.CompletionTokens, Cost: session.Cost, - Summary: summary, - SummarizedAt: summarizedAt, - }) + Summary: sql.NullString{String: session.Summary, Valid: session.Summary != ""}, + SummarizedAt: sql.NullInt64{Int64: session.SummarizedAt, Valid: session.SummarizedAt > 0}, + } + dbSession, err := s.db.UpdateSession(ctx, params) if err != nil { - return Session{}, err + return Session{}, fmt.Errorf("db.UpdateSession: %w", err) } - - session = s.fromDBItem(dbSession) - s.Publish(pubsub.UpdatedEvent, session) - return session, nil + updatedSession := s.fromDBItem(dbSession) + s.broker.Publish(EventSessionUpdated, updatedSession) + return updatedSession, nil } -func (s *service) List(ctx context.Context) ([]Session, error) { - dbSessions, err := s.q.ListSessions(ctx) +func (s *service) Delete(ctx context.Context, id string) error { + s.mu.Lock() + dbSess, err := s.db.GetSessionByID(ctx, id) if err != nil { - return nil, err + s.mu.Unlock() + if err == sql.ErrNoRows { + return fmt.Errorf("session ID '%s' not found for deletion", id) + } + return fmt.Errorf("db.GetSessionByID before delete: %w", err) } - sessions := make([]Session, len(dbSessions)) - for i, dbSession := range dbSessions { - sessions[i] = s.fromDBItem(dbSession) + sessionToPublish := s.fromDBItem(dbSess) + s.mu.Unlock() + + s.mu.Lock() + defer s.mu.Unlock() + err = s.db.DeleteSession(ctx, id) + if err != nil { + return fmt.Errorf("db.DeleteSession: %w", err) } - return sessions, nil + s.broker.Publish(EventSessionDeleted, sessionToPublish) + return nil +} + +func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[Session] { + return s.broker.Subscribe(ctx) } -func (s service) fromDBItem(item db.Session) Session { +func (s *service) fromDBItem(item db.Session) Session { return Session{ ID: item.ID, ParentSessionID: item.ParentSessionID.String, @@ -160,10 +221,30 @@ func (s service) fromDBItem(item db.Session) Session { } } -func NewService(q db.Querier) Service { - broker := pubsub.NewBroker[Session]() - return &service{ - broker, - q, - } +func Create(ctx context.Context, title string) (Session, error) { + return GetService().Create(ctx, title) +} + +func CreateTaskSession(ctx context.Context, toolCallID, parentSessionID, title string) (Session, error) { + return GetService().CreateTaskSession(ctx, toolCallID, parentSessionID, title) +} + +func Get(ctx context.Context, id string) (Session, error) { + return GetService().Get(ctx, id) +} + +func List(ctx context.Context) ([]Session, error) { + return GetService().List(ctx) +} + +func Update(ctx context.Context, session Session) (Session, error) { + return GetService().Update(ctx, session) +} + +func Delete(ctx context.Context, id string) error { + return GetService().Delete(ctx, id) +} + +func Subscribe(ctx context.Context) <-chan pubsub.Event[Session] { + return GetService().Subscribe(ctx) } |
