Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbe38c10

Browse files
committed
feat: implementmagicMetadataMerge()
1 parent553824d commitbe38c10

File tree

1 file changed

+54
-35
lines changed

1 file changed

+54
-35
lines changed

‎site/src/pages/AIBridgePage/RequestLogsPage/RequestLogsRow/RequestLogsRow.tsx‎

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import {
77
TooltipProvider,
88
TooltipTrigger,
99
}from"components/Tooltip/Tooltip";
10+
importeveryfrom"lodash/every";
11+
importkeysfrom"lodash/keys";
12+
importmapValuesfrom"lodash/mapValues";
13+
importsomefrom"lodash/some";
14+
importsumfrom"lodash/sum";
15+
importuniqfrom"lodash/uniq";
1016
import{
1117
ArrowDownIcon,
1218
ArrowUpIcon,
@@ -21,6 +27,43 @@ type RequestLogsRowProps = {
2127
interception:AIBridgeInterception;
2228
};
2329

30+
/**
31+
* This function merges multiple objects with the same keys into a single object.
32+
* It's super unconventional, but it's only a temporary workaround until we
33+
* structure our metadata field for rendering in the UI.
34+
*@param objects - The objects to merge.
35+
*@returns The merged object.
36+
*/
37+
constmagicMetadataMerge=(...objects:Record<string,unknown>[]):unknown=>{
38+
// Filter out empty objects
39+
constnonEmptyObjects=objects.filter((obj)=>keys(obj).length>0);
40+
41+
// If all objects were empty, return null
42+
if(nonEmptyObjects.length===0)returnnull;
43+
44+
// Check if all objects have the same keys
45+
constkeySets=nonEmptyObjects.map((obj)=>keys(obj).sort().join(","));
46+
// If the keys are different, just instantly return the objects
47+
if(uniq(keySets).length>1)returnnonEmptyObjects;
48+
49+
// Group the objects by key
50+
constgrouped=mapValues(nonEmptyObjects[0],(_,key)=>
51+
nonEmptyObjects.map((obj)=>obj[key]),
52+
);
53+
54+
// Map the grouped values to a new object
55+
constresult=mapValues(grouped,(values:unknown[])=>{
56+
constallNumeric=every(values,(v:unknown)=>typeofv==="number");
57+
constallSame=uniq(values).length===1;
58+
59+
if(allNumeric)returnsum(values);
60+
if(allSame)returnvalues[0];
61+
returnnull;// Mark conflict
62+
});
63+
64+
returnsome(result,(v:unknown)=>v===null) ?nonEmptyObjects :result;
65+
};
66+
2467
exportconstRequestLogsRow:FC<RequestLogsRowProps>=({ interception})=>{
2568
const[isOpen,setIsOpen]=useState(false);
2669

@@ -35,35 +78,8 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
3578
0,
3679
);
3780

38-
constKEY_ANTHROPIC_READ="cache_read_input";
39-
constKEY_ANTHROPIC_WRITTEN="cache_creation_input";
40-
41-
constKEY_OPENAI_READ="prompt_cached";
42-
43-
// These are an unstructured metadata field of "Record<string, unknown>",
44-
// so we need to check if they're numbers and if not, return 0.
45-
constcachedReadTokens=interception.token_usages.reduce(
46-
(acc,tokenUsage)=>
47-
acc+
48-
(interception.provider==="anthropic"
49-
?typeoftokenUsage.metadata?.[KEY_ANTHROPIC_READ]==="number"
50-
?tokenUsage.metadata?.[KEY_ANTHROPIC_READ]
51-
:0
52-
:typeoftokenUsage.metadata?.[KEY_OPENAI_READ]==="number"
53-
?tokenUsage.metadata?.[KEY_OPENAI_READ]
54-
:0),
55-
0,
56-
);
57-
constcachedWrittenTokens=interception.token_usages.reduce(
58-
(acc,tokenUsage)=>
59-
acc+
60-
(interception.provider==="anthropic"
61-
?typeoftokenUsage.metadata?.[KEY_ANTHROPIC_WRITTEN]==="number"
62-
?tokenUsage.metadata?.[KEY_ANTHROPIC_WRITTEN]
63-
:0
64-
:// OpenAI doesn't have a cached written tokens field, so we return 0.
65-
0),
66-
0,
81+
consttokenUsagesMetadata=magicMetadataMerge(
82+
...interception.token_usages.map((tokenUsage)=>tokenUsage.metadata),
6783
);
6884

6985
consttoolCalls=interception.tool_usages.length;
@@ -186,12 +202,6 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
186202
<dt>Output Tokens:</dt>
187203
<dddata-chromatic="ignore">{outputTokens}</dd>
188204

189-
<dt>Cached Read Tokens:</dt>
190-
<dddata-chromatic="ignore">{cachedReadTokens}</dd>
191-
192-
<dt>Cached Written Tokens:</dt>
193-
<dddata-chromatic="ignore">{cachedWrittenTokens}</dd>
194-
195205
<dt>Tool Calls:</dt>
196206
<dddata-chromatic="ignore">
197207
{interception.tool_usages.length}
@@ -246,6 +256,15 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
246256
</div>
247257
</div>
248258
)}
259+
260+
{tokenUsagesMetadata!==null&&(
261+
<divclassName="flex flex-col gap-2">
262+
<div>Metadata</div>
263+
<divclassName="bg-surface-secondary rounded-md p-4">
264+
<pre>{JSON.stringify(tokenUsagesMetadata,null,2)}</pre>
265+
</div>
266+
</div>
267+
)}
249268
</div>
250269
</TableCell>
251270
</TableRow>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp