# Extended Thinking Feature Specification ## Claude.ai Clone - Enhanced Reasoning Integration --- ## 1. Vue d'ensemble Extended Thinking est une fonctionnalité de Claude qui permet d'activer des capacités de raisonnement améliorées pour les tâches complexes. Claude génère des blocs `thinking` où il expose son processus de réflexion interne étape par étape avant de fournir sa réponse finale. ### Fonctionnement - Claude crée des blocs `thinking` contenant son raisonnement interne - Ces blocs sont suivis de blocs `text` avec la réponse finale - Le processus de réflexion est résumé (pour Claude 4+) mais facturé au tarif complet - Améliore significativement la qualité des réponses pour les tâches complexes ### Cas d'usage - Mathématiques complexes et calculs - Programmation et débogage - Analyse approfondie de documents - Raisonnement logique multi-étapes - Résolution de problèmes complexes --- ## 2. Modèles supportés | Modèle | ID | Support Extended Thinking | |--------|----|-----------------------------| | Claude Sonnet 4.5 | claude-sonnet-4-5-20250929 | ✅ Oui | | Claude Sonnet 4 | claude-sonnet-4-20250514 | ✅ Oui | | Claude Haiku 4.5 | claude-haiku-4-5-20251001 | ✅ Oui | | Claude Opus 4.5 | claude-opus-4-5-20251101 | ✅ Oui (avec préservation thinking) | | Claude Opus 4.1 | claude-opus-4-1-20250805 | ✅ Oui | | Claude Opus 4 | claude-opus-4-20250514 | ✅ Oui | | Claude Sonnet 3.7 | claude-3-7-sonnet-20250219 | ✅ Oui (déprécié, thinking complet) | **Note**: Claude 4+ retourne du thinking résumé. Claude 3.7 retourne du thinking complet. --- ## 3. Architecture Backend ### 3.1 Modifications API Routes #### `server/routes/claude.js` **Ajouts nécessaires**: ```javascript // POST /api/claude/chat - Non-streaming avec thinking router.post('/chat', async (req, res) => { const { messages, model, system, maxTokens = 4096, temperature = 1, enableMemoryTools = true, // Nouveaux paramètres thinking enableThinking = false, thinkingBudgetTokens = 10000 } = req.body; const apiParams = { model, max_tokens: maxTokens, temperature, system: buildSystemPrompt(system, enableMemoryTools), messages: conversationMessages }; // Ajouter thinking si activé if (enableThinking) { apiParams.thinking = { type: 'enabled', budget_tokens: thinkingBudgetTokens }; } // Ajouter tools si activé if (tools.length > 0) { apiParams.tools = tools; } const response = await anthropic.messages.create(apiParams); // ... rest of logic }); ``` #### `server/routes/messages.js` **Modifications dans les endpoints de streaming**: ```javascript // POST /:conversationId/messages/stream router.post('/:conversationId/messages', async (req, res) => { // Parse settings avec thinking support const settings = JSON.parse(conversation.settings || '{}'); const model = conversation.model || 'claude-sonnet-4-5-20250929'; const temperature = settings.temperature || 1; const maxTokens = settings.maxTokens || 4096; const enableThinking = settings.enableThinking || false; const thinkingBudgetTokens = settings.thinkingBudgetTokens || 10000; // Build request options const requestOptions = { model, max_tokens: maxTokens, temperature, messages: conversationMessages }; // Add thinking if enabled if (enableThinking) { requestOptions.thinking = { type: 'enabled', budget_tokens: thinkingBudgetTokens }; } // Add system prompt if (systemPrompt) { requestOptions.system = systemPrompt; } // Add tools if (tools.length > 0) { requestOptions.tools = tools; } // Create streaming response const stream = await anthropic.messages.stream(requestOptions); // Handle thinking deltas in stream for await (const event of stream) { if (event.type === 'content_block_start') { if (event.content_block.type === 'thinking') { console.log('[Messages] Thinking block started'); res.write(`data: ${JSON.stringify({ type: 'thinking_start', index: event.index })}\n\n`); } } else if (event.type === 'content_block_delta') { if (event.delta.type === 'thinking_delta') { fullThinkingContent += event.delta.thinking; res.write(`data: ${JSON.stringify({ type: 'thinking', text: event.delta.thinking })}\n\n`); } else if (event.delta.type === 'text_delta') { fullContent += event.delta.text; res.write(`data: ${JSON.stringify({ type: 'content', text: event.delta.text })}\n\n`); } } } }); ``` ### 3.2 Nouveaux Types de Réponse **Structure de réponse avec thinking**: ```javascript { "content": [ { "type": "thinking", "thinking": "Let me analyze this step by step...", "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve..." }, { "type": "text", "text": "Based on my analysis..." } ] } ``` **Events de streaming**: ```javascript // Événement de début de thinking { "type": "thinking_start", "index": 0 } // Événement de delta thinking { "type": "thinking", "text": "Let me analyze..." } // Événement de fin de thinking (automatique avec content_block_stop) { "type": "thinking_stop", "index": 0 } // Événements de contenu normal { "type": "content", "text": "Based on..." } ``` ### 3.3 Base de Données #### Modifications du schéma `conversations` ```sql -- Ajouter colonne pour activer thinking par conversation ALTER TABLE conversations ADD COLUMN enable_thinking INTEGER DEFAULT 0; ALTER TABLE conversations ADD COLUMN thinking_budget_tokens INTEGER DEFAULT 10000; ``` #### Modifications du schéma `messages` ```sql -- Ajouter colonne pour stocker thinking content ALTER TABLE messages ADD COLUMN thinking_content TEXT DEFAULT NULL; ALTER TABLE messages ADD COLUMN thinking_signature TEXT DEFAULT NULL; ``` **Migration dans `server/db/index.js`**: ```javascript // Add thinking columns if they don't exist const hasThinkingColumns = db.prepare(` SELECT COUNT(*) as count FROM pragma_table_info('conversations') WHERE name IN ('enable_thinking', 'thinking_budget_tokens') `).get(); if (hasThinkingColumns.count < 2) { console.log('Adding thinking columns to conversations table...'); db.exec(` ALTER TABLE conversations ADD COLUMN enable_thinking INTEGER DEFAULT 0; ALTER TABLE conversations ADD COLUMN thinking_budget_tokens INTEGER DEFAULT 10000; `); } const hasMessageThinking = db.prepare(` SELECT COUNT(*) as count FROM pragma_table_info('messages') WHERE name IN ('thinking_content', 'thinking_signature') `).get(); if (hasMessageThinking.count < 2) { console.log('Adding thinking columns to messages table...'); db.exec(` ALTER TABLE messages ADD COLUMN thinking_content TEXT DEFAULT NULL; ALTER TABLE messages ADD COLUMN thinking_signature TEXT DEFAULT NULL; `); } ``` --- ## 4. Architecture Frontend ### 4.1 Interface Utilisateur #### Nouveau composant: `ThinkingBlock` **Fichier**: `src/components/ThinkingBlock.jsx` ```jsx import React, { useState } from 'react'; function ThinkingBlock({ thinking, signature, isStreaming }) { const [isExpanded, setIsExpanded] = useState(false); return (
{/* Header avec toggle */} {/* Contenu thinking (collapsible) */} {isExpanded && (
{thinking || 'Thinking in progress...'}
)}
); } export default ThinkingBlock; ``` #### Modifications dans `src/App.jsx` **1. État pour thinking dans Message Component**: ```jsx function Message({ message, isStreaming }) { const [thinkingContent, setThinkingContent] = useState(message.thinking_content || ''); const [isThinkingStreaming, setIsThinkingStreaming] = useState(false); return (
{/* Afficher thinking block si présent */} {thinkingContent && ( )} {/* Contenu normal du message */}
{message.content}
); } ``` **2. Settings Panel - Ajouter contrôles thinking**: ```jsx function ConversationSettings({ conversation, onUpdate }) { const [settings, setSettings] = useState(JSON.parse(conversation.settings || '{}')); return (
{/* Existing settings */}
{/* Nouveau: Extended Thinking Toggle */}

Enable enhanced reasoning for complex tasks

{/* Thinking Budget (si thinking activé) */} {settings.enableThinking && (
{ const newSettings = { ...settings, thinkingBudgetTokens: parseInt(e.target.value) }; setSettings(newSettings); onUpdate(newSettings); }} className="w-full" />
1K tokens {(settings.thinkingBudgetTokens || 10000).toLocaleString()} tokens 32K tokens

Higher budgets enable more thorough analysis

)}
); } ``` **3. Streaming Handler - Gérer thinking deltas**: ```jsx async function sendMessage(content) { // ... existing code ... const eventSource = new EventSource(`${API_BASE}/conversations/${conversationId}/messages`); let currentThinking = ''; let currentContent = ''; let isInThinkingBlock = false; eventSource.addEventListener('message', (event) => { const data = JSON.parse(event.data); switch (data.type) { case 'thinking_start': isInThinkingBlock = true; currentThinking = ''; setIsThinkingStreaming(true); break; case 'thinking': currentThinking += data.text; // Update thinking content in real-time setThinkingContent(currentThinking); break; case 'thinking_stop': isInThinkingBlock = false; setIsThinkingStreaming(false); break; case 'content': currentContent += data.text; // Update message content setMessageContent(currentContent); break; case 'done': eventSource.close(); // Save message with thinking saveMessage({ content: currentContent, thinking_content: currentThinking, thinking_signature: data.thinking_signature }); break; } }); } ``` ### 4.2 Indicateurs Visuels #### Badge "Thinking Enabled" dans conversation list ```jsx function ConversationListItem({ conversation }) { const settings = JSON.parse(conversation.settings || '{}'); return (
{conversation.title}
{/* Badge thinking */} {settings.enableThinking && ( Thinking )}
); } ``` --- ## 5. Gestion du Streaming ### 5.1 Events Sequence ``` User: "Solve this complex math problem..." Event 1: thinking_start → Show thinking block with loading animation Event 2-N: thinking deltas → Update thinking content incrementally → Show typing animation Event N+1: thinking_stop (implicit with content_block_stop) → Stop thinking animation → Mark thinking complete Event N+2: content_start → Start showing answer Event N+3-M: content deltas → Stream answer text Event M+1: done → Save complete message with thinking + content ``` ### 5.2 Error Handling **Timeout pour thinking**: ```javascript const THINKING_TIMEOUT = 120000; // 2 minutes let thinkingTimeout = setTimeout(() => { console.warn('[Thinking] Timeout reached'); res.write(`data: ${JSON.stringify({ type: 'thinking_timeout', message: 'Thinking process is taking longer than expected...' })}\n\n`); }, THINKING_TIMEOUT); // Clear timeout when thinking completes stream.on('content_block_stop', () => { clearTimeout(thinkingTimeout); }); ``` **Redacted thinking blocks**: ```javascript if (event.content_block.type === 'redacted_thinking') { console.log('[Thinking] Redacted thinking block detected'); res.write(`data: ${JSON.stringify({ type: 'thinking_redacted', message: 'Some reasoning has been encrypted for safety' })}\n\n`); } ``` --- ## 6. Compatibilité avec Tools ### 6.1 Préservation des Thinking Blocks **Important**: Lors de l'utilisation de tools avec thinking, il faut préserver les thinking blocks: ```javascript // Quand Claude utilise un tool if (finalMessage.stop_reason === 'tool_use') { // Extraire tous les blocks thinking ET tool_use const thinkingBlocks = finalMessage.content.filter(b => b.type === 'thinking' || b.type === 'redacted_thinking' ); const toolUseBlocks = finalMessage.content.filter(b => b.type === 'tool_use' ); // Ajouter à la conversation conversationMessages.push({ role: 'assistant', content: [...thinkingBlocks, ...toolUseBlocks] }); // Exécuter tools const toolResults = await processToolCalls(toolUseBlocks); // Continuer avec les résultats conversationMessages.push({ role: 'user', content: toolResults }); } ``` ### 6.2 Interleaved Thinking (Beta) Pour activer le thinking entre les tool calls: ```javascript // Ajouter le beta header const response = await anthropic.messages.create({ model: 'claude-opus-4-5', thinking: { type: 'enabled', budget_tokens: 20000 }, tools: memoryTools, messages: conversationMessages }, { headers: { 'anthropic-beta': 'interleaved-thinking-2025-05-14' } }); ``` --- ## 7. Pricing & Token Management ### 7.1 Facturation **Résumé (Claude 4+)**: - Input tokens: Tokens de la requête (excluant thinking précédents) - Output tokens (facturés): Tokens thinking originaux complets - Output tokens (visibles): Tokens thinking résumés affichés - Pas de charge: Tokens utilisés pour générer le résumé **Important**: Le nombre de tokens facturés ≠ tokens visibles dans la réponse. ### 7.2 Token Tracking **Backend - Logging détaillé**: ```javascript // Après réponse avec thinking console.log('[Thinking Tokens]', { input_tokens: response.usage.input_tokens, output_tokens: response.usage.output_tokens, // Inclut thinking complet visible_thinking_tokens: calculateTokens(thinkingContent), // Thinking résumé text_output_tokens: calculateTokens(textContent) }); // Sauvegarder dans usage_tracking db.prepare(` INSERT INTO usage_tracking ( id, user_id, conversation_id, message_id, model, input_tokens, output_tokens, thinking_tokens, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( uuidv4(), 'default', conversationId, messageId, model, response.usage.input_tokens, response.usage.output_tokens, calculateTokens(thinkingContent), // Pour tracking new Date().toISOString() ); ``` **Frontend - Affichage dans usage stats**: ```jsx function UsageStats({ conversation }) { return (
{conversation.token_count.toLocaleString()}
{conversation.thinking_tokens > 0 && ( <>
{conversation.thinking_tokens.toLocaleString()}

Thinking tokens are summarized but billed at full rate

)}
); } ``` --- ## 8. Best Practices ### 8.1 Quand activer thinking **Recommandé pour**: - ✅ Problèmes mathématiques complexes - ✅ Analyse de code et debugging - ✅ Raisonnement logique multi-étapes - ✅ Analyse approfondie de documents - ✅ Tâches nécessitant planification **Pas nécessaire pour**: - ❌ Questions simples - ❌ Tâches créatives (écriture, brainstorming) - ❌ Conversations courtes - ❌ Réponses rapides ### 8.2 Budget Recommendations | Type de tâche | Budget recommandé | |---------------|-------------------| | Calculs simples | 1,024 - 4,096 tokens | | Analyse standard | 4,096 - 10,000 tokens | | Problèmes complexes | 10,000 - 16,000 tokens | | Tâches très complexes | 16,000 - 32,000 tokens | | Recherche approfondie | 32,000+ tokens (batch) | **Note**: Au-delà de 32K tokens, utiliser batch processing pour éviter les timeouts. ### 8.3 UI/UX Guidelines 1. **Visibility**: Thinking blocks doivent être collapsibles par défaut 2. **Feedback**: Montrer animation pendant le thinking streaming 3. **Transparency**: Indiquer clairement quand thinking est actif 4. **Performance**: Thinking peut augmenter le temps de réponse de 2-5x 5. **Settings**: Permettre d'activer/désactiver par conversation --- ## 9. Plan d'Implémentation ### Phase 1: Backend Core (2-3h) - [ ] Modifier `server/routes/claude.js` pour supporter thinking parameter - [ ] Modifier `server/routes/messages.js` pour streaming thinking - [ ] Ajouter colonnes DB pour thinking storage - [ ] Migration base de données - [ ] Tests API avec thinking enabled ### Phase 2: Frontend UI (3-4h) - [ ] Créer composant `ThinkingBlock.jsx` - [ ] Intégrer thinking display dans messages - [ ] Ajouter toggle thinking dans settings - [ ] Ajouter thinking budget slider - [ ] Tests visuels et UX ### Phase 3: Streaming & Real-time (2-3h) - [ ] Implémenter thinking_delta handling - [ ] Animations de streaming - [ ] Gestion des timeouts - [ ] Error handling pour redacted thinking - [ ] Tests de streaming ### Phase 4: Tools Integration (2h) - [ ] Préservation thinking blocks avec tools - [ ] Tests thinking + memory tools - [ ] Tests thinking + autres tools futurs ### Phase 5: Polish & Optimization (2h) - [ ] Token tracking et logging - [ ] Usage analytics pour thinking - [ ] Documentation utilisateur - [ ] Performance optimization - [ ] Tests end-to-end ### Phase 6: Testing & Deployment (1-2h) - [ ] Tests avec différents modèles - [ ] Tests avec différents budgets - [ ] Tests cas d'edge (redacted, timeouts) - [ ] Commit et push - [ ] Documentation finale **Temps total estimé**: 12-16 heures --- ## 10. Exemples de Code Complets ### 10.1 Exemple Backend Complet ```javascript // server/routes/messages.js - POST /:conversationId/messages router.post('/:conversationId/messages', async (req, res) => { const db = getDatabase(); const { conversationId } = req.params; const { content, images } = req.body; // Validate conversation exists const conversation = db.prepare('SELECT * FROM conversations WHERE id = ? AND is_deleted = 0') .get(conversationId); if (!conversation) { return res.status(404).json({ error: { message: 'Conversation not found', status: 404 } }); } // Set up SSE headers res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // Parse settings with thinking support const settings = JSON.parse(conversation.settings || '{}'); const model = conversation.model || 'claude-sonnet-4-5-20250929'; const temperature = settings.temperature || 1; const maxTokens = settings.maxTokens || 4096; const enableThinking = settings.enableThinking || false; const thinkingBudgetTokens = settings.thinkingBudgetTokens || 10000; const enableMemoryTools = true; // Save user message const userMessageId = uuidv4(); const now = new Date().toISOString(); db.prepare(` INSERT INTO messages (id, conversation_id, role, content, created_at, images) VALUES (?, ?, ?, ?, ?, ?) `).run(userMessageId, conversationId, 'user', content, now, JSON.stringify(images || [])); // Get conversation history const dbMessages = db.prepare(` SELECT role, content, images FROM messages WHERE conversation_id = ? ORDER BY created_at ASC `).all(conversationId); // Format messages for Claude API const apiMessages = dbMessages.map(m => ({ role: m.role, content: m.content })); // Get tools and system prompt const tools = enableMemoryTools ? getMemoryTools() : []; const systemPrompt = buildSystemPrompt( getGlobalCustomInstructions(), getProjectCustomInstructions(conversation.project_id), enableMemoryTools ); // Tracking variables const assistantMessageId = uuidv4(); let fullThinkingContent = ''; let thinkingSignature = ''; let fullContent = ''; let totalInputTokens = 0; let totalOutputTokens = 0; try { // Build request options const requestOptions = { model, max_tokens: maxTokens, temperature, messages: apiMessages }; if (systemPrompt) { requestOptions.system = systemPrompt; } if (tools.length > 0) { requestOptions.tools = tools; } // Add thinking if enabled if (enableThinking) { requestOptions.thinking = { type: 'enabled', budget_tokens: thinkingBudgetTokens }; console.log(`[Messages] Extended thinking enabled with budget: ${thinkingBudgetTokens}`); } // Create streaming response const stream = await anthropic.messages.stream(requestOptions); let isInThinkingBlock = false; let currentBlockIndex = -1; // Stream events to client for await (const event of stream) { if (event.type === 'content_block_start') { currentBlockIndex = event.index; if (event.content_block.type === 'thinking') { isInThinkingBlock = true; console.log('[Messages] Thinking block started'); res.write(`data: ${JSON.stringify({ type: 'thinking_start', index: currentBlockIndex })}\n\n`); } else if (event.content_block.type === 'tool_use') { console.log(`[Messages] Tool use requested: ${event.content_block.name}`); res.write(`data: ${JSON.stringify({ type: 'tool_use', tool: event.content_block.name, id: event.content_block.id })}\n\n`); } } else if (event.type === 'content_block_delta') { if (event.delta.type === 'thinking_delta') { fullThinkingContent += event.delta.thinking; res.write(`data: ${JSON.stringify({ type: 'thinking', text: event.delta.thinking })}\n\n`); } else if (event.delta.type === 'text_delta') { fullContent += event.delta.text; res.write(`data: ${JSON.stringify({ type: 'content', text: event.delta.text })}\n\n`); } else if (event.delta.type === 'signature_delta') { thinkingSignature += event.delta.signature; } } else if (event.type === 'content_block_stop') { if (isInThinkingBlock) { isInThinkingBlock = false; console.log('[Messages] Thinking block completed'); res.write(`data: ${JSON.stringify({ type: 'thinking_stop', index: currentBlockIndex })}\n\n`); } } else if (event.type === 'message_delta') { if (event.usage) { totalInputTokens += event.usage.input_tokens || 0; totalOutputTokens += event.usage.output_tokens || 0; } } } // Get final message const finalMessage = await stream.finalMessage(); totalInputTokens = finalMessage.usage?.input_tokens || totalInputTokens; totalOutputTokens = finalMessage.usage?.output_tokens || totalOutputTokens; // Save assistant message with thinking const assistantNow = new Date().toISOString(); db.prepare(` INSERT INTO messages ( id, conversation_id, role, content, thinking_content, thinking_signature, created_at, tokens, finish_reason ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( assistantMessageId, conversationId, 'assistant', fullContent, fullThinkingContent || null, thinkingSignature || null, assistantNow, totalOutputTokens, finalMessage.stop_reason ); // Update conversation db.prepare(` UPDATE conversations SET last_message_at = ?, updated_at = ?, message_count = message_count + 2, token_count = token_count + ? WHERE id = ? `).run(assistantNow, assistantNow, totalInputTokens + totalOutputTokens, conversationId); // Track usage db.prepare(` INSERT INTO usage_tracking ( id, user_id, conversation_id, message_id, model, input_tokens, output_tokens, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `).run( uuidv4(), 'default', conversationId, assistantMessageId, model, totalInputTokens, totalOutputTokens, assistantNow ); // Send done event res.write(`data: ${JSON.stringify({ type: 'done', id: assistantMessageId, model: finalMessage.model, stopReason: finalMessage.stop_reason, usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }, thinkingTokens: fullThinkingContent.length > 0 ? Math.ceil(fullThinkingContent.length / 4) : 0 })}\n\n`); res.end(); } catch (error) { console.error('Claude API stream error:', error); res.write(`data: ${JSON.stringify({ type: 'error', message: error.message })}\n\n`); res.end(); } }); ``` ### 10.2 Exemple Frontend Complet ```jsx // src/App.jsx - Message Component with Thinking function Message({ message, isStreaming }) { const [thinkingContent, setThinkingContent] = useState(message.thinking_content || ''); const [isThinkingExpanded, setIsThinkingExpanded] = useState(false); const [isThinkingStreaming, setIsThinkingStreaming] = useState(false); return (
{/* Thinking Block (si présent) */} {thinkingContent && message.role === 'assistant' && (
{/* Header */} {/* Thinking Content (collapsible) */} {isThinkingExpanded && (
{thinkingContent || (
Thinking in progress...
)}
{/* Stats footer */}
~{Math.ceil(thinkingContent.length / 4)} tokens Summarized for display
)}
)} {/* Message Content */}
{message.content}
); } // Streaming handler avec thinking support async function sendMessage(conversationId, content) { const response = await fetch(`${API_BASE}/conversations/${conversationId}/messages`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let currentThinking = ''; let currentContent = ''; let isInThinkingBlock = false; let messageId = null; // Create temporary message const tempMessage = { id: 'temp-' + Date.now(), role: 'assistant', content: '', thinking_content: '', isStreaming: true }; setMessages(prev => [...prev, tempMessage]); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (!line.startsWith('data: ')) continue; try { const data = JSON.parse(line.slice(6)); switch (data.type) { case 'thinking_start': isInThinkingBlock = true; setMessages(prev => prev.map(m => m.id === tempMessage.id ? { ...m, isThinkingStreaming: true } : m )); break; case 'thinking': currentThinking += data.text; setMessages(prev => prev.map(m => m.id === tempMessage.id ? { ...m, thinking_content: currentThinking } : m )); break; case 'thinking_stop': isInThinkingBlock = false; setMessages(prev => prev.map(m => m.id === tempMessage.id ? { ...m, isThinkingStreaming: false } : m )); break; case 'content': currentContent += data.text; setMessages(prev => prev.map(m => m.id === tempMessage.id ? { ...m, content: currentContent } : m )); break; case 'done': messageId = data.id; // Update with final message setMessages(prev => prev.map(m => m.id === tempMessage.id ? { id: messageId, role: 'assistant', content: currentContent, thinking_content: currentThinking, isStreaming: false, isThinkingStreaming: false, usage: data.usage } : m )); break; case 'error': console.error('Streaming error:', data.message); setMessages(prev => prev.filter(m => m.id !== tempMessage.id)); alert('Error: ' + data.message); break; } } catch (e) { console.error('Error parsing SSE data:', e); } } } } ``` --- ## 11. Testing Checklist ### 11.1 Tests Fonctionnels - [ ] Thinking activé pour conversation → blocs thinking apparaissent - [ ] Thinking désactivé → pas de blocs thinking - [ ] Streaming thinking fonctionne en temps réel - [ ] Toggle thinking dans settings fonctionne - [ ] Budget slider fonctionne (1K-32K) - [ ] Thinking blocks sont collapsibles - [ ] Thinking blocks persistent après refresh - [ ] Thinking + memory tools fonctionnent ensemble - [ ] Multiple thinking blocks dans une réponse - [ ] Redacted thinking est géré correctement ### 11.2 Tests Edge Cases - [ ] Thinking timeout (>2 min) géré gracefully - [ ] Erreurs réseau pendant thinking stream - [ ] Thinking avec très grand budget (>32K) - [ ] Thinking avec petit budget (1K) - [ ] Conversation avec 100+ messages et thinking - [ ] Regenerate avec thinking activé - [ ] Edit message avec thinking - [ ] Export conversation avec thinking ### 11.3 Tests Performance - [ ] Temps de réponse thinking vs non-thinking - [ ] Mémoire utilisée avec thinking streaming - [ ] Database performance avec thinking storage - [ ] UI responsive pendant thinking - [ ] Multiple conversations avec thinking simultanées --- ## 12. Documentation Utilisateur ### Guide Rapide **Qu'est-ce que Extended Thinking?** Extended Thinking permet à Claude de "montrer son travail" en exposant son processus de raisonnement étape par étape avant de donner sa réponse finale. Particulièrement utile pour: - Problèmes mathématiques complexes - Analyse de code approfondie - Raisonnement logique multi-étapes - Planification de tâches complexes **Comment l'activer?** 1. Ouvrir les paramètres de conversation (icône ⚙️) 2. Activer "Extended Thinking" 3. Ajuster le budget si nécessaire (10K par défaut) 4. Commencer à discuter **Interpréter les blocs de thinking** - 🧠 **Thinking blocks** (bleu): Processus de réflexion de Claude - Cliquer pour expand/collapse - Contenu est résumé mais bille au tarif complet - Peut augmenter le temps de réponse de 2-5x **Quand l'utiliser?** ✅ **OUI**: Calculs, code, analyse, logique complexe ❌ **NON**: Questions simples, chat rapide, créativité --- ## 13. Notes Importantes ### 13.1 Limitations 1. **Incompatibilités**: - ❌ Pas compatible avec `temperature` custom ou `top_k` - ❌ Pas de pre-fill responses avec thinking - ❌ Pas de forced tool use (`tool_choice: "any"`) - ✅ Compatible avec `top_p` (0.95-1) 2. **Context Window**: - Thinking blocks précédents retirés automatiquement - Token budget thinking compte vers `max_tokens` - Formule: `context = current_input + (thinking + encrypted + output)` 3. **Caching**: - Changer thinking parameters invalide message cache - System prompt reste en cache - Thinking blocks comptent comme input tokens en cache ### 13.2 Modèles Spécifiques **Claude Opus 4.5** (unique): - Préserve thinking blocks par défaut - Meilleure optimization cache - Économies de tokens sur multi-turn **Claude 3.7** (déprécié): - Retourne thinking COMPLET (non résumé) - Tokens visibles = tokens facturés - Migration vers Claude 4+ recommandée --- ## Annexes ### A. Structure de fichiers complète ``` generations/my_project/ ├── server/ │ ├── routes/ │ │ ├── claude.js # Modifié: thinking support │ │ └── messages.js # Modifié: thinking streaming │ ├── db/ │ │ └── index.js # Modifié: thinking columns migration │ └── config/ │ └── thinkingDefaults.js # Nouveau: configuration thinking ├── src/ │ ├── components/ │ │ └── ThinkingBlock.jsx # Nouveau: composant thinking │ ├── App.jsx # Modifié: thinking UI integration │ └── utils/ │ └── thinkingHelpers.js # Nouveau: helpers thinking └── prompts/ └── extended_thinking_spec.md # Cette spec ``` ### B. Variables d'environnement Aucune nouvelle variable nécessaire. Extended Thinking fonctionne avec les credentials Anthropic existants. ### C. Compatibilité navigateurs Extended Thinking utilise EventSource (SSE) qui est supporté par: - ✅ Chrome/Edge 79+ - ✅ Firefox 65+ - ✅ Safari 13+ - ❌ IE11 (non supporté) --- **Fin de la spécification Extended Thinking** Version: 1.0 Date: 2025-12-18 Auteur: Claude Sonnet 4.5