Files
2026-06-06 18:52:20 +03:00

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
}