package engine import ( "context" "m8sh.su/d/jules/chat" "m8sh.su/d/jules/database" "m8sh.su/d/jules/engine/actions" "m8sh.su/d/jules/engine/hooks" "m8sh.su/d/jules/engine/prompt" "m8sh.su/d/jules/engine/trace" "m8sh.su/d/jules/engine/user" ) func (e *Engine) defaultProcessMessage(ctx context.Context, msg chat.Message) { span := trace.FromMessage(ctx, msg) if len([]rune(msg.Text)) > 4000 { err := e.Chats[msg.Chat].Send(ctx, msg.ID, "ERROR: message too long") if err != nil { span.Error("too long message failed to respond", err) return } span.Info("returned an error on too long message") return } if msg.Text == "" { span.Info("skipping empty message") return } u, err := user.FromMessage(ctx, e.Database, msg, e.ActionLimit) if err != nil { span.Error("failed to get user from message", err) return } span.User(u) err = e.Database.Actions().Log(ctx, u.ID, "user_msg", msg.Text) if err != nil { span.Error("failed to record action", err) return } e.process(ctx, span, u, msg.Chat, msg.Text) } func (e *Engine) defaultProcessNotification(ctx context.Context, notif database.Notification) { span := trace.FromNotification(ctx, notif) u, err := user.FromNotification(ctx, e.Database, notif, e.ActionLimit) if err != nil { span.Error("failed to get user from notificaion", err) return } span.User(u) err = e.Database.Actions().Log(ctx, u.ID, "activated_notificaion", notif.Content) if err != nil { span.Error("failed to record action", err) return } var params []any if notif.RepeatOn != "" { params = append(params, prompt.RepeatOn(notif.RepeatOn)) } e.process(ctx, span, u, database.DatabaseSource, notif.Content, params...) } func (e *Engine) process(ctx context.Context, span *trace.Span, user *user.User, source, content string, params ...any) { for range e.Parameters.LLMRetryAttempts { p := prompt.Build(user, source, content, params...) result, err := e.LLM.Process(ctx, p) span.LLMResponse(result) if err != nil { params = append(params, err) span.Warn("failed to receive LLM response", err) continue } actionSlice, err := actions.Parse(result) if err != nil { params = append(params, err) span.Warn("failed to parse actions received from LLM", err) continue } action := hooks.Collect(user, source, content, params...) if action != nil { actionSlice = append(actionSlice, action) } runtime := &actions.Runtime{ User: user, Database: e.Database, Searcher: e.Searcher, Chats: e.Chats, } var validationFailed bool for _, action := range actionSlice { err = action.Validate(ctx, runtime) if err != nil { params = append(params, err) validationFailed = true } } if validationFailed { span.Actions(actionSlice) span.Warn("failed to validate actions", err) continue } for _, action := range actionSlice { err = action.Execute(ctx, runtime) if err != nil { span.Actions(actionSlice) params = append(params, err) span.Warn("failed to execute actions", err) } } span.Info("successfully processed") return } span.Error("all attempts to process event have failed", lastError(params)) } func lastError(params []any) error { if len(params) == 0 { return nil } err, _ := params[len(params)-1].(error) return err }