Skip to content

Commit 719109b

Browse files
Revert "Support API Key passthrough"
This reverts commit 3b1bd27.
1 parent 4451fe2 commit 719109b

File tree

5 files changed

+42
-116
lines changed

5 files changed

+42
-116
lines changed

.env.example

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
# Optional: Your OpenAI API key (for proxy mode)
2-
# If set, all requests will use this key to call OpenAI API
3-
# If not set, the proxy will use client-provided keys directly (passthrough mode)
1+
# Required: Your OpenAI API key
42
OPENAI_API_KEY="sk-your-openai-api-key-here"
53

64
# Optional: Expected Anthropic API key for client validation
7-
# If set: clients must provide this exact key (proxy mode)
8-
# If not set: client keys are used directly as OpenAI keys (passthrough mode)
5+
# If set, clients must provide this exact API key to access the proxy
96
ANTHROPIC_API_KEY="your-expected-anthropic-api-key"
107

118
# Optional: OpenAI API base URL (default: https://api.openai.com/v1)

README.md

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ A proxy server that enables **Claude Code** to work with OpenAI-compatible API p
88

99
- **Full Claude API Compatibility**: Complete `/v1/messages` endpoint support
1010
- **Multiple Provider Support**: OpenAI, Azure OpenAI, local models (Ollama), and any OpenAI-compatible API
11-
- **Multi-tenant Passthrough Mode**: Support for per-user OpenAI API keys
1211
- **Smart Model Mapping**: Configure BIG and SMALL models via environment variables
1312
- **Function Calling**: Complete tool use support with proper conversion
1413
- **Streaming Responses**: Real-time SSE streaming support
@@ -51,36 +50,27 @@ docker compose up -d
5150

5251
### 4. Use with Claude Code
5352

54-
**Proxy Mode (OPENAI_API_KEY configured):**
5553
```bash
56-
# With client validation enabled:
57-
ANTHROPIC_BASE_URL=http://localhost:8082 ANTHROPIC_API_KEY="exact-matching-key" claude
58-
59-
# With client validation disabled:
54+
# If ANTHROPIC_API_KEY is not set in the proxy:
6055
ANTHROPIC_BASE_URL=http://localhost:8082 ANTHROPIC_API_KEY="any-value" claude
61-
```
6256

63-
**Passthrough Mode (OPENAI_API_KEY not configured):**
64-
```bash
65-
# Users provide their own OpenAI API key:
66-
ANTHROPIC_BASE_URL=http://localhost:8082 ANTHROPIC_API_KEY="sk-your-openai-api-key-here" claude
57+
# If ANTHROPIC_API_KEY is set in the proxy:
58+
ANTHROPIC_BASE_URL=http://localhost:8082 ANTHROPIC_API_KEY="exact-matching-key" claude
6759
```
6860

6961
## Configuration
7062

7163
### Environment Variables
7264

73-
**Core Configuration:**
65+
**Required:**
7466

75-
- `OPENAI_API_KEY` - Your API key for the target provider (optional)
76-
- If set: Proxy mode - proxy uses this key for all requests
77-
- If not set: Passthrough mode - users provide their own OpenAI API keys
67+
- `OPENAI_API_KEY` - Your API key for the target provider
7868

7969
**Security:**
8070

81-
- `ANTHROPIC_API_KEY` - Expected Anthropic API key for client validation (optional)
71+
- `ANTHROPIC_API_KEY` - Expected Anthropic API key for client validation
8272
- If set, clients must provide this exact API key to access the proxy
83-
- If not set, client-provided API keys are used directly as OpenAI API keys (passthrough mode)
73+
- If not set, any API key will be accepted
8474

8575
**Model Configuration:**
8676

src/api/endpoints.py

Lines changed: 22 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,50 +17,15 @@
1717

1818
router = APIRouter()
1919

