Skip to content

fix(photon): harden standalone send retries - #53831

Closed
paul-cch wants to merge 2 commits into
NousResearch:mainfrom
paul-cch:codex/photon-standalone-send-retry-upstream
Closed

fix(photon): harden standalone send retries#53831
paul-cch wants to merge 2 commits into
NousResearch:mainfrom
paul-cch:codex/photon-standalone-send-retry-upstream

Conversation

@paul-cch

Copy link
Copy Markdown

Summary

  • retry standalone Photon text sends when the sidecar reports transient send failures
  • fall back from markdown to stripped plain text after markdown send attempts are exhausted
  • keep retry count and base delay bounded via PHOTON_STANDALONE_SEND_RETRIES and PHOTON_STANDALONE_RETRY_BASE_DELAY_SECONDS

Context

Standalone sends are used by out-of-process delivery paths that reuse an already-running Photon sidecar. A transient sidecar or upstream send failure could previously fail the whole delivery immediately. This adds the same kind of bounded retry behavior expected from connected sends, while keeping markdown delivery as the preferred path.

Tests

  • .venv/bin/python -m pytest tests/plugins/platforms/photon/test_markdown.py -q -> 8 passed
  • .venv/bin/python -m pytest tests/plugins/platforms/photon/test_markdown.py tests/plugins/platforms/photon/test_outbound_media.py tests/plugins/platforms/photon/test_sidecar_lifecycle.py -q -> 21 passed

@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jun 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to the Photon outbound-recovery cluster — #51075 (iMessage outbound recovery), #50974 (degraded sidecar streams), #51897 (shared-line outbound). This PR hardens the standalone out-of-process send path specifically (a different facet), so not a duplicate; reviewers may want to land these together.

@paul-cch
paul-cch force-pushed the codex/photon-standalone-send-retry-upstream branch from 40298e4 to 2c55fa1 Compare June 27, 2026 21:44
@paul-cch

Copy link
Copy Markdown
Author

Landing proof for maintainer merge:

  • Rebased onto current NousResearch/hermes-agent:main and pushed refreshed head 2c55fa11e4f89981d380c07be0569b7a9f17360e.
  • Focused Photon test slice passed: .venv/bin/python -m pytest tests/plugins/platforms/photon/test_markdown.py tests/plugins/platforms/photon/test_outbound_media.py tests/plugins/platforms/photon/test_sidecar_lifecycle.py -q -> 21 passed.
  • Autoreview passed with no accepted/actionable findings.
  • Public model identifier gate passed: candidate diff touches Photon adapter/tests only and contains no model/provider identifier matches.
  • Live Photon/iMessage proof was explicitly waived by the repository owner for this item before merge.

No live Hermes runtime mutation, service restart, live config mutation, credential access, message send, or homelab mirror update was performed as part of this PR landing.

@paul-cch
paul-cch marked this pull request as ready for review June 27, 2026 21:55
Copilot AI review requested due to automatic review settings June 27, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for addressing the standalone Photon delivery path. The current-main premise is valid: plugins/platforms/photon/adapter.py:1699-1706 returns the first failed standalone /send result without retrying.

Problems

  • PR-head plugins/platforms/photon/adapter.py:1641 lets httpx.RequestError escape post_sidecar(), bypassing the retry loop at :1664-1679 and falling through to the outer error handler at :1724. This misses safe connection failures; shared policy classifies connection errors as retryable in gateway/platforms/base.py:2149-2166.
  • The new retry controls at PR-head adapter.py:187-202 are not added to plugins/platforms/photon/plugin.yaml or the README table, despite README.md:108-110 stating all env vars are documented there.
  • The unrelated inbound dedup timing change at PR-head adapter.py:530-539 has no focused test in tests/plugins/platforms/photon/test_inbound.py.

Suggested changes

  • Handle safe transport failures inside the retry path and add a connection-error retry test; preserve the existing no-retry treatment for ambiguous read/write timeouts.
  • Document or re-home the retry settings, and separate or test the inbound dedup semantic change.

Automated hermes-sweeper review.

path: str,
body: Dict[str, Any],
) -> tuple[Optional[Dict[str, Any]], Optional[str]]:
resp = await client.post(f"{base}{path}", json=body, headers=headers)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

httpx.RequestError from this call escapes post_sidecar() and therefore skips post_text_with_retry() entirely. Normalize safe connection failures here so they are evaluated by the retry loop; do not blindly retry read/write timeouts because those may already have delivered the message.

}


def _standalone_retry_count() -> int:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These new runtime controls need documentation and setup metadata. plugins/platforms/photon/README.md:108-110 says all Photon env vars are documented in plugin.yaml, but neither new key is added there or to the README table.

logger.exception("[photon] inbound dispatch failed")
return
if msg_id:
self._mark_seen(msg_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This changes inbound retransmission semantics: an event whose dispatch raises is no longer marked seen. Please add a focused failure-then-redelivery test in tests/plugins/platforms/photon/test_inbound.py, or split this unrelated inbound change from the standalone-send retry fix.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 15, 2026
@paul-cch paul-cch closed this Jul 15, 2026
@paul-cch

Copy link
Copy Markdown
Author

Continued in #65203. GitHub would not allow #53831 to be reopened after its closed head branch was rebased and force-updated; the replacement contains the requested review fixes and links back here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants