fix(ssa): Fix 7 integration bugs and refactor frontend unified state management
Bug fixes: - Fix garbled error messages in chat (TypeWriter rendering issue) - Fix R engine NA crash in descriptive.R (defensive isTRUE/is.na checks) - Fix intent misclassification for statistical significance queries - Fix step 2 results not displayed (accept warning status alongside success) - Fix incomplete R code download (only step 1 included) - Fix multi-task state confusion (clicking old card shows new results) - Add R engine and backend parameter logging for debugging Refactor - Unified Record Architecture: - Replace 12 global singleton fields with AnalysisRecord as single source of truth - Remove isWorkflowMode branching across all components - One Analysis = One Record = N Steps paradigm - selectRecord only sets currentRecordId, all rendering derives from currentRecord - Fix cross-hook-instance issue: executeWorkflow fallback to store currentRecordId Updated files: ssaStore, useWorkflow, useAnalysis, SSAChatPane, SSAWorkspacePane, SSACodeModal, WorkflowTimeline, QueryService, WorkflowExecutorService, descriptive.R Tested: Manual integration test passed - multi-task switching, R code completeness Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -411,7 +411,9 @@ export class QueryService {
|
||||
|
||||
if (query.includes('比较') || query.includes('差异') || query.includes('不同') || query.includes('有没有效')) {
|
||||
goal = 'comparison';
|
||||
} else if (query.includes('相关') || query.includes('关系') || query.includes('关联')) {
|
||||
} else if (query.includes('相关') || query.includes('关系') || query.includes('关联')
|
||||
|| query.includes('统计学意义') || query.includes('显著') || query.includes('检验')
|
||||
|| query.includes('p值') || query.includes('有无差别')) {
|
||||
goal = 'correlation';
|
||||
} else if (query.includes('影响') || query.includes('因素') || query.includes('预测') || query.includes('回归')) {
|
||||
goal = 'regression';
|
||||
|
||||
@@ -395,6 +395,11 @@ export class WorkflowExecutorService extends EventEmitter {
|
||||
});
|
||||
|
||||
// 调用 R 服务
|
||||
logger.info('[SSA:Executor] Calling R service', {
|
||||
step: step.stepOrder,
|
||||
toolCode: step.toolCode,
|
||||
inputParams: step.inputParams,
|
||||
});
|
||||
const response = await this.rClient.post(`/api/v1/skills/${step.toolCode}`, {
|
||||
data_source: dataSource,
|
||||
params: step.inputParams,
|
||||
@@ -410,6 +415,16 @@ export class WorkflowExecutorService extends EventEmitter {
|
||||
if (response.data.status === 'error' || response.data.status === 'blocked') {
|
||||
const rMsg = response.data.message || '执行失败';
|
||||
const classified = classifyRError(rMsg);
|
||||
|
||||
logger.warn('[SSA:Executor] R tool returned error', {
|
||||
step: step.stepOrder,
|
||||
toolCode: step.toolCode,
|
||||
rMessage: rMsg,
|
||||
rErrorCode: response.data.error_code,
|
||||
rUserHint: response.data.user_hint,
|
||||
classifiedCode: classified.code,
|
||||
});
|
||||
|
||||
return {
|
||||
stepOrder: step.stepOrder,
|
||||
toolCode: step.toolCode,
|
||||
|
||||
@@ -74,13 +74,47 @@ export interface PrunedProfile {
|
||||
// 2. LLM 原始输出的 Zod Schema(静态版本)
|
||||
// ────────────────────────────────────────────
|
||||
|
||||
const VALID_VAR_TYPES = ['continuous', 'binary', 'categorical', 'ordinal', 'datetime'] as const;
|
||||
|
||||
const VAR_TYPE_ALIAS: Record<string, VariableType> = {
|
||||
numeric: 'continuous',
|
||||
integer: 'continuous',
|
||||
int: 'continuous',
|
||||
float: 'continuous',
|
||||
double: 'continuous',
|
||||
number: 'continuous',
|
||||
real: 'continuous',
|
||||
factor: 'categorical',
|
||||
string: 'categorical',
|
||||
text: 'categorical',
|
||||
character: 'categorical',
|
||||
char: 'categorical',
|
||||
nominal: 'categorical',
|
||||
boolean: 'binary',
|
||||
bool: 'binary',
|
||||
logical: 'binary',
|
||||
dichotomous: 'binary',
|
||||
date: 'datetime',
|
||||
time: 'datetime',
|
||||
timestamp: 'datetime',
|
||||
};
|
||||
|
||||
function normalizeVarType(val: unknown): VariableType {
|
||||
if (typeof val !== 'string') return 'continuous';
|
||||
const lower = val.toLowerCase().trim();
|
||||
if ((VALID_VAR_TYPES as readonly string[]).includes(lower)) return lower as VariableType;
|
||||
return VAR_TYPE_ALIAS[lower] ?? 'continuous';
|
||||
}
|
||||
|
||||
const varTypeSchema = z.preprocess(normalizeVarType, z.enum(VALID_VAR_TYPES));
|
||||
|
||||
/** LLM 直接输出的 JSON 结构(Zod 校验用) */
|
||||
export const LLMIntentOutputSchema = z.object({
|
||||
goal: z.enum(['comparison', 'correlation', 'regression', 'descriptive', 'cohort_study']),
|
||||
outcome_var: z.string().nullable().default(null),
|
||||
outcome_type: z.enum(['continuous', 'binary', 'categorical', 'ordinal', 'datetime']).nullable().default(null),
|
||||
outcome_type: varTypeSchema.nullable().default(null),
|
||||
predictor_vars: z.array(z.string()).default([]),
|
||||
predictor_types: z.array(z.enum(['continuous', 'binary', 'categorical', 'ordinal', 'datetime'])).default([]),
|
||||
predictor_types: z.array(varTypeSchema).default([]),
|
||||
grouping_var: z.string().nullable().default(null),
|
||||
design: z.enum(['independent', 'paired', 'longitudinal', 'cross_sectional']).default('independent'),
|
||||
confidence: z.number().min(0).max(1).default(0.5),
|
||||
|
||||
Reference in New Issue
Block a user