diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..b05ab502 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,30 @@ +name: Documentation + +on: + push: + branches: [main, master] + paths: + - '*.md' + - 'docs/**' + - 'tests/backend/test_docs_*.py' + - '.github/workflows/docs.yml' + pull_request: + branches: [main, master] + paths: + - '*.md' + - 'docs/**' + - 'tests/backend/test_docs_*.py' + - '.github/workflows/docs.yml' + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + cache: pip + cache-dependency-path: requirements.txt + - run: pip install -r requirements.txt + - run: python -m unittest tests.backend.test_docs_links tests.backend.test_docs_sync -v diff --git a/backend/app.py b/backend/app.py index 4543027d..a8d646bc 100644 --- a/backend/app.py +++ b/backend/app.py @@ -830,11 +830,6 @@ def dispatch_api_request(self, method, parsed, body=None): else: self.send_error(404) return - if isinstance(match.handler, str): - handler = getattr(self, match.handler) - handler(parsed, body, dict(match.params)) - return - request = Request( method=method, path=path, diff --git a/backend/routing.py b/backend/routing.py index 1ba7e074..4cae08e1 100644 --- a/backend/routing.py +++ b/backend/routing.py @@ -1,26 +1,20 @@ """Small route registry used by the stdlib HTTP entrypoint.""" from dataclasses import dataclass -from typing import Any, Dict, Mapping, Optional, Tuple +from typing import Callable, Dict, Mapping, Optional, Tuple @dataclass(frozen=True) class RouteMatch: - handler: Any + handler: Callable params: Mapping[str, str] - @property - def handler_name(self) -> str: - if isinstance(self.handler, str): - return self.handler - return getattr(self.handler, "__name__", str(self.handler)) - @dataclass(frozen=True) class _Route: method: str path: str - handler: Any + handler: Callable param_name: Optional[str] = None is_prefix: bool = False @@ -37,7 +31,7 @@ def __init__(self) -> None: self._exact: Dict[Tuple[str, str], RouteMatch] = {} self._prefixes: list[_Route] = [] - def add(self, method: str, path: str, handler: Any) -> "Router": + def add(self, method: str, path: str, handler: Callable) -> "Router": self._exact[(method.upper(), path)] = RouteMatch(handler, {}) return self @@ -45,7 +39,7 @@ def add_prefix( self, method: str, prefix: str, - handler: Any, + handler: Callable, param_name: Optional[str] = None, ) -> "Router": self._prefixes.append( diff --git a/docs/chat-popout-capabilities.md b/docs/chat-popout-capabilities.md index 48ac5a0c..1564ed8d 100644 --- a/docs/chat-popout-capabilities.md +++ b/docs/chat-popout-capabilities.md @@ -1,15 +1,15 @@ # Chat pop-out Phase 1 capability findings Date: 2026-09-11 -Status: Phase 1 fixture evidence (2026-09-11). The production pop-out in `ui/js/chat-window/` is covered in [Chat in a separate window](chat-popout.md) and [Tests](tests.md); this fixture itself implements no production behavior. +Status: Historical Phase 1 evidence. The synthetic fixture was retired on 2026-09-23; +its recorded browser and native findings remain below. Current production behavior +and automated checks are documented in [Chat in a separate window](chat-popout.md) +and [Tests](tests.md). -The disposable check is run with: - -```powershell -node tests/frontend/chat_popout_capabilities.cjs -``` - -The runner launches Playwright Chromium with fresh browser contexts and an ephemeral HTTP server. The server serves only the synthetic fixture page. It does not serve `ui/`, read or write `llama_gui_conversations`, accept chat requests, or expose any `/api/*` route. Each scenario uses a unique nonce and the only storage key it may create is `chat-popout-capability:`. Closing the context removes that disposable state. +The retired probe used fresh Chromium contexts and an ephemeral server serving a +synthetic page, without application code, real chat history, or backend routes. +The following fixture findings describe that 2026-09-11 experiment, not a current +production regression suite. ## Evidence from the loopback fixture @@ -36,29 +36,6 @@ Every fallback case uses a fresh browser context and keeps the source page recov The blocked-popup, blocked-storage, missing-Web-Locks, and missing-opener conditions are controlled browser-context simulations. The normal popup, opener reference, same-context storage sharing, Web Locks contention, secure-context result, and close checks run against Chromium itself. Native window activation is not asserted by the headless fixture. A real embedded host that strips or changes these capabilities still needs a native check. -## Bootstrap and request-setting audit - -The current frontend is a strict ordered global-script page. `app.js` is not a safe popup entry point to run wholesale: - -- Before `DOMContentLoaded`, it selects `llama-server`, replaces `flagCore` values with defaults, creates the shared inference-stat engine, and configures API, sampler-preset, preset, tunnel, external-server, Hugging Face, Quick Launch, benchmark, Monitor, process-lifecycle, and Model Switcher modules. It also subscribes to lifecycle snapshots. -- Its `DOMContentLoaded` handler initializes the theme, tabs, tool and Configure controls, installation/API/preset/Quick Launch/Chat/benchmark/Monitor flows, model-directory controls, release fetching, command preview, endpoint rendering, launch/stop actions, polling visibility handlers, and the remaining page wiring. -- `chatUi.configure()` is a top-level dependency injection call near the end of `app.js`; `initChatTab()` only calls `chatUi.init()`. Loading the full application in a popup would therefore risk a second configuration authority, lifecycle controller, release fetch, and polling cycle. - -The smallest popup bootstrap must retain the ordered dependencies required by the Chat package (`ui/js/chat/`) (`flagCore`, `chatTools`, `app-data`, `chat-rendering`, `chat-compaction`, and `character-cards`) and provide the existing Chat DOM/dependency boundary. It should inject a verified host adapter for settings, runtime identity/status, authorization behavior, and the existing baseline helper. The popup must not initialize its own lifecycle controller or external-server restore. This is an audit result for the next phase; it does not add a popup module or alter the application bootstrap. - -`buildChatBody(history, draft, includeUsage)` currently consumes these request inputs: - -- `#chat-system-prompt` and `chatTools.getInstructions()` for the system message. -- The complete history after `chatCompaction.workingMessages(...)`, passed through `chatTools.requestMessages(...)`, plus the trimmed draft as a final user message. -- The active model from the lifecycle snapshot first, then running status, then shared `flagCore` alias/selected model, with `local-model` as the last fallback. -- `stream: true` and, when requested, `stream_options.include_usage: true`. -- Shared `flagCore` sampler values: `temperature`, `top_p`, `top_k`, `min_p`, `repeat_penalty`, and `n_predict` mapped to `max_tokens` (with numeric normalization and the `-1` server-default sentinel omitted from `max_tokens`). -- `#chat-thinking-effort` (`auto`, `off`, `low`, `medium`, `high`, or `xhigh`). Non-auto values produce the top-level `reasoning_effort` and the compatibility `chat_template_kwargs`; `off` uses `none` and disables thinking. -- `#chat-web-search-toggle` and the clamped `#chat-web-search-max-results` value (1–10, default 5) when search is enabled. -- `chatTools.getDefinitions()` when the configured browser tools list is non-empty. - -Context-preview and compaction keys also include the active runtime, external target, template capabilities, and the same request body. A popup host adapter therefore needs notifications that invalidate stale previews when the main window changes runtime or shared sampler state. - ## Launch-environment limits The Phase 1 fixture evidence is limited to a normal Chromium browser using loopback HTTP and isolated synthetic contexts. The following capabilities are not established by that fixture; later native observations are recorded separately below: diff --git a/docs/chat-popout.md b/docs/chat-popout.md index 30808080..612c8e1d 100644 --- a/docs/chat-popout.md +++ b/docs/chat-popout.md @@ -40,4 +40,4 @@ Only pages running this implementation in the same browser storage partition par For a native launcher or browser check, use disposable test conversations and verify popup/opener retention, shared storage, focus, close/reload recovery, and Return/Send access with both panels and Context open. Check the launcher's embedded view separately from its external-browser link. Keep CI on Linux and Windows; native checks do not require adding macOS runners. -See [capability evidence](chat-popout-capabilities.md) and [test commands](tests.md). +See [historical capability evidence](chat-popout-capabilities.md) and [test commands](tests.md). diff --git a/docs/directory.md b/docs/directory.md index a83851c0..c60a1b58 100644 --- a/docs/directory.md +++ b/docs/directory.md @@ -1364,7 +1364,7 @@ Prefer `rg` for local search. On Windows/PowerShell, use patterns like `rg -n "p | `docs/security.md` | Security model, trust boundaries, and reporting guidance | | `docs/troubleshooting.md` | Common installation, launch, model, GPU, and connectivity problems | | `docs/chat-popout.md` | Chat pop-out user guide: transfer, recovery, and browser/launcher support | -| `docs/chat-popout-capabilities.md` | Phase 1 pop-out capability fixture evidence | +| `docs/chat-popout-capabilities.md` | Historical pop-out capability and native-browser evidence | | `docs/upstream-changes.md` | llama.cpp upstream changes needing coordinated GUI updates | | `docs/software-versioning-policy.md` | CalVer versioning and stable-release policy | | `docs/images/` | Screenshots used by README.md | diff --git a/docs/tests.md b/docs/tests.md index 4f37e2f1..10e7a070 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -7,6 +7,10 @@ This repo has two main test groups: The goal is not exhaustive coverage. Tests should make common regressions easier to diagnose, especially around shared launch state, command generation, route/service behavior, and UI helper logic. +The Documentation workflow runs the link and route-registry documentation checks on +Markdown and `docs/` changes, including docs-only pull requests. The full Tests +workflow retains its documentation exclusions and Linux/Windows matrix. + ## Common Commands `python -m unittest tests.backend.test_official_backends -v` (using the project @@ -19,19 +23,13 @@ installation. These fixtures never download or launch real GPU binaries. npm test ``` -Runs the full frontend suite in three stages: `test:unit` (JavaScript syntax checks, fast Node unit tests, structural flag-definition validation, and module loading/contract checks), `test:flags` (flag compatibility against real binaries), and `test:frontend` (the Playwright browser suite). +Runs the full frontend suite in three stages: `test:unit` (fast Node unit tests, structural flag-definition validation, and module loading/contract checks), `test:flags` (flag compatibility against real binaries), and `test:frontend` (the Playwright browser suite). ```powershell npm run test:unit ``` -Runs the aggregate fast stage only — every Node unit suite plus `test:syntax`, `test:frontend:modules`, and `test:flag-definitions` — with no browser launch and no llama.cpp binary required. Use this as the inner loop; `test:flags` and `test:frontend` remain separate explicit stages, and `npm test` is the full gate. - -```powershell -npm run test:syntax -``` - -Checks every frontend JavaScript file with `node --check`. +Runs the aggregate fast stage only — every Node unit suite plus `test:frontend:modules` and `test:flag-definitions` — with no browser launch and no llama.cpp binary required. Use this as the inner loop; `test:flags` and `test:frontend` remain separate explicit stages, and `npm test` is the full gate. ```powershell npm run test:frontend:modules @@ -68,7 +66,7 @@ npm run test:frontend Runs the Playwright smoke test for browser-level shared-state sync. This is also the only suite that can cover the Configure sampler preset panel, because `renderFlags()` destroys and rebuilds it — the `