Skip to content
Draft
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
14 changes: 12 additions & 2 deletions coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

136 changes: 136 additions & 0 deletions coderd/database/migrations/000537_chat_reasoning_effort.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
DROP VIEW IF EXISTS chats_expanded;

-- Move the reasoning effort default back to the provider-appropriate
-- legacy path. Rows whose provider cannot be determined just lose the
-- reasoning_effort key in the cleanup below.
UPDATE chat_model_configs cmc
SET options = (cmc.options - 'reasoning_effort') || jsonb_build_object(
'provider_options',
COALESCE(cmc.options -> 'provider_options', '{}'::jsonb) || jsonb_build_object(
'openai',
COALESCE(cmc.options #> '{provider_options,openai}', '{}'::jsonb) ||
jsonb_build_object('reasoning_effort', cmc.options #> '{reasoning_effort,default}')
)
)
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id
AND ap.type IN ('openai', 'azure')
AND cmc.options #>> '{reasoning_effort,default}' IS NOT NULL;

UPDATE chat_model_configs cmc
SET options = (cmc.options - 'reasoning_effort') || jsonb_build_object(
'provider_options',
COALESCE(cmc.options -> 'provider_options', '{}'::jsonb) || jsonb_build_object(
'anthropic',
COALESCE(cmc.options #> '{provider_options,anthropic}', '{}'::jsonb) ||
jsonb_build_object('effort', cmc.options #> '{reasoning_effort,default}')
)
)
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id
AND ap.type IN ('anthropic', 'bedrock')
AND cmc.options #>> '{reasoning_effort,default}' IS NOT NULL;

UPDATE chat_model_configs cmc
SET options = (cmc.options - 'reasoning_effort') || jsonb_build_object(
'provider_options',
COALESCE(cmc.options -> 'provider_options', '{}'::jsonb) || jsonb_build_object(
'openaicompat',
COALESCE(cmc.options #> '{provider_options,openaicompat}', '{}'::jsonb) ||
jsonb_build_object('reasoning_effort', cmc.options #> '{reasoning_effort,default}')
)
)
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id
AND ap.type = 'openai-compat'
AND cmc.options #>> '{reasoning_effort,default}' IS NOT NULL;

UPDATE chat_model_configs cmc
SET options = (cmc.options - 'reasoning_effort') || jsonb_build_object(
'provider_options',
COALESCE(cmc.options -> 'provider_options', '{}'::jsonb) || jsonb_build_object(
'openrouter',
COALESCE(cmc.options #> '{provider_options,openrouter}', '{}'::jsonb) || jsonb_build_object(
'reasoning',
COALESCE(cmc.options #> '{provider_options,openrouter,reasoning}', '{}'::jsonb) ||
jsonb_build_object('effort', cmc.options #> '{reasoning_effort,default}')
)
)
)
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id
AND ap.type = 'openrouter'
AND cmc.options #>> '{reasoning_effort,default}' IS NOT NULL;

UPDATE chat_model_configs cmc
SET options = (cmc.options - 'reasoning_effort') || jsonb_build_object(
'provider_options',
COALESCE(cmc.options -> 'provider_options', '{}'::jsonb) || jsonb_build_object(
'vercel',
COALESCE(cmc.options #> '{provider_options,vercel}', '{}'::jsonb) || jsonb_build_object(
'reasoning',
COALESCE(cmc.options #> '{provider_options,vercel,reasoning}', '{}'::jsonb) ||
jsonb_build_object('effort', cmc.options #> '{reasoning_effort,default}')
)
)
)
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id
AND ap.type = 'vercel'
AND cmc.options #>> '{reasoning_effort,default}' IS NOT NULL;

UPDATE chat_model_configs
SET options = options - 'reasoning_effort'
WHERE options ? 'reasoning_effort';

ALTER TABLE chats DROP COLUMN last_reasoning_effort;
ALTER TABLE chat_messages DROP COLUMN reasoning_effort;
ALTER TABLE chat_queued_messages DROP COLUMN reasoning_effort;

CREATE VIEW chats_expanded AS
SELECT c.id,
c.owner_id,
c.workspace_id,
c.title,
c.status,
c.worker_id,
c.started_at,
c.heartbeat_at,
c.created_at,
c.updated_at,
c.parent_chat_id,
c.root_chat_id,
c.last_model_config_id,
c.archived,
c.last_error,
c.mode,
c.mcp_server_ids,
c.labels,
c.build_id,
c.agent_id,
c.pin_order,
c.last_read_message_id,
c.dynamic_tools,
c.organization_id,
c.plan_mode,
c.client_type,
c.last_turn_summary,
c.snapshot_version,
c.history_version,
c.queue_version,
c.generation_attempt,
c.retry_state,
c.retry_state_version,
c.runner_id,
c.requires_action_deadline_at,
COALESCE(root.user_acl, c.user_acl) AS user_acl,
COALESCE(root.group_acl, c.group_acl) AS group_acl,
owner.username AS owner_username,
owner.name AS owner_name,
c.context_aggregate_hash,
c.context_dirty_since,
c.context_dirty_resources,
c.context_error
FROM ((chats c
LEFT JOIN chats root ON ((root.id = COALESCE(c.root_chat_id, c.parent_chat_id))))
JOIN visible_users owner ON ((owner.id = c.owner_id)));
113 changes: 113 additions & 0 deletions coderd/database/migrations/000537_chat_reasoning_effort.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
-- Per-turn reasoning effort. The chats_expanded view must be dropped
-- and recreated so the new chats column can appear in its column list.
DROP VIEW IF EXISTS chats_expanded;

ALTER TABLE chats ADD COLUMN last_reasoning_effort text;
ALTER TABLE chat_messages ADD COLUMN reasoning_effort text;
ALTER TABLE chat_queued_messages ADD COLUMN reasoning_effort text;

COMMENT ON COLUMN chats.last_reasoning_effort IS 'Reasoning effort carried by the most recent message that set one. Used as the per-turn effort for subsequent generations until overridden.';
COMMENT ON COLUMN chat_messages.reasoning_effort IS 'User-selected reasoning effort for the turn triggered by this message. NULL when the sender did not select one.';
COMMENT ON COLUMN chat_queued_messages.reasoning_effort IS 'User-selected reasoning effort carried into the message when the queued row is promoted.';

CREATE VIEW chats_expanded AS
SELECT c.id,
c.owner_id,
c.workspace_id,
c.title,
c.status,
c.worker_id,
c.started_at,
c.heartbeat_at,
c.created_at,
c.updated_at,
c.parent_chat_id,
c.root_chat_id,
c.last_model_config_id,
c.last_reasoning_effort,
c.archived,
c.last_error,
c.mode,
c.mcp_server_ids,
c.labels,
c.build_id,
c.agent_id,
c.pin_order,
c.last_read_message_id,
c.dynamic_tools,
c.organization_id,
c.plan_mode,
c.client_type,
c.last_turn_summary,
c.snapshot_version,
c.history_version,
c.queue_version,
c.generation_attempt,
c.retry_state,
c.retry_state_version,
c.runner_id,
c.requires_action_deadline_at,
COALESCE(root.user_acl, c.user_acl) AS user_acl,
COALESCE(root.group_acl, c.group_acl) AS group_acl,
owner.username AS owner_username,
owner.name AS owner_name,
c.context_aggregate_hash,
c.context_dirty_since,
c.context_dirty_resources,
c.context_error
FROM ((chats c
LEFT JOIN chats root ON ((root.id = COALESCE(c.root_chat_id, c.parent_chat_id))))
JOIN visible_users owner ON ((owner.id = c.owner_id)));

-- Migrate the legacy per-provider effort values inside
-- chat_model_configs.options to the new top-level reasoning_effort
-- config. The old fixed value becomes both the default and the max.
-- The legacy path is selected by the config's provider type; rows
-- with a NULL or unknown provider fall back to the first populated
-- legacy path.
WITH legacy AS (
SELECT
cmc.id,
CASE ap.type
WHEN 'openai' THEN NULLIF(cmc.options #>> '{provider_options,openai,reasoning_effort}', '')
WHEN 'azure' THEN NULLIF(cmc.options #>> '{provider_options,openai,reasoning_effort}', '')
WHEN 'anthropic' THEN NULLIF(cmc.options #>> '{provider_options,anthropic,effort}', '')
WHEN 'bedrock' THEN NULLIF(cmc.options #>> '{provider_options,anthropic,effort}', '')
WHEN 'openai-compat' THEN NULLIF(cmc.options #>> '{provider_options,openaicompat,reasoning_effort}', '')
WHEN 'openrouter' THEN NULLIF(cmc.options #>> '{provider_options,openrouter,reasoning,effort}', '')
WHEN 'vercel' THEN NULLIF(cmc.options #>> '{provider_options,vercel,reasoning,effort}', '')
ELSE COALESCE(
NULLIF(cmc.options #>> '{provider_options,openai,reasoning_effort}', ''),
NULLIF(cmc.options #>> '{provider_options,anthropic,effort}', ''),
NULLIF(cmc.options #>> '{provider_options,openaicompat,reasoning_effort}', ''),
NULLIF(cmc.options #>> '{provider_options,openrouter,reasoning,effort}', ''),
NULLIF(cmc.options #>> '{provider_options,vercel,reasoning,effort}', '')
)
END AS effort
FROM chat_model_configs cmc
LEFT JOIN ai_providers ap ON ap.id = cmc.ai_provider_id
)
UPDATE chat_model_configs
SET options = jsonb_set(
chat_model_configs.options,
'{reasoning_effort}',
jsonb_build_object('default', legacy.effort, 'max', legacy.effort)
)
FROM legacy
WHERE chat_model_configs.id = legacy.id
AND legacy.effort IS NOT NULL;

-- Strip the legacy per-provider effort keys, including empty-string
-- values that were skipped above.
UPDATE chat_model_configs
SET options = ((((options
#- '{provider_options,openai,reasoning_effort}')
#- '{provider_options,anthropic,effort}')
#- '{provider_options,openaicompat,reasoning_effort}')
#- '{provider_options,openrouter,reasoning,effort}')
#- '{provider_options,vercel,reasoning,effort}'
WHERE options #> '{provider_options,openai,reasoning_effort}' IS NOT NULL
OR options #> '{provider_options,anthropic,effort}' IS NOT NULL
OR options #> '{provider_options,openaicompat,reasoning_effort}' IS NOT NULL
OR options #> '{provider_options,openrouter,reasoning,effort}' IS NOT NULL
OR options #> '{provider_options,vercel,reasoning,effort}' IS NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
-- Chat model configs carrying legacy per-provider reasoning effort
-- values inside options. Inserted at 000535 so the 000537 data
-- migration rewrites them into the top-level reasoning_effort config
-- ({default, max}) and strips the legacy keys.
INSERT INTO ai_providers (
id,
type,
name,
display_name,
enabled,
deleted,
base_url,
settings
) VALUES
(
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6e01',
'openai',
'openai-effort-fixture',
'OpenAI (Reasoning Effort Fixture)',
TRUE,
FALSE,
'https://api.openai.com/v1/',
''
),
(
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6e02',
'anthropic',
'anthropic-effort-fixture',
'Anthropic (Reasoning Effort Fixture)',
TRUE,
FALSE,
'https://api.anthropic.com/',
''
);

INSERT INTO chat_model_configs (
id,
model,
display_name,
enabled,
is_default,
deleted,
context_limit,
compression_threshold,
options,
ai_provider_id,
created_at,
updated_at
) VALUES
(
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6f01',
'gpt-5.1',
'GPT-5.1 (Legacy Effort)',
TRUE,
FALSE,
FALSE,
200000,
70,
'{"provider_options": {"openai": {"reasoning_effort": "high", "reasoning_summary": "auto"}}}',
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6e01',
'2024-01-01 00:00:00+00',
'2024-01-01 00:00:00+00'
),
(
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6f02',
'claude-opus-4-6',
'Claude Opus (Legacy Effort)',
TRUE,
FALSE,
FALSE,
200000,
70,
'{"provider_options": {"anthropic": {"effort": "max", "send_reasoning": true}}}',
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6e02',
'2024-01-01 00:00:00+00',
'2024-01-01 00:00:00+00'
),
(
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6f03',
'gpt-5.1-empty-effort',
'GPT-5.1 (Empty Legacy Effort)',
TRUE,
FALSE,
FALSE,
200000,
70,
'{"provider_options": {"openai": {"reasoning_effort": ""}}}',
'4f0a9c2e-1d3b-4a5c-8e7f-6a9b8c7d6e01',
'2024-01-01 00:00:00+00',
'2024-01-01 00:00:00+00'
);
Loading
Loading