mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor(site/src/pages/AgentsPage): drop defensive code for impossible chat states (#27513)
This commit is contained in:
@@ -211,13 +211,10 @@ const SmoothedResponse = memo<{
|
||||
});
|
||||
|
||||
const ReadFileTimelineBlock = memo<{
|
||||
tools: readonly MergedTool[];
|
||||
tools: readonly [MergedTool, ...MergedTool[]];
|
||||
}>(({ tools }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [firstTool] = tools;
|
||||
if (!firstTool) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tools.length === 1) {
|
||||
const readFile = getReadFileToolData(firstTool);
|
||||
@@ -376,17 +373,16 @@ export const BlockList: FC<{
|
||||
</div>
|
||||
);
|
||||
case "tool-group": {
|
||||
const groupTools = block.ids
|
||||
const [firstGroupTool, ...restGroupTools] = block.ids
|
||||
.map((id) => toolByID.get(id))
|
||||
.filter((tool) => tool !== undefined);
|
||||
const [firstGroupTool] = groupTools;
|
||||
if (!firstGroupTool) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ReadFileTimelineBlock
|
||||
key={firstGroupTool.id}
|
||||
tools={groupTools}
|
||||
tools={[firstGroupTool, ...restGroupTools]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -469,8 +465,10 @@ export const BlockList: FC<{
|
||||
sources={block.sources}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
default: {
|
||||
const _exhaustive: never = block;
|
||||
return _exhaustive;
|
||||
}
|
||||
}
|
||||
})}
|
||||
{remainingTools.map((tool) => (
|
||||
|
||||
@@ -84,7 +84,6 @@ export const RetryRateLimited: Story = {
|
||||
attempt: 3,
|
||||
error: "Anthropic is rate limiting requests.",
|
||||
kind: "rate_limit",
|
||||
delayMs: 3000,
|
||||
}),
|
||||
isAwaitingFirstStreamChunk: true,
|
||||
}),
|
||||
@@ -117,7 +116,6 @@ export const RetryInvalidTimestamp: Story = {
|
||||
attempt: 3,
|
||||
error: "Anthropic is rate limiting requests.",
|
||||
kind: "rate_limit",
|
||||
delayMs: 3000,
|
||||
retryingAt: "not-a-date",
|
||||
}),
|
||||
isAwaitingFirstStreamChunk: true,
|
||||
|
||||
@@ -46,8 +46,7 @@ export const extractContextUsageFromMessage = (
|
||||
export const getLatestContextUsage = (
|
||||
messages: readonly TypesGen.ChatMessage[],
|
||||
): AgentContextUsage | null => {
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index];
|
||||
for (const message of messages.toReversed()) {
|
||||
const isCompactionSummary = message.content?.some(
|
||||
(part) =>
|
||||
(part.type === "tool-call" || part.type === "tool-result") &&
|
||||
@@ -65,16 +64,10 @@ export const getLatestContextUsage = (
|
||||
return null;
|
||||
};
|
||||
|
||||
type ChatWithHierarchyMetadata = TypesGen.Chat & {
|
||||
readonly parent_chat_id?: string;
|
||||
};
|
||||
|
||||
export const getParentChatID = (
|
||||
chat: TypesGen.Chat | undefined,
|
||||
): string | undefined => {
|
||||
return asNonEmptyString(
|
||||
(chat as ChatWithHierarchyMetadata | undefined)?.parent_chat_id,
|
||||
);
|
||||
return asNonEmptyString(chat?.parent_chat_id);
|
||||
};
|
||||
|
||||
export const resolveModelFromChatConfig = (
|
||||
|
||||
@@ -279,7 +279,6 @@ describe("setRetryState / clearRetryState", () => {
|
||||
error: "rate limited",
|
||||
kind: "rate_limit",
|
||||
provider: "anthropic",
|
||||
delayMs: 3000,
|
||||
retryingAt: "2025-01-01T00:00:30.000Z",
|
||||
});
|
||||
expect(store.getSnapshot().retryState).toEqual({
|
||||
@@ -287,7 +286,6 @@ describe("setRetryState / clearRetryState", () => {
|
||||
error: "rate limited",
|
||||
kind: "rate_limit",
|
||||
provider: "anthropic",
|
||||
delayMs: 3000,
|
||||
retryingAt: "2025-01-01T00:00:30.000Z",
|
||||
});
|
||||
|
||||
@@ -589,7 +587,6 @@ describe("resetTransientState", () => {
|
||||
error: "rate limit",
|
||||
kind: "rate_limit",
|
||||
provider: "anthropic",
|
||||
delayMs: 5000,
|
||||
retryingAt: "2025-01-01T00:01:00.000Z",
|
||||
});
|
||||
store.setReconnectState({
|
||||
|
||||
@@ -2405,7 +2405,6 @@ describe("useChatStore", () => {
|
||||
error: "upstream timeout",
|
||||
kind: "timeout",
|
||||
provider: "anthropic",
|
||||
delayMs: 5000,
|
||||
retryingAt: "2025-01-01T00:01:00.000Z",
|
||||
});
|
||||
});
|
||||
@@ -2471,7 +2470,6 @@ describe("useChatStore", () => {
|
||||
error: "rate limited",
|
||||
kind: "rate_limit",
|
||||
provider: "anthropic",
|
||||
delayMs: 3000,
|
||||
retryingAt: "2025-01-01T00:00:30.000Z",
|
||||
});
|
||||
});
|
||||
@@ -2551,7 +2549,6 @@ describe("useChatStore", () => {
|
||||
error: "rate limited",
|
||||
kind: "rate_limit",
|
||||
provider: "anthropic",
|
||||
delayMs: 3000,
|
||||
retryingAt: "2025-01-01T00:00:30.000Z",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,17 +91,9 @@ export const chatMessagesEqualByValue = (
|
||||
export const chatQueuedMessagesEqualByID = (
|
||||
left: readonly TypesGen.ChatQueuedMessage[],
|
||||
right: readonly TypesGen.ChatQueuedMessage[],
|
||||
): boolean => {
|
||||
if (left.length !== right.length) {
|
||||
return false;
|
||||
}
|
||||
for (let index = 0; index < left.length; index += 1) {
|
||||
if (left[index]?.id !== right[index]?.id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
): boolean =>
|
||||
left.length === right.length &&
|
||||
left.every((message, index) => message.id === right[index].id);
|
||||
|
||||
const retryStatesEqual = (
|
||||
left: RetryState | null,
|
||||
@@ -118,7 +110,6 @@ const retryStatesEqual = (
|
||||
left.error === right.error &&
|
||||
left.kind === right.kind &&
|
||||
left.provider === right.provider &&
|
||||
left.delayMs === right.delayMs &&
|
||||
left.retryingAt === right.retryingAt
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,7 +47,6 @@ describe("deriveLiveStatus", () => {
|
||||
message: "Anthropic returned an unexpected error.",
|
||||
attempt: 2,
|
||||
provider: "anthropic",
|
||||
delayMs: 2000,
|
||||
retryingAt: "2026-03-10T00:00:02.000Z",
|
||||
};
|
||||
const reconnectingStatus = {
|
||||
|
||||
@@ -21,7 +21,6 @@ export type LiveStatusModel =
|
||||
message: string;
|
||||
attempt: number;
|
||||
provider?: string;
|
||||
delayMs?: number;
|
||||
retryingAt?: string;
|
||||
} & LiveStatusBase)
|
||||
| ({
|
||||
@@ -76,7 +75,6 @@ const toRetryingLiveStatus = (
|
||||
message: retryState.error,
|
||||
attempt: retryState.attempt,
|
||||
provider: retryState.provider,
|
||||
delayMs: retryState.delayMs,
|
||||
retryingAt: retryState.retryingAt,
|
||||
});
|
||||
|
||||
|
||||
@@ -111,11 +111,7 @@ export const getPendingToolCallIDs = (
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index];
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
for (const message of messages.toReversed()) {
|
||||
if (message.role === "user") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ export const buildRetryState = (
|
||||
error: "Anthropic returned an unexpected error.",
|
||||
kind: "generic",
|
||||
provider: "anthropic",
|
||||
delayMs: 2000,
|
||||
retryingAt: "2026-03-10T00:00:02.000Z",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ export const applyMessagePartToStreamState = (
|
||||
// Skip empty and whitespace-only deltas so they don't
|
||||
// create a non-null StreamState with empty blocks, which
|
||||
// would prematurely end the "starting" phase.
|
||||
if (!part.text?.trim()) {
|
||||
if (!part.text.trim()) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
@@ -33,7 +33,7 @@ export const applyMessagePartToStreamState = (
|
||||
};
|
||||
}
|
||||
case "reasoning": {
|
||||
if (!part.text?.trim()) {
|
||||
if (!part.text.trim()) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -73,7 +73,6 @@ export type RetryState = {
|
||||
error: string;
|
||||
kind: TypesGen.ChatErrorKind;
|
||||
provider?: string;
|
||||
delayMs?: number;
|
||||
retryingAt?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ const normalizeRetryState = (retry: TypesGen.ChatStreamRetry): RetryState => ({
|
||||
error: retry.error.trim() || "Retrying request shortly.",
|
||||
kind: retry.kind ?? "generic",
|
||||
provider: retry.provider?.trim() || undefined,
|
||||
delayMs: retry.delay_ms,
|
||||
retryingAt: retry.retrying_at.trim() || undefined,
|
||||
});
|
||||
|
||||
@@ -556,9 +555,8 @@ export const useChatStore = (
|
||||
pendingMessages.push(message);
|
||||
}
|
||||
if (
|
||||
message.id !== undefined &&
|
||||
(lastMessageIdRef.current === undefined ||
|
||||
message.id > lastMessageIdRef.current)
|
||||
lastMessageIdRef.current === undefined ||
|
||||
message.id > lastMessageIdRef.current
|
||||
) {
|
||||
lastMessageIdRef.current = message.id;
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ export const ChatPageInput: FC<ChatPageInputProps> = ({
|
||||
(b): b is TypesGen.ChatFilePart => b.type === "file",
|
||||
);
|
||||
const files = fileBlocks.map((block, i) => {
|
||||
const mt = block.media_type ?? "application/octet-stream";
|
||||
const mt = block.media_type;
|
||||
const ext = mt === "text/plain" ? "txt" : (mt.split("/")[1] ?? "png");
|
||||
// Empty File used as a Map key only, its content is never
|
||||
// read because the existing file_id is reused at send time.
|
||||
|
||||
@@ -43,7 +43,7 @@ export const getQueuedMessageInfo = (
|
||||
const fileBlocks = content.filter((p) => p.type === "file");
|
||||
const textParts: string[] = [];
|
||||
for (const part of content) {
|
||||
if (part.type === "text" && part.text?.trim()) {
|
||||
if (part.type === "text" && part.text.trim()) {
|
||||
textParts.push(part.text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user