20-
# Create OpenAI client - use env key if available, otherwise create a dummy one
21-
if config.openai_api_key:
22-
openai_client = OpenAIClient(
23-
config.openai_api_key,
24-
config.openai_base_url,
25-
config.request_timeout,
26-
api_version=config.azure_api_version,
27-
)
28-
else:
29-
# In passthrough mode, create a client with a dummy key for compatibility
30-
# The actual client will be created per-request with user's API key
31-
openai_client = OpenAIClient(
32-
"sk-dummy", # This won't be used for actual requests
33-
config.openai_base_url,
34-
config.request_timeout,
35-
api_version=config.azure_api_version,
36-
)
37-
38-
def get_openai_client(user_api_key: Optional[str] = None) -> OpenAIClient:
39-
"""Get OpenAI client - use env key if available, otherwise create with user-provided key."""
40-
if config.openai_api_key:
41-
# Proxy mode: use pre-configured client
42-
return openai_client
43-
elif user_api_key:
44-
# Passthrough mode: create client with user-provided key
45-
if not config.validate_api_key(user_api_key):
46-
raise HTTPException(
47-
status_code=401,
48-
detail="Invalid OpenAI API key format. Please provide a valid API key starting with 'sk-'."
49-
)
50-
return OpenAIClient(
51-
user_api_key,
52-
config.openai_base_url,
53-
config.request_timeout,
54-
api_version=config.azure_api_version,
55-
)
56-
else:
57-
raise HTTPException(
58-
status_code=401,
59-
detail="No OpenAI API key available. Please set OPENAI_API_KEY environment variable or provide your OpenAI API key in Authorization header."
60-
)
20+
openai_client = OpenAIClient(
21+
config.openai_api_key,
22+
config.openai_base_url,
23+
config.request_timeout,
24+
api_version=config.azure_api_version,
25+
)
6126

62-
async def validate_api_key_and_extract(x_api_key: Optional[str] = Header(None), authorization: Optional[str] = Header(None)):
63-
"""Extract API key and handle validation based on configuration."""
27+
async def validate_api_key(x_api_key: Optional[str] = Header(None), authorization: Optional[str] = Header(None)):
28+
"""Validate the client's API key from either x-api-key header or Authorization header."""
6429
client_api_key = None
6530

