Skip to content
Draft

pr 6 #26112

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 70 additions & 17 deletions coderd/workspaceagents.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/coderd/x/chatd"
"github.com/coder/coder/v2/coderd/x/chatd/chatprompt"
"github.com/coder/coder/v2/coderd/x/chatd/chatstate"
"github.com/coder/coder/v2/coderd/x/gitsync"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
Expand Down Expand Up @@ -2551,10 +2552,12 @@ func (api *API) workspaceAgentAddChatContext(rw http.ResponseWriter, r *http.Req
return
}

err = api.Database.InTx(func(tx database.Store) error {
locked, err := tx.GetChatByIDForUpdate(sysCtx, chat.ID)
machine := chatstate.NewChatMachine(api.Database, api.Pubsub, chat.ID, chatstate.Options{})
err = machine.Update(sysCtx, func(tx *chatstate.Tx) error {
store := tx.Store()
locked, err := store.GetChatByID(sysCtx, chat.ID)
if err != nil {
return xerrors.Errorf("lock chat: %w", err)
return xerrors.Errorf("load chat: %w", err)
}
if !isActiveAgentChat(locked) {
return errChatNotActive
Expand All @@ -2565,26 +2568,30 @@ func (api *API) workspaceAgentAddChatContext(rw http.ResponseWriter, r *http.Req
if locked.OwnerID != workspace.OwnerID {
return errChatDoesNotBelongToWorkspaceOwner
}
apiKeyID, err := resolveAgentChatContextAPIKeyID(sysCtx, tx, locked)
apiKeyID, err := resolveAgentChatContextAPIKeyID(sysCtx, store, locked)
if err != nil {
return err
}
if _, err := tx.InsertChatMessages(sysCtx, chatd.BuildSingleUserChatMessageInsertParams(
chat.ID,
apiKeyID,
content,
database.ChatMessageVisibilityBoth,
locked.LastModelConfigID,
chatprompt.CurrentContentVersion,
uuid.Nil,
)); err != nil {
return xerrors.Errorf("insert context message: %w", err)
}
if err := updateAgentChatLastInjectedContextFromMessages(sysCtx, api.Logger, tx, chat.ID); err != nil {
sendResult, err := tx.SendMessage(chatstate.SendMessageInput{
Message: agentChatContextStateMessage(
content,
locked.LastModelConfigID,
locked.OwnerID,
apiKeyID,
),
BusyBehavior: chatstate.BusyBehaviorInterrupt,
})
if err != nil {
return err
}
if len(sendResult.InsertedMessages) == 0 {
return nil
}
if err := updateAgentChatLastInjectedContextFromMessages(sysCtx, api.Logger, store, chat.ID); err != nil {
return xerrors.Errorf("rebuild injected context cache: %w", err)
}
return nil
}, nil)
})
if err != nil {
if errors.Is(err, errChatNotActive) || errors.Is(err, errChatDoesNotBelongToAgent) || errors.Is(err, errChatDoesNotBelongToWorkspaceOwner) {
writeAgentChatError(ctx, rw, err)
Expand All @@ -2596,6 +2603,35 @@ func (api *API) workspaceAgentAddChatContext(rw http.ResponseWriter, r *http.Req
})
return
}
if errors.Is(err, chatstate.ErrMessageQueueFull) {
var queueFull *chatstate.MessageQueueFullError
detail := ""
if errors.As(err, &queueFull) {
detail = fmt.Sprintf("Maximum %d messages can be queued.", queueFull.Max)
}
httpapi.Write(ctx, rw, http.StatusTooManyRequests, codersdk.Response{
Message: "Message queue is full.",
Detail: detail,
})
return
}
if errors.Is(err, chatstate.ErrInvalidState) {
httpapi.Write(ctx, rw, http.StatusConflict, codersdk.Response{
Message: "Chat is in an invalid state.",
})
return
}
if errors.Is(err, chatstate.ErrTransitionNotAllowed) {
httpapi.Write(ctx, rw, http.StatusConflict, codersdk.Response{
Message: "Chat is not in a state that accepts new context.",
Detail: err.Error(),
})
return
}
if errors.Is(err, chatstate.ErrChatNotFound) {
writeAgentChatError(ctx, rw, errChatNotFound)
return
}
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to persist context message.",
Detail: err.Error(),
Expand Down Expand Up @@ -2817,6 +2853,23 @@ func resolveAgentChatContextAPIKeyID(ctx context.Context, db database.Store, cha
return newest.ID, nil
}

func agentChatContextStateMessage(
content pqtype.NullRawMessage,
modelConfigID uuid.UUID,
ownerID uuid.UUID,
apiKeyID string,
) chatstate.Message {
return chatstate.Message{
Role: database.ChatMessageRoleUser,
Content: content,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: modelConfigID != uuid.Nil},
CreatedBy: uuid.NullUUID{UUID: ownerID, Valid: ownerID != uuid.Nil},
ContentVersion: chatprompt.CurrentContentVersion,
APIKeyID: sql.NullString{String: apiKeyID, Valid: apiKeyID != ""},
}
}

func clearAgentChatContext(
ctx context.Context,
db database.Store,
Expand Down
Loading
Loading