feat(ssa): Complete Phase I-IV intelligent dialogue and tool system development
Phase I - Session Blackboard + READ Layer: - SessionBlackboardService with Postgres-Only cache - DataProfileService for data overview generation - PicoInferenceService for LLM-driven PICO extraction - Frontend DataContextCard and VariableDictionaryPanel - E2E tests: 31/31 passed Phase II - Conversation Layer LLM + Intent Router: - ConversationService with SSE streaming - IntentRouterService (rule-first + LLM fallback, 6 intents) - SystemPromptService with 6-segment dynamic assembly - TokenTruncationService for context management - ChatHandlerService as unified chat entry - Frontend SSAChatPane and useSSAChat hook - E2E tests: 38/38 passed Phase III - Method Consultation + AskUser Standardization: - ToolRegistryService with Repository Pattern - MethodConsultService with DecisionTable + LLM enhancement - AskUserService with global interrupt handling - Frontend AskUserCard component - E2E tests: 13/13 passed Phase IV - Dialogue-Driven Analysis + QPER Integration: - ToolOrchestratorService (plan/execute/report) - analysis_plan SSE event for WorkflowPlan transmission - Dual-channel confirmation (ask_user card + workspace button) - PICO as optional hint for LLM parsing - E2E tests: 25/25 passed R Statistics Service: - 5 new R tools: anova_one, baseline_table, fisher, linear_reg, wilcoxon - Enhanced guardrails and block helpers - Comprehensive test suite (run_all_tools_test.js) Documentation: - Updated system status document (v5.9) - Updated SSA module status and development plan (v1.8) Total E2E: 107/107 passed (Phase I: 31, Phase II: 38, Phase III: 13, Phase IV: 25) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -349,6 +349,66 @@ export class DataProfileService {
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase I: 获取单变量详细分析(调用 Python variable-detail 端点)
|
||||
*
|
||||
* @param sessionId SSA 会话 ID
|
||||
* @param variableName 目标变量名
|
||||
*/
|
||||
async getVariableDetail(sessionId: string, variableName: string): Promise<any> {
|
||||
try {
|
||||
const csvContent = await this.loadCSVFromSession(sessionId);
|
||||
if (!csvContent) {
|
||||
return { success: false, error: 'No CSV data available for session' };
|
||||
}
|
||||
|
||||
const response = await this.client.post('/api/ssa/variable-detail', {
|
||||
csv_content: csvContent,
|
||||
variable_name: variableName,
|
||||
max_bins: 30,
|
||||
max_qq_points: 200,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
logger.error('[SSA:DataProfile] Variable detail failed', {
|
||||
sessionId, variableName, error: error.message,
|
||||
});
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Session 加载原始 CSV 字符串(供 variable-detail 复用)
|
||||
*/
|
||||
private async loadCSVFromSession(sessionId: string): Promise<string | null> {
|
||||
const session = await prisma.ssaSession.findUnique({ where: { id: sessionId } });
|
||||
if (!session) return null;
|
||||
|
||||
if (session.dataOssKey) {
|
||||
const buffer = await storage.download(session.dataOssKey);
|
||||
return buffer.toString('utf-8');
|
||||
}
|
||||
|
||||
if (session.dataPayload) {
|
||||
const rows = session.dataPayload as unknown as Record<string, any>[];
|
||||
if (rows.length === 0) return null;
|
||||
const cols = Object.keys(rows[0]);
|
||||
const lines = [cols.join(',')];
|
||||
for (const row of rows) {
|
||||
lines.push(cols.map(c => {
|
||||
const v = row[c];
|
||||
if (v === null || v === undefined) return '';
|
||||
const s = String(v);
|
||||
return s.includes(',') || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s;
|
||||
}).join(','));
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 单例导出
|
||||
|
||||
Reference in New Issue
Block a user