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:
donald
2025-12-04 00:05:36 +08:00
parent de89a64bf2
commit bf475d16c1
7 changed files with 832 additions and 2 deletions

View File

@@ -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}`);
}