Add source-grounded multimodal media slots to wiki pages - #626
Conversation
Keep generated media within bounded repository-scoped storage, validate provider inputs and outputs, and make cache reuse content-authenticated. Harden the frontend asset URL boundary and add concurrency, traversal, resource-limit, configuration, and rendering regressions. Verified with the focused wiki/web suites, the frontend test/typecheck/build tiers, pre-commit, and the unit tier.
cb4137b to
a689eb0
Compare
Store the experiment-only SVG beside its excluded guide so the public documentation boundary does not publish an otherwise unlisted static asset. Verified with the public-docs unit tests, a strict MkDocs build, the public boundary checker, and pre-commit.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a689eb03c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (pathname.split("/").some((segment) => segment === "." || segment === "..")) { | ||
| return null; | ||
| } | ||
| return assetUrl(value); |
There was a problem hiding this comment.
Resolve local media URLs against the API origin
When __CODENIB_API_BASE__ points to a different origin, fetchWikiPage correctly loads the page through that API base, but the returned local URI (api/repos/...) is resolved here through the frontend's base path instead. Consequently, both the image preview and “Open generated asset” link request the frontend origin and return 404 even though the asset exists on the backend; backend-relative media should be resolved using the configured API base.
Useful? React with 👍 / 👎.
| elif item.get("url"): | ||
| uri = _validated_remote_asset_url(str(item["url"])) | ||
| mime_type = "image/*" |
There was a problem hiding this comment.
Avoid caching temporary hosted image URLs indefinitely
When an OpenAI-compatible provider returns a signed or otherwise temporary hosted URL, this branch stores the URL in the manifest without downloading the asset or recording an expiry. _read_cached_asset subsequently treats any syntactically valid remote URL as a permanent cache hit, so after the URL expires every later page load keeps returning the broken asset and never calls the provider again; either materialize hosted responses locally or make remote cache entries expire.
Useful? React with 👍 / 👎.
| page_id=str(meta.get("id") or ""), | ||
| title=str(meta.get("title") or meta.get("id") or ""), | ||
| citations=citations, | ||
| relations=[item.__dict__ for item in relations], |
There was a problem hiding this comment.
Serialize slotted relation items without
__dict__
When a freshly generated AgentWiki page has at least one static relation, each item is a RelationItem declared with @dataclass(..., slots=True), so it has no __dict__. This comprehension raises AttributeError while constructing the response, causing the page request to fail before the page is cached; serialize with dataclasses.asdict, explicit fields, or the already-produced evidence metadata instead.
Useful? React with 👍 / 👎.
| citations = _source_citations(slot) | ||
| if citations: | ||
| parts.append("Source citations: " + ", ".join(citations)) |
There was a problem hiding this comment.
Pass actual source evidence to the image provider
For every external provider generation, the constructed prompt contains only citation filenames plus generic purpose text; no source snippets, existing diagram, graph edges, or relation endpoints are sent to the standalone images endpoint. The model therefore cannot follow the slot instructions to use the cited code and graph as its technical source of truth, so potentially hallucinated visuals are presented as source-grounded; include bounded evidence content and structural facts in the generation input.
Useful? React with 👍 / 👎.
| with _ASSET_LOCKS_GUARD: | ||
| lock = _ASSET_LOCKS.get(key) | ||
| if lock is None: | ||
| lock = threading.Lock() | ||
| _ASSET_LOCKS[key] = lock |
There was a problem hiding this comment.
Coordinate asset generation across server processes
When the FastAPI application runs in multiple worker processes, this threading.Lock registry exists independently in each process. Simultaneous first requests for the same uncached page can therefore all pass the cache check and invoke the billed image provider, and their independent image/manifest replacements can interleave and leave a hash mismatch that triggers another regeneration; use a filesystem or other inter-process lock around the cache-check-and-generate transaction.
Useful? React with 👍 / 👎.
Summary
Add a source-grounded multimodal layer to CodeWiki pages. Wiki pages expose deterministic media slots, render a key-free local SVG preview out of the box, and can optionally materialize bounded PNG assets through an OpenAI-compatible image endpoint.
Changes
Type of Change
Testing
Commands and results:
The deselected version assertion also fails on the exact clean base because this machine still has CodeNib 0.2.0 distribution metadata installed while the checkout is 0.2.1; it is unrelated to this PR.
Manual preview:
export CODENIB_WIKI_MEDIA_MODEL=local/svg codenib wiki /path/to/repositoryOpen a local wiki page and look for Multimodal CodeWiki. The local renderer requires no paid model key. Optional provider configuration and its first uncached network call are documented in
docs/experiments/multimodal_codewiki_preview.md.Checklist