143 lines
3.3 KiB
Go
143 lines
3.3 KiB
Go
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
|
|
}
|