6631
# Extract API key from headers
@@ -69,40 +34,27 @@ async def validate_api_key_and_extract(x_api_key: Optional[str] = Header(None),
6934
elif authorization and authorization.startswith("Bearer "):
7035
client_api_key = authorization.replace("Bearer ", "")
7136

72-
# If OPENAI_API_KEY is configured in environment, use proxy validation
73-
if config.openai_api_key:
74-
# Proxy mode: validate against ANTHROPIC_API_KEY if configured
75-
if config.anthropic_api_key:
76-
if not client_api_key or not config.validate_client_api_key(client_api_key):
77-
logger.warning(f"Invalid API key provided by client")
78-
raise HTTPException(
79-
status_code=401,
80-
detail="Invalid API key. Please provide a valid Anthropic API key."
81-
)
82-
# Return None since we'll use env OPENAI_API_KEY
83-
return None
84-
else:
85-
# Passthrough mode: no ANTHROPIC validation, use client key as OpenAI key
86-
if not client_api_key:
87-
raise HTTPException(
88-
status_code=401,
89-
detail="No API key provided. Please provide your OpenAI API key in Authorization header."
90-
)
91-
# Return the client API key to be used as OpenAI key
92-
return client_api_key
37+
# Skip validation if ANTHROPIC_API_KEY is not set in the environment
38+
if not config.anthropic_api_key:
39+
return
40+
41+
# Validate the client API key
42+
if not client_api_key or not config.validate_client_api_key(client_api_key):
43+
logger.warning(f"Invalid API key provided by client")
44+
raise HTTPException(
45+
status_code=401,
46+
detail="Invalid API key. Please provide a valid Anthropic API key."
47+
)
9348

9449
@router.post("/v1/messages")
95-
async def create_message(request: ClaudeMessagesRequest, http_request: Request, user_api_key: str = Depends(validate_api_key_and_extract)):
50+
async def create_message(request: ClaudeMessagesRequest, http_request: Request, _: None = Depends(validate_api_key)):
9651
try:
9752
logger.debug(
9853
f"Processing Claude request: model={request.model}, stream={request.stream}"
9954
)
10055

10156
# Generate unique request ID for cancellation tracking
10257
request_id = str(uuid.uuid4())
103-
104-
# Get OpenAI client (either default or created with user's key)
105-
openai_client = get_openai_client(user_api_key)
10658

10759
# Convert Claude request to OpenAI format
10860
openai_request = convert_claude_to_openai(request, model_manager)
@@ -167,7 +119,7 @@ async def create_message(request: ClaudeMessagesRequest, http_request: Request,
167119

168120

169121
@router.post("/v1/messages/count_tokens")
170-
async def count_tokens(request: ClaudeTokenCountRequest, user_api_key: str = Depends(validate_api_key_and_extract)):
122+
async def count_tokens(request: ClaudeTokenCountRequest, _: None = Depends(validate_api_key)):
171123
try:
172124
# For token counting, we'll use a simple estimation
173125
# In a real implementation, you might want to use tiktoken or similar
@@ -211,7 +163,7 @@ async def health_check():
211163
"status": "healthy",
212164
"timestamp": datetime.now().isoformat(),
213165
"openai_api_configured": bool(config.openai_api_key),
214-
"api_key_valid": config.validate_api_key() if config.openai_api_key else "per_request",
166+
"api_key_valid": config.validate_api_key(),
215167
"client_api_key_validation": bool(config.anthropic_api_key),
216168
}
217169

@@ -220,17 +172,6 @@ async def health_check():
220172
async def test_connection():
221173
"""Test API connectivity to OpenAI"""
222174
try:
223-
# Use default client if available, otherwise require user API key
224-
if not config.openai_api_key:
225-
return JSONResponse(
226-
status_code=400,
227-
content={
228-
"status": "failed",
229-
"message": "No default OpenAI API key configured. Test connection requires OPENAI_API_KEY environment variable.",
230-
"timestamp": datetime.now().isoformat(),
231-
}
232-
)
233-
234175
# Simple test request to verify API connectivity
235176
test_response = await openai_client.create_chat_completion(
236177
{

src/core/config.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
class Config:
66
def __init__(self):
77
self.openai_api_key = os.environ.get("OPENAI_API_KEY")
8-
# Note: openai_api_key is now optional - can be provided per request
8+
if not self.openai_api_key:
9+
raise ValueError("OPENAI_API_KEY not found in environment variables")
910

1011
# Add Anthropic API key for client validation
1112
self.anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
@@ -29,13 +30,12 @@ def __init__(self):
2930
self.middle_model = os.environ.get("MIDDLE_MODEL", self.big_model)
3031
self.small_model = os.environ.get("SMALL_MODEL", "gpt-4o-mini")
3132

32-
def validate_api_key(self, api_key: str = None):
33-
"""Basic API key validation - use provided key or fallback to env key"""
34-
key_to_validate = api_key if api_key else self.openai_api_key
35-
if not key_to_validate:
33+
def validate_api_key(self):
34+
"""Basic API key validation"""
35+
if not self.openai_api_key:
3636
return False
3737
# Basic format check for OpenAI API keys
38-
if not key_to_validate.startswith('sk-'):
38+
if not self.openai_api_key.startswith('sk-'):
3939
return False
4040
return True
4141

src/main.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@ def main():
1515
print("")
1616
print("Usage: python src/main.py")
1717
print("")
18-
print("Environment variables:")
19-
print(" OPENAI_API_KEY - Your OpenAI API key (optional)")
20-
print(" If not set, users must provide their own OpenAI API key")
21-
print(" ANTHROPIC_API_KEY - Expected Anthropic API key for client validation (optional)")
22-
print(" If set, clients must provide this exact API key")
23-
print(" If not set, client API keys are used directly as OpenAI keys")
18+
print("Required environment variables:")
19+
print(" OPENAI_API_KEY - Your OpenAI API key")
2420
print("")
25-
print("Additional optional environment variables:")
21+
print("Optional environment variables:")
22+
print(" ANTHROPIC_API_KEY - Expected Anthropic API key for client validation")
23+
print(" If set, clients must provide this exact API key")
2624
print(
2725
f" OPENAI_BASE_URL - OpenAI API base URL (default: https://api.openai.com/v1)"
2826
)

0 commit comments

Comments
 (0)