mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: persist per-turn model on chats and queued messages (#24688)
Previously, `chats.last_model_config_id` was not updated when a user sent a mid-chat message with a different model, and queued messages did not store their own per-turn model, so promotion ran against whatever the chat row said at promote time. Chat watch events also did not merge `last_model_config_id` into the site's root, child, and per-chat caches, so sidebar labels stayed stale after direct sends and queued promotions. - Add nullable `chat_queued_messages.model_config_id`, backfilled from `chats.last_model_config_id`. Queued inserts round-trip the effective model id at enqueue time. - In `coderd/x/chatd`, direct sends update `chats.last_model_config_id` inside the same transaction that inserts the admitted user message. Manual promotion and auto-promotion use the queued row's stored `model_config_id`, with a fallback to `chats.last_model_config_id` for legacy NULL rows during rollout. `PromoteQueuedOptions.ModelConfigID` is now ignored. - On the site, extract `mergeWatchedChatSummary` and `mergeWatchedChatIntoCaches` in `site/src/api/queries/chats.ts` so status-change watch events merge `last_model_config_id` into the root infinite chat list, the parent-embedded child entry, and the per-chat `chatKey(chatId)` cache. `updated_at` guards against stale watch payloads clobbering newer cached state, while diff status events still merge their PR metadata because they are timestamped outside the chat row. Watch timestamps are compared as instants so variable fractional precision does not make fresh events look stale. - Queued promotion validates stored model config IDs before admission. Invalid legacy queued IDs fall back to the chat's current model config instead of dropping the queued message during auto-promotion. - Backend and frontend regression coverage added for admission, queue promotion (including FIFO across mixed models, legacy NULL fallback, and invalid queued model IDs), and chat watch cache merging. > Mux is acting on Mike's behalf.
This commit is contained in:
@@ -1517,10 +1517,11 @@ func ChatQueuedMessage(message database.ChatQueuedMessage) codersdk.ChatQueuedMe
|
||||
}
|
||||
|
||||
return codersdk.ChatQueuedMessage{
|
||||
ID: message.ID,
|
||||
ChatID: message.ChatID,
|
||||
Content: parts,
|
||||
CreatedAt: message.CreatedAt,
|
||||
ID: message.ID,
|
||||
ChatID: message.ChatID,
|
||||
ModelConfigID: nullUUIDPtr(message.ModelConfigID),
|
||||
Content: parts,
|
||||
CreatedAt: message.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+2
-1
@@ -1422,7 +1422,8 @@ CREATE TABLE chat_queued_messages (
|
||||
id bigint NOT NULL,
|
||||
chat_id uuid NOT NULL,
|
||||
content jsonb NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
model_config_id uuid
|
||||
);
|
||||
|
||||
CREATE SEQUENCE chat_queued_messages_id_seq
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE chat_queued_messages
|
||||
DROP COLUMN model_config_id;
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE chat_queued_messages
|
||||
ADD COLUMN model_config_id uuid;
|
||||
|
||||
UPDATE chat_queued_messages AS cqm
|
||||
SET model_config_id = chats.last_model_config_id
|
||||
FROM chats
|
||||
WHERE chats.id = cqm.chat_id
|
||||
AND cqm.model_config_id IS NULL;
|
||||
@@ -4509,10 +4509,11 @@ type ChatProvider struct {
|
||||
}
|
||||
|
||||
type ChatQueuedMessage struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
|
||||
Content json.RawMessage `db:"content" json:"content"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ID int64 `db:"id" json:"id"`
|
||||
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
|
||||
Content json.RawMessage `db:"content" json:"content"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ModelConfigID uuid.NullUUID `db:"model_config_id" json:"model_config_id"`
|
||||
}
|
||||
|
||||
type ChatUsageLimitConfig struct {
|
||||
|
||||
@@ -6753,7 +6753,7 @@ func (q *sqlQuerier) GetChatModelConfigsForTelemetry(ctx context.Context) ([]Get
|
||||
}
|
||||
|
||||
const getChatQueuedMessages = `-- name: GetChatQueuedMessages :many
|
||||
SELECT id, chat_id, content, created_at FROM chat_queued_messages
|
||||
SELECT id, chat_id, content, created_at, model_config_id FROM chat_queued_messages
|
||||
WHERE chat_id = $1
|
||||
ORDER BY id ASC
|
||||
`
|
||||
@@ -6772,6 +6772,7 @@ func (q *sqlQuerier) GetChatQueuedMessages(ctx context.Context, chatID uuid.UUID
|
||||
&i.ChatID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.ModelConfigID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -7642,24 +7643,30 @@ func (q *sqlQuerier) InsertChatMessages(ctx context.Context, arg InsertChatMessa
|
||||
}
|
||||
|
||||
const insertChatQueuedMessage = `-- name: InsertChatQueuedMessage :one
|
||||
INSERT INTO chat_queued_messages (chat_id, content)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, chat_id, content, created_at
|
||||
INSERT INTO chat_queued_messages (chat_id, content, model_config_id)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3::uuid
|
||||
)
|
||||
RETURNING id, chat_id, content, created_at, model_config_id
|
||||
`
|
||||
|
||||
type InsertChatQueuedMessageParams struct {
|
||||
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
|
||||
Content json.RawMessage `db:"content" json:"content"`
|
||||
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
|
||||
Content json.RawMessage `db:"content" json:"content"`
|
||||
ModelConfigID uuid.NullUUID `db:"model_config_id" json:"model_config_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertChatQueuedMessage(ctx context.Context, arg InsertChatQueuedMessageParams) (ChatQueuedMessage, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertChatQueuedMessage, arg.ChatID, arg.Content)
|
||||
row := q.db.QueryRowContext(ctx, insertChatQueuedMessage, arg.ChatID, arg.Content, arg.ModelConfigID)
|
||||
var i ChatQueuedMessage
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ChatID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.ModelConfigID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -7884,7 +7891,7 @@ WHERE id = (
|
||||
ORDER BY cqm.id ASC
|
||||
LIMIT 1
|
||||
)
|
||||
RETURNING id, chat_id, content, created_at
|
||||
RETURNING id, chat_id, content, created_at, model_config_id
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) PopNextQueuedMessage(ctx context.Context, chatID uuid.UUID) (ChatQueuedMessage, error) {
|
||||
@@ -7895,6 +7902,7 @@ func (q *sqlQuerier) PopNextQueuedMessage(ctx context.Context, chatID uuid.UUID)
|
||||
&i.ChatID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.ModelConfigID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -882,8 +882,12 @@ RETURNING
|
||||
*;
|
||||
|
||||
-- name: InsertChatQueuedMessage :one
|
||||
INSERT INTO chat_queued_messages (chat_id, content)
|
||||
VALUES (@chat_id, @content)
|
||||
INSERT INTO chat_queued_messages (chat_id, content, model_config_id)
|
||||
VALUES (
|
||||
@chat_id,
|
||||
@content,
|
||||
sqlc.narg('model_config_id')::uuid
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetChatQueuedMessages :many
|
||||
|
||||
Reference in New Issue
Block a user