fix: address review comments on InsertChatMessages (#23239)

Follow-up to #23220, addressing Cian's review comments:

- **SQL casing**: Uppercase `UNNEST` to match `NULLIF`/`COALESCE`
convention in the query.
- **Builder pattern**: `chatMessage` struct now uses unexported fields
with a `newChatMessage` constructor for required fields (role, content,
visibility, modelConfigID, contentVersion) and chainable builder methods
(`withCreatedBy`, `withCompressed`, `withUsage`, `withContextLimit`,
`withTotalCostMicros`, `withRuntimeMs`) for optional/nullable fields.
- **Batch test in chats_test**: Replaced the `for i := 0; i < 2` loop
with a single batch insert of 2 messages to actually exercise the batch
logic.
- **Multi-message querier test**: Added `BatchInsertMultipleMessages`
test verifying 3-message batch insert with role ordering, sequential
IDs, nullable field semantics (NULL for zero UUIDs and zero ints), and
token/cost assertions.

---------

Co-authored-by: Cian Johnston <cian@coder.com>
This commit is contained in:
Kyle Carberry
2026-03-18 13:06:44 -04:00
committed by GitHub
parent c46136ff73
commit d4a072b61e
5 changed files with 294 additions and 194 deletions
+166 -128
View File
@@ -487,33 +487,30 @@ func (p *Server) CreateChat(ctx context.Context, opts CreateOptions) (database.C
if err != nil {
return xerrors.Errorf("marshal system prompt: %w", err)
}
appendChatMessage(&msgParams, chatMessage{
Role: database.ChatMessageRoleSystem,
Content: systemContent,
Visibility: database.ChatMessageVisibilityModel,
ModelConfigID: opts.ModelConfigID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
})
appendChatMessage(&msgParams, newChatMessage(
database.ChatMessageRoleSystem,
systemContent,
database.ChatMessageVisibilityModel,
opts.ModelConfigID,
chatprompt.CurrentContentVersion,
))
}
appendChatMessage(&msgParams, chatMessage{
Role: database.ChatMessageRoleSystem,
Content: workspaceAwarenessContent,
Visibility: database.ChatMessageVisibilityModel,
ModelConfigID: opts.ModelConfigID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
})
appendChatMessage(&msgParams, newChatMessage(
database.ChatMessageRoleSystem,
workspaceAwarenessContent,
database.ChatMessageVisibilityModel,
opts.ModelConfigID,
chatprompt.CurrentContentVersion,
))
appendChatMessage(&msgParams, chatMessage{
Role: database.ChatMessageRoleUser,
Content: userContent,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: opts.ModelConfigID,
CreatedBy: opts.OwnerID,
ContentVersion: chatprompt.CurrentContentVersion,
})
appendChatMessage(&msgParams, newChatMessage(
database.ChatMessageRoleUser,
userContent,
database.ChatMessageVisibilityBoth,
opts.ModelConfigID,
chatprompt.CurrentContentVersion,
).withCreatedBy(opts.OwnerID))
_, err = tx.InsertChatMessages(ctx, msgParams)
if err != nil {
@@ -1109,27 +1106,82 @@ func insertChatMessageWithStore(
}
// chatMessage describes a single message to insert as part of a batch.
// For nullable UUID fields (ModelConfigID, CreatedBy), use uuid.Nil to
// represent NULL — the SQL uses NULLIF to convert zero UUIDs to NULL.
// For nullable int64 fields, use 0 to represent NULL — the SQL uses
// NULLIF to convert zeros to NULL.
// Use newChatMessage to create one, then chain builder methods for
// optional fields. For nullable UUID fields (ModelConfigID, CreatedBy),
// use uuid.Nil to represent NULL — the SQL uses NULLIF to convert zero
// UUIDs to NULL. For nullable int64 fields, use 0 to represent NULL
// the SQL uses NULLIF to convert zeros to NULL.
type chatMessage struct {
Role database.ChatMessageRole
Content pqtype.NullRawMessage
Visibility database.ChatMessageVisibility
ModelConfigID uuid.UUID
CreatedBy uuid.UUID
ContentVersion int16
Compressed bool
InputTokens int64
OutputTokens int64
TotalTokens int64
ReasoningTokens int64
CacheCreationTokens int64
CacheReadTokens int64
ContextLimit int64
TotalCostMicros int64
RuntimeMs int64
role database.ChatMessageRole
content pqtype.NullRawMessage
visibility database.ChatMessageVisibility
modelConfigID uuid.UUID
createdBy uuid.UUID
contentVersion int16
compressed bool
inputTokens int64
outputTokens int64
totalTokens int64
reasoningTokens int64
cacheCreationTokens int64
cacheReadTokens int64
contextLimit int64
totalCostMicros int64
runtimeMs int64
}
func newChatMessage(
role database.ChatMessageRole,
content pqtype.NullRawMessage,
visibility database.ChatMessageVisibility,
modelConfigID uuid.UUID,
contentVersion int16,
) chatMessage {
return chatMessage{
role: role,
content: content,
visibility: visibility,
modelConfigID: modelConfigID,
contentVersion: contentVersion,
}
}
func (m chatMessage) withCreatedBy(id uuid.UUID) chatMessage {
m.createdBy = id
return m
}
func (m chatMessage) withCompressed() chatMessage {
m.compressed = true
return m
}
func (m chatMessage) withUsage(
inputTokens, outputTokens, totalTokens, reasoningTokens,
cacheCreationTokens, cacheReadTokens int64,
) chatMessage {
m.inputTokens = inputTokens
m.outputTokens = outputTokens
m.totalTokens = totalTokens
m.reasoningTokens = reasoningTokens
m.cacheCreationTokens = cacheCreationTokens
m.cacheReadTokens = cacheReadTokens
return m
}
func (m chatMessage) withContextLimit(limit int64) chatMessage {
m.contextLimit = limit
return m
}
func (m chatMessage) withTotalCostMicros(cost int64) chatMessage {
m.totalCostMicros = cost
return m
}
func (m chatMessage) withRuntimeMs(ms int64) chatMessage {
m.runtimeMs = ms
return m
}
// appendChatMessage appends a single message to the batch insert params.
@@ -1137,22 +1189,22 @@ func appendChatMessage(
params *database.InsertChatMessagesParams,
msg chatMessage,
) {
params.CreatedBy = append(params.CreatedBy, msg.CreatedBy)
params.ModelConfigID = append(params.ModelConfigID, msg.ModelConfigID)
params.Role = append(params.Role, msg.Role)
params.Content = append(params.Content, string(msg.Content.RawMessage))
params.ContentVersion = append(params.ContentVersion, msg.ContentVersion)
params.Visibility = append(params.Visibility, msg.Visibility)
params.InputTokens = append(params.InputTokens, msg.InputTokens)
params.OutputTokens = append(params.OutputTokens, msg.OutputTokens)
params.TotalTokens = append(params.TotalTokens, msg.TotalTokens)
params.ReasoningTokens = append(params.ReasoningTokens, msg.ReasoningTokens)
params.CacheCreationTokens = append(params.CacheCreationTokens, msg.CacheCreationTokens)
params.CacheReadTokens = append(params.CacheReadTokens, msg.CacheReadTokens)
params.ContextLimit = append(params.ContextLimit, msg.ContextLimit)
params.Compressed = append(params.Compressed, msg.Compressed)
params.TotalCostMicros = append(params.TotalCostMicros, msg.TotalCostMicros)
params.RuntimeMs = append(params.RuntimeMs, msg.RuntimeMs)
params.CreatedBy = append(params.CreatedBy, msg.createdBy)
params.ModelConfigID = append(params.ModelConfigID, msg.modelConfigID)
params.Role = append(params.Role, msg.role)
params.Content = append(params.Content, string(msg.content.RawMessage))
params.ContentVersion = append(params.ContentVersion, msg.contentVersion)
params.Visibility = append(params.Visibility, msg.visibility)
params.InputTokens = append(params.InputTokens, msg.inputTokens)
params.OutputTokens = append(params.OutputTokens, msg.outputTokens)
params.TotalTokens = append(params.TotalTokens, msg.totalTokens)
params.ReasoningTokens = append(params.ReasoningTokens, msg.reasoningTokens)
params.CacheCreationTokens = append(params.CacheCreationTokens, msg.cacheCreationTokens)
params.CacheReadTokens = append(params.CacheReadTokens, msg.cacheReadTokens)
params.ContextLimit = append(params.ContextLimit, msg.contextLimit)
params.Compressed = append(params.Compressed, msg.compressed)
params.TotalCostMicros = append(params.TotalCostMicros, msg.totalCostMicros)
params.RuntimeMs = append(params.RuntimeMs, msg.runtimeMs)
}
func insertUserMessageAndSetPending(
@@ -1166,14 +1218,13 @@ func insertUserMessageAndSetPending(
msgParams := database.InsertChatMessagesParams{ //nolint:exhaustruct // Fields populated by appendChatMessage.
ChatID: lockedChat.ID,
}
appendChatMessage(&msgParams, chatMessage{
Role: database.ChatMessageRoleUser,
Content: content,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: modelConfigID,
CreatedBy: createdBy,
ContentVersion: chatprompt.CurrentContentVersion,
})
appendChatMessage(&msgParams, newChatMessage(
database.ChatMessageRoleUser,
content,
database.ChatMessageVisibilityBoth,
modelConfigID,
chatprompt.CurrentContentVersion,
).withCreatedBy(createdBy))
messages, err := insertChatMessageWithStore(ctx, store, msgParams)
if err != nil {
return database.ChatMessage{}, database.Chat{}, err
@@ -2135,17 +2186,16 @@ func (p *Server) tryAutoPromoteQueuedMessage(
msgParams := database.InsertChatMessagesParams{ //nolint:exhaustruct // Fields populated by appendChatMessage.
ChatID: chat.ID,
}
appendChatMessage(&msgParams, chatMessage{
Role: database.ChatMessageRoleUser,
Content: pqtype.NullRawMessage{
appendChatMessage(&msgParams, newChatMessage(
database.ChatMessageRoleUser,
pqtype.NullRawMessage{
RawMessage: nextQueued.Content,
Valid: len(nextQueued.Content) > 0,
},
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: chat.LastModelConfigID,
CreatedBy: chat.OwnerID,
ContentVersion: chatprompt.CurrentContentVersion,
})
database.ChatMessageVisibilityBoth,
chat.LastModelConfigID,
chatprompt.CurrentContentVersion,
).withCreatedBy(chat.OwnerID))
msgs, err := insertChatMessageWithStore(ctx, tx, msgParams)
if err != nil {
logger.Error(ctx, "failed to promote queued message",
@@ -2715,34 +2765,28 @@ func (p *Server) runChat(
}
if assistantContent.Valid {
appendChatMessage(&stepParams, chatMessage{
Role: database.ChatMessageRoleAssistant,
Content: assistantContent,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: modelConfig.ID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
InputTokens: inputTokens,
OutputTokens: outputTokens,
TotalTokens: totalTokens,
ReasoningTokens: reasoningTokens,
CacheCreationTokens: cacheCreationTokens,
CacheReadTokens: cacheReadTokens,
ContextLimit: contextLimit,
TotalCostMicros: totalCostVal,
RuntimeMs: runtimeMs,
})
appendChatMessage(&stepParams, newChatMessage(
database.ChatMessageRoleAssistant,
assistantContent,
database.ChatMessageVisibilityBoth,
modelConfig.ID,
chatprompt.CurrentContentVersion,
).withUsage(
inputTokens, outputTokens, totalTokens,
reasoningTokens, cacheCreationTokens, cacheReadTokens,
).withContextLimit(contextLimit).
withTotalCostMicros(totalCostVal).
withRuntimeMs(runtimeMs))
}
for _, resultContent := range toolResultContents {
appendChatMessage(&stepParams, chatMessage{
Role: database.ChatMessageRoleTool,
Content: resultContent,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: modelConfig.ID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
})
appendChatMessage(&stepParams, newChatMessage(
database.ChatMessageRoleTool,
resultContent,
database.ChatMessageVisibilityBoth,
modelConfig.ID,
chatprompt.CurrentContentVersion,
))
}
if len(stepParams.Role) > 0 {
@@ -3117,37 +3161,31 @@ func (p *Server) persistChatContextSummary(
}
// Hidden summary user message (not published to subscribers).
appendChatMessage(&summaryParams, chatMessage{
Role: database.ChatMessageRoleUser,
Content: systemContent,
Visibility: database.ChatMessageVisibilityModel,
ModelConfigID: modelConfigID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
Compressed: true,
})
appendChatMessage(&summaryParams, newChatMessage(
database.ChatMessageRoleUser,
systemContent,
database.ChatMessageVisibilityModel,
modelConfigID,
chatprompt.CurrentContentVersion,
).withCompressed())
// Assistant tool-call message.
appendChatMessage(&summaryParams, chatMessage{
Role: database.ChatMessageRoleAssistant,
Content: assistantContent,
Visibility: database.ChatMessageVisibilityUser,
ModelConfigID: modelConfigID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
Compressed: true,
})
appendChatMessage(&summaryParams, newChatMessage(
database.ChatMessageRoleAssistant,
assistantContent,
database.ChatMessageVisibilityUser,
modelConfigID,
chatprompt.CurrentContentVersion,
).withCompressed())
// Tool result message.
appendChatMessage(&summaryParams, chatMessage{
Role: database.ChatMessageRoleTool,
Content: toolResult,
Visibility: database.ChatMessageVisibilityBoth,
ModelConfigID: modelConfigID,
CreatedBy: uuid.Nil,
ContentVersion: chatprompt.CurrentContentVersion,
Compressed: true,
})
appendChatMessage(&summaryParams, newChatMessage(
database.ChatMessageRoleTool,
toolResult,
database.ChatMessageVisibilityBoth,
modelConfigID,
chatprompt.CurrentContentVersion,
).withCompressed())
allInserted, txErr := tx.InsertChatMessages(ctx, summaryParams)
if txErr != nil {
+28 -28
View File
@@ -4059,35 +4059,35 @@ func seedChatCostFixture(t *testing.T) chatCostTestFixture {
})
require.NoError(t, err)
var earliestCreatedAt time.Time
var latestCreatedAt time.Time
for i := 0; i < 2; i++ {
results, err := db.InsertChatMessages(dbauthz.AsSystemRestricted(ctx), database.InsertChatMessagesParams{
ChatID: chat.ID,
CreatedBy: []uuid.UUID{uuid.Nil},
ModelConfigID: []uuid.UUID{modelConfig.ID},
Role: []database.ChatMessageRole{"assistant"},
Content: []string{"null"},
ContentVersion: []int16{0},
Visibility: []database.ChatMessageVisibility{database.ChatMessageVisibilityBoth},
InputTokens: []int64{100},
OutputTokens: []int64{50},
TotalTokens: []int64{0},
ReasoningTokens: []int64{0},
CacheCreationTokens: []int64{0},
CacheReadTokens: []int64{0},
ContextLimit: []int64{0},
Compressed: []bool{false},
TotalCostMicros: []int64{500},
RuntimeMs: []int64{0},
})
require.NoError(t, err)
message := results[0]
if i == 0 || message.CreatedAt.Before(earliestCreatedAt) {
earliestCreatedAt = message.CreatedAt
results, err := db.InsertChatMessages(dbauthz.AsSystemRestricted(ctx), database.InsertChatMessagesParams{
ChatID: chat.ID,
CreatedBy: []uuid.UUID{uuid.Nil, uuid.Nil},
ModelConfigID: []uuid.UUID{modelConfig.ID, modelConfig.ID},
Role: []database.ChatMessageRole{"assistant", "assistant"},
Content: []string{"null", "null"},
ContentVersion: []int16{0, 0},
Visibility: []database.ChatMessageVisibility{database.ChatMessageVisibilityBoth, database.ChatMessageVisibilityBoth},
InputTokens: []int64{100, 100},
OutputTokens: []int64{50, 50},
TotalTokens: []int64{0, 0},
ReasoningTokens: []int64{0, 0},
CacheCreationTokens: []int64{0, 0},
CacheReadTokens: []int64{0, 0},
ContextLimit: []int64{0, 0},
Compressed: []bool{false, false},
TotalCostMicros: []int64{500, 500},
RuntimeMs: []int64{0, 0},
})
require.NoError(t, err)
require.Len(t, results, 2)
earliestCreatedAt := results[0].CreatedAt
latestCreatedAt := results[0].CreatedAt
for _, msg := range results {
if msg.CreatedAt.Before(earliestCreatedAt) {
earliestCreatedAt = msg.CreatedAt
}
if i == 0 || message.CreatedAt.After(latestCreatedAt) {
latestCreatedAt = message.CreatedAt
if msg.CreatedAt.After(latestCreatedAt) {
latestCreatedAt = msg.CreatedAt
}
}
+62
View File
@@ -9534,6 +9534,68 @@ func TestInsertChatMessages(t *testing.T) {
require.NoError(t, err)
require.Equal(t, modelConfigA.ID, gotChat.LastModelConfigID)
})
t.Run("BatchInsertMultipleMessages", func(t *testing.T) {
t.Parallel()
store, ctx, user, chat, _, modelConfigA := setupChat(t)
msgs, err := store.InsertChatMessages(ctx, database.InsertChatMessagesParams{
ChatID: chat.ID,
CreatedBy: []uuid.UUID{user.ID, uuid.Nil, uuid.Nil},
ModelConfigID: []uuid.UUID{modelConfigA.ID, modelConfigA.ID, modelConfigA.ID},
Role: []database.ChatMessageRole{database.ChatMessageRoleUser, database.ChatMessageRoleAssistant, database.ChatMessageRoleTool},
ContentVersion: []int16{chatprompt.CurrentContentVersion, chatprompt.CurrentContentVersion, chatprompt.CurrentContentVersion},
Visibility: []database.ChatMessageVisibility{database.ChatMessageVisibilityBoth, database.ChatMessageVisibilityBoth, database.ChatMessageVisibilityBoth},
Content: []string{`"hello"`, `"response"`, `"tool result"`},
InputTokens: []int64{10, 0, 0},
OutputTokens: []int64{0, 20, 0},
TotalTokens: []int64{10, 20, 0},
ReasoningTokens: []int64{0, 5, 0},
CacheCreationTokens: []int64{0, 0, 0},
CacheReadTokens: []int64{0, 0, 0},
ContextLimit: []int64{0, 0, 0},
Compressed: []bool{false, false, false},
TotalCostMicros: []int64{0, 100, 0},
RuntimeMs: []int64{0, 500, 0},
})
require.NoError(t, err)
require.Len(t, msgs, 3)
// Verify ordering and roles.
require.Equal(t, database.ChatMessageRoleUser, msgs[0].Role)
require.Equal(t, database.ChatMessageRoleAssistant, msgs[1].Role)
require.Equal(t, database.ChatMessageRoleTool, msgs[2].Role)
// Verify IDs are sequential.
require.Less(t, msgs[0].ID, msgs[1].ID)
require.Less(t, msgs[1].ID, msgs[2].ID)
// Verify nullable fields: user message has CreatedBy set.
require.True(t, msgs[0].CreatedBy.Valid)
require.Equal(t, user.ID, msgs[0].CreatedBy.UUID)
// Assistant and tool messages have NULL CreatedBy.
require.False(t, msgs[1].CreatedBy.Valid)
require.False(t, msgs[2].CreatedBy.Valid)
// Verify token fields stored as NULL when zero.
require.True(t, msgs[0].InputTokens.Valid)
require.Equal(t, int64(10), msgs[0].InputTokens.Int64)
require.False(t, msgs[0].OutputTokens.Valid) // 0 → NULL
require.True(t, msgs[1].OutputTokens.Valid)
require.Equal(t, int64(20), msgs[1].OutputTokens.Int64)
// Verify cost: assistant has cost, others NULL.
require.True(t, msgs[1].TotalCostMicros.Valid)
require.Equal(t, int64(100), msgs[1].TotalCostMicros.Int64)
require.False(t, msgs[0].TotalCostMicros.Valid)
require.False(t, msgs[2].TotalCostMicros.Valid)
// Verify runtime_ms on assistant message.
require.True(t, msgs[1].RuntimeMs.Valid)
require.Equal(t, int64(500), msgs[1].RuntimeMs.Int64)
require.False(t, msgs[0].RuntimeMs.Valid)
})
}
func TestGetChatMessagesForPromptByChatID(t *testing.T) {
+19 -19
View File
@@ -4848,7 +4848,7 @@ WITH updated_chat AS (
SET
last_model_config_id = (
SELECT val
FROM unnest($3::uuid[])
FROM UNNEST($3::uuid[])
WITH ORDINALITY AS t(val, ord)
WHERE val != '00000000-0000-0000-0000-000000000000'::uuid
ORDER BY ord DESC
@@ -4858,12 +4858,12 @@ WITH updated_chat AS (
id = $1::uuid
AND EXISTS (
SELECT 1
FROM unnest($3::uuid[])
FROM UNNEST($3::uuid[])
WHERE unnest != '00000000-0000-0000-0000-000000000000'::uuid
)
AND chats.last_model_config_id IS DISTINCT FROM (
SELECT val
FROM unnest($3::uuid[])
FROM UNNEST($3::uuid[])
WITH ORDINALITY AS t(val, ord)
WHERE val != '00000000-0000-0000-0000-000000000000'::uuid
ORDER BY ord DESC
@@ -4891,22 +4891,22 @@ INSERT INTO chat_messages (
)
SELECT
$1::uuid,
NULLIF(unnest($2::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
NULLIF(unnest($3::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
unnest($4::chat_message_role[]),
unnest($5::text[])::jsonb,
unnest($6::smallint[]),
unnest($7::chat_message_visibility[]),
NULLIF(unnest($8::bigint[]), 0),
NULLIF(unnest($9::bigint[]), 0),
NULLIF(unnest($10::bigint[]), 0),
NULLIF(unnest($11::bigint[]), 0),
NULLIF(unnest($12::bigint[]), 0),
NULLIF(unnest($13::bigint[]), 0),
NULLIF(unnest($14::bigint[]), 0),
unnest($15::boolean[]),
NULLIF(unnest($16::bigint[]), 0),
NULLIF(unnest($17::bigint[]), 0)
NULLIF(UNNEST($2::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
NULLIF(UNNEST($3::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
UNNEST($4::chat_message_role[]),
UNNEST($5::text[])::jsonb,
UNNEST($6::smallint[]),
UNNEST($7::chat_message_visibility[]),
NULLIF(UNNEST($8::bigint[]), 0),
NULLIF(UNNEST($9::bigint[]), 0),
NULLIF(UNNEST($10::bigint[]), 0),
NULLIF(UNNEST($11::bigint[]), 0),
NULLIF(UNNEST($12::bigint[]), 0),
NULLIF(UNNEST($13::bigint[]), 0),
NULLIF(UNNEST($14::bigint[]), 0),
UNNEST($15::boolean[]),
NULLIF(UNNEST($16::bigint[]), 0),
NULLIF(UNNEST($17::bigint[]), 0)
RETURNING
id, chat_id, model_config_id, created_at, role, content, visibility, input_tokens, output_tokens, total_tokens, reasoning_tokens, cache_creation_tokens, cache_read_tokens, context_limit, compressed, created_by, content_version, total_cost_micros, runtime_ms
`
+19 -19
View File
@@ -185,7 +185,7 @@ WITH updated_chat AS (
SET
last_model_config_id = (
SELECT val
FROM unnest(@model_config_id::uuid[])
FROM UNNEST(@model_config_id::uuid[])
WITH ORDINALITY AS t(val, ord)
WHERE val != '00000000-0000-0000-0000-000000000000'::uuid
ORDER BY ord DESC
@@ -195,12 +195,12 @@ WITH updated_chat AS (
id = @chat_id::uuid
AND EXISTS (
SELECT 1
FROM unnest(@model_config_id::uuid[])
FROM UNNEST(@model_config_id::uuid[])
WHERE unnest != '00000000-0000-0000-0000-000000000000'::uuid
)
AND chats.last_model_config_id IS DISTINCT FROM (
SELECT val
FROM unnest(@model_config_id::uuid[])
FROM UNNEST(@model_config_id::uuid[])
WITH ORDINALITY AS t(val, ord)
WHERE val != '00000000-0000-0000-0000-000000000000'::uuid
ORDER BY ord DESC
@@ -228,22 +228,22 @@ INSERT INTO chat_messages (
)
SELECT
@chat_id::uuid,
NULLIF(unnest(@created_by::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
NULLIF(unnest(@model_config_id::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
unnest(@role::chat_message_role[]),
unnest(@content::text[])::jsonb,
unnest(@content_version::smallint[]),
unnest(@visibility::chat_message_visibility[]),
NULLIF(unnest(@input_tokens::bigint[]), 0),
NULLIF(unnest(@output_tokens::bigint[]), 0),
NULLIF(unnest(@total_tokens::bigint[]), 0),
NULLIF(unnest(@reasoning_tokens::bigint[]), 0),
NULLIF(unnest(@cache_creation_tokens::bigint[]), 0),
NULLIF(unnest(@cache_read_tokens::bigint[]), 0),
NULLIF(unnest(@context_limit::bigint[]), 0),
unnest(@compressed::boolean[]),
NULLIF(unnest(@total_cost_micros::bigint[]), 0),
NULLIF(unnest(@runtime_ms::bigint[]), 0)
NULLIF(UNNEST(@created_by::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
NULLIF(UNNEST(@model_config_id::uuid[]), '00000000-0000-0000-0000-000000000000'::uuid),
UNNEST(@role::chat_message_role[]),
UNNEST(@content::text[])::jsonb,
UNNEST(@content_version::smallint[]),
UNNEST(@visibility::chat_message_visibility[]),
NULLIF(UNNEST(@input_tokens::bigint[]), 0),
NULLIF(UNNEST(@output_tokens::bigint[]), 0),
NULLIF(UNNEST(@total_tokens::bigint[]), 0),
NULLIF(UNNEST(@reasoning_tokens::bigint[]), 0),
NULLIF(UNNEST(@cache_creation_tokens::bigint[]), 0),
NULLIF(UNNEST(@cache_read_tokens::bigint[]), 0),
NULLIF(UNNEST(@context_limit::bigint[]), 0),
UNNEST(@compressed::boolean[]),
NULLIF(UNNEST(@total_cost_micros::bigint[]), 0),
NULLIF(UNNEST(@runtime_ms::bigint[]), 0)
RETURNING
*;