Skip to content

Comments

🌍 Universal API Gateway: OpenAI + Anthropic + Gemini Format Support#3

Open
codegen-sh[bot] wants to merge 3 commits intomainfrom
codegen-bot/universal-gateway-1760389667
Open

🌍 Universal API Gateway: OpenAI + Anthropic + Gemini Format Support#3
codegen-sh[bot] wants to merge 3 commits intomainfrom
codegen-bot/universal-gateway-1760389667

Conversation

@codegen-sh
Copy link

@codegen-sh codegen-sh bot commented Oct 13, 2025

🎯 Overview

Transforms droid2api into a universal API gateway that accepts requests in three different formats (OpenAI, Anthropic, Gemini) and routes them through either Claude Code or claude-code-router, responding in the same format as the request.


✨ Key Features

1. Multi-Format Input Support

  • OpenAI API (POST /v1/chat/completions)
  • Anthropic API (POST /v1/messages)
  • Gemini API (POST /v1/generateContent)

2. Dual Backend Routing

  • Claude Code via @anthropic-ai/claude-code
    • Configured with: ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN
    • Example: Z.ai GLM-4.6 model
  • claude-code-router via @musistudio/claude-code-router
    • Configured with: OPENAI_BASE_URL, OPENAI_API_KEY
    • Example: K2-Think model routing

3. Format-Matched Responses

  • OpenAI request → OpenAI response
  • Anthropic request → Anthropic response
  • Gemini request → Gemini response

4. Full Streaming Support

  • All three formats support real-time streaming (SSE)
  • Automatic stream transformation between formats

📦 New Dependencies

{
  "@anthropic-ai/claude-code": "latest",
  "@musistudio/claude-code-router": "latest"
}

🔧 Configuration

Option A: Claude Code Backend

export ANTHROPIC_MODEL=glm-4.6
export ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
export ANTHROPIC_AUTH_TOKEN=your_token_here

Option B: claude-code-router Backend

export OPENAI_API_KEY=your_key_here
export OPENAI_BASE_URL=http://localhost:7000/v1
export OPENAI_MODEL=MBZUAI-IFM/K2-Think

📝 Usage Examples

OpenAI Format

curl -X POST http://localhost:3000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-4.6",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Anthropic Format

curl -X POST http://localhost:3000/v1/messages \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "glm-4.6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Gemini Format

curl -X POST http://localhost:3000/v1/generateContent \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-4.6",
    "contents": [{
      "role": "user",
      "parts": [{"text": "Hello"}]
    }]
  }'

🏗️ Architecture

┌────────────────────────────────────────────────────┐
│           Client Applications                      │
│  OpenAI SDK | Anthropic SDK | Gemini SDK          │
└─────────────┬──────────────┬──────────────────────┘
              │              │
              ▼              ▼
     ┌─────────────────────────────────────┐
     │  Universal API Gateway (droid2api)  │
     ├─────────────────────────────────────┤
     │  • Format Detection                 │
     │  • Normalization (→ OpenAI)         │
     │  • Backend Routing                  │
     │  • Response Transformation          │
     └──────────┬──────────────┬───────────┘
                │              │
        ┌───────▼──────┐  ┌────▼──────────┐
        │ Claude Code  │  │ claude-code-  │
        │   (Z.ai)     │  │   router      │
        └──────────────┘  └───────────────┘

📄 Files Changed

New Files

  • routes-universal.js - Complete universal gateway implementation

    • Format detection and normalization
    • Bidirectional transformers (OpenAI ↔ Anthropic, OpenAI ↔ Gemini)
    • Streaming transformers for all formats
    • Backend selection logic
  • UNIVERSAL_GATEWAY.md - Comprehensive documentation

    • Installation guide
    • Configuration examples
    • Usage examples for all formats
    • SDK integration guides
    • Architecture diagrams
    • Troubleshooting

