EPIC R03 (Team Duy) — Model Providers & Routing. All six tasks done.
R03-T02 — Provider catalogue
domain/models/provider_descriptor.py ProviderDescriptor (frozen), WireProtocol, AuthKind
infrastructure/providers/provider_registry.py
thread-safe registry: id/alias lookup, dynamic
lookup by model id, adapter selection by protocol
providers/factory.py drops its own _REGISTRY table and delegates to the
registry, still raising ProviderError for callers
R03-T03 — RoutingApplicationService (pure Python, 4 modes)
application/model_routing/routing_models.py
RoutingMode (off/auto/manual/fallback),
RoutingRequest (immutable snapshot), RouteEvaluation,
RoutingOutcome
application/model_routing/routing_application_service.py
the single decision flow, reached through two narrow
ports plus a caller-supplied confirm callback, so no
Qt import is needed
application/model_routing/core_routing_adapter.py
binds the ports to core/routing and AppContext
Fallback is a new resilience mode: keep the selected model while it can serve the turn,
re-route only when it cannot. Wired end to end through config.py, state.py,
ui/routing_toggle.py and i18n.py (EN/JA/VI).
R03-T04 / T05 — Remove the duplicated routing flow
ui/chat_panel.py (#L638), ui/co4e_tab.py, ui/folder_tab.py each drop ~35 lines of copied
logic and call the shared service; the widgets now only build a RoutingRequest, host the
Manual-mode modal and render the outcome.
R03-T06 — Token usage as an event
infrastructure/telemetry/usage_sink.py UsageEvent + UsageEventSink protocol, with tracker,
in-memory and composite sinks
providers/openai_compat.py, providers/anthropic.py
publish a UsageEvent instead of writing to the
usage tracker themselves
core/usage_tracker.py adds current_context() so a sink can borrow and
restore a thread's attribution
R03-T01 — Contract tests
tests/contracts/test_providers.py parametrises over every provider in the registry: chat()
signature, canonical assistant message, normalised tool calls, response closed, tool schema
translation, ProviderError, list_models/test_connection, one UsageEvent per turn.
Test infrastructure fix (required to verify any of the above): tests/conftest.py used to put
the repository's PARENT directory on sys.path, so `import cowork_local.*` resolved against
whichever sibling folder happened to carry that name — on a dev machine, an unrelated older
checkout. The suite reported green while exercising different code. The conftest now binds
this checkout to the cowork_local name in sys.modules.
Verification
pytest tests/ 236 passed in ~1.8s (102 before this change)
scripts/check_imports.py PASS, 0 forbidden imports in domain/ and application/
new production files largest is 288 lines, all under the 400 LOC ceiling
new tests 134 (50 contract, 70 unit, 14 integration), all offline
scripts/run_quality_gate.py does not exist yet (R10-T02), so DoD item 7 was covered by
check_imports.py plus the full suite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
3.2 KiB
Python
76 lines
3.2 KiB
Python
"""Make THIS checkout importable as the ``cowork_local`` package during tests.
|
|
|
|
Why this is not just a ``sys.path`` insert
|
|
------------------------------------------
|
|
Test modules import the app in two different styles:
|
|
|
|
* top-level (``from providers.base import ...``) — resolved by the repository
|
|
root already sitting on ``sys.path`` when pytest is launched from it;
|
|
* fully qualified (``from cowork_local.core.routing.service import ...``) —
|
|
which only resolves when a directory literally named ``cowork_local`` is
|
|
importable.
|
|
|
|
Simply appending the repository's PARENT directory to ``sys.path`` (the previous
|
|
behaviour) makes the second style resolve against *whatever* sibling folder
|
|
happens to be called ``cowork_local`` — on a developer machine that is often an
|
|
unrelated older checkout, so the whole suite silently exercises the wrong code
|
|
while still reporting green. Instead we bind the name ``cowork_local`` in
|
|
``sys.modules`` to the package rooted at THIS repository, so both import styles
|
|
always reach the working copy under test regardless of the checkout's directory
|
|
name.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# .../<checkout>/tests/conftest.py -> .../<checkout>
|
|
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
|
|
PACKAGE_NAME = "cowork_local"
|
|
|
|
# The repository root must stay importable so the top-level import style
|
|
# (``providers``/``domain``/``application``/``tests``) keeps working.
|
|
if str(PACKAGE_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PACKAGE_ROOT))
|
|
|
|
|
|
def _bind_checkout_as_package() -> None:
|
|
"""Register this checkout in ``sys.modules`` under the canonical package name.
|
|
|
|
Executed at import time of the conftest (i.e. before any test module is
|
|
imported) so that a stale same-named directory elsewhere on ``sys.path`` can
|
|
never win the lookup. A no-op when the package is already bound to this very
|
|
directory, which keeps repeated conftest loads (pytest-xdist, sub-sessions)
|
|
idempotent.
|
|
"""
|
|
existing = sys.modules.get(PACKAGE_NAME)
|
|
if existing is not None:
|
|
# Already bound. Only rebind when it points at a DIFFERENT checkout,
|
|
# otherwise re-executing the package __init__ would duplicate module
|
|
# state that tests may already hold references to.
|
|
origin = getattr(existing, "__file__", "") or ""
|
|
if Path(origin).resolve().parent == PACKAGE_ROOT:
|
|
return
|
|
|
|
spec = importlib.util.spec_from_file_location(
|
|
PACKAGE_NAME,
|
|
PACKAGE_ROOT / "__init__.py",
|
|
# Declaring the search locations is what turns the module into a real
|
|
# package, so ``cowork_local.core.routing`` and friends resolve as
|
|
# sub-modules of this directory.
|
|
submodule_search_locations=[str(PACKAGE_ROOT)],
|
|
)
|
|
if spec is None or spec.loader is None: # pragma: no cover — defensive
|
|
return
|
|
module = importlib.util.module_from_spec(spec)
|
|
# Insert BEFORE executing so that a circular ``import cowork_local`` from
|
|
# inside the package body resolves to the partially-initialised module
|
|
# instead of restarting the import (standard CPython import semantics).
|
|
sys.modules[PACKAGE_NAME] = module
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
_bind_checkout_as_package()
|