Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ body:
id: pipe-version
attributes:
label: Pipe Version
placeholder: "e.g. 1.0.0"
placeholder: "e.g. 1.2.0"
validations:
required: true

Expand Down
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
## Checklist

- [ ] I have read the [CONTRIBUTING.md](../CONTRIBUTING.md) guidelines
- [ ] All tests pass (`python test_pipe.py` — 234/234 ✓)
- [ ] All tests pass (`python test_pipe.py` — 249/249 ✓)
- [ ] I have added tests for new functionality (if applicable)
- [ ] I have updated `CHANGELOG.md` under `[Unreleased]`
- [ ] I have updated documentation (if applicable)
Expand Down
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,14 @@ htmlcov/

tmpclaude-*
nul

# Auto Claude data directory
.auto-claude/

# Auto Claude generated files
.auto-claude-security.json
.auto-claude-status
.claude_settings.json
.worktrees/
.security-key
logs/security/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Icon sync: correct prefixed model IDs** — `_sync_model_icons()` now discovers the pipe's `function_id` via `type(self).__module__` and writes DB records with the full prefixed ID (e.g. `openrouter_pipe.openai/gpt-4o`) matching what Open WebUI's frontend requests at `/models/model/profile/image`
- **Streaming status event** — the "done" status event is now correctly emitted at the end of streaming responses (async generator wrapper replaces sync generator that could not `await`)
- **Dead provider-icon code removed** — `info.meta.profile_image_url` was included in model dicts returned by `pipes()` but Open WebUI ignores all fields except `id` and `name`; the field has been removed in favour of the new DB-sync approach
- **`pipes()` response always closed** — added `finally: response.close()` to guarantee HTTP connections are returned to the session pool in all code paths (auth errors, JSON decode failures, unexpected exceptions)

## [1.2.0] - 2026-02-17

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pip install -r requirements.txt
### Running Tests

```bash
python test_pipe.py # Unit tests (234 tests)
python test_pipe.py # Unit tests (249 tests)
python integration_test.py # Live API tests (requires OPENROUTER_API_KEY)
```

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ All settings are configurable via **Valves** in the Open WebUI admin panel. Ever
| `FALLBACK_MODELS` | `OPENROUTER_FALLBACK_MODELS` | `""` | Fallback model IDs (comma-separated) |
| `ENABLE_MIDDLE_OUT` | `OPENROUTER_ENABLE_MIDDLE_OUT` | `false` | Middle-out compression for long prompts |
| `ENABLE_CACHE_CONTROL` | `OPENROUTER_ENABLE_CACHE_CONTROL` | `false` | Anthropic cache_control injection |
| `SYNC_PROVIDER_ICONS` | `OPENROUTER_SYNC_ICONS` | `true` | Sync provider icons into Open WebUI's model database |

### Network

Expand Down Expand Up @@ -215,7 +216,7 @@ It also removes `user` when sent as a dict (OWUI format) since OpenRouter expect
OpenRouter-Pipe/
├── openrouter_pipe.py # Main pipe source (install this in Open WebUI)
├── function.json # Open WebUI community manifest (metadata, tags, categories)
├── test_pipe.py # Unit test suite (234 tests)
├── test_pipe.py # Unit test suite (249 tests)
├── integration_test.py # Live API integration tests (47 tests)
├── TESTING.md # Pre-release testing checklist
├── SECURITY.md # Security policy and vulnerability reporting
Expand Down Expand Up @@ -274,7 +275,7 @@ Your API key is incorrect or malformed. Get a valid key from [openrouter.ai/keys
<details>
<summary><strong>"Rate limit exceeded (HTTP 429)"</strong></summary>

You're sending too many requests. Wait a moment and try again. Consider setting `MAX_RETRIES` to `2` or higher for automatic backoff.
You're sending too many requests. Wait a moment and try again, or consider upgrading your OpenRouter plan. Note: `MAX_RETRIES` only retries on network timeouts and connection failures — HTTP rate limit errors are returned immediately without retry.
</details>

<details>
Expand Down
4 changes: 3 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
## Supported Versions

| Version | Supported |
|---------|-----------|| 1.2.x | Yes || 1.1.x | Yes |
|---------|-----------|
| 1.2.x | Yes |
| 1.1.x | Yes |
| 1.0.x | Yes |
| < 1.0 | No |

Expand Down
8 changes: 4 additions & 4 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Manual checklist to verify every Pipe feature before release.
python test_pipe.py
```

Must print **234/234 passed**. If any test fails, **do not release**.
Must print **249/249 passed**. If any test fails, **do not release**.
Comment thread
sena-labs marked this conversation as resolved.
Outdated

---

Expand Down Expand Up @@ -41,7 +41,7 @@ Must print **234/234 passed**. If any test fails, **do not release**.
| # | Action | Expected result |
|---|--------|-----------------|
| 3.1 | Select a model (e.g. `openai/gpt-4o`), type "Hello" with `stream: false` | The response appears all at once, correct text |
| 3.2 | Select a reasoning model (e.g. `anthropic/claude-3.7-sonnet:thinking`) | The response contains `<think>...</think>` blocks followed by content |
| 3.2 | Select a reasoning model (e.g. `deepseek/deepseek-r1`) | The response contains `<think>...</think>` blocks followed by content |

---

Expand Down Expand Up @@ -105,7 +105,7 @@ Must print **234/234 passed**. If any test fails, **do not release**.

| # | Action | Expected result |
|---|--------|-----------------|
| 9.1 | Set `FALLBACK_MODELS = openai/gpt-4o, anthropic/claude-3.5-sonnet` | payload > `"models": ["openai/gpt-4o", "anthropic/claude-3.5-sonnet"]` |
| 9.1 | While using `openai/gpt-4o` as primary, set `FALLBACK_MODELS = anthropic/claude-3.5-sonnet` | payload contains `"models": ["openai/gpt-4o", "anthropic/claude-3.5-sonnet"]` (primary model first, then fallbacks) |
| 9.2 | Leave `FALLBACK_MODELS` empty | No `models` field in payload |

---
Expand Down Expand Up @@ -191,7 +191,7 @@ Must print **234/234 passed**. If any test fails, **do not release**.
## Quick pre-release checklist

```
[ ] python test_pipe.py → 234/234
[ ] python test_pipe.py → 249/249
[ ] python integration_test.py → 47/47 ✓
[ ] Empty API key → clear error
[ ] Valid API key → 340+ models
Expand Down
4 changes: 4 additions & 0 deletions openrouter_pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ def pipes(self) -> List[dict]:
return self._models_cache

headers = self._build_headers(include_content_type=False)
response = None
try:
response = self._session.get(
self.models_url, headers=headers, timeout=self.valves.REQUEST_TIMEOUT
Expand Down Expand Up @@ -313,6 +314,9 @@ def pipes(self) -> List[dict]:
print(f"[OpenRouter Pipe] Model fetch error: {exc}")
traceback.print_exc()
return [{"id": "error", "name": f"Unexpected error: {exc}"}]
finally:
if response is not None:
response.close()
Comment on lines +317 to +319

provider_filter = self._parse_provider_filter()
prefix = self.valves.MODEL_PREFIX or ""
Expand Down
Loading