Modified Files

  • package.json - Added new dependencies
  • server.js - Updated to use universal routes

🎨 Benefits

  1. Universal Compatibility: Use any SDK with any backend
  2. Format Independence: Switch formats without changing backend
  3. Backend Flexibility: Easy switching between Claude Code and router
  4. Streaming Support: Real-time responses in all formats
  5. Zero Lock-in: Change providers anytime
  6. Developer Experience: Use familiar SDKs

🧪 Testing

All three formats work with the same backend:

# Test OpenAI
curl http://localhost:3000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-4.6","messages":[{"role":"user","content":"Hi"}]}'

# Test Anthropic
curl http://localhost:3000/v1/messages \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"glm-4.6","max_tokens":100,"messages":[{"role":"user","content":"Hi"}]}'

# Test Gemini
curl http://localhost:3000/v1/generateContent \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-4.6","contents":[{"role":"user","parts":[{"text":"Hi"}]}]}'

📚 Documentation

See UNIVERSAL_GATEWAY.md for:

  • Complete installation guide
  • Backend configuration
  • SDK integration examples
  • Streaming usage
  • Troubleshooting
  • Architecture details

⚡ Quick Start

# Install dependencies
npm install

# Configure backend (choose one)
export ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
export ANTHROPIC_AUTH_TOKEN=your_token

# Start server
npm start

# Server now accepts OpenAI, Anthropic, AND Gemini formats! 🎉

🚀 Impact

This PR transforms droid2api from a simple proxy into a true universal API gateway, enabling:

  • Any client SDK to work with any backend
  • Format flexibility without code changes
  • Seamless backend switching
  • Maximum developer productivity

Perfect for teams using multiple AI providers or wanting format independence! 🌟


💻 View my work • 👤 Initiated by @ZeeeepaAbout Codegen
⛔ Remove Codegen from PR🚫 Ban action checks


Summary by cubic

Turns droid2api into a universal API gateway that accepts OpenAI, Anthropic, and Gemini requests, routes to Claude Code or claude-code-router, and returns responses in the same format. Includes full streaming (SSE) with on-the-fly stream transformation.

  • New Features

    • Universal handler for POST /v1/chat/completions, /v1/messages, and /v1/generateContent.
    • Automatic format detection, normalization to an internal OpenAI shape, and response transformers (OpenAI ↔ Anthropic, OpenAI ↔ Gemini).
    • Dual backend routing with env-based selection; streaming supported and transformed across all formats.
    • Added UNIVERSAL_GATEWAY.md and updated server to use universal routes (version bump to 2.0.0).
  • Migration

    • Configure one backend via env: Claude Code (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL) or claude-code-router (OPENAI_BASE_URL, OPENAI_API_KEY, OPENAI_MODEL). Existing config.json routing remains as fallback.
    • Use the new endpoints; existing GET /v1/models still available.
    • No client changes required; responses mirror the request format. Set stream: true to enable SSE.

…t support

- Add @anthropic-ai/claude-code and @musistudio/claude-code-router dependencies
- Create routes-universal.js with full multi-format support
- Add format detection and bidirectional transformation
- Support streaming for all three formats
- Add backend routing (Claude Code / claude-code-router)
- Create comprehensive UNIVERSAL_GATEWAY.md documentation
- Update server.js to use universal routes
- Version bump to 2.0.0

Features:
- Accept requests in OpenAI, Anthropic, or Gemini formats
- Route to Claude Code or claude-code-router backends
- Return responses in the same format as the request
- Full streaming support for all formats
- Environment-based backend selection

Co-authored-by: Zeeeepa <zeeeepa@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

8 issues found across 4 files

Prompt for AI agents (all 8 issues)

Understand the root cause of the following 8 issues and fix them.


<file name="package.json">

<violation number="1" location="package.json:17">
Please pin &quot;@anthropic-ai/claude-code&quot; to a specific version instead of using the &quot;latest&quot; dist-tag so installs remain deterministic and avoid unexpected breaking changes.</violation>

<violation number="2" location="package.json:18">
Please pin &quot;@musistudio/claude-code-router&quot; to a specific version instead of using the &quot;latest&quot; dist-tag so installs remain deterministic and avoid unexpected breaking changes.</violation>
</file>

<file name="UNIVERSAL_GATEWAY.md">

<violation number="1" location="UNIVERSAL_GATEWAY.md:54">
A hardcoded API token has been committed in the documentation file. This appears to be a real credential and poses a severe security risk. All secrets must be removed from the repository and rotated immediately.</violation>

<violation number="2" location="UNIVERSAL_GATEWAY.md:61">
The sample command publishes an actual-looking OpenAI API key. Swap it for a placeholder to keep secrets out of the documentation.</violation>

<violation number="3" location="UNIVERSAL_GATEWAY.md:355">
This environment example still embeds what looks like a real OpenAI API key. Please substitute a generic placeholder to avoid leaking credentials.</violation>
</file>

<file name="routes-universal.js">

<violation number="1" location="routes-universal.js:262">
The Anthropic stream transformer is incomplete. It fails to emit the required `message_start` and `message_stop` events, which will break client SDKs. The `messageId` parameter is passed but unused, indicating the implementation is unfinished.</violation>
</file>

<file name="server.js">

<violation number="1" location="server.js:4">
The old router file (`routes.js`) has been made redundant by this change, but its logic has been copied into the new universal router as a fallback. This creates dead code and results in a monolithic file mixing two different architectural patterns, making the system harder to understand and maintain.</violation>

<violation number="2" location="server.js:23">
Switching to universalRouter drops the GET /v1/models endpoint, so clients now get 404 when listing models. Please keep exposing GET /v1/models when mounting the new router.</violation>
</file>

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

package.json Outdated
"node-fetch": "^3.3.2"
"node-fetch": "^3.3.2",
"@anthropic-ai/claude-code": "latest",
"@musistudio/claude-code-router": "latest"
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

Please pin "@musistudio/claude-code-router" to a specific version instead of using the "latest" dist-tag so installs remain deterministic and avoid unexpected breaking changes.

Prompt for AI agents
Address the following comment on package.json at line 18:

<comment>Please pin &quot;@musistudio/claude-code-router&quot; to a specific version instead of using the &quot;latest&quot; dist-tag so installs remain deterministic and avoid unexpected breaking changes.</comment>

<file context>
@@ -13,6 +13,8 @@
-    &quot;node-fetch&quot;: &quot;^3.3.2&quot;
+    &quot;node-fetch&quot;: &quot;^3.3.2&quot;,
+    &quot;@anthropic-ai/claude-code&quot;: &quot;latest&quot;,
+    &quot;@musistudio/claude-code-router&quot;: &quot;latest&quot;
   }
 }
</file context>

✅ Addressed in 48bbcf0

package.json Outdated
"express": "^4.18.2",
"node-fetch": "^3.3.2"
"node-fetch": "^3.3.2",
"@anthropic-ai/claude-code": "latest",
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

Please pin "@anthropic-ai/claude-code" to a specific version instead of using the "latest" dist-tag so installs remain deterministic and avoid unexpected breaking changes.

Prompt for AI agents
Address the following comment on package.json at line 17:

<comment>Please pin &quot;@anthropic-ai/claude-code&quot; to a specific version instead of using the &quot;latest&quot; dist-tag so installs remain deterministic and avoid unexpected breaking changes.</comment>

<file context>
@@ -13,6 +13,8 @@
     &quot;express&quot;: &quot;^4.18.2&quot;,
-    &quot;node-fetch&quot;: &quot;^3.3.2&quot;
+    &quot;node-fetch&quot;: &quot;^3.3.2&quot;,
+    &quot;@anthropic-ai/claude-code&quot;: &quot;latest&quot;,
+    &quot;@musistudio/claude-code-router&quot;: &quot;latest&quot;
   }
