Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- CreateTable
CREATE TABLE "LiteLLM_DailyGuardrailMetrics" (
"guardrail_id" TEXT NOT NULL,
"date" TEXT NOT NULL,
"requests_evaluated" BIGINT NOT NULL DEFAULT 0,
"passed_count" BIGINT NOT NULL DEFAULT 0,
"blocked_count" BIGINT NOT NULL DEFAULT 0,
"flagged_count" BIGINT NOT NULL DEFAULT 0,
"avg_score" DOUBLE PRECISION,
"avg_latency_ms" DOUBLE PRECISION,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,

CONSTRAINT "LiteLLM_DailyGuardrailMetrics_pkey" PRIMARY KEY ("guardrail_id","date")
);

-- CreateTable
CREATE TABLE "LiteLLM_DailyPolicyMetrics" (
"policy_id" TEXT NOT NULL,
"date" TEXT NOT NULL,
"requests_evaluated" BIGINT NOT NULL DEFAULT 0,
"passed_count" BIGINT NOT NULL DEFAULT 0,
"blocked_count" BIGINT NOT NULL DEFAULT 0,
"flagged_count" BIGINT NOT NULL DEFAULT 0,
"avg_score" DOUBLE PRECISION,
"avg_latency_ms" DOUBLE PRECISION,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,

CONSTRAINT "LiteLLM_DailyPolicyMetrics_pkey" PRIMARY KEY ("policy_id","date")
);

-- CreateTable
CREATE TABLE "LiteLLM_SpendLogGuardrailIndex" (
"request_id" TEXT NOT NULL,
"guardrail_id" TEXT NOT NULL,
"policy_id" TEXT,
"start_time" TIMESTAMP(3) NOT NULL,

CONSTRAINT "LiteLLM_SpendLogGuardrailIndex_pkey" PRIMARY KEY ("request_id","guardrail_id")
);

-- CreateIndex
CREATE INDEX "LiteLLM_DailyGuardrailMetrics_date_idx" ON "LiteLLM_DailyGuardrailMetrics"("date");

-- CreateIndex
CREATE INDEX "LiteLLM_DailyGuardrailMetrics_guardrail_id_idx" ON "LiteLLM_DailyGuardrailMetrics"("guardrail_id");

-- CreateIndex
CREATE INDEX "LiteLLM_DailyPolicyMetrics_date_idx" ON "LiteLLM_DailyPolicyMetrics"("date");

-- CreateIndex
CREATE INDEX "LiteLLM_DailyPolicyMetrics_policy_id_idx" ON "LiteLLM_DailyPolicyMetrics"("policy_id");

-- CreateIndex
CREATE INDEX "LiteLLM_SpendLogGuardrailIndex_guardrail_id_start_time_idx" ON "LiteLLM_SpendLogGuardrailIndex"("guardrail_id", "start_time");

-- CreateIndex
CREATE INDEX "LiteLLM_SpendLogGuardrailIndex_policy_id_start_time_idx" ON "LiteLLM_SpendLogGuardrailIndex"("policy_id", "start_time");

95 changes: 48 additions & 47 deletions litellm-proxy-extras/litellm_proxy_extras/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -213,53 +213,6 @@ model LiteLLM_DeletedTeamTable {
@@index([created_at])
}

// Audit table for deleted teams - preserves spend and team information for historical tracking
model LiteLLM_DeletedTeamTable {
id String @id @default(uuid())
team_id String // Original team_id
team_alias String?
organization_id String?
object_permission_id String?
admins String[]
members String[]
members_with_roles Json @default("{}")
metadata Json @default("{}")
max_budget Float?
soft_budget Float?
spend Float @default(0.0)
models String[]
max_parallel_requests Int?
tpm_limit BigInt?
rpm_limit BigInt?
budget_duration String?
budget_reset_at DateTime?
blocked Boolean @default(false)
model_spend Json @default("{}")
model_max_budget Json @default("{}")
router_settings Json? @default("{}")
team_member_permissions String[] @default([])
access_group_ids String[] @default([])
policies String[] @default([])
model_id Int? // id for LiteLLM_ModelTable -> stores team-level model aliases
allow_team_guardrail_config Boolean @default(false)

// Original timestamps from team creation/updates
created_at DateTime? @map("created_at")
updated_at DateTime? @map("updated_at")

// Deletion metadata
deleted_at DateTime @default(now()) @map("deleted_at")
deleted_by String? @map("deleted_by") // User who deleted the team
deleted_by_api_key String? @map("deleted_by_api_key") // API key hash that performed the deletion
litellm_changed_by String? @map("litellm_changed_by") // From litellm-changed-by header if provided

@@index([team_id])
@@index([deleted_at])
@@index([organization_id])
@@index([team_alias])
@@index([created_at])
}

