feat: Add Expert Transformation Agent with multi-expert perspective system
- Backend: Add expert transformation router with 3-step SSE pipeline - Step 0: Generate diverse expert team (random domains) - Step 1: Each expert generates keywords for attributes - Step 2: Batch generate descriptions for expert keywords - Backend: Add simplified prompts for reliable JSON output - Frontend: Add TransformationPanel with React Flow visualization - Frontend: Add TransformationInputPanel for expert configuration - Expert count (2-8), keywords per expert (1-3) - Custom expert domains support - Frontend: Add expert keyword nodes with expert badges - Frontend: Improve description card layout (wider cards, more spacing) - Frontend: Add fallback for missing descriptions with visual indicators 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
import { ConfigProvider, Layout, theme, Typography, Space } from 'antd';
|
||||
import { ApartmentOutlined } from '@ant-design/icons';
|
||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { ConfigProvider, Layout, theme, Typography, Space, Tabs } from 'antd';
|
||||
import { ApartmentOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
||||
import { ThemeToggle } from './components/ThemeToggle';
|
||||
import { InputPanel } from './components/InputPanel';
|
||||
import { TransformationInputPanel } from './components/TransformationInputPanel';
|
||||
import { MindmapPanel } from './components/MindmapPanel';
|
||||
import { TransformationPanel } from './components/TransformationPanel';
|
||||
import { useAttribute } from './hooks/useAttribute';
|
||||
import { getModels } from './services/api';
|
||||
import type { MindmapDAGRef } from './components/MindmapDAG';
|
||||
import type { TransformationDAGRef } from './components/TransformationDAG';
|
||||
import type { CategoryMode } from './types';
|
||||
|
||||
const { Header, Sider, Content } = Layout;
|
||||
@@ -18,12 +22,51 @@ interface VisualSettings {
|
||||
|
||||
function App() {
|
||||
const [isDark, setIsDark] = useState(true);
|
||||
const [activeTab, setActiveTab] = useState<string>('attribute');
|
||||
const { loading, progress, error, currentResult, history, analyze, loadFromHistory } = useAttribute();
|
||||
const [visualSettings, setVisualSettings] = useState<VisualSettings>({
|
||||
nodeSpacing: 32,
|
||||
fontSize: 14,
|
||||
});
|
||||
const mindmapRef = useRef<MindmapDAGRef>(null);
|
||||
const transformationRef = useRef<TransformationDAGRef>(null);
|
||||
|
||||
// Transformation Agent settings
|
||||
const [transformModel, setTransformModel] = useState<string>('');
|
||||
const [transformTemperature, setTransformTemperature] = useState<number>(0.7);
|
||||
const [expertConfig, setExpertConfig] = useState<{
|
||||
expert_count: number;
|
||||
keywords_per_expert: number;
|
||||
custom_experts?: string[];
|
||||
}>({
|
||||
expert_count: 3,
|
||||
keywords_per_expert: 1,
|
||||
custom_experts: undefined,
|
||||
});
|
||||
const [customExpertsInput, setCustomExpertsInput] = useState('');
|
||||
const [shouldStartTransform, setShouldStartTransform] = useState(false);
|
||||
const [transformLoading, setTransformLoading] = useState(false);
|
||||
|
||||
// Available models from API
|
||||
const [availableModels, setAvailableModels] = useState<string[]>([]);
|
||||
|
||||
// Fetch models on mount
|
||||
useEffect(() => {
|
||||
async function fetchModels() {
|
||||
try {
|
||||
const response = await getModels();
|
||||
setAvailableModels(response.models);
|
||||
// Set default model for transformation if not set
|
||||
if (response.models.length > 0 && !transformModel) {
|
||||
const defaultModel = response.models.find((m) => m.includes('qwen3')) || response.models[0];
|
||||
setTransformModel(defaultModel);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch models:', err);
|
||||
}
|
||||
}
|
||||
fetchModels();
|
||||
}, []);
|
||||
|
||||
const handleAnalyze = async (
|
||||
query: string,
|
||||
@@ -41,6 +84,10 @@ function App() {
|
||||
mindmapRef.current?.resetView();
|
||||
}, []);
|
||||
|
||||
const handleTransform = useCallback(() => {
|
||||
setShouldStartTransform(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
@@ -82,7 +129,7 @@ function App() {
|
||||
backgroundClip: 'text',
|
||||
}}
|
||||
>
|
||||
Attribute Agent
|
||||
Novelty Seeking
|
||||
</Title>
|
||||
</Space>
|
||||
<ThemeToggle isDark={isDark} onToggle={setIsDark} />
|
||||
@@ -95,13 +142,58 @@ function App() {
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<MindmapPanel
|
||||
ref={mindmapRef}
|
||||
data={currentResult}
|
||||
loading={loading}
|
||||
error={error}
|
||||
isDark={isDark}
|
||||
visualSettings={visualSettings}
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
style={{ height: '100%' }}
|
||||
tabBarStyle={{ marginBottom: 8 }}
|
||||
items={[
|
||||
{
|
||||
key: 'attribute',
|
||||
label: (
|
||||
<span>
|
||||
<ApartmentOutlined style={{ marginRight: 8 }} />
|
||||
Attribute Agent
|
||||
</span>
|
||||
),
|
||||
children: (
|
||||
<div style={{ height: 'calc(100vh - 140px)' }}>
|
||||
<MindmapPanel
|
||||
ref={mindmapRef}
|
||||
data={currentResult}
|
||||
loading={loading}
|
||||
error={error}
|
||||
isDark={isDark}
|
||||
visualSettings={visualSettings}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'transformation',
|
||||
label: (
|
||||
<span>
|
||||
<ThunderboltOutlined style={{ marginRight: 8 }} />
|
||||
Transformation Agent
|
||||
</span>
|
||||
),
|
||||
children: (
|
||||
<div style={{ height: 'calc(100vh - 140px)' }}>
|
||||
<TransformationPanel
|
||||
ref={transformationRef}
|
||||
attributeData={currentResult}
|
||||
isDark={isDark}
|
||||
model={transformModel}
|
||||
temperature={transformTemperature}
|
||||
expertConfig={expertConfig}
|
||||
shouldStartTransform={shouldStartTransform}
|
||||
onTransformComplete={() => setShouldStartTransform(false)}
|
||||
onLoadingChange={setTransformLoading}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Content>
|
||||
<Sider
|
||||
@@ -112,17 +204,35 @@ function App() {
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<InputPanel
|
||||
loading={loading}
|
||||
progress={progress}
|
||||
history={history}
|
||||
currentResult={currentResult}
|
||||
onAnalyze={handleAnalyze}
|
||||
onLoadHistory={loadFromHistory}
|
||||
onResetView={handleResetView}
|
||||
visualSettings={visualSettings}
|
||||
onVisualSettingsChange={setVisualSettings}
|
||||
/>
|
||||
{activeTab === 'attribute' ? (
|
||||
<InputPanel
|
||||
loading={loading}
|
||||
progress={progress}
|
||||
history={history}
|
||||
currentResult={currentResult}
|
||||
onAnalyze={handleAnalyze}
|
||||
onLoadHistory={loadFromHistory}
|
||||
onResetView={handleResetView}
|
||||
visualSettings={visualSettings}
|
||||
onVisualSettingsChange={setVisualSettings}
|
||||
/>
|
||||
) : (
|
||||
<TransformationInputPanel
|
||||
onTransform={handleTransform}
|
||||
loading={transformLoading}
|
||||
hasData={!!currentResult}
|
||||
isDark={isDark}
|
||||
model={transformModel}
|
||||
temperature={transformTemperature}
|
||||
expertConfig={expertConfig}
|
||||
customExpertsInput={customExpertsInput}
|
||||
onModelChange={setTransformModel}
|
||||
onTemperatureChange={setTransformTemperature}
|
||||
onExpertConfigChange={setExpertConfig}
|
||||
onCustomExpertsInputChange={setCustomExpertsInput}
|
||||
availableModels={availableModels}
|
||||
/>
|
||||
)}
|
||||
</Sider>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
Reference in New Issue
Block a user