Skip to content

Commit b88e102

Browse files
committed
feat: walkthrough
1 parent 17b5c6b commit b88e102

File tree

7 files changed

+195
-3
lines changed

7 files changed

+195
-3
lines changed

packages/livekit/src/chat/live-chat-kit.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
type PrepareRequestGetters,
1717
} from "./flexible-chat-transport";
1818
import { compactTask } from "./llm";
19+
import { walkthroughTask } from "./llm/walkthrough-task";
1920
import { createModel } from "./models";
2021

2122
const logger = getLogger("LiveChatKit");
@@ -97,6 +98,7 @@ export class LiveChatKit<
9798
messages: Message[];
9899
}) => void;
99100
readonly compact: () => Promise<string>;
101+
readonly walkthrough: () => Promise<string>;
100102
private lastStepStartTimestamp: number | undefined;
101103

102104
constructor({
@@ -202,6 +204,20 @@ export class LiveChatKit<
202204
}
203205
return summary;
204206
};
207+
this.walkthrough = async () => {
208+
const { messages } = this.chat;
209+
const model = createModel({ llm: getters.getLLM() });
210+
const walkthrough = await walkthroughTask({
211+
store: this.store,
212+
taskId: this.taskId,
213+
model,
214+
messages,
215+
});
216+
if (!walkthrough) {
217+
throw new Error("Failed to walkthrough task");
218+
}
219+
return walkthrough;
220+
};
205221
}
206222

207223
init(cwd: string | undefined, options?: InitOptions | undefined) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { LanguageModelV2 } from "@ai-sdk/provider";
2+
import { formatters, getLogger, prompts } from "@getpochi/common";
3+
import type { Store } from "@livestore/livestore";
4+
import { convertToModelMessages, generateText } from "ai";
5+
import { makeDownloadFunction } from "../../store-blob";
6+
import type { Message } from "../../types";
7+
8+
const logger = getLogger("walkthroughTask");
9+
10+
export async function walkthroughTask({
11+
store,
12+
taskId,
13+
model,
14+
messages,
15+
abortSignal,
16+
}: {
17+
store: Store;
18+
taskId: string;
19+
model: LanguageModelV2;
20+
messages: Message[];
21+
abortSignal?: AbortSignal;
22+
}): Promise<string | undefined> {
23+
if (messages.length < 1) {
24+
throw new Error("No messages to walkthrough");
25+
}
26+
27+
try {
28+
const text = prompts.inlineCompact(
29+
await generateWalkthrough(
30+
store,
31+
taskId,
32+
model,
33+
abortSignal,
34+
messages.slice(0, -1),
35+
),
36+
messages.length - 1,
37+
);
38+
39+
return text;
40+
} catch (err) {
41+
logger.warn("Failed to create walkthrough", err);
42+
}
43+
}
44+
45+
async function generateWalkthrough(
46+
store: Store,
47+
taskId: string,
48+
model: LanguageModelV2,
49+
abortSignal: AbortSignal | undefined,
50+
inputMessages: Message[],
51+
) {
52+
const messages: Message[] = [
53+
...inputMessages,
54+
{
55+
id: crypto.randomUUID(),
56+
role: "user",
57+
parts: [
58+
{
59+
type: "text",
60+
text: 'Please create "walkthrough" artifacts for the conversation above message. This type of artifact includes a concise summary of the changes that have been made to remind the user of what has happened in the active conversation.',
61+
// AT "2025/12/15 16:56"
62+
// TODO MAYBE (╭ರ_•́)
63+
// fine tune this prompt
64+
},
65+
],
66+
},
67+
];
68+
69+
const resp = await generateText({
70+
providerOptions: {
71+
pochi: {
72+
taskId,
73+
version: globalThis.POCHI_CLIENT,
74+
useCase: "walkthrough-task",
75+
},
76+
},
77+
model,
78+
prompt: convertToModelMessages(
79+
formatters.llm(messages, {
80+
removeSystemReminder: true,
81+
}),
82+
),
83+
experimental_download: makeDownloadFunction(store),
84+
abortSignal,
85+
maxOutputTokens: 3_000,
86+
maxRetries: 0,
87+
});
88+
89+
return resp.text;
90+
}

