Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita1c03b6

Browse files
feat: add experimental Chat UI (#17650)
Builds on#17570Frontend portion ofhttps://github.com/coder/coder/tree/chat originallyauthored by@kylecarbsAdditional changes:- Addresses linter complaints- Brings `ChatToolInvocation` argument definitions in line with thosedefined in `codersdk/toolsdk`- Ensures chat-related features are not shown unless`ExperimentAgenticChat` is enabled.Co-authored-by: Kyle Carberry <kyle@carberry.com>
1 parent8f64d49 commita1c03b6

File tree

14 files changed

+3381
-6
lines changed

14 files changed

+3381
-6
lines changed

‎site/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
"update-emojis":"cp -rf ./node_modules/emoji-datasource-apple/img/apple/64/* ./static/emojis"
3636
},
3737
"dependencies": {
38+
"@ai-sdk/provider-utils":"2.2.6",
39+
"@ai-sdk/react":"1.2.6",
3840
"@emoji-mart/data":"1.2.1",
3941
"@emoji-mart/react":"1.1.1",
4042
"@emotion/cache":"11.14.0",
@@ -111,6 +113,7 @@
111113
"react-virtualized-auto-sizer":"1.0.24",
112114
"react-window":"1.8.11",
113115
"recharts":"2.15.0",
116+
"rehype-raw":"7.0.0",
114117
"remark-gfm":"4.0.0",
115118
"resize-observer-polyfill":"1.5.1",
116119
"rollup-plugin-visualizer":"5.14.0",

‎site/pnpm-lock.yaml

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

‎site/src/api/api.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,13 @@ class ApiMethods {
827827
returnresponse.data;
828828
};
829829

830+
getDeploymentLLMs=async():Promise<TypesGen.LanguageModelConfig>=>{
831+
constresponse=awaitthis.axios.get<TypesGen.LanguageModelConfig>(
832+
"/api/v2/deployment/llms",
833+
);
834+
returnresponse.data;
835+
};
836+
830837
getOrganizationIdpSyncClaimFieldValues=async(
831838
organization:string,
832839
field:string,
@@ -2489,6 +2496,23 @@ class ApiMethods {
24892496
markAllInboxNotificationsAsRead=async()=>{
24902497
awaitthis.axios.put<void>("/api/v2/notifications/inbox/mark-all-as-read");
24912498
};
2499+
2500+
createChat=async()=>{
2501+
constres=awaitthis.axios.post<TypesGen.Chat>("/api/v2/chats");
2502+
returnres.data;
2503+
};
2504+
2505+
getChats=async()=>{
2506+
constres=awaitthis.axios.get<TypesGen.Chat[]>("/api/v2/chats");
2507+
returnres.data;
2508+
};
2509+
2510+
getChatMessages=async(chatId:string)=>{
2511+
constres=awaitthis.axios.get<TypesGen.ChatMessage[]>(
2512+
`/api/v2/chats/${chatId}/messages`,
2513+
);
2514+
returnres.data;
2515+
};
24922516
}
24932517

24942518
// This is a hard coded CSRF token/cookie pair for local development. In prod,

‎site/src/api/queries/chats.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import{API}from"api/api";
2+
importtype{QueryClient}from"react-query";
3+
4+
exportconstcreateChat=(queryClient:QueryClient)=>{
5+
return{
6+
mutationFn:API.createChat,
7+
onSuccess:async()=>{
8+
awaitqueryClient.invalidateQueries(["chats"]);
9+
},
10+
};
11+
};
12+
13+
exportconstgetChats=()=>{
14+
return{
15+
queryKey:["chats"],
16+
queryFn:API.getChats,
17+
};
18+
};
19+
20+
exportconstgetChatMessages=(chatID:string)=>{
21+
return{
22+
queryKey:["chatMessages",chatID],
23+
queryFn:()=>API.getChatMessages(chatID),
24+
};
25+
};

‎site/src/api/queries/deployment.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,10 @@ export const deploymentIdpSyncFieldValues = (field: string) => {
3636
queryFn:()=>API.getDeploymentIdpSyncFieldValues(field),
3737
};
3838
};
39+
40+
exportconstdeploymentLanguageModels=()=>{
41+
return{
42+
queryKey:["deployment","llms"],
43+
queryFn:API.getDeploymentLLMs,
44+
};
45+
};

‎site/src/contexts/useAgenticChat.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import{experiments}from"api/queries/experiments";
2+
3+
import{useEmbeddedMetadata}from"hooks/useEmbeddedMetadata";
4+
import{useQuery}from"react-query";
5+
6+
interfaceAgenticChat{
7+
readonlyenabled:boolean;
8+
}
9+
10+
exportconstuseAgenticChat=():AgenticChat=>{
11+
const{ metadata}=useEmbeddedMetadata();
12+
constenabledExperimentsQuery=useQuery(experiments(metadata.experiments));
13+
return{
14+
enabled:enabledExperimentsQuery.data?.includes("agentic-chat")??false,
15+
};
16+
};

‎site/src/modules/dashboard/Navbar/NavbarView.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Button } from "components/Button/Button";
44
import{ExternalImage}from"components/ExternalImage/ExternalImage";
55
import{CoderIcon}from"components/Icons/CoderIcon";
66
importtype{ProxyContextValue}from"contexts/ProxyContext";
7+
import{useAgenticChat}from"contexts/useAgenticChat";
78
import{useWebpushNotifications}from"contexts/useWebpushNotifications";
89
import{NotificationsInbox}from"modules/notifications/NotificationsInbox/NotificationsInbox";
910
importtype{FC}from"react";
@@ -45,8 +46,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
4546
canViewAuditLog,
4647
proxyContextValue,
4748
})=>{
48-
const{ subscribed, enabled, loading, subscribe, unsubscribe}=
49-
useWebpushNotifications();
49+
constwebPush=useWebpushNotifications();
5050

5151
return(
5252
<divclassName="border-0 border-b border-solid h-[72px] flex items-center leading-none px-6">
@@ -76,13 +76,21 @@ export const NavbarView: FC<NavbarViewProps> = ({
7676
/>
7777
</div>
7878

79-
{enabled ?(
80-
subscribed ?(
81-
<Buttonvariant="outline"disabled={loading}onClick={unsubscribe}>
79+
{webPush.enabled ?(
80+
webPush.subscribed ?(
81+
<Button
82+
variant="outline"
83+
disabled={webPush.loading}
84+
onClick={webPush.unsubscribe}
85+
>
8286
Disable WebPush
8387
</Button>
8488
) :(
85-
<Buttonvariant="outline"disabled={loading}onClick={subscribe}>
89+
<Button
90+
variant="outline"
91+
disabled={webPush.loading}
92+
onClick={webPush.subscribe}
93+
>
8694
Enable WebPush
8795
</Button>
8896
)
@@ -132,6 +140,7 @@ interface NavItemsProps {
132140

133141
constNavItems:FC<NavItemsProps>=({ className})=>{
134142
constlocation=useLocation();
143+
constagenticChat=useAgenticChat();
135144

136145
return(
137146
<navclassName={cn("flex items-center gap-4 h-full",className)}>
@@ -154,6 +163,16 @@ const NavItems: FC<NavItemsProps> = ({ className }) => {
154163
>
155164
Templates
156165
</NavLink>
166+
{agenticChat.enabled ?(
167+
<NavLink
168+
className={({ isActive})=>{
169+
returncn(linkStyles.default,isActive ?linkStyles.active :"");
170+
}}
171+
to="/chat"
172+
>
173+
Chat
174+
</NavLink>
175+
) :null}
157176
</nav>
158177
);
159178
};
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import{useTheme}from"@emotion/react";
2+
importSendIconfrom"@mui/icons-material/Send";
3+
importButtonfrom"@mui/material/Button";
4+
importIconButtonfrom"@mui/material/IconButton";
5+
importPaperfrom"@mui/material/Paper";
6+
importStackfrom"@mui/material/Stack";
7+
importTextFieldfrom"@mui/material/TextField";
8+
import{createChat}from"api/queries/chats";
9+
importtype{Chat}from"api/typesGenerated";
10+
import{Margins}from"components/Margins/Margins";
11+
import{useAuthenticated}from"hooks";
12+
import{typeFC,typeFormEvent,useState}from"react";
13+
import{useMutation,useQueryClient}from"react-query";
14+
import{useNavigate}from"react-router-dom";
15+
import{LanguageModelSelector}from"./LanguageModelSelector";
16+
17+
exportinterfaceChatLandingLocationState{
18+
chat:Chat;
19+
message:string;
20+
}
21+
22+
constChatLanding:FC=()=>{
23+
const{ user}=useAuthenticated();
24+
consttheme=useTheme();
25+
const[input,setInput]=useState("");
26+
constnavigate=useNavigate();
27+
constqueryClient=useQueryClient();
28+
constcreateChatMutation=useMutation(createChat(queryClient));
29+
30+
return(
31+
<Margins>
32+
<div
33+
css={{
34+
display:"flex",
35+
flexDirection:"column",
36+
marginTop:theme.spacing(24),
37+
alignItems:"center",
38+
paddingBottom:theme.spacing(4),
39+
}}
40+
>
41+
{/* Initial Welcome Message Area */}
42+
<div
43+
css={{
44+
flexGrow:1,
45+
display:"flex",
46+
flexDirection:"column",
47+
justifyContent:"center",
48+
alignItems:"center",
49+
gap:theme.spacing(1),
50+
padding:theme.spacing(1),
51+
width:"100%",
52+
maxWidth:"700px",
53+
marginBottom:theme.spacing(4),
54+
}}
55+
>
56+
<h1
57+
css={{
58+
fontSize:theme.typography.h4.fontSize,
59+
fontWeight:theme.typography.h4.fontWeight,
60+
lineHeight:theme.typography.h4.lineHeight,
61+
marginBottom:theme.spacing(1),
62+
textAlign:"center",
63+
}}
64+
>
65+
Good evening,{user?.name.split(" ")[0]}
66+
</h1>
67+
<p
68+
css={{
69+
fontSize:theme.typography.h6.fontSize,
70+
fontWeight:theme.typography.h6.fontWeight,
71+
lineHeight:theme.typography.h6.lineHeight,
72+
color:theme.palette.text.secondary,
73+
textAlign:"center",
74+
margin:0,
75+
maxWidth:"500px",
76+
marginInline:"auto",
77+
}}
78+
>
79+
How can I help you today?
80+
</p>
81+
</div>
82+
83+
{/* Input Form and Suggestions - Always Visible */}
84+
<divcss={{width:"100%",maxWidth:"700px",marginTop:"auto"}}>
85+
<Stack
86+
direction="row"
87+
spacing={2}
88+
justifyContent="center"
89+
sx={{mb:2}}
90+
>
91+
<Button
92+
variant="outlined"
93+
onClick={()=>setInput("Help me work on issue #...")}
94+
>
95+
Work on Issue
96+
</Button>
97+
<Button
98+
variant="outlined"
99+
onClick={()=>setInput("Help me build a template for...")}
100+
>
101+
Build a Template
102+
</Button>
103+
<Button
104+
variant="outlined"
105+
onClick={()=>setInput("Help me start a new project using...")}
106+
>
107+
Start a Project
108+
</Button>
109+
</Stack>
110+
<LanguageModelSelector/>
111+
<Paper
112+
component="form"
113+
onSubmit={async(e:FormEvent<HTMLFormElement>)=>{
114+
e.preventDefault();
115+
setInput("");
116+
constchat=awaitcreateChatMutation.mutateAsync();
117+
navigate(`/chat/${chat.id}`,{
118+
state:{
119+
chat,
120+
message:input,
121+
},
122+
});
123+
}}
124+
elevation={2}
125+
css={{
126+
padding:"16px",
127+
display:"flex",
128+
alignItems:"center",
129+
width:"100%",
130+
borderRadius:"12px",
131+
border:`1px solid${theme.palette.divider}`,
132+
}}
133+
>
134+
<TextField
135+
value={input}
136+
onChange={(event:React.ChangeEvent<HTMLInputElement>)=>{
137+
setInput(event.target.value);
138+
}}
139+
placeholder="Ask Coder..."
140+
required
141+
fullWidth
142+
variant="outlined"
143+
multiline
144+
maxRows={5}
145+
css={{
146+
marginRight:theme.spacing(1),
147+
"& .MuiOutlinedInput-root":{
148+
borderRadius:"8px",
149+
padding:"10px 14px",
150+
},
151+
}}
152+
autoFocus
153+
/>
154+
<IconButtontype="submit"color="primary"disabled={!input.trim()}>
155+
<SendIcon/>
156+
</IconButton>
157+
</Paper>
158+
</div>
159+
</div>
160+
</Margins>
161+
);
162+
};
163+
164+
exportdefaultChatLanding;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp