Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
172 changes: 0 additions & 172 deletions coderd/exp_chats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,36 +253,6 @@ func (s *failNextUpdateChatModelConfigStore) UpdateChatModelConfig(
return s.Store.UpdateChatModelConfig(ctx, arg)
}

func requireChatUsageLimitExceededError(
t *testing.T,
err error,
wantSpentMicros int64,
wantLimitMicros int64,
wantResetsAt time.Time,
) *codersdk.ChatUsageLimitExceededResponse {
t.Helper()

sdkErr, ok := codersdk.AsError(err)
require.True(t, ok)
require.Equal(t, http.StatusConflict, sdkErr.StatusCode())
require.Equal(t, "Chat usage limit exceeded.", sdkErr.Message)

limitErr := codersdk.ChatUsageLimitExceededFrom(err)
require.NotNil(t, limitErr)
require.Equal(t, "Chat usage limit exceeded.", limitErr.Message)
require.Equal(t, wantSpentMicros, limitErr.SpentMicros)
require.Equal(t, wantLimitMicros, limitErr.LimitMicros)
require.True(
t,
limitErr.ResetsAt.Equal(wantResetsAt),
"expected resets_at %s, got %s",
wantResetsAt.UTC().Format(time.RFC3339),
limitErr.ResetsAt.UTC().Format(time.RFC3339),
)

return limitErr
}

func enableDailyChatUsageLimit(
ctx context.Context,
t *testing.T,
Expand Down Expand Up @@ -893,33 +863,6 @@ func TestPostChats(t *testing.T) {
require.Equal(t, `content[0].type "image" is not supported.`, sdkErr.Detail)
})

t.Run("UsageLimitExceeded", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitLong)
client, db := newChatClientWithDatabase(t)
user := coderdtest.CreateFirstUser(t, client.Client)
modelConfig := createChatModelConfig(t, client)
wantResetsAt := enableDailyChatUsageLimit(ctx, t, db, 100)

existingChat := dbgen.Chat(t, db, database.Chat{
OrganizationID: user.OrganizationID,
OwnerID: user.UserID,
LastModelConfigID: modelConfig.ID,
Title: "existing-limit-chat",
})
insertAssistantCostMessage(t, db, existingChat.ID, modelConfig.ID, 100)

_, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: user.OrganizationID,
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "over limit",
}},
})
requireChatUsageLimitExceededError(t, err, 100, 100, wantResetsAt)
})

t.Run("NilOrganizationID", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -7150,35 +7093,6 @@ func TestPostChatMessages(t *testing.T) {
require.Equal(t, "content[0].text cannot be empty.", sdkErr.Detail)
})

t.Run("UsageLimitExceeded", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitLong)
client, db := newChatClientWithDatabase(t)
firstUser := coderdtest.CreateFirstUser(t, client.Client)
modelConfig := createChatModelConfig(t, client)

chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: firstUser.OrganizationID,
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "initial message for usage-limit test",
}},
})
require.NoError(t, err)

wantResetsAt := enableDailyChatUsageLimit(ctx, t, db, 100)
insertAssistantCostMessage(t, db, chat.ID, modelConfig.ID, 100)

_, err = client.CreateChatMessage(ctx, chat.ID, codersdk.CreateChatMessageRequest{
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "over limit",
}},
})
requireChatUsageLimitExceededError(t, err, 100, 100, wantResetsAt)
})

t.Run("ChatNotFound", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -8784,47 +8698,6 @@ func TestPatchChatMessage(t *testing.T) {
require.True(t, foundFileInChat, "chat should preserve file_id after edit")
})

t.Run("UsageLimitExceeded", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitLong)
client, db := newChatClientWithDatabase(t)
firstUser := coderdtest.CreateFirstUser(t, client.Client)
modelConfig := createChatModelConfig(t, client)

chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: firstUser.OrganizationID,
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "hello before edit",
}},
})
require.NoError(t, err)

messagesResult, err := client.GetChatMessages(ctx, chat.ID, nil)
require.NoError(t, err)

var userMessageID int64
for _, message := range messagesResult.Messages {
if message.Role == codersdk.ChatMessageRoleUser {
userMessageID = message.ID
break
}
}
require.NotZero(t, userMessageID)

wantResetsAt := enableDailyChatUsageLimit(ctx, t, db, 100)
insertAssistantCostMessage(t, db, chat.ID, modelConfig.ID, 100)

_, err = client.EditChatMessage(ctx, chat.ID, userMessageID, codersdk.EditChatMessageRequest{
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "edited over limit",
}},
})
requireChatUsageLimitExceededError(t, err, 100, 100, wantResetsAt)
})

t.Run("MessageNotFound", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -9665,51 +9538,6 @@ func TestRegenerateChatTitle(t *testing.T) {
requireSDKError(t, err, http.StatusUnauthorized)
})

t.Run("UsageLimitExceeded", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitLong)
client, db := newChatClientWithDatabase(t)
firstUser := coderdtest.CreateFirstUser(t, client.Client)
modelConfig := createChatModelConfig(t, client)

chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: firstUser.OrganizationID,
Content: []codersdk.ChatInputPart{{
Type: codersdk.ChatInputPartTypeText,
Text: "chat over usage limit",
}},
})
require.NoError(t, err)

