diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index ebc220b3496..bab877096d0 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -2994,22 +2994,6 @@ "rerank": false } }, - "charity_engine": { - "display_name": "Charity Engine (`charity_engine`)", - "url": "https://docs.litellm.ai/docs/providers/charity_engine", - "endpoints": { - "chat_completions": true, - "messages": true, - "responses": true, - "embeddings": false, - "image_generations": false, - "audio_transcriptions": false, - "audio_speech": false, - "moderations": false, - "batches": false, - "rerank": false - } - }, "empiriolabs": { "display_name": "EmpirioLabs (`empiriolabs`)", "url": "https://docs.litellm.ai/docs/providers/empiriolabs", diff --git a/tests/code_coverage_tests/check_endpoint_coverage.py b/tests/code_coverage_tests/check_endpoint_coverage.py index 2d46d1ab469..b0e7c9df3a2 100644 --- a/tests/code_coverage_tests/check_endpoint_coverage.py +++ b/tests/code_coverage_tests/check_endpoint_coverage.py @@ -5,6 +5,7 @@ This script: 1. Extracts all endpoint entries from the "Supported Endpoints" section of sidebars.js 2. Validates that each endpoint has a corresponding entry in the "endpoints" object of provider_endpoints_support.json 3. Checks that the "docs_label" field is present in each endpoint definition +4. Checks that no object in the file declares the same key twice """ import json @@ -20,6 +21,12 @@ class MissingEndpointDefinitionError(Exception): pass +class DuplicateKeyError(Exception): + """Raised when provider_endpoints_support.json declares the same key twice in one object.""" + + pass + + def get_repo_root() -> Path: """Get the repository root directory.""" # Check if litellm directory exists in current working directory @@ -111,6 +118,36 @@ def load_provider_endpoints_file() -> Dict: return json.load(f) +def find_duplicate_keys() -> List[Tuple[str, int]]: + """ + Find keys declared more than once inside the same JSON object. + + json.load() keeps the last of a repeated key and reports no error, so an edit to + the earlier copy is dropped without a trace: the file parses, CI passes, and the + added fields simply are not there. That is why this check re-reads the raw file + with object_pairs_hook instead of inspecting the parsed dict - the parsed dict + has already lost the evidence. + + Returns a list of (key, occurrences). + """ + repo_root = get_repo_root() + file_path = repo_root / "provider_endpoints_support.json" + + duplicates: List[Tuple[str, int]] = [] + + def collect(pairs): + counts: Dict[str, int] = {} + for key, _ in pairs: + counts[key] = counts.get(key, 0) + 1 + duplicates.extend((key, n) for key, n in counts.items() if n > 1) + return dict(pairs) + + with open(file_path, "r") as f: + json.loads(f.read(), object_pairs_hook=collect) + + return duplicates + + def get_defined_endpoints(data: Dict) -> Dict[str, Dict]: """Get all endpoint definitions from provider_endpoints_support.json.""" return data.get("endpoints", {}) @@ -212,6 +249,28 @@ def main(): has_errors = False + # Test 0: Check for duplicate keys before anything reads the parsed file + print("\nšŸ”‘ Test 0: Checking for duplicate keys...") + duplicate_keys = find_duplicate_keys() + + if duplicate_keys: + error_msg = "\nāŒ ERROR: The following keys are declared more than once in the same object:\n" + error_msg += "=" * 70 + "\n" + for key, occurrences in duplicate_keys: + error_msg += f" - {key} ({occurrences} times)\n" + error_msg += "\n" + "=" * 70 + "\n" + error_msg += ( + "\nšŸ’” Only the last copy survives parsing. Anything added to an earlier\n" + ) + error_msg += " copy is silently discarded. Merge the copies into one entry.\n" + print(error_msg) + raise DuplicateKeyError( + f"Duplicate key(s) in provider_endpoints_support.json: " + f"{', '.join(key for key, _ in duplicate_keys)}" + ) + + print("āœ… No duplicate keys found!") + # Load provider_endpoints_support.json data = load_provider_endpoints_file() defined_endpoints = get_defined_endpoints(data)