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.
Files changed (67) hide show
  1. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.claude/CLAUDE.md +10 -3
  2. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/PKG-INFO +49 -110
  3. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/README.md +47 -108
  4. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/README_zh.md +51 -112
  5. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/web_search.py +6 -8
  6. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/pyproject.toml +2 -2
  7. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/__init__.py +1 -1
  8. local_openai2anthropic-0.3.8/src/local_openai2anthropic/config.py +328 -0
  9. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/main.py +2 -2
  10. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/protocol.py +12 -11
  11. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/web_search.py +30 -21
  12. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tavily_client.py +21 -2
  13. local_openai2anthropic-0.3.8/tests/integration/__init__.py +7 -0
  14. local_openai2anthropic-0.3.8/tests/integration/conftest.py +267 -0
  15. local_openai2anthropic-0.3.8/tests/integration/test_messages_endpoint.py +343 -0
  16. local_openai2anthropic-0.3.8/tests/integration/test_server_startup.py +118 -0
  17. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_config.py +285 -0
  18. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_logging.py +78 -74
  19. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_main.py +1 -1
  20. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_protocol.py +4 -0
  21. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router.py +53 -2
  22. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_streaming.py +243 -120
  23. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_server_tools.py +16 -5
  24. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_tavily_client.py +52 -1
  25. local_openai2anthropic-0.3.6/src/local_openai2anthropic/config.py +0 -181
  26. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.github/workflows/publish.yml +0 -0
  27. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.gitignore +0 -0
  28. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/.reports/dead-code-analysis.md +0 -0
  29. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/LICENSE +0 -0
  30. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/basic_chat.py +0 -0
  31. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/streaming.py +0 -0
  32. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/thinking_mode.py +0 -0
  33. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/tool_calling.py +0 -0
  34. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/examples/vision.py +0 -0
  35. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/__main__.py +0 -0
  36. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/converter.py +0 -0
  37. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/daemon.py +0 -0
  38. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/daemon_runner.py +0 -0
  39. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/openai_types.py +0 -0
  40. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/router.py +0 -0
  41. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/__init__.py +0 -0
  42. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/server_tools/base.py +0 -0
  43. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/streaming/__init__.py +0 -0
  44. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/streaming/handler.py +0 -0
  45. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tools/__init__.py +0 -0
  46. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/tools/handler.py +0 -0
  47. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/utils/__init__.py +0 -0
  48. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/src/local_openai2anthropic/utils/tokens.py +0 -0
  49. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/__init__.py +0 -0
  50. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage.json +0 -0
  51. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_detailed.json +0 -0
  52. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_report.json +0 -0
  53. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_report_new.json +0 -0
  54. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/coverage/coverage_summary.json +0 -0
  55. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_converter.py +0 -0
  56. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_converter_edge_cases.py +0 -0
  57. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon.py +0 -0
  58. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon_advanced.py +0 -0
  59. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_daemon_runner.py +0 -0
  60. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_e2e_multimodel.py +0 -0
  61. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_e2e_websearch.py +0 -0
  62. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_integration.py +0 -0
  63. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_openai_types.py +0 -0
  64. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_comprehensive.py +0 -0
  65. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_router_edge_cases.py +0 -0
  66. {local_openai2anthropic-0.3.6 → local_openai2anthropic-0.3.8}/tests/test_upstream.sh +0 -0
  67. {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. 创建标签: `git tag vx.y.z`
102
- 5. 推送代码和标签: `git push && git push origin vx.y.z`
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.6
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>=2.0.0; python_version < '3.11'
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
- **Option A: Run in background (recommended)**
114
+ Run the following command to start the proxy in background mode:
115
115
 
116
116
  ```bash
117
- export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1 # Your local LLM endpoint
118
- export OA2A_OPENAI_API_KEY=dummy # Any value, not used by local backends
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
- oa2a start # Start server in background
121
- # Server starts at http://localhost:8080
126
+ After configuration, the server starts at `http://localhost:8080`.
122
127
 
123
- # View logs
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
- export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
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** - Give your local LLM internet access (see below)
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 Capability 🔍
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
- ### The Problem
249
+ Enable web search for your local LLM using [Tavily](https://tavily.com).
256
250
 
257
- | Scenario | Web Search Available? |
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
- ### How It Works
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
- 1. **Get a free API key** at [tavily.com](https://tavily.com) - generous free tier available
260
+ 3. Use `web_search_20250305` tool in your app - the proxy handles search automatically.
276
261
 
277
- 2. **Configure the proxy:**
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
- | Variable | Required | Default | Description |
268
+ Config file: `~/.oa2a/config.toml` (auto-created on first run)
269
+
270
+ | Option | Required | Default | Description |
334
271
  |----------|----------|---------|-------------|
335
- | `OA2A_OPENAI_BASE_URL` | ✅ | - | Your local LLM's OpenAI-compatible endpoint |
336
- | `OA2A_OPENAI_API_KEY` | ✅ | - | Any value (local backends usually ignore this) |
337
- | `OA2A_PORT` | ❌ | 8080 | Proxy server port |
338
- | `OA2A_HOST` | ❌ | 0.0.0.0 | Proxy server host |
339
- | `OA2A_TAVILY_API_KEY` | ❌ | - | Enable web search ([tavily.com](https://tavily.com)) |
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
- **Option A: Run in background (recommended)**
78
+ Run the following command to start the proxy in background mode:
79
79
 
80
80
  ```bash
81
- export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1 # Your local LLM endpoint
82
- export OA2A_OPENAI_API_KEY=dummy # Any value, not used by local backends
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
- oa2a start # Start server in background
85
- # Server starts at http://localhost:8080
90
+ After configuration, the server starts at `http://localhost:8080`.
86
91
 
87
- # View logs
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
- export OA2A_OPENAI_BASE_URL=http://localhost:8000/v1
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** - Give your local LLM internet access (see below)
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 Capability 🔍
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
- ### The Problem
213
+ Enable web search for your local LLM using [Tavily](https://tavily.com).
220
214
 
221
- | Scenario | Web Search Available? |
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
- ### How It Works
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
- 1. **Get a free API key** at [tavily.com](https://tavily.com) - generous free tier available
224
+ 3. Use `web_search_20250305` tool in your app - the proxy handles search automatically.
240
225
 
241
- 2. **Configure the proxy:**
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
- | Variable | Required | Default | Description |
232
+ Config file: `~/.oa2a/config.toml` (auto-created on first run)
233
+
234
+ | Option | Required | Default | Description |
298
235
  |----------|----------|---------|-------------|
299
- | `OA2A_OPENAI_BASE_URL` | ✅ | - | Your local LLM's OpenAI-compatible endpoint |
300
- | `OA2A_OPENAI_API_KEY` | ✅ | - | Any value (local backends usually ignore this) |
301
- | `OA2A_PORT` | ❌ | 8080 | Proxy server port |
302
- | `OA2A_HOST` | ❌ | 0.0.0.0 | Proxy server host |
303
- | `OA2A_TAVILY_API_KEY` | ❌ | - | Enable web search ([tavily.com](https://tavily.com)) |
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