checkpoint: pre-audit baseline state
This commit is contained in:
421
docs/architecture/OPENROUTER_BYOK_CONTEXT_STREAMING_SPEC.md
Normal file
421
docs/architecture/OPENROUTER_BYOK_CONTEXT_STREAMING_SPEC.md
Normal file
@@ -0,0 +1,421 @@
|
||||
# OpenRouter BYOK Context and Streaming Spec
|
||||
|
||||
**Date:** 2026-06-05
|
||||
**Status:** Proposed implementation direction
|
||||
**Goal:** Replace local bash/proxy-first text generation with an OpenRouter BYOK-first API path while preserving article continuity and improving streamed editor UX.
|
||||
|
||||
## Decision
|
||||
|
||||
Use OpenRouter as the primary text transport for `chat`, `clarity`, `planning`, `writing`, and `refinement`, with the user's OpenRouter workspace configured for BYOK provider keys.
|
||||
|
||||
The plugin should continue to store conversation and article memory in WordPress. OpenRouter should be treated as a stateless model gateway: it streams model output, returns usage metadata, applies provider routing, and can cache identical responses. It does not own article continuity.
|
||||
|
||||
Local Backend should become optional or legacy. It is useful for experiments, but it should not be the recommended default because it asks users to run local scripts/proxy tooling and creates trust friction.
|
||||
|
||||
## Current Implementation Snapshot
|
||||
|
||||
The current plugin already has most of the foundation:
|
||||
|
||||
- `includes/interface-ai-provider.php` defines `chat()`, `chat_stream()`, `generate_image()`, `is_configured()`, `test_connection()`, and `supports_task_type()`.
|
||||
- `includes/class-provider-manager.php` routes each task through configured providers and already prevents silent OpenRouter spend when fallback is disabled.
|
||||
- `includes/class-openrouter-provider.php` supports non-streaming and streaming chat completions through OpenRouter.
|
||||
- `includes/class-local-backend-provider.php` supports a local proxy at `/v1/messages`, including a cURL streaming parser and plain JSON fallback.
|
||||
- `includes/class-conversation-manager.php` stores sessions in `{$wpdb->prefix}wpaw_conversations` with `messages` and `context` JSON fields.
|
||||
- `includes/class-context-service.php` is already documented as the single source of truth for messages, `_wpaw_plan`, `_wpaw_post_config`, and legacy chat migration.
|
||||
- `includes/class-gutenberg-sidebar.php` exposes the main REST routes: `/chat`, `/generate-plan`, `/revise-plan`, `/execute-article`, `/refine-block`, `/refine-from-chat`, `/summarize-context`, `/detect-intent`, `/writing-state/{post_id}`, and conversation routes.
|
||||
- Cost tracking already records `post_id`, `session_id`, `model`, `provider`, `action`, input tokens, output tokens, cost, and status.
|
||||
|
||||
The main gap is not lack of streaming. The gap is that several routes still accept full `chatHistory` from the browser and inject it into prompts. That makes continuity depend on the browser payload and can re-send too much context.
|
||||
|
||||
## Product Positioning
|
||||
|
||||
Recommended provider settings:
|
||||
|
||||
```php
|
||||
'task_providers' => array(
|
||||
'chat' => 'openrouter',
|
||||
'clarity' => 'openrouter',
|
||||
'planning' => 'openrouter',
|
||||
'writing' => 'openrouter',
|
||||
'refinement' => 'openrouter',
|
||||
'image' => 'openrouter',
|
||||
),
|
||||
'allow_openrouter_fallback' => false,
|
||||
```
|
||||
|
||||
The UI copy should present this as:
|
||||
|
||||
- Connect OpenRouter API key.
|
||||
- Configure BYOK provider keys inside OpenRouter.
|
||||
- Stream directly into WordPress.
|
||||
- Keep all article memory in WordPress.
|
||||
- Local Backend is advanced or legacy.
|
||||
|
||||
OpenRouter BYOK details to reflect in docs:
|
||||
|
||||
- BYOK lets users route requests through their own provider keys while still using OpenRouter's API surface.
|
||||
- BYOK provider keys are encrypted and used for requests routed through the selected provider.
|
||||
- OpenRouter's BYOK fee is documented as 5 percent of the normal OpenRouter model/provider cost, waived for the first 1M BYOK requests per month.
|
||||
- Users can prevent fallback to OpenRouter shared endpoints by enabling the provider key's "Always use for this provider" behavior in OpenRouter.
|
||||
- OpenRouter usage data is returned in normal responses and in the last SSE message for streamed responses.
|
||||
|
||||
Sources:
|
||||
|
||||
- https://openrouter.ai/docs/guides/overview/auth/byok
|
||||
- https://openrouter.ai/docs/cookbook/administration/usage-accounting
|
||||
- https://openrouter.ai/docs/guides/features/response-caching/
|
||||
|
||||
## Continuity Ownership
|
||||
|
||||
Continuity is owned by WordPress, not OpenRouter.
|
||||
|
||||
Persisted state:
|
||||
|
||||
| State | Current storage | Keep or change |
|
||||
| --- | --- | --- |
|
||||
| Conversation messages | `wpaw_conversations.messages` | Keep |
|
||||
| Session context | `wpaw_conversations.context` | Extend |
|
||||
| Article plan | `_wpaw_plan` post meta | Keep |
|
||||
| Post config | `_wpaw_post_config` post meta | Keep |
|
||||
| Writing state | `_wpaw_writing_status`, `_wpaw_current_section`, `_wpaw_sections_written`, `_wpaw_resume_token` | Keep |
|
||||
| Section to block mapping | `_wpaw_section_blocks` | Keep |
|
||||
| Lightweight post memory | `_wpaw_memory` | Extend or migrate into `context` |
|
||||
| Cost and token usage | `wpaw_cost_tracking` | Extend |
|
||||
|
||||
Recommended new session context shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"working_summary": {
|
||||
"text": "The article is about ...",
|
||||
"updated_at": "2026-06-05T10:30:00+07:00",
|
||||
"source_message_count": 14
|
||||
},
|
||||
"decisions": [
|
||||
{
|
||||
"type": "accept",
|
||||
"target": "outline.section.2",
|
||||
"summary": "Keep the practical checklist framing.",
|
||||
"created_at": "2026-06-05T10:31:00+07:00"
|
||||
}
|
||||
],
|
||||
"rejections": [
|
||||
{
|
||||
"target": "outline.section.4",
|
||||
"summary": "Too generic; needs concrete WordPress examples.",
|
||||
"created_at": "2026-06-05T10:32:00+07:00"
|
||||
}
|
||||
],
|
||||
"research_notes": [
|
||||
{
|
||||
"source": "manual",
|
||||
"title": "User supplied constraint",
|
||||
"excerpt": "Avoid local bash instructions in the default UX.",
|
||||
"tags": ["trust", "onboarding"]
|
||||
}
|
||||
],
|
||||
"token_policy": {
|
||||
"max_recent_messages": 6,
|
||||
"max_summary_tokens": 600,
|
||||
"max_research_snippets": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Store this in `wpaw_conversations.context` first. Avoid adding a new custom table until `context` becomes too large or needs relational querying.
|
||||
|
||||
## Context Builder
|
||||
|
||||
Add a dedicated builder instead of assembling continuity inside each REST handler.
|
||||
|
||||
New file:
|
||||
|
||||
```text
|
||||
includes/class-context-builder.php
|
||||
```
|
||||
|
||||
Primary API:
|
||||
|
||||
```php
|
||||
class WP_Agentic_Writer_Context_Builder {
|
||||
public function build_for_task( $task, $session_id, $post_id, $request_params = array() ) {
|
||||
// Returns normalized prompt parts for chat, planning, writing, refinement, SEO.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Return shape:
|
||||
|
||||
```php
|
||||
array(
|
||||
'system_context' => 'Stable task and policy instructions.',
|
||||
'working_context' => 'Compact summary, decisions, plan, selected post config.',
|
||||
'active_content' => 'The exact section/block/article slice being edited.',
|
||||
'research_context' => 'Only relevant excerpts.',
|
||||
'audit' => array(
|
||||
'included_recent_messages' => 6,
|
||||
'included_research_items' => 3,
|
||||
'estimated_input_tokens' => 2200,
|
||||
'used_full_history' => false,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
Context assembly rules:
|
||||
|
||||
- Always include the task system prompt and language instruction.
|
||||
- Always include post config summary: audience, tone, language, article length, SEO fields, web search preference.
|
||||
- Include `_wpaw_plan` for planning, writing, and outline refinement.
|
||||
- Include only the active block or section for block refinement.
|
||||
- Include recent raw messages only up to `max_recent_messages`.
|
||||
- Include `working_summary` when message history is long.
|
||||
- Include decisions and rejections as compact bullet points.
|
||||
- Include post content only when the task requires whole-article awareness, such as final polish or article-wide refinement.
|
||||
- Never trust browser-provided `chatHistory` as authoritative if `sessionId` is available.
|
||||
|
||||
## Endpoint Changes
|
||||
|
||||
### `/chat`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Receives `messages` from the browser.
|
||||
- Prepends a system prompt.
|
||||
- Streams or returns a chat response.
|
||||
- Persists user and assistant messages.
|
||||
|
||||
Required change:
|
||||
|
||||
- Use browser `messages` only to identify the latest user message.
|
||||
- Load authoritative session context from `WP_Agentic_Writer_Context_Service`.
|
||||
- Build final messages through `WP_Agentic_Writer_Context_Builder`.
|
||||
- Persist the raw user message and assistant response after completion.
|
||||
|
||||
### `/generate-plan`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Accepts `topic`, `context`, `chatHistory`, and other config.
|
||||
- Serializes full `chatHistory` into the planning prompt.
|
||||
- Stores `_wpaw_plan` and `_wpaw_memory`.
|
||||
|
||||
Required change:
|
||||
|
||||
- Keep `topic`, `context`, `clarificationAnswers`, and `post_config`.
|
||||
- Replace full `chatHistory` injection with a context package from the builder.
|
||||
- Save generated plan to `_wpaw_plan`.
|
||||
- Update `wpaw_conversations.context.working_summary` after plan generation.
|
||||
|
||||
### `/revise-plan`
|
||||
|
||||
Required behavior:
|
||||
|
||||
- Include current `_wpaw_plan`.
|
||||
- Include latest user instruction.
|
||||
- Include accepted/rejected outline decisions.
|
||||
- Ask for raw JSON plan only.
|
||||
- Save previous plan as a version entry inside `wpaw_conversations.context.plan_versions` before overwriting `_wpaw_plan`.
|
||||
|
||||
### `/execute-article`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Writes sections from the plan.
|
||||
- Streams section content and block events.
|
||||
- Updates `_wpaw_plan` section statuses.
|
||||
|
||||
Required change:
|
||||
|
||||
- For each section, send the section brief, global article summary, relevant decisions, and relevant research.
|
||||
- Do not send the full conversation for every section.
|
||||
- After each section completes, update writing state and append a section summary to session context.
|
||||
|
||||
### `/refine-block` and `/refine-from-chat`
|
||||
|
||||
Required behavior:
|
||||
|
||||
- Send active block content, neighboring heading/section context, relevant plan entry, and latest instruction.
|
||||
- Include compact working summary and decisions.
|
||||
- Do not include the full draft unless the requested operation is article-wide.
|
||||
|
||||
### `/summarize-context`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Summarizes browser-provided `chatHistory`.
|
||||
- Returns summary but does not appear to be the authoritative persistence mechanism.
|
||||
|
||||
Required change:
|
||||
|
||||
- Accept `sessionId`.
|
||||
- Load authoritative session messages.
|
||||
- Save the resulting summary into `wpaw_conversations.context.working_summary`.
|
||||
- Return `summary`, `message_count`, `source_message_count`, `tokens_saved`, and provider metadata.
|
||||
|
||||
## Streaming Transport
|
||||
|
||||
OpenRouter streaming is already implemented in `WP_Agentic_Writer_OpenRouter_Provider::chat_stream()`.
|
||||
|
||||
Keep this transport shape:
|
||||
|
||||
```php
|
||||
$body = array(
|
||||
'model' => $model,
|
||||
'messages' => $messages,
|
||||
'stream' => true,
|
||||
);
|
||||
```
|
||||
|
||||
Modernize usage handling:
|
||||
|
||||
- OpenRouter now returns full usage metadata automatically.
|
||||
- `usage: { include: true }` and `stream_options: { include_usage: true }` are documented as deprecated and no longer required.
|
||||
- Keep parsing the final `usage` object from streamed chunks.
|
||||
- Extend cost tracking to store cache metadata when available.
|
||||
|
||||
Recommended emitted SSE events:
|
||||
|
||||
```json
|
||||
{"type":"provider","provider":"openrouter","model":"openai/gpt-4o-mini","byok_expected":true}
|
||||
{"type":"conversational_stream","content":"partial accumulated text"}
|
||||
{"type":"usage","input_tokens":1200,"output_tokens":360,"cached_tokens":0,"cost":0.0012}
|
||||
{"type":"complete","session_id":"abc123","totalCost":0.0012}
|
||||
```
|
||||
|
||||
Use the existing browser parsing path in `assets/js/sidebar.js` and add support for the optional `provider` and `usage` event types.
|
||||
|
||||
## Response Caching Policy
|
||||
|
||||
OpenRouter response caching should be used for deterministic, duplicate-safe operations only. It is not article memory.
|
||||
|
||||
Recommended use:
|
||||
|
||||
- `detect_intent`
|
||||
- `summarize_context` retry
|
||||
- connection test
|
||||
- repeated model capability lookups if routed through completion calls
|
||||
|
||||
Avoid by default:
|
||||
|
||||
- article draft generation
|
||||
- outline revision
|
||||
- refinement requests
|
||||
- image prompt generation
|
||||
|
||||
Provider implementation change:
|
||||
|
||||
```php
|
||||
if ( ! empty( $options['openrouter_response_cache'] ) ) {
|
||||
$headers[] = 'X-OpenRouter-Cache: true';
|
||||
$headers[] = 'X-OpenRouter-Cache-TTL: ' . (int) ( $options['openrouter_cache_ttl'] ?? 300 );
|
||||
}
|
||||
```
|
||||
|
||||
Important limitations:
|
||||
|
||||
- Cache hits only happen for identical requests.
|
||||
- Streaming and non-streaming requests are cached separately.
|
||||
- Cache hit usage counters are zeroed.
|
||||
- Response caching is beta and requires OpenRouter to store response data temporarily.
|
||||
|
||||
## Usage and Budget Tracking
|
||||
|
||||
Extend `wpaw_cost_tracking` with optional cache and upstream fields:
|
||||
|
||||
```sql
|
||||
ALTER TABLE {$wpdb->prefix}wpaw_cost_tracking
|
||||
ADD COLUMN cached_tokens int(11) DEFAULT 0 AFTER output_tokens,
|
||||
ADD COLUMN cache_write_tokens int(11) DEFAULT 0 AFTER cached_tokens,
|
||||
ADD COLUMN upstream_inference_cost decimal(10,6) DEFAULT NULL AFTER cost,
|
||||
ADD COLUMN generation_id varchar(64) DEFAULT '' AFTER status;
|
||||
```
|
||||
|
||||
Implementation notes:
|
||||
|
||||
- Put this behind a schema version bump, not plugin version alone.
|
||||
- Keep existing `maybe_upgrade_table()` pattern in `WP_Agentic_Writer_Cost_Tracker`.
|
||||
- Parse `usage.prompt_tokens_details.cached_tokens`.
|
||||
- Parse `usage.prompt_tokens_details.cache_write_tokens`.
|
||||
- Parse `usage.cost_details.upstream_inference_cost` for BYOK requests.
|
||||
- Include a monthly token budget view alongside the existing cost view.
|
||||
|
||||
Budget metric examples:
|
||||
|
||||
```php
|
||||
billable_input_tokens = max( 0, input_tokens - cached_tokens );
|
||||
total_monthly_tokens = sum( input_tokens + output_tokens );
|
||||
byok_free_request_counter = count( provider = 'openrouter' and status = 'success' );
|
||||
```
|
||||
|
||||
Note: OpenRouter documents the BYOK waiver as first 1M BYOK requests per month, not first 1M tokens. Keep UI wording precise.
|
||||
|
||||
## Settings UI Changes
|
||||
|
||||
Update Settings V2:
|
||||
|
||||
- Rename default cloud path to `OpenRouter BYOK / API`.
|
||||
- Keep API key storage in `wp_agentic_writer_settings.openrouter_api_key`.
|
||||
- Add a help panel explaining that provider BYOK keys are configured in OpenRouter, not in WordPress.
|
||||
- Add a "Prevent shared fallback" checklist item that links users to OpenRouter BYOK provider settings.
|
||||
- Move Local Backend to an `Advanced` or `Legacy Local Backend` section.
|
||||
- Make provider routing default all text tasks to `openrouter`.
|
||||
- Keep image task on `openrouter`.
|
||||
- Show a trust note: WordPress streams directly to OpenRouter; no local shell or CLI process is required.
|
||||
|
||||
Do not collect provider keys directly in WordPress unless there is a deliberate product decision to bypass OpenRouter BYOK management. The safer default is only storing the OpenRouter API key.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Documentation and defaults
|
||||
|
||||
- Add this spec.
|
||||
- Update user-facing Local Backend docs to say local backend is optional/advanced.
|
||||
- Default new installs to OpenRouter for all tasks.
|
||||
- Keep existing installs unchanged unless the user opts in.
|
||||
|
||||
### Phase 2: Context builder
|
||||
|
||||
- Add `includes/class-context-builder.php`.
|
||||
- Load it from `wp-agentic-writer.php`.
|
||||
- Move repeated context assembly out of `class-gutenberg-sidebar.php`.
|
||||
- Make `/chat`, `/generate-plan`, `/revise-plan`, and refinement endpoints use the builder.
|
||||
|
||||
### Phase 3: Authoritative summaries
|
||||
|
||||
- Extend `WP_Agentic_Writer_Context_Service` with:
|
||||
- `get_session_context( $session_id )`
|
||||
- `update_session_context( $session_id, $patch )`
|
||||
- `summarize_session_if_needed( $session_id, $post_id )`
|
||||
- Make `/summarize-context` persist summaries to `wpaw_conversations.context`.
|
||||
- Store plan versions and section summaries in context.
|
||||
|
||||
### Phase 4: Streaming and usage polish
|
||||
|
||||
- Remove deprecated OpenRouter usage request parameters.
|
||||
- Emit optional `provider` and `usage` SSE events.
|
||||
- Extend cost tracking schema for cached tokens and BYOK upstream cost.
|
||||
- Add UI display for monthly token usage.
|
||||
|
||||
### Phase 5: Local backend repositioning
|
||||
|
||||
- Move local backend downloads and setup UI to advanced/legacy.
|
||||
- Keep `WP_Agentic_Writer_Local_Backend_Provider` for existing users.
|
||||
- Disable automatic local backend recommendation in onboarding.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A new article can be planned and written through OpenRouter streaming without any local bash/proxy setup.
|
||||
- Existing conversation history persists through `wpaw_conversations`.
|
||||
- Plan generation no longer sends full browser `chatHistory` when `sessionId` is available.
|
||||
- Refining a block includes active block, relevant plan, compact decisions, and recent messages, not full raw history.
|
||||
- Streaming responses show partial text in the editor and finish with usage metadata.
|
||||
- Cost tracking records provider, model, action, session, tokens, and cost as it does today.
|
||||
- New cache fields are recorded when OpenRouter returns them.
|
||||
- Local Backend still works for users who already configured it, but it is no longer the default recommendation.
|
||||
|
||||
## Implementation Risks
|
||||
|
||||
- Some existing frontend flows rely on `messages` as the full source of truth. Those flows need to pass `sessionId` reliably before backend context can become authoritative.
|
||||
- `wpaw_conversations.context` is `LONGTEXT`, so it can hold rich JSON, but large contexts should still be summarized to keep admin queries fast.
|
||||
- OpenRouter response caching is beta and should not be presented as durable memory.
|
||||
- BYOK provider fallback behavior is configured in OpenRouter, so the WordPress UI can guide and detect symptoms but cannot fully enforce provider-key policy from this plugin alone.
|
||||
7
docs/user-facing/downloads/.gitignore
vendored
7
docs/user-facing/downloads/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
# Ignore node_modules in local backend package
|
||||
agentic-writer-local-backend/node_modules/
|
||||
agentic-writer-local-backend/proxy.log
|
||||
agentic-writer-local-backend/proxy.pid
|
||||
|
||||
# Keep the distributable ZIP
|
||||
!agentic-writer-local-backend.zip
|
||||
Binary file not shown.
@@ -1,170 +0,0 @@
|
||||
# Agentic Writer Local Backend
|
||||
|
||||
Run unlimited AI content generation on your own machine using your Claude CLI + Z.ai/Anthropic account.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
|
||||
- ✅ **Claude CLI** installed and configured
|
||||
- Get it: [https://claude.ai/code](https://claude.ai/code) or [https://z.ai](https://z.ai)
|
||||
- Verify: `claude --version` or `which claude`
|
||||
- ✅ **Node.js 18+** installed
|
||||
- Download: [https://nodejs.org](https://nodejs.org)
|
||||
- Verify: `node --version`
|
||||
- ✅ **Z.ai Coding Plan** or **Anthropic API key** configured in Claude CLI
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Extract Package
|
||||
|
||||
```bash
|
||||
unzip agentic-writer-local-backend.zip
|
||||
cd agentic-writer-local-backend
|
||||
```
|
||||
|
||||
### 2. Start the Proxy
|
||||
|
||||
```bash
|
||||
chmod +x *.sh
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
You'll see:
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════
|
||||
✅ Local Backend Running!
|
||||
═══════════════════════════════════════════════════
|
||||
|
||||
Your Configuration:
|
||||
Base URL: http://192.168.1.105:8080
|
||||
API Key: dummy
|
||||
Model: claude-local
|
||||
```
|
||||
|
||||
### 3. Configure WordPress Plugin
|
||||
|
||||
1. Open **WP Admin** → **Agentic Writer** → **Settings** → **Local Backend**
|
||||
2. Paste the **Base URL** shown above
|
||||
3. API Key: `dummy`
|
||||
4. Click **Test Connection** → should show ✅
|
||||
5. Start generating content!
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
./start-proxy.sh # Start proxy (runs in background)
|
||||
./stop-proxy.sh # Stop proxy
|
||||
./test-connection.sh # Test if proxy responds
|
||||
./get-local-ip.sh # Find your local IP address
|
||||
tail -f proxy.log # View real-time logs
|
||||
```
|
||||
|
||||
## Firewall Setup
|
||||
|
||||
The proxy needs to accept connections from your WordPress site.
|
||||
|
||||
### macOS
|
||||
|
||||
1. **System Settings** → **Network** → **Firewall**
|
||||
2. Click **Options** → **Add** → Select `node`
|
||||
3. Set to **Allow incoming connections**
|
||||
|
||||
### Linux (ufw)
|
||||
|
||||
```bash
|
||||
sudo ufw allow 8080/tcp
|
||||
sudo ufw reload
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. **Windows Defender Firewall** → **Advanced Settings**
|
||||
2. **Inbound Rules** → **New Rule**
|
||||
3. **Port** → TCP **8080** → **Allow**
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
WordPress Plugin → HTTP POST → Local Proxy (port 8080)
|
||||
↓
|
||||
Spawns Claude CLI
|
||||
↓
|
||||
Returns AI Response
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- 🆓 **Free**: Uses your existing Z.ai/Anthropic subscription
|
||||
- 🔒 **Private**: Content never leaves your network
|
||||
- ⚡ **Fast**: LAN latency (~50-200ms)
|
||||
- 🚀 **Unlimited**: No rate limits, no token counting
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for detailed solutions.
|
||||
|
||||
### Quick Fixes
|
||||
|
||||
**"Connection failed" in plugin:**
|
||||
```bash
|
||||
# Check proxy is running
|
||||
ps aux | grep claude-proxy
|
||||
|
||||
# Restart if needed
|
||||
./stop-proxy.sh && ./start-proxy.sh
|
||||
```
|
||||
|
||||
**"Claude CLI not found":**
|
||||
```bash
|
||||
# Verify Claude is installed
|
||||
which claude
|
||||
claude --version
|
||||
|
||||
# Test Claude works
|
||||
echo "Hello" | claude
|
||||
```
|
||||
|
||||
**"Wrong IP address":**
|
||||
```bash
|
||||
# Find your correct IP
|
||||
./get-local-ip.sh
|
||||
|
||||
# Or manually:
|
||||
# macOS: ipconfig getifaddr en0
|
||||
# Linux: ip route get 1 | awk '{print $7}'
|
||||
```
|
||||
|
||||
**Port 8080 already in use:**
|
||||
```bash
|
||||
# Find what's using it
|
||||
lsof -i :8080
|
||||
|
||||
# Change port (edit claude-proxy.js)
|
||||
PORT=9000 node claude-proxy.js
|
||||
# Update plugin Base URL to: http://your-ip:9000
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Proxy binds to `0.0.0.0` (all network interfaces) for LAN access
|
||||
- No authentication by design (LAN trust model)
|
||||
- All request prompts are logged to `proxy.log`
|
||||
- For internet exposure, use ngrok/reverse proxy with authentication
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
PORT=9000 ./start-proxy.sh # Use different port
|
||||
NODE_ENV=production # Production mode
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation**: [Plugin Docs](https://github.com/your/plugin)
|
||||
- **Issues**: [GitHub Issues](https://github.com/your/plugin/issues)
|
||||
- **Community**: [Discord](https://discord.gg/your-server)
|
||||
|
||||
## License
|
||||
|
||||
GPL-2.0+ - Same as WP Agentic Writer plugin
|
||||
@@ -1,339 +0,0 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
Common issues and solutions for Agentic Writer Local Backend.
|
||||
|
||||
## Connection Issues
|
||||
|
||||
### "Connection timeout" in Plugin
|
||||
|
||||
**Symptoms:**
|
||||
- Plugin shows "Connection timeout" error
|
||||
- Test connection fails
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check proxy is running:**
|
||||
```bash
|
||||
ps aux | grep claude-proxy
|
||||
```
|
||||
|
||||
2. **Restart proxy:**
|
||||
```bash
|
||||
./stop-proxy.sh
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
3. **Check logs:**
|
||||
```bash
|
||||
tail -f proxy.log
|
||||
```
|
||||
|
||||
4. **Verify IP address:**
|
||||
```bash
|
||||
./get-local-ip.sh
|
||||
```
|
||||
|
||||
### "Connection refused"
|
||||
|
||||
**Cause:** Proxy not running or wrong IP
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Start proxy:**
|
||||
```bash
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
2. **Check firewall:**
|
||||
- macOS: System Settings → Network → Firewall → Allow Node.js
|
||||
- Linux: `sudo ufw allow 8080/tcp`
|
||||
- Windows: Defender Firewall → Allow port 8080
|
||||
|
||||
3. **Test locally first:**
|
||||
```bash
|
||||
curl http://localhost:8080/ping
|
||||
# Should return: pong
|
||||
```
|
||||
|
||||
## Claude CLI Issues
|
||||
|
||||
### "Claude CLI not found"
|
||||
|
||||
**Verify installation:**
|
||||
```bash
|
||||
which claude
|
||||
# macOS: /opt/homebrew/bin/claude or /usr/local/bin/claude
|
||||
# Linux: ~/.local/bin/claude or /usr/bin/claude
|
||||
```
|
||||
|
||||
**Fix PATH:**
|
||||
```bash
|
||||
# Add to ~/.zshrc or ~/.bashrc
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
**Reinstall Claude CLI:**
|
||||
- Visit: [https://claude.ai/code](https://claude.ai/code)
|
||||
- Follow installation instructions
|
||||
|
||||
### "No response from Claude"
|
||||
|
||||
**Test Claude manually:**
|
||||
```bash
|
||||
echo "Hello, reply with: Test successful" | claude
|
||||
```
|
||||
|
||||
**Check authentication:**
|
||||
```bash
|
||||
claude --version
|
||||
# Should show version and auth status
|
||||
```
|
||||
|
||||
**Reconfigure Claude:**
|
||||
- Check Z.ai account: [https://z.ai](https://z.ai)
|
||||
- Or Anthropic API key setup
|
||||
|
||||
## Network Issues
|
||||
|
||||
### Wrong IP Address Detected
|
||||
|
||||
**Find correct IP:**
|
||||
```bash
|
||||
# macOS
|
||||
ipconfig getifaddr en0 # WiFi
|
||||
ipconfig getifaddr en1 # Ethernet
|
||||
|
||||
# Linux
|
||||
ip route get 1 | awk '{print $7}'
|
||||
hostname -I
|
||||
|
||||
# Windows
|
||||
ipconfig
|
||||
# Look for "IPv4 Address" under active adapter
|
||||
```
|
||||
|
||||
**Update plugin settings:**
|
||||
- Use the correct IP in Base URL: `http://CORRECT-IP:8080`
|
||||
|
||||
### Port 8080 Already in Use
|
||||
|
||||
**Find what's using it:**
|
||||
```bash
|
||||
lsof -i :8080
|
||||
# or
|
||||
netstat -anp | grep 8080
|
||||
```
|
||||
|
||||
**Change port:**
|
||||
|
||||
1. Edit `claude-proxy.js`:
|
||||
```javascript
|
||||
const PORT = process.env.PORT || 9000; // Change 8080 to 9000
|
||||
```
|
||||
|
||||
2. Restart proxy:
|
||||
```bash
|
||||
./stop-proxy.sh
|
||||
PORT=9000 ./start-proxy.sh
|
||||
```
|
||||
|
||||
3. Update plugin Base URL: `http://your-ip:9000`
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Response Times
|
||||
|
||||
**Normal latency:**
|
||||
- Local network: 50-200ms
|
||||
- Claude CLI processing: 2-30 seconds depending on prompt
|
||||
|
||||
**If consistently slow:**
|
||||
|
||||
1. **Check network:**
|
||||
```bash
|
||||
ping 192.168.1.105 # Your proxy IP
|
||||
```
|
||||
|
||||
2. **Monitor logs:**
|
||||
```bash
|
||||
tail -f proxy.log
|
||||
```
|
||||
|
||||
3. **Check machine resources:**
|
||||
- CPU usage: Claude CLI is CPU-intensive
|
||||
- Memory: Ensure sufficient RAM available
|
||||
|
||||
### Proxy Crashes
|
||||
|
||||
**Check logs:**
|
||||
```bash
|
||||
cat proxy.log | tail -50
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
- Out of memory: Close other applications
|
||||
- Claude CLI timeout: Increase timeout in `claude-proxy.js`
|
||||
- Malformed requests: Check plugin version compatibility
|
||||
|
||||
**Restart with clean state:**
|
||||
```bash
|
||||
./stop-proxy.sh
|
||||
rm proxy.log
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
## Plugin Integration Issues
|
||||
|
||||
### "Invalid response format"
|
||||
|
||||
**Cause:** Claude response doesn't match expected JSON format
|
||||
|
||||
**Debug:**
|
||||
1. Check `proxy.log` for actual Claude output
|
||||
2. Test manually:
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/v1/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"messages":[{"role":"user","content":"Hello"}]}'
|
||||
```
|
||||
|
||||
3. Update Claude CLI if outdated:
|
||||
```bash
|
||||
claude --version
|
||||
# Upgrade if needed
|
||||
```
|
||||
|
||||
### Cost Tracking Shows $0
|
||||
|
||||
**Expected behavior:** Local backend is free, plugin should show `$0.00 (Local)`
|
||||
|
||||
**If concerned:**
|
||||
- This is correct - local backend has no API costs
|
||||
- Dashboard should show "X requests local (free)"
|
||||
|
||||
## Advanced Troubleshooting
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
Edit `claude-proxy.js`:
|
||||
```javascript
|
||||
const DEBUG = true; // Add at top of file
|
||||
|
||||
// In /v1/messages handler:
|
||||
if (DEBUG) {
|
||||
console.log('Full request:', JSON.stringify(req.body, null, 2));
|
||||
console.log('Full response:', output);
|
||||
}
|
||||
```
|
||||
|
||||
### Test with curl
|
||||
|
||||
**Ping:**
|
||||
```bash
|
||||
curl http://localhost:8080/ping
|
||||
# Expected: pong
|
||||
```
|
||||
|
||||
**Inference:**
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/v1/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "Reply with: Test successful"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected response:**
|
||||
```json
|
||||
{
|
||||
"id": "local-1234567890",
|
||||
"object": "chat.completion",
|
||||
"model": "claude-local",
|
||||
"choices": [{
|
||||
"message": {
|
||||
"content": "Test successful"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Permissions Issues (macOS)
|
||||
|
||||
**Make scripts executable:**
|
||||
```bash
|
||||
chmod +x start-proxy.sh stop-proxy.sh test-connection.sh get-local-ip.sh
|
||||
```
|
||||
|
||||
**If "permission denied":**
|
||||
```bash
|
||||
# Check file permissions
|
||||
ls -la *.sh
|
||||
|
||||
# Reset if needed
|
||||
chmod 755 *.sh
|
||||
```
|
||||
|
||||
## Still Having Issues?
|
||||
|
||||
1. **Check system requirements:**
|
||||
- Node.js 18+: `node --version`
|
||||
- Claude CLI installed: `which claude`
|
||||
- Sufficient disk space: `df -h`
|
||||
|
||||
2. **Collect diagnostic info:**
|
||||
```bash
|
||||
echo "Node version:" $(node --version)
|
||||
echo "Claude path:" $(which claude)
|
||||
echo "Local IP:" $(./get-local-ip.sh)
|
||||
echo "Proxy status:" $(ps aux | grep claude-proxy)
|
||||
tail -20 proxy.log
|
||||
```
|
||||
|
||||
3. **Reset everything:**
|
||||
```bash
|
||||
./stop-proxy.sh
|
||||
rm -rf node_modules proxy.log proxy.pid
|
||||
npm install
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
4. **Get help:**
|
||||
- GitHub Issues: [Report Bug](https://github.com/your/plugin/issues)
|
||||
- Discord Community: [Join Chat](https://discord.gg/your-server)
|
||||
- Include: OS, Node version, Claude CLI version, error logs
|
||||
|
||||
## Environment-Specific Notes
|
||||
|
||||
### macOS
|
||||
|
||||
- Default Claude path: `/opt/homebrew/bin/claude`
|
||||
- Firewall: System Settings → Network → Firewall
|
||||
- IP detection: `ipconfig getifaddr en0`
|
||||
|
||||
### Linux
|
||||
|
||||
- Default Claude path: `~/.local/bin/claude`
|
||||
- Firewall: `sudo ufw allow 8080/tcp`
|
||||
- IP detection: `ip route get 1 | awk '{print $7}'`
|
||||
|
||||
### Windows
|
||||
|
||||
- Claude path varies, check `where claude`
|
||||
- Firewall: Windows Defender → Allow port 8080
|
||||
- IP detection: `ipconfig` (look for IPv4)
|
||||
- Scripts: Use Git Bash or WSL to run `.sh` scripts
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **LAN only:** Don't expose proxy to internet without authentication
|
||||
2. **Firewall:** Restrict to specific IPs if on shared network
|
||||
3. **Logs:** `proxy.log` contains all prompts - review periodically
|
||||
4. **Updates:** Keep Node.js and Claude CLI updated
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-02-27
|
||||
**Version:** 1.0.0
|
||||
@@ -1,122 +0,0 @@
|
||||
const express = require('express');
|
||||
const { spawn } = require('child_process');
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/ping', (req, res) => {
|
||||
res.send('pong');
|
||||
});
|
||||
|
||||
// Main inference endpoint (OpenAI-compatible format)
|
||||
app.post('/v1/messages', async (req, res) => {
|
||||
const { messages } = req.body;
|
||||
|
||||
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
message: 'Invalid request: messages array required'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract the last user message as the prompt
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
const prompt = lastMessage.content;
|
||||
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('Request from:', req.ip);
|
||||
console.log('Prompt length:', prompt.length, 'chars');
|
||||
console.log('Prompt preview:', prompt.substring(0, 150) + '...');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
|
||||
// Spawn Claude CLI process
|
||||
const claude = spawn('claude', [], {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
let output = '';
|
||||
let errorOutput = '';
|
||||
|
||||
claude.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
process.stdout.write('.');
|
||||
});
|
||||
|
||||
claude.stderr.on('data', (data) => {
|
||||
errorOutput += data.toString();
|
||||
console.error('Claude stderr:', data.toString());
|
||||
});
|
||||
|
||||
claude.on('close', (code) => {
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('Claude exit code:', code);
|
||||
console.log('Response length:', output.length, 'chars');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
if (code !== 0 || !output.trim()) {
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Claude CLI error',
|
||||
details: errorOutput || 'No response from Claude'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return OpenAI-compatible response format
|
||||
res.json({
|
||||
id: 'local-' + Date.now(),
|
||||
object: 'chat.completion',
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
model: 'claude-local',
|
||||
choices: [{
|
||||
index: 0,
|
||||
message: {
|
||||
role: 'assistant',
|
||||
content: output.trim()
|
||||
},
|
||||
finish_reason: 'stop'
|
||||
}],
|
||||
usage: {
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
claude.on('error', (err) => {
|
||||
console.error('Failed to spawn Claude CLI:', err);
|
||||
res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to spawn Claude CLI',
|
||||
details: err.message
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Send prompt to Claude after brief pause
|
||||
setTimeout(() => {
|
||||
claude.stdin.write(prompt + '\n');
|
||||
claude.stdin.end();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 8080;
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log('═══════════════════════════════════════════════════');
|
||||
console.log('🚀 Agentic Writer Local Backend Started!');
|
||||
console.log('═══════════════════════════════════════════════════');
|
||||
console.log(`Local: http://localhost:${PORT}`);
|
||||
console.log(`Network: http://YOUR-IP:${PORT}`);
|
||||
console.log('');
|
||||
console.log('Plugin Configuration:');
|
||||
console.log(` Base URL: http://YOUR-IP:${PORT}`);
|
||||
console.log(` API Key: dummy`);
|
||||
console.log(` Model: claude-local`);
|
||||
console.log('');
|
||||
console.log('Health check: GET /ping');
|
||||
console.log('Inference: POST /v1/messages');
|
||||
console.log('═══════════════════════════════════════════════════');
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Detecting your local IP address..."
|
||||
echo ""
|
||||
|
||||
# Detect local IP based on OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS - try en0 (WiFi) then en1 (Ethernet)
|
||||
IP=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo "")
|
||||
INTERFACE=$(ifconfig en0 &>/dev/null && echo "en0 (WiFi)" || echo "en1 (Ethernet)")
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
# Linux
|
||||
IP=$(ip route get 1 | awk '{print $7;exit}' 2>/dev/null || hostname -I | awk '{print $1}')
|
||||
INTERFACE="default"
|
||||
else
|
||||
# Windows or unknown
|
||||
IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "")
|
||||
INTERFACE="unknown"
|
||||
fi
|
||||
|
||||
if [ -z "$IP" ]; then
|
||||
echo "❌ Could not detect IP address automatically"
|
||||
echo ""
|
||||
echo "Manual detection:"
|
||||
echo " macOS: ipconfig getifaddr en0"
|
||||
echo " Linux: ip route get 1 | awk '{print \$7}'"
|
||||
echo " Windows: ipconfig (look for IPv4 Address)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Your local IP: $IP ($INTERFACE)"
|
||||
echo ""
|
||||
echo "Use this in your plugin settings:"
|
||||
echo " Base URL: http://$IP:8080"
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "agentic-writer-local-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Local backend proxy for WP Agentic Writer using Claude CLI",
|
||||
"main": "claude-proxy.js",
|
||||
"scripts": {
|
||||
"start": "node claude-proxy.js",
|
||||
"test": "curl http://localhost:8080/ping"
|
||||
},
|
||||
"keywords": [
|
||||
"wordpress",
|
||||
"ai",
|
||||
"claude",
|
||||
"proxy"
|
||||
],
|
||||
"author": "WP Agentic Writer",
|
||||
"license": "GPL-2.0+",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🚀 Starting Agentic Writer Local Backend..."
|
||||
echo ""
|
||||
|
||||
# Check dependencies
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "❌ Node.js not found. Install from https://nodejs.org/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v claude &> /dev/null; then
|
||||
echo "❌ Claude CLI not found. Install and configure first."
|
||||
echo " Check: https://claude.ai/code or https://z.ai"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Auto-install express if needed
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "📦 Installing dependencies..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
# Detect local IP
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
LOCAL_IP=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo "127.0.0.1")
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
# Linux
|
||||
LOCAL_IP=$(ip route get 1 | awk '{print $7;exit}' 2>/dev/null || echo "127.0.0.1")
|
||||
else
|
||||
# Windows/other
|
||||
LOCAL_IP="127.0.0.1"
|
||||
fi
|
||||
|
||||
echo "✅ Dependencies OK"
|
||||
echo "✅ Claude CLI found: $(which claude)"
|
||||
echo ""
|
||||
echo "Starting proxy server..."
|
||||
echo ""
|
||||
|
||||
# Start server in background
|
||||
nohup node claude-proxy.js > proxy.log 2>&1 &
|
||||
PID=$!
|
||||
echo $PID > proxy.pid
|
||||
|
||||
# Wait for server to boot
|
||||
sleep 2
|
||||
|
||||
# Test if running
|
||||
if kill -0 $PID 2>/dev/null; then
|
||||
echo "═══════════════════════════════════════════════════"
|
||||
echo "✅ Local Backend Running!"
|
||||
echo "═══════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "Your Configuration:"
|
||||
echo " Base URL: http://$LOCAL_IP:8080"
|
||||
echo " API Key: dummy"
|
||||
echo " Model: claude-local"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Open your WordPress Admin"
|
||||
echo " 2. Go to Agentic Writer → Settings → Local Backend"
|
||||
echo " 3. Paste the Base URL above"
|
||||
echo " 4. Click 'Test Connection'"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " Logs: tail -f proxy.log"
|
||||
echo " Stop: ./stop-proxy.sh"
|
||||
echo " Test: ./test-connection.sh"
|
||||
echo "═══════════════════════════════════════════════════"
|
||||
else
|
||||
echo "❌ Failed to start. Check proxy.log for errors."
|
||||
cat proxy.log
|
||||
rm -f proxy.pid
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f proxy.pid ]; then
|
||||
PID=$(cat proxy.pid)
|
||||
if kill -0 $PID 2>/dev/null; then
|
||||
kill $PID
|
||||
rm proxy.pid
|
||||
echo "🛑 Local Backend stopped (PID: $PID)"
|
||||
else
|
||||
echo "⚠️ No process found with PID: $PID"
|
||||
rm proxy.pid
|
||||
fi
|
||||
else
|
||||
# Fallback: kill by process name
|
||||
pkill -f claude-proxy.js
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "🛑 Stopped all claude-proxy processes"
|
||||
else
|
||||
echo "ℹ️ No claude-proxy processes running"
|
||||
fi
|
||||
fi
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Testing local backend connection..."
|
||||
echo ""
|
||||
|
||||
# Test /ping endpoint
|
||||
echo "1. Testing health check..."
|
||||
PING_RESPONSE=$(curl -s http://localhost:8080/ping 2>&1)
|
||||
|
||||
if [ "$PING_RESPONSE" = "pong" ]; then
|
||||
echo " ✅ Health check passed"
|
||||
else
|
||||
echo " ❌ Health check failed"
|
||||
echo " Response: $PING_RESPONSE"
|
||||
echo ""
|
||||
echo "Is the proxy running? Check: ps aux | grep claude-proxy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test /v1/messages endpoint
|
||||
echo "2. Testing inference..."
|
||||
RESPONSE=$(curl -s -X POST http://localhost:8080/v1/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"messages":[{"role":"user","content":"Reply with exactly: Test successful"}]}' 2>&1)
|
||||
|
||||
echo " Response: $RESPONSE"
|
||||
|
||||
if echo "$RESPONSE" | grep -q "choices"; then
|
||||
echo " ✅ Inference endpoint working"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Local Backend is working correctly!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
else
|
||||
echo " ❌ Inference test failed"
|
||||
echo ""
|
||||
echo "Troubleshooting:"
|
||||
echo " 1. Check Claude CLI: echo 'test' | claude"
|
||||
echo " 2. Check logs: tail -f proxy.log"
|
||||
echo " 3. Restart proxy: ./stop-proxy.sh && ./start-proxy.sh"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user