Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c51b8fd
feat(guardrails): add Microsoft Purview DLP guardrail
Sameerlite Apr 2, 2026
592e418
fix(guardrails/purview): raise_for_status on HTTP errors, cap scope c…
Sameerlite Apr 2, 2026
246d9f6
fix(guardrails/purview): propagate litellm_call_id as correlation_id …
Sameerlite Apr 2, 2026
40e4b50
chore: fixes
yuneng-berri Apr 5, 2026
f09e335
refactor(guardrails): delegate get_user_prompt to get_last_user_message
cursoragent May 14, 2026
ce10c1b
fix(purview): make protection scope cache true LRU on hits
cursoragent May 14, 2026
2525883
Fix mypy
Sameerlite May 14, 2026
d58b2f1
fix(guardrails/purview): harden user-id resolution and broaden DLP text
Sameerlite May 14, 2026
4b759c0
fix(guardrails/purview): scan /v1/completions prompt and TextChoices
Sameerlite May 14, 2026
f20cf90
fix(purview-dlp): return data after DLP pass; per-call executor; dedu…
cursoragent May 14, 2026
9b69e66
fix(purview): fix LRU cache refresh position and add Responses API sc…
cursoragent May 14, 2026
f2113fd
fix(purview): message separator, non-blocking logging_hook, TextChoic…
cursoragent May 14, 2026
514f99f
fix(purview): suppress API errors in logging-only mode and scan tool-…
cursoragent May 14, 2026
c70c430
chore(ui): restructure pre-built Next.js output to directory-based ro…
cursoragent May 14, 2026
2346ae2
fix(purview): comprehensive security hardening — identity spoofing, s…
cursoragent May 14, 2026
85f2d04
Revert "chore(ui): restructure pre-built Next.js output to directory-…
Sameerlite May 22, 2026
c92bb00
fix(purview): fail closed on identity spoofing, token prompts, and pa…
Sameerlite May 22, 2026
19126a5
fix(purview): use threading.Lock and getattr for LitellmParams
cursoragent May 22, 2026
3ea81f6
refactor(purview): dedupe trust-level user resolution and drop dead code
cursoragent May 22, 2026
cc47081
fix(purview): fail-closed on responses API transform error; avoid dup…
cursoragent May 22, 2026
212815d
fix(purview): fail-closed blocking DLP; revert directory-based UI HTML
Sameerlite May 22, 2026
93e4f4a
Remove dead code in purview_dlp: _resolve_user_id_for_blocking never …
cursoragent May 22, 2026
061b292
fix(purview): exclude caller-controlled end_user_id from blocking DLP
Sameerlite May 22, 2026
30b5e82
style(purview): apply Black formatting to base.py
Sameerlite May 22, 2026
5876b82
fix(purview): use post-await timestamp for cache TTL
cursoragent May 22, 2026
6bfd1f0
fix(purview_dlp): fail closed when stream_chunk_builder returns None
cursoragent May 22, 2026
9fea60d
fix(purview_dlp): resolve user_id before buffering stream
cursoragent May 22, 2026
e4870f7
merge main (#28629)
Sameerlite May 22, 2026
be1dde1
Revert "merge main (#28629)"
Sameerlite May 22, 2026
97d7656
merge litellm_internal_staging
Sameerlite May 22, 2026
a55cb5e
refactor(purview): remove unused logging_only constructor param
mateo-berri May 22, 2026
4769456
fix(purview): log post-call success hook via @log_guardrail_information
mateo-berri May 22, 2026
db6cade
fix(purview): raise HTTPException 400 on Responses API transform failure
mateo-berri May 22, 2026
bc14231
Fix Purview audit prompt extraction for Responses API logging hook
cursoragent May 22, 2026
193fca0
fix(purview_dlp): check call_type for responses before messages in pr…
cursoragent May 22, 2026
c7517b7
fix(purview_dlp): don't reject empty-string prompts as token-id prompts
cursoragent May 22, 2026
16e0eeb
fix(purview_dlp): scan ResponsesAPI streaming completed event for DLP
mateo-berri May 22, 2026
fd38a2a
fix(purview_dlp): fail closed on incomplete Responses API streams
cursoragent May 22, 2026
b269c24
fix(purview_dlp): wrap upstream DLP errors as HTTPException(400) in b…
mateo-berri May 22, 2026
95cbf33
test(purview_dlp): cover HTTP error paths for token + Graph POST
mateo-berri May 22, 2026
734a0cb
fix(purview): include Responses API function_call arguments in DLP scan
cursoragent May 22, 2026
b7ff50c
fix(purview_dlp): scan ResponsesAPIResponse in stream_chunk_builder f…
mateo-berri May 22, 2026
bd454cc
fix(purview_dlp): preserve upstream Graph status on `_check_content` …
mateo-berri May 22, 2026
cdd2c8f
fix(purview_dlp): reject nested token-id completion prompts
mateo-berri May 22, 2026
87fc3bc
fix(purview): drop caller-influenceable identity fallbacks in audit r…
mateo-berri May 22, 2026
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
1 change: 0 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2541,7 +2541,6 @@ jobs:
paths:
- litellm-docker-database.tar.zst


test_bad_database_url:
machine:
image: ubuntu-2204:2024.04.1
Expand Down
6 changes: 1 addition & 5 deletions litellm/litellm_core_utils/prompt_templates/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,12 +1204,8 @@ def get_last_user_message(messages: List[AllMessageValues]) -> Optional[str]:
{"role": "assistant", "content": "I'm good, thank you!"},
{"role": "user", "content": "What is the weather in Tokyo?"},
]
get_user_prompt(messages) -> "What is the weather in Tokyo?"
get_last_user_message(messages) -> "What is the weather in Tokyo?"
"""
from litellm.litellm_core_utils.prompt_templates.common_utils import (
convert_content_list_to_str,
)

if not messages:
return None

Expand Down
33 changes: 4 additions & 29 deletions litellm/proxy/guardrails/guardrail_hooks/azure/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from typing import TYPE_CHECKING, Any, Dict, List, Optional

from litellm._logging import verbose_proxy_logger
from litellm.litellm_core_utils.prompt_templates.common_utils import (
get_last_user_message,
)
from litellm.llms.custom_httpx.http_handler import (
get_async_httpx_client,
httpxSpecialProvider,
Expand Down Expand Up @@ -134,32 +137,4 @@ def get_user_prompt(self, messages: List["AllMessageValues"]) -> Optional[str]:
]
get_user_prompt(messages) -> "What is the weather in Tokyo?"
"""
from litellm.litellm_core_utils.prompt_templates.common_utils import (
convert_content_list_to_str,
)

