mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-26 22:19:47 +00:00
Merge pull request #18729 from BerriAI/litellm_endpoint_usage_breakdown
[Feature] Add endpoint to Daily Activity Tables
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+72
@@ -0,0 +1,72 @@
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyAgentSpend_agent_id_date_api_key_model_custom__key";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyEndUserSpend_end_user_id_date_api_key_model_cu_key";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyOrganizationSpend_organization_id_date_api_key_key";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyTagSpend_tag_date_api_key_model_custom_llm_pro_key";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyTeamSpend_team_id_date_api_key_model_custom_ll_key";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_DailyUserSpend_user_id_date_api_key_model_custom_ll_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyAgentSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyEndUserSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyOrganizationSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyTagSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyTeamSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_DailyUserSpend" ADD COLUMN "endpoint" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyAgentSpend_endpoint_idx" ON "LiteLLM_DailyAgentSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyAgentSpend_agent_id_date_api_key_model_custom__key" ON "LiteLLM_DailyAgentSpend"("agent_id", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyEndUserSpend_endpoint_idx" ON "LiteLLM_DailyEndUserSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyEndUserSpend_end_user_id_date_api_key_model_cu_key" ON "LiteLLM_DailyEndUserSpend"("end_user_id", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyOrganizationSpend_endpoint_idx" ON "LiteLLM_DailyOrganizationSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyOrganizationSpend_organization_id_date_api_key_key" ON "LiteLLM_DailyOrganizationSpend"("organization_id", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyTagSpend_endpoint_idx" ON "LiteLLM_DailyTagSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyTagSpend_tag_date_api_key_model_custom_llm_pro_key" ON "LiteLLM_DailyTagSpend"("tag", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyTeamSpend_endpoint_idx" ON "LiteLLM_DailyTeamSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyTeamSpend_team_id_date_api_key_model_custom_ll_key" ON "LiteLLM_DailyTeamSpend"("team_id", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_DailyUserSpend_endpoint_idx" ON "LiteLLM_DailyUserSpend"("endpoint");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_DailyUserSpend_user_id_date_api_key_model_custom_ll_key" ON "LiteLLM_DailyUserSpend"("user_id", "date", "api_key", "model", "custom_llm_provider", "mcp_namespaced_tool_name", "endpoint");
|
||||
|
||||
@@ -422,6 +422,7 @@ model LiteLLM_DailyUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -433,12 +434,13 @@ model LiteLLM_DailyUserSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily organization spend metrics per model and key
|
||||
@@ -451,6 +453,7 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -462,12 +465,13 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([organization_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily end user (customer) spend metrics per model and key
|
||||
@@ -480,6 +484,7 @@ model LiteLLM_DailyEndUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -490,12 +495,13 @@ model LiteLLM_DailyEndUserSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([end_user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily agent spend metrics per model and key
|
||||
@@ -508,6 +514,7 @@ model LiteLLM_DailyAgentSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -518,12 +525,13 @@ model LiteLLM_DailyAgentSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([agent_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -536,6 +544,7 @@ model LiteLLM_DailyTeamSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -547,12 +556,13 @@ model LiteLLM_DailyTeamSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([team_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -566,6 +576,7 @@ model LiteLLM_DailyTagSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -577,12 +588,13 @@ model LiteLLM_DailyTagSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([tag])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "litellm-proxy-extras"
|
||||
version = "0.4.18"
|
||||
version = "0.4.19"
|
||||
description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package."
|
||||
authors = ["BerriAI"]
|
||||
readme = "README.md"
|
||||
@@ -22,7 +22,7 @@ requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.commitizen]
|
||||
version = "0.4.18"
|
||||
version = "0.4.19"
|
||||
version_files = [
|
||||
"pyproject.toml:version",
|
||||
"../requirements.txt:litellm-proxy-extras==",
|
||||
|
||||
@@ -3735,6 +3735,7 @@ class BaseDailySpendTransaction(TypedDict):
|
||||
model_group: Optional[str]
|
||||
mcp_namespaced_tool_name: Optional[str]
|
||||
custom_llm_provider: Optional[str]
|
||||
endpoint: Optional[str]
|
||||
|
||||
# token count metrics
|
||||
prompt_tokens: int
|
||||
|
||||
@@ -42,6 +42,7 @@ from litellm.proxy.db.db_transaction_queue.daily_spend_update_queue import (
|
||||
from litellm.proxy.db.db_transaction_queue.pod_lock_manager import PodLockManager
|
||||
from litellm.proxy.db.db_transaction_queue.redis_update_buffer import RedisUpdateBuffer
|
||||
from litellm.proxy.db.db_transaction_queue.spend_update_queue import SpendUpdateQueue
|
||||
from litellm.proxy.route_llm_request import ROUTE_ENDPOINT_MAPPING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.proxy.utils import PrismaClient, ProxyLogging
|
||||
@@ -1205,6 +1206,7 @@ class DBSpendUpdateWriter:
|
||||
"mcp_namespaced_tool_name"
|
||||
)
|
||||
or "",
|
||||
"endpoint": transaction.get("endpoint") or "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,6 +1227,7 @@ class DBSpendUpdateWriter:
|
||||
"custom_llm_provider": transaction.get(
|
||||
"custom_llm_provider"
|
||||
),
|
||||
"endpoint": transaction.get("endpoint"),
|
||||
"prompt_tokens": transaction["prompt_tokens"],
|
||||
"completion_tokens": transaction["completion_tokens"],
|
||||
"spend": transaction["spend"],
|
||||
@@ -1287,6 +1290,9 @@ class DBSpendUpdateWriter:
|
||||
if entity_type == "tag" and "request_id" in transaction:
|
||||
update_data["request_id"] = transaction.get("request_id")
|
||||
|
||||
# Add endpoint to update_data so existing rows get their endpoint field updated
|
||||
update_data["endpoint"] = transaction.get("endpoint") or ""
|
||||
|
||||
table.upsert(
|
||||
where=where_clause,
|
||||
data={
|
||||
@@ -1347,7 +1353,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="user",
|
||||
entity_id_field="user_id",
|
||||
table_name="litellm_dailyuserspend",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1368,7 +1374,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="team",
|
||||
entity_id_field="team_id",
|
||||
table_name="litellm_dailyteamspend",
|
||||
unique_constraint_name="team_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="team_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1389,7 +1395,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="org",
|
||||
entity_id_field="organization_id",
|
||||
table_name="litellm_dailyorganizationspend",
|
||||
unique_constraint_name="organization_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="organization_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1410,7 +1416,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="end_user",
|
||||
entity_id_field="end_user_id",
|
||||
table_name="litellm_dailyenduserspend",
|
||||
unique_constraint_name="end_user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="end_user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1431,7 +1437,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="agent",
|
||||
entity_id_field="agent_id",
|
||||
table_name="litellm_dailyagentspend",
|
||||
unique_constraint_name="agent_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="agent_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1452,7 +1458,7 @@ class DBSpendUpdateWriter:
|
||||
entity_type="tag",
|
||||
entity_id_field="tag",
|
||||
table_name="litellm_dailytagspend",
|
||||
unique_constraint_name="tag_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name",
|
||||
unique_constraint_name="tag_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
async def _common_add_spend_log_transaction_to_daily_transaction(
|
||||
@@ -1513,6 +1519,12 @@ class DBSpendUpdateWriter:
|
||||
)
|
||||
return None
|
||||
try:
|
||||
# Map call_type to endpoint using ROUTE_ENDPOINT_MAPPING
|
||||
call_type = payload.get("call_type", None)
|
||||
endpoint = None
|
||||
if call_type:
|
||||
endpoint = ROUTE_ENDPOINT_MAPPING.get(call_type, None)
|
||||
|
||||
daily_transaction = BaseDailySpendTransaction(
|
||||
date=date,
|
||||
api_key=payload["api_key"],
|
||||
@@ -1520,6 +1532,7 @@ class DBSpendUpdateWriter:
|
||||
model_group=payload.get("model_group", None),
|
||||
mcp_namespaced_tool_name=payload.get("mcp_namespaced_tool_name", None),
|
||||
custom_llm_provider=payload.get("custom_llm_provider", None),
|
||||
endpoint=endpoint,
|
||||
prompt_tokens=payload["prompt_tokens"],
|
||||
completion_tokens=payload["completion_tokens"],
|
||||
spend=payload["spend"],
|
||||
@@ -1563,7 +1576,8 @@ class DBSpendUpdateWriter:
|
||||
if base_daily_transaction is None:
|
||||
return
|
||||
|
||||
daily_transaction_key = f"{payload['user']}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{payload['user']}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyUserSpendTransaction(
|
||||
user_id=payload["user"], **base_daily_transaction
|
||||
)
|
||||
@@ -1595,7 +1609,8 @@ class DBSpendUpdateWriter:
|
||||
)
|
||||
return
|
||||
|
||||
daily_transaction_key = f"{payload['team_id']}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{payload['team_id']}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyTeamSpendTransaction(
|
||||
team_id=payload["team_id"], **base_daily_transaction
|
||||
)
|
||||
@@ -1637,7 +1652,8 @@ class DBSpendUpdateWriter:
|
||||
if base_daily_transaction is None:
|
||||
return
|
||||
|
||||
daily_transaction_key = f"{org_id}_{base_daily_transaction['date']}_{payload_with_org['api_key']}_{payload_with_org['model']}_{payload_with_org['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{org_id}_{base_daily_transaction['date']}_{payload_with_org['api_key']}_{payload_with_org['model']}_{payload_with_org['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyOrganizationSpendTransaction(
|
||||
organization_id=org_id, **base_daily_transaction
|
||||
)
|
||||
@@ -1679,7 +1695,8 @@ class DBSpendUpdateWriter:
|
||||
if base_daily_transaction is None:
|
||||
return
|
||||
|
||||
daily_transaction_key = f"{end_user_id}_{base_daily_transaction['date']}_{payload_with_end_user_id['api_key']}_{payload_with_end_user_id['model']}_{payload_with_end_user_id['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{end_user_id}_{base_daily_transaction['date']}_{payload_with_end_user_id['api_key']}_{payload_with_end_user_id['model']}_{payload_with_end_user_id['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyEndUserSpendTransaction(
|
||||
end_user_id=end_user_id, **base_daily_transaction
|
||||
)
|
||||
@@ -1723,7 +1740,8 @@ class DBSpendUpdateWriter:
|
||||
)
|
||||
if base_daily_transaction is None:
|
||||
return
|
||||
daily_transaction_key = f"{payload['agent_id']}_{base_daily_transaction['date']}_{payload_with_agent_id['api_key']}_{payload_with_agent_id['model']}_{payload_with_agent_id['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{payload['agent_id']}_{base_daily_transaction['date']}_{payload_with_agent_id['api_key']}_{payload_with_agent_id['model']}_{payload_with_agent_id['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyAgentSpendTransaction(
|
||||
agent_id=payload['agent_id'], **base_daily_transaction
|
||||
)
|
||||
@@ -1763,7 +1781,8 @@ class DBSpendUpdateWriter:
|
||||
else:
|
||||
raise ValueError(f"Invalid request_tags: {payload['request_tags']}")
|
||||
for tag in request_tags:
|
||||
daily_transaction_key = f"{tag}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}"
|
||||
endpoint_str = base_daily_transaction.get("endpoint") or ""
|
||||
daily_transaction_key = f"{tag}_{base_daily_transaction['date']}_{payload['api_key']}_{payload['model']}_{payload['custom_llm_provider']}_{endpoint_str}"
|
||||
daily_transaction = DailyTagSpendTransaction(
|
||||
tag=tag, **base_daily_transaction, request_id=payload["request_id"]
|
||||
)
|
||||
|
||||
@@ -227,6 +227,41 @@ def update_breakdown_metrics(
|
||||
)
|
||||
)
|
||||
|
||||
# Update endpoint breakdown
|
||||
if record.endpoint:
|
||||
if record.endpoint not in breakdown.endpoints:
|
||||
breakdown.endpoints[record.endpoint] = MetricWithMetadata(
|
||||
metrics=SpendMetrics(),
|
||||
metadata={},
|
||||
)
|
||||
breakdown.endpoints[record.endpoint].metrics = update_metrics(
|
||||
breakdown.endpoints[record.endpoint].metrics, record
|
||||
)
|
||||
|
||||
# Update API key breakdown for this endpoint
|
||||
if record.api_key not in breakdown.endpoints[record.endpoint].api_key_breakdown:
|
||||
breakdown.endpoints[record.endpoint].api_key_breakdown[record.api_key] = (
|
||||
KeyMetricWithMetadata(
|
||||
metrics=SpendMetrics(),
|
||||
metadata=KeyMetadata(
|
||||
key_alias=api_key_metadata.get(record.api_key, {}).get(
|
||||
"key_alias", None
|
||||
),
|
||||
team_id=api_key_metadata.get(record.api_key, {}).get(
|
||||
"team_id", None
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
breakdown.endpoints[record.endpoint].api_key_breakdown[record.api_key].metrics = (
|
||||
update_metrics(
|
||||
breakdown.endpoints[record.endpoint]
|
||||
.api_key_breakdown[record.api_key]
|
||||
.metrics,
|
||||
record,
|
||||
)
|
||||
)
|
||||
|
||||
# Update api key breakdown
|
||||
if record.api_key not in breakdown.api_keys:
|
||||
breakdown.api_keys[record.api_key] = KeyMetricWithMetadata(
|
||||
|
||||
@@ -422,6 +422,7 @@ model LiteLLM_DailyUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -433,12 +434,13 @@ model LiteLLM_DailyUserSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily organization spend metrics per model and key
|
||||
@@ -451,6 +453,7 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -462,12 +465,13 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([organization_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily end user (customer) spend metrics per model and key
|
||||
@@ -480,6 +484,7 @@ model LiteLLM_DailyEndUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -490,12 +495,13 @@ model LiteLLM_DailyEndUserSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([end_user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily agent spend metrics per model and key
|
||||
@@ -508,6 +514,7 @@ model LiteLLM_DailyAgentSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -518,12 +525,13 @@ model LiteLLM_DailyAgentSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([agent_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -536,6 +544,7 @@ model LiteLLM_DailyTeamSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -547,12 +556,13 @@ model LiteLLM_DailyTeamSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([team_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -566,6 +576,7 @@ model LiteLLM_DailyTagSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -577,12 +588,13 @@ model LiteLLM_DailyTagSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([tag])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@ class BreakdownMetrics(BaseModel):
|
||||
providers: Dict[str, MetricWithMetadata] = Field(
|
||||
default_factory=dict
|
||||
) # provider -> {metrics, metadata}
|
||||
endpoints: Dict[str, MetricWithMetadata] = Field(
|
||||
default_factory=dict
|
||||
) # endpoint -> {metrics, metadata}
|
||||
api_keys: Dict[str, KeyMetricWithMetadata] = Field(
|
||||
default_factory=dict
|
||||
) # api_key -> {metrics, metadata}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ websockets = {version = "^15.0.1", optional = true}
|
||||
boto3 = {version = "1.36.0", optional = true}
|
||||
redisvl = {version = "^0.4.1", optional = true, markers = "python_version >= '3.9' and python_version < '3.14'"}
|
||||
mcp = {version = "^1.21.2", optional = true, python = ">=3.10"}
|
||||
litellm-proxy-extras = {version = "0.4.18", optional = true}
|
||||
litellm-proxy-extras = {version = "0.4.19", optional = true}
|
||||
rich = {version = "13.7.1", optional = true}
|
||||
litellm-enterprise = {version = "0.1.27", optional = true}
|
||||
diskcache = {version = "^5.6.1", optional = true}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ sentry_sdk==2.21.0 # for sentry error handling
|
||||
detect-secrets==1.5.0 # Enterprise - secret detection / masking in LLM requests
|
||||
cryptography==44.0.1
|
||||
tzdata==2025.1 # IANA time zone database
|
||||
litellm-proxy-extras==0.4.18 # for proxy extras - e.g. prisma migrations
|
||||
litellm-proxy-extras==0.4.19 # for proxy extras - e.g. prisma migrations
|
||||
llm-sandbox==0.3.31 # for skill execution in sandbox
|
||||
### LITELLM PACKAGE DEPENDENCIES
|
||||
python-dotenv==1.0.1 # for env
|
||||
|
||||
+18
-6
@@ -422,6 +422,7 @@ model LiteLLM_DailyUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -433,12 +434,13 @@ model LiteLLM_DailyUserSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily organization spend metrics per model and key
|
||||
@@ -451,6 +453,7 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -462,12 +465,13 @@ model LiteLLM_DailyOrganizationSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([organization_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([organization_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily end user (customer) spend metrics per model and key
|
||||
@@ -480,6 +484,7 @@ model LiteLLM_DailyEndUserSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -490,12 +495,13 @@ model LiteLLM_DailyEndUserSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([end_user_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([end_user_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily agent spend metrics per model and key
|
||||
@@ -508,6 +514,7 @@ model LiteLLM_DailyAgentSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -518,12 +525,13 @@ model LiteLLM_DailyAgentSpend {
|
||||
failed_requests BigInt @default(0)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([agent_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([agent_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -536,6 +544,7 @@ model LiteLLM_DailyTeamSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -547,12 +556,13 @@ model LiteLLM_DailyTeamSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([team_id, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([team_id])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
// Track daily team spend metrics per model and key
|
||||
@@ -566,6 +576,7 @@ model LiteLLM_DailyTagSpend {
|
||||
model_group String?
|
||||
custom_llm_provider String?
|
||||
mcp_namespaced_tool_name String?
|
||||
endpoint String?
|
||||
prompt_tokens BigInt @default(0)
|
||||
completion_tokens BigInt @default(0)
|
||||
cache_read_input_tokens BigInt @default(0)
|
||||
@@ -577,12 +588,13 @@ model LiteLLM_DailyTagSpend {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name])
|
||||
@@unique([tag, date, api_key, model, custom_llm_provider, mcp_namespaced_tool_name, endpoint])
|
||||
@@index([date])
|
||||
@@index([tag])
|
||||
@@index([api_key])
|
||||
@@index([model])
|
||||
@@index([mcp_namespaced_tool_name])
|
||||
@@index([endpoint])
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ async def test_update_daily_spend_with_null_entity_id():
|
||||
entity_type="user",
|
||||
entity_id_field="user_id",
|
||||
table_name="litellm_dailyuserspend",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
# Verify that table.upsert was called
|
||||
@@ -115,12 +115,14 @@ async def test_update_daily_spend_with_null_entity_id():
|
||||
|
||||
# Verify the where clause contains null entity_id
|
||||
call_args = mock_table.upsert.call_args[1]
|
||||
where_clause = call_args["where"]["user_id_date_api_key_model_custom_llm_provider"]
|
||||
where_clause = call_args["where"]["user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint"]
|
||||
assert where_clause["user_id"] is None
|
||||
assert where_clause["date"] == "2024-01-01"
|
||||
assert where_clause["api_key"] == "test-api-key"
|
||||
assert where_clause["model"] == "gpt-4"
|
||||
assert where_clause["custom_llm_provider"] == "openai"
|
||||
assert where_clause["mcp_namespaced_tool_name"] == ""
|
||||
assert where_clause["endpoint"] == ""
|
||||
|
||||
# Verify the create data contains null entity_id
|
||||
create_data = call_args["data"]["create"]
|
||||
@@ -129,6 +131,8 @@ async def test_update_daily_spend_with_null_entity_id():
|
||||
assert create_data["api_key"] == "test-api-key"
|
||||
assert create_data["model"] == "gpt-4"
|
||||
assert create_data["custom_llm_provider"] == "openai"
|
||||
assert create_data["mcp_namespaced_tool_name"] == ""
|
||||
assert create_data["endpoint"] is None
|
||||
assert create_data["prompt_tokens"] == 10
|
||||
assert create_data["completion_tokens"] == 20
|
||||
assert create_data["spend"] == 0.1
|
||||
@@ -171,13 +175,14 @@ async def test_update_daily_spend_sorting():
|
||||
}
|
||||
upsert_calls.append(call(
|
||||
where={
|
||||
"user_id_date_api_key_model_custom_llm_provider": {
|
||||
"user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint": {
|
||||
"user_id": f"user{i+11}", # user11 ... user60, sorted order
|
||||
"date": "2024-01-01",
|
||||
"api_key": "test-api-key",
|
||||
"model": "gpt-4",
|
||||
"custom_llm_provider": "openai",
|
||||
"mcp_namespaced_tool_name": "",
|
||||
"endpoint": "",
|
||||
}
|
||||
},
|
||||
data={
|
||||
@@ -189,6 +194,7 @@ async def test_update_daily_spend_sorting():
|
||||
"model_group": None,
|
||||
"mcp_namespaced_tool_name": "",
|
||||
"custom_llm_provider": "openai",
|
||||
"endpoint": None,
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 20,
|
||||
"spend": 0.1,
|
||||
@@ -203,6 +209,7 @@ async def test_update_daily_spend_sorting():
|
||||
"api_requests": {"increment": 1},
|
||||
"successful_requests": {"increment": 1},
|
||||
"failed_requests": {"increment": 0},
|
||||
"endpoint": "",
|
||||
},
|
||||
},
|
||||
))
|
||||
@@ -216,7 +223,7 @@ async def test_update_daily_spend_sorting():
|
||||
entity_type="user",
|
||||
entity_id_field="user_id",
|
||||
table_name="litellm_dailyuserspend",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
# Verify that table.upsert was called
|
||||
@@ -372,7 +379,7 @@ async def test_update_daily_spend_with_none_values_in_sorting_fields():
|
||||
entity_type="user",
|
||||
entity_id_field="user_id",
|
||||
table_name="litellm_dailyuserspend",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider",
|
||||
unique_constraint_name="user_id_date_api_key_model_custom_llm_provider_mcp_namespaced_tool_name_endpoint",
|
||||
)
|
||||
|
||||
# Verify that table.upsert was called (should be called 5 times, once for each transaction)
|
||||
@@ -588,7 +595,7 @@ async def test_add_spend_log_transaction_to_daily_org_transaction_injects_org_id
|
||||
update_dict = call_args["update"]
|
||||
assert len(update_dict) == 1
|
||||
for key, transaction in update_dict.items():
|
||||
assert key == f"{org_id}_2024-01-01_test-key_gpt-4_openai"
|
||||
assert key == f"{org_id}_2024-01-01_test-key_gpt-4_openai_"
|
||||
assert transaction["organization_id"] == org_id
|
||||
assert transaction["date"] == "2024-01-01"
|
||||
assert transaction["api_key"] == "test-key"
|
||||
@@ -665,7 +672,7 @@ async def test_add_spend_log_transaction_to_daily_end_user_transaction_injects_e
|
||||
update_dict = call_args["update"]
|
||||
assert len(update_dict) == 1
|
||||
for key, transaction in update_dict.items():
|
||||
assert key == f"{end_user_id}_2024-01-01_test-key_gpt-4_openai"
|
||||
assert key == f"{end_user_id}_2024-01-01_test-key_gpt-4_openai_"
|
||||
assert transaction["end_user_id"] == end_user_id
|
||||
assert transaction["date"] == "2024-01-01"
|
||||
assert transaction["api_key"] == "test-key"
|
||||
@@ -741,7 +748,7 @@ async def test_add_spend_log_transaction_to_daily_agent_transaction_injects_agen
|
||||
update_dict = call_args["update"]
|
||||
assert len(update_dict) == 1
|
||||
for key, transaction in update_dict.items():
|
||||
assert key == f"{agent_id}_2024-01-01_test-key_gpt-4_openai"
|
||||
assert key == f"{agent_id}_2024-01-01_test-key_gpt-4_openai_"
|
||||
assert transaction["agent_id"] == agent_id
|
||||
assert transaction["date"] == "2024-01-01"
|
||||
assert transaction["api_key"] == "test-key"
|
||||
@@ -780,4 +787,55 @@ async def test_add_spend_log_transaction_to_daily_agent_transaction_skips_when_a
|
||||
prisma_client=mock_prisma,
|
||||
)
|
||||
|
||||
writer.daily_agent_spend_update_queue.add_update.assert_not_called()
|
||||
writer.daily_agent_spend_update_queue.add_update.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_endpoint_field_is_correctly_mapped_from_call_type():
|
||||
"""
|
||||
Test that the endpoint field is correctly mapped from call_type using ROUTE_ENDPOINT_MAPPING.
|
||||
Verifies that when call_type is provided, the endpoint is set in the transaction and included in the key.
|
||||
"""
|
||||
writer = DBSpendUpdateWriter()
|
||||
mock_prisma = MagicMock()
|
||||
mock_prisma.get_request_status = MagicMock(return_value="success")
|
||||
|
||||
payload = {
|
||||
"request_id": "req-endpoint-test",
|
||||
"user": "test-user",
|
||||
"call_type": "acompletion", # Maps to "/chat/completions"
|
||||
"startTime": "2024-01-01T12:00:00",
|
||||
"api_key": "test-key",
|
||||
"model": "gpt-4",
|
||||
"custom_llm_provider": "openai",
|
||||
"model_group": "gpt-4-group",
|
||||
"prompt_tokens": 100,
|
||||
"completion_tokens": 50,
|
||||
"spend": 0.15,
|
||||
"metadata": '{"usage_object": {}}',
|
||||
}
|
||||
|
||||
writer.daily_spend_update_queue.add_update = AsyncMock()
|
||||
|
||||
await writer.add_spend_log_transaction_to_daily_user_transaction(
|
||||
payload=payload,
|
||||
prisma_client=mock_prisma,
|
||||
)
|
||||
|
||||
writer.daily_spend_update_queue.add_update.assert_called_once()
|
||||
|
||||
call_args = writer.daily_spend_update_queue.add_update.call_args[1]
|
||||
update_dict = call_args["update"]
|
||||
assert len(update_dict) == 1
|
||||
|
||||
for key, transaction in update_dict.items():
|
||||
# Verify endpoint is included in the key
|
||||
assert key == f"test-user_2024-01-01_test-key_gpt-4_openai_/chat/completions"
|
||||
|
||||
# Verify endpoint is set in the transaction
|
||||
assert transaction["endpoint"] == "/chat/completions"
|
||||
assert transaction["user_id"] == "test-user"
|
||||
assert transaction["date"] == "2024-01-01"
|
||||
assert transaction["api_key"] == "test-key"
|
||||
assert transaction["model"] == "gpt-4"
|
||||
assert transaction["custom_llm_provider"] == "openai"
|
||||
@@ -12,6 +12,7 @@ from litellm.proxy.management_endpoints.common_daily_activity import (
|
||||
_is_user_agent_tag,
|
||||
compute_tag_metadata_totals,
|
||||
get_daily_activity,
|
||||
get_daily_activity_aggregated,
|
||||
)
|
||||
|
||||
|
||||
@@ -124,3 +125,86 @@ def test_compute_tag_metadata_totals():
|
||||
result = compute_tag_metadata_totals([])
|
||||
assert result.spend == 0.0
|
||||
assert result.prompt_tokens == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_daily_activity_aggregated_with_endpoint_breakdown():
|
||||
"""Test that endpoint breakdown is included in aggregated daily activity."""
|
||||
# Mock PrismaClient
|
||||
mock_prisma = MagicMock()
|
||||
mock_prisma.db = MagicMock()
|
||||
|
||||
# Create mock records with endpoint fields
|
||||
class MockRecord:
|
||||
def __init__(self, date, endpoint, api_key, model, spend, prompt_tokens, completion_tokens):
|
||||
self.date = date
|
||||
self.endpoint = endpoint
|
||||
self.api_key = api_key
|
||||
self.model = model
|
||||
self.model_group = None
|
||||
self.custom_llm_provider = "openai"
|
||||
self.mcp_namespaced_tool_name = None
|
||||
self.spend = spend
|
||||
self.prompt_tokens = prompt_tokens
|
||||
self.completion_tokens = completion_tokens
|
||||
self.total_tokens = prompt_tokens + completion_tokens
|
||||
self.cache_read_input_tokens = 0
|
||||
self.cache_creation_input_tokens = 0
|
||||
self.api_requests = 1
|
||||
self.successful_requests = 1
|
||||
self.failed_requests = 0
|
||||
|
||||
mock_records = [
|
||||
MockRecord("2024-01-01", "/v1/chat/completions", "key-1", "gpt-4", 10.0, 100, 50),
|
||||
MockRecord("2024-01-01", "/v1/chat/completions", "key-1", "gpt-4", 5.0, 50, 25),
|
||||
MockRecord("2024-01-01", "/v1/embeddings", "key-2", "text-embedding-ada-002", 3.0, 30, 0),
|
||||
]
|
||||
|
||||
# Mock the table methods
|
||||
mock_table = MagicMock()
|
||||
mock_table.find_many = AsyncMock(return_value=mock_records)
|
||||
mock_prisma.db.litellm_dailyuserspend = mock_table
|
||||
mock_prisma.db.litellm_verificationtoken = MagicMock()
|
||||
mock_prisma.db.litellm_verificationtoken.find_many = AsyncMock(return_value=[])
|
||||
|
||||
# Call the function
|
||||
result = await get_daily_activity_aggregated(
|
||||
prisma_client=mock_prisma,
|
||||
table_name="litellm_dailyuserspend",
|
||||
entity_id_field="user_id",
|
||||
entity_id=None,
|
||||
entity_metadata_field=None,
|
||||
start_date="2024-01-01",
|
||||
end_date="2024-01-01",
|
||||
model=None,
|
||||
api_key=None,
|
||||
)
|
||||
|
||||
# Verify the results
|
||||
assert len(result.results) == 1
|
||||
daily_data = result.results[0]
|
||||
assert daily_data.date.strftime("%Y-%m-%d") == "2024-01-01"
|
||||
|
||||
# Verify endpoint breakdown exists
|
||||
assert "endpoints" in daily_data.breakdown.model_fields
|
||||
assert len(daily_data.breakdown.endpoints) == 2
|
||||
|
||||
# Verify /v1/chat/completions endpoint breakdown
|
||||
assert "/v1/chat/completions" in daily_data.breakdown.endpoints
|
||||
chat_endpoint = daily_data.breakdown.endpoints["/v1/chat/completions"]
|
||||
assert chat_endpoint.metrics.spend == 15.0 # 10.0 + 5.0
|
||||
assert chat_endpoint.metrics.prompt_tokens == 150 # 100 + 50
|
||||
assert chat_endpoint.metrics.completion_tokens == 75 # 50 + 25
|
||||
|
||||
# Verify /v1/embeddings endpoint breakdown
|
||||
assert "/v1/embeddings" in daily_data.breakdown.endpoints
|
||||
embeddings_endpoint = daily_data.breakdown.endpoints["/v1/embeddings"]
|
||||
assert embeddings_endpoint.metrics.spend == 3.0
|
||||
assert embeddings_endpoint.metrics.prompt_tokens == 30
|
||||
assert embeddings_endpoint.metrics.completion_tokens == 0
|
||||
|
||||
# Verify API key breakdowns within endpoints
|
||||
assert "key-1" in chat_endpoint.api_key_breakdown
|
||||
assert chat_endpoint.api_key_breakdown["key-1"].metrics.spend == 15.0
|
||||
assert "key-2" in embeddings_endpoint.api_key_breakdown
|
||||
assert embeddings_endpoint.api_key_breakdown["key-2"].metrics.spend == 3.0
|
||||
|
||||
Reference in New Issue
Block a user