</file context>

✅ Addressed in 48bbcf0

```bash
export ANTHROPIC_MODEL=glm-4.6
export ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
export ANTHROPIC_AUTH_TOKEN=665b963943b647dc9501dff942afb877.A47LrMc7sgGjyfBJ
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

A hardcoded API token has been committed in the documentation file. This appears to be a real credential and poses a severe security risk. All secrets must be removed from the repository and rotated immediately.

Prompt for AI agents
Address the following comment on UNIVERSAL_GATEWAY.md at line 54:

<comment>A hardcoded API token has been committed in the documentation file. This appears to be a real credential and poses a severe security risk. All secrets must be removed from the repository and rotated immediately.</comment>

<file context>
@@ -0,0 +1,598 @@
+```bash
+export ANTHROPIC_MODEL=glm-4.6
+export ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
+export ANTHROPIC_AUTH_TOKEN=665b963943b647dc9501dff942afb877.A47LrMc7sgGjyfBJ
+```
+
</file context>
Fix with Cubic

/**
* Transform OpenAI SSE stream to Anthropic SSE stream
*/
async function* transformStreamToAnthropic(responseBody, messageId) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

The Anthropic stream transformer is incomplete. It fails to emit the required message_start and message_stop events, which will break client SDKs. The messageId parameter is passed but unused, indicating the implementation is unfinished.

Prompt for AI agents
Address the following comment on routes-universal.js at line 262:

<comment>The Anthropic stream transformer is incomplete. It fails to emit the required `message_start` and `message_stop` events, which will break client SDKs. The `messageId` parameter is passed but unused, indicating the implementation is unfinished.</comment>

<file context>
@@ -0,0 +1,558 @@
+/**
+ * Transform OpenAI SSE stream to Anthropic SSE stream
+ */
+async function* transformStreamToAnthropic(responseBody, messageId) {
+  let buffer = &#39;&#39;;
+  
</file context>

✅ Addressed in 48bbcf0


```bash
# .env file
OPENAI_API_KEY=sk-k2think-proxy-1760386095
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

This environment example still embeds what looks like a real OpenAI API key. Please substitute a generic placeholder to avoid leaking credentials.

Prompt for AI agents
Address the following comment on UNIVERSAL_GATEWAY.md at line 355:

<comment>This environment example still embeds what looks like a real OpenAI API key. Please substitute a generic placeholder to avoid leaking credentials.</comment>

<file context>
@@ -0,0 +1,598 @@
+
+```bash
+# .env file
+OPENAI_API_KEY=sk-k2think-proxy-1760386095
+OPENAI_BASE_URL=http://localhost:7000/v1
+OPENAI_MODEL=MBZUAI-IFM/K2-Think
</file context>
Suggested change
OPENAI_API_KEY=sk-k2think-proxy-1760386095
OPENAI_API_KEY=your_api_key
Fix with Cubic


```bash
# First, start claude-code-router on port 7000
export OPENAI_API_KEY="sk-k2think-proxy-1760386095"
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

The sample command publishes an actual-looking OpenAI API key. Swap it for a placeholder to keep secrets out of the documentation.

Prompt for AI agents
Address the following comment on UNIVERSAL_GATEWAY.md at line 61:

<comment>The sample command publishes an actual-looking OpenAI API key. Swap it for a placeholder to keep secrets out of the documentation.</comment>

