open-notebook/docs
Luis Novo 7d674bd53a
feat(search): scope Search and Ask to selected notebooks (#1331)
* feat(search): scope Search and Ask to selected notebooks

Search and Ask always ran against the whole knowledge base; `notebook_id`
was accepted by POST /api/search and silently ignored because the request
model never declared it. Both endpoints now take an optional notebook
scope and the Search/Ask page gets a shared notebook selector.

- Migration 24: fn::text_search / fn::vector_search gain an optional
  trailing `$notebook_ids: option<array<record<notebook>>>` parameter.
  NONE/empty keeps the unfiltered path; otherwise sources are limited via
  the `reference` edge and notes via the `artifact` edge (chunks and
  insights follow their parent source). The 4-/5-arg call shapes still
  work, so existing callers are unaffected; `24_down` restores the
  previous definitions.
- API: SearchRequest / AskRequest accept `notebook_ids` (list) and
  `notebook_id` (single, the shape clients already send); ids are
  validated (400 for non-notebook ids, 404 for unknown notebooks) before
  reaching the query so a typo never masquerades as "no matches".
- Domain: text_search / vector_search take `notebook_ids`; the highlight
  overflow fallback preserves the scope.
- Ask graph: scope travels in the thread state and reaches every fan-out
  vector search.
- Frontend: NotebookScopeSelector (collapsible checkbox list, "All
  notebooks" by default, Clear action) shared by the Ask and Search tabs;
  strings in all 14 locales.
- Docs: user guide section on scoping; API reference note.

Closes #574
Closes #87

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U53d3BNjGghLsf6tufRTKX

* fix(search): harden notebook scope validation after review

- Raise typed InvalidInputError / NotFoundError instead of bare
  HTTPException, per the API error convention.
- Check notebook existence with a single query for the whole scope; a
  database failure now surfaces as 500 instead of a misleading 404.
- Bound notebook_ids to 50 entries; keep empty ids so they are rejected
  (400) rather than silently widening the scope to everything.
- Replace the pluralized "N notebooks selected" string with a neutral
  "N selected" form: the locale parity test forbids per-language extra
  keys, so _few/_many forms for ru/pl were not an option.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U53d3BNjGghLsf6tufRTKX

* refactor(search): move scope resolution to the domain, add ADR-008

- resolve_notebook_scope() now lives in open_notebook/domain/notebook.py
  next to text_search / vector_search; the router only calls it.
- Reject malformed ids such as "notebook:" (empty key) and anything
  RecordID.parse cannot handle with InvalidInputError instead of letting
  them surface as 500.
- ADR-008 records the notebook-scope semantics, the compatibility choice
  (notebook_id + notebook_ids, optional trailing SurrealQL parameter) and
  how it relates to the #1315 retrieval contract.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U53d3BNjGghLsf6tufRTKX

* test(search): cover migration 24, validate Ask scope before model lookups

- Ask endpoints resolve the notebook scope first, so a malformed or
  unknown scope returns 400/404 before any model or embedding lookup.
- Registration and content tests for 24.surrealql / 24_down.surrealql,
  following the existing migration test pattern.
- ADR-008 context trimmed to the template's sentence budget.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U53d3BNjGghLsf6tufRTKX

* fix(search): surface a typed error when scope resolution hits the database

resolve_notebook_scope() now logs the raw driver error and raises
DatabaseOperationError with a fixed message, matching text_search /
vector_search, so connection details never reach the 500 response body.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U53d3BNjGghLsf6tufRTKX

* fix(search): compare notebook scope ids as strings

The driver returns `id` as RecordID objects, which are unhashable and
never equal to the request strings, so every real scoped request raised
TypeError before reaching the search. Normalize to str before comparing
and add a regression test that mocks RecordID rows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014kRBoPiBD1NKeKHCo6EyrE

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 16:09:04 -03:00
..
0-START-HERE docs: standardize Models navigation path (#1330) 2026-09-05 13:44:00 -03:00
1-INSTALLATION docs: standardize Models navigation path (#1330) 2026-09-05 13:44:00 -03:00
2-CORE-CONCEPTS feat(podcasts): model registry integration, credential passthrough & new features (#632) 2026-02-27 11:06:47 -03:00
3-USER-GUIDE feat(search): scope Search and Ask to selected notebooks (#1331) 2026-09-05 16:09:04 -03:00
4-AI-PROVIDERS docs: standardize Models navigation path (#1330) 2026-09-05 13:44:00 -03:00
5-CONFIGURATION docs: standardize Models navigation path (#1330) 2026-09-05 13:44:00 -03:00
6-TROUBLESHOOTING docs: standardize Models navigation path (#1330) 2026-09-05 13:44:00 -03:00
7-DEVELOPMENT feat(search): scope Search and Ask to selected notebooks (#1331) 2026-09-05 16:09:04 -03:00
assets docs: fix removed assets in https://github.com/lfnovo/open-notebook/pull/379 affecting README.md 2026-01-06 15:07:58 +01:00
index.md docs: restructure documentation around AGENTS.md, VISION.md and decision records (#1032) 2026-07-10 15:33:19 -03:00
SECURITY_REVIEW.md chore: remove dead auth helper and fix stale default-password docs (#1026) 2026-07-10 15:47:40 -03:00