Add Claude AI support as alternative to Mistral
- Create claudeClient.js service for Claude API integration - Add CLAUDE_API_KEY to .env configuration - Claude uses identical prompt structure and section extraction logic - Supports claude-3-5-sonnet-20241022 model Next: Add provider selection to orchestrator and routes
This commit is contained in:
parent
5a2ca101f8
commit
60fdc9a66f
144
backend/src/services/claudeClient.js
Normal file
144
backend/src/services/claudeClient.js
Normal file
@ -0,0 +1,144 @@
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const CLAUDE_API_KEY = process.env.CLAUDE_API_KEY
|
||||
const CLAUDE_API_URL = 'https://api.anthropic.com/v1/messages'
|
||||
|
||||
/**
|
||||
* Generic AI prompt for collaborative document editing
|
||||
*/
|
||||
function getAgentPrompt(agentName) {
|
||||
return `You are an AI assistant named ${agentName} collaborating on a technical document design.
|
||||
|
||||
Your responsibilities:
|
||||
1. Review the current document structure
|
||||
2. Either:
|
||||
a) Modify ONE existing section (identified by #, ##, ###, #### headers), OR
|
||||
b) Create a NEW section if you think it's needed, OR
|
||||
c) Delete a section if you think it's redundant or not useful
|
||||
3. Provide your thinking process and reasoning
|
||||
4. Return ONLY the section (modified or new) with its header, or command to delete, or confirm it's good as-is
|
||||
|
||||
CRITICAL RULES - FOLLOW THESE EXACTLY:
|
||||
- Work on exactly ONE section only
|
||||
- NEVER return the entire document
|
||||
- NEVER return multiple sections
|
||||
- Return ONLY the section you're working on, not the whole document
|
||||
- You CAN create a new section if document is missing important content
|
||||
- You CAN delete a section if it's redundant, duplicate, or not useful
|
||||
- To delete a section, respond: "DELETE: ## Section Name" (with exact header)
|
||||
- If section is good, respond: "Section is good, no changes needed"
|
||||
- Think step-by-step about what could be improved or removed
|
||||
- Share your reasoning process
|
||||
|
||||
RESPONSE FORMAT - FOLLOW THIS EXACTLY:
|
||||
THINKING: [Your analysis and reasoning about the current document]
|
||||
DECISION: [Exactly what you will do: modify section X, create new section Y, delete section Z, or keep as-is]
|
||||
SECTION:
|
||||
[ONLY ONE: Either a markdown section starting with # or ##, a DELETE command, or the text "Section is good, no changes needed"]
|
||||
|
||||
EXAMPLE OF CORRECT RESPONSE:
|
||||
THINKING: The Overview section is too brief and doesn't explain the main purpose.
|
||||
DECISION: I will modify the Overview section to be more comprehensive.
|
||||
SECTION:
|
||||
## Overview
|
||||
This is a technical document for designing system architecture...
|
||||
|
||||
EXAMPLE OF INCORRECT RESPONSE (DO NOT DO THIS):
|
||||
[The entire document repeated here] <- WRONG!`
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Claude API
|
||||
*/
|
||||
async function callClaudeAPI(messages) {
|
||||
const response = await fetch(CLAUDE_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': CLAUDE_API_KEY,
|
||||
'anthropic-version': '2023-06-01'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'claude-3-5-sonnet-20241022',
|
||||
max_tokens: 2048,
|
||||
system: messages[0].content,
|
||||
messages: messages.slice(1)
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text()
|
||||
throw new Error(`Claude API error: ${error}`)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate agent response using Claude
|
||||
*/
|
||||
export async function generateAgentResponseSync(agentName, prompt, currentDocument = '') {
|
||||
const systemPrompt = getAgentPrompt(agentName)
|
||||
|
||||
const messages = [
|
||||
{ role: 'user', content: systemPrompt },
|
||||
{ role: 'user', content: `Project description: ${prompt}\n\nCurrent document:\n${currentDocument}` }
|
||||
]
|
||||
|
||||
try {
|
||||
const response = await callClaudeAPI(messages)
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.content?.[0]?.text) {
|
||||
throw new Error('Invalid response from Claude API')
|
||||
}
|
||||
|
||||
return data.content[0].text
|
||||
} catch (error) {
|
||||
console.error(`Error generating response from ${agentName}:`, error)
|
||||
return `Error: ${error.message}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract section from AI response
|
||||
* Validates that we get a proper section, not the entire document
|
||||
*/
|
||||
export function extractSection(aiResponse) {
|
||||
const sectionMatch = aiResponse.match(/SECTION:\s*([\s\S]*?)(?:$)/)
|
||||
if (sectionMatch) {
|
||||
const extracted = sectionMatch[1].trim()
|
||||
|
||||
// Validate: extracted should be either:
|
||||
// 1. A markdown section starting with # (DELETE: or "Section is good...")
|
||||
// 2. OR a single section with < 5000 chars (not entire document)
|
||||
const isMarkdownSection = /^(#|DELETE:|Section is good)/.test(extracted)
|
||||
const isShortEnough = extracted.length < 5000 // Single section should be < 5KB
|
||||
|
||||
if (isMarkdownSection || isShortEnough) {
|
||||
return extracted
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: if no SECTION: tag found, treat entire response as section
|
||||
// but only if it starts with markdown header or is a command
|
||||
if (/^(#|DELETE:|Section is good)/.test(aiResponse)) {
|
||||
return aiResponse.trim()
|
||||
}
|
||||
|
||||
// If none of the above, return error indicator
|
||||
return "ERROR: Response does not contain a valid section format"
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract thinking from AI response
|
||||
*/
|
||||
export function extractThinking(aiResponse) {
|
||||
const thinkingMatch = aiResponse.match(/THINKING:\s*([\s\S]*?)(?:DECISION:|SECTION:)/)
|
||||
if (thinkingMatch) {
|
||||
return thinkingMatch[1].trim()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user