Skip to content

Commit f1df9e1

Browse files
reasoning streaming llm
1 parent 2a36ee5 commit f1df9e1

File tree

9 files changed

+876
-158
lines changed

9 files changed

+876
-158
lines changed

src/frontend/src/api/apiService.tsx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ const API_ENDPOINTS = {
2121
STEPS: '/steps',
2222
HUMAN_FEEDBACK: '/human_feedback',
2323
APPROVE_STEPS: '/approve_step_or_steps',
24+
APPROVE_STEPS_STREAM: '/approve_step_or_steps_stream',
2425
HUMAN_CLARIFICATION: '/human_clarification_on_plan',
26+
HUMAN_CLARIFICATION_STREAM: '/human_clarification_on_plan_stream',
2527
AGENT_MESSAGES: '/agent_messages',
2628
MESSAGES: '/messages'
2729
};
@@ -149,6 +151,51 @@ export class APIService {
149151
return response.body;
150152
}
151153

154+
/**
155+
* Approve step with streaming response like original experience
156+
* @param stepId Step ID to approve
157+
* @param planId Plan ID
158+
* @param sessionId Session ID
159+
* @param approved Whether the step is approved
160+
* @returns ReadableStream for streaming response
161+
*/
162+
async approveStepStream(
163+
stepId: string,
164+
planId: string,
165+
sessionId: string,
166+
approved: boolean
167+
): Promise<ReadableStream<Uint8Array>> {
168+
// Import the config functions
169+
const { headerBuilder, getApiUrl } = await import('./config');
170+
171+
const authHeaders = headerBuilder();
172+
const apiUrl = getApiUrl();
173+
174+
const response = await fetch(`${apiUrl}${API_ENDPOINTS.APPROVE_STEPS}`, {
175+
method: 'POST',
176+
headers: {
177+
'Content-Type': 'application/json',
178+
...authHeaders,
179+
},
180+
body: JSON.stringify({
181+
step_id: stepId,
182+
plan_id: planId,
183+
session_id: sessionId,
184+
approved
185+
})
186+
});
187+
188+
if (!response.ok) {
189+
throw new Error(`HTTP error! status: ${response.status}`);
190+
}
191+
192+
if (!response.body) {
193+
throw new Error('Response body is null');
194+
}
195+
196+
return response.body;
197+
}
198+
152199
/**
153200
* Get all plans, optionally filtered by session ID
154201
* @param sessionId Optional session ID to filter plans
@@ -510,6 +557,93 @@ export class APIService {
510557
this._cache.clear();
511558
}
512559

560+
/**
561+
* Submit clarification for a plan with streaming response
562+
* @param planId Plan ID
563+
* @param sessionId Session ID
564+
* @param clarification Clarification text
565+
* @returns ReadableStream for streaming response
566+
*/
567+
async submitClarificationStream(
568+
planId: string,
569+
sessionId: string,
570+
clarification: string
571+
): Promise<ReadableStream<Uint8Array>> {
572+
// Import the config functions
573+
const { headerBuilder, getApiUrl } = await import('./config');
574+
575+
const authHeaders = headerBuilder();
576+
const apiUrl = getApiUrl();
577+
578+
const response = await fetch(`${apiUrl}${API_ENDPOINTS.HUMAN_CLARIFICATION_STREAM}`, {
579+
method: 'POST',
580+
headers: {
581+
'Content-Type': 'application/json',
582+
...authHeaders,
583+
},
584+
body: JSON.stringify({
585+
plan_id: planId,
586+
session_id: sessionId,
587+
human_clarification: clarification
588+
})
589+
});
590+
591+
if (!response.ok) {
592+
throw new Error(`HTTP error! status: ${response.status}`);
593+
}
594+
595+
if (!response.body) {
596+
throw new Error('Response body is null');
597+
}
598+
599+
return response.body;
600+
}
601+
602+
/**
603+
* Approve step with streaming response
604+
* @param stepId Step ID to approve
605+
* @param planId Plan ID
606+
* @param sessionId Session ID
607+
* @param approved Whether the step is approved
608+
* @returns ReadableStream for streaming response
609+
*/
610+
async approveStepStreamNew(
611+
stepId: string,
612+
planId: string,
613+
sessionId: string,
614+
approved: boolean
615+
): Promise<ReadableStream<Uint8Array>> {
616+
// Import the config functions
617+
const { headerBuilder, getApiUrl } = await import('./config');
618+
619+
const authHeaders = headerBuilder();
620+
const apiUrl = getApiUrl();
621+
622+
const response = await fetch(`${apiUrl}${API_ENDPOINTS.APPROVE_STEPS_STREAM}`, {
623+
method: 'POST',
624+
headers: {
625+
'Content-Type': 'application/json',
626+
...authHeaders,
627+
},
628+
body: JSON.stringify({
629+
step_id: stepId,
630+
plan_id: planId,
631+
session_id: sessionId,
632+
approved
633+
})
634+
});
635+
636+
if (!response.ok) {
637+
throw new Error(`HTTP error! status: ${response.status}`);
638+
}
639+
640+
if (!response.body) {
641+
throw new Error('Response body is null');
642+
}
643+
644+
return response.body;
645+
}
646+
513647
/**
514648
* Get progress status counts for a plan
515649
* @param plan Plan with steps

src/frontend/src/components/content/HomeInput.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ const HomeInput: React.FC<HomeInputProps> = ({
6565
}
6666

6767
if (response.plan_id && response.plan_id !== null) {
68-
showToast("Plan created!", "success");
6968
dismissToast(id);
70-
// Navigate to the create page to show streaming generation
69+
// Navigate immediately to show unified reasoning + planning experience
70+
showToast("Starting planner agent reasoning...", "success");
71+
// Navigate immediately to the create page to show unified experience
7172
navigate(`/plan/${response.plan_id}/create`, {
7273
state: { isNewPlan: true, autoStartGeneration: true }
7374
});

src/frontend/src/components/content/PlanChat.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,53 @@ const PlanChat: React.FC<PlanChatProps> = ({
7171
setShowScrollButton(false);
7272
};
7373

74-
if (!planData)
74+
if (!planData) {
75+
// If no plan data, still show the chat input for user to provide clarifications
7576
return (
76-
<ContentNotFound subtitle="The requested page could not be found." />
77+
<div className="chat-container">
78+
<div className="messages" ref={messagesContainerRef}>
79+
<div className="message-wrapper">
80+
<div className="message assistant">
81+
<div className="plan-chat-header">
82+
<div className="plan-chat-speaker">
83+
<Body1 className="speaker-name">AI Assistant</Body1>
84+
<Tag size="extra-small" shape="rounded" appearance="brand">
85+
Ready
86+
</Tag>
87+
</div>
88+
</div>
89+
<Body1>
90+
<div className="plan-chat-message-content">
91+
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypePrism]}>
92+
Plan completed! Feel free to ask questions or provide additional clarifications.
93+
</ReactMarkdown>
94+
</div>
95+
</Body1>
96+
</div>
97+
</div>
98+
</div>
99+
100+
<div className="plan-chat-input-container" ref={inputContainerRef}>
101+
<div className="plan-chat-input-wrapper">
102+
<ChatInput
103+
value={input}
104+
placeholder="Ask questions or provide clarifications about your plan..."
105+
onChange={setInput}
106+
onEnter={() => OnChatSubmit(input)}
107+
disabledChat={submittingChatDisableInput}
108+
>
109+
<Button
110+
appearance="subtle"
111+
onClick={() => OnChatSubmit(input)}
112+
disabled={submittingChatDisableInput}
113+
icon={<Send />}
114+
/>
115+
</ChatInput>
116+
</div>
117+
</div>
118+
</div>
77119
);
120+
}
78121
return (
79122
<div className="chat-container">
80123
<div className="messages" ref={messagesContainerRef}>

src/frontend/src/components/content/PlanPanelRight.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,18 @@ const PlanPanelRight: React.FC<TaskDetailsProps> = ({
1010
OnApproveStep,
1111
processingSubtaskId
1212
}) => {
13-
if (!planData) return null;
13+
// Only show panel when there are tasks created with relevant agents
14+
// Hide panel during initial streaming or when no steps exist
15+
const shouldShowPanel = planData &&
16+
planData.steps &&
17+
planData.steps.length > 0 &&
18+
planData.agents &&
19+
planData.agents.length > 0 &&
20+
!loading;
21+
22+
if (!shouldShowPanel) {
23+
return null; // Hide the panel completely
24+
}
1425

1526
return (
1627
<PanelRight
@@ -19,8 +30,7 @@ const PlanPanelRight: React.FC<TaskDetailsProps> = ({
1930
panelResize={true}
2031
panelType="first"
2132
>
22-
23-
<div >
33+
<div>
2434
<TaskDetails
2535
planData={planData}
2636
OnApproveStep={OnApproveStep}

0 commit comments

Comments
 (0)