Skip to content

Promote internal LLM/session helpers to explicit public API; remove underscore re-exports from __init__.py - #70

Merged
CyberSecDef merged 2 commits into
mainfrom
copilot/stop-re-exporting-internals
Apr 3, 2026
Merged

Promote internal LLM/session helpers to explicit public API; remove underscore re-exports from __init__.py#70
CyberSecDef merged 2 commits into
mainfrom
copilot/stop-re-exporting-internals

Conversation

Copilot AI commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Package __init__.py files were re-exporting underscore-prefixed names, making implementation details accidentally part of the stable public API and blurring the package boundary.

Changes

novelforge/llm/client.py

  • Renamed _reset_llm_usagereset_llm_usage, _get_llm_usageget_llm_usage, _friendly_llm_errorfriendly_llm_error
  • Added reset_circuit_breakers() — encapsulates the previously leaked _llm_circuit_breakers iteration pattern

novelforge/llm/__init__.py

  • Removed all 6 underscore re-exports (_reset_llm_usage, _get_llm_usage, _friendly_llm_error, _llm_circuit_breaker, _llm_circuit_breakers, _get_circuit_breaker)
  • Exports the 4 new public names in their place

novelforge/session/persistence.py

  • Renamed _persist_completed_chapterspersist_completed_chapters

novelforge/session/__init__.py

  • Dropped _SESSION_SCHEMA (used only inside persistence.py) and _persist_completed_chapters
  • Exports persist_completed_chapters

Callers updatedroutes/generation.py, routes/export.py, routes/outline.py, tests/conftest.py, tests/test_coverage.py, tests/test_session.py

Before / After

# Before — internals leaked through __init__
from novelforge.llm import _reset_llm_usage, _llm_circuit_breakers
for cb in _llm_circuit_breakers.values():
    cb.reset()
_reset_llm_usage()

# After — explicit public API
from novelforge.llm import reset_circuit_breakers, reset_llm_usage
reset_circuit_breakers()
reset_llm_usage()

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.openai.com
    • Triggering command: /usr/bin/python python -m pytest tests/ --tb=short -q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Fix package init modules re-exporting underscored internals Promote internal LLM/session helpers to explicit public API; remove underscore re-exports from __init__.py Apr 3, 2026
Copilot AI requested a review from CyberSecDef April 3, 2026 12:45
@CyberSecDef
CyberSecDef marked this pull request as ready for review April 3, 2026 13:11
Copilot AI review requested due to automatic review settings April 3, 2026 13:11
@CyberSecDef
CyberSecDef merged commit 4e2b514 into main Apr 3, 2026
2 checks passed
@CyberSecDef
CyberSecDef deleted the copilot/stop-re-exporting-internals branch April 3, 2026 13:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes previously underscore-prefixed LLM/session helpers an explicit public API and removes underscore re-exports from package __init__.py files to prevent internal details from becoming accidental stable interfaces.

Changes:

  • Promotes LLM helpers to public names (reset_llm_usage, get_llm_usage, friendly_llm_error) and adds reset_circuit_breakers().
  • Promotes session persistence helper to public name (persist_completed_chapters) and stops exporting internal schema/constants.
  • Updates routes and tests to use the new public names.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
novelforge/llm/client.py Renames usage/error helpers and introduces reset_circuit_breakers()
novelforge/llm/__init__.py Stops re-exporting underscore-prefixed internals; exports new public helpers
novelforge/session/persistence.py Renames _persist_completed_chapters to persist_completed_chapters
novelforge/session/__init__.py Removes underscore exports; exports persist_completed_chapters
novelforge/routes/generation.py Migrates callers to new LLM/session helper names
novelforge/routes/outline.py Uses friendly_llm_error instead of underscore variant
novelforge/routes/export.py Uses friendly_llm_error instead of underscore variant
tests/conftest.py Uses reset_circuit_breakers() instead of iterating internal dict
tests/test_coverage.py Updates imports for renamed LLM helpers
tests/test_session.py Updates imports/docs for renamed persistence helper
Comments suppressed due to low confidence (1)

novelforge/llm/client.py:520

  • friendly_llm_error() is now part of the explicit public API and is being called with non-RuntimeError exceptions (e.g., requests.exceptions.RequestException in routes/generation.py). The docstring currently says it converts a RuntimeError from call_llm, which is misleading; please update it to describe the broader intended input (any LLM/request/parsing exception) and the tagging/stripping behavior.
def friendly_llm_error(exc: Exception) -> str:
    """
    Convert a RuntimeError from call_llm into a user-friendly message.

    The error string from call_llm is prefixed with a tag like LLM_TIMEOUT:
    which is stripped here. If no known tag is found, a generic message is used.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread novelforge/llm/client.py
def _friendly_llm_error(exc: Exception) -> str:
def reset_circuit_breakers() -> None:
"""Reset all per-provider LLM circuit breakers."""
for cb in _llm_circuit_breakers.values():

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reset_circuit_breakers() iterates over _llm_circuit_breakers.values() without holding _llm_cb_lock. Since _get_circuit_breaker() can add new entries concurrently, this risks RuntimeError: dictionary changed size during iteration (and partially-reset state). Consider acquiring _llm_cb_lock and iterating over a snapshot (e.g., list(_llm_circuit_breakers.values())) before calling reset() on each breaker.

Suggested change
for cb in _llm_circuit_breakers.values():
with _llm_cb_lock:
breakers = list(_llm_circuit_breakers.values())
for cb in breakers:

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop package __init__ modules from re-exporting underscored internals as public API

3 participants