feat: refine token usage display modes (#2329)

* feat: refine token usage display modes

* docs: clarify token usage accounting semantics

* fix: avoid duplicate subtask debug keys

* style: format token usage tests

* chore: address token attribution review feedback

* Update test_token_usage_middleware.py

* Update test_token_usage_middleware.py

* chore: simplify token attribution fallback

* fix token usage metadata follow-up handling

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
YuJitang
2026-05-04 09:56:16 +08:00
committed by GitHub
parent 82e7936d36
commit d02f762ab0
20 changed files with 2346 additions and 222 deletions
+44 -6
View File
@@ -18,7 +18,7 @@ interface AssistantClarificationGroup extends GenericMessageGroup<"assistant:cla
interface AssistantSubagentGroup extends GenericMessageGroup<"assistant:subagent"> {}
type MessageGroup =
export type MessageGroup =
| HumanMessageGroup
| AssistantProcessingGroup
| AssistantMessageGroup
@@ -26,10 +26,7 @@ type MessageGroup =
| AssistantClarificationGroup
| AssistantSubagentGroup;
export function groupMessages<T>(
messages: Message[],
mapper: (group: MessageGroup) => T,
): T[] {
export function getMessageGroups(messages: Message[]): MessageGroup[] {
if (messages.length === 0) {
return [];
}
@@ -124,11 +121,52 @@ export function groupMessages<T>(
}
}
return groups
return groups;
}
export function groupMessages<T>(
messages: Message[],
mapper: (group: MessageGroup) => T,
): T[] {
return getMessageGroups(messages)
.map(mapper)
.filter((result) => result !== undefined && result !== null) as T[];
}
export function getAssistantTurnUsageMessages(groups: MessageGroup[]) {
const usageMessagesByGroupIndex: Array<Message[] | null> = Array.from(
{ length: groups.length },
() => null,
);
let turnStartIndex: number | null = null;
for (const [index, group] of groups.entries()) {
if (group.type === "human") {
turnStartIndex = null;
continue;
}
turnStartIndex ??= index;
const nextGroup = groups[index + 1];
const isTurnEnd = !nextGroup || nextGroup.type === "human";
if (!isTurnEnd) {
continue;
}
usageMessagesByGroupIndex[index] = groups
.slice(turnStartIndex, index + 1)
.flatMap((currentGroup) => currentGroup.messages)
.filter((message) => message.type === "ai");
turnStartIndex = null;
}
return usageMessagesByGroupIndex;
}
export function extractTextFromMessage(message: Message) {
if (typeof message.content === "string") {
return (