Upgrade Alias-Agent to 0.2.0 (#51)

Upgrade Alias-Agent to 0.2.0

---------

Co-authored-by: ZiTao-Li <zitao.l@alibaba-inc.com>
Co-authored-by: xieyxclack <yuexiang.xyx@alibaba-inc.com>
Co-authored-by: Zexi Li <tomleeze@qq.com>
Co-authored-by: SSSuperDan <dlaura2218@gmail.com>
Co-authored-by: lalaliat <78087788+lalaliat@users.noreply.github.com>
Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
Co-authored-by: Dengjiaji <dengjiaji.djj@alibaba-inc.com>
Co-authored-by: 于南 <zengtianjing.ztj@alibaba-inc.com>
Co-authored-by: JustinDing <166603159+sleepy-bird-world@users.noreply.github.com>
Co-authored-by: y1y5 <269557841@qq.com>
Co-authored-by: 柳佚 <yly287738@alibaba-inc.com>
Co-authored-by: LiangguiWeng <347185100@qq.com>
Co-authored-by: 潜星 <zhijian.mzj@alibaba-inc.com>
Co-authored-by: StCarmen <1106135234@qq.com>
Co-authored-by: LuYi <yilu_2000@outlook.com>
Co-authored-by: 刺葳 <ciwei.cy@alibaba-inc.com>
This commit is contained in:
Yue Cui
2025-12-03 20:58:25 +08:00
committed by GitHub
parent 8af2dc6477
commit cb87558efe
430 changed files with 49058 additions and 3471 deletions

View File

@@ -0,0 +1,91 @@
.workspaceHeader {
@apply flex items-center justify-between;
.titleContainer {
@apply flex items-center;
.title {
@apply font-alibaba text-[17px] font-bold leading-[28px] text-text-primary;
}
}
.computerIcon {
font-size: 20px;
font-weight: bold;
margin-right: 8px;
}
.collectButton {
@apply flex items-center px-4 py-1 rounded-[12px] border border-black;
background-color: var(--message-content);
img {
@apply w-5 h-5 mr-1;
}
span {
@apply font-alibaba text-[13px] font-bold leading-[22px] text-text-primary;
}
}
}
.todoHeader {
@apply flex items-center mt-1;
.stepIcon {
@apply w-5 h-5 mr-[6px];
}
.stepCount {
@apply px-2 py-[2px] mr-[5px] rounded-[8px];
@apply font-alibaba text-[13px] font-bold leading-[22px];
background-color: var(--sps-color-mauve-bg);
color: var(--sps-color-mauve);
}
.stepTitle {
@apply font-alibaba text-[15px] leading-6 tracking-[0.2px] text-black;
width: 100%;
}
.workspaceSelect {
height: 56px;
width: 100%;
}
}
.todoList {
@apply mx-0 my-[14px] mb-[22px];
:global(.ant-collapse-content-box) {
padding: 0;
}
:global {
pre {
background-color: transparent !important;
}
code {
color: var(--sps-color-text-secondary) !important;
background-color: transparent !important;
}
}
// .collapse {
// :global {
// .sps-collapse-content-box {
// padding: 0 !important;
// }
// }
// }
}
.workWrap {
display: flex;
flex-direction: column;
padding: 16px 20px;
border-top: solid 1px var(--sps-color-border-secondary);
.workspaceHeader {
height: 52px;
}
}
.renderLabel {
display: flex;
justify-content: space-between;
.labelId {
color: var(--sps-color-text-tertiary);
margin-left: 12px;
}
}

View File

@@ -0,0 +1,380 @@
import { useTheme } from "@/context/ThemeContext";
import { useWorkspace } from "@/context/WorkspaceContext.tsx";
import { Message, MessageType, ToolCallMessage } from "@/types/message";
import { Collapse, CollapseProps, Select } from "@agentscope-ai/design";
import { SparkComputerLine } from "@agentscope-ai/icons";
import type { SelectProps } from "antd";
import { memo, useEffect, useMemo, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark, prism } from "react-syntax-highlighter/dist/esm/styles/prism";
import { UniversalViewer } from "../Viewer";
import styles from "./index.module.scss";
const Workspace = () => {
const {
displayedContent,
args,
messageList = [],
setDisplayedContent,
} = useWorkspace();
const { theme } = useTheme();
const [serialNum, setSerialNum] = useState<number>(0);
const [useToolInfo, setUseToolInfo] = useState<{
name: string;
arguments: string;
type: string;
}>({ name: "", arguments: "", type: "" });
type LabelRender = SelectProps["labelRender"];
const updateToolInfo = (toolMsg?: ToolCallMessage) => {
if (
toolMsg &&
toolMsg.arguments &&
Object.keys(toolMsg.arguments).length > 0
) {
setUseToolInfo({
name: toolMsg.tool_name || "",
arguments: JSON.stringify(toolMsg.arguments, null, 2),
type: toolMsg.type,
});
} else {
setUseToolInfo({
name: "",
arguments: "",
type: "",
});
}
};
useEffect(() => {
if (displayedContent) {
const selectedMessage = messageList.find(
(msg: Message) => msg.content === displayedContent,
);
if (selectedMessage) {
findIndex(selectedMessage.id);
if ((selectedMessage as ToolCallMessage).arguments) {
const toolMsg = selectedMessage as ToolCallMessage;
updateToolInfo(toolMsg);
} else {
updateToolInfo(); // Set to null
}
}
} else {
updateToolInfo(); // Set to null
}
}, [displayedContent, messageList]);
const customStyle = {
borderTopRightRadius: 0,
borderTopLeftRadius: 0,
// maxHeight: 500,
overflow: "auto",
padding: 0,
backgroundColor: "var(--sps-color-bg-base)",
};
const renderMarkdown = (content: string, language: string = "text") => {
return (
<SyntaxHighlighter
language={language}
showLineNumbers={true}
wrapLines={true}
style={theme === "dark" ? oneDark : prism}
customStyle={theme === "dark" ? customStyle : { ...customStyle }}
>
{content}
</SyntaxHighlighter>
);
};
const findIndex = (v: string) => {
const index = messageList.findIndex((item) => item.id === v);
setSerialNum(index >= 0 ? index + 1 : 0);
};
const getBaseLabel = () => {
if (!!displayedContent && typeof displayedContent === "string") {
const displayedContentObj = JSON.parse(displayedContent);
if (
Array.isArray(displayedContentObj) &&
displayedContentObj.length > 0
) {
if (displayedContentObj[0].type === MessageType.TOOL_USE)
return "Raw data";
if (displayedContentObj[0].type === MessageType.TOOL_RESULT)
return "Tool result";
}
if (displayedContentObj.hasOwnProperty("type")) {
if (displayedContentObj.type === MessageType.TOOL_USE)
return "Raw data";
if (displayedContentObj.type === MessageType.TOOL_RESULT)
return "Tool result";
}
}
return "Raw result";
};
// Create an array that refreshes each time displayedContent changes
const items: CollapseProps["items"] = useMemo(() => {
const base: CollapseProps["items"] = [];
if (displayedContent === null) {
return [];
}
const currentMessage = messageList.find(
(msg: Message) => msg.content === displayedContent,
);
if (currentMessage) {
findIndex(currentMessage.id);
}
base.push({
key: "1",
label: getBaseLabel(),
children: (
<SyntaxHighlighter
language="JSON"
showLineNumbers={true}
wrapLines={true}
style={theme === "dark" ? oneDark : prism}
// Background color change
customStyle={
theme === "dark"
? customStyle
: { ...customStyle, background: "transparent" }
}
>
{(() => {
try {
return JSON.stringify(JSON.parse(displayedContent), null, 2);
} catch (error) {
return displayedContent;
}
})()}
</SyntaxHighlighter>
),
});
try {
const toolResultBlocks = JSON.parse(displayedContent);
if (Array.isArray(toolResultBlocks)) {
const toolResultBlock = toolResultBlocks[0];
if (
toolResultBlock.hasOwnProperty("name") &&
toolResultBlock.hasOwnProperty("output")
) {
const { name, output } = toolResultBlock;
// show output 0 for temp
switch (name) {
case "edit_file":
base.unshift({
key: "output-0",
label: `Output of 🛠️ ${name}`,
children: (
<UniversalViewer
content={output[0].text}
fileName="fake.diff"
style={customStyle}
/>
),
});
return base;
case "read_file":
base.unshift({
key: "output-0",
label: `Output of 🛠️ ${name}`,
children: (
<UniversalViewer
content={output[0].text}
fileName={args?.path}
style={customStyle}
/>
),
});
return base;
case "write_file":
base.unshift({
key: "output-0",
label: `Output of 🛠️ ${name}`,
children: (
<UniversalViewer
content={args?.content || output[0].text}
fileName={args?.path}
style={customStyle}
/>
),
});
return base;
case "generate_chart":
base.unshift({
key: "output-0",
label: `Output of 🛠️ ${name}`,
children: (
<UniversalViewer
content={output[0].text}
fileName="fake.chart"
style={customStyle}
/>
),
});
return base;
}
if (Array.isArray(output)) {
output.forEach((item, index) => {
if (item.hasOwnProperty("type")) {
switch (item.type) {
case "text":
if (base[0].key === "image") {
// Insert at second position
base.splice(1, 0, {
key: `output-${index}`,
label: `Output of 🛠️ ${name}`,
children: renderMarkdown(item.text),
});
} else {
base.unshift({
key: `output-${index}`,
label: `Output of 🛠️ ${name}`,
children: renderMarkdown(item.text),
});
}
break;
case "image":
base.unshift({
key: `image-${index}`,
label: "Image",
children: (
<img
src={"data:image/jpeg;base64," + item.data}
alt="Image"
style={{
maxWidth: "100%",
maxHeight: 500,
overflow: "auto",
}}
/>
),
});
break;
}
}
});
}
}
}
if (
useToolInfo.arguments &&
useToolInfo.name &&
useToolInfo.type === MessageType.TOOL_USE
) {
base.unshift({
key: "input",
label: `Arguments of 🛠️ ${useToolInfo.name}`,
children: renderMarkdown(useToolInfo.arguments),
});
}
} catch (error) {}
return base;
}, [displayedContent, args, theme, useToolInfo]);
const selectOnchange = (v: string) => {
// v is now id, need to find corresponding message based on id
const selectedMessage = messageList.find((msg: Message) => msg.id === v);
if (selectedMessage && selectedMessage.content !== displayedContent) {
findIndex(v);
setDisplayedContent(selectedMessage.content);
// Find corresponding message and set arguments
if ((selectedMessage as ToolCallMessage).arguments) {
const toolMsg = selectedMessage as ToolCallMessage;
updateToolInfo(toolMsg);
} else {
updateToolInfo(); // Set to null
}
setTimeout(() => {
const element = document.getElementById(
`message-toolcall-${selectedMessage.id}`,
);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}, 100);
}
};
const renderLabel = (d: ToolCallMessage | Message) => {
let prefixName = `Output of `;
if (d?.type === MessageType.TOOL_USE) prefixName = `Using tool input of `;
if (d?.type === MessageType.TOOL_RESULT)
prefixName = `Tool result output of `;
return (
<div className={styles.renderLabel}>
<span> {`${prefixName}🛠️ ${d?.tool_name ?? "Unknown Tool"}`}</span>
<span className={styles.labelId}>{d.id}</span>
</div>
);
};
const labelRender: LabelRender = (props) => {
const { value } = props;
// Find corresponding message based on value (id)
const message = messageList.find((msg: Message) => msg.id === value);
if (!message) {
return <span>No option match</span>;
}
const d = message as ToolCallMessage;
let prefixName = `Output of `;
if (d?.type === MessageType.TOOL_USE) prefixName = `Using tool input of `;
if (d?.type === MessageType.TOOL_RESULT)
prefixName = `Tool result output of `;
return <span>{`${prefixName}🛠️ ${d?.tool_name ?? "Unknown Tool"}`}</span>;
};
return (
<div className={styles.workWrap}>
<div className={styles.workspaceHeader}>
<div className={styles.titleContainer}>
<SparkComputerLine className={styles.computerIcon} />
<h2 className={styles.title}>Agent Workspace</h2>
</div>
</div>
{displayedContent && (
<div className={styles.todoHeader}>
<span className={styles.stepTitle}>
<Select
className={styles.workspaceSelect}
prefix={
<span className={styles.stepCount}>
{serialNum}/{messageList?.length}
</span>
}
value={
messageList.find(
(msg: Message) => msg.content === displayedContent,
)?.id
}
onChange={(v) => {
selectOnchange(v);
}}
notFoundContent={null}
options={(messageList || []).map((d: Message) => ({
value: d.id,
label: renderLabel(d),
// disabled: displayedContent === null
}))}
labelRender={labelRender}
/>
</span>
</div>
)}
<div className={styles.todoList}>
{displayedContent ? (
<Collapse
items={items}
className={styles.collapse}
defaultActiveKey={
items.length === 1 ? ["1"] : ["file", "output", "image"]
}
/>
) : null}
</div>
</div>
);
};
export default memo(Workspace);