Skip to content

Claude/laughing beaver f67164#13

Closed
sena-labs wants to merge 10 commits into
mainfrom
claude/laughing-beaver-f67164
Closed

Claude/laughing beaver f67164#13
sena-labs wants to merge 10 commits into
mainfrom
claude/laughing-beaver-f67164

Conversation

@sena-labs
Copy link
Copy Markdown
Owner

Summary

Related Issue

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to break)
  • Documentation update
  • Refactoring (no functional changes)
  • Test improvement

Changes Made

Testing

  • All unit tests pass (python test_pipe.py — 431/431 ✓)
  • New tests added for the changes
  • Integration tests pass (python integration_test.py) — if applicable
  • CHANGELOG.md updated under [Unreleased]

Screenshots / Demo

Checklist

  • My code follows the project's coding standards
  • I have added docstrings to new public methods
  • My changes do not introduce debug print statements beyond [OpenRouter Pipe] logging
  • No secrets, API keys, or credentials are included in this PR
  • My commit messages follow Conventional Commits

sena-labs and others added 10 commits May 7, 2026 17:12
- Add Ko-fi and PayPal badges to the README header
- Add Support section before License with Ko-fi (primary) and PayPal links
- Update Table of Contents to include Support section
- Update .github/FUNDING.yml to surface Ko-fi and PayPal in GitHub's
  Sponsor button alongside the existing GitHub Sponsors entry

Ko-fi: https://ko-fi.com/ivansena (0% fee)
PayPal: https://paypal.me/SenaIvan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove ruvector.db (1.5MB binary) and local .claude worktree state from
version control and extend .gitignore to cover swarm/vector/cache dirs
(.claude/, .claude-flow/, .swarm/, .remember/, .mypy_cache/, ruvector.db).
These are machine-local artifacts that should never have been committed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI installed bare `requests pydantic`, ignoring the version floors in
requirements.txt. Install from the file so the tested environment enforces
the security-critical `requests>=2.32.4` floor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rmance

Audit-driven hardening of the OpenRouter pipe. Code + tests + metadata.

Security:
- Pin requests>=2.32.4 and pass allow_redirects=False on all calls so a
  misconfigured base URL cannot leak the Authorization header on redirect
  (CVE-2024-35195 family).
- Reject plaintext http:// base URLs except for loopback hosts (SSRF /
  in-transit bearer leakage).
- Add _md_escape_url to percent-encode markdown-breaking characters in
  citation/image URLs (link-injection defence).
- Block data:image/svg+xml and any .svg path from image auto-rendering
  (inline-script XSS), even on trusted CDN hosts.

Output / correctness fixes:
- Image-gen models that return the image as message.content (bare CDN URL or
  data: URI) now render via _looks_like_image_content + an image-CDN
  allow-list, replacing the false-positive-prone bare-URL heuristic.
- Request usage:{include:true} when SHOW_COST_INFO so streaming responses
  actually carry cost.
- Fix HTTPError handler crash when exc.response is None.
- Include OPENROUTER_BASE_URL in the model-cache key (stale-cache fix).
- Route audio transcripts through _insert_citations.
- Retry 502/503/504/429 with proportional backoff, honouring Retry-After.
- Accept SSE lines without a space after "data:".
- Skip bare-string content parts in cache_control injection.

Icon sync:
- Track insert-exhausted models separately from synced so a late OWUI
  registration is still picked up; prune icon-state on cache rebuild.
- Extract _write_model_icon to dedupe the update call sites.

Misc:
- Unify pipe() streaming return to AsyncGenerator.
- HTTPAdapter connection-pool tuning; named constants for retry/backoff/caps.

Test suite expanded to 603 passing (was 252); mypy and pyflakes clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Correct provider-icon count (22 -> 13) across README, function.json was
  already fixed in the metadata commit.
- Update unit-test count to 603 and integration-test count to 44.
- Add SHOW_COST_INFO / COST_CURRENCY valves to the README configuration table.
- Document the new security behaviours (SecretStr decision, http:// rejection,
  SVG block, image-CDN allow-list, redirect disabling, 5xx/429 retry) in README
  Features and SECURITY.md.
- Record all fixes under CHANGELOG [Unreleased], including the http:// base-URL
  breaking change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Recent Open WebUI (open-webui:main) migrated the Models table methods
