Set up document management for Stirling Engine (#6476)

# Description of Changes
Change Stirling Engine to support deleting documents automatically. This
happens both on user logout and after an amount of time specified by the
Java when ingesting a document (allowing for personal documents to have
short lifetimes but org documents to be left in the db with no expiry
date). Also sets up an [ACL
policy](https://en.wikipedia.org/wiki/Access-control_list) for the
documents so the database knows which users have access to which
documents. This is not fully implemented in the Java, so currently all
docs are treated as having a single owner, the uploader, but
theoretically when we need to support org storage, we shouldn't need to
change the db schema.
This commit is contained in:
James Brunton
2026-06-03 11:52:11 +00:00
committed by GitHub
parent 71633861d0
commit 1264f4cfed
48 changed files with 2039 additions and 411 deletions
@@ -8,6 +8,7 @@ contradiction and the right cross-references and anchor handling.
from __future__ import annotations
import json
from collections.abc import Iterator
from dataclasses import replace
from typing import Literal
from unittest.mock import AsyncMock
@@ -27,11 +28,25 @@ from stirling.contracts import (
)
from stirling.contracts.contradiction import Claim
from stirling.documents import DocumentService, SqliteVecStore
from stirling.models import FileId, ToolEndpoint
from stirling.models import FileId, OwnerId, PrincipalId, ToolEndpoint, UserId
from stirling.models.tool_models import AddCommentsParams
from stirling.services import current_user_id
from stirling.services.runtime import AppRuntime
from tests.test_pdf_question_agent import StubEmbedder
USER = UserId("test-user")
OWNER = OwnerId("test-user")
OWNER_PRINCIPALS = [PrincipalId("test-user")]
@pytest.fixture(autouse=True)
def _set_user_context() -> Iterator[None]:
token = current_user_id.set(USER)
try:
yield
finally:
current_user_id.reset(token)
def _file(file_id: str, name: str) -> AiFile:
return AiFile(id=FileId(file_id), name=name)
@@ -88,6 +103,9 @@ async def test_localiser_prompt_escapes_verdict_tag_injection(
file.id,
[PageText(page_number=1, text="x")],
source=file.name,
owner_id=OWNER,
read_principals=OWNER_PRINCIPALS,
expires_at=None,
)
agent = PdfReviewAgent(runtime_with_stub_docs)
@@ -159,6 +177,9 @@ async def test_contradiction_intent_emits_add_comments_plan(
file.id,
[PageText(page_number=1, text="ignored"), PageText(page_number=5, text="ignored")],
source=file.name,
owner_id=OWNER,
read_principals=OWNER_PRINCIPALS,
expires_at=None,
)
agent = PdfReviewAgent(runtime_with_stub_docs)
@@ -264,6 +285,9 @@ async def test_contradiction_takes_precedence_over_math(
file.id,
[PageText(page_number=1, text="x")],
source=file.name,
owner_id=OWNER,
read_principals=OWNER_PRINCIPALS,
expires_at=None,
)
agent = PdfReviewAgent(runtime_with_stub_docs)