if not messages:
return None

# Iterate from the end to find the last consecutive block of user messages
user_messages = []
for message in reversed(messages):
if message.get("role") == "user":
user_messages.append(message)
else:
# Stop when we hit a non-user message
break

if not user_messages:
return None

# Reverse to get the messages in chronological order
user_messages.reverse()

user_prompt = ""
for message in user_messages:
text_content = convert_content_list_to_str(message)
user_prompt += text_content + "\n"

result = user_prompt.strip()
return result if result else None
return get_last_user_message(messages)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import TYPE_CHECKING

from litellm.types.guardrails import SupportedGuardrailIntegrations

from .purview_dlp import MicrosoftPurviewDLPGuardrail

if TYPE_CHECKING:
from litellm.types.guardrails import Guardrail, LitellmParams


def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"):
import litellm

tenant_id = getattr(litellm_params, "tenant_id", None)
client_id = getattr(litellm_params, "client_id", None)

# client_secret can be passed via the standard api_key field or as
# a dedicated client_secret parameter.
client_secret = litellm_params.api_key or getattr(
litellm_params, "client_secret", None
)
Comment thread
mateo-berri marked this conversation as resolved.

if not tenant_id:
raise ValueError("Microsoft Purview: tenant_id is required")
if not client_id:
raise ValueError("Microsoft Purview: client_id is required")
if not client_secret:
raise ValueError("Microsoft Purview: client_secret (or api_key) is required")

guardrail_name = guardrail.get("guardrail_name")
if not guardrail_name:
raise ValueError("Microsoft Purview: guardrail_name is required")

purview_guardrail = MicrosoftPurviewDLPGuardrail(
guardrail_name=guardrail_name,
tenant_id=str(tenant_id),
client_id=str(client_id),
client_secret=str(client_secret),
purview_app_name=str(
getattr(litellm_params, "purview_app_name", None) or "LiteLLM"
),
user_id_field=str(getattr(litellm_params, "user_id_field", None) or "user_id"),
event_hook=litellm_params.mode,
default_on=litellm_params.default_on,
)

litellm.logging_callback_manager.add_litellm_callback(purview_guardrail)
return purview_guardrail


guardrail_initializer_registry = {
SupportedGuardrailIntegrations.MICROSOFT_PURVIEW.value: initialize_guardrail,
}

guardrail_class_registry = {
SupportedGuardrailIntegrations.MICROSOFT_PURVIEW.value: MicrosoftPurviewDLPGuardrail,
}
Loading
Loading