Coverage for src/local_deep_research/exceptions.py: 100%
6 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 23:15 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 23:15 +0000
1"""Project-wide exception classes."""
4class ResearchTerminatedException(BaseException):
5 """Raised when a user cancels an in-progress research process.
7 Inherits from BaseException (not Exception) so that ``except Exception``
8 blocks throughout the strategy code naturally let it propagate -- the same
9 pattern Python's stdlib uses for asyncio.CancelledError (since 3.9),
10 KeyboardInterrupt, and SystemExit.
11 """
13 pass
16class SystemAtCapacityError(Exception):
17 """Raised when ``start_research_process`` cannot acquire the global
18 concurrency semaphore.
20 Previously the semaphore was acquired *inside* the worker thread, so a
21 full system would silently park the thread after the HTTP route had
22 already returned 200 — the user saw a thinking spinner that never
23 advanced, and the partial unique in-progress index blocked retries on
24 the same chat session.
26 Acquiring synchronously in the caller and surfacing this exception lets
27 routes return HTTP 429 (or queue/retry, depending on caller) before any
28 ``ResearchHistory`` row is committed.
29 """
31 pass
34class DuplicateResearchError(Exception):
35 """Raised when a research should not be (re-)spawned.
37 Two triggering cases, both handled identically by callers:
39 1. A live thread already exists in the active-research dict for this
40 ``research_id`` — typically a retry after a prior attempt's
41 post-spawn ``UserActiveResearch`` commit failed.
42 2. The ``ResearchHistory.status`` is non-QUEUED (``IN_PROGRESS`` from
43 a prior attempt's pre-spawn commit that succeeded; terminal
44 ``COMPLETED`` / ``FAILED`` / ``SUSPENDED`` from a thread that
45 already finished and cleaned itself out of ``_active_research``).
46 Re-spawning would either contradict the live thread or re-run a
47 finished research.
49 Callers that wrap the spawn in ``except Exception`` to clean up orphan
50 state on spawn failure MUST catch ``DuplicateResearchError`` separately
51 *before* that generic branch and re-raise / return without mutating the
52 research's status or deleting the ``UserActiveResearch`` row — those
53 rows belong to the live thread, and marking them FAILED terminates a
54 running thread from the user's perspective while it keeps executing.
55 """
57 pass