local-openai2anthropic 0.3.6__tar.gz → 0.3.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.claude/CLAUDE.md +10 -3
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/PKG-INFO +49 -110
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/README.md +47 -108
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/README_zh.md +51 -112
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/web_search.py +6 -8
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/pyproject.toml +2 -2
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/__init__.py +1 -1
- local_openai2anthropic-0.3.8/src/local_openai2anthropic/config.py +328 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/main.py +2 -2
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/protocol.py +12 -11
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/web_search.py +30 -21
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tavily_client.py +21 -2
- local_openai2anthropic-0.3.8/tests/integration/__init__.py +7 -0
- local_openai2anthropic-0.3.8/tests/integration/conftest.py +267 -0
- local_openai2anthropic-0.3.8/tests/integration/test_messages_endpoint.py +343 -0
- local_openai2anthropic-0.3.8/tests/integration/test_server_startup.py +118 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_config.py +285 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_logging.py +78 -74
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_main.py +1 -1
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_protocol.py +4 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router.py +53 -2
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_streaming.py +243 -120
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_server_tools.py +16 -5
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_tavily_client.py +52 -1
- local_openai2anthropic-0.3.6/src/local_openai2anthropic/config.py +0 -181
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.github/workflows/publish.yml +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.gitignore +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.reports/dead-code-analysis.md +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/LICENSE +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/basic_chat.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/streaming.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/thinking_mode.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/tool_calling.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/vision.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/__main__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/converter.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/daemon.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/daemon_runner.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/openai_types.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/router.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/__init__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/base.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/streaming/__init__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/streaming/handler.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tools/__init__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tools/handler.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/utils/__init__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/utils/tokens.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/__init__.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage.json +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_detailed.json +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_report.json +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_report_new.json +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_summary.json +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_converter.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_converter_edge_cases.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon_advanced.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon_runner.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_e2e_multimodel.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_e2e_websearch.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_integration.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_openai_types.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_comprehensive.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_edge_cases.py +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_upstream.sh +0 -0
- {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/uv.lock +0 -0
|
@@ -77,7 +77,7 @@ websearch_max_uses = 5
|
|
|
77
77
|
- **格式**: `assert app.version == "x.y.z"`
|
|
78
78
|
- **示例**: `assert app.version == "0.3.5"`
|
|
79
79
|
|
|
80
|
-
### 6. Git Tag
|
|
80
|
+
### 6. Git Tag (重要:必须推送才能触发发布)
|
|
81
81
|
- **格式**: `vx.y.z`
|
|
82
82
|
- **示例**: `v0.3.5`
|
|
83
83
|
- **命令**:
|
|
@@ -85,6 +85,7 @@ websearch_max_uses = 5
|
|
|
85
85
|
git tag v0.3.5
|
|
86
86
|
git push origin v0.3.5
|
|
87
87
|
```
|
|
88
|
+
- **注意**: 推送 tag 会触发 GitHub Actions 自动发布到 PyPI,不要忘记推送 tag!
|
|
88
89
|
|
|
89
90
|
## 版本号格式
|
|
90
91
|
|
|
@@ -98,10 +99,16 @@ websearch_max_uses = 5
|
|
|
98
99
|
1. 更新上述所有文件中的版本号
|
|
99
100
|
2. 运行测试确保通过: `pytest`
|
|
100
101
|
3. 提交更改: `git commit -m "chore(release): bump version to x.y.z"`
|
|
101
|
-
4.
|
|
102
|
-
5.
|
|
102
|
+
4. 推送代码: `git push`
|
|
103
|
+
5. **创建并推送标签** (触发 GitHub Actions 发布):
|
|
104
|
+
```bash
|
|
105
|
+
git tag vx.y.z
|
|
106
|
+
git push origin vx.y.z
|
|
107
|
+
```
|
|
103
108
|
6. GitHub Actions 将自动发布到 PyPI
|
|
104
109
|
|
|
110
|
+
**重要**: 第 5 步推送 tag 是触发自动发布的关键步骤,不要忘记!
|
|
111
|
+
|
|
105
112
|
## 代码提交规范
|
|
106
113
|
|
|
107
114
|
### 测试覆盖率要求
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-openai2anthropic
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: A lightweight proxy server that converts Anthropic Messages API to OpenAI API
|
|
5
5
|
Project-URL: Homepage, https://github.com/dongfangzan/local-openai2anthropic
|
|
6
6
|
Project-URL: Repository, https://github.com/dongfangzan/local-openai2anthropic
|
|
@@ -24,7 +24,7 @@ Requires-Dist: httpx>=0.25.0
|
|
|
24
24
|
Requires-Dist: openai>=1.30.0
|
|
25
25
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
26
26
|
Requires-Dist: pydantic>=2.0.0
|
|
27
|
-
Requires-Dist: tomli>=
|
|
27
|
+
Requires-Dist: tomli-w>=1.0.0
|
|
28
28
|
Requires-Dist: uvicorn[standard]>=0.23.0
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
@@ -109,37 +109,47 @@ Examples:
|
|
|
109
109
|
|
|
110
110
|
> **Note:** If you're using [Ollama](https://ollama.com), it natively supports the Anthropic API format, so you don't need this proxy. Just point your Claude SDK directly to `http://localhost:11434/v1`.
|
|
111
111
|
|
|
112
|
-
### 3. Start the Proxy
|
|
112
|
+
### 3. Start the Proxy (Recommended)
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
Run the following command to start the proxy in background mode:
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
oa2a start
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**First-time setup**: If `~/.oa2a/config.toml` doesn't exist, an interactive setup wizard will guide you through:
|
|
121
|
+
- Enter your OpenAI API Key (for the local LLM backend)
|
|
122
|
+
- Enter the base URL of your local LLM (e.g., `http://localhost:8000/v1`)
|
|
123
|
+
- Configure server host and port (optional)
|
|
124
|
+
- Set server API key for authentication (optional)
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
# Server starts at http://localhost:8080
|
|
126
|
+
After configuration, the server starts at `http://localhost:8080`.
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
**Daemon management commands:**
|
|
129
|
+
|
|
130
|
+
```bash
|
|
124
131
|
oa2a logs # Show last 50 lines of logs
|
|
125
132
|
oa2a logs -f # Follow logs in real-time (Ctrl+C to exit)
|
|
126
|
-
|
|
127
|
-
# Check status
|
|
128
133
|
oa2a status # Check if server is running
|
|
129
|
-
|
|
130
|
-
# Stop server
|
|
131
134
|
oa2a stop # Stop background server
|
|
132
|
-
|
|
133
|
-
# Restart server
|
|
134
135
|
oa2a restart # Restart with same settings
|
|
135
136
|
```
|
|
136
137
|
|
|
138
|
+
**Manual Configuration**
|
|
139
|
+
|
|
140
|
+
You can also manually create/edit the config file at `~/.oa2a/config.toml`:
|
|
141
|
+
|
|
142
|
+
```toml
|
|
143
|
+
# OA2A Configuration File
|
|
144
|
+
openai_api_key = "dummy"
|
|
145
|
+
openai_base_url = "http://localhost:8000/v1"
|
|
146
|
+
host = "0.0.0.0"
|
|
147
|
+
port = 8080
|
|
148
|
+
```
|
|
149
|
+
|
|
137
150
|
**Option B: Run in foreground**
|
|
138
151
|
|
|
139
152
|
```bash
|
|
140
|
-
export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
|
|
141
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
142
|
-
|
|
143
153
|
oa2a # Run server in foreground (blocking)
|
|
144
154
|
# Press Ctrl+C to stop
|
|
145
155
|
```
|
|
@@ -195,15 +205,6 @@ You can configure [Claude Code](https://github.com/anthropics/claude-code) to us
|
|
|
195
205
|
| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Default model for Haiku mode |
|
|
196
206
|
| `ANTHROPIC_REASONING_MODEL` | Default model for reasoning tasks |
|
|
197
207
|
|
|
198
|
-
2. **Or set environment variables** before running Claude Code:
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
export ANTHROPIC_BASE_URL=http://localhost:8080
|
|
202
|
-
export ANTHROPIC_API_KEY=dummy-key
|
|
203
|
-
|
|
204
|
-
claude
|
|
205
|
-
```
|
|
206
|
-
|
|
207
208
|
### Complete Workflow Example
|
|
208
209
|
|
|
209
210
|
Make sure `~/.claude/settings.json` is configured as described above.
|
|
@@ -215,10 +216,7 @@ vllm serve meta-llama/Llama-2-7b-chat-hf
|
|
|
215
216
|
|
|
216
217
|
Terminal 2 - Start the proxy (background mode):
|
|
217
218
|
```bash
|
|
218
|
-
|
|
219
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
220
|
-
export OA2A_TAVILY_API_KEY="tvly-your-tavily-api-key" # Optional: enable web search
|
|
221
|
-
|
|
219
|
+
# First run: interactive setup wizard will guide you
|
|
222
220
|
oa2a start
|
|
223
221
|
```
|
|
224
222
|
|
|
@@ -241,102 +239,43 @@ oa2a stop
|
|
|
241
239
|
- ✅ **Streaming responses** - Real-time token streaming via SSE
|
|
242
240
|
- ✅ **Tool calling** - Local LLM function calling support
|
|
243
241
|
- ✅ **Vision models** - Multi-modal input for vision-capable models
|
|
244
|
-
- ✅ **Web Search** -
|
|
242
|
+
- ✅ **Web Search** - Built-in Tavily web search for local models
|
|
245
243
|
- ✅ **Thinking mode** - Supports reasoning/thinking model outputs
|
|
246
244
|
|
|
247
245
|
---
|
|
248
246
|
|
|
249
|
-
## Web Search
|
|
250
|
-
|
|
251
|
-
**Bridge the gap: Give your local LLM the web search power that Claude Code users enjoy!**
|
|
252
|
-
|
|
253
|
-
When using locally-hosted models with Claude Code, you lose access to the built-in web search tool. This proxy fills that gap by providing a server-side web search implementation powered by [Tavily](https://tavily.com).
|
|
247
|
+
## Web Search 🔍
|
|
254
248
|
|
|
255
|
-
|
|
249
|
+
Enable web search for your local LLM using [Tavily](https://tavily.com).
|
|
256
250
|
|
|
257
|
-
|
|
258
|
-
|----------|----------------------|
|
|
259
|
-
| Using Claude (cloud) in Claude Code | ✅ Built-in |
|
|
260
|
-
| Using local vLLM/SGLang in Claude Code | ❌ Not available |
|
|
261
|
-
| **Using this proxy + local LLM** | ✅ **Enabled via Tavily** |
|
|
251
|
+
**Setup:**
|
|
262
252
|
|
|
263
|
-
|
|
253
|
+
1. Get a free API key at [tavily.com](https://tavily.com)
|
|
264
254
|
|
|
255
|
+
2. Add to your config (`~/.oa2a/config.toml`):
|
|
256
|
+
```toml
|
|
257
|
+
tavily_api_key = "tvly-your-api-key"
|
|
265
258
|
```
|
|
266
|
-
Claude Code → Anthropic SDK → This Proxy → Local LLM
|
|
267
|
-
↓
|
|
268
|
-
Tavily API (Web Search)
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
The proxy intercepts `web_search_20250305` tool calls and handles them directly, regardless of whether your local model supports web search natively.
|
|
272
|
-
|
|
273
|
-
### Setup Tavily Search
|
|
274
259
|
|
|
275
|
-
|
|
260
|
+
3. Use `web_search_20250305` tool in your app - the proxy handles search automatically.
|
|
276
261
|
|
|
277
|
-
|
|
278
|
-
```bash
|
|
279
|
-
export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
|
|
280
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
281
|
-
export OA2A_TAVILY_API_KEY="tvly-your-tavily-api-key" # Enable web search
|
|
282
|
-
|
|
283
|
-
oa2a
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
3. **Use in your app:**
|
|
287
|
-
```python
|
|
288
|
-
import anthropic
|
|
289
|
-
|
|
290
|
-
client = anthropic.Anthropic(
|
|
291
|
-
base_url="http://localhost:8080",
|
|
292
|
-
api_key="dummy-key",
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
message = client.messages.create(
|
|
296
|
-
model="meta-llama/Llama-2-7b-chat-hf",
|
|
297
|
-
max_tokens=1024,
|
|
298
|
-
tools=[
|
|
299
|
-
{
|
|
300
|
-
"name": "web_search_20250305",
|
|
301
|
-
"description": "Search the web for current information",
|
|
302
|
-
"input_schema": {
|
|
303
|
-
"type": "object",
|
|
304
|
-
"properties": {
|
|
305
|
-
"query": {"type": "string", "description": "Search query"},
|
|
306
|
-
},
|
|
307
|
-
"required": ["query"],
|
|
308
|
-
},
|
|
309
|
-
}
|
|
310
|
-
],
|
|
311
|
-
messages=[{"role": "user", "content": "What happened in AI today?"}],
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
if message.stop_reason == "tool_use":
|
|
315
|
-
tool_use = message.content[-1]
|
|
316
|
-
print(f"Searching: {tool_use.input}")
|
|
317
|
-
# The proxy automatically calls Tavily and returns results
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### Tavily Configuration Options
|
|
321
|
-
|
|
322
|
-
| Variable | Default | Description |
|
|
323
|
-
|----------|---------|-------------|
|
|
324
|
-
| `OA2A_TAVILY_API_KEY` | - | Your Tavily API key ([get free at tavily.com](https://tavily.com)) |
|
|
325
|
-
| `OA2A_TAVILY_MAX_RESULTS` | 5 | Number of search results to return |
|
|
326
|
-
| `OA2A_TAVILY_TIMEOUT` | 30 | Search timeout in seconds |
|
|
327
|
-
| `OA2A_WEBSEARCH_MAX_USES` | 5 | Max search calls per request |
|
|
262
|
+
**Options:** `tavily_max_results` (default: 5), `tavily_timeout` (default: 30), `websearch_max_uses` (default: 5)
|
|
328
263
|
|
|
329
264
|
---
|
|
330
265
|
|
|
331
266
|
## Configuration
|
|
332
267
|
|
|
333
|
-
|
|
268
|
+
Config file: `~/.oa2a/config.toml` (auto-created on first run)
|
|
269
|
+
|
|
270
|
+
| Option | Required | Default | Description |
|
|
334
271
|
|----------|----------|---------|-------------|
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
339
|
-
| `
|
|
272
|
+
| `openai_base_url` | ✅ | - | Local LLM endpoint (e.g., `http://localhost:8000/v1`) |
|
|
273
|
+
| `openai_api_key` | ✅ | - | API key for local LLM |
|
|
274
|
+
| `port` | ❌ | 8080 | Proxy port |
|
|
275
|
+
| `host` | ❌ | 0.0.0.0 | Proxy host |
|
|
276
|
+
| `api_key` | ❌ | - | Auth key for this proxy |
|
|
277
|
+
| `tavily_api_key` | ❌ | - | Enable web search |
|
|
278
|
+
| `log_level` | ❌ | INFO | DEBUG, INFO, WARNING, ERROR |
|
|
340
279
|
|
|
341
280
|
---
|
|
342
281
|
|
|
@@ -73,37 +73,47 @@ Examples:
|
|
|
73
73
|
|
|
74
74
|
> **Note:** If you're using [Ollama](https://ollama.com), it natively supports the Anthropic API format, so you don't need this proxy. Just point your Claude SDK directly to `http://localhost:11434/v1`.
|
|
75
75
|
|
|
76
|
-
### 3. Start the Proxy
|
|
76
|
+
### 3. Start the Proxy (Recommended)
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Run the following command to start the proxy in background mode:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
oa2a start
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**First-time setup**: If `~/.oa2a/config.toml` doesn't exist, an interactive setup wizard will guide you through:
|
|
85
|
+
- Enter your OpenAI API Key (for the local LLM backend)
|
|
86
|
+
- Enter the base URL of your local LLM (e.g., `http://localhost:8000/v1`)
|
|
87
|
+
- Configure server host and port (optional)
|
|
88
|
+
- Set server API key for authentication (optional)
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
# Server starts at http://localhost:8080
|
|
90
|
+
After configuration, the server starts at `http://localhost:8080`.
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
**Daemon management commands:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
88
95
|
oa2a logs # Show last 50 lines of logs
|
|
89
96
|
oa2a logs -f # Follow logs in real-time (Ctrl+C to exit)
|
|
90
|
-
|
|
91
|
-
# Check status
|
|
92
97
|
oa2a status # Check if server is running
|
|
93
|
-
|
|
94
|
-
# Stop server
|
|
95
98
|
oa2a stop # Stop background server
|
|
96
|
-
|
|
97
|
-
# Restart server
|
|
98
99
|
oa2a restart # Restart with same settings
|
|
99
100
|
```
|
|
100
101
|
|
|
102
|
+
**Manual Configuration**
|
|
103
|
+
|
|
104
|
+
You can also manually create/edit the config file at `~/.oa2a/config.toml`:
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
# OA2A Configuration File
|
|
108
|
+
openai_api_key = "dummy"
|
|
109
|
+
openai_base_url = "http://localhost:8000/v1"
|
|
110
|
+
host = "0.0.0.0"
|
|
111
|
+
port = 8080
|
|
112
|
+
```
|
|
113
|
+
|
|
101
114
|
**Option B: Run in foreground**
|
|
102
115
|
|
|
103
116
|
```bash
|
|
104
|
-
export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
|
|
105
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
106
|
-
|
|
107
117
|
oa2a # Run server in foreground (blocking)
|
|
108
118
|
# Press Ctrl+C to stop
|
|
109
119
|
```
|
|
@@ -159,15 +169,6 @@ You can configure [Claude Code](https://github.com/anthropics/claude-code) to us
|
|
|
159
169
|
| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Default model for Haiku mode |
|
|
160
170
|
| `ANTHROPIC_REASONING_MODEL` | Default model for reasoning tasks |
|
|
161
171
|
|
|
162
|
-
2. **Or set environment variables** before running Claude Code:
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
export ANTHROPIC_BASE_URL=http://localhost:8080
|
|
166
|
-
export ANTHROPIC_API_KEY=dummy-key
|
|
167
|
-
|
|
168
|
-
claude
|
|
169
|
-
```
|
|
170
|
-
|
|
171
172
|
### Complete Workflow Example
|
|
172
173
|
|
|
173
174
|
Make sure `~/.claude/settings.json` is configured as described above.
|
|
@@ -179,10 +180,7 @@ vllm serve meta-llama/Llama-2-7b-chat-hf
|
|
|
179
180
|
|
|
180
181
|
Terminal 2 - Start the proxy (background mode):
|
|
181
182
|
```bash
|
|
182
|
-
|
|
183
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
184
|
-
export OA2A_TAVILY_API_KEY="tvly-your-tavily-api-key" # Optional: enable web search
|
|
185
|
-
|
|
183
|
+
# First run: interactive setup wizard will guide you
|
|
186
184
|
oa2a start
|
|
187
185
|
```
|
|
188
186
|
|
|
@@ -205,102 +203,43 @@ oa2a stop
|
|
|
205
203
|
- ✅ **Streaming responses** - Real-time token streaming via SSE
|
|
206
204
|
- ✅ **Tool calling** - Local LLM function calling support
|
|
207
205
|
- ✅ **Vision models** - Multi-modal input for vision-capable models
|
|
208
|
-
- ✅ **Web Search** -
|
|
206
|
+
- ✅ **Web Search** - Built-in Tavily web search for local models
|
|
209
207
|
- ✅ **Thinking mode** - Supports reasoning/thinking model outputs
|
|
210
208
|
|
|
211
209
|
---
|
|
212
210
|
|
|
213
|
-
## Web Search
|
|
214
|
-
|
|
215
|
-
**Bridge the gap: Give your local LLM the web search power that Claude Code users enjoy!**
|
|
216
|
-
|
|
217
|
-
When using locally-hosted models with Claude Code, you lose access to the built-in web search tool. This proxy fills that gap by providing a server-side web search implementation powered by [Tavily](https://tavily.com).
|
|
211
|
+
## Web Search 🔍
|
|
218
212
|
|
|
219
|
-
|
|
213
|
+
Enable web search for your local LLM using [Tavily](https://tavily.com).
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
|----------|----------------------|
|
|
223
|
-
| Using Claude (cloud) in Claude Code | ✅ Built-in |
|
|
224
|
-
| Using local vLLM/SGLang in Claude Code | ❌ Not available |
|
|
225
|
-
| **Using this proxy + local LLM** | ✅ **Enabled via Tavily** |
|
|
215
|
+
**Setup:**
|
|
226
216
|
|
|
227
|
-
|
|
217
|
+
1. Get a free API key at [tavily.com](https://tavily.com)
|
|
228
218
|
|
|
219
|
+
2. Add to your config (`~/.oa2a/config.toml`):
|
|
220
|
+
```toml
|
|
221
|
+
tavily_api_key = "tvly-your-api-key"
|
|
229
222
|
```
|
|
230
|
-
Claude Code → Anthropic SDK → This Proxy → Local LLM
|
|
231
|
-
↓
|
|
232
|
-
Tavily API (Web Search)
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
The proxy intercepts `web_search_20250305` tool calls and handles them directly, regardless of whether your local model supports web search natively.
|
|
236
|
-
|
|
237
|
-
### Setup Tavily Search
|
|
238
223
|
|
|
239
|
-
|
|
224
|
+
3. Use `web_search_20250305` tool in your app - the proxy handles search automatically.
|
|
240
225
|
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
|
|
244
|
-
export OA2A_OPENAI_API_KEY=dummy
|
|
245
|
-
export OA2A_TAVILY_API_KEY="tvly-your-tavily-api-key" # Enable web search
|
|
246
|
-
|
|
247
|
-
oa2a
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
3. **Use in your app:**
|
|
251
|
-
```python
|
|
252
|
-
import anthropic
|
|
253
|
-
|
|
254
|
-
client = anthropic.Anthropic(
|
|
255
|
-
base_url="http://localhost:8080",
|
|
256
|
-
api_key="dummy-key",
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
message = client.messages.create(
|
|
260
|
-
model="meta-llama/Llama-2-7b-chat-hf",
|
|
261
|
-
max_tokens=1024,
|
|
262
|
-
tools=[
|
|
263
|
-
{
|
|
264
|
-
"name": "web_search_20250305",
|
|
265
|
-
"description": "Search the web for current information",
|
|
266
|
-
"input_schema": {
|
|
267
|
-
"type": "object",
|
|
268
|
-
"properties": {
|
|
269
|
-
"query": {"type": "string", "description": "Search query"},
|
|
270
|
-
},
|
|
271
|
-
"required": ["query"],
|
|
272
|
-
},
|
|
273
|
-
}
|
|
274
|
-
],
|
|
275
|
-
messages=[{"role": "user", "content": "What happened in AI today?"}],
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
if message.stop_reason == "tool_use":
|
|
279
|
-
tool_use = message.content[-1]
|
|
280
|
-
print(f"Searching: {tool_use.input}")
|
|
281
|
-
# The proxy automatically calls Tavily and returns results
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Tavily Configuration Options
|
|
285
|
-
|
|
286
|
-
| Variable | Default | Description |
|
|
287
|
-
|----------|---------|-------------|
|
|
288
|
-
| `OA2A_TAVILY_API_KEY` | - | Your Tavily API key ([get free at tavily.com](https://tavily.com)) |
|
|
289
|
-
| `OA2A_TAVILY_MAX_RESULTS` | 5 | Number of search results to return |
|
|
290
|
-
| `OA2A_TAVILY_TIMEOUT` | 30 | Search timeout in seconds |
|
|
291
|
-
| `OA2A_WEBSEARCH_MAX_USES` | 5 | Max search calls per request |
|
|
226
|
+
**Options:** `tavily_max_results` (default: 5), `tavily_timeout` (default: 30), `websearch_max_uses` (default: 5)
|
|
292
227
|
|
|
293
228
|
---
|
|
294
229
|
|
|
295
230
|
## Configuration
|
|
296
231
|
|
|
297
|
-
|
|
232
|
+
Config file: `~/.oa2a/config.toml` (auto-created on first run)
|
|
233
|
+
|
|
234
|
+
| Option | Required | Default | Description |
|
|
298
235
|
|----------|----------|---------|-------------|
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
236
|
+
| `openai_base_url` | ✅ | - | Local LLM endpoint (e.g., `http://localhost:8000/v1`) |
|
|
237
|
+
| `openai_api_key` | ✅ | - | API key for local LLM |
|
|
238
|
+
| `port` | ❌ | 8080 | Proxy port |
|
|
239
|
+
| `host` | ❌ | 0.0.0.0 | Proxy host |
|
|
240
|
+
| `api_key` | ❌ | - | Auth key for this proxy |
|
|
241
|
+
| `tavily_api_key` | ❌ | - | Enable web search |
|
|
242
|
+
| `log_level` | ❌ | INFO | DEBUG, INFO, WARNING, ERROR |
|
|
304
243
|
|
|
305
244
|
---
|
|
306
245
|
|