mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: move chat messages to dedicated /chats/{id}/messages endpoint (#23021)
## Summary
Moves the messages response out of `GET /chats/{id}` and into a
dedicated `GET /chats/{id}/messages` endpoint.
### Backend
- `GET /chats/{id}` now returns just the `Chat` object (no messages)
- `GET /chats/{id}/messages` is a new endpoint returning
`ChatMessagesResponse` with `messages` and `queued_messages`
- Added `ChatMessagesResponse` SDK type and `GetChatMessages` client
method
### Frontend
- `getChat()` API method returns `Chat` instead of `ChatWithMessages`
- Added `getChatMessages()` API method for the new endpoint
- Split `chatQuery` into two: `chatQuery` (metadata) and
`chatMessagesQuery` (messages)
- Updated all cache mutations, optimistic updates, and websocket
handlers
- Updated tests and stories
### Files changed
| File | Change |
|---|---|
| `coderd/coderd.go` | Register `GET /messages` route |
| `coderd/chats.go` | Simplify `getChat`, add `getChatMessages` handler
|
| `codersdk/chats.go` | New type + method, update `GetChat` return |
| `site/src/api/api.ts` | New method, update `getChat` |
| `site/src/api/queries/chats.ts` | New query, update cache mutations |
| `site/src/pages/AgentsPage/AgentDetail.tsx` | Use separate queries |
| `site/src/pages/AgentsPage/AgentDetail/ChatContext.ts` | Update types
and cache writes |
| `site/src/pages/AgentsPage/AgentsPage.tsx` | Update websocket cache
handler |
This commit is contained in:
+24
-18
@@ -176,7 +176,7 @@ func TestSubagentChatExcludesWorkspaceProvisioningTools(t *testing.T) {
|
||||
if getErr != nil {
|
||||
return false
|
||||
}
|
||||
if got.Chat.Status != codersdk.ChatStatusWaiting && got.Chat.Status != codersdk.ChatStatusError {
|
||||
if got.Status != codersdk.ChatStatusWaiting && got.Status != codersdk.ChatStatusError {
|
||||
return false
|
||||
}
|
||||
// Also ensure the subagent LLM call has been made.
|
||||
@@ -1055,32 +1055,35 @@ func TestCreateWorkspaceTool_EndToEnd(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var chatWithMessages codersdk.ChatWithMessages
|
||||
var chatResult codersdk.Chat
|
||||
require.Eventually(t, func() bool {
|
||||
got, getErr := client.GetChat(ctx, chat.ID)
|
||||
if getErr != nil {
|
||||
return false
|
||||
}
|
||||
chatWithMessages = got
|
||||
return got.Chat.Status == codersdk.ChatStatusWaiting || got.Chat.Status == codersdk.ChatStatusError
|
||||
chatResult = got
|
||||
return got.Status == codersdk.ChatStatusWaiting || got.Status == codersdk.ChatStatusError
|
||||
}, testutil.WaitLong, testutil.IntervalFast)
|
||||
|
||||
if chatWithMessages.Chat.Status == codersdk.ChatStatusError {
|
||||
if chatResult.Status == codersdk.ChatStatusError {
|
||||
lastError := ""
|
||||
if chatWithMessages.Chat.LastError != nil {
|
||||
lastError = *chatWithMessages.Chat.LastError
|
||||
if chatResult.LastError != nil {
|
||||
lastError = *chatResult.LastError
|
||||
}
|
||||
require.FailNowf(t, "chat run failed", "last_error=%q", lastError)
|
||||
}
|
||||
|
||||
require.NotNil(t, chatWithMessages.Chat.WorkspaceID)
|
||||
workspaceID := *chatWithMessages.Chat.WorkspaceID
|
||||
require.NotNil(t, chatResult.WorkspaceID)
|
||||
workspaceID := *chatResult.WorkspaceID
|
||||
workspace, err := client.Workspace(ctx, workspaceID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, workspaceName, workspace.Name)
|
||||
|
||||
chatMsgs, err := client.GetChatMessages(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
var foundCreateWorkspaceResult bool
|
||||
for _, message := range chatWithMessages.Messages {
|
||||
for _, message := range chatMsgs.Messages {
|
||||
if message.Role != "tool" {
|
||||
continue
|
||||
}
|
||||
@@ -1223,33 +1226,36 @@ func TestStartWorkspaceTool_EndToEnd(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var chatWithMessages codersdk.ChatWithMessages
|
||||
var chatResult codersdk.Chat
|
||||
require.Eventually(t, func() bool {
|
||||
got, getErr := client.GetChat(ctx, chat.ID)
|
||||
if getErr != nil {
|
||||
return false
|
||||
}
|
||||
chatWithMessages = got
|
||||
return got.Chat.Status == codersdk.ChatStatusWaiting || got.Chat.Status == codersdk.ChatStatusError
|
||||
chatResult = got
|
||||
return got.Status == codersdk.ChatStatusWaiting || got.Status == codersdk.ChatStatusError
|
||||
}, testutil.WaitSuperLong, testutil.IntervalFast)
|
||||
|
||||
if chatWithMessages.Chat.Status == codersdk.ChatStatusError {
|
||||
if chatResult.Status == codersdk.ChatStatusError {
|
||||
lastError := ""
|
||||
if chatWithMessages.Chat.LastError != nil {
|
||||
lastError = *chatWithMessages.Chat.LastError
|
||||
if chatResult.LastError != nil {
|
||||
lastError = *chatResult.LastError
|
||||
}
|
||||
require.FailNowf(t, "chat run failed", "last_error=%q", lastError)
|
||||
}
|
||||
|
||||
// Verify the workspace was started.
|
||||
require.NotNil(t, chatWithMessages.Chat.WorkspaceID)
|
||||
require.NotNil(t, chatResult.WorkspaceID)
|
||||
updatedWorkspace, err := client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, codersdk.WorkspaceTransitionStart, updatedWorkspace.LatestBuild.Transition)
|
||||
|
||||
chatMsgs, err := client.GetChatMessages(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify start_workspace tool result exists in the chat messages.
|
||||
var foundStartWorkspaceResult bool
|
||||
for _, message := range chatWithMessages.Messages {
|
||||
for _, message := range chatMsgs.Messages {
|
||||
if message.Role != "tool" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -92,17 +92,19 @@ func TestAnthropicWebSearchRoundTrip(t *testing.T) {
|
||||
// Verify the chat completed and messages were persisted.
|
||||
chatData, err := client.GetChat(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
chatMsgs, err := client.GetChatMessages(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Chat status after step 1: %s, messages: %d",
|
||||
chatData.Chat.Status, len(chatData.Messages))
|
||||
logMessages(t, chatData.Messages)
|
||||
chatData.Status, len(chatMsgs.Messages))
|
||||
logMessages(t, chatMsgs.Messages)
|
||||
|
||||
require.Equal(t, codersdk.ChatStatusWaiting, chatData.Chat.Status,
|
||||
require.Equal(t, codersdk.ChatStatusWaiting, chatData.Status,
|
||||
"chat should be in waiting status after step 1")
|
||||
|
||||
// Find the first assistant message and verify it has the
|
||||
// content parts the UI needs to render web search results:
|
||||
// tool-call(PE), source, tool-result(PE), and text.
|
||||
assistantMsg := findAssistantWithText(t, chatData.Messages)
|
||||
assistantMsg := findAssistantWithText(t, chatMsgs.Messages)
|
||||
require.NotNil(t, assistantMsg,
|
||||
"expected an assistant message with text content after step 1")
|
||||
|
||||
@@ -152,17 +154,19 @@ func TestAnthropicWebSearchRoundTrip(t *testing.T) {
|
||||
// Verify the follow-up completed and produced content.
|
||||
chatData2, err := client.GetChat(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
chatMsgs2, err := client.GetChatMessages(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Chat status after step 2: %s, messages: %d",
|
||||
chatData2.Chat.Status, len(chatData2.Messages))
|
||||
logMessages(t, chatData2.Messages)
|
||||
chatData2.Status, len(chatMsgs2.Messages))
|
||||
logMessages(t, chatMsgs2.Messages)
|
||||
|
||||
require.Equal(t, codersdk.ChatStatusWaiting, chatData2.Chat.Status,
|
||||
require.Equal(t, codersdk.ChatStatusWaiting, chatData2.Status,
|
||||
"chat should be in waiting status after step 2")
|
||||
require.Greater(t, len(chatData2.Messages), len(chatData.Messages),
|
||||
require.Greater(t, len(chatMsgs2.Messages), len(chatMsgs.Messages),
|
||||
"follow-up should have added more messages")
|
||||
|
||||
// The last assistant message should have text.
|
||||
lastAssistant := findLastAssistantWithText(t, chatData2.Messages)
|
||||
lastAssistant := findLastAssistantWithText(t, chatMsgs2.Messages)
|
||||
require.NotNil(t, lastAssistant,
|
||||
"expected an assistant message with text in the follow-up")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user