feat: polish assistant conversations

This commit is contained in:
ajnart committed 2026-08-14 11:32:10 +02:00
1 parent 1ad1439828
commit f991dc6334
14 files changed
+509 -120

No files matched your search

@@ -11,7 +11,7 @@ describe("assistant chat input", () => {
parts: [
{
type: "text",
text: "Check :integration[integration-1]{name=Media server} on :board[board-1]",
text: "Check :integration[Media server]{name=integration-1} on :board[Home]{name=board-1}",
},
],
},
@@ -3,7 +3,7 @@ export type AssistantMentionReference = {
id: string;
};
const mentionPattern = /:(app|integration|board|widget)\[([^\]\n]{1,1024})\](?:\{name=[^}\n]{1,1024}\})?/gu;
const mentionPattern = /:(app|integration|board|widget)\[([^\]\n]{1,1024})\](?:\{name=([^}\n]{1,1024})\})?/gu;
const getMessageText = (message: { parts: unknown[] }) =>
message.parts
@@ -20,7 +20,9 @@ export const getRequestedMentionIds = (messages: { role: "user" | "assistant"; p
if (message.role !== "user") continue;
for (const match of getMessageText(message).matchAll(mentionPattern)) {
const type = match[1] as AssistantMentionReference["type"];
const id = match[2];
// assistant-ui stores the display label between brackets and the stable entity ID in `name`.
// Keep the bracket value as a fallback for older messages written before named directives.
const id = match[3] ?? match[2];
if (id) mentions.set(`${type}:${id}`, { type, id });
if (mentions.size >= 30) return [...mentions.values()];
}
@@ -646,6 +646,7 @@ export async function POST(request: Request) {
return result.toUIMessageStreamResponse<UIMessage<AssistantMessageMetadata>>({
originalMessages: parsed.data.messages as UIMessage<AssistantMessageMetadata>[],
sendReasoning: true,
messageMetadata: ({ part }) => {
const common = {
requestId,
@@ -682,7 +683,11 @@ export async function POST(request: Request) {
latestStep?.inputTokens !== undefined && latestStep.outputTokens !== undefined
? latestStep.inputTokens + latestStep.outputTokens
: undefined;
const contextUsed = latestContextUsed ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
const contextUsed =
latestContextUsed ??
(usage.inputTokens !== undefined || usage.outputTokens !== undefined
? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)
: undefined);
const promptPrice = asFiniteNumber(selectedModel?.promptPrice);
const completionPrice = asFiniteNumber(selectedModel?.completionPrice);
const estimatedCost =
@@ -721,8 +726,8 @@ export async function POST(request: Request) {
...(outputTokensPerSecond !== undefined ? { outputTokensPerSecond } : {}),
...(providerOutputTokensPerSecond !== undefined ? { providerOutputTokensPerSecond } : {}),
...(providerGenerationTimeMs !== undefined ? { generationTimeMs: providerGenerationTimeMs } : {}),
...(contextUsed > 0 ? { contextUsed } : {}),
...(selectedModel?.contextLength && contextUsed > 0
...(contextUsed !== undefined && contextUsed > 0 ? { contextUsed } : {}),
...(selectedModel?.contextLength && contextUsed !== undefined && contextUsed > 0
? { contextUtilization: Math.min(contextUsed / selectedModel.contextLength, 1) }
: {}),
...(cost !== undefined ? { cost, costType: hasReportedCost ? "reported" : "estimated" } : {}),