fix: merge the duplicate charity_engine entry, and catch the next one

`provider_endpoints_support.json` declared `charity_engine` twice. `json.load()`
keeps the last of a repeated key and reports nothing, so the first copy was
silently discarded on every read.

The two copies were not identical. The discarded one carried two endpoint flags
the surviving one did not:

    a2a: false
    interactions: false

So the effect was not a harmless duplicate — an edit that added those two fields
never took effect, and nothing failed to say so. Removing the later copy restores
them (10 endpoint keys -> 12) and leaves every other provider byte-identical.

Added Test 0 to check_endpoint_coverage.py to stop this recurring. It re-reads the
raw file with `object_pairs_hook` rather than inspecting the parsed dict, because
by the time the dict exists the evidence is already gone — which is why the
existing checks, all of which read the parsed dict, could not see this.

Verified the check fails when it should: restoring the duplicate makes Test 0 name
`charity_engine` and raise; removing it again passes.
This commit is contained in:
Claude 2026-09-05 17:12:18 +00:00
parent 658f50663d
commit 215ced2bdb
No known key found for this signature in database
2 changed files with 59 additions and 16 deletions

View file

@ -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",

View file

@ -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)