wantResetsAt := enableDailyChatUsageLimit(ctx, t, db, 100)
insertAssistantCostMessage(t, db, chat.ID, modelConfig.ID, 100)

_, err = db.UpdateChatStatus(dbauthz.AsSystemRestricted(ctx), database.UpdateChatStatusParams{
ID: chat.ID,
Status: database.ChatStatusWaiting,
WorkerID: uuid.NullUUID{},
StartedAt: sql.NullTime{},
HeartbeatAt: sql.NullTime{},
LastError: pqtype.NullRawMessage{},
})
require.NoError(t, err)

_, err = client.RegenerateChatTitle(ctx, chat.ID)
limitErr := codersdk.ChatUsageLimitExceededFrom(err)
require.NotNil(t, limitErr)
require.Equal(t, "Chat usage limit exceeded.", limitErr.Message)
require.Equal(t, int64(100), limitErr.SpentMicros)
require.Equal(t, int64(100), limitErr.LimitMicros)
require.True(
t,
limitErr.ResetsAt.Equal(wantResetsAt),
"expected resets_at %s, got %s",
wantResetsAt.UTC().Format(time.RFC3339),
limitErr.ResetsAt.UTC().Format(time.RFC3339),
)
})

t.Run("PasteOnlyChat", func(t *testing.T) {
t.Parallel()

Expand Down
25 changes: 3 additions & 22 deletions coderd/x/chatd/chatd.go
Original file line number Diff line number Diff line change
Expand Up @@ -1518,28 +1518,9 @@ func (p *Server) SendMessage(
return result, nil
}

func (p *Server) checkUsageLimit(ctx context.Context, store database.Store, ownerID uuid.UUID, organizationID uuid.NullUUID) error {
status, err := ResolveUsageLimitStatus(ctx, store, ownerID, organizationID, time.Now())
if err != nil {
// Fail open: never block chat due to a limit-resolution failure.
p.logger.Warn(ctx, "usage limit check failed, allowing message",
slog.F("owner_id", ownerID),
slog.Error(err),
)
return nil
}
if status == nil {
return nil
}
// Block when current spend reaches or exceeds limit (>= ensures
// the user cannot start new conversations once the limit is hit).
if status.SpendLimitMicros != nil && status.CurrentSpend >= *status.SpendLimitMicros {
return &UsageLimitExceededError{
LimitMicros: *status.SpendLimitMicros,
ConsumedMicros: status.CurrentSpend,
PeriodEnd: status.PeriodEnd,
}
}
// checkUsageLimit is a no-op. Usage limits (a.k.a. "Budgets") are now enforced
// by AI Gateway.
func (*Server) checkUsageLimit(_ context.Context, _ database.Store, _ uuid.UUID, _ uuid.NullUUID) error {
Comment thread
johnstcn marked this conversation as resolved.
return nil
}

Expand Down
2 changes: 0 additions & 2 deletions coderd/x/chatd/chatd_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,6 @@ func TestRegenerateChatTitle_PersistsAndBroadcasts(t *testing.T) {
}}, nil).AnyTimes()
db.EXPECT().GetAIProviderKeysByProviderID(gomock.Any(), providerID).Return([]database.AIProviderKey{{ProviderID: providerID, APIKey: "test-key"}}, nil).AnyTimes()
db.EXPECT().GetAIProviderKeysByProviderIDs(gomock.Any(), gomock.Any()).Return([]database.AIProviderKey{{ProviderID: providerID, APIKey: "test-key"}}, nil).AnyTimes()
db.EXPECT().GetChatUsageLimitConfig(gomock.Any()).Return(database.ChatUsageLimitConfig{}, sql.ErrNoRows)
db.EXPECT().GetChatGatewayAPIKey(gomock.Any(), database.GetChatGatewayAPIKeyParams{UserID: ownerID, TokenName: GatewayTokenName(ownerID)}).Return(database.APIKey{ID: activeAPIKeyID, UserID: ownerID, ExpiresAt: time.Now().Add(48 * time.Hour)}, nil)
db.EXPECT().GetChatMessagesByChatIDAscPaginated(
gomock.Any(),
Expand Down Expand Up @@ -1250,7 +1249,6 @@ func TestRegenerateChatTitle_SkipsPersistWhenTitleChangedConcurrently(t *testing
}}, nil).AnyTimes()
db.EXPECT().GetAIProviderKeysByProviderID(gomock.Any(), providerID).Return([]database.AIProviderKey{{ProviderID: providerID, APIKey: "test-key"}}, nil).AnyTimes()
db.EXPECT().GetAIProviderKeysByProviderIDs(gomock.Any(), gomock.Any()).Return([]database.AIProviderKey{{ProviderID: providerID, APIKey: "test-key"}}, nil).AnyTimes()
db.EXPECT().GetChatUsageLimitConfig(gomock.Any()).Return(database.ChatUsageLimitConfig{}, sql.ErrNoRows)
db.EXPECT().GetChatGatewayAPIKey(gomock.Any(), database.GetChatGatewayAPIKeyParams{UserID: ownerID, TokenName: GatewayTokenName(ownerID)}).Return(database.APIKey{ID: activeAPIKeyID, UserID: ownerID, ExpiresAt: time.Now().Add(48 * time.Hour)}, nil)
db.EXPECT().GetChatMessagesByChatIDAscPaginated(
gomock.Any(),
Expand Down
Loading
Loading