Improvements to documentation with regards to remote OAuth issue#97
Improvements to documentation with regards to remote OAuth issue#97mojomast wants to merge 3 commits intoMirrowel:mainfrom
Conversation
…rt forwarding Add comprehensive documentation explaining OAuth token authentication issue when proxy runs on remote hosts and the SSH port forwarding solution. Updates README.md, DOCUMENTATION.md, and Deployment guide.md with detailed examples and workflows. - Added new section in README.md explaining remote host OAuth problem - Updated DOCUMENTATION.md with section 2.6.4 on SSH port forwarding - Modified Deployment guide.md to standardize SSH tunnel command ordering - Added troubleshooting entry for OAuth callback failures on VPS - Included both SSH tunnel and local authentication export approaches
|
I'm beginning my review of the documentation improvements for remote OAuth authentication. I'll examine the new SSH port forwarding instructions and the troubleshooting guide to ensure everything is clear and accurate. |
There was a problem hiding this comment.
Overall Assessment
This PR provides excellent and much-needed documentation for handling OAuth authentication on remote hosts. The explanation of why localhost callbacks fail on a VPS and how to use SSH port forwarding as a solution is clear and will save users a lot of troubleshooting time.
Architectural Feedback
The approach of documenting both the SSH tunnel method and the "local auth + export" alternative is great, as it covers different deployment styles (persistent VPS vs. stateless cloud).
Key Suggestions
- Consistency: I noticed the iFlow port (
11451) was missing from a few of the aggregate examples inREADME.md. Keeping all three ports (8085, 51121, 11451) in the "all-at-once" examples ensures all providers are covered. - Headless VPS Context: I suggested a small clarification regarding the "opening browser" behavior, as users on headless servers will typically need to copy-paste a URL manually.
- Actionability: Added a suggestion to include the specific
--exportflag in the alternative workflow section to make it easier for users to follow.
Questions for the Author
None. The documentation is very straightforward and helpful.
This review was generated by an AI assistant.
|
|
||
| If SSH port forwarding is not feasible: | ||
| 1. Complete OAuth flows locally on your machine | ||
| 2. Export credentials to environment variables using credential tool's export feature |
There was a problem hiding this comment.
It might be helpful to include the actual command for exporting credentials here, e.g., python -m rotator_library.credential_tool --export, so users don’t have to hunt for it.
| ssh -L 51121:localhost:51121 user@your-vps-ip # For Antigravity | ||
| ssh -L 8085:localhost:8085 user@your-vps-ip # For Gemini CLI | ||
| ssh -L 11451:localhost:11451 user@your-vps-ip # For iFlow | ||
| ssh -L 51121:localhost:51121 user@your-vps-ip # For Antigravity |
There was a problem hiding this comment.
There is a slight formatting inconsistency with the indentation of the comment here (triple space vs single space on other lines).
| ssh -L 51121:localhost:51121 user@your-vps-ip # For Antigravity | |
| ssh -L 51121:localhost:51121 user@your-vps-ip # For Antigravity |
|
|
||
| **Complete Workflow:** | ||
|
|
||
| 1. **Establish SSH tunnel** (keep this connection open): |
There was a problem hiding this comment.
The iFlow port (11451) is missing from this example, while it is included in the examples just above (line 809). Adding it here would ensure consistency.
| 1. **Establish SSH tunnel** (keep this connection open): | |
| ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip |
| python -m rotator_library.credential_tool | ||
| ``` | ||
|
|
||
| 3. **Complete OAuth authentication**: |
There was a problem hiding this comment.
On many remote servers (especially headless VPS instances), the credential tool cannot automatically open a browser window. It usually prints a URL for the user to copy. Clarifying this (e.g., "The tool will provide a URL to open in your local browser") would be more accurate for VPS users.
| | All keys on cooldown | All keys failed recently; check `logs/detailed_logs/` for upstream errors | | ||
| | Model not found | Verify format is `provider/model_name` (e.g., `gemini/gemini-2.5-flash`) | | ||
| | OAuth callback failed | Ensure callback port (8085, 51121, 11451) isn't blocked by firewall | | ||
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip` | |
There was a problem hiding this comment.
The iFlow port (11451) is also missing from this troubleshooting entry. Including it would ensure all OAuth providers are covered.
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip` | | |
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip` | |
…mini compatibility This update restructures how JSON schemas are sanitized before being sent to LLM providers to improve model acceptance rates. - Split validation keywords into specific groups: those stripped only for Claude (which Gemini supports) and those rejected by all models. - Preserved validation keywords for Gemini that were previously stripped, such as `pattern`, `format`, `minItems`, and `not`. - Expanded the list of globally stripped meta-keywords to include `$comment`, `$dynamicRef`, and conditional logic (`if`/`then`/`else`) to prevent API errors across all providers.
Implements a custom provider for Hatz AI that translates between OpenAI Chat Completions format (used by clients like Claude Code) and Hatz's Responses API (/v1/openai/responses). Key features: - Bidirectional message format conversion (Chat Completions <-> Responses API) - Streaming SSE translation (response.output_text.delta -> delta.content, etc.) - Tool calling support with proper function_call/function_call_output mapping - reasoning_effort -> reasoning.effort passthrough for thinking models - Handles response.incomplete events (maps to finish_reason: length) - X-API-Key authentication (not Bearer token) - Dynamic model discovery from /v1/chat/models endpoint Also fixes: - Anthropic /v1/messages endpoint now skips auth when PROXY_API_KEY unset (matching existing OpenAI endpoint behavior) - Adds Hatz configuration section to .env.example
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughA new Hatz AI provider implementation is introduced alongside documentation and configuration updates for remote host deployment via SSH port forwarding. The proxy now supports optional API key verification, and schema cleaning logic for the Antigravity provider is enhanced to handle additional keyword groups conditionally based on target model. Changes
Sequence DiagramsequenceDiagram
participant Client
participant HatzProvider
participant Hatz_API
participant OpenAI_Format
Client->>HatzProvider: acompletion() with OpenAI format
HatzProvider->>HatzProvider: Translate messages & tools to Hatz format
HatzProvider->>Hatz_API: POST /v1/openai/responses (SSE stream)
Hatz_API-->>HatzProvider: Streaming events (text, function_calls, etc.)
HatzProvider->>HatzProvider: Parse SSE chunks
HatzProvider->>OpenAI_Format: Translate to OpenAI streaming chunks
OpenAI_Format-->>Client: Yield OpenAI ModelResponse chunks
Hatz_API-->>HatzProvider: Stream complete
HatzProvider->>Client: Return final completion with usage data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/rotator_library/providers/antigravity_provider.py (2)
746-759: 🧹 Nitpick | 🔵 TrivialRemove duplicated ownership of
examplesacross keyword sets.
examplesis in bothmeta_keywordsandvalidation_keywords_all_models. Because meta keywords are filtered first (Line [910]), the all-model entry is unreachable and can drift over time.Also applies to: 791-813
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/rotator_library/providers/antigravity_provider.py` around lines 746 - 759, The symbol "examples" is duplicated between meta_keywords and validation_keywords_all_models making the latter unreachable; remove "examples" from validation_keywords_all_models (keep it in meta_keywords) and likewise remove any other duplicated keyword entries in the nearby validation_keywords_* sets referenced around the same region so ownership is single-source (check the block where validation_keywords_all_models and related validation_keywords_* are defined and delete duplicated entries such as "examples" there).
918-923:⚠️ Potential issue | 🟠 MajorPreserving Gemini-only keys currently skips recursive schema cleaning.
When
for_geminiisTrue, this branch copiesvalueas-is and exits. For structured keys added here (not,prefixItems), nested schemas bypass_clean_claude_schema, so nested$ref/default/examplescan leak through unexpectedly.Proposed fix
# Strip Claude-only keywords when not targeting Gemini if key in validation_keywords_claude_only: if for_gemini: - # Gemini accepts these - preserve them - cleaned[key] = value + # Gemini accepts these - preserve keyword, but still clean nested schemas + if isinstance(value, dict): + cleaned[key] = _clean_claude_schema(value, for_gemini) + elif isinstance(value, list): + cleaned[key] = [ + _clean_claude_schema(item, for_gemini) + if isinstance(item, dict) + else item + for item in value + ] + else: + cleaned[key] = value # For Claude: skip - not supported continueAlso applies to: 786-788
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/rotator_library/providers/antigravity_provider.py` around lines 918 - 923, The branch that preserves Gemini-only keys in validation_keywords_claude_only currently copies value as-is and uses continue, which skips recursive cleaning by _clean_claude_schema and lets nested $ref/default/examples pass through; update the handling in the loop over keys so that when for_gemini is True you still recursively clean structured schema values (e.g., for keys like "not" call _clean_claude_schema(value) and for "prefixItems" iterate and clean each item) before assigning to cleaned[key], and only skip/continue immediately for simple scalar keys that do not require recursion; keep the original skip behavior for Claude (when for_gemini is False) so those keys are omitted.
♻️ Duplicate comments (2)
README.md (2)
828-830:⚠️ Potential issue | 🟡 MinorBrowser behavior wording is too absolute for VPS/headless environments.
“The credential tool will open a browser window” is not always true remotely; usually a URL is printed for manual opening on the local machine.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 828 - 830, Rewrite the absolute sentence "The credential tool will open a browser window" to a conditional/softer wording that covers headless/VPS cases (e.g., "The credential tool will attempt to open a browser window; on headless or remote systems it will instead print a URL for you to open manually on your local machine"), and add a short note after the callback sentence clarifying that SSH-tunneled callbacks are forwarded to the local machine and may require copying the printed URL if no GUI is available.
816-819:⚠️ Potential issue | 🟡 MinorInclude iFlow port (
11451) in the workflow and troubleshooting tunnel commands.Both commands currently forward only
8085and51121, but iFlow callbacks use11451, so remote OAuth for iFlow can still fail with the provided commands.🛠️ Suggested doc correction
- ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip + ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip-| OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip` | +| OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip` |Also applies to: 1043-1043
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 816 - 819, Update the SSH tunnel instructions to also forward the iFlow callback port 11451 so remote OAuth works: add an additional -L 11451:localhost:11451 to the existing ssh tunnel examples (the lines that currently show -L 8085:localhost:8085 -L 51121:localhost:51121) and mirror this change in the troubleshooting tunnel command(s); keep the note about keeping the connection open and mention that 11451 is required for iFlow callbacks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@DOCUMENTATION.md`:
- Line 240: The subsection "2.6.4. Remote Host Authentication (SSH Port
Forwarding)" is placed before "2.6.3", breaking the document order; locate the
heading text "2.6.4. Remote Host Authentication (SSH Port Forwarding)" and
either renumber it to "2.6.3" (and adjust any subsequent section numbers) or
move that entire subsection below the existing "2.6.3" entry so numbering is
sequential, and update any in-doc cross-references that point to "2.6.4"
accordingly.
In `@README.md`:
- Around line 816-823: Add a blank line before each fenced code block under the
numbered list items "Establish SSH tunnel" and "Run the credential tool on the
VPS" so the Markdown has an empty line immediately above each ```bash fence;
update the two list entries that contain the code fences to insert a blank line
before their opening ```bash to satisfy MD031 (blanks-around-fences).
- Line 1028: The link fragment "#remote-host-deployment-ssh-port-forwarding"
points to a <summary> text (not a true heading) so it doesn't resolve; fix by
making the target a real anchor: either convert the existing <summary> block
"Remote Host Deployment (SSH Port Forwarding)" into a proper Markdown heading
(e.g., prefix with "##") or add an explicit HTML anchor immediately before that
section (e.g., <a name="remote-host-deployment-ssh-port-forwarding"></a>), then
update/keep the link text "See the [Remote Host Deployment (SSH Port
Forwarding)](`#remote-host-deployment-ssh-port-forwarding`)..." to point to that
valid anchor.
In `@src/proxy_app/main.py`:
- Around line 706-708: The current check silently allows open access when
PROXY_API_KEY is empty (the conditional using PROXY_API_KEY with "if not
PROXY_API_KEY: return x_api_key or auth"), which must be changed to require an
explicit opt-in flag; update this branch to only bypass authentication when a
dedicated environment flag (e.g., PROXY_ALLOW_UNAUTHENTICATED or
PROXY_AUTH_DISABLED) is set to a truthy value, otherwise treat a missing
PROXY_API_KEY as a configuration error and deny/require authentication (return a
failure rather than implicitly returning x_api_key or auth); locate and modify
the conditional that references PROXY_API_KEY, x_api_key, and auth to enforce
the explicit opt-in flag check and add a clear error/deny path when
PROXY_API_KEY is unset and the opt-in is not enabled.
In `@src/rotator_library/providers/hatz_provider.py`:
- Around line 73-75: The current parsing uses split("/")[-1] which drops path
segments; instead keep the full model ID for routing and dedup by setting
model_name to the full model string (not the last segment) and still append
models using the existing logic (models.append(model if "/" in model else
f"hatz/{model}")); for environment variable keys, derive a safe identifier from
the full model (e.g., replace "/" with "_" or another sanitizer) and add that
sanitized string to env_var_ids so uniqueness is preserved while producing a
valid env var name — update the references to model_name, models.append(...),
and env_var_ids.add(...) accordingly.
---
Outside diff comments:
In `@src/rotator_library/providers/antigravity_provider.py`:
- Around line 746-759: The symbol "examples" is duplicated between meta_keywords
and validation_keywords_all_models making the latter unreachable; remove
"examples" from validation_keywords_all_models (keep it in meta_keywords) and
likewise remove any other duplicated keyword entries in the nearby
validation_keywords_* sets referenced around the same region so ownership is
single-source (check the block where validation_keywords_all_models and related
validation_keywords_* are defined and delete duplicated entries such as
"examples" there).
- Around line 918-923: The branch that preserves Gemini-only keys in
validation_keywords_claude_only currently copies value as-is and uses continue,
which skips recursive cleaning by _clean_claude_schema and lets nested
$ref/default/examples pass through; update the handling in the loop over keys so
that when for_gemini is True you still recursively clean structured schema
values (e.g., for keys like "not" call _clean_claude_schema(value) and for
"prefixItems" iterate and clean each item) before assigning to cleaned[key], and
only skip/continue immediately for simple scalar keys that do not require
recursion; keep the original skip behavior for Claude (when for_gemini is False)
so those keys are omitted.
---
Duplicate comments:
In `@README.md`:
- Around line 828-830: Rewrite the absolute sentence "The credential tool will
open a browser window" to a conditional/softer wording that covers headless/VPS
cases (e.g., "The credential tool will attempt to open a browser window; on
headless or remote systems it will instead print a URL for you to open manually
on your local machine"), and add a short note after the callback sentence
clarifying that SSH-tunneled callbacks are forwarded to the local machine and
may require copying the printed URL if no GUI is available.
- Around line 816-819: Update the SSH tunnel instructions to also forward the
iFlow callback port 11451 so remote OAuth works: add an additional -L
11451:localhost:11451 to the existing ssh tunnel examples (the lines that
currently show -L 8085:localhost:8085 -L 51121:localhost:51121) and mirror this
change in the troubleshooting tunnel command(s); keep the note about keeping the
connection open and mention that 11451 is required for iFlow callbacks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c3c763f7-282e-4f72-a5b1-80009cf25c1b
📒 Files selected for processing (7)
.env.exampleDOCUMENTATION.mdDeployment guide.mdREADME.mdsrc/proxy_app/main.pysrc/rotator_library/providers/antigravity_provider.pysrc/rotator_library/providers/hatz_provider.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
🧬 Code graph analysis (1)
src/rotator_library/providers/hatz_provider.py (4)
src/rotator_library/providers/provider_interface.py (1)
ProviderInterface(71-711)src/rotator_library/model_definitions.py (1)
get_all_provider_models(108-111)src/rotator_library/timeout_config.py (1)
streaming(76-89)src/rotator_library/transaction_logger.py (3)
ProviderLogger(472-582)log_error(539-547)log_response_chunk(521-528)
🪛 markdownlint-cli2 (0.21.0)
README.md
[warning] 817-817: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 822-822: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 1028-1028: Link fragments should be valid
(MD051, link-fragments)
🪛 Ruff (0.15.2)
src/rotator_library/providers/hatz_provider.py
[warning] 28-28: Import from collections.abc instead: AsyncGenerator
Import from collections.abc
(UP035)
[warning] 28-28: typing.List is deprecated, use list instead
(UP035)
[warning] 28-28: typing.Dict is deprecated, use dict instead
(UP035)
[warning] 50-50: Missing return type annotation for special method __init__
Add return type annotation: None
(ANN204)
[warning] 77-77: Logging statement uses f-string
(G004)
[warning] 96-97: Logging statement uses f-string
(G004)
[warning] 100-100: Logging statement uses f-string
(G004)
[warning] 101-101: Do not catch blind exception: Exception
(BLE001)
[warning] 102-102: Logging statement uses f-string
(G004)
[warning] 244-244: Missing type annotation for **kwargs
(ANN003)
[warning] 594-594: Missing return type annotation for private function _get
(ANN202)
[warning] 703-703: Missing type annotation for **kwargs
(ANN003)
[warning] 704-704: Use X | Y for type annotations
Convert to X | Y
(UP007)
[warning] 720-720: Missing return type annotation for private function make_request
(ANN202)
[warning] 740-740: Logging statement uses f-string
(G004)
[warning] 750-750: Missing return type annotation for private function stream_handler
(ANN202)
[warning] 773-778: Abstract raise to an inner function
(TRY301)
[warning] 773-778: Avoid specifying long messages outside the exception class
(TRY003)
[warning] 780-785: Abstract raise to an inner function
(TRY301)
[warning] 780-785: Avoid specifying long messages outside the exception class
(TRY003)
[warning] 791-795: Abstract raise to an inner function
(TRY301)
[warning] 791-795: Avoid specifying long messages outside the exception class
(TRY003)
[warning] 817-817: Logging statement uses f-string
(G004)
[warning] 832-832: Logging .exception(...) should be used instead of .error(..., exc_info=True)
(G201)
[warning] 833-833: Logging statement uses f-string
(G004)
[warning] 837-837: Missing return type annotation for private function logging_stream_wrapper
(ANN202)
[warning] 853-853: Missing return type annotation for private function non_stream_wrapper
(ANN202)
🔇 Additional comments (3)
.env.example (1)
55-60: Hatz env configuration docs look clear and consistent.This section correctly documents key naming, endpoint usage, and
X-API-Keyauth behavior.Deployment guide.md (1)
526-533: SSH tunnel examples are now clearer and consistently ordered.The combined and per-port examples remain correct and improve readability for remote OAuth setup.
src/rotator_library/providers/hatz_provider.py (1)
329-497: Solid SSE event translation coverage.The mapping for
response.created, text/tool deltas, and terminal usage/finish chunks is well structured and practical for Chat Completions compatibility.
| - This is the key to "Stateless Deployment" for platforms like Railway, Render, Heroku | ||
| - Credentials are referenced internally using `env://` URIs (e.g., `env://gemini_cli/1`) | ||
|
|
||
| #### 2.6.4. Remote Host Authentication (SSH Port Forwarding) |
There was a problem hiding this comment.
Section numbering is out of order.
2.6.4 appears before 2.6.3, which breaks doc structure and cross-reference clarity. Please renumber/reorder this subsection sequence.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@DOCUMENTATION.md` at line 240, The subsection "2.6.4. Remote Host
Authentication (SSH Port Forwarding)" is placed before "2.6.3", breaking the
document order; locate the heading text "2.6.4. Remote Host Authentication (SSH
Port Forwarding)" and either renumber it to "2.6.3" (and adjust any subsequent
section numbers) or move that entire subsection below the existing "2.6.3" entry
so numbering is sequential, and update any in-doc cross-references that point to
"2.6.4" accordingly.
| 1. **Establish SSH tunnel** (keep this connection open): | ||
| ```bash | ||
| ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip | ||
| ``` | ||
|
|
||
| 2. **Run the credential tool on the VPS** (in a separate terminal or SSH session): | ||
| ```bash | ||
| ssh user@your-vps-ip |
There was a problem hiding this comment.
Add blank lines before fenced code blocks in list items.
This section triggers MD031 (blanks-around-fences). Insert a blank line before each ```bash fence under the numbered steps.
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 817-817: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 822-822: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 816 - 823, Add a blank line before each fenced code
block under the numbered list items "Establish SSH tunnel" and "Run the
credential tool on the VPS" so the Markdown has an empty line immediately above
each ```bash fence; update the two list entries that contain the code fences to
insert a blank line before their opening ```bash to satisfy MD031
(blanks-around-fences).
| ``` | ||
|
|
||
| See [VPS Deployment](Deployment%20guide.md#appendix-deploying-to-a-custom-vps) for complete guide. | ||
| See the [Remote Host Deployment (SSH Port Forwarding)](#remote-host-deployment-ssh-port-forwarding) section above for detailed OAuth setup instructions. |
There was a problem hiding this comment.
The fragment link target is invalid (MD051).
#remote-host-deployment-ssh-port-forwarding does not resolve reliably because it points to <summary> text, not a markdown heading anchor.
🔗 Suggested fix
-See the [Remote Host Deployment (SSH Port Forwarding)](`#remote-host-deployment-ssh-port-forwarding`) section above for detailed OAuth setup instructions.
+See the **Remote Host Deployment (SSH Port Forwarding)** section above for detailed OAuth setup instructions.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| See the [Remote Host Deployment (SSH Port Forwarding)](#remote-host-deployment-ssh-port-forwarding) section above for detailed OAuth setup instructions. | |
| See the **Remote Host Deployment (SSH Port Forwarding)** section above for detailed OAuth setup instructions. |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 1028-1028: Link fragments should be valid
(MD051, link-fragments)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` at line 1028, The link fragment
"#remote-host-deployment-ssh-port-forwarding" points to a <summary> text (not a
true heading) so it doesn't resolve; fix by making the target a real anchor:
either convert the existing <summary> block "Remote Host Deployment (SSH Port
Forwarding)" into a proper Markdown heading (e.g., prefix with "##") or add an
explicit HTML anchor immediately before that section (e.g., <a
name="remote-host-deployment-ssh-port-forwarding"></a>), then update/keep the
link text "See the [Remote Host Deployment (SSH Port
Forwarding)](`#remote-host-deployment-ssh-port-forwarding`)..." to point to that
valid anchor.
| # If PROXY_API_KEY is not set or empty, skip verification (open access) | ||
| if not PROXY_API_KEY: | ||
| return x_api_key or auth |
There was a problem hiding this comment.
Fail-open auth here should be explicit opt-in, not implicit.
Line 706 silently disables endpoint authentication when PROXY_API_KEY is unset/empty. A missing env var then exposes Anthropic endpoints without auth.
🔐 Suggested fix (explicit unauthenticated mode)
async def verify_anthropic_api_key(
x_api_key: str = Depends(anthropic_api_key_header),
auth: str = Depends(api_key_header),
):
@@
- # If PROXY_API_KEY is not set or empty, skip verification (open access)
- if not PROXY_API_KEY:
- return x_api_key or auth
+ allow_unauthenticated = (
+ os.getenv("ALLOW_UNAUTHENTICATED", "false").lower() == "true"
+ )
+ if not PROXY_API_KEY:
+ if allow_unauthenticated:
+ return x_api_key or auth
+ raise HTTPException(
+ status_code=500,
+ detail="Server misconfigured: PROXY_API_KEY is not set",
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # If PROXY_API_KEY is not set or empty, skip verification (open access) | |
| if not PROXY_API_KEY: | |
| return x_api_key or auth | |
| async def verify_anthropic_api_key( | |
| x_api_key: str = Depends(anthropic_api_key_header), | |
| auth: str = Depends(api_key_header), | |
| ): | |
| allow_unauthenticated = ( | |
| os.getenv("ALLOW_UNAUTHENTICATED", "false").lower() == "true" | |
| ) | |
| if not PROXY_API_KEY: | |
| if allow_unauthenticated: | |
| return x_api_key or auth | |
| raise HTTPException( | |
| status_code=500, | |
| detail="Server misconfigured: PROXY_API_KEY is not set", | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/proxy_app/main.py` around lines 706 - 708, The current check silently
allows open access when PROXY_API_KEY is empty (the conditional using
PROXY_API_KEY with "if not PROXY_API_KEY: return x_api_key or auth"), which must
be changed to require an explicit opt-in flag; update this branch to only bypass
authentication when a dedicated environment flag (e.g.,
PROXY_ALLOW_UNAUTHENTICATED or PROXY_AUTH_DISABLED) is set to a truthy value,
otherwise treat a missing PROXY_API_KEY as a configuration error and
deny/require authentication (return a failure rather than implicitly returning
x_api_key or auth); locate and modify the conditional that references
PROXY_API_KEY, x_api_key, and auth to enforce the explicit opt-in flag check and
add a clear error/deny path when PROXY_API_KEY is unset and the opt-in is not
enabled.
| model_name = model.split("/")[-1] if "/" in model else model | ||
| models.append(model if "/" in model else f"hatz/{model}") | ||
| env_var_ids.add(model_name) |
There was a problem hiding this comment.
Model parsing drops path segments and can send the wrong model ID upstream.
Using split("/")[-1] truncates nested model IDs. For example, hatz/openai/gpt-4o becomes gpt-4o, which can break routing and also weakens deduplication in model discovery.
🧩 Suggested fix
- model_name = model.split("/")[-1] if "/" in model else model
+ model_name = model[len("hatz/"):] if model.startswith("hatz/") else model
models.append(model if "/" in model else f"hatz/{model}")
env_var_ids.add(model_name)- model_name = model.split("/")[-1] if "/" in model else model
+ model_name = model[len("hatz/"):] if model.startswith("hatz/") else model
kwargs_with_stripped_model = {**kwargs, "model": model_name}Also applies to: 723-724
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/rotator_library/providers/hatz_provider.py` around lines 73 - 75, The
current parsing uses split("/")[-1] which drops path segments; instead keep the
full model ID for routing and dedup by setting model_name to the full model
string (not the last segment) and still append models using the existing logic
(models.append(model if "/" in model else f"hatz/{model}")); for environment
variable keys, derive a safe identifier from the full model (e.g., replace "/"
with "_" or another sanitizer) and add that sanitized string to env_var_ids so
uniqueness is preserved while producing a valid env var name — update the
references to model_name, models.append(...), and env_var_ids.add(...)
accordingly.
|
|
|
||
| 1. **Establish SSH tunnel** (keep this connection open): | ||
| ```bash | ||
| ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip |
There was a problem hiding this comment.
The "Complete Workflow" step 1 SSH tunnel command is missing the iFlow port 11451. This is inconsistent with every other section in the PR (the "Multiple Providers at Once" block at line 811, the "Custom VPS / Systemd" section at line 1003, and DOCUMENTATION.md section 2.6.4).
A user following this workflow who also needs iFlow credentials won't have that port forwarded, causing their OAuth callback to silently fail.
| ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip | |
| ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip |
| | All keys on cooldown | All keys failed recently; check `logs/detailed_logs/` for upstream errors | | ||
| | Model not found | Verify format is `provider/model_name` (e.g., `gemini/gemini-2.5-flash`) | | ||
| | OAuth callback failed | Ensure callback port (8085, 51121, 11451) isn't blocked by firewall | | ||
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip` | |
There was a problem hiding this comment.
The troubleshooting table entry for "OAuth callback failed on remote VPS" is also missing iFlow port 11451. This is inconsistent with the "Multiple Providers at Once" command block immediately above (line 811), which correctly lists all three ports.
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 user@your-vps-ip` | | |
| | OAuth callback failed on remote VPS | Use SSH port forwarding: `ssh -L 8085:localhost:8085 -L 51121:localhost:51121 -L 11451:localhost:11451 user@your-vps-ip` | |
| if "max_tokens" in kwargs and kwargs["max_tokens"] is not None: | ||
| max_tokens = kwargs["max_tokens"] | ||
| if max_tokens >= 4096: | ||
| payload["max_output_tokens"] = max_tokens |
There was a problem hiding this comment.
When a caller explicitly sets max_tokens to any value under 4096, the parameter is silently discarded with no warning logged. The code comment at line 275 documents the constraint, but callers receive no indication that their token limit was not honored.
This creates a silent failure mode: a caller requesting a 512-token limit will receive a response using Hatz's default, which can be significantly larger. Without a warning, operators won't detect this mismatch.
At minimum, log a warning when the value is dropped to alert operators to the constraint.
Summary
Added comprehensive documentation explaining the OAuth token authentication issue when the proxy runs on remote hosts and the SSH port forwarding solution.
Changes
Details
This documentation addresses the issue where OAuth callbacks to
localhostfail on remote VPS deployments becauselocalhoston the VPS refers to the VPS itself, not the user's local machine. The solution uses SSH port forwarding to tunnel OAuth callback ports back to the local machine during credential setup.Two approaches are documented:
Example Usage
SSH Port Forwarding (for adding credentials directly on VPS)
Alternative: Local Authentication + Export
.envfile to remote serverImportant
Improves documentation for handling OAuth callback issues on remote hosts using SSH port forwarding, with updates in
README.md,DOCUMENTATION.md, andDeployment guide.md.README.md: Adds "Remote Host Deployment (SSH Port Forwarding)" section explaining OAuth callback issues and SSH port forwarding solution.DOCUMENTATION.md: Introduces section 2.6.4 on remote host authentication using SSH port forwarding.Deployment guide.md: Updates SSH tunnel commands to standardized examples.README.md.This description was created by
for c1e07c0. You can customize this summary. It will automatically update as commits are pushed.