Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
79 changes: 79 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Using official Python slim image for a smaller footprint
FROM python:3.13.0-slim

# Set username from build argument
ARG BUILD_USER=appuser
ENV USER=$BUILD_USER \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# Derive APP_HOME from USER
ENV APP_HOME=/home/$USER

RUN apt-get update && apt-get install --no-install-recommends -y \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -m -s /bin/bash $USER

# Install Node.js and npm for Claude Code CLI
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s /usr/bin/node /usr/local/bin/node \
&& ln -s /usr/bin/npm /usr/local/bin/npm

# Install Claude Code CLI globally with proper permissions
RUN npm install -g @anthropic-ai/claude-code \
&& chmod -R 755 /usr/lib/node_modules \
&& chmod -R 755 /usr/bin/claude* || true


# Download the latest installer
COPY --from=ghcr.io/astral-sh/uv:0.7.2 /uv /uvx /bin/

# Set application directory and working directory
ENV APP_DIR=$APP_HOME
WORKDIR $APP_DIR

# Create a virtual environment in user space
ENV VIRTUAL_ENV=$APP_HOME/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV

# Print debug information
RUN echo "Configuration: USER=$USER, APP_HOME=$APP_HOME, APP_DIR=$APP_DIR"

# Create virtual environment
RUN uv venv $VIRTUAL_ENV

RUN --mount=type=cache,target=/home/$USER/.cache/uv \
--mount=type=bind,source=./uv.lock,target=uv.lock \
--mount=type=bind,source=./pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project

# Copy only necessary files
COPY pyproject.toml $APP_DIR/
COPY uv.lock $APP_DIR/
COPY *.py $APP_DIR/
COPY README.md $APP_DIR/

RUN --mount=type=cache,target=/home/$USER/.cache/uv \
uv sync --frozen

ENV PYTHONPATH=$APP_DIR

RUN chown -R "$USER":"$USER" $APP_DIR
USER $USER

# Final debug output
RUN echo "Final configuration - APP_HOME: $APP_HOME, USER: $USER, APP_DIR: $APP_DIR"

# Test Node.js and Claude CLI are available
RUN which node && node --version || echo "Node.js not found in PATH"
RUN which claude && claude --version || echo "Claude CLI not found in PATH"

EXPOSE 8000

CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
90 changes: 76 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ An OpenAI API-compatible wrapper for Claude Code, allowing you to use Claude Cod
### ⚡ **Advanced Features**
- **System prompt support** via SDK options
- **Optional tool usage** - Enable Claude Code tools (Read, Write, Bash, etc.) when needed
- **⚠️ Roo Code Users**: Must set `enable_tools: true` for file operations and commands
- **Fast default mode** - Tools disabled by default for OpenAI API compatibility
- **Development mode** with auto-reload (`uvicorn --reload`)
- **Interactive API key protection** - Optional security with auto-generated tokens
Expand All @@ -64,13 +65,13 @@ claude auth login # Recommended for development
# 3. Clone and setup the wrapper
git clone https://github.com/RichardAtCT/claude-code-openai-wrapper
cd claude-code-openai-wrapper
poetry install
poetry install # or: uv sync (for UV users)

# 4. Start the server
poetry run uvicorn main:app --reload --port 8000
poetry run uvicorn main:app --reload --port 8000 # or: uv run uvicorn main:app --reload --port 8000

# 5. Test it works
poetry run python test_endpoints.py
poetry run python test_endpoints.py # or: uv run python test_endpoints.py
```

🎉 **That's it!** Your OpenAI-compatible Claude Code API is running on `http://localhost:8000`
Expand All @@ -96,11 +97,19 @@ poetry run python test_endpoints.py

3. **Python 3.10+**: Required for the server

4. **Poetry**: For dependency management
4. **Poetry or UV**: For dependency management

**Option A: Poetry** (traditional)
```bash
# Install Poetry (if not already installed)
# Install Poetry
curl -sSL https://install.python-poetry.org | python3 -
```

**Option B: UV** (modern, faster)
```bash
# Install UV
curl -LsSf https://astral.sh/uv/install.sh | sh
```

## Installation

Expand All @@ -110,12 +119,28 @@ poetry run python test_endpoints.py
cd claude-code-openai-wrapper
```

2. Install dependencies with Poetry:
2. Install dependencies:

**Option A: Using Poetry (traditional method)**
```bash
poetry install
```

This will create a virtual environment and install all dependencies.
**Option B: Using UV (faster, modern Python package manager)**
```bash
# Create virtual environment
uv venv

# Activate virtual environment (Linux/macOS)
source .venv/bin/activate
# Or on Windows:
# .venv\Scripts\activate

# Install dependencies
uv sync
```

Both methods will create a virtual environment and install all dependencies.

3. Configure environment:
```bash
Expand Down Expand Up @@ -195,20 +220,34 @@ poetry run python main.py
2. Start the server:

**Development mode (recommended - auto-reloads on changes):**

Using Poetry:
```bash
poetry run uvicorn main:app --reload --port 8000
```

Using UV:
```bash
uv run uvicorn main:app --reload --port 8000
```

**Production mode:**

Using Poetry:
```bash
poetry run python main.py
```

Using UV:
```bash
uv run python main.py
```

**Port Options for production mode:**
- Default: Uses port 8000 (or PORT from .env)
- If port is in use, automatically finds next available port
- Specify custom port: `poetry run python main.py 9000`
- Set in environment: `PORT=9000 poetry run python main.py`
- Specify custom port: `poetry run python main.py 9000` or `uv run python main.py 9000`
- Set in environment: `PORT=9000 poetry run python main.py` or `PORT=9000 uv run python main.py`

## Usage Examples

