From d9a0ea2a6ee505ac4a7f8f65e76334e6d700a7e8 Mon Sep 17 00:00:00 2001 From: Yinon Kahta Date: Thu, 27 Aug 2026 13:05:58 +0300 Subject: [PATCH] feat(pointfive): add pointfive to the dashboard logging integrations --- litellm/integrations/callback_configs.json | 21 ++++++++ .../proxy/test_pointfive_dashboard_config.py | 47 ++++++++++++++++++ .../public/assets/logos/pointfive.png | Bin 0 -> 5284 bytes .../src/components/callback_info_helpers.tsx | 12 +++++ 4 files changed, 80 insertions(+) create mode 100644 tests/test_litellm/proxy/test_pointfive_dashboard_config.py create mode 100644 ui/litellm-dashboard/public/assets/logos/pointfive.png diff --git a/litellm/integrations/callback_configs.json b/litellm/integrations/callback_configs.json index c40b90cee25..85bfcc6e7ed 100644 --- a/litellm/integrations/callback_configs.json +++ b/litellm/integrations/callback_configs.json @@ -378,6 +378,27 @@ }, "description": "OpenTelemetry Logging Integration" }, + { + "id": "pointfive", + "displayName": "PointFive", + "logo": "pointfive.png", + "supports_key_team_logging": false, + "dynamic_params": { + "POINTFIVE_API_KEY": { + "type": "password", + "ui_name": "API Key", + "description": "PointFive API key, used to request an upload url for each batch of logs", + "required": true + }, + "POINTFIVE_API_URL": { + "type": "text", + "ui_name": "API URL", + "description": "PointFive API endpoint. Leave blank to use https://api.pointfive.co/api/v1/ingestion", + "required": false + } + }, + "description": "PointFive Logging Integration" + }, { "id": "s3", "displayName": "S3", diff --git a/tests/test_litellm/proxy/test_pointfive_dashboard_config.py b/tests/test_litellm/proxy/test_pointfive_dashboard_config.py new file mode 100644 index 00000000000..9e46e7f0b3e --- /dev/null +++ b/tests/test_litellm/proxy/test_pointfive_dashboard_config.py @@ -0,0 +1,47 @@ +import json +from pathlib import Path + +import litellm +from litellm.integrations.custom_logger import CustomLogger + + +def _dashboard_configs() -> tuple[dict, ...]: + path = Path(litellm.__file__).parent / "integrations" / "callback_configs.json" + return tuple(json.loads(path.read_text())) + + +def _pointfive_config() -> dict: + return next(config for config in _dashboard_configs() if config["id"] == "pointfive") + + +def test_pointfive_appears_in_the_dashboard_callback_dropdown(): + """The dropdown is served from callback_configs.json, so an entry only in the dashboard source is invisible.""" + entry = _pointfive_config() + + assert entry["displayName"] == "PointFive" + assert entry["supports_key_team_logging"] is False + assert entry["dynamic_params"]["POINTFIVE_API_KEY"]["required"] is True + assert entry["dynamic_params"]["POINTFIVE_API_KEY"]["type"] == "password" + assert entry["dynamic_params"]["POINTFIVE_API_URL"]["required"] is False + + +def test_the_dropdown_logo_asset_exists(): + """A logo the dashboard cannot resolve degrades silently to a letter tile.""" + logo = _pointfive_config()["logo"] + repo_root = Path(litellm.__file__).parent.parent + asset = repo_root / "ui" / "litellm-dashboard" / "public" / "assets" / "logos" / logo + + assert logo == "pointfive.png" + assert asset.is_file() + + +def test_the_dropdown_fields_are_the_env_vars_the_logger_reads(): + """ + The field names are the environment variables verbatim. + + The proxy would uppercase them either way, but naming them as stored means the edit + form finds the saved values and prefills them instead of showing blanks. + """ + fields = tuple(_pointfive_config()["dynamic_params"]) + + assert fields == tuple(CustomLogger.get_callback_env_vars("pointfive")) diff --git a/ui/litellm-dashboard/public/assets/logos/pointfive.png b/ui/litellm-dashboard/public/assets/logos/pointfive.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7a6b8939ee49b967a8cd6cb89b3dd23f212cef GIT binary patch literal 5284 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 literal 0 HcmV?d00001 diff --git a/ui/litellm-dashboard/src/components/callback_info_helpers.tsx b/ui/litellm-dashboard/src/components/callback_info_helpers.tsx index dfa35de1b34..8e343e1a6e0 100644 --- a/ui/litellm-dashboard/src/components/callback_info_helpers.tsx +++ b/ui/litellm-dashboard/src/components/callback_info_helpers.tsx @@ -9,6 +9,7 @@ import langsmithLogo from "../../public/assets/logos/langsmith.png"; import newrelicLogo from "../../public/assets/logos/newrelic.png"; import openmeterLogo from "../../public/assets/logos/openmeter.png"; import otelLogo from "../../public/assets/logos/otel.png"; +import pointfiveLogo from "../../public/assets/logos/pointfive.png"; interface CallbackConfig { id: string; @@ -162,6 +163,17 @@ export const CALLBACK_CONFIGS: CallbackConfig[] = [ }, description: "OpenTelemetry Logging Integration", }, + { + id: "pointfive", + displayName: "PointFive", + logo: pointfiveLogo.src, + supports_key_team_logging: false, + dynamic_params: { + POINTFIVE_API_KEY: "password", + POINTFIVE_API_URL: "text", + }, + description: "PointFive Logging Integration", + }, { id: "s3", displayName: "S3",