diff --git a/surfsense_backend/app/schemas/obsidian_plugin.py b/surfsense_backend/app/schemas/obsidian_plugin.py index 745886ef6..1b3ea7971 100644 --- a/surfsense_backend/app/schemas/obsidian_plugin.py +++ b/surfsense_backend/app/schemas/obsidian_plugin.py @@ -21,6 +21,13 @@ class _PluginBase(BaseModel): model_config = _PLUGIN_MODEL_CONFIG +class HeadingRef(_PluginBase): + """One markdown heading extracted from Obsidian metadata cache.""" + + heading: str + level: int = Field(ge=1, le=6) + + class NotePayload(_PluginBase): """One Obsidian note as pushed by the plugin (the source of truth).""" @@ -36,7 +43,7 @@ class NotePayload(_PluginBase): frontmatter: dict[str, Any] = Field(default_factory=dict) tags: list[str] = Field(default_factory=list) - headings: list[str] = Field(default_factory=list) + headings: list[HeadingRef] = Field(default_factory=list) resolved_links: list[str] = Field(default_factory=list) unresolved_links: list[str] = Field(default_factory=list) embeds: list[str] = Field(default_factory=list) diff --git a/surfsense_backend/app/services/obsidian_plugin_indexer.py b/surfsense_backend/app/services/obsidian_plugin_indexer.py index 5afdbf886..5b037a098 100644 --- a/surfsense_backend/app/services/obsidian_plugin_indexer.py +++ b/surfsense_backend/app/services/obsidian_plugin_indexer.py @@ -53,7 +53,6 @@ from app.schemas.obsidian_plugin import ( ManifestResponse, NotePayload, ) -from app.services.llm_service import get_user_long_context_llm from app.utils.document_converters import generate_unique_identifier_hash from app.utils.document_versioning import create_version_snapshot @@ -102,7 +101,7 @@ def _build_metadata( "extension": payload.extension, "frontmatter": payload.frontmatter, "tags": payload.tags, - "headings": payload.headings, + "headings": [h.model_dump() for h in payload.headings], "outgoing_links": payload.resolved_links, "unresolved_links": payload.unresolved_links, "embeds": payload.embeds, @@ -237,6 +236,8 @@ async def upsert_note( document = prepared[0] + from app.services.llm_service import get_user_long_context_llm + llm = await get_user_long_context_llm(session, str(user_id), search_space_id) return await pipeline.index(document, connector_doc, llm) diff --git a/surfsense_backend/tests/integration/test_obsidian_plugin_routes.py b/surfsense_backend/tests/integration/test_obsidian_plugin_routes.py index 1dd7e2a23..d1e94c1c5 100644 --- a/surfsense_backend/tests/integration/test_obsidian_plugin_routes.py +++ b/surfsense_backend/tests/integration/test_obsidian_plugin_routes.py @@ -46,6 +46,7 @@ from app.schemas.obsidian_plugin import ( ConnectRequest, DeleteAck, DeleteBatchRequest, + HeadingRef, ManifestResponse, NotePayload, RenameAck, @@ -74,6 +75,7 @@ def _make_note_payload(vault_id: str, path: str, content_hash: str) -> NotePaylo name=path.rsplit("/", 1)[-1].rsplit(".", 1)[0], extension="md", content="# Test\n\nbody", + headings=[HeadingRef(heading="Test", level=1)], content_hash=content_hash, mtime=now, ctime=now, diff --git a/surfsense_backend/tests/unit/test_obsidian_plugin_indexer.py b/surfsense_backend/tests/unit/test_obsidian_plugin_indexer.py new file mode 100644 index 000000000..a557ea208 --- /dev/null +++ b/surfsense_backend/tests/unit/test_obsidian_plugin_indexer.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from datetime import UTC, datetime + +from app.schemas.obsidian_plugin import HeadingRef, NotePayload +from app.services.obsidian_plugin_indexer import _build_metadata + + +def test_build_metadata_serializes_headings_to_plain_json() -> None: + now = datetime.now(UTC) + payload = NotePayload( + vault_id="vault-1", + path="notes.md", + name="notes", + extension="md", + content="# Notes", + headings=[HeadingRef(heading="Notes", level=1)], + content_hash="abc123", + mtime=now, + ctime=now, + ) + + metadata = _build_metadata(payload, vault_name="My Vault", connector_id=42) + + assert metadata["headings"] == [{"heading": "Notes", "level": 1}]