mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
d2d956edb1
Despite the SDK type having an `Archived` field for chats, this data was
never fetched from the database — the `GetChatsByOwnerID` query
hardcoded `AND archived = false`, and the `convertChat` function never
mapped the field.
This PR adds an optional `archived` query parameter to `GET
/api/experimental/chats`:
| Value | Behavior |
|-------|----------|
| *(not provided)* | Returns all chats (active and archived) |
| `archived=false` | Returns only non-archived chats |
| `archived=true` | Returns only archived chats |
This follows the same pattern used by template versions
(`sqlc.narg('archived')` nullable boolean).
Also fixes `convertChat` to populate the `Archived` field in API
responses, which was never being set despite existing on the SDK type.
414 lines
8.7 KiB
SQL
414 lines
8.7 KiB
SQL
-- name: ArchiveChatByID :exec
|
|
UPDATE chats SET archived = true, updated_at = NOW()
|
|
WHERE id = @id OR root_chat_id = @id;
|
|
|
|
-- name: UnarchiveChatByID :exec
|
|
UPDATE chats SET archived = false, updated_at = NOW() WHERE id = @id::uuid;
|
|
|
|
-- name: DeleteChatMessagesByChatID :exec
|
|
DELETE FROM
|
|
chat_messages
|
|
WHERE
|
|
chat_id = @chat_id::uuid;
|
|
|
|
-- name: DeleteChatMessagesAfterID :exec
|
|
DELETE FROM
|
|
chat_messages
|
|
WHERE
|
|
chat_id = @chat_id::uuid
|
|
AND id > @after_id::bigint;
|
|
|
|
-- name: GetChatByID :one
|
|
SELECT
|
|
*
|
|
FROM
|
|
chats
|
|
WHERE
|
|
id = @id::uuid;
|
|
|
|
-- name: GetChatMessageByID :one
|
|
SELECT
|
|
*
|
|
FROM
|
|
chat_messages
|
|
WHERE
|
|
id = @id::bigint;
|
|
|
|
-- name: GetChatMessagesByChatID :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
chat_messages
|
|
WHERE
|
|
chat_id = @chat_id::uuid
|
|
AND id > @after_id::bigint
|
|
AND visibility IN ('user', 'both')
|
|
ORDER BY
|
|
created_at ASC;
|
|
|
|
-- name: GetChatMessagesForPromptByChatID :many
|
|
WITH latest_compressed_summary AS (
|
|
SELECT
|
|
id
|
|
FROM
|
|
chat_messages
|
|
WHERE
|
|
chat_id = @chat_id::uuid
|
|
AND role = 'system'
|
|
AND visibility IN ('model', 'both')
|
|
AND compressed = TRUE
|
|
ORDER BY
|
|
created_at DESC,
|
|
id DESC
|
|
LIMIT
|
|
1
|
|
)
|
|
SELECT
|
|
*
|
|
FROM
|
|
chat_messages
|
|
WHERE
|
|
chat_id = @chat_id::uuid
|
|
AND visibility IN ('model', 'both')
|
|
AND (
|
|
(
|
|
role = 'system'
|
|
AND compressed = FALSE
|
|
)
|
|
OR (
|
|
compressed = FALSE
|
|
AND (
|
|
NOT EXISTS (
|
|
SELECT
|
|
1
|
|
FROM
|
|
latest_compressed_summary
|
|
)
|
|
OR id > (
|
|
SELECT
|
|
id
|
|
FROM
|
|
latest_compressed_summary
|
|
)
|
|
)
|
|
)
|
|
OR id = (
|
|
SELECT
|
|
id
|
|
FROM
|
|
latest_compressed_summary
|
|
)
|
|
)
|
|
ORDER BY
|
|
created_at ASC,
|
|
id ASC;
|
|
|
|
-- name: GetChatsByOwnerID :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
chats
|
|
WHERE
|
|
owner_id = @owner_id::uuid
|
|
AND CASE
|
|
WHEN sqlc.narg('archived') :: boolean IS NULL THEN true
|
|
ELSE chats.archived = sqlc.narg('archived') :: boolean
|
|
END
|
|
ORDER BY
|
|
updated_at DESC;
|
|
|
|
-- name: ListChildChatsByParentID :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
chats
|
|
WHERE
|
|
parent_chat_id = @parent_chat_id::uuid
|
|
ORDER BY
|
|
created_at ASC;
|
|
|
|
-- name: ListChatsByRootID :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
chats
|
|
WHERE
|
|
root_chat_id = @root_chat_id::uuid
|
|
ORDER BY
|
|
created_at ASC;
|
|
|
|
-- name: InsertChat :one
|
|
INSERT INTO chats (
|
|
owner_id,
|
|
workspace_id,
|
|
parent_chat_id,
|
|
root_chat_id,
|
|
last_model_config_id,
|
|
title
|
|
) VALUES (
|
|
@owner_id::uuid,
|
|
sqlc.narg('workspace_id')::uuid,
|
|
sqlc.narg('parent_chat_id')::uuid,
|
|
sqlc.narg('root_chat_id')::uuid,
|
|
@last_model_config_id::uuid,
|
|
@title::text
|
|
)
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: InsertChatMessage :one
|
|
WITH updated_chat AS (
|
|
UPDATE
|
|
chats
|
|
SET
|
|
last_model_config_id = sqlc.narg('model_config_id')::uuid
|
|
WHERE
|
|
id = @chat_id::uuid
|
|
AND sqlc.narg('model_config_id')::uuid IS NOT NULL
|
|
)
|
|
INSERT INTO chat_messages (
|
|
chat_id,
|
|
model_config_id,
|
|
role,
|
|
content,
|
|
visibility,
|
|
input_tokens,
|
|
output_tokens,
|
|
total_tokens,
|
|
reasoning_tokens,
|
|
cache_creation_tokens,
|
|
cache_read_tokens,
|
|
context_limit,
|
|
compressed
|
|
) VALUES (
|
|
@chat_id::uuid,
|
|
sqlc.narg('model_config_id')::uuid,
|
|
@role::text,
|
|
sqlc.narg('content')::jsonb,
|
|
@visibility::chat_message_visibility,
|
|
sqlc.narg('input_tokens')::bigint,
|
|
sqlc.narg('output_tokens')::bigint,
|
|
sqlc.narg('total_tokens')::bigint,
|
|
sqlc.narg('reasoning_tokens')::bigint,
|
|
sqlc.narg('cache_creation_tokens')::bigint,
|
|
sqlc.narg('cache_read_tokens')::bigint,
|
|
sqlc.narg('context_limit')::bigint,
|
|
COALESCE(sqlc.narg('compressed')::boolean, FALSE)
|
|
)
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: UpdateChatMessageByID :one
|
|
UPDATE
|
|
chat_messages
|
|
SET
|
|
model_config_id = COALESCE(sqlc.narg('model_config_id')::uuid, model_config_id),
|
|
content = sqlc.narg('content')::jsonb
|
|
WHERE
|
|
id = @id::bigint
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: UpdateChatByID :one
|
|
UPDATE
|
|
chats
|
|
SET
|
|
title = @title::text,
|
|
updated_at = NOW()
|
|
WHERE
|
|
id = @id::uuid
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: UpdateChatWorkspace :one
|
|
UPDATE
|
|
chats
|
|
SET
|
|
workspace_id = sqlc.narg('workspace_id')::uuid,
|
|
updated_at = NOW()
|
|
WHERE
|
|
id = @id::uuid
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: AcquireChat :one
|
|
-- Acquires a pending chat for processing. Uses SKIP LOCKED to prevent
|
|
-- multiple replicas from acquiring the same chat.
|
|
UPDATE
|
|
chats
|
|
SET
|
|
status = 'running'::chat_status,
|
|
started_at = @started_at::timestamptz,
|
|
heartbeat_at = @started_at::timestamptz,
|
|
updated_at = @started_at::timestamptz,
|
|
worker_id = @worker_id::uuid
|
|
WHERE
|
|
id = (
|
|
SELECT
|
|
id
|
|
FROM
|
|
chats
|
|
WHERE
|
|
status = 'pending'::chat_status
|
|
ORDER BY
|
|
updated_at ASC
|
|
FOR UPDATE
|
|
SKIP LOCKED
|
|
LIMIT
|
|
1
|
|
)
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: UpdateChatStatus :one
|
|
UPDATE
|
|
chats
|
|
SET
|
|
status = @status::chat_status,
|
|
worker_id = sqlc.narg('worker_id')::uuid,
|
|
started_at = sqlc.narg('started_at')::timestamptz,
|
|
heartbeat_at = sqlc.narg('heartbeat_at')::timestamptz,
|
|
last_error = sqlc.narg('last_error')::text,
|
|
updated_at = NOW()
|
|
WHERE
|
|
id = @id::uuid
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: GetStaleChats :many
|
|
-- Find chats that appear stuck (running but heartbeat has expired).
|
|
-- Used for recovery after coderd crashes or long hangs.
|
|
SELECT
|
|
*
|
|
FROM
|
|
chats
|
|
WHERE
|
|
status = 'running'::chat_status
|
|
AND heartbeat_at < @stale_threshold::timestamptz;
|
|
|
|
-- name: UpdateChatHeartbeat :execrows
|
|
-- Bumps the heartbeat timestamp for a running chat so that other
|
|
-- replicas know the worker is still alive.
|
|
UPDATE
|
|
chats
|
|
SET
|
|
heartbeat_at = NOW()
|
|
WHERE
|
|
id = @id::uuid
|
|
AND worker_id = @worker_id::uuid
|
|
AND status = 'running'::chat_status;
|
|
|
|
-- name: GetChatDiffStatusByChatID :one
|
|
SELECT
|
|
*
|
|
FROM
|
|
chat_diff_statuses
|
|
WHERE
|
|
chat_id = @chat_id::uuid;
|
|
|
|
-- name: GetChatDiffStatusesByChatIDs :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
chat_diff_statuses
|
|
WHERE
|
|
chat_id = ANY(@chat_ids::uuid[]);
|
|
|
|
-- name: UpsertChatDiffStatusReference :one
|
|
INSERT INTO chat_diff_statuses (
|
|
chat_id,
|
|
url,
|
|
git_branch,
|
|
git_remote_origin,
|
|
stale_at
|
|
) VALUES (
|
|
@chat_id::uuid,
|
|
sqlc.narg('url')::text,
|
|
@git_branch::text,
|
|
@git_remote_origin::text,
|
|
@stale_at::timestamptz
|
|
)
|
|
ON CONFLICT (chat_id) DO UPDATE
|
|
SET
|
|
url = CASE
|
|
WHEN EXCLUDED.url IS NOT NULL THEN EXCLUDED.url
|
|
ELSE chat_diff_statuses.url
|
|
END,
|
|
git_branch = CASE
|
|
WHEN EXCLUDED.git_branch != '' THEN EXCLUDED.git_branch
|
|
ELSE chat_diff_statuses.git_branch
|
|
END,
|
|
git_remote_origin = CASE
|
|
WHEN EXCLUDED.git_remote_origin != '' THEN EXCLUDED.git_remote_origin
|
|
ELSE chat_diff_statuses.git_remote_origin
|
|
END,
|
|
stale_at = EXCLUDED.stale_at,
|
|
updated_at = NOW()
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: UpsertChatDiffStatus :one
|
|
INSERT INTO chat_diff_statuses (
|
|
chat_id,
|
|
url,
|
|
pull_request_state,
|
|
changes_requested,
|
|
additions,
|
|
deletions,
|
|
changed_files,
|
|
refreshed_at,
|
|
stale_at
|
|
) VALUES (
|
|
@chat_id::uuid,
|
|
sqlc.narg('url')::text,
|
|
sqlc.narg('pull_request_state')::text,
|
|
@changes_requested::boolean,
|
|
@additions::integer,
|
|
@deletions::integer,
|
|
@changed_files::integer,
|
|
@refreshed_at::timestamptz,
|
|
@stale_at::timestamptz
|
|
)
|
|
ON CONFLICT (chat_id) DO UPDATE
|
|
SET
|
|
url = EXCLUDED.url,
|
|
pull_request_state = EXCLUDED.pull_request_state,
|
|
changes_requested = EXCLUDED.changes_requested,
|
|
additions = EXCLUDED.additions,
|
|
deletions = EXCLUDED.deletions,
|
|
changed_files = EXCLUDED.changed_files,
|
|
refreshed_at = EXCLUDED.refreshed_at,
|
|
stale_at = EXCLUDED.stale_at,
|
|
updated_at = NOW()
|
|
RETURNING
|
|
*;
|
|
|
|
-- name: InsertChatQueuedMessage :one
|
|
INSERT INTO chat_queued_messages (chat_id, content)
|
|
VALUES (@chat_id, @content)
|
|
RETURNING *;
|
|
|
|
-- name: GetChatQueuedMessages :many
|
|
SELECT * FROM chat_queued_messages
|
|
WHERE chat_id = @chat_id
|
|
ORDER BY id ASC;
|
|
|
|
-- name: DeleteChatQueuedMessage :exec
|
|
DELETE FROM chat_queued_messages WHERE id = @id AND chat_id = @chat_id;
|
|
|
|
-- name: DeleteAllChatQueuedMessages :exec
|
|
DELETE FROM chat_queued_messages WHERE chat_id = @chat_id;
|
|
|
|
-- name: PopNextQueuedMessage :one
|
|
DELETE FROM chat_queued_messages
|
|
WHERE id = (
|
|
SELECT cqm.id FROM chat_queued_messages cqm
|
|
WHERE cqm.chat_id = @chat_id
|
|
ORDER BY cqm.id ASC
|
|
LIMIT 1
|
|
)
|
|
RETURNING *;
|
|
|
|
-- name: GetChatByIDForUpdate :one
|
|
SELECT * FROM chats WHERE id = @id::uuid FOR UPDATE;
|