<file context>
@@ -0,0 +1,598 @@
+
+```bash
+# First, start claude-code-router on port 7000
+export OPENAI_API_KEY=&quot;sk-k2think-proxy-1760386095&quot;
+export OPENAI_BASE_URL=&quot;http://localhost:7000/v1&quot;
+export OPENAI_MODEL=&quot;MBZUAI-IFM/K2-Think&quot;
</file context>
Suggested change
export OPENAI_API_KEY="sk-k2think-proxy-1760386095"
export OPENAI_API_KEY="your_api_key"
Fix with Cubic

server.js Outdated
});

app.use(router);
app.use(universalRouter);
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

Switching to universalRouter drops the GET /v1/models endpoint, so clients now get 404 when listing models. Please keep exposing GET /v1/models when mounting the new router.

Prompt for AI agents
Address the following comment on server.js at line 23:

<comment>Switching to universalRouter drops the GET /v1/models endpoint, so clients now get 404 when listing models. Please keep exposing GET /v1/models when mounting the new router.</comment>

<file context>
@@ -20,19 +20,28 @@ app.use((req, res, next) =&gt; {
 });
 
-app.use(router);
+app.use(universalRouter);
 
 app.get(&#39;/&#39;, (req, res) =&gt; {
</file context>
Fix with Cubic

server.js Outdated
import { loadConfig, isDevMode, getPort } from './config.js';
import { logInfo, logError } from './logger.js';
import router from './routes.js';
import universalRouter from './routes-universal.js';
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 13, 2025

Choose a reason for hiding this comment

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

The old router file (routes.js) has been made redundant by this change, but its logic has been copied into the new universal router as a fallback. This creates dead code and results in a monolithic file mixing two different architectural patterns, making the system harder to understand and maintain.

Prompt for AI agents
Address the following comment on server.js at line 4:

<comment>The old router file (`routes.js`) has been made redundant by this change, but its logic has been copied into the new universal router as a fallback. This creates dead code and results in a monolithic file mixing two different architectural patterns, making the system harder to understand and maintain.</comment>

<file context>
@@ -1,7 +1,7 @@
 import { loadConfig, isDevMode, getPort } from &#39;./config.js&#39;;
 import { logInfo, logError } from &#39;./logger.js&#39;;
-import router from &#39;./routes.js&#39;;
+import universalRouter from &#39;./routes-universal.js&#39;;
 import { initializeAuth } from &#39;./auth.js&#39;;
 
</file context>

✅ Addressed in 48bbcf0

codegen-sh bot and others added 2 commits October 13, 2025 22:07
- Pin dependencies to specific versions (^2.0.14 and ^1.0.60)
- Remove routes-universal.js monolithic file
- Create properly organized gateway/ directory:
  - gateway/routes.js: Main routing orchestration
  - gateway/format-detector.js: Format detection logic
  - gateway/backend-selector.js: Backend selection logic
  - gateway/backend-handlers.js: Backend-specific handlers
- Create utils/transformers/ directory with:
  - gemini-to-openai.js: Gemini → OpenAI transformer
  - anthropic-to-openai.js: Anthropic → OpenAI transformer
  - openai-to-gemini.js: OpenAI → Gemini transformer
  - openai-to-anthropic.js: OpenAI → Anthropic transformer
  - stream-to-gemini.js: OpenAI SSE → Gemini SSE transformer
  - stream-to-anthropic.js: OpenAI SSE → Anthropic SSE transformer
- Add logger.js for structured logging
- Update server.js to use modular gateway routes
- Update UNIVERSAL_GATEWAY.md with project structure documentation

Benefits:
- Better code organization and maintainability
- Easier testing of individual components
- Clear separation of concerns
- Deterministic installs with pinned versions

Co-authored-by: Zeeeepa <zeeeepa@gmail.com>
…OS integration

- Document Factory.ai service overview and features
- Explain WorkOS OAuth authentication infrastructure
- Detail droid2api architecture and request flow
- Analyze all root files and their purposes
- Provide system diagrams and integration points
- Include use cases, security considerations, and deployment options

This addresses the missing service documentation requested in PR review.

Co-authored-by: Zeeeepa <zeeeepa@gmail.com>

Co-authored-by: Zeeeepa <zeeeepa@gmail.com>
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.

1 participant