Add Claude API support and fix CORS issues
- Add Claude API integration to LLM service - Create Express backend server with CORS support - Add API proxy example page - Fix CORS errors by routing through backend - Update LLM configuration to support Claude - Add package.json with dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,68 @@ class LLMService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 測試 Claude API 連線
|
||||
*/
|
||||
async testClaudeConnection() {
|
||||
try {
|
||||
if (!isProviderEnabled('claude')) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Claude API key not configured',
|
||||
provider: 'claude',
|
||||
};
|
||||
}
|
||||
|
||||
const config = getProviderConfig('claude');
|
||||
const url = `${config.apiUrl}/messages`;
|
||||
|
||||
const response = await axios.post(
|
||||
url,
|
||||
{
|
||||
model: config.model,
|
||||
max_tokens: 50,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, this is a connection test.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': config.apiKey,
|
||||
'anthropic-version': config.version,
|
||||
},
|
||||
timeout: config.timeout,
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.data.content) {
|
||||
return {
|
||||
success: true,
|
||||
message: 'Claude API connection successful',
|
||||
provider: 'claude',
|
||||
model: config.model,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Unexpected response from Claude API',
|
||||
provider: 'claude',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: error.response?.data?.error?.message || error.message,
|
||||
provider: 'claude',
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 測試所有 LLM 連線
|
||||
*/
|
||||
@@ -201,6 +263,7 @@ class LLMService {
|
||||
gemini: await this.testGeminiConnection(),
|
||||
deepseek: await this.testDeepSeekConnection(),
|
||||
openai: await this.testOpenAIConnection(),
|
||||
claude: await this.testClaudeConnection(),
|
||||
};
|
||||
|
||||
return results;
|
||||
@@ -342,6 +405,55 @@ class LLMService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Claude 生成內容
|
||||
*/
|
||||
async generateWithClaude(prompt, options = {}) {
|
||||
try {
|
||||
if (!isProviderEnabled('claude')) {
|
||||
throw new Error('Claude API not configured');
|
||||
}
|
||||
|
||||
const config = getProviderConfig('claude');
|
||||
const url = `${config.apiUrl}/messages`;
|
||||
|
||||
const response = await axios.post(
|
||||
url,
|
||||
{
|
||||
model: config.model,
|
||||
max_tokens: options.maxTokens || llmConfig.maxTokens,
|
||||
temperature: options.temperature || llmConfig.temperature,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': config.apiKey,
|
||||
'anthropic-version': config.version,
|
||||
},
|
||||
timeout: config.timeout,
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data?.content?.[0]?.text) {
|
||||
return {
|
||||
success: true,
|
||||
content: response.data.content[0].text,
|
||||
provider: 'claude',
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error('Invalid response format from Claude');
|
||||
} catch (error) {
|
||||
throw new Error(`Claude API error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用預設或指定的 LLM 生成內容
|
||||
*/
|
||||
@@ -355,6 +467,8 @@ class LLMService {
|
||||
return await this.generateWithDeepSeek(prompt, options);
|
||||
case 'openai':
|
||||
return await this.generateWithOpenAI(prompt, options);
|
||||
case 'claude':
|
||||
return await this.generateWithClaude(prompt, options);
|
||||
default:
|
||||
throw new Error(`Unknown provider: ${selectedProvider}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user