feat!: inbound media support with FlowChat::Input turn value object#2
Open
thedumbtechguy wants to merge 18 commits into
Open
feat!: inbound media support with FlowChat::Input turn value object#2thedumbtechguy wants to merge 18 commits into
thedumbtechguy wants to merge 18 commits into
Conversation
… object
Every turn is now a FlowChat::Input value object (app.input) with two
independent axes: text and an optional attachment (media/location/contact).
Gateways set context.input to plain text only; a structured turn with no text
sets it to "". The prompt gates on Input#submitted? (text OR attachment), and
Input delegates public String methods to its text so existing validators and
transforms keep working unchanged. app.media is always an Array so multi-
attachment messages never silently drop items.
BREAKING CHANGE: The "$media$", "$location$", and "$contact$" input sentinels
are removed. context.input is always plain text ("" when a turn carries no
text). app.input is now a FlowChat::Input rather than a raw string, and
app.media always returns an Array<FlowChat::Media> (empty when none) instead of
a single item. Branch on app.attachment_type instead of comparing input to a
sentinel string.
Claude-Session: https://claude.ai/code/session_01MgpYCqkxqafrxTp2CUtGii
Left over from removing the input sentinel block; fixes standardrb Layout/EmptyLinesAroundModuleBody. Claude-Session: https://claude.ai/code/session_01MgpYCqkxqafrxTp2CUtGii
Addresses review findings on the FlowChat::Input turn model: - App#screen now treats a screen as answered when its key is present in the session (non-nil), not merely truthy. A caption-less attachment answers with "", which previously failed the `.present?` cache-hit guard and caused the screen to re-ask every turn. Also fixes false/blank transform results looping. - App#prepare_user_input lets an opening message that carries an attachment through to the first screen instead of swallowing it as a bare session-opener; text-only openers keep the existing "wake the flow" behavior. - Input#== coerces a FlowChat::Input operand to its text so two Inputs with equal text compare equal; non-string operands still compare false. - Media#url is memoized so repeated reads don't re-issue the platform lookup. - HTTP gateway validates caller-supplied media_type against Media::CANONICAL_TYPES, falling back to :document. - Mark the historical inbound-media spec/plan as superseded by the Input model. Claude-Session: https://claude.ai/code/session_01MgpYCqkxqafrxTp2CUtGii
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds inbound media support so flows can read the media, location, and contact data users send — previously
app.media/app.location/app.contact_namewere hardcoded tonil, stranding data the WhatsApp/Telegram gateways already parsed. Fills in inbound parsing for Intercom and HTTP, and — as the branch evolved — replaces the old input-sentinel model with a richFlowChat::Inputvalue object so text and attachments are first-class on every turn.The turn is now a value object
Every turn is a
FlowChat::Input(app.input) with two independent axes: text and an optional attachment.app.text→ the turn's text (message body or a media caption). Always a String;""when the turn carried no text (e.g. a caption-less photo).app.attachment_type→:media/:location/:contact/nil— the discriminator to branch on.app.media→ always anArray<FlowChat::Media>(empty when none), so multi-attachment messages never silently drop items.app.location,app.contact,app.contact_name.FlowChat::Media:type(normalized::photo→:image,:voice→:audio),raw_type(platform-native),mime_type,caption,filename,id/file_id, plusurl/downloadto fetch bytes per platform.FlowChat::Inputdelegates public String methods to its text, so existingvalidate/transformlambdas (input.strip,input.to_i,input.blank?) keep working unchanged — and they can now also inspect attachments (input.media.any?). A screen is satisfied wheninput.submitted?— text or an attachment — so a caption-less photo still answers.Platform coverage
get_media_url/download_mediaget_file/file_url/download_file; message-level captions capturedmedia_url/media_type/mime_typeparams"$media$","$location$", and"$contact$"input sentinels are removed.context.inputis always plain text now (""when a turn carries no text); the structured payload lives onrequest.media/request.location/request.contact. Branch onapp.attachment_typeinstead of comparinginputto a sentinel string.app.inputis now aFlowChat::Input, not a raw String (it still behaves like its text for string ops and==).app.mediaalways returns anArray<FlowChat::Media>(empty when none) rather than a single object — iterate uniformly. The interimapp.media_itemsaccessor is gone;app.mediais the list.Conveyed to git-cliff via a
refactor!:commit with aBREAKING CHANGE:footer.Test Plan
standardrbcleanFlowChat::Inputunit tests (text coercion,submitted?,attachment_type, media-always-a-list, string delegation, public-onlyrespond_to?, equality,START)FlowChat::Media(readers, per-platformurl/downloaddispatch, direct-HTTP download, type normalization)input, including multi-attachment and caption casesAppaccessor +media_clientwiring testsDesign spec and implementation plan under
docs/superpowers/.🤖 Generated with Claude Code