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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/pages/ChatPage/ChatMessages.tsx

Lines changed: 2 additions & 1 deletion
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":
Lines changed: 42 additions & 0 deletions
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+
};
Lines changed: 227 additions & 53 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy