mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: separate default and additional Coder Agents system prompts (#23616)
Admins can now control whether the built-in Coder Agents default system prompt is prepended to their custom instructions, rather than having the custom prompt silently replace the default. **Changes:** - New `include_default_system_prompt` boolean toggle (defaults to `true` for existing deployments) stored as a site config key — no migration needed. - GET `/api/experimental/chats/config/system-prompt` returns the toggle state, the custom prompt, and a preview of the built-in default. - PUT persists both the toggle and custom prompt atomically in a single transaction. - `resolvedChatSystemPrompt()` composes `[default?, custom?]` joined by `\n\n`, falling back to the built-in default on DB errors. - Settings UI adds a Switch toggle with conditional helper text and a "Preview" button that shows the built-in default prompt via the existing `TextPreviewDialog`. - Comprehensive test coverage: 15 subtests covering toggle behavior, prompt composition matrix, auth boundaries, and integration with chat creation.
This commit is contained in:
@@ -17736,6 +17736,30 @@ func (q *sqlQuerier) GetChatDesktopEnabled(ctx context.Context) (bool, error) {
|
||||
return enable_desktop, err
|
||||
}
|
||||
|
||||
const getChatIncludeDefaultSystemPrompt = `-- name: GetChatIncludeDefaultSystemPrompt :one
|
||||
SELECT
|
||||
COALESCE(
|
||||
(SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_include_default_system_prompt'),
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM site_configs
|
||||
WHERE key = 'agents_chat_system_prompt'
|
||||
AND value != ''
|
||||
)
|
||||
) :: boolean AS include_default_system_prompt
|
||||
`
|
||||
|
||||
// GetChatIncludeDefaultSystemPrompt preserves the legacy default
|
||||
// for deployments created before the explicit include-default toggle.
|
||||
// When the toggle is unset, a non-empty custom prompt implies false;
|
||||
// otherwise the setting defaults to true.
|
||||
func (q *sqlQuerier) GetChatIncludeDefaultSystemPrompt(ctx context.Context) (bool, error) {
|
||||
row := q.db.QueryRowContext(ctx, getChatIncludeDefaultSystemPrompt)
|
||||
var include_default_system_prompt bool
|
||||
err := row.Scan(&include_default_system_prompt)
|
||||
return include_default_system_prompt, err
|
||||
}
|
||||
|
||||
const getChatSystemPrompt = `-- name: GetChatSystemPrompt :one
|
||||
SELECT
|
||||
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_system_prompt'), '') :: text AS chat_system_prompt
|
||||
@@ -17748,6 +17772,37 @@ func (q *sqlQuerier) GetChatSystemPrompt(ctx context.Context) (string, error) {
|
||||
return chat_system_prompt, err
|
||||
}
|
||||
|
||||
const getChatSystemPromptConfig = `-- name: GetChatSystemPromptConfig :one
|
||||
SELECT
|
||||
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_chat_system_prompt'), '') :: text AS chat_system_prompt,
|
||||
COALESCE(
|
||||
(SELECT value = 'true' FROM site_configs WHERE key = 'agents_chat_include_default_system_prompt'),
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM site_configs
|
||||
WHERE key = 'agents_chat_system_prompt'
|
||||
AND value != ''
|
||||
)
|
||||
) :: boolean AS include_default_system_prompt
|
||||
`
|
||||
|
||||
type GetChatSystemPromptConfigRow struct {
|
||||
ChatSystemPrompt string `db:"chat_system_prompt" json:"chat_system_prompt"`
|
||||
IncludeDefaultSystemPrompt bool `db:"include_default_system_prompt" json:"include_default_system_prompt"`
|
||||
}
|
||||
|
||||
// GetChatSystemPromptConfig returns both chat system prompt settings in a
|
||||
// single read to avoid torn reads between separate site-config lookups.
|
||||
// The include-default fallback preserves the legacy behavior where a
|
||||
// non-empty custom prompt implied opting out before the explicit toggle
|
||||
// existed.
|
||||
func (q *sqlQuerier) GetChatSystemPromptConfig(ctx context.Context) (GetChatSystemPromptConfigRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getChatSystemPromptConfig)
|
||||
var i GetChatSystemPromptConfigRow
|
||||
err := row.Scan(&i.ChatSystemPrompt, &i.IncludeDefaultSystemPrompt)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getChatTemplateAllowlist = `-- name: GetChatTemplateAllowlist :one
|
||||
SELECT
|
||||
COALESCE((SELECT value FROM site_configs WHERE key = 'agents_template_allowlist'), '') :: text AS template_allowlist
|
||||
@@ -17983,6 +18038,28 @@ func (q *sqlQuerier) UpsertChatDesktopEnabled(ctx context.Context, enableDesktop
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertChatIncludeDefaultSystemPrompt = `-- name: UpsertChatIncludeDefaultSystemPrompt :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES (
|
||||
'agents_chat_include_default_system_prompt',
|
||||
CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
)
|
||||
ON CONFLICT (key) DO UPDATE
|
||||
SET value = CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
WHERE site_configs.key = 'agents_chat_include_default_system_prompt'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) UpsertChatIncludeDefaultSystemPrompt(ctx context.Context, includeDefaultSystemPrompt bool) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertChatIncludeDefaultSystemPrompt, includeDefaultSystemPrompt)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertChatSystemPrompt = `-- name: UpsertChatSystemPrompt :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('agents_chat_system_prompt', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'agents_chat_system_prompt'
|
||||
|
||||
Reference in New Issue
Block a user