Skip to content

Commit e16ea6b

Browse files
committed
fix (tasks): minor bug fixes (WIP)
1 parent 7a2ea61 commit e16ea6b

File tree

5 files changed

+375
-108
lines changed

5 files changed

+375
-108
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"use client"
2+
3+
import React from "react"
4+
import {
5+
IconTool,
6+
IconBrain,
7+
IconArrowRight,
8+
IconMessageCircle,
9+
IconInfoCircle,
10+
IconAlertTriangle
11+
} from "@tabler/icons-react"
12+
import ReactMarkdown from "react-markdown"
13+
import { cn } from "@utils/cn"
14+
15+
const ExecutionUpdate = ({ update }) => {
16+
const { message, timestamp } = update
17+
18+
const formattedTimestamp = new Date(timestamp).toLocaleTimeString([], {
19+
hour: "2-digit",
20+
minute: "2-digit",
21+
second: "2-digit"
22+
})
23+
24+
if (typeof message === "string") {
25+
// Handle old string-based updates for backward compatibility
26+
return (
27+
<div className="flex gap-3 text-sm">
28+
<span className="text-neutral-500 font-mono text-xs flex-shrink-0 pt-0.5">
29+
[{formattedTimestamp}]
30+
</span>
31+
<p className="text-neutral-300">{message}</p>
32+
</div>
33+
)
34+
}
35+
36+
const { type, content, tool_name, parameters, result, is_error } = message
37+
38+
const renderContent = () => {
39+
switch (type) {
40+
case "info":
41+
return (
42+
<div className="flex items-start gap-2 text-neutral-400">
43+
<IconInfoCircle
44+
size={16}
45+
className="flex-shrink-0 mt-1 text-neutral-500"
46+
/>
47+
<p>{content}</p>
48+
</div>
49+
)
50+
case "error":
51+
return (
52+
<div className="flex items-start gap-2 text-red-400">
53+
<IconAlertTriangle
54+
size={16}
55+
className="flex-shrink-0 mt-1"
56+
/>
57+
<p>{content}</p>
58+
</div>
59+
)
60+
case "thought":
61+
return (
62+
<div className="flex items-start gap-2 text-neutral-400">
63+
<IconBrain
64+
size={16}
65+
className="flex-shrink-0 mt-1 text-yellow-400/80"
66+
/>
67+
<ReactMarkdown className="prose prose-sm prose-invert text-neutral-400">
68+
{content}
69+
</ReactMarkdown>
70+
</div>
71+
)
72+
case "tool_call":
73+
return (
74+
<div className="flex items-start gap-2">
75+
<IconTool
76+
size={16}
77+
className="flex-shrink-0 mt-1 text-blue-400"
78+
/>
79+
<div className="flex-1">
80+
<p className="font-medium text-neutral-200">
81+
<span className="text-blue-400">
82+
Tool Call:
83+
</span>{" "}
84+
<span className="font-mono text-sm">
85+
{tool_name}
86+
</span>
87+
</p>
88+
<pre className="text-xs bg-neutral-900/50 p-2 rounded-md mt-1 whitespace-pre-wrap font-mono border border-neutral-700">
89+
{JSON.stringify(parameters, null, 2)}
90+
</pre>
91+
</div>
92+
</div>
93+
)
94+
case "tool_result":
95+
return (
96+
<div className="flex items-start gap-2">
97+
<IconArrowRight
98+
size={16}
99+
className={cn(
100+
"flex-shrink-0 mt-1",
101+
is_error ? "text-red-400" : "text-green-400"
102+
)}
103+
/>
104+
<div className="flex-1">
105+
<p
106+
className={cn(
107+
"font-medium",
108+
is_error ? "text-red-400" : "text-green-400"
109+
)}
110+
>
111+
Tool Result:{" "}
112+
<span className="font-mono text-sm">
113+
{tool_name}
114+
</span>
115+
</p>
116+
<pre className="text-xs bg-neutral-900/50 p-2 rounded-md mt-1 whitespace-pre-wrap font-mono border border-neutral-700">
117+
{typeof result === "object"
118+
? JSON.stringify(result, null, 2)
119+
: String(result)}
120+
</pre>
121+
</div>
122+
</div>
123+
)
124+
case "final_answer":
125+
return (
126+
<div className="p-3 bg-green-500/10 border border-green-500/20 rounded-lg">
127+
<div className="flex items-center gap-2 font-semibold text-green-300 mb-2">
128+
<IconMessageCircle size={16} />
129+
Final Answer
130+
</div>
131+
<ReactMarkdown className="prose prose-sm prose-invert text-neutral-200">
132+
{content}
133+
</ReactMarkdown>
134+
</div>
135+
)
136+
default:
137+
return (
138+
<p className="text-xs font-mono text-neutral-500">
139+
{JSON.stringify(message)}
140+
</p>
141+
)
142+
}
143+
}
144+
145+
return (
146+
<div className="flex gap-3">
147+
<span className="text-neutral-500 font-mono text-xs flex-shrink-0 pt-1">
148+
[{formattedTimestamp}]
149+
</span>
150+
<div className="flex-1">{renderContent()}</div>
151+
</div>
152+
)
153+
}
154+
155+
export default ExecutionUpdate

src/client/components/tasks/TaskDetailsContent.js

Lines changed: 120 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,91 @@
11
"use client"
22

3-
import React from "react"
3+
import React, { useState } from "react"
4+
import toast from "react-hot-toast"
45
import { cn } from "@utils/cn"
56
import { taskStatusColors, priorityMap } from "./constants"
67
import {
78
IconGripVertical,
89
IconPlus,
910
IconSparkles,
1011
IconUser,
11-
IconX
12+
IconX,
13+
IconLoader
1214
} from "@tabler/icons-react"
1315
import ScheduleEditor from "@components/tasks/ScheduleEditor"
16+
import ExecutionUpdate from "./ExecutionUpdate"
17+
18+
// New component for handling clarification questions
19+
const ClarificationInputSection = ({ run, task, onAnswerClarifications }) => {
20+
const [answers, setAnswers] = useState({})
21+
const [isSubmitting, setIsSubmitting] = useState(false)
22+
23+
const handleAnswerChange = (questionId, text) => {
24+
setAnswers((prev) => ({ ...prev, [questionId]: text }))
25+
}
26+
27+
const handleSubmit = async () => {
28+
const unansweredQuestions = run.clarifying_questions.filter(
29+
(q) => !answers[q.question_id]?.trim()
30+
)
31+
if (unansweredQuestions.length > 0) {
32+
toast.error("Please answer all questions before submitting.")
33+
return
34+
}
35+
36+
setIsSubmitting(true)
37+
const answersPayload = Object.entries(answers).map(
38+
([question_id, answer_text]) => ({
39+
question_id,
40+
answer_text
41+
})
42+
)
43+
// This function is passed down from the main page and includes closing the panel
44+
await onAnswerClarifications(task.task_id, answersPayload)
45+
setIsSubmitting(false) // This might not be reached if the component unmounts
46+
}
47+
48+
return (
49+
<div>
50+
<h4 className="font-semibold text-neutral-300 mb-2">
51+
Clarifying Questions
52+
</h4>
53+
<div className="space-y-4 bg-yellow-500/10 border border-yellow-500/20 p-4 rounded-lg">
54+
{run.clarifying_questions.map((q, index) => (
55+
<div key={q.question_id || index}>
56+
<label className="block text-sm font-medium text-yellow-200 mb-2">
57+
{q.text}
58+
</label>
59+
<textarea
60+
value={answers[q.question_id] || ""}
61+
onChange={(e) =>
62+
handleAnswerChange(
63+
q.question_id,
64+
e.target.value
65+
)
66+
}
67+
rows={2}
68+
className="w-full p-2 bg-neutral-800 border border-neutral-700 rounded-md text-sm text-white transition-colors focus:border-yellow-400 focus:ring-0"
69+
placeholder="Your answer..."
70+
/>
71+
</div>
72+
))}
73+
<div className="flex justify-end">
74+
<button
75+
onClick={handleSubmit}
76+
disabled={isSubmitting}
77+
className="px-4 py-2 text-sm font-semibold bg-yellow-400 text-black rounded-md hover:bg-yellow-300 disabled:opacity-50 flex items-center gap-2"
78+
>
79+
{isSubmitting && (
80+
<IconLoader size={16} className="animate-spin" />
81+
)}
82+
{isSubmitting ? "Submitting..." : "Submit Answers"}
83+
</button>
84+
</div>
85+
</div>
86+
</div>
87+
)
88+
}
1489

1590
const TaskDetailsContent = ({
1691
task,
@@ -22,7 +97,8 @@ const TaskDetailsContent = ({
2297
handleRemoveStep,
2398
handleStepChange,
2499
allTools,
25-
integrations
100+
integrations,
101+
onAnswerClarifications
26102
}) => {
27103
if (!task) {
28104
return null
@@ -281,66 +357,54 @@ const TaskDetailsContent = ({
281357
</div>
282358
)}
283359
{run.clarifying_questions &&
284-
run.clarifying_questions.length > 0 && (
285-
<div>
286-
<h4 className="font-semibold text-neutral-300 mb-2">
287-
Clarifying Questions
288-
</h4>
289-
<div className="space-y-2">
290-
{run.clarifying_questions.map(
291-
(q, index) => (
292-
<div
293-
key={index}
294-
className="p-3 bg-yellow-500/10 border border-yellow-500/20 text-yellow-300 rounded-lg text-sm"
295-
>
296-
<p>{q.text}</p>
297-
</div>
298-
)
299-
)}
300-
</div>
360+
run.clarifying_questions.length > 0 &&
361+
task.status === "clarification_pending" ? (
362+
<ClarificationInputSection
363+
run={run}
364+
task={task}
365+
onAnswerClarifications={onAnswerClarifications}
366+
/>
367+
) : run.clarifying_questions &&
368+
run.clarifying_questions.length > 0 ? (
369+
<div>
370+
<h4 className="font-semibold text-neutral-300 mb-2">
371+
Clarifying Questions
372+
</h4>
373+
<div className="space-y-2">
374+
{run.clarifying_questions.map(
375+
(q, index) => (
376+
<div
377+
key={q.question_id || index}
378+
className="p-3 bg-neutral-800/50 border border-neutral-700/50 rounded-lg text-sm"
379+
>
380+
<p className="text-yellow-300">
381+
{q.text}
382+
</p>
383+
{q.answer && (
384+
<p className="mt-2 pt-2 border-t border-neutral-700 text-neutral-300 italic">
385+
Your Answer: {q.answer}
386+
</p>
387+
)}
388+
</div>
389+
)
390+
)}
301391
</div>
302-
)}
392+
</div>
393+
) : null}
303394
{run.progress_updates &&
304395
run.progress_updates.length > 0 && (
305396
<div>
306397
<h4 className="font-semibold text-neutral-300 mb-2">
307-
Progress
398+
Execution Log
308399
</h4>
309-
<div className="bg-neutral-800/50 p-4 rounded-lg border border-neutral-700/50 text-sm text-neutral-300 space-y-3">
400+
<div className="bg-neutral-800/50 p-4 rounded-lg border border-neutral-700/50 space-y-4">
310401
{run.progress_updates.map(
311-
(update, index) => {
312-
const isString =
313-
typeof update === "string"
314-
const message = isString
315-
? update
316-
: update.message
317-
const timestamp = isString
318-
? null
319-
: new Date(
320-
update.timestamp
321-
).toLocaleTimeString(
322-
[],
323-
{
324-
hour: "2-digit",
325-
minute: "2-digit",
326-
second: "2-digit"
327-
}
328-
)
329-
330-
return (
331-
<div
332-
key={index}
333-
className="flex gap-3"
334-
>
335-
{timestamp && (
336-
<span className="text-neutral-500 font-mono text-xs flex-shrink-0 pt-0.5">
337-
[{timestamp}]
338-
</span>
339-
)}
340-
<p>{message}</p>
341-
</div>
342-
)
343-
}
402+
(update, index) => (
403+
<ExecutionUpdate
404+
key={index}
405+
update={update}
406+
/>
407+
)
344408
)}
345409
</div>
346410
</div>

src/client/components/tasks/TaskDetailsPanel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ const TaskDetailsPanel = ({
135135
handleStepChange={handleStepChange}
136136
allTools={allTools}
137137
integrations={integrations}
138+
onAnswerClarifications={onAnswerClarifications}
138139
/>
139140
</main>
140141

src/server/main/tasks/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ class ProgressUpdateRequest(BaseModel):
4141
user_id: str
4242
task_id: str
4343
run_id: str
44-
message: str
44+
message: Any # Changed from str to Any to allow structured updates

0 commit comments

Comments
 (0)