This commit is contained in:
wuyifan18
2025-10-18 14:31:34 +08:00
parent e40cdd1dee
commit da2318a40c
27 changed files with 14793 additions and 3642 deletions

View File

@@ -15,8 +15,9 @@ FROM base AS runner
WORKDIR /app
EXPOSE 8080/tcp
COPY .npmrc ./
RUN npm install @modern-js/app-tools @modern-js/runtime --no-optional --no-shrinkwrap && mkdir src
COPY src ./src
COPY modern.config.ts package.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
ENV API_BASE=
CMD ["npm", "run", "serve"]

View File

@@ -65,7 +65,6 @@ export default observer(({ agent, style = {} }: IAgentCardProps) => (
userSelect: 'none',
}}
>
{agent.profile}
</Box>
</Box>
<Box
@@ -105,7 +104,7 @@ export default observer(({ agent, style = {} }: IAgentCardProps) => (
marginLeft: '2px',
}}
>
Current Duty:
:
</Box>
<Box>
{agent.actions.map((action, index) => (

View File

@@ -4,6 +4,7 @@ import Box from '@mui/material/Box';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import { IAgentAction, globalStorage } from '@/storage';
import { getActionTypeDisplayText } from '@/storage/plan/action'; // 导入映射函数
export interface IDutyItem {
action: IAgentAction;
@@ -38,7 +39,7 @@ export default React.memo<IDutyItem>(({ action, style = {} }) => {
..._style,
}}
>
{action.type}
{getActionTypeDisplayText(action.type)}
<Box
onClick={() => setExpand(true)}
sx={{
@@ -83,7 +84,7 @@ export default React.memo<IDutyItem>(({ action, style = {} }) => {
marginLeft: '2px',
}}
>
{action.type}
{getActionTypeDisplayText(action.type)}
</Box>
<Divider
sx={{

View File

@@ -88,7 +88,7 @@ export default observer(({ style = {} }: IAgentBoardProps) => {
'& > input': { display: 'none' },
}}
>
<Title title="Agent Board" />
<Title title="智能体库" />
<Box
sx={{
flexGrow: 1,

View File

@@ -1,3 +1,4 @@
import AbigailChenMilitary from '@/static/AgentIcons/Abigail_Chen_military.png';
import AbigailChen from '@/static/AgentIcons/Abigail_Chen.png';
import AdamSmith from '@/static/AgentIcons/Adam_Smith.png';
import ArthurBurton from '@/static/AgentIcons/Arthur_Burton.png';
@@ -26,6 +27,7 @@ import YurikoYamamoto from '@/static/AgentIcons/Yuriko_Yamamoto.png';
import Unknown from '@/static/AgentIcons/Unknow.png';
export enum IconName {
AbigailChenMilitary = 'Abigail_Chen_military',
AbigailChen = 'Abigail_Chen',
AdamSmith = 'Adam_Smith',
ArthurBurton = 'Arthur_Burton',
@@ -82,6 +84,7 @@ export const IconMap = new Proxy<{ [key: string]: IconName }>(
export const IconUrl: { [key in IconName]: string } = {
[IconName.Unknown]: Unknown,
[IconName.AbigailChenMilitary]: AbigailChenMilitary,
[IconName.AbigailChen]: AbigailChen,
[IconName.AdamSmith]: AdamSmith,
[IconName.ArthurBurton]: ArthurBurton,

View File

@@ -151,7 +151,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
>
<LogoIcon />
<Box sx={{ marginLeft: '6px', fontWeight: 800, fontSize: '20px' }}>
AGENTCOORD
</Box>
</Box>
) : (

View File

@@ -8,6 +8,7 @@ import RemoveIcon from '@mui/icons-material/Remove';
import AdjustIcon from '@mui/icons-material/Adjust';
import { ObjectProps, ProcessProps } from './interface';
import AgentIcon from '@/components/AgentIcon';
import DescriptionCard from '@/components/ProcessDiscription/DescriptionCard';
import { globalStorage } from '@/storage';
export interface IEditObjectProps {
@@ -329,6 +330,13 @@ export const ProcessCard: React.FC<IProcessCardProps> = React.memo(
// handleEditStep(step.name, { ...step, task: text });
}}
/>
{/* 这里直接渲染对应的 DescriptionCard */}
{/* {(() => {
const step = globalStorage.planManager.currentPlan.find(
s => s.id === process.id
);
return step ? <DescriptionCard step={step} /> : null;
})()} */}
</Box>
)}
</Box>

View File

@@ -118,10 +118,10 @@ const D3Graph: React.FC<D3GraphProps> = ({
stepName: stepCardName,
objectName: objectCardName,
type,
x1: objectRect.left + objectRect.width,
y1: objectRect.top + 0.5 * objectRect.height,
x2: stepRect.left,
y2: stepRect.top + 0.5 * stepRect.height,
x1: stepRect.left + stepRect.width,
y1: stepRect.top + 0.5 * stepRect.height,
x2: objectRect.left,
y2: objectRect.top + 0.5 * objectRect.height,
};
});
const objectMetrics = calculateLineMetrics(cardRect, 'object');
@@ -152,17 +152,17 @@ const D3Graph: React.FC<D3GraphProps> = ({
userSelect: 'none',
}}
>
<marker
id="arrowhead"
markerWidth="4"
markerHeight="4"
refX="2"
refY="2"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L4,2 L0,4 z" fill="#E5E5E5" />
</marker>
<marker
id="arrowhead"
markerWidth="2"
markerHeight="2"
refX="1"
refY="1"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L2,1 L0,2 z" fill="#E5E5E5" />
</marker>
<marker
id="starter"
markerWidth="4"
@@ -184,7 +184,7 @@ const D3Graph: React.FC<D3GraphProps> = ({
fill="#898989"
fontWeight="800"
>
Key Object
</text>
<line
x1={objectLine.x}
@@ -204,7 +204,7 @@ const D3Graph: React.FC<D3GraphProps> = ({
fill="#898989"
fontWeight="800"
>
Process
</text>
<line
x1={processLine.x}

View File

@@ -53,7 +53,7 @@ export default observer(() => {
const handleEditContent = (stepTaskId: string, newContent: string) => {
globalStorage.setStepTaskContent(stepTaskId, newContent);
};
const WidthRatio = ['30%', '15%', '52.5%'];
const WidthRatio = ['35%', '10%', '50%'];
const [cardRefMapReady, setCardRefMapReady] = React.useState(false);
@@ -175,6 +175,34 @@ export default observer(() => {
sx={{ display: 'flex' }}
ref={ref}
>
<Box
sx={{
// display: 'flex',
alignItems: 'center',
// width: WidthRatio[2],
justifyContent: 'center',
flex: `0 0 ${WidthRatio[2]}`,
}}
>
{name && (
<ProcessCard
process={{
id,
name,
icons: agentIcons,
agents,
content,
cardRef: getCardRef(`process.${name}`),
}}
handleProcessClick={handleProcessClick}
isFocusing={focusingStepTaskId === id}
isAddActive={id === activeProcessIdAdd}
handleAddActive={handleProcessAdd}
handleEditContent={handleEditContent}
/>
)}
</Box>
<Box sx={{ flex: `0 0 ${WidthRatio[1]}` }} />
<Box
sx={{
display: 'flex',
@@ -209,34 +237,6 @@ export default observer(() => {
/>
)}
</Box>
<Box sx={{ flex: `0 0 ${WidthRatio[1]}` }} />
<Box
sx={{
// display: 'flex',
alignItems: 'center',
// width: WidthRatio[2],
justifyContent: 'center',
flex: `0 0 ${WidthRatio[2]}`,
}}
>
{name && (
<ProcessCard
process={{
id,
name,
icons: agentIcons,
agents,
content,
cardRef: getCardRef(`process.${name}`),
}}
handleProcessClick={handleProcessClick}
isFocusing={focusingStepTaskId === id}
isAddActive={id === activeProcessIdAdd}
handleAddActive={handleProcessAdd}
handleEditContent={handleEditContent}
/>
)}
</Box>
</Box>
),
)}

View File

@@ -25,7 +25,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
...style,
}}
>
<Title title="Plan Outline" />
<Title title="任务大纲" />
<Box
sx={{
position: 'relative',

View File

@@ -19,7 +19,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
...style,
}}
>
<Title title="Task Process" />
<Title title="任务流程" />
<Stack
spacing={1}
sx={{

View File

@@ -1,6 +1,6 @@
import React from 'react';
import React, { useState } from 'react';
import { observer } from 'mobx-react-lite';
import { Divider, SxProps } from '@mui/material';
import { Divider, SxProps, Tooltip, Typography} from '@mui/material';
import Box from '@mui/material/Box';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
@@ -9,6 +9,7 @@ import { globalStorage } from '@/storage';
import type { IExecuteStepHistoryItem } from '@/apis/execute-plan';
import AgentIcon from '@/components/AgentIcon';
import { getAgentActionStyle } from '@/storage/plan';
import { getActionTypeDisplayText } from '@/storage/plan/action';
export interface IStepHistoryItemProps {
item: IExecuteStepHistoryItem;
@@ -46,6 +47,170 @@ export default observer(
}, 10);
}
}, [expand]);
interface DataSpaceCard {
name: string;
data_space: string;
doId: string;
fromRepo: string;
}
interface DataSpaceIndicatorProps {
cards?: DataSpaceCard[];
}
const DataSpaceIndicator = ({ cards = [] }: DataSpaceIndicatorProps) => {
const [currentCard, setCurrentCard] = useState(0);
if (!cards || cards.length === 0) {
return null;
}
const tooltipContent = (
<>
{/* 添加标题 */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px 12px',
backgroundColor: '#1565c0',
color: 'white',
borderRadius: '4px 4px 0 0',
fontWeight: 'bold',
fontSize: '14px',
}}
>
<span></span>
<Box sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<span style={{ fontSize: '12px', color: 'rgba(255,255,255,0.8)' }}>
{currentCard + 1}/{cards.length}
</span>
<button
onClick={(e) => {
e.stopPropagation();
setCurrentCard((prev) => (prev + 1) % cards.length);
}}
style={{
fontSize: '12px',
padding: '2px 8px',
backgroundColor: 'white',
color: '#1565c0',
border: 'none',
borderRadius: '3px',
cursor: 'pointer',
fontWeight: 'bold'
}}
>
</button>
</Box>
</Box>
<Box
sx={{
padding: '12px',
borderRadius: '4px',
backgroundColor: '#e3f2fd',
border: '1px solid #1565c0',
minWidth: '450px', // 设置卡片最小宽度
}}
>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.name}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.data_space}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
DOID:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.doId}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.fromRepo}
</Typography>
</Box>
</Box>
</>
);
return (
<Tooltip
title={tooltipContent}
arrow
slotProps={{
tooltip: {
sx: {
maxWidth: 500, // 只需要这一行来增加宽度
},
},
}}
>
<Box
sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: '20px',
height: '20px',
borderRadius: '50%',
backgroundColor: '#9e9e9e',
color: 'white',
fontSize: '12px',
fontWeight: '600',
cursor: 'pointer',
'&:hover': {
backgroundColor: '#1565c0',
}
}}
>
{cards.length}
</Box>
</Tooltip>
);
};
const cardData = [
{
name: "3D Semantic-Geometric Corrosion Mapping Implementations",
data_space: "江苏省产研院",
doId: "bdware.scenario/d8f3ff8c-3fb3-4573-88a6-5dd823627c37",
fromRepo: "https://arxiv.org/abs/2404.13691"
},
{
name: "RustSEG -- Automated segmentation of corrosion using deep learning",
data_space: "江苏省产研院",
doId: "bdware.scenario/67445299-110a-4a4e-9fda-42e4b5a493c2",
fromRepo: "https://arxiv.org/abs/2205.05426"
},
{
name: "Pixel-level Corrosion Detection on Metal Constructions by Fusion of Deep Learning Semantic and Contour Segmentation",
data_space: "江苏省产研院",
doId: "bdware.scenario/115d5135-85d3-4123-8b81-9eb9f07b6153",
fromRepo: "https://arxiv.org/abs/2008.05204"
},
];
const s = { ...getAgentActionStyle(item.type), ...style } as SxProps;
React.useEffect(() => {
console.log(item);
@@ -77,7 +242,7 @@ export default observer(
{item.agent}
</Box>
<Box component="span" sx={{ fontWeight: 400 }}>
: {item.type}
: {getActionTypeDisplayText(item.type)}
</Box>
</Box>
{item.result ? (
@@ -103,10 +268,14 @@ export default observer(
marginBottom: '4px',
color: '#0009',
fontWeight: 400,
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
}}
>
{item.description}
</Box>
<DataSpaceIndicator cards={cardData} />
<MarkdownBlock
text={item.result}
style={{

View File

@@ -79,7 +79,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
alignItems: 'center',
}}
>
<Title title="Execution Result" />
<Title title="执行结果" />
<Box
sx={{
flexGrow: 1,

View File

@@ -4,6 +4,8 @@ import { SxProps } from '@mui/material';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import FormControl from '@mui/material/FormControl';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';
import FilledInput from '@mui/material/FilledInput';
import TelegramIcon from '@mui/icons-material/Telegram';
import CircularProgress from '@mui/material/CircularProgress';
@@ -16,6 +18,18 @@ export interface UserGoalInputProps {
export default observer(({ style = {} }: UserGoalInputProps) => {
const inputRef = React.useRef<string>('');
const inputElementRef = React.useRef<HTMLInputElement>(null);
const goalOptions = [
'如何快速筛选慢性肾脏病药物潜在受试者?',
'如何补充“丹芍活血胶囊”不良反应数据?',
'如何快速研发用于战场失血性休克的药物?',
'二维材料的光电性质受哪些关键因素影响?',
'如何通过AI模拟的方法分析材料的微观结构?',
'如何分析获取液态金属热力学参数?',
'如何解决固态电池的成本和寿命难题?',
'如何解决船舶制造中的材料腐蚀难题?',
'如何解决船舶制造中流体模拟和建模优化难题?',
];
React.useEffect(() => {
if (inputElementRef.current) {
if (globalStorage.planManager) {
@@ -32,42 +46,70 @@ export default observer(({ style = {} }: UserGoalInputProps) => {
...style,
}}
>
<FilledInput
<Autocomplete
freeSolo
disableClearable
disabled={
globalStorage.api.busy ||
!globalStorage.api.agentsReady ||
globalStorage.api.planReady
}
placeholder="Yout Goal"
fullWidth
inputRef={inputElementRef}
onChange={event => (inputRef.current = event.target.value)}
size="small"
sx={{
borderRadius: '10px',
background: '#E1E1E1',
borderBottom: 'none !important',
'&::before': {
borderBottom: 'none !important',
},
'& > input': {
padding: '10px',
},
options={goalOptions}
value={inputRef.current || (globalStorage.planManager ? globalStorage.planManager.goal : globalStorage.briefGoal)}
onChange={(event, newValue) => {
if (newValue) {
inputRef.current = newValue;
}
}}
startAdornment={
<Box
onInputChange={(event, newInputValue) => {
inputRef.current = newInputValue;
}}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
placeholder="请输入你的任务"
fullWidth
size="small"
inputRef={inputElementRef}
sx={{
color: '#4A9C9E',
fontWeight: 800,
fontSize: '18px',
textWrap: 'nowrap',
userSelect: 'none',
borderRadius: '10px',
background: '#E1E1E1',
borderBottom: 'none !important',
'& .MuiFilledInput-root': {
height: '45px',
borderRadius: '10px',
background: '#E1E1E1',
paddingTop: '5px',
borderBottom: 'none !important',
'&::before': {
borderBottom: 'none !important',
},
'&::after': {
borderBottom: 'none !important',
},
},
}}
>
\General Goal:
</Box>
}
/>
InputProps={{
...params.InputProps,
startAdornment: (
<Box
sx={{
color: '#4A9C9E',
fontWeight: 800,
fontSize: '18px',
textWrap: 'nowrap',
userSelect: 'none',
}}
>
</Box>
),
}}
/>
)}
/>
{globalStorage.api.planGenerating ? (
<CircularProgress
sx={{

View File

@@ -88,24 +88,24 @@ export default observer(() => {
setConnectors(<></>);
}
const outlinePosition =
ElementToLink[0]?.current?.getBoundingClientRect?.();
ElementToLink[1]?.current?.getBoundingClientRect?.();
if (!outlinePosition) {
return;
}
const agentRects = ElementToLink[1]
const agentRects = ElementToLink[0]
.map(ele => ele.current?.getBoundingClientRect?.() as DOMRect)
.filter(rect => rect);
if (agentRects.length === 0) {
return;
}
const agentsPosition = mergeRects(...agentRects);
const descriptionPosition =
ElementToLink[2]?.current?.getBoundingClientRect?.();
if (!descriptionPosition) {
return;
}
// const descriptionPosition =
// ElementToLink[2]?.current?.getBoundingClientRect?.();
// if (!descriptionPosition) {
// return;
// }
const LogRects = ElementToLink[3]
const LogRects = ElementToLink[2]
.map(ele => ele?.current?.getBoundingClientRect?.() as DOMRect)
.filter(rect => rect);
if (LogRects.length > 0) {
@@ -115,15 +115,14 @@ export default observer(() => {
setConnectors(
drawConnectors(
outlinePosition,
agentsPosition,
descriptionPosition,
outlinePosition,
logPosition,
),
);
} else {
setConnectors(
drawConnectors(outlinePosition, agentsPosition, descriptionPosition),
drawConnectors(agentsPosition,outlinePosition),
);
}
} catch (e) {

View File

@@ -99,16 +99,7 @@ export default React.memo(() => {
}}
/>
<Box sx={{ height: 0, flexGrow: 1, display: 'flex' }}>
<ResizeableColumn columnWidth="25%">
<Outline
style={{
height: '100%',
width: '100%',
marginRight: '6px',
}}
/>
</ResizeableColumn>
<ResizeableColumn columnWidth="25%">
<ResizeableColumn columnWidth="19%">
<AgentBoard
style={{
height: '100%',
@@ -118,8 +109,8 @@ export default React.memo(() => {
onAddAgent={() => setShowAgentHiring(true)}
/>
</ResizeableColumn>
<ResizeableColumn columnWidth="25%">
<ProcessDiscrption
<ResizeableColumn columnWidth="26%">
<Outline
style={{
height: '100%',
width: '100%',
@@ -127,6 +118,15 @@ export default React.memo(() => {
}}
/>
</ResizeableColumn>
{/* <ResizeableColumn columnWidth="25%">
<ProcessDiscrption
style={{
height: '100%',
width: '100%',
marginRight: '6px',
}}
/>
</ResizeableColumn> */}
<ProcessRehearsal
style={{
height: '100%',

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -285,17 +285,15 @@ export class GlobalStorage {
}
public get ElementToLink(): [
React.RefObject<HTMLElement> | undefined,
React.RefObject<HTMLElement>[],
React.RefObject<HTMLElement> | undefined,
(React.RefObject<HTMLElement> | undefined)[],
] {
return [
this.refMap.OutlineCard.get(this.focusingStepTaskId ?? ''),
this.focusingStepTask?.agentSelection?.agents?.map(
agent => this.refMap.AgentCard.get(agent) ?? [],
) ?? ([] as any),
this.refMap.DiscriptionCard.get(this.focusingStepTaskId ?? ''),
this.refMap.OutlineCard.get(this.focusingStepTaskId ?? ''),
this.logManager.outdate
? []
: [

View File

@@ -11,6 +11,17 @@ export enum ActionType {
Finalize = 'Finalize',
}
export const getActionTypeDisplayText = (actionType: ActionType | string): string => {
const displayMap: Record<string, string> = {
[ActionType.Propose]: '提议',
[ActionType.Critique]: '评审',
[ActionType.Improve]: '改进',
[ActionType.Finalize]: '总结',
};
return displayMap[actionType] || actionType;
};
const AgentActionStyles = new Map<ActionType | '', SxProps>([
[ActionType.Propose, { backgroundColor: '#B9EBF9', borderColor: '#94c2dc' }],
[ActionType.Critique, { backgroundColor: '#EFF9B9', borderColor: '#c0dc94' }],

View File

@@ -20,7 +20,7 @@ const nameJoin = (names: string[]) => {
const last = tmp.pop()!;
let t = tmp.join(', ');
if (t.length > 0) {
t = `${t} and ${last}`;
t = `${t} ${last}`;
} else {
t = last;
}
@@ -102,14 +102,14 @@ export class StepTaskNode implements INodeBase {
text: this.output,
style: { background: '#FFCA8C' },
};
outputSentence = `to obtain !<${indexOffset}>!`;
outputSentence = `得到 !<${indexOffset}>!`;
}
// Join them togeter
let content = inputSentence;
if (content) {
content = `Based on ${content}, ${nameSentence} perform the task of !<${actionIndex}>!`;
content = `基于${content}, ${nameSentence} 执行任务 !<${actionIndex}>!`;
} else {
content = `${nameSentence} perform the task of !<${actionIndex}>!`;
content = `${nameSentence} 执行任务 !<${actionIndex}>!`;
}
if (outputSentence) {
content = `${content}, ${outputSentence}.`;