mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
feat: add some stubbed data - help show concept
This commit is contained in:
parent
f81152d5e1
commit
69e4b4d485
4 changed files with 314 additions and 15 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue