From 2a0085b260fa7a1eca66a80f1bd5c73cb8360e9f Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 21 Feb 2026 18:25:57 -0800 Subject: [PATCH] fix: add migration.sql for prisma --- .../migration.sql | 21 ++++++ .../litellm_proxy_extras/schema.prisma | 65 +++++-------------- litellm/proxy/schema.prisma | 50 -------------- .../types/proxy/policy_engine/policy_types.py | 47 +------------- schema.prisma | 18 ++++- 5 files changed, 54 insertions(+), 147 deletions(-) create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20260221182522_add_policy_versioning/migration.sql diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260221182522_add_policy_versioning/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260221182522_add_policy_versioning/migration.sql new file mode 100644 index 00000000000..5fd158f2560 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260221182522_add_policy_versioning/migration.sql @@ -0,0 +1,21 @@ +-- DropIndex +DROP INDEX "LiteLLM_PolicyTable_policy_name_key"; + + +-- AlterTable +ALTER TABLE "LiteLLM_PolicyTable" ADD COLUMN "is_latest" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "parent_version_id" TEXT, +ADD COLUMN "production_at" TIMESTAMP(3), +ADD COLUMN "published_at" TIMESTAMP(3), +ADD COLUMN "version_number" INTEGER NOT NULL DEFAULT 1, +ADD COLUMN "version_status" TEXT NOT NULL DEFAULT 'production'; + +-- CreateIndex +CREATE INDEX "LiteLLM_PolicyTable_policy_name_version_status_idx" ON "LiteLLM_PolicyTable"("policy_name", "version_status"); + +-- CreateIndex +CREATE INDEX "LiteLLM_PolicyTable_version_status_idx" ON "LiteLLM_PolicyTable"("version_status"); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_PolicyTable_policy_name_version_number_key" ON "LiteLLM_PolicyTable"("policy_name", "version_number"); + diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index 45cd90f3413..7018b2709e2 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -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 @@ -320,6 +273,7 @@ model LiteLLM_MCPServerTable { alias String? description String? url String? + spec_path String? transport String @default("sse") auth_type String? credentials Json? @default("{}") @@ -1009,20 +963,33 @@ model LiteLLM_SkillsTable { updated_by String? } -// Policy table for storing guardrail policies +// Policy table for storing guardrail policies with versioning support model LiteLLM_PolicyTable { policy_id String @id @default(uuid()) - policy_name String @unique + policy_name String // Policy name (multiple versions can share same name) inherit String? // Name of parent policy to inherit from description String? guardrails_add String[] @default([]) guardrails_remove String[] @default([]) condition Json? @default("{}") // Policy conditions (e.g., model matching) pipeline Json? // Optional guardrail pipeline (mode + steps[]) + + // Versioning fields + version_number Int @default(1) + version_status String @default("production") // "draft", "published", or "production" + parent_version_id String? // Reference to the policy version this was created from + is_latest Boolean @default(true) + published_at DateTime? // When this version was published + production_at DateTime? // When this version was promoted to production + created_at DateTime @default(now()) created_by String? updated_at DateTime @default(now()) @updatedAt updated_by String? + + @@unique([policy_name, version_number]) + @@index([policy_name, version_status]) + @@index([version_status]) } // Policy attachment table for defining where policies apply diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 57f02afe4ae..7018b2709e2 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -987,9 +987,6 @@ model LiteLLM_PolicyTable { updated_at DateTime @default(now()) @updatedAt updated_by String? - // Relations - mirroring_configs LiteLLM_PolicyMirroringTable[] - @@unique([policy_name, version_number]) @@index([policy_name, version_status]) @@index([version_status]) @@ -1010,53 +1007,6 @@ model LiteLLM_PolicyAttachmentTable { updated_by String? } -// Policy mirroring configuration for silent/shadow testing -model LiteLLM_PolicyMirroringTable { - mirroring_id String @id @default(uuid()) - policy_id String // References LiteLLM_PolicyTable.policy_id - traffic_percentage Float @default(0.0) // 0.0-100.0 - max_requests Int? // Optional limit on total mirrored requests - current_requests Int @default(0) - enabled Boolean @default(true) - fail_silently Boolean @default(true) // Don't block requests if mirroring fails - log_results Boolean @default(true) // Store results in mirror logs - started_at DateTime @default(now()) - expires_at DateTime? // Optional expiration time for mirroring - created_at DateTime @default(now()) - created_by String? - updated_at DateTime @default(now()) @updatedAt - updated_by String? - - // Relations - policy LiteLLM_PolicyTable @relation(fields: [policy_id], references: [policy_id]) - mirror_logs LiteLLM_PolicyMirrorLogs[] - - @@index([policy_id, enabled]) - @@index([expires_at]) -} - -// Policy mirroring execution logs -model LiteLLM_PolicyMirrorLogs { - log_id String @id @default(uuid()) - mirroring_id String // References LiteLLM_PolicyMirroringTable.mirroring_id - request_id String? // Original request ID if available - executed_at DateTime @default(now()) - execution_time_ms Float? // How long the mirrored policy execution took - result String // "pass", "fail", "error" - terminal_action String? // Final action taken by policy (allow/block/modify) - step_results Json? // Detailed step-by-step results - error_message String? // Error details if result = "error" - metadata Json? @default("{}") - created_at DateTime @default(now()) - - // Relations - mirroring_config LiteLLM_PolicyMirroringTable @relation(fields: [mirroring_id], references: [mirroring_id]) - - @@index([mirroring_id, executed_at]) - @@index([result]) - @@index([executed_at]) -} - //Unified Access Groups table for storing unified access groups model LiteLLM_AccessGroupTable { access_group_id String @id @default(uuid()) diff --git a/litellm/types/proxy/policy_engine/policy_types.py b/litellm/types/proxy/policy_engine/policy_types.py index 542fc1dad38..fb73106ea70 100644 --- a/litellm/types/proxy/policy_engine/policy_types.py +++ b/litellm/types/proxy/policy_engine/policy_types.py @@ -48,7 +48,7 @@ class PolicyVersionStatus(str, Enum): Status of a policy version in the version workflow. - DRAFT: Policy is being edited, not yet published - - PUBLISHED: Policy is ready for testing/mirroring + - PUBLISHED: Policy is ready for testing - PRODUCTION: Policy is active and applied to matching requests """ @@ -57,46 +57,6 @@ class PolicyVersionStatus(str, Enum): PRODUCTION = "production" -class SilentMirroringConfig(BaseModel): - """ - Configuration for silent/shadow testing of policy versions. - - Silent mirroring executes a policy version on a percentage of traffic - without blocking requests, allowing safe testing before promotion. - - Example usage: - - Test new policy on 10% of traffic - - Limit to 1000 test executions - - Log all results for analysis - - Automatically expire after testing period - """ - - traffic_percentage: float = Field( - default=0.0, - ge=0.0, - le=100.0, - description="Percentage of traffic to mirror (0.0-100.0)", - ) - max_requests: Optional[int] = Field( - default=None, - description="Maximum number of requests to mirror (optional limit)", - ) - fail_silently: bool = Field( - default=True, - description="Continue request processing if mirroring fails", - ) - log_results: bool = Field( - default=True, - description="Store execution results in mirror logs", - ) - expires_at: Optional[datetime] = Field( - default=None, - description="Optional expiration time for mirroring", - ) - - model_config = ConfigDict(extra="forbid") - - # ───────────────────────────────────────────────────────────────────────────── # Policy Condition # ───────────────────────────────────────────────────────────────────────────── @@ -248,7 +208,6 @@ class Policy(BaseModel): - Policies support version workflow: draft → published → production - Multiple versions of the same policy can coexist - Only production versions are applied to requests by default - - Published versions can be tested via silent mirroring Example configuration: ```yaml @@ -331,10 +290,6 @@ class Policy(BaseModel): default=None, description="When this version was promoted to production", ) - silent_mirroring: Optional[SilentMirroringConfig] = Field( - default=None, - description="Silent mirroring configuration (for testing)", - ) model_config = ConfigDict(extra="forbid") diff --git a/schema.prisma b/schema.prisma index d483e92e528..7018b2709e2 100644 --- a/schema.prisma +++ b/schema.prisma @@ -273,6 +273,7 @@ model LiteLLM_MCPServerTable { alias String? description String? url String? + spec_path String? transport String @default("sse") auth_type String? credentials Json? @default("{}") @@ -962,20 +963,33 @@ model LiteLLM_SkillsTable { updated_by String? } -// Policy table for storing guardrail policies +// Policy table for storing guardrail policies with versioning support model LiteLLM_PolicyTable { policy_id String @id @default(uuid()) - policy_name String @unique + policy_name String // Policy name (multiple versions can share same name) inherit String? // Name of parent policy to inherit from description String? guardrails_add String[] @default([]) guardrails_remove String[] @default([]) condition Json? @default("{}") // Policy conditions (e.g., model matching) pipeline Json? // Optional guardrail pipeline (mode + steps[]) + + // Versioning fields + version_number Int @default(1) + version_status String @default("production") // "draft", "published", or "production" + parent_version_id String? // Reference to the policy version this was created from + is_latest Boolean @default(true) + published_at DateTime? // When this version was published + production_at DateTime? // When this version was promoted to production + created_at DateTime @default(now()) created_by String? updated_at DateTime @default(now()) @updatedAt updated_by String? + + @@unique([policy_name, version_number]) + @@index([policy_name, version_status]) + @@index([version_status]) } // Policy attachment table for defining where policies apply