Skip to content

codenib.web.local

Prepare a local repository for the CodeNib Wiki runtime.

Functions:

Name Description
prepare_local_wiki

Write the registry and config consumed by the existing Wiki service.

prepare_local_wiki

prepare_local_wiki(
    repo_path: Path,
    manifest_path: Path,
    *,
    frontend_port: int,
    repository_slug: str | None = None,
    agent_wiki: bool = False,
    model: str | None = None,
    api_base: str | None = None,
    api_key_env: str | None = None,
    embedding_api_key_env: str | None = None,
    model_options: Mapping[str, Any] | None = None
) -> LocalWiki

Write the registry and config consumed by the existing Wiki service.

Source code in codenib/web/local.py
def prepare_local_wiki(
    repo_path: Path,
    manifest_path: Path,
    *,
    frontend_port: int,
    repository_slug: str | None = None,
    agent_wiki: bool = False,
    model: str | None = None,
    api_base: str | None = None,
    api_key_env: str | None = None,
    embedding_api_key_env: str | None = None,
    model_options: Mapping[str, Any] | None = None,
) -> LocalWiki:
    """Write the registry and config consumed by the existing Wiki service."""
    repo_path = lexical_repository_path(repo_path)
    manifest_path = Path(os.path.abspath(os.fspath(manifest_path.expanduser())))
    if repository_slug is not None:
        if (
            not isinstance(repository_slug, str)
            or not repository_slug
            or "\x00" in repository_slug
        ):
            raise ValueError("explicit repository slug must be bounded non-empty text")
        try:
            slug_bytes = repository_slug.encode("utf-8", errors="strict")
        except UnicodeEncodeError as exc:
            raise ValueError(
                "explicit repository slug must be valid UTF-8 text"
            ) from exc
        if len(slug_bytes) > 4_096:
            raise ValueError("explicit repository slug must be bounded non-empty text")
    manifest = RepoManifest.load(str(manifest_path))
    if not is_secure_source_fingerprint_v2(manifest.source_fingerprint):
        raise ValueError("local wiki source reads require source fingerprint v2")
    from ..artifacts.runtime import (
        SourceBindingCleanupOwner,
        _attach_source_cleanup_owner,
    )

    cleanup_owner = SourceBindingCleanupOwner()
    try:
        source_binding = capture_repository_source(
            repo_path,
            manifest=manifest,
            exclude_roots=(manifest_path.parent,),
            _source_owner=cleanup_owner.retain,
        )
        require_manifest_source_identity(
            source_binding.authenticated_identity_snapshot(),
            manifest,
            label="local wiki repository",
            mismatch_message=("repository source content does not match the manifest"),
        )
    except BaseException as primary:  # noqa: B036 - preserve validation fault
        try:
            cleanup_owner.close()
        except BaseException:  # noqa: B036 - expose retry owner on primary
            _attach_source_cleanup_owner(primary, cleanup_owner)
        raise
    try:
        cleanup_owner.close()
    except BaseException as cleanup_failure:  # noqa: B036 - retryable owner
        _attach_source_cleanup_owner(cleanup_failure, cleanup_owner)
        raise

    data_dir = manifest_path.parent / "wiki"
    data_dir.mkdir(parents=True, exist_ok=True)
    # Interactive callers retain the historical Git-origin display identity.
    # Security-sensitive exporters pass an explicit provenance-safe label so
    # ambient .git/config never becomes part of an authenticated publication.
    repo_name = repository_slug or _repository_slug(repo_path)
    repo_id = _repo_id(repo_name.rsplit("/", 1)[-1])
    language = manifest.languages[0] if manifest.languages else "unknown"
    save_registry(
        str(data_dir / "qa_registry.json"),
        [
            RepoEntry(
                instance_id=repo_id,
                repo=repo_name,
                base_commit=manifest.commit,
                language=language,
                repo_dir=str(repo_path),
                manifest_path=str(manifest_path),
            )
        ],
    )

    mode = "hybrid" if manifest.index_is_current("vector") else "sparse"
    config_path = data_dir / "config.yaml"
    config = {
        "data_dir": str(data_dir),
        "mode": mode,
        "wiki_agent": agent_wiki,
        "cors_origins": [
            f"http://localhost:{frontend_port}",
            f"http://127.0.0.1:{frontend_port}",
        ],
    }
    if model:
        config["model"] = model
    if api_base:
        config["model_api_base"] = api_base
    options = validate_model_options(model_options)
    if options:
        config["model_options"] = options
    with config_path.open("w", encoding="utf-8") as handle:
        yaml.safe_dump(config, handle, sort_keys=True)

    runtime_env: dict[str, str] = {}
    if model:
        runtime_env["CODENIB_DEMO_MODEL"] = model
    if api_base:
        runtime_env["CODENIB_DEMO_API_BASE"] = api_base
    if options:
        runtime_env["CODENIB_DEMO_MODEL_OPTIONS"] = json.dumps(
            options,
            sort_keys=True,
            separators=(",", ":"),
        )
    if api_key_env:
        api_key = os.environ.get(api_key_env)
        if not api_key:
            raise ValueError(
                f"API key environment variable is unset or empty: {api_key_env}"
            )
        runtime_env["CODENIB_DEMO_API_KEY"] = api_key
    if embedding_api_key_env:
        embedding_api_key = os.environ.get(embedding_api_key_env)
        if not embedding_api_key:
            raise ValueError(
                "Embedding API key environment variable is unset or empty: "
                f"{embedding_api_key_env}"
            )
        runtime_env["CODENIB_EMBEDDING_API_KEY"] = embedding_api_key

    return LocalWiki(
        repo_path=repo_path,
        manifest_path=manifest_path,
        data_dir=data_dir,
        config_path=config_path,
        repo_id=repo_id,
        runtime_env=runtime_env,
    )