Skip to content

Commit 7c2fd21

Browse files
committed
Support complex user_request structure in MPlan
Updated backend and frontend to handle user_request as either a string or a structured object. Added new TypeScript interfaces for user_request, adjusted PlanDataService to extract text from complex user_request objects, and modified PlanPage logic for improved UI state handling.
1 parent 40993f8 commit 7c2fd21

File tree

4 files changed

+87
-11
lines changed

4 files changed

+87
-11
lines changed

src/backend/v3/api/router.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,12 +1340,13 @@ async def get_plan_by_id(request: Request, plan_id: Optional[str] = Query(None)
13401340

13411341
team = await memory_store.get_team_by_id(team_id=plan.team_id)
13421342
agent_messages = await memory_store.get_agent_messages(plan_id=plan.plan_id)
1343-
1343+
mplan = plan.m_plan if plan.m_plan else None
1344+
plan.m_plan = None # remove m_plan from plan object for response
13441345
return {
13451346
"plan": plan,
13461347
"team": team if team else None,
13471348
"messages": agent_messages,
1348-
"m_plan": plan.m_plan,
1349+
"m_plan": mplan,
13491350
}
13501351
else:
13511352
track_event_if_configured(

src/frontend/src/models/plan.tsx

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,50 @@ export interface MStepBE {
127127
/** Action to be performed */
128128
action: string;
129129
}
130+
/**
131+
* Represents a user request item within the user_request object
132+
*/
133+
export interface UserRequestItem {
134+
/** AI model identifier */
135+
ai_model_id?: string | null;
136+
/** Metadata */
137+
metadata?: Record<string, any>;
138+
/** Content type */
139+
content_type?: string;
140+
/** Text content */
141+
text?: string;
142+
/** Encoding */
143+
encoding?: string | null;
144+
}
130145

131-
export interface MPlanBE extends BaseModel {
132-
/** The type of data model */
133-
data_type: "m_plan";
146+
/**
147+
* Represents the user_request object structure from the database
148+
*/
149+
export interface UserRequestObject {
150+
/** AI model identifier */
151+
ai_model_id?: string | null;
152+
/** Metadata */
153+
metadata?: Record<string, any>;
154+
/** Content type */
155+
content_type?: string;
156+
/** Role */
157+
role?: string;
158+
/** Name */
159+
name?: string | null;
160+
/** Items array containing the actual request text */
161+
items?: UserRequestItem[];
162+
/** Encoding */
163+
encoding?: string | null;
164+
/** Finish reason */
165+
finish_reason?: string | null;
166+
/** Status */
167+
status?: string | null;
168+
}
169+
170+
export interface MPlanBE {
171+
172+
/** Unique identifier */
173+
id: string;
134174
/** User identifier */
135175
user_id: string;
136176
/** Team identifier */
@@ -139,8 +179,8 @@ export interface MPlanBE extends BaseModel {
139179
plan_id: string;
140180
/** Overall status of the plan */
141181
overall_status: PlanStatus;
142-
/** User's original request */
143-
user_request: string;
182+
/** User's original request - can be string or complex object */
183+
user_request: string | UserRequestObject;
144184
/** List of team member names */
145185
team: string[];
146186
/** Facts or context for the plan */

src/frontend/src/pages/PlanPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const PlanPage: React.FC = () => {
4747
const [waitingForPlan, setWaitingForPlan] = useState<boolean>(true);
4848
const [showProcessingPlanSpinner, setShowProcessingPlanSpinner] = useState<boolean>(false);
4949
const [showApprovalButtons, setShowApprovalButtons] = useState<boolean>(true);
50-
const [continueWithWebsocketFlow, setContinueWithWebsocketFlow] = useState<boolean>(true);
50+
const [continueWithWebsocketFlow, setContinueWithWebsocketFlow] = useState<boolean>(false);
5151
// WebSocket connection state
5252
const [wsConnected, setWsConnected] = useState<boolean>(false);
5353
const [streamingMessages, setStreamingMessages] = useState<StreamingPlanUpdate[]>([]);
@@ -279,7 +279,7 @@ const PlanPage: React.FC = () => {
279279
// WebSocket connection with proper error handling and v3 backend compatibility
280280
useEffect(() => {
281281
if (planId && continueWithWebsocketFlow) {
282-
console.log('🔌 Connecting WebSocket:', { planId });
282+
console.log('🔌 Connecting WebSocket:', { planId, continueWithWebsocketFlow });
283283

284284
const connectWebSocket = async () => {
285285
try {
@@ -355,6 +355,7 @@ const PlanPage: React.FC = () => {
355355
setShowApprovalButtons(true);
356356

357357
} else {
358+
setShowApprovalButtons(false);
358359
setWaitingForPlan(false);
359360
}
360361
if (planResult?.plan?.overall_status !== PlanStatus.COMPLETED) {

src/frontend/src/services/PlanDataService.tsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,37 @@ export class PlanDataService {
122122
}))
123123
};
124124
}
125+
/**
126+
* Extracts the actual text from a user_request object or string
127+
* @param userRequest - Either a string or UserRequestObject
128+
* @returns The extracted text string
129+
*/
130+
static extractUserRequestText(userRequest: string | UserRequestObject): string {
131+
if (typeof userRequest === 'string') {
132+
return userRequest;
133+
}
134+
135+
if (userRequest && typeof userRequest === 'object') {
136+
// Look for text in the items array
137+
if (Array.isArray(userRequest.items)) {
138+
const textItem = userRequest.items.find(item => item.text);
139+
if (textItem?.text) {
140+
return textItem.text;
141+
}
142+
}
143+
144+
// Fallback: try to find any text content
145+
if (userRequest.content_type === 'text' && 'text' in userRequest) {
146+
return (userRequest as any).text || '';
147+
}
148+
149+
// Last resort: stringify the object
150+
return JSON.stringify(userRequest);
151+
}
152+
153+
return '';
154+
}
155+
125156
/**
126157
* Converts MPlanBE to MPlanData
127158
* @param mplanBE - MPlanBE from backend
@@ -132,6 +163,9 @@ export class PlanDataService {
132163
return null;
133164
}
134165

166+
// Extract the actual user request text
167+
const userRequestText = this.extractUserRequestText(mplanBE.user_request);
168+
135169
// Convert MStepBE[] to the MPlanData steps format
136170
const steps = mplanBE.steps.map((stepBE: MStepBE, index: number) => ({
137171
id: index + 1, // MPlanData expects numeric id starting from 1
@@ -151,12 +185,12 @@ export class PlanDataService {
151185
return {
152186
id: mplanBE.id,
153187
status: mplanBE.overall_status.toString().toUpperCase(),
154-
user_request: mplanBE.user_request,
188+
user_request: userRequestText,
155189
team: mplanBE.team,
156190
facts: mplanBE.facts,
157191
steps: steps,
158192
context: {
159-
task: mplanBE.user_request,
193+
task: userRequestText,
160194
participant_descriptions: {} // Default empty object since it's not in MPlanBE
161195
},
162196
// Additional fields from m_plan

0 commit comments

Comments
 (0)