Skip to content

Commit 5dc89cc

Browse files
committed
fix (tasks): fixes for recurring and scheduled tasks, added tester for recurring and scheduled tasks, added fixes for task progress display on tasks page
1 parent d43e17e commit 5dc89cc

File tree

8 files changed

+248
-107
lines changed

8 files changed

+248
-107
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { NextResponse } from "next/server"
2+
import { withAuth } from "@lib/api-utils"
3+
4+
const appServerUrl =
5+
process.env.NEXT_PUBLIC_ENVIRONMENT === "selfhost"
6+
? process.env.INTERNAL_APP_SERVER_URL
7+
: process.env.NEXT_PUBLIC_APP_SERVER_URL
8+
9+
export const POST = withAuth(async function POST(request, { authHeader }) {
10+
try {
11+
const response = await fetch(
12+
`${appServerUrl}/testing/trigger-scheduler`,
13+
{
14+
method: "POST",
15+
headers: { "Content-Type": "application/json", ...authHeader }
16+
}
17+
)
18+
19+
const data = await response.json()
20+
if (!response.ok) {
21+
throw new Error(data.detail || "Failed to trigger scheduler")
22+
}
23+
return NextResponse.json(data)
24+
} catch (error) {
25+
console.error("API Error in /testing/trigger-scheduler:", error)
26+
return NextResponse.json({ detail: error.message }, { status: 500 })
27+
}
28+
})

src/client/app/settings/page.js

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ const TestingTools = () => {
510510
)
511511
}
512512
}
513+
const [isTriggeringScheduler, setIsTriggeringScheduler] = useState(false)
513514

514515
const [whatsAppNumber, setWhatsAppNumber] = useState("")
515516
const [isVerifying, setIsVerifying] = useState(false)
@@ -519,6 +520,23 @@ const TestingTools = () => {
519520
message: ""
520521
})
521522

523+
const handleTriggerScheduler = async () => {
524+
setIsTriggeringScheduler(true)
525+
try {
526+
const response = await fetch("/api/testing/trigger-scheduler", {
527+
method: "POST"
528+
})
529+
const result = await response.json()
530+
if (!response.ok) {
531+
throw new Error(result.detail || "Failed to trigger scheduler.")
532+
}
533+
toast.success(result.message)
534+
} catch (error) {
535+
toast.error(error.message)
536+
} finally {
537+
setIsTriggeringScheduler(false)
538+
}
539+
}
522540
const handleVerifyWhatsApp = async () => {
523541
if (!whatsAppNumber) {
524542
toast.error("Please enter a phone number to verify.")
@@ -717,6 +735,31 @@ const TestingTools = () => {
717735
</p>
718736
)}
719737
</div>
738+
{/* Scheduler Test Tool */}
739+
<div className="bg-[var(--color-primary-surface)]/50 p-4 md:p-6 rounded-lg border border-[var(--color-primary-surface-elevated)] mt-6">
740+
<h3 className="font-semibold text-lg text-white mb-2">
741+
Trigger Task Scheduler
742+
</h3>
743+
<p className="text-gray-400 text-sm mb-4">
744+
Manually run the Celery Beat scheduler task
745+
(`run_due_tasks`) to immediately check for and execute any
746+
due scheduled or recurring tasks. This is useful for testing
747+
without waiting for the 5-minute interval.
748+
</p>
749+
<div className="flex justify-end">
750+
<button
751+
onClick={handleTriggerScheduler}
752+
disabled={isTriggeringScheduler}
753+
className="flex items-center py-2 px-4 rounded-md bg-purple-600 hover:bg-purple-500 text-white font-medium transition-colors disabled:opacity-50"
754+
>
755+
{isTriggeringScheduler ? (
756+
<IconLoader className="w-5 h-5 animate-spin" />
757+
) : (
758+
"Run Scheduler Now"
759+
)}
760+
</button>
761+
</div>
762+
</div>
720763
</section>
721764
)
722765
}
@@ -1451,10 +1494,10 @@ const ProfilePage = () => {
14511494
<WhatsAppSettings />
14521495
<LinkedInSettings />
14531496
<ShortcutsSettings />
1454-
{(process.env.NEXT_PUBLIC_ENVIRONMENT !== "prod" ||
1455-
process.env.NEXT_PUBLIC_ENVIRONMENT !== "stag") && (
1456-
<TestingTools />
1457-
)}
1497+
{process.env.NEXT_PUBLIC_ENVIRONMENT !== "prod" &&
1498+
process.env.NEXT_PUBLIC_ENVIRONMENT !== "stag" && (
1499+
<TestingTools />
1500+
)}
14581501
</div>
14591502
</main>
14601503
</div>
Lines changed: 96 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,65 @@
11
"use client"
22

3-
import React from "react"
3+
import React, { useState } from "react"
4+
import { motion, AnimatePresence } from "framer-motion"
45
import {
56
IconTool,
67
IconBrain,
78
IconArrowRight,
89
IconMessageCircle,
910
IconInfoCircle,
10-
IconAlertTriangle
11+
IconAlertTriangle,
12+
IconChevronDown
1113
} from "@tabler/icons-react"
1214
import ReactMarkdown from "react-markdown"
1315
import { cn } from "@utils/cn"
1416

17+
const CollapsibleSection = ({
18+
title,
19+
icon,
20+
colorClass,
21+
children,
22+
defaultOpen = false
23+
}) => {
24+
const [isExpanded, setIsExpanded] = useState(defaultOpen)
25+
26+
return (
27+
<div
28+
className={cn(
29+
"border-l-2 pl-3 transition-colors",
30+
isExpanded ? colorClass : "border-neutral-700"
31+
)}
32+
>
33+
<button
34+
onClick={() => setIsExpanded(!isExpanded)}
35+
className="flex items-center gap-2 w-full text-left text-sm font-semibold"
36+
>
37+
{icon}
38+
<span className="flex-grow">{title}</span>
39+
<IconChevronDown
40+
size={16}
41+
className={cn(
42+
"transform transition-transform duration-200",
43+
isExpanded ? "rotate-180" : "rotate-0"
44+
)}
45+
/>
46+
</button>
47+
<AnimatePresence>
48+
{isExpanded && (
49+
<motion.div
50+
initial={{ height: 0, opacity: 0, marginTop: 0 }}
51+
animate={{ height: "auto", opacity: 1, marginTop: 8 }}
52+
exit={{ height: 0, opacity: 0, marginTop: 0 }}
53+
className="overflow-hidden"
54+
>
55+
{children}
56+
</motion.div>
57+
)}
58+
</AnimatePresence>
59+
</div>
60+
)
61+
}
62+
1563
const ExecutionUpdate = ({ update }) => {
1664
const { message, timestamp } = update
1765

@@ -21,18 +69,6 @@ const ExecutionUpdate = ({ update }) => {
2169
second: "2-digit"
2270
})
2371

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-
3672
const { type, content, tool_name, parameters, result, is_error } = message
3773

3874
const renderContent = () => {
@@ -42,7 +78,7 @@ const ExecutionUpdate = ({ update }) => {
4278
<div className="flex items-start gap-2 text-neutral-400">
4379
<IconInfoCircle
4480
size={16}
45-
className="flex-shrink-0 mt-1 text-neutral-500"
81+
className="flex-shrink-0 mt-0.5 text-neutral-500"
4682
/>
4783
<p>{content}</p>
4884
</div>
@@ -52,74 +88,70 @@ const ExecutionUpdate = ({ update }) => {
5288
<div className="flex items-start gap-2 text-red-400">
5389
<IconAlertTriangle
5490
size={16}
55-
className="flex-shrink-0 mt-1"
91+
className="flex-shrink-0 mt-0.5"
5692
/>
5793
<p>{content}</p>
5894
</div>
5995
)
6096
case "thought":
6197
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>
98+
<CollapsibleSection
99+
title="Thought Process"
100+
icon={
101+
<IconBrain
102+
size={16}
103+
className="text-yellow-400/80"
104+
/>
105+
}
106+
colorClass="border-yellow-500/50"
107+
>
108+
<div className="p-3 bg-neutral-800/50 rounded-md">
109+
<ReactMarkdown className="prose prose-sm prose-invert text-neutral-300 whitespace-pre-wrap">
110+
{content}
111+
</ReactMarkdown>
112+
</div>
113+
</CollapsibleSection>
71114
)
72115
case "tool_call":
73116
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">
117+
<CollapsibleSection
118+
title={`Tool Call: ${tool_name}`}
119+
icon={<IconTool size={16} className="text-blue-400" />}
120+
colorClass="border-blue-500/50"
121+
>
122+
<div className="p-3 bg-neutral-800/50 rounded-md">
123+
<pre className="text-xs text-neutral-300 whitespace-pre-wrap font-mono">
89124
{JSON.stringify(parameters, null, 2)}
90125
</pre>
91126
</div>
92-
</div>
127+
</CollapsibleSection>
93128
)
94129
case "tool_result":
95130
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",
131+
<CollapsibleSection
132+
title={`Tool Result: ${tool_name}`}
133+
icon={
134+
<IconArrowRight
135+
size={16}
136+
className={
108137
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">
138+
}
139+
/>
140+
}
141+
colorClass={
142+
is_error
143+
? "border-red-500/50"
144+
: "border-green-500/50"
145+
}
146+
>
147+
<div className="p-3 bg-neutral-800/50 rounded-md">
148+
<pre className="text-xs text-neutral-300 whitespace-pre-wrap font-mono">
117149
{typeof result === "object"
118150
? JSON.stringify(result, null, 2)
119151
: String(result)}
120152
</pre>
121153
</div>
122-
</div>
154+
</CollapsibleSection>
123155
)
124156
case "final_answer":
125157
return (
@@ -143,7 +175,7 @@ const ExecutionUpdate = ({ update }) => {
143175
}
144176

145177
return (
146-
<div className="flex gap-3">
178+
<div className="flex gap-3 text-sm">
147179
<span className="text-neutral-500 font-mono text-xs flex-shrink-0 pt-1">
148180
[{formattedTimestamp}]
149181
</span>
@@ -152,4 +184,4 @@ const ExecutionUpdate = ({ update }) => {
152184
)
153185
}
154186

155-
export default ExecutionUpdate
187+
export default ExecutionUpdate

src/client/components/tasks/ScheduleEditor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const ScheduleEditor = ({ schedule, setSchedule }) => {
7979
<input
8080
type="datetime-local"
8181
value={getLocalDateTimeString(schedule.run_at)}
82+
step="1" // Ensures seconds are included in the value
8283
onChange={handleRunAtChange}
8384
className="w-full p-2 bg-neutral-700 border border-neutral-600 rounded-md focus:border-blue-500"
8485
/>

0 commit comments

Comments
 (0)