mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
Merge pull request #42042 from BerriAI/litellm_websearch_activation_status
feat(ui): report whether the serving proxy has applied web search interception
This commit is contained in:
commit
3df159308d
5 changed files with 100 additions and 2 deletions
|
|
@ -506,6 +506,16 @@ class WebSearchInterceptionSettings(BaseModel):
|
|||
class WebSearchInterceptionSettingsResponse(SettingsResponse):
|
||||
"""Response model for web search interception settings"""
|
||||
|
||||
active_on_this_pod: bool = Field(
|
||||
default=False,
|
||||
description=(
|
||||
"Whether the process answering this request has the interception callback "
|
||||
"registered. Read-only: it reports what is running here, while values.enabled "
|
||||
"is the cluster-wide setting, and the two disagree while a pod is still "
|
||||
"applying a change or failed to apply it."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _with_websearch_enabled_resolved(config: Mapping[str, object]) -> dict[str, object]:
|
||||
"""
|
||||
|
|
@ -1496,11 +1506,22 @@ async def get_websearch_interception_settings(
|
|||
|
||||
config: Final = await proxy_config.get_config()
|
||||
|
||||
return await _get_settings_with_schema(
|
||||
from litellm.integrations.websearch_interception.handler import (
|
||||
WebSearchInterceptionLogger,
|
||||
)
|
||||
|
||||
settings: Final = await _get_settings_with_schema(
|
||||
settings_key="websearch_interception_params",
|
||||
settings_class=WebSearchInterceptionSettings,
|
||||
config=_with_websearch_enabled_resolved(config),
|
||||
)
|
||||
return WebSearchInterceptionSettingsResponse(
|
||||
values=settings["values"],
|
||||
field_schema=settings["field_schema"],
|
||||
active_on_this_pod=bool(
|
||||
litellm.logging_callback_manager.get_custom_loggers_for_type(WebSearchInterceptionLogger)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.patch(
|
||||
|
|
|
|||
|
|
@ -3324,6 +3324,38 @@ class TestWebSearchInterceptionSettingsEndpoints:
|
|||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["values"]["enabled"] is True
|
||||
|
||||
def test_get_flags_a_pod_that_has_not_applied_the_stored_setting(
|
||||
self, mock_proxy_config, mock_auth, monkeypatch
|
||||
):
|
||||
import litellm
|
||||
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", object())
|
||||
monkeypatch.setattr(litellm, "callbacks", [])
|
||||
mock_proxy_config["config"]["litellm_settings"]["websearch_interception_params"] = {"enabled": True}
|
||||
|
||||
resp = client.get("/get/websearch_interception_settings")
|
||||
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["values"]["enabled"] is True
|
||||
assert resp.json()["active_on_this_pod"] is False
|
||||
|
||||
def test_get_reports_the_pod_as_active_once_the_callback_is_registered(
|
||||
self, mock_proxy_config, mock_auth, monkeypatch
|
||||
):
|
||||
import litellm
|
||||
from litellm.integrations.websearch_interception.handler import (
|
||||
WebSearchInterceptionLogger,
|
||||
)
|
||||
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", object())
|
||||
monkeypatch.setattr(litellm, "callbacks", [WebSearchInterceptionLogger(search_tool_name="running")])
|
||||
mock_proxy_config["config"]["litellm_settings"]["websearch_interception_params"] = {"enabled": True}
|
||||
|
||||
resp = client.get("/get/websearch_interception_settings")
|
||||
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["active_on_this_pod"] is True
|
||||
|
||||
def test_get_reports_no_database_instead_of_empty_settings(self, mock_proxy_config, mock_auth, monkeypatch):
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None)
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,32 @@ describe("WebSearchInterceptionSettings", () => {
|
|||
expect(mockMutate.mock.calls[0][0]).toEqual(ENABLED_PAYLOAD);
|
||||
});
|
||||
|
||||
it("warns when the cluster has it on but the serving pod has not applied it", async () => {
|
||||
vi.mocked(useWebSearchInterceptionSettings).mockReturnValue({
|
||||
data: { ...storedSettings, values: { ...storedSettings.values, enabled: true }, active_on_this_pod: false },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
} as any);
|
||||
|
||||
await renderSettings();
|
||||
|
||||
expect(screen.getByText(/has not applied it/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("stays quiet when the serving pod has applied the cluster setting", async () => {
|
||||
vi.mocked(useWebSearchInterceptionSettings).mockReturnValue({
|
||||
data: { ...storedSettings, values: { ...storedSettings.values, enabled: true }, active_on_this_pod: true },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
} as any);
|
||||
|
||||
await renderSettings();
|
||||
|
||||
expect(screen.queryByText(/has not applied it/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("ignores stored values whose types do not match the field", async () => {
|
||||
vi.mocked(useWebSearchInterceptionSettings).mockReturnValue({
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useUpdateWebSearchInterceptionSettings } from "@/app/(dashboard)/hooks/
|
|||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { toast } from "@/lib/toast";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { CircleHelp, Info, Save } from "lucide-react";
|
||||
import { CircleHelp, Info, Save, TriangleAlert } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
|
@ -293,9 +293,22 @@ export default function WebSearchInterceptionSettings() {
|
|||
}
|
||||
|
||||
const values: WebSearchInterceptionStoredValues = toStoredValues(data?.values ?? NO_STORED_VALUES);
|
||||
const notAppliedHere = values.enabled === true && data?.active_on_this_pod === false;
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{notAppliedHere && (
|
||||
<Alert variant="warning" className="mb-6">
|
||||
<TriangleAlert />
|
||||
<AlertTitle>Not running on the proxy that answered this page</AlertTitle>
|
||||
<AlertDescription>
|
||||
Interception is switched on for the cluster, but the proxy serving this page has not applied it. That is
|
||||
expected for about 10 seconds after a change or a restart. If it persists, check that proxy's logs:
|
||||
requests it handles are not being intercepted.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Alert variant="info" className="mb-6">
|
||||
<Info />
|
||||
<AlertTitle>Web Search Interception</AlertTitle>
|
||||
|
|
|
|||
6
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
6
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -41343,6 +41343,12 @@ export interface components {
|
|||
* @description Response model for web search interception settings
|
||||
*/
|
||||
WebSearchInterceptionSettingsResponse: {
|
||||
/**
|
||||
* Active On This Pod
|
||||
* @description Whether the process answering this request has the interception callback registered. Read-only: it reports what is running here, while values.enabled is the cluster-wide setting, and the two disagree while a pod is still applying a change or failed to apply it.
|
||||
* @default false
|
||||
*/
|
||||
active_on_this_pod: boolean;
|
||||
/** Field Schema */
|
||||
field_schema: {
|
||||
[key: string]: unknown;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue