[Feat] Community Local: Studio — generate artifacts from your documents - #1753
Conversation
LLM emits structured content, a trusted per-format builder renders it, and the result persists as an ARTIFACT document + artifacts sidecar. Ships the summary format end to end: worker studio pipeline (gather/generate/build/ index), the artifacts API (studio jobs, list, detail, file, delete), the studio_job task, and an artifacts SSE event kind.
A Studio dialog off the Sources header: pick a format, pick indexed sources, optional prompt, Generate. Artifacts list with live status (poll while running), a viewer for the rendered body, and per-file downloads for later formats.
…/flashcards/quiz (phase 4b) Each format prompts the model for a JSON spec and renders it with a trusted per-format function: python-docx/pptx and XlsxWriter for Office files, an escaped template for HTML, and markdown projections for mind maps, flashcards and quizzes. json parsing tolerates fenced/prose-wrapped output. The frozen worker bundles the docx/pptx templates and registers the studio task.
markdown-spec -> PDF via pure-Python fpdf2 (no Pango/Cairo system libs to bundle). Uses an on-disk Unicode TTF when present, else Helvetica with a Latin-1 fallback; a bundled font pack is the upgrade path.
…raphic (phase 4e)
|
@CREDO23 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ed office code - office/ writes docx/pptx/xlsx/pdf as model-authored code (spec + SKILL.md per format, shared prompt/runner), replacing the deterministic builders for those formats; generate.run_model is the shared model call. - media/ groups the audio/visual family: podcast (transcript builder + nested Kokoro tts) and visual (BYO OpenRouter image/infographic). - builders/ keeps the deterministic formats (summary/html/mindmap/flashcards/quiz). - Shared contract lifted to studio root (artifact.py, builder.py, text.py) so the three families are peers, none importing from a sibling. - pipeline routes every catalog key to exactly one family; an invariant test asserts the partition.
The model now authors PDF code; reportlab is pure-Python and bundles cleanly, matching the cloud PDF skill. fpdf2 is no longer used.
# Conflicts: # surfsense_local/frontend/src/features/sources/sources-panel.tsx
Parametrize the worker pipeline test over the whole Studio catalogue so all twelve formats run their real builder/office-code/media path to a ready artifact, faking only the model (plus podcast TTS and the image API). Drops the two single-format cases now subsumed by the matrix.
Replace the parametrized matrix with a named test per format, each documenting what that format produces and asserting the optional user prompt reaches the model. Still fakes only the model (plus podcast TTS and the image API); the builders, office code runner, and persistence run for real.
Resolve the model's inline [n] against the sources it was given: an ordinal with no source is dropped rather than left dangling, and the survivors are renumbered densely by first appearance so the stored turn and its listed sources agree. Ordinals inside code spans pass through untouched. The stream now stores and emits only the sources actually cited.
Unit tests for normalize_citations: invented ordinals dropped and survivors renumbered, a repeated source maps to one number, ordinals inside code spans are left alone, and the empty cases.
Adds Studio to the community local app: pick documents, pick a format, get a grounded artifact. Built on the existing document/ingestion/LLM stack — an artifact is a
Document, so it rides search and freshness like any source.What you can make
How it works
The worker runs
gather → generate → render → persist, streaming status to the UI over the existing SSE path. Every format routes to exactly one of three peer families over a shared contract (artifact,builder,text); an invariant test asserts the partition.python-docx,python-pptx,xlsxwriter,reportlab) and the worker runs it in-process with a timeout. Unsandboxed by design: a local, single-user app. Each format is a folder with its ownSKILL.mdof authoring craft.UI: a dialog off the Sources panel to create, browse, and view artifacts; audio and images render inline.
Tests
Backend unit + integration suites (deterministic builders, office code-execution, media, and pipeline routing) and the frontend studio suite pass.
High-level PR Summary
This PR adds Studio to the community local app, enabling users to generate artifacts (summaries, Office files, PDFs, podcasts, images, study aids) from their workspace documents. The implementation uses a deterministic builder architecture where LLMs emit structured content (JSON/markdown) and trusted Python functions render files using standard libraries (
python-docx,python-pptx,XlsxWriter,fpdf2), avoiding LLM-authored code execution and sandboxing. Artifacts are stored asARTIFACTdocuments that integrate with the existing search and indexing infrastructure. The feature includes 10 text/Office formats with local builders, offline podcast synthesis via Kokoro-82M (optional), and visual formats (image/infographic) that route to a BYO OpenRouter model. The UI provides a dialog off the Sources panel for creation, browsing, and viewing artifacts with inline rendering for audio and images. The entire feature ships with comprehensive test coverage (165 backend tests, frontend suite) and detailed planning documentation.⏱️ Estimated Review Time: 3+ hours
💡 Review Order Suggestion
docs/adr/0003-artifacts-as-documents.mdplans/community-local/00-umbrella-plan.mdplans/community-local/api/04-studio.mdplans/community-local/worker/04-studio.mdplans/community-local/frontend/04-studio.mdsurfsense_local/backend/modules/artifacts/formats.pysurfsense_local/backend/modules/artifacts/models.pysurfsense_local/backend/modules/artifacts/schemas.pysurfsense_local/backend/modules/artifacts/service.pysurfsense_local/backend/modules/artifacts/dependencies.pysurfsense_local/backend/modules/artifacts/tasks.pysurfsense_local/backend/modules/artifacts/router.pysurfsense_local/backend/worker/studio/builders/types.pysurfsense_local/backend/worker/studio/builders/util.pysurfsense_local/backend/worker/studio/builders/summary.pysurfsense_local/backend/worker/studio/builders/docx.pysurfsense_local/backend/worker/studio/builders/pptx.pysurfsense_local/backend/worker/studio/builders/xlsx.pysurfsense_local/backend/worker/studio/builders/html_doc.pysurfsense_local/backend/worker/studio/builders/pdf.pysurfsense_local/backend/worker/studio/builders/mindmap.pysurfsense_local/backend/worker/studio/builders/flashcards.pysurfsense_local/backend/worker/studio/builders/quiz.pysurfsense_local/backend/worker/studio/builders/podcast.pysurfsense_local/backend/worker/studio/builders/__init__.pysurfsense_local/backend/worker/studio/tts.pysurfsense_local/backend/worker/studio/visual.pysurfsense_local/backend/worker/studio/gather.pysurfsense_local/backend/worker/studio/generate.pysurfsense_local/backend/worker/studio/persist.pysurfsense_local/backend/worker/studio/pipeline.pysurfsense_local/backend/worker/studio/__init__.pysurfsense_local/backend/worker/notify.pysurfsense_local/backend/api/main.pysurfsense_local/backend/shared/config.pysurfsense_local/backend/shared/queue.pysurfsense_local/backend/pyproject.tomlsurfsense_local/backend/scripts/fetch_kokoro_model.pysurfsense_local/backend/bundling/worker.specsurfsense_local/backend/modules/events/schemas.pysurfsense_local/backend/uv.locksurfsense_local/frontend/src/features/studio/api.tssurfsense_local/frontend/src/features/studio/use-studio.tssurfsense_local/frontend/src/features/studio/studio-dialog.tsxsurfsense_local/frontend/src/features/sources/sources-panel.tsxsurfsense_local/frontend/src/features/dashboard/dashboard-page.tsxsurfsense_local/frontend/src/lib/api.tssurfsense_local/backend/tests/unit/worker/test_studio_builders.pysurfsense_local/backend/tests/unit/worker/test_studio_visual.pysurfsense_local/backend/tests/integration/artifacts/test_routes.pysurfsense_local/backend/tests/integration/worker/test_studio.pysurfsense_local/frontend/src/features/studio/studio-dialog.test.tsx