diff --git a/.github/workflows/test-code-quality.yml b/.github/workflows/test-code-quality.yml index 987f66773f2..a49fcf0cfcb 100644 --- a/.github/workflows/test-code-quality.yml +++ b/.github/workflows/test-code-quality.yml @@ -65,6 +65,9 @@ jobs: - name: check_licenses run: uv run --no-sync python ./tests/code_coverage_tests/check_licenses.py + - name: check_json_duplicate_keys + run: uv run --no-sync python ./tests/code_coverage_tests/check_json_duplicate_keys.py + - name: check_provider_folders_documented run: uv run --no-sync python ./tests/code_coverage_tests/check_provider_folders_documented.py diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index b8d1621cde3..fa46132c45a 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -3020,22 +3020,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_json_duplicate_keys.py b/tests/code_coverage_tests/check_json_duplicate_keys.py new file mode 100644 index 00000000000..96969c5e1ab --- /dev/null +++ b/tests/code_coverage_tests/check_json_duplicate_keys.py @@ -0,0 +1,98 @@ +""" +Fail if a hand-maintained JSON config file declares the same key twice inside one object. + +Why this needs its own check: + + json.load() keeps the LAST of a repeated key and reports no error. So when two + copies of the same object exist and they are not identical, the earlier copy is + dropped without a trace - the file parses, CI passes, and whatever fields only + the earlier copy had are simply not there. Nobody is told. + + Every existing check reads the PARSED file, and the duplicate is already gone by + then. This check re-reads the raw text with object_pairs_hook, which is the only + place the evidence still exists. + +Add a file to FILES_TO_CHECK when it is edited by hand and read by code at runtime. +Generated files and lockfiles do not belong here. +""" + +import json +import sys +from pathlib import Path +from typing import Dict, List, Tuple + +# Repo-root-relative paths of hand-maintained JSON that code reads at runtime. +FILES_TO_CHECK = [ + "provider_endpoints_support.json", +] + + +def get_repo_root() -> Path: + return Path(__file__).parent.parent.parent + + +def find_duplicate_keys(file_path: Path) -> List[Tuple[str, int]]: + """Return [(key, occurrences), ...] for keys repeated inside the same object.""" + 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 main() -> None: + repo_root = get_repo_root() + failures: List[str] = [] + checked = 0 + + print("šŸ”‘ Checking hand-maintained JSON config for duplicate keys...\n") + + for rel_path in FILES_TO_CHECK: + file_path = repo_root / rel_path + + if not file_path.exists(): + # A path that no longer exists means this check is silently protecting + # nothing, so say so instead of passing. + failures.append(f"{rel_path}: file not found at {file_path}") + continue + + checked += 1 + duplicates = find_duplicate_keys(file_path) + + if duplicates: + for key, occurrences in duplicates: + failures.append( + f"{rel_path}: '{key}' is declared {occurrences} times inside the " + "same object; only the last one survives json.load()" + ) + else: + print(f" āœ… {rel_path}") + + if not checked: + # Scanning zero files must never look like success. + print("\nāŒ No files were checked - FILES_TO_CHECK is empty or all paths are stale") + sys.exit(1) + + if failures: + print("\nāŒ Duplicate keys found:\n") + for line in failures: + print(f" - {line}") + print( + "\nMerge the duplicate objects into one, keeping every field that appears " + "in either copy." + ) + sys.exit(1) + + print(f"\nāœ… {checked} file(s) checked, no duplicate keys found") + + +if __name__ == "__main__": + main()