packages/vscode-webui/src/components/token-usage.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,18 @@ interface Props {
3232
newCompactTask: () => void;
3333
enabled: boolean;
3434
};
35+
walkthrough?: {
36+
walkthroughTaskPending: boolean;
37+
walkthroughTask: () => void;
38+
enabled: boolean;
39+
};
3540
}
3641

3742
export function TokenUsage({
3843
totalTokens,
3944
className,
4045
compact,
46+
walkthrough,
4147
selectedModel,
4248
}: Props) {
4349
const { t } = useTranslation();
@@ -223,6 +229,29 @@ export function TokenUsage({
223229
{minTokenTooltip}
224230
</Tooltip>
225231
</TooltipProvider>
232+
<TooltipProvider>
233+
<Tooltip>
234+
<TooltipTrigger asChild>
235+
<div className="inline-block">
236+
<Button
237+
variant="secondary"
238+
size="sm"
239+
className="text-xs"
240+
onClick={() => {
241+
walkthrough?.walkthroughTask();
242+
setIsOpen(false);
243+
}}
244+
disabled={!walkthrough?.enabled}
245+
>
246+
{walkthrough?.walkthroughTaskPending
247+
? t("tokenUsage.walkthroughGenerating")
248+
: t("tokenUsage.walkthroughTask")}
249+
</Button>
250+
</div>
251+
</TooltipTrigger>
252+
{minTokenTooltip}
253+
</Tooltip>
254+
</TooltipProvider>
226255
</div>
227256
</div>
228257
</PopoverContent>

packages/vscode-webui/src/features/chat/components/chat-toolbar.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import {
1414
} from "@/components/ui/hover-card";
1515
import { ApprovalButton, type useApprovalAndRetry } from "@/features/approval";
1616
import { useAutoApproveGuard, useTaskChangedFiles } from "@/features/chat";
17-
import { useSelectedModels } from "@/features/settings";
18-
import { AutoApproveMenu } from "@/features/settings";
17+
import { AutoApproveMenu, useSelectedModels } from "@/features/settings";
1918
import { TodoList, useTodos } from "@/features/todo";
2019
import { useAddCompleteToolCalls } from "@/lib/hooks/use-add-complete-tool-calls";
2120
import type { useAttachmentUpload } from "@/lib/hooks/use-attachment-upload";
@@ -33,6 +32,7 @@ import { useChatSubmit } from "../hooks/use-chat-submit";
3332
import { useInlineCompactTask } from "../hooks/use-inline-compact-task";
3433
import { useNewCompactTask } from "../hooks/use-new-compact-task";
3534
import type { SubtaskInfo } from "../hooks/use-subtask-info";
35+
import { useWalkthroughTask } from "../hooks/use-walkthrough-task";
3636
import { ChatInputForm } from "./chat-input-form";
3737
import { ErrorMessageView } from "./error-message-view";
3838
import { CompleteSubtaskButton } from "./subtask";
@@ -41,6 +41,7 @@ interface ChatToolbarProps {
4141
task?: Task;
4242
approvalAndRetry: ReturnType<typeof useApprovalAndRetry>;
4343
compact: () => Promise<string>;
44+
walkthrough: () => Promise<string>;
4445
chat: UseChatHelpers<Message>;
4546
attachmentUpload: ReturnType<typeof useAttachmentUpload>;
4647
isSubTask: boolean;
@@ -54,6 +55,7 @@ export const ChatToolbar: React.FC<ChatToolbarProps> = ({
5455
chat,
5556
approvalAndRetry: { pendingApproval, retry },
5657
compact,
58+
walkthrough,
5759
attachmentUpload,
5860
isSubTask,
5961
subtask,
@@ -106,6 +108,9 @@ export const ChatToolbar: React.FC<ChatToolbarProps> = ({
106108
compact,
107109
});
108110

111+
const { walkthroughTask, walkthroughTaskPending } = useWalkthroughTask({
112+
walkthrough,
113+
});
109114
const {
110115
isExecuting,
111116
isBusyCore,
@@ -191,6 +196,11 @@ export const ChatToolbar: React.FC<ChatToolbarProps> = ({
191196
newCompactTask,
192197
newCompactTaskPending,
193198
};
199+
const walkthroughOptions = {
200+
enabled: !isLoading && !isExecuting && !walkthroughTaskPending,
201+
walkthroughTaskPending,
202+
walkthroughTask,
203+
};
194204

195205
const messageContent = useMemo(
196206
() => JSON.stringify(messages, null, 2),
@@ -290,6 +300,7 @@ export const ChatToolbar: React.FC<ChatToolbarProps> = ({
290300
totalTokens={totalTokens}
291301
className="mr-5"
292302
compact={compactOptions}
303+
walkthrough={walkthroughOptions}
293304
selectedModel={selectedModel}
294305
/>
295306
)}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { vscodeHost } from "@/lib/vscode";
2+
import type { Task } from "@getpochi/livekit";
3+
import { useMutation } from "@tanstack/react-query";
4+
5+
export const useWalkthroughTask = ({
6+
task,
7+
walkthrough,
8+
}: {
9+
task?: Task;
10+
walkthrough: () => Promise<string>;
11+
}) => {
12+
const mutation = useMutation({
13+
mutationFn: async () => {
14+
return walkthrough();
15+
},
16+
onSuccess: async (walkthroughText) => {
17+
const cwd = task?.cwd;
18+
if (!cwd) {
19+
throw new Error("Cannot get task cwd when creating walkthrough.");
20+
}
21+
22+
// AT "2025/12/16 10:04"
23+
// TODO IMPLEMENT ..._〆(°▽°*)
24+
await vscodeHost.executeToolCall(
25+
"writeToFile",
26+
{
27+
path: ".pochi/walkthrough/xx.md",
28+
content: walkthroughText,
29+
},
30+
{
31+
toolCallId: "mock-tool-call-id",
32+
// abortSignal: undefined,
33+
},
34+
);
35+
// toast or notification
36+
},
37+
});
38+
39+
return {
40+
walkthroughTaskPending: mutation.isPending,
41+
walkthroughTask: () => mutation.mutate(),
42+
};
43+
};

packages/vscode-webui/src/features/chat/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { vscodeHost } from "@/lib/vscode";
99
import { useChat } from "@ai-sdk/react";
1010
import { formatters } from "@getpochi/common";
1111
import type { UserInfo } from "@getpochi/common/configuration";
12-
import { type Task, catalog, taskCatalog } from "@getpochi/livekit";
1312
import type { Message } from "@getpochi/livekit";
13+
import { type Task, catalog, taskCatalog } from "@getpochi/livekit";
1414
import { useLiveChatKit } from "@getpochi/livekit/react";
1515
import type { Todo } from "@getpochi/tools";
1616
import { useStore } from "@livestore/react";
@@ -438,6 +438,7 @@ function Chat({
438438
task={task}
439439
todosRef={todosRef}
440440
compact={chatKit.compact}
441+
walkthrough={chatKit.walkthrough}
441442
approvalAndRetry={approvalAndRetry}
442443
attachmentUpload={attachmentUpload}
443444
isSubTask={isSubTask}

packages/vscode-webui/src/i18n/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
},
3939
"tokenUsage": {
4040
"compacting": "Compacting...",
41+
"walkthroughGenerating": "Generating walkthrough",
42+
"walkthroughTask": "Walkthrough Task",
4143
"ofTokens": "% of {{tokens}} tokens",
4244
"rules": "Rules",
4345
"contextWindow": "Context Window",

0 commit comments

Comments
 (0)