feat: add some stubbed data - help show concept

This commit is contained in:
Krrish Dholakia 2026-03-03 18:34:09 -08:00
parent f81152d5e1
commit 69e4b4d485
4 changed files with 314 additions and 15 deletions

View file

@ -37,7 +37,7 @@ function getTrendSubtitle(newToday: number, newYesterday: number): string | unde
return `${diff} since yesterday`;
}
type SortField = "tool_name" | "call_policy" | "team_id" | "key_alias" | "created_at" | "call_count";
type SortField = "tool_name" | "call_policy" | "team_id" | "key_alias" | "created_at" | "call_count" | "agent_id";
interface FilterValues {
[key: string]: string;
@ -135,6 +135,10 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
label: v as string,
value: v as string,
}));
const agentIdOptions = Array.from(new Set(tools.map((t) => t.agent_id).filter(Boolean))).map((v) => ({
label: v as string,
value: v as string,
}));
const filterOptions: FilterOption[] = [
{
@ -152,6 +156,11 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
label: "Key Name",
options: keyAliasOptions,
},
{
name: "Agent ID",
label: "Agent ID",
options: agentIdOptions,
},
];
// Derived counts for summary cards and "Needs Review" (UTC today/yesterday)
@ -207,12 +216,14 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
(t.team_id ?? "").toLowerCase().includes(q) ||
(t.key_alias ?? "").toLowerCase().includes(q) ||
(t.key_hash ?? "").toLowerCase().includes(q) ||
(t.agent_id ?? "").toLowerCase().includes(q) ||
t.call_policy.toLowerCase().includes(q);
if (!matchesSearch) return false;
}
if (activeFilters["Policy"] && t.call_policy !== activeFilters["Policy"]) return false;
if (activeFilters["Team Name"] && t.team_id !== activeFilters["Team Name"]) return false;
if (activeFilters["Key Name"] && t.key_alias !== activeFilters["Key Name"]) return false;
if (activeFilters["Agent ID"] && t.agent_id !== activeFilters["Agent ID"]) return false;
return true;
});
@ -423,6 +434,9 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
<TableHeaderCell className="py-1 h-8">
<SortHeader label="# Calls" field="call_count" />
</TableHeaderCell>
<TableHeaderCell className="py-1 h-8">
<SortHeader label="Agent ID" field="agent_id" />
</TableHeaderCell>
<TableHeaderCell className="py-1 h-8">
<SortHeader label="Team Name" field="team_id" />
</TableHeaderCell>
@ -436,13 +450,13 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
<TableBody>
{loading ? (
<TableRow>
<TableCell colSpan={8} className="h-8 text-center text-gray-500">
<TableCell colSpan={9} className="h-8 text-center text-gray-500">
Loading tools…
</TableCell>
</TableRow>
) : paginated.length === 0 ? (
<TableRow>
<TableCell colSpan={8} className="h-8 text-center text-gray-500">
<TableCell colSpan={9} className="h-8 text-center text-gray-500">
No tools discovered yet. Make a chat completion that returns tool_calls to start auto-discovery.
</TableCell>
</TableRow>
@ -476,6 +490,13 @@ export const ToolPolicies: React.FC<ToolPoliciesProps> = ({ accessToken, onSelec
{(tool.call_count ?? 0).toLocaleString()}
</div>
</TableCell>
<TableCell className="py-0.5 max-h-8 overflow-hidden whitespace-nowrap">
<Tooltip title={tool.agent_id || "current weather agent"}>
<span className="max-w-[15ch] truncate block font-mono text-xs">
{tool.agent_id || "current weather agent"}
</span>
</Tooltip>
</TableCell>
<TableCell className="py-0.5 max-h-8 overflow-hidden whitespace-nowrap">
<Tooltip title={tool.team_id ?? "-"}>
<span className="max-w-[15ch] truncate block">{tool.team_id ?? "-"}</span>

View file

@ -27,7 +27,6 @@ import { ActivityMetrics, processActivityData } from "../../../activity_metrics"
import { UsageExportHeader } from "../../../EntityUsageExport";
import type { EntityType } from "../../../EntityUsageExport/types";
import {
agentDailyActivityCall,
customerDailyActivityCall,
organizationDailyActivityCall,
tagDailyActivityCall,
@ -71,6 +70,220 @@ interface EntitySpendData {
};
}
/** Stub data for agent usage until /agent/daily/activity is implemented. */
const AGENT_USAGE_STUB: EntitySpendData = (() => {
const emptyMetrics = {
spend: 0,
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
api_requests: 0,
successful_requests: 0,
failed_requests: 0,
cache_read_input_tokens: 0,
cache_creation_input_tokens: 0,
};
const makeMetrics = (overrides: Partial<typeof emptyMetrics>) => ({ ...emptyMetrics, ...overrides });
const days = 7;
const results: ExtendedDailyData[] = [];
let totalSpend = 0;
let totalRequests = 0;
let totalSuccessful = 0;
let totalFailed = 0;
let totalTokens = 0;
for (let d = days - 1; d >= 0; d--) {
const date = new Date();
date.setDate(date.getDate() - d);
const dateStr = date.toISOString().slice(0, 10);
const daySpend = 12 + d * 8 + (d % 3) * 5;
const dayRequests = 800 + d * 120 + (d % 2) * 200;
const daySuccessful = Math.floor(dayRequests * 0.94);
const dayFailed = dayRequests - daySuccessful;
const dayTokens = 45000 + d * 8000 + (d % 4) * 5000;
totalSpend += daySpend;
totalRequests += dayRequests;
totalSuccessful += daySuccessful;
totalFailed += dayFailed;
totalTokens += dayTokens;
const entitySpend1 = Math.round(daySpend * 0.4);
const entitySpend2 = Math.round(daySpend * 0.35);
const entitySpend3 = Math.round(daySpend * 0.25);
results.push({
date: dateStr,
metrics: makeMetrics({
spend: daySpend,
api_requests: dayRequests,
successful_requests: daySuccessful,
failed_requests: dayFailed,
total_tokens: dayTokens,
prompt_tokens: Math.floor(dayTokens * 0.6),
completion_tokens: Math.floor(dayTokens * 0.4),
}),
breakdown: {
model_groups: {},
mcp_servers: {},
entities: {
"agent-support-1": {
metrics: makeMetrics({
spend: entitySpend1,
api_requests: Math.floor(dayRequests * 0.45),
successful_requests: Math.floor(daySuccessful * 0.45),
failed_requests: Math.floor(dayFailed * 0.5),
total_tokens: Math.floor(dayTokens * 0.42),
prompt_tokens: Math.floor(dayTokens * 0.25),
completion_tokens: Math.floor(dayTokens * 0.17),
}),
metadata: { alias: "Support Agent" },
api_key_breakdown: {},
},
"agent-sales-1": {
metrics: makeMetrics({
spend: entitySpend2,
api_requests: Math.floor(dayRequests * 0.35),
successful_requests: Math.floor(daySuccessful * 0.35),
failed_requests: Math.floor(dayFailed * 0.3),
total_tokens: Math.floor(dayTokens * 0.35),
prompt_tokens: Math.floor(dayTokens * 0.21),
completion_tokens: Math.floor(dayTokens * 0.14),
}),
metadata: { alias: "Sales Bot" },
api_key_breakdown: {},
},
"agent-code-1": {
metrics: makeMetrics({
spend: entitySpend3,
api_requests: Math.floor(dayRequests * 0.2),
successful_requests: Math.floor(daySuccessful * 0.2),
failed_requests: Math.floor(dayFailed * 0.2),
total_tokens: Math.floor(dayTokens * 0.23),
prompt_tokens: Math.floor(dayTokens * 0.14),
completion_tokens: Math.floor(dayTokens * 0.09),
}),
metadata: { alias: "Code Reviewer" },
api_key_breakdown: {},
},
},
models: {
"gpt-4o": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.55),
api_requests: Math.floor(dayRequests * 0.5),
successful_requests: Math.floor(daySuccessful * 0.5),
failed_requests: Math.floor(dayFailed * 0.5),
total_tokens: Math.floor(dayTokens * 0.5),
prompt_tokens: Math.floor(dayTokens * 0.3),
completion_tokens: Math.floor(dayTokens * 0.2),
}),
metadata: {},
api_key_breakdown: {},
},
"claude-3-5-sonnet-20241022": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.35),
api_requests: Math.floor(dayRequests * 0.35),
successful_requests: Math.floor(daySuccessful * 0.35),
failed_requests: Math.floor(dayFailed * 0.35),
total_tokens: Math.floor(dayTokens * 0.35),
prompt_tokens: Math.floor(dayTokens * 0.21),
completion_tokens: Math.floor(dayTokens * 0.14),
}),
metadata: {},
api_key_breakdown: {},
},
"gpt-4o-mini": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.1),
api_requests: Math.floor(dayRequests * 0.15),
successful_requests: Math.floor(daySuccessful * 0.15),
failed_requests: Math.floor(dayFailed * 0.15),
total_tokens: Math.floor(dayTokens * 0.15),
prompt_tokens: Math.floor(dayTokens * 0.09),
completion_tokens: Math.floor(dayTokens * 0.06),
}),
metadata: {},
api_key_breakdown: {},
},
},
providers: {
openai: {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.65),
api_requests: Math.floor(dayRequests * 0.65),
successful_requests: Math.floor(daySuccessful * 0.65),
failed_requests: Math.floor(dayFailed * 0.65),
total_tokens: Math.floor(dayTokens * 0.65),
prompt_tokens: Math.floor(dayTokens * 0.39),
completion_tokens: Math.floor(dayTokens * 0.26),
}),
metadata: {},
api_key_breakdown: {},
},
anthropic: {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.35),
api_requests: Math.floor(dayRequests * 0.35),
successful_requests: Math.floor(daySuccessful * 0.35),
failed_requests: Math.floor(dayFailed * 0.35),
total_tokens: Math.floor(dayTokens * 0.35),
prompt_tokens: Math.floor(dayTokens * 0.21),
completion_tokens: Math.floor(dayTokens * 0.14),
}),
metadata: {},
api_key_breakdown: {},
},
},
api_keys: {
"sk-agent-dashboard-1": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.5),
api_requests: Math.floor(dayRequests * 0.5),
successful_requests: Math.floor(daySuccessful * 0.5),
failed_requests: Math.floor(dayFailed * 0.5),
total_tokens: Math.floor(dayTokens * 0.5),
}),
metadata: { key_alias: "Dashboard Key", team_id: null },
},
"sk-agent-integration-2": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.3),
api_requests: Math.floor(dayRequests * 0.3),
successful_requests: Math.floor(daySuccessful * 0.3),
failed_requests: Math.floor(dayFailed * 0.3),
total_tokens: Math.floor(dayTokens * 0.3),
}),
metadata: { key_alias: "Integration Key", team_id: null },
},
"sk-agent-dev-3": {
metrics: makeMetrics({
spend: Math.round(daySpend * 0.2),
api_requests: Math.floor(dayRequests * 0.2),
successful_requests: Math.floor(daySuccessful * 0.2),
failed_requests: Math.floor(dayFailed * 0.2),
total_tokens: Math.floor(dayTokens * 0.2),
}),
metadata: { key_alias: "Dev Key", team_id: null },
},
},
},
});
}
return {
results,
metadata: {
total_spend: totalSpend,
total_api_requests: totalRequests,
total_successful_requests: totalSuccessful,
total_failed_requests: totalFailed,
total_tokens: totalTokens,
},
};
})();
export interface EntityList {
label: string;
value: string;
@ -149,14 +362,8 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
);
setSpendData(data);
} else if (entityType === "agent") {
const data = await agentDailyActivityCall(
accessToken,
startTime,
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
);
setSpendData(data);
setSpendData(AGENT_USAGE_STUB);
return;
} else if (entityType === "user") {
const data = await userDailyActivityCall(
accessToken,

View file

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { Modal, Form, message, Select, Input, Steps, Radio, Tag, Divider } from "antd";
import { Modal, Form, message, Select, Input, InputNumber, Steps, Radio, Tag, Divider, Switch } from "antd";
import { Button } from "@tremor/react";
import { CheckCircleFilled, KeyOutlined, RobotOutlined, AppstoreOutlined, InfoCircleOutlined } from "@ant-design/icons";
import CreatedKeyDisplay from "../shared/CreatedKeyDisplay";
@ -218,6 +218,14 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
}
}
agentData.require_trace_id_on_outgoing = values.require_trace_id_on_outgoing ?? false;
agentData.require_trace_id_on_incoming = values.require_trace_id_on_incoming ?? false;
if (values.require_trace_id_on_outgoing && values.max_iteration_cap != null && values.max_iteration_cap > 0) {
agentData.litellm_params = agentData.litellm_params ?? {};
agentData.litellm_params.max_iterations = Number(values.max_iteration_cap);
}
const agentResponse = await createAgentCall(accessToken, agentData);
const agentId: string = agentResponse.agent_id;
const agentName: string = agentResponse.agent_name || values.agent_name || agentId;
@ -315,6 +323,55 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
</div>
)}
</Form.Item>
<Divider />
<div className="mt-4">
<h4 className="text-sm font-medium text-gray-700 mb-1">Agent tracing</h4>
<p className="text-sm text-gray-600 mb-3">
Require x-litellm-trace-id for observability. When enabled, requests without the header will be rejected.
</p>
<Form.Item
label="Require trace ID on outgoing calls"
name="require_trace_id_on_outgoing"
valuePropName="checked"
tooltip="When on, the agent's requests to LiteLLM/MCP must include this header."
>
<Switch />
</Form.Item>
<Form.Item
label="Require trace ID on incoming calls"
name="require_trace_id_on_incoming"
valuePropName="checked"
tooltip="When on, callers must send this header when calling this agent."
>
<Switch />
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prev, curr) => prev.require_trace_id_on_outgoing !== curr.require_trace_id_on_outgoing}
>
{() => {
const tracingOutgoing = form.getFieldValue("require_trace_id_on_outgoing");
return (
<Form.Item
label="Max iteration cap"
name="max_iteration_cap"
tooltip="Limits how many iterations (e.g. tool-call rounds) an agent can perform per trace. Only applied when agent tracing is enabled for outgoing calls."
help={!tracingOutgoing ? "Only available when agent tracing is enabled for outgoing calls above." : undefined}
>
<InputNumber
min={1}
max={1000}
placeholder="e.g. 25"
style={{ width: "100%" }}
disabled={!tracingOutgoing}
/>
</Form.Item>
);
}}
</Form.Item>
</div>
</div>
);
@ -653,8 +710,21 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
layout="vertical"
initialValues={
agentType === "a2a"
? { ...getDefaultFormValues(), allowed_mcp_servers_and_groups: { servers: [], accessGroups: [] }, mcp_tool_permissions: {} }
: { allowed_mcp_servers_and_groups: { servers: [], accessGroups: [] }, mcp_tool_permissions: {} }
? {
...getDefaultFormValues(),
allowed_mcp_servers_and_groups: { servers: [], accessGroups: [] },
mcp_tool_permissions: {},
require_trace_id_on_outgoing: false,
require_trace_id_on_incoming: false,
max_iteration_cap: undefined,
}
: {
allowed_mcp_servers_and_groups: { servers: [], accessGroups: [] },
mcp_tool_permissions: {},
require_trace_id_on_outgoing: false,
require_trace_id_on_incoming: false,
max_iteration_cap: undefined,
}
}
className="space-y-4"
>

View file

@ -9996,6 +9996,7 @@ export interface ToolRow {
key_hash?: string;
team_id?: string;
key_alias?: string;
agent_id?: string;
created_at?: string;
updated_at?: string;
created_by?: string;