Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a333c66

Browse files
committed
Add tools
1 parent 567d395 commit a333c66

File tree

6 files changed

+276
-54
lines changed

6 files changed

+276
-54
lines changed

codersdk/toolsdk/toolsdk.go

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var (
4646
DeleteTemplate.Generic(),
4747
GetWorkspaceAgentLogs.Generic(),
4848
GetWorkspaceBuildLogs.Generic(),
49+
GetWorkspace.Generic(),
4950
}
5051

5152
ReportTask = Tool[string]{

site/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"update-emojis": "cp -rf ./node_modules/emoji-datasource-apple/img/apple/64/* ./static/emojis"
3535
},
3636
"dependencies": {
37+
"@ai-sdk/provider-utils": "2.2.6",
3738
"@ai-sdk/react": "1.2.6",
3839
"@ai-sdk/ui-utils": "1.2.7",
3940
"@emoji-mart/data": "1.2.1",

site/pnpm-lock.yaml

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/pages/ChatPage/ChatMessages.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Loader } from "components/Loader/Loader";
1717
import ReactMarkdown from "react-markdown";
1818
import remarkGfm from "remark-gfm";
1919
import rehypeRaw from "rehype-raw";
20+
import { ChatToolInvocation } from "./ChatToolInvocation";
2021

2122
const fadeIn = keyframes`
2223
from {
@@ -259,7 +260,7 @@ const MessageBubble: FC<MessageBubbleProps> = memo(({ message }) => {
259260
case "tool-invocation":
260261
return (
261262
<div key={partIndex}>
262-
{renderToolInvocation(part.toolInvocation, theme)}
263+
<ChatToolInvocation toolInvocation={part.toolInvocation as any} />
263264
</div>
264265
);
265266
case "reasoning":
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Meta, StoryObj } from "@storybook/react";
2+
import { ChatToolInvocation } from "./ChatToolInvocation";
3+
import { MockWorkspace } from "testHelpers/entities";
4+
5+
const meta: Meta<typeof ChatToolInvocation> = {
6+
title: "pages/ChatPage/ChatToolInvocation",
7+
component: ChatToolInvocation,
8+
};
9+
10+
export default meta;
11+
type Story = StoryObj<typeof ChatToolInvocation>;
12+
13+
export const GetWorkspace: Story = {
14+
args: {
15+
toolInvocation: {
16+
toolName: "coder_get_workspace",
17+
args: {
18+
id: MockWorkspace.id,
19+
},
20+
result: MockWorkspace,
21+
state: "result",
22+
toolCallId: "some-id",
23+
},
24+
},
25+
};
26+
27+
export const CreateWorkspace: Story = {
28+
args: {
29+
toolInvocation: {
30+
toolName: "coder_create_workspace",
31+
args: {
32+
name: MockWorkspace.name,
33+
rich_parameters: {},
34+
template_version_id: MockWorkspace.template_active_version_id,
35+
user: MockWorkspace.owner_name,
36+
},
37+
result: MockWorkspace,
38+
state: "result",
39+
toolCallId: "some-id",
40+
},
41+
},
42+
};
+227-53
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,252 @@
1-
import { FC } from "react";
2-
import type { ToolInvocation } from "@ai-sdk/ui-utils";
1+
import { FC, useMemo } from "react";
32
import { useTheme } from "@emotion/react";
3+
import type { ToolCall, ToolResult } from "@ai-sdk/provider-utils";
4+
import * as TypesGen from "api/typesGenerated";
5+
import CheckCircle from "@mui/icons-material/CheckCircle";
6+
import CircularProgress from "@mui/material/CircularProgress";
7+
import ErrorIcon from "@mui/icons-material/Error";
48

59
interface ChatToolInvocationProps {
6-
toolInvocation: ToolInvocation;
10+
toolInvocation: ChatToolInvocations;
711
}
812

913
export const ChatToolInvocation: FC<ChatToolInvocationProps> = ({
1014
toolInvocation,
1115
}) => {
1216
const theme = useTheme();
17+
18+
let preview: React.ReactNode;
19+
switch (toolInvocation.toolName) {
20+
case "coder_get_workspace":
21+
switch (toolInvocation.state) {
22+
case "partial-call":
23+
case "call":
24+
preview = <div>Getting Workspace By ID...</div>;
25+
break;
26+
case "result":
27+
preview = (
28+
<div css={{ display: "flex", alignItems: "center" }}>
29+
<img
30+
src={toolInvocation.result.template_icon || "/icon/code.svg"}
31+
alt={toolInvocation.result.name}
32+
/>
33+
<div>
34+
<div>{toolInvocation.result.name}</div>
35+
<div>{toolInvocation.result.template_display_name}</div>
36+
</div>
37+
</div>
38+
);
39+
break;
40+
}
41+
break;
42+
default:
43+
switch (toolInvocation.state) {
44+
case "partial-call":
45+
case "call":
46+
preview = <div>Running Tool...</div>;
47+
break;
48+
case "result":
49+
preview = <pre>{JSON.stringify(toolInvocation.result, null, 2)}</pre>;
50+
break;
51+
}
52+
}
53+
54+
const hasError = useMemo(() => {
55+
if (toolInvocation.state !== "result") {
56+
return false;
57+
}
58+
return (
59+
typeof toolInvocation.result === "object" &&
60+
"error" in toolInvocation.result
61+
);
62+
}, [toolInvocation]);
63+
const statusColor = useMemo(() => {
64+
if (toolInvocation.state !== "result") {
65+
return theme.palette.primary.main;
66+
}
67+
return hasError ? theme.palette.error.main : theme.palette.success.main;
68+
}, [toolInvocation, hasError]);
69+
const friendlyName = useMemo(() => {
70+
return toolInvocation.toolName
71+
.replace("coder_", "")
72+
.replace("_", " ")
73+
.replace(/\b\w/g, (char) => char.toUpperCase());
74+
}, [toolInvocation.toolName]);
75+
1376
return (
1477
<div
1578
css={{
1679
marginTop: theme.spacing(1),
1780
marginLeft: theme.spacing(2),
1881
borderLeft: `2px solid ${theme.palette.info.light}`,
1982
paddingLeft: theme.spacing(1.5),
20-
fontSize: "0.875em",
21-
fontFamily: "monospace",
83+
display: "flex",
84+
flexDirection: "column",
85+
gap: theme.spacing(1),
2286
}}
2387
>
24-
<div
25-
css={{
26-
color: theme.palette.info.light,
27-
fontStyle: "italic",
28-
fontWeight: 500,
29-
marginBottom: theme.spacing(0.5),
30-
}}
31-
>
32-
🛠️ Tool Call: {toolInvocation.toolName}
33-
</div>
34-
<div
35-
css={{
36-
backgroundColor: theme.palette.action.hover,
37-
padding: theme.spacing(1.5),
38-
borderRadius: "6px",
39-
marginTop: theme.spacing(0.5),
40-
color: theme.palette.text.secondary,
41-
}}
42-
>
43-
<div css={{ marginBottom: theme.spacing(1) }}>
44-
Arguments:
45-
<div
88+
<div css={{ display: "flex", alignItems: "center" }}>
89+
{toolInvocation.state !== "result" && (
90+
<CircularProgress
91+
size={16}
4692
css={{
47-
marginTop: theme.spacing(0.5),
48-
fontFamily: "monospace",
49-
whiteSpace: "pre-wrap",
50-
wordBreak: "break-all",
51-
fontSize: "0.9em",
52-
color: theme.palette.text.primary,
93+
color: statusColor,
5394
}}
54-
>
55-
{JSON.stringify(toolInvocation.args, null, 2)}
56-
</div>
57-
</div>
58-
{"result" in toolInvocation && (
59-
<div>
60-
Result:
61-
<div
62-
css={{
63-
marginTop: theme.spacing(0.5),
64-
fontFamily: "monospace",
65-
whiteSpace: "pre-wrap",
66-
wordBreak: "break-all",
67-
fontSize: "0.9em",
68-
color: theme.palette.text.primary,
69-
}}
70-
>
71-
{JSON.stringify(toolInvocation.result, null, 2)}
72-
</div>
73-
</div>
95+
/>
7496
)}
97+
{toolInvocation.state === "result" ? (
98+
hasError ? (
99+
<ErrorIcon sx={{ color: statusColor, fontSize: 16 }} />
100+
) : (
101+
<CheckCircle sx={{ color: statusColor, fontSize: 16 }} />
102+
)
103+
) : null}
104+
<div
105+
css={{
106+
flex: 1,
107+
}}
108+
>
109+
{friendlyName}
110+
</div>
75111
</div>
112+
<div>{preview}</div>
76113
</div>
77114
);
78115
};
116+
117+
export type ChatToolInvocations =
118+
| ToolInvocation<
119+
"coder_get_workspace",
120+
{
121+
id: string;
122+
},
123+
TypesGen.Workspace
124+
>
125+
| ToolInvocation<
126+
"coder_create_workspace",
127+
{
128+
user: string;
129+
template_version_id: string;
130+
name: string;
131+
rich_parameters: Record<string, any>;
132+
},
133+
TypesGen.Workspace
134+
>
135+
| ToolInvocation<
136+
"coder_list_workspaces",
137+
{
138+
owner: string;
139+
},
140+
Pick<
141+
TypesGen.Workspace,
142+
| "id"
143+
| "name"
144+
| "template_id"
145+
| "template_name"
146+
| "template_display_name"
147+
| "template_icon"
148+
| "template_active_version_id"
149+
| "outdated"
150+
>[]
151+
>
152+
| ToolInvocation<
153+
"coder_list_templates",
154+
{},
155+
Pick<
156+
TypesGen.Template,
157+
| "id"
158+
| "name"
159+
| "description"
160+
| "active_version_id"
161+
| "active_user_count"
162+
>[]
163+
>
164+
| ToolInvocation<
165+
"coder_template_version_parameters",
166+
{
167+
template_version_id: string;
168+
},
169+
TypesGen.TemplateVersionParameter[]
170+
>
171+
| ToolInvocation<"coder_get_authenticated_user", {}, TypesGen.User>
172+
| ToolInvocation<
173+
"coder_create_workspace_build",
174+
{
175+
workspace_id: string;
176+
transition: "start" | "stop" | "delete";
177+
},
178+
TypesGen.WorkspaceBuild
179+
>
180+
| ToolInvocation<
181+
"coder_create_template_version",
182+
{
183+
template_id?: string;
184+
file_id: string;
185+
},
186+
TypesGen.TemplateVersion
187+
>
188+
| ToolInvocation<
189+
"coder_get_workspace_agent_logs",
190+
{
191+
workspace_agent_id: string;
192+
},
193+
string[]
194+
>
195+
| ToolInvocation<
196+
"coder_get_workspace_build_logs",
197+
{
198+
workspace_build_id: string;
199+
},
200+
string[]
201+
>
202+
| ToolInvocation<
203+
"coder_get_template_version_logs",
204+
{
205+
template_version_id: string;
206+
},
207+
string[]
208+
>
209+
| ToolInvocation<
210+
"coder_update_template_active_version",
211+
{
212+
template_id: string;
213+
template_version_id: string;
214+
},
215+
string
216+
>
217+
| ToolInvocation<
218+
"coder_upload_tar_file",
219+
{
220+
mime_type: string;
221+
files: Record<string, string>;
222+
},
223+
TypesGen.UploadResponse
224+
>
225+
| ToolInvocation<
226+
"coder_create_template",
227+
{
228+
name: string;
229+
},
230+
TypesGen.Template
231+
>
232+
| ToolInvocation<
233+
"coder_delete_template",
234+
{
235+
template_id: string;
236+
},
237+
string
238+
>;
239+
240+
type ToolInvocation<N extends string, A, R> =
241+
| ({
242+
state: "partial-call";
243+
step?: number;
244+
} & ToolCall<N, A>)
245+
| ({
246+
state: "call";
247+
step?: number;
248+
} & ToolCall<N, A>)
249+
| ({
250+
state: "result";
251+
step?: number;
252+
} & ToolResult<N, A, R>);

0 commit comments

Comments
 (0)