diff --git a/server.js b/server.js index a0f686e..4229d2e 100644 --- a/server.js +++ b/server.js @@ -17,9 +17,12 @@ app.get(['/q', '/q.html'], (_req, res) => res.sendFile(path.join(PUBLIC_DIR, 'q' app.use(express.static(PUBLIC_DIR)); const MODELS = { + opus: 'claude-opus-4-7', sonnet: 'claude-sonnet-4-6', haiku: 'claude-haiku-4-5-20251001', }; +const THINKING_MODELS = new Set(['claude-opus-4-7']); +const THINKING_BUDGET_TOKENS = 16000; // "high effort" const CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); const CREDS_PATH = path.join(CONFIG_DIR, '.credentials.json'); @@ -108,6 +111,16 @@ const SYSTEM_PROMPT = [ ].join("\n"); async function callClaude(modelId, content, accessToken) { + const body = { + model: modelId, + max_tokens: THINKING_MODELS.has(modelId) ? THINKING_BUDGET_TOKENS + 4096 : 8192, + stream: true, + system: SYSTEM_PROMPT, + messages: [{ role: 'user', content }], + }; + if (THINKING_MODELS.has(modelId)) { + body.thinking = { type: 'enabled', budget_tokens: THINKING_BUDGET_TOKENS }; + } return fetch('https://api.anthropic.com/v1/messages', { method: 'POST', dispatcher: anthropicAgent, @@ -119,18 +132,12 @@ async function callClaude(modelId, content, accessToken) { 'x-app': 'cli', 'User-Agent': 'claude-cli/2.1.114', }, - body: JSON.stringify({ - model: modelId, - max_tokens: 8192, - stream: true, - system: SYSTEM_PROMPT, - messages: [{ role: 'user', content }], - }), + body: JSON.stringify(body), }); } app.post('/api/chat', express.json({ limit: '50mb' }), async (req, res) => { - const { text, model = 'sonnet', image, images } = req.body; + const { text, model = 'opus', image, images } = req.body; const hasText = text?.trim(); const imgList = Array.isArray(images) ? images : (image ? [image] : []); if (!hasText && imgList.length === 0) return res.status(400).json({ error: 'text or image required' }); @@ -210,12 +217,13 @@ app.post('/api/chat', express.json({ limit: '50mb' }), async (req, res) => { const CC_SYSTEM_PREFIX = "You are Claude Code, Anthropic's official CLI for Claude."; function mapModel(name) { - if (!name) return MODELS.sonnet; + if (!name) return MODELS.opus; const n = String(name).toLowerCase(); + if (n.includes('opus')) return MODELS.opus; if (n.includes('haiku')) return MODELS.haiku; if (n.includes('sonnet')) return MODELS.sonnet; if (MODELS[n]) return MODELS[n]; - return MODELS.sonnet; + return MODELS.opus; } function translateContent(content) { @@ -270,10 +278,11 @@ app.post('/v1/chat/completions', express.json({ limit: '50mb' }), async (req, re }, body: JSON.stringify({ model: modelId, - max_tokens, + max_tokens: THINKING_MODELS.has(modelId) ? Math.max(max_tokens, THINKING_BUDGET_TOKENS + 2048) : max_tokens, stream: false, system: systemParts.join('\n\n'), messages: anthMessages, + ...(THINKING_MODELS.has(modelId) ? { thinking: { type: 'enabled', budget_tokens: THINKING_BUDGET_TOKENS } } : {}), }), });