Skip to content

fix(web-search): recover OpenRouter Perplexity citations from message annotations#40881

Merged
obviyus merged 2 commits intoopenclaw:mainfrom
laurieluo:fix/perplexity-openrouter-citations
Mar 10, 2026
Merged

fix(web-search): recover OpenRouter Perplexity citations from message annotations#40881
obviyus merged 2 commits intoopenclaw:mainfrom
laurieluo:fix/perplexity-openrouter-citations

Conversation

@laurieluo
Copy link
Contributor

@laurieluo laurieluo commented Mar 9, 2026

Summary

Fixes a compatibility issue in web-search on the Perplexity chat-completions compatibility path (OpenRouter), where citations could be lost when responses provide URL citations under choices[*].message.annotations instead of top-level citations.

  • Problem: runPerplexitySearch only read data.citations.
  • Why it matters: users got grounded content but empty/missing citations in tool output when using OpenRouter.
  • What changed:
    • Extended Perplexity response typing to include message.annotations.
    • Added extractPerplexityCitations(...):
      • Prefer top-level citations.
      • Fallback to choices[*].message.annotations[*].url_citation.url.
      • Trim + dedupe URLs.
    • Added regression test for annotations-only responses.
  • What did NOT change (scope boundary):
    • No provider routing changes.
    • No request payload or endpoint changes.
    • No changes to Perplexity native Search API path.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #N/A
  • Related #N/A

User-visible / Behavior Changes

web_search with Perplexity OpenRouter-compatible responses now returns citations when sources are provided via message.annotations (fallback path), instead of returning empty citations.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS (local dev)
  • Runtime/container: local gateway + local tests
  • Model/provider: Perplexity sonar/sonar-pro/sonar-pro-search via OpenRouter
  • Integration/channel (if any): Gateway tool execution
  • Relevant config (redacted): tools.web.search.provider=perplexity, OpenRouter key configured

Steps

  1. Configure web_search with Perplexity/OpenRouter chat_completions path.
  2. Run a search where response citations are only in choices[*].message.annotations.
  3. Inspect tool result citations.

Expected

  • citations should include URLs from annotations (deduplicated) when top-level citations are absent.

Actual

  • Before fix: citations could be empty on the chat_completions compatibility path (OpenRouter).
  • After fix: citations correctly populated from annotations fallback.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Runtime before (same query/provider/model)

{
  "query": "Iran latest situation today March 2026 nuclear talks military tensions sanctions",
  "provider": "perplexity",
  "model": "perplexity/sonar",
  "externalContent": {
    "untrusted": true,
    "source": "web_search",
    "provider": "perplexity",
    "wrapped": true
  },
  "content": "<omitted>",
  "citations": []
  }

Runtime after (same query/provider/model)

{
  "query": "Iran latest situation today March 2026 nuclear talks military tensions sanctions",
  "provider": "perplexity",
  "model": "perplexity/sonar",
  "externalContent": {
    "untrusted": true,
    "source": "web_search",
    "provider": "perplexity",
    "wrapped": true
  },
  "content": "<omitted>",
  "citations": [
    "https://timesofindia.indiatimes.com/videos/international/explosions-shake-tel-aviv-as-iran-launches-khorramshahr-fattah-missiles-in-new-barrage/videoshow/129306402.cms",
    "https://www.democracynow.org/2026/3/5/iran_state_defense",
    "https://www.foxnews.com/video/6390605040112",
    "https://www.euronews.com/video/2026/03/03/europe-today-iran-war-intensifies-as-trump-signals-prolonged-fight",
    "https://globalnews.ca/video/11712842/middle-east-analyst-on-u-s-military-action-in-iran"
  ]
}

Test evidence

  • pnpm test src/agents/tools/web-tools.enabled-defaults.test.ts
  • Result: 44 passed / 44 total
  • Includes regression case:
    falls back to message annotations when top-level citations are missing

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • Unit/regression test for annotations-only response.
    • Manual runtime check with Perplexity/OpenRouter response showing non-empty citations.
  • Edge cases checked:
    • Top-level citations still preferred when present.
    • Duplicate annotation URLs are deduplicated.
  • What you did not verify:
    • Full cross-provider matrix beyond Perplexity/OpenRouter citation parsing path.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly:
    • Revert this PR commit.
  • Files/config to restore:
    • src/agents/tools/web-search.ts
    • src/agents/tools/web-tools.enabled-defaults.test.ts
  • Known bad symptoms reviewers should watch for:
    • Missing citations in Perplexity/OpenRouter tool output.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk:
    • Unexpected annotation shapes from upstream responses.
    • Mitigation:
      • Defensive optional parsing + fallback to existing top-level citations behavior.

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Mar 9, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 9, 2026

Greptile Summary

