Question
Should Asset grow content access, a media type, and a durability mechanism — so that user-supplied inbound files are first-class in a Conversation — or is that deliberately out of scope for the shared interface?
Raising as an RFC rather than a PR, because the answer determines whether downstream implementations should be extending Asset or building alongside it, and it would be better to settle that once than to have several implementations diverge.
What exists today
Asset : LlmReferenceProvider, StableIdentified, Timestamped, with a persistent() flag. Conversation : AssetView derives assets from its assetTracker plus any message implementing AssetView, deduplicated by id, tracker taking priority.
So an asset is defined by being referenceable by the LLM, and the model is derived — assets come from messages.
Where it doesn't reach
Building a chat surface that accepts arbitrary user-uploaded files (images, PDFs, CSVs, data exports), four gaps showed up. Each is arguably by design; that's the question.
-
No content access. An Asset yields an LLM reference. There is no way for a non-LLM consumer to obtain the bytes. Anything that must inspect content — sniff a format, parse a table, checksum a file — cannot work through the interface.
-
No media type, size, or content hash. Nothing to route on, nothing to bound cost with, nothing to recognise a re-upload of identical content by.
-
persistent() is declared, but no persistence mechanism exists. The interface anticipates that an asset might be durable and leaves the how open. Reasonable, but it means "this asset is persistent" is currently unfalsifiable from inside the interface.
-
Direction of travel is outbound. Assets are derived from messages — things already produced and already understood. A user upload is inbound: it arrives before anything knows what it is, and may never be understood at all. AssetView has no way to represent "present, stored, intent unresolved".
Options
A. Leave Asset as-is. It stays the LLM-facing abstraction. Storage, media types and durability belong to whatever persistence layer an application already has, and an inbound file surfaces as an Asset once it is understood well enough to be referenced. Downstream builds alongside, and nothing in the shared interface obliges an in-memory Conversation to answer questions it structurally cannot.
B. Widen Asset. Add optional content access and media-type metadata, and define what persistent() actually promises. More capable out of the box, at the cost of every implementation — including purely in-memory ones with nowhere to put bytes — having to answer for them.
C. A sibling abstraction. Leave Asset alone and introduce something inbound-shaped next to it, with Asset remaining the projection once an inbound file becomes referenceable.
Our own reading leans towards A, on the grounds that the current shape looks deliberate rather than incomplete — persistent() existing with no mechanism reads as an intentional seam. But that is a guess about intent, which is exactly what this issue is asking.
What would help
Mainly: was the outbound, LLM-facing framing a deliberate boundary? If so it would be worth saying so in the KDoc, since the natural first instinct on encountering Asset is to reach for it to model uploads, and discovering the gap takes a while.
Question
Should
Assetgrow content access, a media type, and a durability mechanism — so that user-supplied inbound files are first-class in aConversation— or is that deliberately out of scope for the shared interface?Raising as an RFC rather than a PR, because the answer determines whether downstream implementations should be extending
Assetor building alongside it, and it would be better to settle that once than to have several implementations diverge.What exists today
Asset : LlmReferenceProvider, StableIdentified, Timestamped, with apersistent()flag.Conversation : AssetViewderivesassetsfrom itsassetTrackerplus any message implementingAssetView, deduplicated by id, tracker taking priority.So an asset is defined by being referenceable by the LLM, and the model is derived — assets come from messages.
Where it doesn't reach
Building a chat surface that accepts arbitrary user-uploaded files (images, PDFs, CSVs, data exports), four gaps showed up. Each is arguably by design; that's the question.
No content access. An
Assetyields an LLM reference. There is no way for a non-LLM consumer to obtain the bytes. Anything that must inspect content — sniff a format, parse a table, checksum a file — cannot work through the interface.No media type, size, or content hash. Nothing to route on, nothing to bound cost with, nothing to recognise a re-upload of identical content by.
persistent()is declared, but no persistence mechanism exists. The interface anticipates that an asset might be durable and leaves the how open. Reasonable, but it means "this asset is persistent" is currently unfalsifiable from inside the interface.Direction of travel is outbound. Assets are derived from messages — things already produced and already understood. A user upload is inbound: it arrives before anything knows what it is, and may never be understood at all.
AssetViewhas no way to represent "present, stored, intent unresolved".Options
A. Leave
Assetas-is. It stays the LLM-facing abstraction. Storage, media types and durability belong to whatever persistence layer an application already has, and an inbound file surfaces as anAssetonce it is understood well enough to be referenced. Downstream builds alongside, and nothing in the shared interface obliges an in-memoryConversationto answer questions it structurally cannot.B. Widen
Asset. Add optional content access and media-type metadata, and define whatpersistent()actually promises. More capable out of the box, at the cost of every implementation — including purely in-memory ones with nowhere to put bytes — having to answer for them.C. A sibling abstraction. Leave
Assetalone and introduce something inbound-shaped next to it, withAssetremaining the projection once an inbound file becomes referenceable.Our own reading leans towards A, on the grounds that the current shape looks deliberate rather than incomplete —
persistent()existing with no mechanism reads as an intentional seam. But that is a guess about intent, which is exactly what this issue is asking.What would help
Mainly: was the outbound, LLM-facing framing a deliberate boundary? If so it would be worth saying so in the KDoc, since the natural first instinct on encountering
Assetis to reach for it to model uploads, and discovering the gap takes a while.