(get_model_by_id / insert_new_model / update_model_by_id) to async
coroutines. _sync_model_icons called them synchronously, so every read
returned an un-awaited coroutine and every write was a silent no-op — no
provider icon ever reached the database (every model showed OWUI's default).
Confirmed by inspecting the live instance: all openrouter_pipe.* model
records held /static/favicon.png.

Fixes:
- Add _resolve_owui_call: runs a returned coroutine to completion in a
  dedicated thread (pipes() executes inside OWUI's running event loop, so
  asyncio.run on the current loop is impossible) and passes synchronous
  results through unchanged, so the pipe works on both sync and async OWUI.
- Recognise /static/ as a managed/overwritable default icon (OWUI's current
  default for pipe sub-models is /static/favicon.png); previously treated as
  a user-set icon and never overwritten.
- Add the Tencent provider icon (live-verified at /images/icons/Tencent.png);
  14 provider icons total.

Note: OpenRouter only hosts icons for these 14 providers at
/images/icons/; other providers (x-ai, z-ai, nvidia, minimax, …) have no
icon there and fall back to OWUI's default.

Test suite: 622 passing (adds §44 async-OWUI regression + Tencent coverage).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- CONTRIBUTING: requests prerequisite >=2.20 -> >=2.32.4 (matches
  requirements.txt / function.json).
- TESTING: add pre-release checklist sections for cost display
  (SHOW_COST_INFO), image-generation output rendering, and base-URL
  security (https enforcement, 5xx/429 retry).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
OpenRouter's live catalog is ~356 models; "300+" understated it. Updated the
description in the module docstring, function.json, and README to "350+".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Strip UTF-8 BOM from function.json (OWUI's JSON manifest parser rejects
  BOM-prefixed files; json.load() raises JSONDecodeError) and LICENSE
  (cosmetic; consistent encoding).
- Remove the "Feature gallery" placeholder section from README.md — captions
  without actual screenshot images. The dedicated Features section below
  already describes every capability.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 02:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR strengthens the OpenRouter Pipe’s security and reliability while improving UI rendering for image-generation outputs and adding optional cost display. It also updates dependencies, CI installation, and documentation/test coverage to reflect the new behaviors.

Changes:

  • Hardened request handling: enforced safer base URL validation, disabled redirects, bumped requests minimum, and added transparent retries for transient HTTP errors (incl. 429 with Retry-After).
  • Improved output rendering: markdown-escape URLs, block SVG data: rendering, and detect image-gen outputs delivered as message.content (streaming and non-streaming).
  • Improved Open WebUI integration: adapted provider icon syncing to work with OWUI’s async Models API and added extensive regression/unit tests and updated docs/checklists.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
openrouter_pipe.py Core security/retry hardening, cost opt-in, image-gen detection/rendering, async OWUI icon sync compatibility.
test_pipe.py Large expansion of unit/regression coverage for new security and behavior branches.
integration_test.py Minor assertion message cleanup.
requirements.txt Bumps minimum requests version to >=2.32.4.
.github/workflows/tests.yml Installs dependencies via requirements.txt to enforce minimum versions in CI.
CHANGELOG.md Documents new security and behavior changes and bug fixes.
README.md Updates feature list/counts and adds cost display + support section.
SECURITY.md Updates documented security practices (base URL/TLS, redirects, URL escaping, etc.).
TESTING.md Adds manual checklist items for cost display, image output, base URL security; updates test counts.
CONTRIBUTING.md Updates dependency requirements and test-count references.
function.json Updates requirements list and metadata timestamp; removes BOM.
LICENSE Removes BOM.
.gitignore Ignores new local tooling/artifact directories/files.
.github/FUNDING.yml Adds Ko-fi and PayPal funding links.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread openrouter_pipe.py
Comment on lines +480 to +488
if v.startswith("http://"):
# Reject plaintext HTTP except for loopback hosts to prevent
# bearer-token leakage in transit and SSRF to public origins.
host = v[len("http://"):].split("/", 1)[0].split(":", 1)[0].lower()
if host in ("localhost", "127.0.0.1", "::1") or host.endswith(".localhost"):
return v
raise ValueError(
"Base URL must use https:// (plaintext http:// allowed only for localhost)"
)
Comment thread openrouter_pipe.py
Comment on lines +785 to 793
async def _wrap_stream():
try:
for chunk in gen:
yield chunk
finally:
if __event_emitter__:
await __event_emitter__(
{"type": "status", "data": {"description": "", "done": True}}
)
Comment thread CHANGELOG.md
Comment on lines +18 to +20
- **Empty-model guard** — `pipe()` now returns `"OpenRouter Error: No model specified."` instead of forwarding a blank model ID to the upstream API
- **Icon-insert retry cap** — `_sync_model_icons` no longer retries an insert indefinitely; after 3 attempts the model is marked synced to avoid unbounded DB churn when OWUI never registers it
- **Backoff/Retry helpers** — `_backoff_delay()` and `_parse_retry_after()` extracted as static methods; backoff jitter is now proportional (`0.5x–1.5x`) to better distribute load under correlated failures
@sena-labs
Copy link
Copy Markdown
Owner Author

Closing — superseded by direct commits to main per the main-direct workflow. Branch tip diverged + conflicts; all useful changes from this branch are already shipped in main (commits e26da51fc5b411, 2026-05-29 → 2026-05-30) and the v1.10.0 release prep.

@sena-labs sena-labs closed this May 30, 2026
@sena-labs sena-labs deleted the claude/laughing-beaver-f67164 branch May 30, 2026 21:19
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