Skip to content

fix: parse recipient and content from request text on Node turns - #30

Merged
NeonDaniel merged 3 commits into
NeonGeckoCom:devfrom
OscillateLabsLLC:fix/node-extract-content-fallback
Sep 22, 2026
Merged

NeonDaniel merged 3 commits into
NeonGeckoCom:devfrom
OscillateLabsLLC:fix/node-extract-content-fallback

Conversation

@mikejgray

@mikejgray mikejgray commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #28. On a real Hub, nearly every Node request that reached this skill spoke ErrorDialog ("Something went wrong and I can't send your message").

Cause

CMS_match_message_phrase checks vocab first. When the utterance contains text, sms, or email it returns an exact match with only kind set and never runs _extract_content_sms / _extract_content_email; those run only when no vocab word is present. The Node branch from #28 assumed extraction had already happened, so a vocab match arrived with an empty payload and hit the error path. The mobile path had always handled this by re-running the extractors on the request text; that fallback was not carried into the Node branch.

DraftEmailIntent is a direct Adapt match with no skill_data at all, so it landed in the same error path.

The unit tests in #28 missed this because they hand-build skill_data with recipient and content already filled in.

Change

  • Both Node handlers fall back to the existing extractors on the request text (request from the Common Messaging callback, utterance from an Adapt match) before giving up.
  • _extract_content_sms returned two values on its early exit when its contract is three; every caller unpacks three, so a request without "to" raised ValueError. Pre-existing, also affects the mobile path.
  • Node email requests parse a spoken body. Seen on a Hub: "draft email to emily that says hi" matched DraftEmailIntent, but _extract_content_email only knows subject, so it took "emily that says hi" as the recipient and found no content. _extract_content_sms already parses "that says" and "saying"; the Node email path composes the two. The spoken-address normalization ("sarah at example dot com") is lifted into _parse_email_address so both paths share it; _extract_content_email behavior is unchanged.
  • Tests for a vocab-only SMS match, a vocab-only email match, an Adapt-only email match, "that says" as an email body, a spoken address with "saying", and a vocab match with no recipient.

Scope note

The extractors themselves are unchanged. They still require "to" to find a recipient, so "send a text to Sarah that says I'm late" works and "text Sarah that says I'm late" does not. Widening that grammar is a separate change to shared code.

Verification

  • pytest test/test_skill.py: 17 passed
  • minerva test-resources: 3 passed
  • minerva test-intents --padacioso: 4 passed

CMS_match_message_phrase returns only kind when an sms or email vocab
word matches; the extractors run only when no vocab word is present.
The Node branch assumed extraction had already happened, so nearly
every matched Node request arrived with an empty payload and spoke
ErrorDialog. Fall back to the extractors on the request text, as the
mobile path does. DraftEmailIntent, a direct Adapt match with no
skill_data at all, takes the same fallback via message.data.utterance.

Also fix _extract_content_sms returning two values on its early exit
when the function's contract is three; every caller unpacks three.
The existing Node tests hand-build skill_data with recipient and
content already filled in, which is why the empty-payload path went
unnoticed. Add cases where skill_data carries only kind, and where
DraftEmailIntent delivers no skill_data at all.
Seen on a Hub: "draft email to emily that says hi" matched
DraftEmailIntent, but _extract_content_email only knows "subject", so
it took "emily that says hi" as the recipient and found no content.
_extract_content_sms already parses "that says" and "saying"; compose
the two for Node email requests and share the spoken-address
normalization so "sarah at example dot com" still resolves.
Comment thread __init__.py
Comment on lines +690 to +693
if "dot" in recipient.split():
recipient = recipient.replace(" dot ", ".")
if "at" in recipient.split():
recipient = recipient.replace(" at ", "@").lower()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for later that this should use resource files to support translation, possibly simplifying to regex instead of keyword matching/splitting

@NeonDaniel
NeonDaniel merged commit a0b37d5 into NeonGeckoCom:dev Sep 22, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants