feat: Add dynamic category system for attribute analysis

Backend:
- Add CategoryMode enum with 4 modes (fixed_only, fixed_plus_custom, custom_only, dynamic_auto)
- Add Step 0 for LLM category analysis before attribute generation
- Implement dynamic prompts for Step 1/2 that work with N categories
- Add execute_step0(), resolve_final_categories(), assemble_dynamic_attribute_tree()
- Update SSE events to include step0_start, step0_complete, categories_resolved

Frontend:
- Add CategorySelector component with mode selection, custom category input, and category count slider
- Update types with CategoryDefinition, Step0Result, DynamicStep1Result, DynamicCausalChain
- Update api.ts with new SSE event handlers
- Update useAttribute hook with category parameters
- Integrate CategorySelector into InputPanel
- Fix mindmap to dynamically extract and display N categories (was hardcoded to 4)
- Add CSS styles for depth 5-8 to support more category levels

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-02 23:04:35 +08:00
parent eb6c0c51fa
commit 91f7f41bc1
11 changed files with 727 additions and 53 deletions

View File

@@ -3,17 +3,24 @@ import type {
StreamAnalyzeRequest,
StreamAnalyzeResponse,
Step1Result,
CausalChain
CausalChain,
Step0Result,
CategoryDefinition,
DynamicStep1Result,
DynamicCausalChain
} from '../types';
// 自動使用當前瀏覽器的 hostname支援遠端存取
const API_BASE_URL = `http://${window.location.hostname}:8000/api`;
export interface SSECallbacks {
onStep0Start?: () => void;
onStep0Complete?: (result: Step0Result) => void;
onCategoriesResolved?: (categories: CategoryDefinition[]) => void;
onStep1Start?: () => void;
onStep1Complete?: (result: Step1Result) => void;
onStep1Complete?: (result: Step1Result | DynamicStep1Result) => void;
onChainStart?: (index: number, total: number) => void;
onChainComplete?: (index: number, chain: CausalChain) => void;
onChainComplete?: (index: number, chain: CausalChain | DynamicCausalChain) => void;
onChainError?: (index: number, error: string) => void;
onDone?: (response: StreamAnalyzeResponse) => void;
onError?: (error: string) => void;
@@ -65,6 +72,15 @@ export async function analyzeAttributesStream(
const eventData = JSON.parse(dataMatch[1]);
switch (eventType) {
case 'step0_start':
callbacks.onStep0Start?.();
break;
case 'step0_complete':
callbacks.onStep0Complete?.(eventData.result);
break;
case 'categories_resolved':
callbacks.onCategoriesResolved?.(eventData.categories);
break;
case 'step1_start':
callbacks.onStep1Start?.();
break;