@@ -6,19 +6,33 @@ import (
66 "strings"
77 "time"
88
9+ "github.com/mudler/LocalAGI/core/scheduler"
910 "github.com/mudler/LocalAGI/core/types"
10- "github.com/robfig/cron/v3"
1111 "github.com/sashabaranov/go-openai/jsonschema"
1212)
1313
1414const (
15+ RecurringReminderActionName = "set_recurring_reminder"
16+ OneTimeReminderActionName = "set_onetime_reminder"
17+ ListRemindersName = "list_reminders"
18+ RemoveReminderName = "remove_reminder"
19+
20+ // Deprecated: use RecurringReminderActionName or OneTimeReminderActionName
1521 ReminderActionName = "set_reminder"
16- ListRemindersName = "list_reminders"
17- RemoveReminderName = "remove_reminder"
1822)
1923
20- func NewReminder () * ReminderAction {
21- return & ReminderAction {}
24+ func NewRecurringReminder () * RecurringReminderAction {
25+ return & RecurringReminderAction {}
26+ }
27+
28+ func NewOneTimeReminder () * OneTimeReminderAction {
29+ return & OneTimeReminderAction {}
30+ }
31+
32+ // NewReminder returns a RecurringReminderAction for backward compatibility.
33+ // Deprecated: use NewRecurringReminder or NewOneTimeReminder.
34+ func NewReminder () * RecurringReminderAction {
35+ return & RecurringReminderAction {}
2236}
2337
2438func NewListReminders () * ListRemindersAction {
@@ -29,77 +43,122 @@ func NewRemoveReminder() *RemoveReminderAction {
2943 return & RemoveReminderAction {}
3044}
3145
32- type ReminderAction struct {}
46+ type RecurringReminderAction struct {}
47+ type OneTimeReminderAction struct {}
3348type ListRemindersAction struct {}
3449type RemoveReminderAction struct {}
3550
3651type RemoveReminderParams struct {
3752 Index int `json:"index"`
3853}
3954
40- func (a * ReminderAction ) Run (ctx context.Context , sharedState * types.AgentSharedState , params types.ActionParams ) (types.ActionResult , error ) {
41- result := types.ReminderActionResponse {}
55+ func (a * RecurringReminderAction ) Run (ctx context.Context , sharedState * types.AgentSharedState , params types.ActionParams ) (types.ActionResult , error ) {
56+ result := types.RecurringReminderParams {}
4257 err := params .Unmarshal (& result )
4358 if err != nil {
4459 return types.ActionResult {}, err
4560 }
4661
47- // Validate the cron expression
48- parser := cron .NewParser (cron .Second | cron .Minute | cron .Hour | cron .Dom | cron .Month | cron .Dow )
49- _ , err = parser .Parse (result .CronExpr )
62+ task , err := scheduler .NewTask (
63+ sharedState .AgentName ,
64+ result .Message ,
65+ scheduler .ScheduleTypeCron ,
66+ result .CronExpr ,
67+ )
68+ if err != nil {
69+ return types.ActionResult {}, err
70+ }
71+
72+ task .Metadata ["reminder_type" ] = "user_created"
73+
74+ err = sharedState .Scheduler .CreateTask (task )
75+ if err != nil {
76+ return types.ActionResult {}, err
77+ }
78+
79+ return types.ActionResult {
80+ Result : fmt .Sprintf ("Recurring reminder set successfully (ID: %s). Next run: %s" , task .ID , task .NextRun .Format (time .RFC3339 )),
81+ Metadata : map [string ]interface {}{
82+ "task_id" : task .ID ,
83+ "message" : result .Message ,
84+ "next_run" : task .NextRun ,
85+ },
86+ }, nil
87+ }
88+
89+ func (a * OneTimeReminderAction ) Run (ctx context.Context , sharedState * types.AgentSharedState , params types.ActionParams ) (types.ActionResult , error ) {
90+ result := types.OneTimeReminderParams {}
91+ err := params .Unmarshal (& result )
5092 if err != nil {
5193 return types.ActionResult {}, err
5294 }
5395
54- // Calculate next run time
55- now := time .Now ()
56- schedule , _ := parser .Parse (result .CronExpr ) // We can ignore the error since we validated above
57- nextRun := schedule .Next (now )
96+ // Validate the delay parses correctly before creating the task
97+ _ , err = scheduler .ParseDuration (result .Delay )
98+ if err != nil {
99+ return types.ActionResult {}, fmt .Errorf ("invalid delay format, expected a duration like '30m', '2h', '1d', '1d12h': %w" , err )
100+ }
58101
59- // Set the reminder details
60- result .LastRun = now
61- result .NextRun = nextRun
62- // IsRecurring is set by the user through the action parameters
102+ task , err := scheduler .NewTask (
103+ sharedState .AgentName ,
104+ result .Message ,
105+ scheduler .ScheduleTypeOnce ,
106+ result .Delay ,
107+ )
108+ if err != nil {
109+ return types.ActionResult {}, err
110+ }
111+
112+ task .Metadata ["reminder_type" ] = "user_created"
63113
64- // Store the reminder in the shared state
65- if sharedState . Reminders = = nil {
66- sharedState . Reminders = make ([] types.ReminderActionResponse , 0 )
114+ err = sharedState . Scheduler . CreateTask ( task )
115+ if err ! = nil {
116+ return types.ActionResult {}, err
67117 }
68- sharedState .Reminders = append (sharedState .Reminders , result )
69118
70119 return types.ActionResult {
71- Result : "Reminder set successfully" ,
120+ Result : fmt . Sprintf ( "One-time reminder set in %s (at %s, ID: %s)" , result . Delay , task . NextRun . Format ( time . RFC3339 ), task . ID ) ,
72121 Metadata : map [string ]interface {}{
73- "reminder" : result ,
122+ "task_id" : task .ID ,
123+ "message" : result .Message ,
124+ "next_run" : task .NextRun ,
74125 },
75126 }, nil
76127}
77128
78129func (a * ListRemindersAction ) Run (ctx context.Context , sharedState * types.AgentSharedState , params types.ActionParams ) (types.ActionResult , error ) {
79- if sharedState .Reminders == nil || len (sharedState .Reminders ) == 0 {
130+ tasks , err := sharedState .Scheduler .GetAllTasks ()
131+ if err != nil {
132+ return types.ActionResult {}, err
133+ }
134+
135+ if len (tasks ) == 0 {
80136 return types.ActionResult {
81137 Result : "No reminders set" ,
82138 }, nil
83139 }
84140
85141 var result strings.Builder
86142 result .WriteString ("Current reminders:\n " )
87- for i , reminder := range sharedState .Reminders {
143+
144+ for i , task := range tasks {
88145 status := "one-time"
89- if reminder . IsRecurring {
146+ if task . ScheduleType == scheduler . ScheduleTypeCron || task . ScheduleType == scheduler . ScheduleTypeInterval {
90147 status = "recurring"
91148 }
92- result .WriteString (fmt .Sprintf ("%d. %s (Next run: %s, Status: %s)\n " ,
149+
150+ result .WriteString (fmt .Sprintf ("%d. %s (Next run: %s, Status: %s, ID: %s)\n " ,
93151 i + 1 ,
94- reminder .Message ,
95- reminder .NextRun .Format (time .RFC3339 ),
96- status ))
152+ task .Prompt ,
153+ task .NextRun .Format (time .RFC3339 ),
154+ status ,
155+ task .ID ))
97156 }
98157
99158 return types.ActionResult {
100159 Result : result .String (),
101160 Metadata : map [string ]interface {}{
102- "reminders " : sharedState . Reminders ,
161+ "tasks " : tasks ,
103162 },
104163 }, nil
105164}
@@ -111,31 +170,42 @@ func (a *RemoveReminderAction) Run(ctx context.Context, sharedState *types.Agent
111170 return types.ActionResult {}, err
112171 }
113172
114- if sharedState .Reminders == nil || len (sharedState .Reminders ) == 0 {
173+ tasks , err := sharedState .Scheduler .GetAllTasks ()
174+ if err != nil {
175+ return types.ActionResult {}, err
176+ }
177+
178+ if len (tasks ) == 0 {
115179 return types.ActionResult {
116180 Result : "No reminders to remove" ,
117181 }, nil
118182 }
119183
120184 // Convert from 1-based index to 0-based
121185 index := removeParams .Index - 1
122- if index < 0 || index >= len (sharedState . Reminders ) {
186+ if index < 0 || index >= len (tasks ) {
123187 return types.ActionResult {}, fmt .Errorf ("invalid reminder index: %d" , removeParams .Index )
124188 }
125189
126- // Remove the reminder
127- removed := sharedState .Reminders [index ]
128- sharedState .Reminders = append (sharedState .Reminders [:index ], sharedState .Reminders [index + 1 :]... )
190+ task := tasks [index ]
191+ err = sharedState .Scheduler .DeleteTask (task .ID )
192+ if err != nil {
193+ return types.ActionResult {}, err
194+ }
129195
130196 return types.ActionResult {
131- Result : fmt .Sprintf ("Removed reminder: %s" , removed . Message ),
197+ Result : fmt .Sprintf ("Removed reminder: %s" , task . Prompt ),
132198 Metadata : map [string ]interface {}{
133- "removed_reminder " : removed ,
199+ "removed_task_id " : task . ID ,
134200 },
135201 }, nil
136202}
137203
138- func (a * ReminderAction ) Plannable () bool {
204+ func (a * RecurringReminderAction ) Plannable () bool {
205+ return true
206+ }
207+
208+ func (a * OneTimeReminderAction ) Plannable () bool {
139209 return true
140210}
141211
@@ -147,25 +217,39 @@ func (a *RemoveReminderAction) Plannable() bool {
147217 return true
148218}
149219
150- func (a * ReminderAction ) Definition () types.ActionDefinition {
220+ func (a * RecurringReminderAction ) Definition () types.ActionDefinition {
151221 return types.ActionDefinition {
152- Name : ReminderActionName ,
153- Description : "Set a reminder for the agent to wake up and perform a task based on a cron schedule. Examples: '0 0 * * *' (daily at midnight), '0 */2 * * *' (every 2 hours), '0 0 * * 1' (every Monday at midnight)" ,
222+ Name : RecurringReminderActionName ,
223+ Description : "Set a recurring reminder for the agent to wake up and perform a task on a cron schedule. The reminder will keep repeating . Examples: '0 0 * * *' (daily at midnight), '0 */2 * * *' (every 2 hours), '0 0 * * 1' (every Monday at midnight)" ,
154224 Properties : map [string ]jsonschema.Definition {
155225 "message" : {
156226 Type : jsonschema .String ,
157227 Description : "The message or task to be reminded about" ,
158228 },
159229 "cron_expr" : {
160230 Type : jsonschema .String ,
161- Description : "Cron expression for scheduling (e.g. '0 0 * * *' for daily at midnight). Format: 'second minute hour day month weekday'" ,
231+ Description : "Cron expression for scheduling (e.g. '0 0 * * *' for daily at midnight). Format: 'minute hour day month weekday'" ,
232+ },
233+ },
234+ Required : []string {"message" , "cron_expr" },
235+ }
236+ }
237+
238+ func (a * OneTimeReminderAction ) Definition () types.ActionDefinition {
239+ return types.ActionDefinition {
240+ Name : OneTimeReminderActionName ,
241+ Description : "Set a one-time reminder for the agent to wake up and perform a task after a delay. The reminder triggers only once and is then automatically removed. Use this when asked to do something 'in X minutes/hours/days'. Examples: '30m' (30 minutes), '2h' (2 hours), '1d' (1 day), '1d12h' (1.5 days), '2h30m' (2.5 hours)" ,
242+ Properties : map [string ]jsonschema.Definition {
243+ "message" : {
244+ Type : jsonschema .String ,
245+ Description : "The message or task to be reminded about" ,
162246 },
163- "is_recurring " : {
164- Type : jsonschema .Boolean ,
165- Description : "Whether this reminder should repeat according to the cron schedule (true) or trigger only once (false )" ,
247+ "delay " : {
248+ Type : jsonschema .String ,
249+ Description : "How long to wait before triggering. Use Go duration format: '30m' (30 minutes), '2h' (2 hours), '1d' (1 day), '1d12h' (1.5 days), '2h30m' (2.5 hours )" ,
166250 },
167251 },
168- Required : []string {"message" , "cron_expr" , "is_recurring " },
252+ Required : []string {"message" , "delay " },
169253 }
170254}
171255
0 commit comments