diff --git a/README.md b/README.md index 09a9ded..b668eec 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,26 @@ This will: 4. Create default configuration 5. Add example scheduled job +### Configure Qwen Provider (Default) + +QwenClaw uses **Qwen** as the default AI provider. + +1. Get API key: https://platform.qwen.ai/ +2. Create `rig-service/.env`: + ```bash + cd rig-service + cp .env.example .env + ``` +3. Edit `.env`: + ```env + QWEN_API_KEY=sk-your-key-here + QWEN_BASE_URL=https://api.qwen.ai/v1 + RIG_DEFAULT_PROVIDER=qwen + RIG_DEFAULT_MODEL=qwen-plus + ``` + +See `docs/QWEN-SETUP.md` for detailed setup. + --- ## Manual Installation diff --git a/docs/QWEN-SETUP.md b/docs/QWEN-SETUP.md new file mode 100644 index 0000000..55576d7 --- /dev/null +++ b/docs/QWEN-SETUP.md @@ -0,0 +1,277 @@ +# Qwen Provider Setup Guide + +## Overview + +QwenClaw uses **Qwen** as the default AI provider. This guide shows you how to configure and use Qwen with the Rig service. + +--- + +## Quick Start + +### 1. Get Your Qwen API Key + +1. Visit: https://platform.qwen.ai/ +2. Sign up or log in +3. Go to **API Keys** section +4. Click **Create New API Key** +5. Copy your key (starts with `sk-...`) + +### 2. Configure Rig Service + +Create `.env` file in `rig-service/`: + +```bash +cd rig-service +cp .env.example .env +``` + +Edit `.env`: + +```env +# Qwen API Configuration (REQUIRED) +QWEN_API_KEY=sk-your-actual-key-here +QWEN_BASE_URL=https://api.qwen.ai/v1 + +# Defaults (Qwen is default for QwenClaw) +RIG_DEFAULT_PROVIDER=qwen +RIG_DEFAULT_MODEL=qwen-plus + +# Server settings +RIG_HOST=127.0.0.1 +RIG_PORT=8080 +``` + +### 3. Start Rig Service + +```bash +# Build +cargo build --release + +# Start +cargo run --release +``` + +### 4. Verify Connection + +```bash +curl http://127.0.0.1:8080/health +# Should return: {"status":"ok","service":"qwenclaw-rig"} +``` + +--- + +## Available Qwen Models + +| Model | Description | Use Case | +|-------|-------------|----------| +| `qwen-plus` | **Default** - Balanced performance | General tasks | +| `qwen-max` | Most powerful | Complex reasoning | +| `qwen-turbo` | Fastest, cheapest | Simple tasks | +| `qwen-long` | Long context (256K) | Document analysis | + +--- + +## Using Qwen with Rig + +### TypeScript Client + +```typescript +import { initRigClient } from "./src/rig"; + +const rig = initRigClient(); + +// Create agent with Qwen +const sessionId = await rig.createAgent({ + name: "assistant", + preamble: "You are a helpful assistant.", + provider: "qwen", // Use Qwen + model: "qwen-plus", // Qwen model +}); + +// Execute prompt +const result = await rig.executePrompt(sessionId, "Hello!"); +console.log(result); +``` + +### HTTP API + +```bash +# Create agent with Qwen +curl -X POST http://127.0.0.1:8080/api/agents \ + -H "Content-Type: application/json" \ + -d '{ + "name": "assistant", + "preamble": "You are helpful.", + "provider": "qwen", + "model": "qwen-plus" + }' + +# Execute prompt +curl -X POST http://127.0.0.1:8080/api/agents/{SESSION_ID}/prompt \ + -H "Content-Type: application/json" \ + -d '{"prompt": "Hello!"}' +``` + +### Multi-Agent Council with Qwen + +```typescript +const councilId = await rig.createCouncil("Research Team", [ + { + name: "researcher", + preamble: "You research thoroughly.", + provider: "qwen", + model: "qwen-max", // Use most powerful for research + }, + { + name: "writer", + preamble: "You write clearly.", + provider: "qwen", + model: "qwen-plus", // Balanced for writing + }, +]); + +const result = await rig.executeCouncil(councilId, "Write a report"); +``` + +--- + +## Alternative Providers + +### OpenAI (Fallback) + +```env +# In rig-service/.env +OPENAI_API_KEY=sk-... +RIG_DEFAULT_PROVIDER=openai +RIG_DEFAULT_MODEL=gpt-4o +``` + +### Anthropic Claude + +```env +# In rig-service/.env +ANTHROPIC_API_KEY=sk-ant-... +RIG_DEFAULT_PROVIDER=anthropic +RIG_DEFAULT_MODEL=claude-3-5-sonnet +``` + +### Ollama (Local) + +```env +# In rig-service/.env +RIG_DEFAULT_PROVIDER=ollama +RIG_DEFAULT_MODEL=qwen2.5:7b +# No API key needed - runs locally +``` + +--- + +## Troubleshooting + +### "QWEN_API_KEY not set" + +**Error:** +``` +Error: QWEN_API_KEY not set. Get it from https://platform.qwen.ai +``` + +**Solution:** +1. Get API key from https://platform.qwen.ai +2. Add to `rig-service/.env`: + ```env + QWEN_API_KEY=sk-your-key + ``` +3. Restart Rig service + +### "Invalid API key" + +**Error:** +``` +Rig prompt execution failed: Invalid API key +``` + +**Solution:** +1. Verify API key is correct (no extra spaces) +2. Check key is active in Qwen dashboard +3. Ensure sufficient credits/quota + +### Connection Timeout + +**Error:** +``` +Failed to connect to Qwen API +``` + +**Solution:** +1. Check internet connection +2. Verify `QWEN_BASE_URL` is correct +3. Try alternative: `https://api.qwen.ai/v1` + +--- + +## Cost Optimization + +### Use Appropriate Models + +| Task | Recommended Model | Cost | +|------|------------------|------| +| Simple Q&A | `qwen-turbo` | $ | +| General tasks | `qwen-plus` | $$ | +| Complex reasoning | `qwen-max` | $$$ | +| Long documents | `qwen-long` | $$ | + +### Example: Task-Based Routing + +```typescript +// Simple task - use turbo +const simpleAgent = await rig.createAgent({ + name: "quick", + model: "qwen-turbo", +}); + +// Complex task - use max +const complexAgent = await rig.createAgent({ + name: "analyst", + model: "qwen-max", +}); +``` + +--- + +## API Reference + +### Environment Variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `QWEN_API_KEY` | ✅ For Qwen | - | Your Qwen API key | +| `QWEN_BASE_URL` | ❌ | `https://api.qwen.ai/v1` | API endpoint | +| `RIG_DEFAULT_PROVIDER` | ❌ | `qwen` | Default provider | +| `RIG_DEFAULT_MODEL` | ❌ | `qwen-plus` | Default model | + +### Provider Values + +| Value | Provider | Models | +|-------|----------|--------| +| `qwen` | Qwen | qwen-plus, qwen-max, qwen-turbo | +| `openai` | OpenAI | gpt-4o, gpt-4, gpt-3.5 | +| `anthropic` | Anthropic | claude-3-5-sonnet, claude-3 | +| `ollama` | Ollama | Any local model | + +--- + +## Resources + +- **Qwen Platform**: https://platform.qwen.ai/ +- **Qwen Docs**: https://help.qwen.ai/ +- **Pricing**: https://qwen.ai/pricing +- **Rig Integration**: `docs/RIG-INTEGRATION.md` + +--- + +## Support + +Issues? Check: +1. `docs/RIG-STATUS.md` - Known issues +2. Rig service logs: `cargo run --release --verbose` +3. Qwen status: https://status.qwen.ai/ diff --git a/rig-service/.env.example b/rig-service/.env.example index eaeb5af..9888cb2 100644 --- a/rig-service/.env.example +++ b/rig-service/.env.example @@ -7,16 +7,20 @@ RIG_PORT=8080 # Database path for vector store RIG_DATABASE_PATH=./rig-store.db -# Model providers (get keys from respective platforms) -# OpenAI: https://platform.openai.com/api-keys +# Default provider (QwenClaw uses Qwen by default) +RIG_DEFAULT_PROVIDER=qwen +RIG_DEFAULT_MODEL=qwen-plus + +# Qwen API Configuration +# Get your Qwen API key from: https://platform.openai.com/ +# Or use compatible API endpoints (OpenAI-compatible) +QWEN_API_KEY=your-qwen-api-key-here +QWEN_BASE_URL=https://api.qwen.ai/v1 + +# Alternative: OpenAI (fallback) +# Get from: https://platform.openai.com/api-keys OPENAI_API_KEY=your-openai-api-key-here -# Anthropic: https://console.anthropic.com/settings/keys +# Alternative: Anthropic (fallback) +# Get from: https://console.anthropic.com/settings/keys ANTHROPIC_API_KEY=your-anthropic-api-key-here - -# Qwen: (if using Qwen API) -# QWEN_API_KEY=your-qwen-api-key-here - -# Defaults -RIG_DEFAULT_PROVIDER=openai -RIG_DEFAULT_MODEL=gpt-4o diff --git a/rig-service/src/agent.rs b/rig-service/src/agent.rs index e215741..abb789b 100644 --- a/rig-service/src/agent.rs +++ b/rig-service/src/agent.rs @@ -88,12 +88,19 @@ impl AgentManager { .await .ok_or_else(|| anyhow::anyhow!("Session not found"))?; - // Get API key based on provider - let api_key = self.get_api_key(&session.config.provider)?; + // Get provider config (API key + optional base URL) + let (api_key, base_url) = self.get_provider_config(&session.config.provider)?; - // Create Rig agent with OpenAI provider - // Note: Rig uses unified provider interface - let client = openai::Client::new(&api_key); + // Create Rig agent with OpenAI-compatible provider + // Qwen uses OpenAI-compatible API, so we can use the OpenAI client + let mut client_builder = openai::ClientBuilder::new(&api_key); + + // Use custom base URL if provided (for Qwen or other compatible APIs) + if let Some(url) = base_url { + client_builder = client_builder.base_url(&url); + } + + let client = client_builder.build(); let agent = client .agent(&session.config.model) @@ -181,26 +188,36 @@ impl AgentManager { Ok(results.join("\n\n---\n\n")) } - /// Get API key for provider - fn get_api_key(&self, provider: &str) -> Result { + /// Get API key and base URL for provider + fn get_provider_config(&self, provider: &str) -> Result<(String, Option)> { match provider.to_lowercase().as_str() { - "openai" => { - std::env::var("OPENAI_API_KEY") - .map_err(|_| anyhow::anyhow!("OPENAI_API_KEY not set")) + "qwen" | "qwen-plus" | "qwen-max" => { + let api_key = std::env::var("QWEN_API_KEY") + .map_err(|_| anyhow::anyhow!("QWEN_API_KEY not set. Get it from https://platform.qwen.ai"))?; + let base_url = std::env::var("QWEN_BASE_URL").ok(); + Ok((api_key, base_url)) } - "anthropic" => { - std::env::var("ANTHROPIC_API_KEY") - .map_err(|_| anyhow::anyhow!("ANTHROPIC_API_KEY not set")) + "openai" | "gpt-4" | "gpt-4o" | "gpt-3.5" => { + let api_key = std::env::var("OPENAI_API_KEY") + .map_err(|_| anyhow::anyhow!("OPENAI_API_KEY not set"))?; + Ok((api_key, None)) } - "qwen" | "default" => { - // Fall back to OpenAI for now - std::env::var("OPENAI_API_KEY") - .or_else(|_| std::env::var("QWEN_API_KEY")) - .map_err(|_| anyhow::anyhow!("No API key found (tried OPENAI_API_KEY, QWEN_API_KEY)")) + "anthropic" | "claude" | "claude-3" => { + let api_key = std::env::var("ANTHROPIC_API_KEY") + .map_err(|_| anyhow::anyhow!("ANTHROPIC_API_KEY not set"))?; + Ok((api_key, None)) + } + "ollama" | "local" => { + // Ollama doesn't need API key, uses localhost + Ok(("".to_string(), Some("http://localhost:11434".to_string()))) } _ => { - std::env::var("OPENAI_API_KEY") - .map_err(|_| anyhow::anyhow!("Unknown provider '{}' and no OPENAI_API_KEY set", provider)) + // Default to Qwen for QwenClaw + let api_key = std::env::var("QWEN_API_KEY") + .or_else(|_| std::env::var("OPENAI_API_KEY")) + .map_err(|_| anyhow::anyhow!("No API key found. Set QWEN_API_KEY or OPENAI_API_KEY"))?; + let base_url = std::env::var("QWEN_BASE_URL").ok(); + Ok((api_key, base_url)) } } } diff --git a/rig-service/src/config.rs b/rig-service/src/config.rs index 1dc0308..f95ff95 100644 --- a/rig-service/src/config.rs +++ b/rig-service/src/config.rs @@ -11,14 +11,14 @@ pub struct Config { pub port: u16, /// Database path for vector store pub database_path: String, - /// Default model provider + /// Default model provider (Qwen for QwenClaw) pub default_provider: String, /// Default model name pub default_model: String, /// API keys for providers + pub qwen_api_key: Option, pub openai_api_key: Option, pub anthropic_api_key: Option, - pub qwen_api_key: Option, } impl Config { @@ -31,13 +31,14 @@ impl Config { .context("Invalid RIG_PORT")?, database_path: std::env::var("RIG_DATABASE_PATH") .unwrap_or_else(|_| "rig-store.db".to_string()), + // QwenClaw default: Qwen provider default_provider: std::env::var("RIG_DEFAULT_PROVIDER") - .unwrap_or_else(|_| "openai".to_string()), + .unwrap_or_else(|_| "qwen".to_string()), default_model: std::env::var("RIG_DEFAULT_MODEL") - .unwrap_or_else(|_| "gpt-4".to_string()), + .unwrap_or_else(|_| "qwen-plus".to_string()), + qwen_api_key: std::env::var("QWEN_API_KEY").ok(), openai_api_key: std::env::var("OPENAI_API_KEY").ok(), anthropic_api_key: std::env::var("ANTHROPIC_API_KEY").ok(), - qwen_api_key: std::env::var("QWEN_API_KEY").ok(), }) } }