// Track spend, rate limit, budget Users
model LiteLLM_UserTable {
user_id String @id
Expand Down Expand Up @@ -912,6 +865,54 @@ model LiteLLM_GuardrailsTable {
updated_at DateTime @updatedAt
}

// Daily guardrail metrics for usage dashboard (one row per guardrail per day)
model LiteLLM_DailyGuardrailMetrics {
guardrail_id String // logical id; may not FK if guardrail from config
date String // YYYY-MM-DD
requests_evaluated BigInt @default(0)
passed_count BigInt @default(0)
blocked_count BigInt @default(0)
flagged_count BigInt @default(0)
avg_score Float?
avg_latency_ms Float?
created_at DateTime @default(now())
updated_at DateTime @updatedAt

@@id([guardrail_id, date])
@@index([date])
@@index([guardrail_id])
}

// Daily policy metrics for usage dashboard (one row per policy per day)
model LiteLLM_DailyPolicyMetrics {
policy_id String
date String // YYYY-MM-DD
requests_evaluated BigInt @default(0)
passed_count BigInt @default(0)
blocked_count BigInt @default(0)
flagged_count BigInt @default(0)
avg_score Float?
avg_latency_ms Float?
created_at DateTime @default(now())
updated_at DateTime @updatedAt

@@id([policy_id, date])
@@index([date])
@@index([policy_id])
}

// Index for fast "last N logs for guardrail/policy" from SpendLogs
model LiteLLM_SpendLogGuardrailIndex {
request_id String
guardrail_id String
policy_id String? // set when run as part of a policy pipeline
start_time DateTime

@@id([request_id, guardrail_id])
@@index([guardrail_id, start_time])
@@index([policy_id, start_time])
}

// Prompt table for storing prompt configurations
model LiteLLM_PromptTable {
id String @id @default(uuid())
Expand Down
7 changes: 4 additions & 3 deletions litellm/integrations/custom_guardrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,10 @@ def _append_guardrail_info(container: dict) -> None:
elif "litellm_metadata" in request_data:
_append_guardrail_info(request_data["litellm_metadata"])
else:
verbose_logger.warning(
"unable to log guardrail information. No metadata found in request_data"
)
# Ensure guardrail info is always logged (e.g. proxy may not have set
# metadata yet). Attach to "metadata" so spend log / standard logging see it.
request_data["metadata"] = {}
_append_guardrail_info(request_data["metadata"])
Comment on lines +592 to +593
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silently overwrites existing metadata key

If request_data has neither "metadata" nor "litellm_metadata" but has some other structure that already uses metadata later in the pipeline, this unconditionally sets request_data["metadata"] = {}, which could overwrite a falsy-but-present value (e.g. None, 0, False, ""). The previous code logged a warning, which was safer for diagnosing unexpected states. Consider checking request_data.get("metadata") is None or using setdefault to avoid accidental overwrites:

request_data.setdefault("metadata", {})
_append_guardrail_info(request_data["metadata"])


async def apply_guardrail(
self,
Expand Down
72 changes: 39 additions & 33 deletions litellm/proxy/guardrails/guardrail_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.proxy.guardrails.guardrail_registry import GuardrailRegistry
from litellm.types.guardrails import (PII_ENTITY_CATEGORIES_MAP,
ApplyGuardrailRequest,
ApplyGuardrailResponse,
BaseLitellmParams,
BedrockGuardrailConfigModel, Guardrail,
GuardrailEventHooks,
GuardrailInfoResponse,
GuardrailUIAddGuardrailSettings,
LakeraV2GuardrailConfigModel,
ListGuardrailsResponse, LitellmParams,
PatchGuardrailRequest, PiiAction,
PiiEntityType,
PresidioPresidioConfigModelUserInterface,
SupportedGuardrailIntegrations,
ToolPermissionGuardrailConfigModel)
from litellm.proxy.guardrails.usage_endpoints import router as guardrails_usage_router
from litellm.types.guardrails import (
PII_ENTITY_CATEGORIES_MAP,
ApplyGuardrailRequest,
ApplyGuardrailResponse,
BaseLitellmParams,
BedrockGuardrailConfigModel,
Guardrail,
GuardrailEventHooks,
GuardrailInfoResponse,
GuardrailUIAddGuardrailSettings,
LakeraV2GuardrailConfigModel,
ListGuardrailsResponse,
LitellmParams,
PatchGuardrailRequest,
PiiAction,
PiiEntityType,
PresidioPresidioConfigModelUserInterface,
SupportedGuardrailIntegrations,
ToolPermissionGuardrailConfigModel,
)

#### GUARDRAILS ENDPOINTS ####

Expand Down Expand Up @@ -147,8 +153,7 @@ async def list_guardrails_v2():
```
"""
from litellm.litellm_core_utils.litellm_logging import _get_masked_values
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
Expand Down Expand Up @@ -288,8 +293,7 @@ async def create_guardrail(request: CreateGuardrailRequest):
}
```
"""
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
Expand Down Expand Up @@ -378,8 +382,7 @@ async def update_guardrail(guardrail_id: str, request: UpdateGuardrailRequest):
}
```
"""
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
Expand Down Expand Up @@ -447,8 +450,7 @@ async def delete_guardrail(guardrail_id: str):
}
```
"""
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
Expand Down Expand Up @@ -541,8 +543,7 @@ async def patch_guardrail(guardrail_id: str, request: PatchGuardrailRequest):
}
```
"""
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client

if prisma_client is None:
Expand Down Expand Up @@ -664,8 +665,7 @@ async def get_guardrail_info(guardrail_id: str):
"""

from litellm.litellm_core_utils.litellm_logging import _get_masked_values
from litellm.proxy.guardrails.guardrail_registry import \
IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
from litellm.proxy.proxy_server import prisma_client
from litellm.types.guardrails import GUARDRAIL_DEFINITION_LOCATION

Expand Down Expand Up @@ -740,8 +740,10 @@ async def get_guardrail_ui_settings():
- Content filter settings (patterns and categories)
"""
from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.patterns import (
PATTERN_CATEGORIES, get_available_content_categories,
get_pattern_metadata)
PATTERN_CATEGORIES,
get_available_content_categories,
get_pattern_metadata,
)

# Convert the PII_ENTITY_CATEGORIES_MAP to the format expected by the UI
category_maps = []
Expand Down Expand Up @@ -1313,8 +1315,7 @@ async def get_provider_specific_params():
}

### get the config model for the guardrail - go through the registry and get the config model for the guardrail
from litellm.proxy.guardrails.guardrail_registry import \
guardrail_class_registry
from litellm.proxy.guardrails.guardrail_registry import guardrail_class_registry

for guardrail_name, guardrail_class in guardrail_class_registry.items():
guardrail_config_model = guardrail_class.get_config_model()
Expand Down Expand Up @@ -1442,8 +1443,9 @@ async def test_custom_code_guardrail(request: TestCustomCodeGuardrailRequest):
import concurrent.futures
import re

from litellm.proxy.guardrails.guardrail_hooks.custom_code.primitives import \
get_custom_code_primitives
from litellm.proxy.guardrails.guardrail_hooks.custom_code.primitives import (
get_custom_code_primitives,
)

# Security validation patterns
FORBIDDEN_PATTERNS = [
Expand Down Expand Up @@ -1633,3 +1635,7 @@ async def apply_guardrail(
)
except Exception as e:
raise handle_exception_on_proxy(e)


# Usage (dashboard) endpoints: overview, detail, logs
router.include_router(guardrails_usage_router)
Loading
Loading