refactor(asl): ASL frontend architecture refactoring with left navigation

- feat: Create ASLLayout component with 7-module left navigation
- feat: Implement Title Screening Settings page with optimized PICOS layout
- feat: Add placeholder pages for Workbench and Results
- fix: Fix nested routing structure for React Router v6
- fix: Resolve Spin component warning in MainLayout
- fix: Add QueryClientProvider to App.tsx
- style: Optimize PICOS form layout (P+I left, C+O+S right)
- style: Align Inclusion/Exclusion criteria side-by-side
- docs: Add architecture refactoring and routing fix reports

Ref: Week 2 Frontend Development
Scope: ASL module MVP - Title Abstract Screening
This commit is contained in:
2025-11-18 21:51:51 +08:00
parent e3e7e028e8
commit 3634933ece
213 changed files with 20054 additions and 442 deletions

View File

@@ -0,0 +1,359 @@
/**
* CloseAI集成测试脚本
*
* 测试通过CloseAI代理访问GPT-5和Claude-4.5模型
*
* 运行方式:
* ```bash
* cd backend
* npx tsx src/scripts/test-closeai.ts
* ```
*
* 环境变量要求:
* - CLOSEAI_API_KEY: CloseAI API密钥
* - CLOSEAI_OPENAI_BASE_URL: OpenAI端点
* - CLOSEAI_CLAUDE_BASE_URL: Claude端点
*
* 参考文档docs/02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md
*/
import { LLMFactory } from '../common/llm/adapters/LLMFactory.js';
import { config } from '../config/env.js';
/**
* 测试配置验证
*/
function validateConfig() {
console.log('🔍 验证环境配置...\n');
const checks = [
{
name: 'CLOSEAI_API_KEY',
value: config.closeaiApiKey,
required: true,
},
{
name: 'CLOSEAI_OPENAI_BASE_URL',
value: config.closeaiOpenaiBaseUrl,
required: true,
},
{
name: 'CLOSEAI_CLAUDE_BASE_URL',
value: config.closeaiClaudeBaseUrl,
required: true,
},
];
let allValid = true;
for (const check of checks) {
const status = check.value ? '✅' : '❌';
console.log(`${status} ${check.name}: ${check.value ? '已配置' : '未配置'}`);
if (check.required && !check.value) {
allValid = false;
}
}
console.log('');
if (!allValid) {
throw new Error('环境配置不完整,请检查 .env 文件');
}
console.log('✅ 环境配置验证通过\n');
}
/**
* 测试GPT-5-Pro
*/
async function testGPT5() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('1⃣ 测试 GPT-5-Pro');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
try {
const gpt5 = LLMFactory.getAdapter('gpt-5');
console.log('📤 发送测试请求...');
console.log('提示词: "你好,请用一句话介绍你自己。"\n');
const startTime = Date.now();
const response = await gpt5.chat([
{
role: 'user',
content: '你好,请用一句话介绍你自己。',
},
]);
const duration = Date.now() - startTime;
console.log('📥 收到响应:\n');
console.log(`模型: ${response.model}`);
console.log(`内容: ${response.content}`);
console.log(`耗时: ${duration}ms`);
if (response.usage) {
console.log(`Token使用: ${response.usage.totalTokens} (输入: ${response.usage.promptTokens}, 输出: ${response.usage.completionTokens})`);
}
console.log('\n✅ GPT-5测试通过\n');
return true;
} catch (error) {
console.error('\n❌ GPT-5测试失败:', error);
return false;
}
}
/**
* 测试Claude-4.5-Sonnet
*/
async function testClaude() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('2⃣ 测试 Claude-4.5-Sonnet');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
try {
const claude = LLMFactory.getAdapter('claude-4.5');
console.log('📤 发送测试请求...');
console.log('提示词: "你好,请用一句话介绍你自己。"\n');
const startTime = Date.now();
const response = await claude.chat([
{
role: 'user',
content: '你好,请用一句话介绍你自己。',
},
]);
const duration = Date.now() - startTime;
console.log('📥 收到响应:\n');
console.log(`模型: ${response.model}`);
console.log(`内容: ${response.content}`);
console.log(`耗时: ${duration}ms`);
if (response.usage) {
console.log(`Token使用: ${response.usage.totalTokens} (输入: ${response.usage.promptTokens}, 输出: ${response.usage.completionTokens})`);
}
console.log('\n✅ Claude测试通过\n');
return true;
} catch (error) {
console.error('\n❌ Claude测试失败:', error);
return false;
}
}
/**
* 测试文献筛选场景(实际应用)
*/
async function testLiteratureScreening() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('3⃣ 测试文献筛选场景(双模型对比)');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
const testLiterature = {
title: 'Deep learning in medical imaging: A systematic review',
abstract: 'Background: Deep learning has shown remarkable performance in various medical imaging tasks. Methods: We systematically reviewed 150 studies on deep learning applications in radiology, pathology, and ophthalmology. Results: Deep learning models achieved high accuracy (>90%) in most tasks. Conclusion: Deep learning is a promising tool for medical image analysis.',
};
const picoPrompt = `
请根据以下PICO标准判断这篇文献是否应该纳入系统评价
**PICO标准**
- Population: 成年患者
- Intervention: 深度学习模型
- Comparison: 传统机器学习方法
- Outcome: 诊断准确率
**文献信息:**
标题:${testLiterature.title}
摘要:${testLiterature.abstract}
请输出JSON格式
{
"decision": "include/exclude/uncertain",
"reason": "判断理由",
"confidence": 0.0-1.0
}
`;
try {
console.log('📤 使用DeepSeek和GPT-5进行双模型对比筛选...\n');
// 并行调用两个模型
const [deepseekAdapter, gpt5Adapter] = [
LLMFactory.getAdapter('deepseek-v3'),
LLMFactory.getAdapter('gpt-5'),
];
const startTime = Date.now();
const [deepseekResponse, gpt5Response] = await Promise.all([
deepseekAdapter.chat([{ role: 'user', content: picoPrompt }]),
gpt5Adapter.chat([{ role: 'user', content: picoPrompt }]),
]);
const duration = Date.now() - startTime;
console.log('📥 DeepSeek响应:');
console.log(deepseekResponse.content);
console.log('');
console.log('📥 GPT-5响应:');
console.log(gpt5Response.content);
console.log('');
console.log(`⏱️ 总耗时: ${duration}ms并行`);
console.log(`💰 总Token: ${(deepseekResponse.usage?.totalTokens || 0) + (gpt5Response.usage?.totalTokens || 0)}`);
// 尝试解析JSON结果简单验证
try {
const deepseekDecision = JSON.parse(deepseekResponse.content);
const gpt5Decision = JSON.parse(gpt5Response.content);
console.log('\n✅ 双模型筛选结果:');
console.log(`DeepSeek决策: ${deepseekDecision.decision} (置信度: ${deepseekDecision.confidence})`);
console.log(`GPT-5决策: ${gpt5Decision.decision} (置信度: ${gpt5Decision.confidence})`);
if (deepseekDecision.decision === gpt5Decision.decision) {
console.log('✅ 两个模型一致,共识度高');
} else {
console.log('⚠️ 两个模型不一致建议人工复核或启用第三方仲裁Claude');
}
} catch (parseError) {
console.log('⚠️ JSON解析失败测试环境实际应用需要优化提示词');
}
console.log('\n✅ 文献筛选场景测试通过\n');
return true;
} catch (error) {
console.error('\n❌ 文献筛选场景测试失败:', error);
return false;
}
}
/**
* 测试流式调用(可选)
*/
async function testStreamMode() {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('4⃣ 测试流式调用GPT-5');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
try {
const gpt5 = LLMFactory.getAdapter('gpt-5');
console.log('📤 发送流式请求...');
console.log('提示词: "请写一首关于人工智能的短诗4行"\n');
console.log('📥 流式响应:\n');
const startTime = Date.now();
let fullContent = '';
let chunkCount = 0;
for await (const chunk of gpt5.chatStream([
{
role: 'user',
content: '请写一首关于人工智能的短诗4行',
},
])) {
if (chunk.content) {
process.stdout.write(chunk.content);
fullContent += chunk.content;
chunkCount++;
}
if (chunk.done) {
const duration = Date.now() - startTime;
console.log('\n');
console.log(`\n⏱ 耗时: ${duration}ms`);
console.log(`📦 Chunk数: ${chunkCount}`);
console.log(`📝 总字符数: ${fullContent.length}`);
if (chunk.usage) {
console.log(`💰 Token使用: ${chunk.usage.totalTokens}`);
}
}
}
console.log('\n✅ 流式调用测试通过\n');
return true;
} catch (error) {
console.error('\n❌ 流式调用测试失败:', error);
return false;
}
}
/**
* 主测试函数
*/
async function main() {
console.log('╔═══════════════════════════════════════════════════╗');
console.log('║ 🧪 CloseAI集成测试 ║');
console.log('║ 测试GPT-5和Claude-4.5通过CloseAI代理访问 ║');
console.log('╚═══════════════════════════════════════════════════╝\n');
try {
// 验证配置
validateConfig();
// 测试结果
const results = {
gpt5: false,
claude: false,
literatureScreening: false,
stream: false,
};
// 1. 测试GPT-5
results.gpt5 = await testGPT5();
// 2. 测试Claude-4.5
results.claude = await testClaude();
// 3. 测试文献筛选场景
results.literatureScreening = await testLiteratureScreening();
// 4. 测试流式调用(可选)
results.stream = await testStreamMode();
// 总结
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('📊 测试总结');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
const allPassed = Object.values(results).every((r) => r === true);
console.log(`GPT-5测试: ${results.gpt5 ? '✅ 通过' : '❌ 失败'}`);
console.log(`Claude测试: ${results.claude ? '✅ 通过' : '❌ 失败'}`);
console.log(`文献筛选场景: ${results.literatureScreening ? '✅ 通过' : '❌ 失败'}`);
console.log(`流式调用测试: ${results.stream ? '✅ 通过' : '❌ 失败'}`);
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
if (allPassed) {
console.log('🎉 所有测试通过CloseAI集成成功');
console.log('\n✅ 可以在ASL模块中使用GPT-5和Claude-4.5进行双模型对比筛选');
console.log('✅ 支持三模型共识仲裁DeepSeek + GPT-5 + Claude');
console.log('✅ 支持流式调用,适用于实时响应场景\n');
process.exit(0);
} else {
console.error('⚠️ 部分测试失败,请检查配置和网络连接\n');
process.exit(1);
}
} catch (error) {
console.error('❌ 测试执行失败:', error);
process.exit(1);
}
}
// 运行测试
main();