Skip to content

Commit 720ddd1

Browse files
authored
Merge pull request #470 from easyops-cn/steve/aside
fix(): show tool call detail in aside
2 parents d9afdaa + e9efa9b commit 720ddd1

File tree

7 files changed

+131
-123
lines changed

7 files changed

+131
-123
lines changed

bricks/ai-portal/src/chat-stream/Aside/Aside.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
flex-direction: column;
4848
}
4949

50+
.body.scrollable {
51+
overflow-y: auto;
52+
}
53+
5054
.app {
5155
flex: 1;
5256
min-height: 0;

bricks/ai-portal/src/chat-stream/Aside/Aside.tsx

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
import React, { useContext, useMemo } from "react";
1+
import React, { useContext } from "react";
22
import classNames from "classnames";
33
import type { GeneralIconProps } from "@next-bricks/icons/general-icon";
44
import styles from "./Aside.module.css";
55
import sharedStyles from "../../cruise-canvas/shared.module.css";
66
import { WrappedCodeBlock, WrappedIconButton } from "../../shared/bricks";
7-
import type { CmdbInstanceDetailData } from "../../cruise-canvas/interfaces";
87
import { ToolCallStatus } from "../../cruise-canvas/ToolCallStatus/ToolCallStatus";
98
import { TaskContext } from "../../shared/TaskContext";
109
import { StreamContext } from "../StreamContext";
1110
import type {
1211
ActiveDetailOfActivity,
13-
FileInfo,
1412
FulfilledActiveDetail,
1513
} from "../../shared/interfaces";
1614
import { FlowApp } from "./FlowApp/FlowApp";
15+
import { ToolCallDetail } from "../../cruise-canvas/ToolCallDetail/ToolCallDetail";
1716

1817
const ICON_SHRINK: GeneralIconProps = {
1918
lib: "easyops",
@@ -30,38 +29,6 @@ export function Aside({ detail, isSubTask, faded }: AsideProps) {
3029
const { setActiveDetail, setSubActiveDetail } = useContext(TaskContext);
3130
const { setUserClosedAside } = useContext(StreamContext);
3231

33-
const [toolMarkdownContent, cmdbInstanceDetails /* , files */] =
34-
useMemo(() => {
35-
const contents: string[] = [];
36-
const instanceDetails: CmdbInstanceDetailData[] = [];
37-
const files: FileInfo[] = [];
38-
39-
if (detail.type === "job") {
40-
detail.job.messages?.forEach((message) => {
41-
if (message.role === "tool") {
42-
for (const part of message.parts) {
43-
if (part.type === "data") {
44-
switch (part.data?.type) {
45-
case "markdown":
46-
contents.push(part.data.content);
47-
break;
48-
case "cmdb_instance_detail":
49-
instanceDetails.push(part.data as CmdbInstanceDetailData);
50-
break;
51-
}
52-
} else if (part.type === "file") {
53-
files.push(part.file);
54-
}
55-
}
56-
}
57-
});
58-
}
59-
60-
const markdownContent = contents.join("");
61-
62-
return [markdownContent, instanceDetails, files] as const;
63-
}, [detail]);
64-
6532
return (
6633
<div
6734
className={classNames(styles.aside, {
@@ -85,7 +52,12 @@ export function Aside({ detail, isSubTask, faded }: AsideProps) {
8552
}}
8653
/>
8754
</div>
88-
<div className={styles.body}>
55+
<div
56+
className={classNames(styles.body, {
57+
[styles.scrollable]:
58+
detail.type === "job" && !detail.job.generatedView,
59+
})}
60+
>
8961
{detail.type === "job" ? (
9062
<>
9163
<ToolCallStatus job={detail.job} variant="read-only" />
@@ -95,19 +67,9 @@ export function Aside({ detail, isSubTask, faded }: AsideProps) {
9567
source={detail.job.generatedView.code}
9668
language="jsx"
9769
/>
98-
) : toolMarkdownContent ? (
99-
<EditorApp
100-
name="Content"
101-
source={toolMarkdownContent}
102-
language="md"
103-
/>
104-
) : cmdbInstanceDetails.length > 0 ? (
105-
<EditorApp
106-
name="CMDB"
107-
source={JSON.stringify(cmdbInstanceDetails, null, 2)}
108-
language="json"
109-
/>
110-
) : null}
70+
) : (
71+
<ToolCallDetail job={detail.job} />
72+
)}
11173
</>
11274
) : (
11375
<FlowApp

bricks/ai-portal/src/cruise-canvas/CruiseCanvas.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import {
5050
import { DONE_STATES, GENERAL_DONE_STATES } from "../shared/constants.js";
5151
import { WrappedIcon } from "../shared/bricks";
5252
import { CanvasContext } from "./CanvasContext.js";
53-
import { ToolCallDetail } from "./ToolCallDetail/ToolCallDetail.js";
53+
import { ToolCallDetailDrawer } from "./ToolCallDetail/ToolCallDetailDrawer";
5454
import { getScrollTo } from "./utils/getScrollTo.js";
5555
import { handleKeyboardNav } from "./utils/handleKeyboardNav.js";
5656
import { ExpandedView } from "../shared/ExpandedView/ExpandedView.js";
@@ -962,7 +962,7 @@ export function CruiseCanvasComponent(
962962
) : null}
963963
</div>
964964
{fulfilledActiveDetail?.type === "job" && (
965-
<ToolCallDetail job={fulfilledActiveDetail.job} />
965+
<ToolCallDetailDrawer job={fulfilledActiveDetail.job} />
966966
)}
967967
{activeExpandedViewJobId && <ExpandedView views={views!} />}
968968
{activeFile && <FilePreview file={activeFile} />}

bricks/ai-portal/src/cruise-canvas/ToolCallDetail/ToolCallDetail.tsx

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,23 @@
1-
import React, {
2-
useCallback,
3-
useContext,
4-
useEffect,
5-
useMemo,
6-
useRef,
7-
useState,
8-
} from "react";
9-
import type { Drawer } from "@next-bricks/containers/drawer";
1+
import React, { useMemo } from "react";
102
import classNames from "classnames";
3+
import { initializeI18n } from "@next-core/i18n";
114
import type { DataPart, Job, Part } from "../../shared/interfaces";
12-
import { WrappedCodeBlock, WrappedDrawer } from "../../shared/bricks";
5+
import { WrappedCodeBlock } from "../../shared/bricks";
136
import styles from "./ToolCallDetail.module.css";
147
import sharedStyles from "../shared.module.css";
15-
import { K, t } from "../i18n";
16-
import { TaskContext } from "../../shared/TaskContext";
17-
import { ToolCallStatus } from "../ToolCallStatus/ToolCallStatus";
8+
import { K, t, locales, NS } from "./i18n";
189
import { ToolProgressLine } from "../ToolProgressLine/ToolProgressLine";
1910
import { EnhancedMarkdown } from "../EnhancedMarkdown/EnhancedMarkdown";
2011
import { MarkdownPre } from "../../shared/MarkdownPre";
2112

13+
initializeI18n(NS, locales);
14+
2215
export interface ToolCallDetailProps {
2316
job: Job;
2417
}
2518

26-
function getDrawerWidth() {
27-
const { innerWidth } = window;
28-
return innerWidth < 800
29-
? Math.min(500, innerWidth)
30-
: innerWidth < 1000
31-
? innerWidth * 0.8
32-
: 800;
33-
}
34-
3519
export function ToolCallDetail({ job }: ToolCallDetailProps): JSX.Element {
36-
const { setActiveDetail } = useContext(TaskContext);
3720
const toolCall = job.toolCall!;
38-
const toolTitle = toolCall.annotations?.title;
3921

4022
const [progress, intermediateParts, responseParts] = useMemo(() => {
4123
const toolCallMessages = job.messages?.filter((msg) => msg.role === "tool");
@@ -67,32 +49,6 @@ export function ToolCallDetail({ job }: ToolCallDetailProps): JSX.Element {
6749
return [progress, intermediateParts, responseParts];
6850
}, [job.messages]);
6951

70-
const handleClose = useCallback(() => {
71-
setTimeout(() => {
72-
setActiveDetail(null);
73-
}, 300);
74-
}, [setActiveDetail]);
75-
76-
const ref = useRef<Drawer>(null);
77-
78-
useEffect(() => {
79-
setTimeout(() => {
80-
ref.current?.open();
81-
});
82-
}, []);
83-
84-
const [width, setWidth] = useState(getDrawerWidth);
85-
86-
useEffect(() => {
87-
const onResize = () => {
88-
setWidth(getDrawerWidth);
89-
};
90-
window.addEventListener("resize", onResize);
91-
return () => {
92-
window.removeEventListener("resize", onResize);
93-
};
94-
}, []);
95-
9652
const hasProcessParts = intermediateParts.length > 0 || !!progress;
9753
const hasResponseParts = responseParts.length > 0;
9854
const toolState =
@@ -104,18 +60,7 @@ export function ToolCallDetail({ job }: ToolCallDetailProps): JSX.Element {
10460
const failed = job.isError || toolState === "failed";
10561

10662
return (
107-
<WrappedDrawer
108-
ref={ref}
109-
customTitle={toolTitle || toolCall.name}
110-
width={width}
111-
closable
112-
mask
113-
maskClosable
114-
keyboard
115-
themeVariant="elevo"
116-
onClose={handleClose}
117-
>
118-
<ToolCallStatus job={job} variant="read-only" />
63+
<>
11964
<div className={styles.detail}>
12065
<div className={styles.heading}>{t(K.ARGUMENTS)}:</div>
12166
<div className={`${styles.body} ${sharedStyles.markdown}`}>
@@ -180,7 +125,7 @@ export function ToolCallDetail({ job }: ToolCallDetailProps): JSX.Element {
180125
</div>
181126
</div>
182127
)}
183-
</WrappedDrawer>
128+
</>
184129
);
185130
}
186131

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, {
2+
useCallback,
3+
useContext,
4+
useEffect,
5+
useRef,
6+
useState,
7+
} from "react";
8+
import type { Drawer } from "@next-bricks/containers/drawer";
9+
import type { Job } from "../../shared/interfaces";
10+
import { WrappedDrawer } from "../../shared/bricks";
11+
import { TaskContext } from "../../shared/TaskContext";
12+
import { ToolCallStatus } from "../ToolCallStatus/ToolCallStatus";
13+
import { ToolCallDetail } from "./ToolCallDetail";
14+
15+
export interface ToolCallDetailDrawerProps {
16+
job: Job;
17+
}
18+
19+
function getDrawerWidth() {
20+
const { innerWidth } = window;
21+
return innerWidth < 800
22+
? Math.min(500, innerWidth)
23+
: innerWidth < 1000
24+
? innerWidth * 0.8
25+
: 800;
26+
}
27+
28+
export function ToolCallDetailDrawer({
29+
job,
30+
}: ToolCallDetailDrawerProps): JSX.Element {
31+
const { setActiveDetail } = useContext(TaskContext);
32+
const toolCall = job.toolCall!;
33+
const toolTitle = toolCall.annotations?.title;
34+
35+
const handleClose = useCallback(() => {
36+
setTimeout(() => {
37+
setActiveDetail(null);
38+
}, 300);
39+
}, [setActiveDetail]);
40+
41+
const ref = useRef<Drawer>(null);
42+
43+
useEffect(() => {
44+
setTimeout(() => {
45+
ref.current?.open();
46+
});
47+
}, []);
48+
49+
const [width, setWidth] = useState(getDrawerWidth);
50+
51+
useEffect(() => {
52+
const onResize = () => {
53+
setWidth(getDrawerWidth);
54+
};
55+
window.addEventListener("resize", onResize);
56+
return () => {
57+
window.removeEventListener("resize", onResize);
58+
};
59+
}, []);
60+
61+
return (
62+
<WrappedDrawer
63+
ref={ref}
64+
customTitle={toolTitle || toolCall.name}
65+
width={width}
66+
closable
67+
mask
68+
maskClosable
69+
keyboard
70+
themeVariant="elevo"
71+
onClose={handleClose}
72+
>
73+
<ToolCallStatus job={job} variant="read-only" />
74+
<ToolCallDetail job={job} />
75+
</WrappedDrawer>
76+
);
77+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { i18n } from "@next-core/i18n";
2+
3+
export enum K {
4+
ARGUMENTS = "ARGUMENTS",
5+
PROCESS = "PROCESS",
6+
RESPONSE = "RESPONSE",
7+
}
8+
9+
const en: Locale = {
10+
ARGUMENTS: "Arguments",
11+
PROCESS: "Process",
12+
RESPONSE: "Response",
13+
};
14+
15+
const zh: Locale = {
16+
ARGUMENTS: "参数",
17+
PROCESS: "过程",
18+
RESPONSE: "响应",
19+
};
20+
21+
export const NS = "bricks/ai-portal/ToolCallDetail";
22+
23+
export const locales = { en, zh };
24+
25+
export const t = i18n.getFixedT(null, NS);
26+
27+
type Locale = { [k in K]: string } & {
28+
[k in K as `${k}_plural`]?: string;
29+
};

bricks/ai-portal/src/cruise-canvas/i18n.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ export enum K {
66
TASK_COMPLETED = "TASK_COMPLETED",
77
SHARE = "SHARE",
88
TYPE_YOUR_MESSAGE_HERE = "TYPE_YOUR_MESSAGE_HERE",
9-
ARGUMENTS = "ARGUMENTS",
10-
PROCESS = "PROCESS",
11-
RESPONSE = "RESPONSE",
129
SWITCH_TO_CHAT = "SWITCH_TO_CHAT",
1310
BACK_TO_CENTER = "BACK_TO_CENTER",
1411
ZOOM_IN = "ZOOM_IN",
@@ -27,9 +24,6 @@ const en: Locale = {
2724
TASK_COMPLETED: "Task completed",
2825
SHARE: "Share",
2926
TYPE_YOUR_MESSAGE_HERE: "Type your message here",
30-
ARGUMENTS: "Arguments",
31-
PROCESS: "Process",
32-
RESPONSE: "Response",
3327
SWITCH_TO_CHAT: "Switch to chat",
3428
BACK_TO_CENTER: "Back to center",
3529
ZOOM_IN: "Zoom in",
@@ -48,9 +42,6 @@ const zh: Locale = {
4842
TASK_COMPLETED: "任务完成",
4943
SHARE: "分享",
5044
TYPE_YOUR_MESSAGE_HERE: "在这里输入信息",
51-
ARGUMENTS: "参数",
52-
PROCESS: "过程",
53-
RESPONSE: "响应",
5445
SWITCH_TO_CHAT: "切换为聊天",
5546
BACK_TO_CENTER: "回中",
5647
ZOOM_IN: "放大",

0 commit comments

Comments
 (0)