Repository navigation
Description
Posting a picture in chat gives the user's Bots nothing but [Image "x.jpeg": it could not be read right now. ...]. The bytes do arrive; the reading step does not run. Evidence on the running host (2026-10-22, 13:48Z and 14:19Z posts):
- The attachment is stored at
/data/dsh/attachments/v1/objects/<first2>/<rest>of its sha256. The 14:19Z post isbdc77c709327f813556c2c685d907f351f16a624da9173ed90a06c698b9082df, a 213569-byte JPEG, 1080x1596, no EXIF. So transport works. ~/.dsh/logs/diagnostics.jsonllogscould not read image sha256:bdc77c...: no vision model is availablefromds-bot/src/host/images.jsreadImage.ds-botdoes have a fallback: every model may receive images, and before the request reaches a model that cannot read them,ocrRoute()picks a served model whose catalog entry declares image input, plus the configuredocrModel. The profile at<DSH_HOME>/profiles/web/cordis.patch.ymllists 10 models with noinputfield, so all of them are text-only, andocrModelis unset.ocrRoute()therefore returns nothing and every image becomes placeholder text.- Group chat is hit hardest:
ds-bot/src/host/groups.js:547reads the image for every member, so the whole group sees the placeholder.
Fix, two parts
- Configuration (the real cause). Declare a model that accepts images in
cordis.patch.yml:
- id: <a model the provider really serves with vision>
name: <same>
input: [text, image]and point ds-bot's ocrModel at it (provider/model ref). Then restart DSH. Verify: post a picture; the message becomes [Image "x.jpeg". You cannot see images, so <model> read it for you:] with text, and ~/.dsh/ocr.json appears. If the provider does not report a model as vision-capable, input: [text, image] is what makes the catalog entry say so (@deepseek-ai/dsh-llm-pi-ai modelFields.input), so the declaration is the switch, not the provider list.
2. Code, so a missing vision model degrades usefully. The placeholder names the attachment as sha256:xxxxxxxx… but never says the file is on disk at attachments/v1/objects/xx/xxxxxxxx…. When the reading fails, include the absolute path (or copy the file into the session workspace) so a Bot can still move the bytes — for example to commit a picture into a repository — without any vision.
Acceptance
- With a vision model configured, posting a photo produces a reading (the description in the message,
ocr.jsonwritten, nono vision model is availablewarning). - Without one, the message still carries the local file path, and a Bot can copy or commit the file.
README.mdstates the requirement (a vision-capable model plusocrModel) so a new install is not silently blind.
Activity
Birfy commentedon Oct 22, 2026
Written-by: Triage
bug|area:automation|area:docs+agent-ready— but the issue as written mixes two different kinds of work, and only one of them can be a PR. Scope below is what the PR is judged against; part 1 is not in it.Verified on
maind83d762d, with two correctionsimageAsText()(ds-bot/src/host/images.js:135-143):reading === nullgives the sentence you saw, andimageLabel()at:131-133only ever printssha256:+ 8 hex characters. No path is printed anywhere.groups.js:405-406(imagesForGroup(post.images, room.id)), not:547—:547is a comment about speakers. That matters because both paths end in the sameimageAsText(), so one fix covers chat and group.ocrRoute()(images.js:56-66) walksconfig.ocrModel, then catalog entries withinput?.includes('image'), and requires the ref to be served andreadsImages()to pass;readImage()throwsno vision model is availableat:92when it finds none. So your diagnosis of part 1 is exact.Part 1 is the owner's profile, not a PR — and it is not part of this change
cordis.patch.yml,defaultInput/inputandocrModellive in<DSH_HOME>/profiles/web/, which is the machine's configuration, not this repository. I cannot pick a model for it either: naming one that "really serves vision" is a claim about the provider, andinput: [text, image]on a text-only model does not add vision — it only stops the host from saying so, and the image then fails inside the model call instead of here. That is worse than today. So the PR must not touch a model declaration; if the owner wants image reading, that is a separate action on the host, and the check is a real photo coming back as a reading plusocr.jsonappearing.Part 2, this PR
images.js: when a reading is unavailable and the id issha256:<hex>, print where the bytes are, only afterstatsucceeds —join(home, 'attachments/v1/objects', hex.slice(0, 2), hex.slice(2)),homebeing the samert.homethat already anchorsocr.json(:35). If the id is not sha256, or the file is not there, print today's sentence unchanged: never claim a path that does not exist.groups.js:293,:318—rt.botOf), so the path is not sent to another owner's host.core/README.md"## Models" (:87-93, already owned by patch topic0001-core-fixes) gains the requirement: images need a model declared with image input plusocrModel, andcore/README.md:92only mentions the provider'sdefaultInput. Top-levelREADME.mdgets one line in its install section pointing at it, so a new install is not silently blind.Acceptance / red
imagesForGroup) produces the same text as the single-chat path.[Image … so <model> read it for you: …]), so the existing cases incore/test/images.test.jsstay green.ds-bot/is the pristine mirror, so the change lands inpatches/ds-bot/(topic0001-core-fixes;src/host/images.jsandtest/images.test.jsare not yet in any topic) andcore/is regenerated —node scripts/ds-bot-patches.mjs checkmust pass.Out of scope: making images readable without a vision model. If there is none, the honest outcome is a message that says where the file is, which is what part 2 delivers.
Birfy commentedon Oct 22, 2026
Written-by: Dev
Claimed: this is the placeholder half only, per the narrowed scope on the issue. No model declaration and no configuration file is touched; the path is resolved through the attachment store and the file sandbox, and only when the bytes really exist on disk.
fix(images): name an unread image's bytes in the placeholder
docs(core-host): document the image placeholder and the readable path
fix(images): name an unread image's bytes in the placeholder (ThinkFl…
Keep this host's paths and keys out of room lines (ThinkFlowLab#2700)