Expand Down Expand Up @@ -265,13 +304,35 @@ print(response.choices[0].message.content)
# Output: Fast response without tool usage (default behavior)

# Enable tools when you need them (e.g., to read files)
# IMPORTANT: When using with Roo Code, always set enable_tools=True to enable file operations and commands
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "user", "content": "What files are in the current directory?"}
],
extra_body={"enable_tools": True} # Enable tools for file access
)
### 🤖 **Using with Roo Code**

**IMPORTANT**: When using this wrapper with Roo Code, you must **always enable tools** by setting `enable_tools: true` in your requests. Roo Code requires tool access for file operations, command execution, and other core functionality.

```python
# Example: Enabling tools for Roo Code
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "user", "content": "Create a Python web application"}
],
extra_body={"enable_tools": True} # REQUIRED for Roo Code functionality
)
```

Without `enable_tools: true`, Roo Code will be limited to simple Q&A responses and won't be able to:
- Read or write files
- Execute commands
- Browse the web
- Perform any file system operations

print(response.choices[0].message.content)
# Output: Claude will actually read your directory and list the files!

Expand Down Expand Up @@ -457,17 +518,18 @@ See `examples/session_continuity.py` for comprehensive Python examples and `exam
Test all endpoints with a simple script:
```bash
# Make sure server is running first
poetry run python test_endpoints.py
poetry run python test_endpoints.py # or: uv run python test_endpoints.py
```

### 📝 **Basic Test Suite**
Run the comprehensive test suite:
```bash
# Make sure server is running first
poetry run python test_basic.py
poetry run python test_basic.py # or: uv run python test_basic.py

# With API key protection enabled, set TEST_API_KEY:
TEST_API_KEY=your-generated-key poetry run python test_basic.py
# or: TEST_API_KEY=your-generated-key uv run python test_basic.py
```

The test suite automatically detects whether API key protection is enabled and provides helpful guidance for providing the necessary authentication.
Expand All @@ -481,13 +543,13 @@ curl http://localhost:8000/v1/auth/status | python -m json.tool
### ⚙️ **Development Tools**
```bash
# Install development dependencies
poetry install --with dev
poetry install --with dev # or: uv sync --with dev

# Format code
poetry run black .
poetry run black . # or: uv run black .

# Run full tests (when implemented)
poetry run pytest tests/
poetry run pytest tests/ # or: uv run pytest tests/
```

### ✅ **Expected Results**
Expand Down
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '3.8'

services:
app:
build: .
ports:
- "8002:8000"
environment:
- CLAUDE_API_KEY=${CLAUDE_API_KEY}
- PORT=8000
- VERBOSE=true
- DEBUG_MODE=false
volumes:
- ~/.claude:/home/appuser/.claude:rw
restart: unless-stopped
1 change: 1 addition & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ async def generate_streaming_response(
# Handle tools - disabled by default for OpenAI compatibility
if not request.enable_tools:
# Set disallowed_tools to all available tools to disable them

disallowed_tools = ['Task', 'Bash', 'Glob', 'Grep', 'LS', 'exit_plan_mode',
'Read', 'Edit', 'MultiEdit', 'Write', 'NotebookRead',
'NotebookEdit', 'WebFetch', 'TodoRead', 'TodoWrite', 'WebSearch']
Expand Down
2 changes: 1 addition & 1 deletion models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ChatCompletionRequest(BaseModel):
logit_bias: Optional[Dict[str, float]] = None
user: Optional[str] = None
session_id: Optional[str] = Field(default=None, description="Optional session ID for conversation continuity")
enable_tools: Optional[bool] = Field(default=False, description="Enable Claude Code tools (Read, Write, Bash, etc.) - disabled by default for OpenAI compatibility")
enable_tools: Optional[bool] = Field(default=True, description="Enable Claude Code tools (Read, Write, Bash, etc.) - enabled by default")

@field_validator('n')
@classmethod
Expand Down
57 changes: 31 additions & 26 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
[tool.poetry]
[project]
name = "claude-code-openai-wrapper"
version = "1.0.0"
description = "OpenAI API-compatible wrapper for Claude Code"
authors = ["Richard Atkinson <richardatk01@gmail.com>"]
authors = [{name = "Richard Atkinson", email = "richardatk01@gmail.com"}]
readme = "README.md"
license = "MIT"
packages = [{include = "*.py"}]
license = {text = "MIT"}
requires-python = ">=3.10"
dependencies = [
"fastapi>=0.115.0",
"uvicorn[standard]>=0.32.0",
"pydantic>=2.10.0",
"python-dotenv>=1.0.1",
"httpx>=0.27.2",
"sse-starlette>=2.1.3",
"python-multipart>=0.0.12",
"claude-code-sdk>=0.0.14",
]

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.115.0"
uvicorn = {extras = ["standard"], version = "^0.32.0"}
pydantic = "^2.10.0"
python-dotenv = "^1.0.1"
httpx = "^0.27.2"
sse-starlette = "^2.1.3"
python-multipart = "^0.0.12"
claude-code-sdk = "^0.0.14"
[project.optional-dependencies]
dev = [
"black>=24.0.0",
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"requests>=2.32.0",
"openai>=1.0.0",
]

[tool.poetry.group.dev.dependencies]
black = "^24.0.0"
pytest = "^8.0.0"
pytest-asyncio = "^0.23.0"
requests = "^2.32.0"
openai = "^1.0.0"
[project.scripts]
claude-wrapper = "main:run_server"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["."]
include = ["*.py"]

[tool.black]
line-length = 100
target-version = ['py310']

[tool.poetry.scripts]
claude-wrapper = "main:run_server"
target-version = ['py310']
Loading