mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: consolidate experimental chats API types (#23143)
## Summary
Consolidates three areas of type duplication in the experimental chats
API:
### 1. Merge archive/unarchive into `PATCH /{chat}`
- **Before:** `POST /{chat}/archive` + `POST /{chat}/unarchive` (two
endpoints, two handlers with mirrored logic)
- **After:** `PATCH /{chat}` accepting `{ "archived": true/false }` via
`UpdateChatRequest`
- Removes one endpoint and ~30 lines of duplicated handler code
### 2. Collapse identical request/response prompt types
- `ChatSystemPromptResponse` + `UpdateChatSystemPromptRequest` →
`ChatSystemPrompt`
- `UserChatCustomPromptResponse` + `UpdateUserChatCustomPromptRequest` →
`UserChatCustomPrompt`
- These pairs were field-for-field identical (single string field)
### 3. Merge duplicate reasoning options types
- `ChatModelOpenRouterReasoningOptions` +
`ChatModelVercelReasoningOptions` → `ChatModelReasoningOptions`
- Same 4 fields, same types — only field ordering and enum value sets
differed
- Unified type uses the superset of enum values
### Files changed
- `codersdk/chats.go` — SDK types and client methods
- `coderd/chats.go` — Handler consolidation
- `coderd/coderd.go` — Route change
- `coderd/chats_test.go` — Test updates
- `site/src/api/api.ts` — Frontend API client
- `site/src/api/queries/chats.ts` — Query mutations
- `site/src/api/queries/chats.test.ts` — Test mocks
- `site/src/pages/AgentsPage/AgentsPage.tsx` — Call site
- Generated files (`typesGenerated.ts`,
`chatModelOptionsGenerated.json`)
### Testing
- All Go tests pass (`TestArchiveChat`, `TestUnarchiveChat`,
`TestChatSystemPrompt`)
- All frontend tests pass (31/31 in `chats.test.ts`)
This commit is contained in:
+37
-64
@@ -245,7 +245,8 @@ type CreateChatRequest struct {
|
||||
|
||||
// UpdateChatRequest is the request to update a chat.
|
||||
type UpdateChatRequest struct {
|
||||
Title string `json:"title"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
}
|
||||
|
||||
// CreateChatMessageRequest is the request to add a message to a chat.
|
||||
@@ -307,25 +308,15 @@ type ChatModelsResponse struct {
|
||||
Providers []ChatModelProvider `json:"providers"`
|
||||
}
|
||||
|
||||
// ChatSystemPromptResponse is the response for getting the chat system prompt.
|
||||
type ChatSystemPromptResponse struct {
|
||||
// ChatSystemPrompt is the request and response body for the chat
|
||||
// system prompt configuration endpoint.
|
||||
type ChatSystemPrompt struct {
|
||||
SystemPrompt string `json:"system_prompt"`
|
||||
}
|
||||
|
||||
// UpdateChatSystemPromptRequest is the request to update the chat system prompt.
|
||||
type UpdateChatSystemPromptRequest struct {
|
||||
SystemPrompt string `json:"system_prompt"`
|
||||
}
|
||||
|
||||
// UserChatCustomPromptResponse is the response for getting a user's
|
||||
// custom chat prompt.
|
||||
type UserChatCustomPromptResponse struct {
|
||||
CustomPrompt string `json:"custom_prompt"`
|
||||
}
|
||||
|
||||
// UpdateUserChatCustomPromptRequest is the request to update a user's
|
||||
// custom chat prompt.
|
||||
type UpdateUserChatCustomPromptRequest struct {
|
||||
// UserChatCustomPrompt is the request and response body for the
|
||||
// user chat custom prompt configuration endpoint.
|
||||
type UserChatCustomPrompt struct {
|
||||
CustomPrompt string `json:"custom_prompt"`
|
||||
}
|
||||
|
||||
@@ -466,12 +457,13 @@ type ChatModelOpenAICompatProviderOptions struct {
|
||||
ReasoningEffort *string `json:"reasoning_effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
|
||||
}
|
||||
|
||||
// ChatModelOpenRouterReasoningOptions configures OpenRouter reasoning behavior.
|
||||
type ChatModelOpenRouterReasoningOptions struct {
|
||||
// ChatModelReasoningOptions configures reasoning behavior for model
|
||||
// providers that support it.
|
||||
type ChatModelReasoningOptions struct {
|
||||
Enabled *bool `json:"enabled,omitempty" description:"Whether reasoning is enabled"`
|
||||
Exclude *bool `json:"exclude,omitempty" description:"Whether to exclude reasoning content from the response"`
|
||||
MaxTokens *int64 `json:"max_tokens,omitempty" description:"Maximum number of tokens for reasoning output"`
|
||||
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"low,medium,high"`
|
||||
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
|
||||
}
|
||||
|
||||
// ChatModelOpenRouterProvider configures OpenRouter routing preferences.
|
||||
@@ -488,22 +480,14 @@ type ChatModelOpenRouterProvider struct {
|
||||
|
||||
// ChatModelOpenRouterProviderOptions configures OpenRouter provider behavior.
|
||||
type ChatModelOpenRouterProviderOptions struct {
|
||||
Reasoning *ChatModelOpenRouterReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
|
||||
ExtraBody map[string]any `json:"extra_body,omitempty" description:"Additional fields to include in the request body" hidden:"true"`
|
||||
IncludeUsage *bool `json:"include_usage,omitempty" description:"Whether to include token usage information in the response" hidden:"true"`
|
||||
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
|
||||
LogProbs *bool `json:"log_probs,omitempty" description:"Whether to return log probabilities of output tokens" hidden:"true"`
|
||||
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty" description:"Whether the model may make multiple tool calls in parallel"`
|
||||
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
|
||||
Provider *ChatModelOpenRouterProvider `json:"provider,omitempty" description:"Routing preferences for provider selection" hidden:"true"`
|
||||
}
|
||||
|
||||
// ChatModelVercelReasoningOptions configures Vercel reasoning behavior.
|
||||
type ChatModelVercelReasoningOptions struct {
|
||||
Enabled *bool `json:"enabled,omitempty" description:"Whether reasoning is enabled"`
|
||||
MaxTokens *int64 `json:"max_tokens,omitempty" description:"Maximum number of tokens for reasoning output"`
|
||||
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
|
||||
Exclude *bool `json:"exclude,omitempty" description:"Whether to exclude reasoning content from the response"`
|
||||
Reasoning *ChatModelReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
|
||||
ExtraBody map[string]any `json:"extra_body,omitempty" description:"Additional fields to include in the request body" hidden:"true"`
|
||||
IncludeUsage *bool `json:"include_usage,omitempty" description:"Whether to include token usage information in the response" hidden:"true"`
|
||||
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
|
||||
LogProbs *bool `json:"log_probs,omitempty" description:"Whether to return log probabilities of output tokens" hidden:"true"`
|
||||
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty" description:"Whether the model may make multiple tool calls in parallel"`
|
||||
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
|
||||
Provider *ChatModelOpenRouterProvider `json:"provider,omitempty" description:"Routing preferences for provider selection" hidden:"true"`
|
||||
}
|
||||
|
||||
// ChatModelVercelGatewayProviderOptions configures Vercel routing behavior.
|
||||
@@ -514,7 +498,7 @@ type ChatModelVercelGatewayProviderOptions struct {
|
||||
|
||||
// ChatModelVercelProviderOptions configures Vercel provider behavior.
|
||||
type ChatModelVercelProviderOptions struct {
|
||||
Reasoning *ChatModelVercelReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
|
||||
Reasoning *ChatModelReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
|
||||
ProviderOptions *ChatModelVercelGatewayProviderOptions `json:"providerOptions,omitempty" description:"Gateway routing options for provider selection" hidden:"true"`
|
||||
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
|
||||
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
|
||||
@@ -1246,21 +1230,21 @@ func (c *Client) GetChatCostUsers(ctx context.Context, opts ChatCostUsersOptions
|
||||
}
|
||||
|
||||
// GetChatSystemPrompt returns the deployment-wide chat system prompt.
|
||||
func (c *Client) GetChatSystemPrompt(ctx context.Context) (ChatSystemPromptResponse, error) {
|
||||
func (c *Client) GetChatSystemPrompt(ctx context.Context) (ChatSystemPrompt, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, "/api/experimental/chats/config/system-prompt", nil)
|
||||
if err != nil {
|
||||
return ChatSystemPromptResponse{}, err
|
||||
return ChatSystemPrompt{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return ChatSystemPromptResponse{}, ReadBodyAsError(res)
|
||||
return ChatSystemPrompt{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp ChatSystemPromptResponse
|
||||
var resp ChatSystemPrompt
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
// UpdateChatSystemPrompt updates the deployment-wide chat system prompt.
|
||||
func (c *Client) UpdateChatSystemPrompt(ctx context.Context, req UpdateChatSystemPromptRequest) error {
|
||||
func (c *Client) UpdateChatSystemPrompt(ctx context.Context, req ChatSystemPrompt) error {
|
||||
res, err := c.Request(ctx, http.MethodPut, "/api/experimental/chats/config/system-prompt", req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1273,30 +1257,30 @@ func (c *Client) UpdateChatSystemPrompt(ctx context.Context, req UpdateChatSyste
|
||||
}
|
||||
|
||||
// GetUserChatCustomPrompt fetches the user's custom chat prompt.
|
||||
func (c *Client) GetUserChatCustomPrompt(ctx context.Context) (UserChatCustomPromptResponse, error) {
|
||||
func (c *Client) GetUserChatCustomPrompt(ctx context.Context) (UserChatCustomPrompt, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, "/api/experimental/chats/config/user-prompt", nil)
|
||||
if err != nil {
|
||||
return UserChatCustomPromptResponse{}, err
|
||||
return UserChatCustomPrompt{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return UserChatCustomPromptResponse{}, ReadBodyAsError(res)
|
||||
return UserChatCustomPrompt{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp UserChatCustomPromptResponse
|
||||
var resp UserChatCustomPrompt
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
// UpdateUserChatCustomPrompt updates the user's custom chat prompt.
|
||||
func (c *Client) UpdateUserChatCustomPrompt(ctx context.Context, req UpdateUserChatCustomPromptRequest) (UserChatCustomPromptResponse, error) {
|
||||
func (c *Client) UpdateUserChatCustomPrompt(ctx context.Context, req UserChatCustomPrompt) (UserChatCustomPrompt, error) {
|
||||
res, err := c.Request(ctx, http.MethodPut, "/api/experimental/chats/config/user-prompt", req)
|
||||
if err != nil {
|
||||
return UserChatCustomPromptResponse{}, err
|
||||
return UserChatCustomPrompt{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return UserChatCustomPromptResponse{}, ReadBodyAsError(res)
|
||||
return UserChatCustomPrompt{}, ReadBodyAsError(res)
|
||||
}
|
||||
var resp UserChatCustomPromptResponse
|
||||
var resp UserChatCustomPrompt
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
@@ -1499,20 +1483,9 @@ func (c *Client) GetChatMessages(ctx context.Context, chatID uuid.UUID, opts *Ch
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
func (c *Client) ArchiveChat(ctx context.Context, chatID uuid.UUID) error {
|
||||
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/experimental/chats/%s/archive", chatID), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UnarchiveChat(ctx context.Context, chatID uuid.UUID) error {
|
||||
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/experimental/chats/%s/unarchive", chatID), nil)
|
||||
// UpdateChat patches a chat resource.
|
||||
func (c *Client) UpdateChat(ctx context.Context, chatID uuid.UUID, req UpdateChatRequest) error {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/experimental/chats/%s", chatID), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user