mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd): stop manual title generation from writing to chat_messages (#27087)
Coder Agents chats could get stuck showing "Thinking" forever when a title regenerate/propose request ran while a generation was in flight. Manual title generation recorded token cost by inserting a hidden assistant message into `chat_messages` and immediately soft-deleting it. Triggers on that table sync `chats.history_version` to `snapshot_version`, so this out-of-band write broke the `history_version` fence of an in-flight generation task, killing it without a replacement and leaving the chat stuck in `running`. Remove the accounting path entirely; AI Gateway already records title-call usage in `aibridge_interceptions`/`aibridge_token_usages`. The manual title endpoints no longer write to `chat_messages` at all, and new regression tests assert `history_version` stays untouched. Note this intentionally drops title-generation cost from chatd's chat-level cost surfaces; it still counts against the user's AI budget via AI Gateway. Closes CODAGT-595
This commit is contained in:
@@ -9085,6 +9085,43 @@ func TestRegenerateChatTitle(t *testing.T) {
|
||||
require.Equal(t, "Test Chat", updated.Title)
|
||||
})
|
||||
|
||||
t.Run("DoesNotBumpHistoryVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
LastModelConfigID: modelConfig.ID,
|
||||
Title: "history fence chat",
|
||||
})
|
||||
seedManualTitleSourceMessage(t, db, chat, modelConfig.ID)
|
||||
|
||||
// Leave history_version lagging snapshot_version, as when a
|
||||
// generation task is in flight. A chat_messages write here would
|
||||
// sync it and break that task's commit fence.
|
||||
_, err := db.LockChatAndBumpSnapshotVersion(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
before, err := db.GetChatByID(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, before.SnapshotVersion, before.HistoryVersion,
|
||||
"setup must leave history_version lagging snapshot_version")
|
||||
|
||||
updated, err := client.RegenerateChatTitle(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Test Chat", updated.Title)
|
||||
|
||||
after, err := db.GetChatByID(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before.HistoryVersion, after.HistoryVersion,
|
||||
"manual title regeneration must not touch chat_messages")
|
||||
})
|
||||
|
||||
t.Run("NoDefaultModelConfig", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -9256,6 +9293,41 @@ func TestProposeChatTitle(t *testing.T) {
|
||||
require.True(t, persisted.UpdatedAt.Equal(before.UpdatedAt))
|
||||
})
|
||||
|
||||
t.Run("DoesNotBumpHistoryVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
LastModelConfigID: modelConfig.ID,
|
||||
Title: "history fence chat",
|
||||
})
|
||||
seedManualTitleSourceMessage(t, db, chat, modelConfig.ID)
|
||||
|
||||
// See the matching TestRegenerateChatTitle subtest.
|
||||
_, err := db.LockChatAndBumpSnapshotVersion(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
before, err := db.GetChatByID(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, before.SnapshotVersion, before.HistoryVersion,
|
||||
"setup must leave history_version lagging snapshot_version")
|
||||
|
||||
resp, err := client.ProposeChatTitle(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Test Chat", resp.Title)
|
||||
|
||||
after, err := db.GetChatByID(dbauthz.AsSystemRestricted(ctx), chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before.HistoryVersion, after.HistoryVersion,
|
||||
"title proposal must not touch chat_messages")
|
||||
})
|
||||
|
||||
t.Run("NoDefaultModelConfig", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user