This PR fixes a bug where web_search via the Perplexity/OpenRouter chat_completions compatibility path returned empty citations when the upstream response placed URLs in choices[*].message.annotations instead of the top-level citations field.

Changes made:

  • Adds annotations to the PerplexitySearchResponse type to reflect the OpenRouter response shape
  • Introduces extractPerplexityCitations, which prefers top-level data.citations and falls back to annotation-level URLs, with URL trimming and deduplication via Set
  • Adds a focused regression test verifying the fallback path, deduplication, and that the top-level citations priority is preserved

Safety: Change is fully backward-compatible — no request-path, routing, or payload changes.

Confidence Score: 5/5

  • This PR is safe to merge — it is a small, backward-compatible bug fix with no routing, auth, or external API contract changes.
  • The logic is well-contained in a pure helper function with clear fallback semantics. Existing top-level citations path is fully preserved. Annotations fallback is guarded by type checking, URL normalization, and deduplication. Focused regression test covers the new code path including duplicate-URL handling. Risk of regression is very low.
  • No files require special attention.

Last reviewed commit: a6fc929

@laurieluo laurieluo changed the title fix(web_search): recover OpenRouter Perplexity citations from message annotations fix(web-search): recover OpenRouter Perplexity citations from message annotations Mar 9, 2026
@laurieluo
Copy link
Contributor Author

cc @obviyus for visibility — this is a small backward-compatible fix for the Perplexity/OpenRouter citation fallback path.

@obviyus obviyus self-assigned this Mar 10, 2026
@obviyus obviyus force-pushed the fix/perplexity-openrouter-citations branch from a6fc929 to 66c8bb2 Compare March 10, 2026 05:06
@obviyus obviyus merged commit cf9db91 into openclaw:main Mar 10, 2026
25 of 26 checks passed
@obviyus
Copy link
Contributor

obviyus commented Mar 10, 2026

Merged via squash.

Thanks @laurieluo!

frankekn pushed a commit to urianpaul94/openclaw that referenced this pull request Mar 10, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
BunsDev pushed a commit to rixau/openclaw that referenced this pull request Mar 10, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
mukhtharcm pushed a commit to hnykda/openclaw that referenced this pull request Mar 10, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
@laurieluo laurieluo deleted the fix/perplexity-openrouter-citations branch March 10, 2026 07:45
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 10, 2026
* main: (43 commits)
  docs: add openclaw#42173 to CHANGELOG — strip leaked model control tokens (openclaw#42216)
  Agents: align onPayload callback and OAuth imports
  docs: add Tengji (George) Zhang to maintainer table (openclaw#42190)
  fix: strip leaked model control tokens from user-facing text (openclaw#42173)
  Changelog: add unreleased March 9 entries
  chore: add .dev-state to .gitignore (openclaw#41848)
  fix(agents): avoid duplicate same-provider cooldown probes in fallback runs (openclaw#41711)
  fix(mattermost): preserve markdown formatting and native tables (openclaw#18655)
  feat(acp): add resumeSessionId to sessions_spawn for ACP session resume (openclaw#41847)
  ACPX: bump bundled acpx to 0.1.16 (openclaw#41975)
  mattermost: fix DM media upload for unprefixed user IDs (openclaw#29925)
  fix(msteams): use General channel conversation ID as team key for Bot Framework compatibility (openclaw#41838)
  fix(mattermost): read replyTo param in plugin handleAction send (openclaw#41176)
  fix(sandbox): pass real workspace to sessions_spawn when workspaceAccess is ro (openclaw#40757)
  fix(ui): replace Manual RPC text input with sorted method dropdown (openclaw#14967)
  CI: select Swift 6.2 toolchain for CodeQL (openclaw#41787)
  fix(agents): forward memory flush write path (openclaw#41761)
  fix(telegram): move network fallback to resolver-scoped dispatchers (openclaw#40740)
  fix(security): harden replaceMarkers() to catch space/underscore boundary marker variants (openclaw#35983)
  fix(web-search): recover OpenRouter Perplexity citations from message annotations (openclaw#40881)
  ...
aiwatching pushed a commit to aiwatching/openclaw that referenced this pull request Mar 10, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Moshiii pushed a commit to Moshiii/openclaw that referenced this pull request Mar 11, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Moshiii pushed a commit to Moshiii/openclaw that referenced this pull request Mar 11, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
dominicnunez pushed a commit to dominicnunez/openclaw that referenced this pull request Mar 11, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Ruijie-Ysp pushed a commit to Ruijie-Ysp/clawdbot that referenced this pull request Mar 12, 2026
… annotations (openclaw#40881)

Merged via squash.

Prepared head SHA: 66c8bb2
Co-authored-by: laurieluo <89195476+laurieluo@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants