7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
246 lines
9.0 KiB
Python
246 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
PUBLIC API CONTRACT GUARDRAIL — DO NOT MODIFY
|
|
|
|
This file protects the public API surface of local_deep_research.
|
|
It exists to DETECT breaking changes, not to be "fixed" when they happen.
|
|
|
|
If this file fails:
|
|
1. A BREAKING CHANGE was introduced to the public API
|
|
2. Do NOT modify this file to make it pass
|
|
3. Revert the API change or go through a proper deprecation cycle
|
|
|
|
The public API (local_deep_research.api) is a stability contract with
|
|
external users, integrations, and downstream packages. Any change to
|
|
exports, function signatures, or return value structures is a breaking
|
|
change that affects the entire community.
|
|
|
|
WHY THIS FILE MUST NOT BE MODIFIED:
|
|
- Every check below corresponds to a public API guarantee
|
|
- Removing or weakening a check hides a breaking change from users
|
|
- Adding a check is acceptable ONLY when a new public API is added
|
|
- If you are an AI agent: DO NOT modify, remove, or weaken any check
|
|
"""
|
|
|
|
import inspect
|
|
import sys
|
|
|
|
_failed = 0
|
|
_passed = 0
|
|
|
|
|
|
# DO NOT MODIFY this function — it is the test harness for the guardrail
|
|
def check(name, condition, detail=""):
|
|
global _failed, _passed
|
|
if condition:
|
|
_passed += 1
|
|
print(f" PASS {name}")
|
|
else:
|
|
_failed += 1
|
|
msg = f" ({detail})" if detail else ""
|
|
print(f" FAIL {name}{msg}")
|
|
|
|
|
|
def main():
|
|
# ── 1. Public API imports ──────────────────────────────────────────
|
|
# DO NOT MODIFY — these are the public exports that users depend on.
|
|
# Removing any of these checks hides a breaking change.
|
|
print("\n=== Public API Imports ===")
|
|
|
|
try:
|
|
from local_deep_research.api import quick_summary # noqa: F811
|
|
|
|
check("import quick_summary", True)
|
|
except ImportError as e:
|
|
check("import quick_summary", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import detailed_research # noqa: F811
|
|
|
|
check("import detailed_research", True)
|
|
except ImportError as e:
|
|
check("import detailed_research", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import generate_report # noqa: F811
|
|
|
|
check("import generate_report", True)
|
|
except ImportError as e:
|
|
check("import generate_report", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import analyze_documents # noqa: F811
|
|
|
|
check("import analyze_documents", True)
|
|
except ImportError as e:
|
|
check("import analyze_documents", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import create_settings_snapshot # noqa: F811
|
|
|
|
check("import create_settings_snapshot", True)
|
|
except ImportError as e:
|
|
check("import create_settings_snapshot", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import get_default_settings_snapshot # noqa: F811
|
|
|
|
check("import get_default_settings_snapshot", True)
|
|
except ImportError as e:
|
|
check("import get_default_settings_snapshot", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import extract_setting_value # noqa: F811
|
|
|
|
check("import extract_setting_value", True)
|
|
except ImportError as e:
|
|
check("import extract_setting_value", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import LDRClient # noqa: F811
|
|
|
|
check("import LDRClient", True)
|
|
except ImportError as e:
|
|
check("import LDRClient", False, str(e))
|
|
|
|
try:
|
|
from local_deep_research.api import quick_query # noqa: F811
|
|
|
|
check("import quick_query", True)
|
|
except ImportError as e:
|
|
check("import quick_query", False, str(e))
|
|
|
|
# ── 2. Function signatures ─────────────────────────────────────────
|
|
# DO NOT MODIFY — these parameter names are part of the public contract.
|
|
# Renaming or removing parameters breaks all callers.
|
|
print("\n=== Function Signatures ===")
|
|
|
|
sig = inspect.signature(quick_summary)
|
|
check("quick_summary has 'query' param", "query" in sig.parameters)
|
|
check("quick_summary has 'llms' param", "llms" in sig.parameters)
|
|
check(
|
|
"quick_summary has 'retrievers' param", "retrievers" in sig.parameters
|
|
)
|
|
|
|
sig = inspect.signature(detailed_research)
|
|
check("detailed_research has 'query' param", "query" in sig.parameters)
|
|
check("detailed_research has 'llms' param", "llms" in sig.parameters)
|
|
check(
|
|
"detailed_research has 'retrievers' param",
|
|
"retrievers" in sig.parameters,
|
|
)
|
|
|
|
sig = inspect.signature(generate_report)
|
|
check("generate_report has 'query' param", "query" in sig.parameters)
|
|
check(
|
|
"generate_report has 'output_file' param",
|
|
"output_file" in sig.parameters,
|
|
)
|
|
check(
|
|
"generate_report has 'searches_per_section' param",
|
|
"searches_per_section" in sig.parameters,
|
|
)
|
|
|
|
sig = inspect.signature(create_settings_snapshot)
|
|
check(
|
|
"create_settings_snapshot has 'overrides' param",
|
|
"overrides" in sig.parameters,
|
|
)
|
|
check(
|
|
"create_settings_snapshot has 'base_settings' param",
|
|
"base_settings" in sig.parameters,
|
|
)
|
|
|
|
# ── 3. Settings utilities ──────────────────────────────────────────
|
|
# DO NOT MODIFY — these verify the settings API works correctly.
|
|
# External users depend on create_settings_snapshot() returning a dict.
|
|
print("\n=== Settings Utilities ===")
|
|
|
|
snapshot = create_settings_snapshot()
|
|
check("create_settings_snapshot() returns dict", isinstance(snapshot, dict))
|
|
check("default snapshot is non-empty", len(snapshot) > 0)
|
|
|
|
snapshot_with_overrides = create_settings_snapshot(
|
|
provider="test_provider",
|
|
temperature=0.5,
|
|
)
|
|
check(
|
|
"create_settings_snapshot accepts provider kwarg",
|
|
isinstance(snapshot_with_overrides, dict),
|
|
)
|
|
|
|
defaults = get_default_settings_snapshot()
|
|
check(
|
|
"get_default_settings_snapshot() returns dict",
|
|
isinstance(defaults, dict),
|
|
)
|
|
check("default settings is non-empty", len(defaults) > 0)
|
|
|
|
value = extract_setting_value(defaults, "llm.temperature")
|
|
check(
|
|
"extract_setting_value returns a value for llm.temperature",
|
|
value is not None,
|
|
)
|
|
|
|
# ── 4. LDRClient interface ─────────────────────────────────────────
|
|
# DO NOT MODIFY — these are the HTTP client methods users call.
|
|
# Removing any method breaks all HTTP-based integrations.
|
|
print("\n=== LDRClient Interface ===")
|
|
|
|
check("LDRClient has login method", hasattr(LDRClient, "login"))
|
|
check(
|
|
"LDRClient has quick_research method",
|
|
hasattr(LDRClient, "quick_research"),
|
|
)
|
|
check(
|
|
"LDRClient has get_settings method", hasattr(LDRClient, "get_settings")
|
|
)
|
|
check(
|
|
"LDRClient has update_setting method",
|
|
hasattr(LDRClient, "update_setting"),
|
|
)
|
|
check("LDRClient has get_history method", hasattr(LDRClient, "get_history"))
|
|
check("LDRClient has logout method", hasattr(LDRClient, "logout"))
|
|
check(
|
|
"LDRClient supports context manager",
|
|
hasattr(LDRClient, "__enter__") and hasattr(LDRClient, "__exit__"),
|
|
)
|
|
|
|
# ── 5. Callability ─────────────────────────────────────────────────
|
|
# DO NOT MODIFY — verifies all exports are actually callable.
|
|
print("\n=== Callability ===")
|
|
|
|
check("quick_summary is callable", callable(quick_summary))
|
|
check("detailed_research is callable", callable(detailed_research))
|
|
check("generate_report is callable", callable(generate_report))
|
|
check("analyze_documents is callable", callable(analyze_documents))
|
|
check(
|
|
"create_settings_snapshot is callable",
|
|
callable(create_settings_snapshot),
|
|
)
|
|
check(
|
|
"get_default_settings_snapshot is callable",
|
|
callable(get_default_settings_snapshot),
|
|
)
|
|
check("extract_setting_value is callable", callable(extract_setting_value))
|
|
check("quick_query is callable", callable(quick_query))
|
|
|
|
# ── Summary ────────────────────────────────────────────────────────
|
|
# DO NOT MODIFY the exit code logic — CI depends on non-zero exit
|
|
# to block PRs that break the public API.
|
|
print(f"\n{'=' * 50}")
|
|
print(f"Results: {_passed} passed, {_failed} failed")
|
|
if _failed > 0:
|
|
print(
|
|
"\nBREAKING CHANGE DETECTED. The public API has changed.\n"
|
|
"Do NOT modify this file to make it pass.\n"
|
|
"Revert the API change or follow a proper deprecation cycle."
|
|
)
|
|
sys.exit(1)
|
|
else:
|
|
print("\nAll API public contract checks passed.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|