140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/d1nch8g/jules/chat"
|
|
"github.com/d1nch8g/jules/database"
|
|
"github.com/d1nch8g/jules/engine/actions"
|
|
"github.com/d1nch8g/jules/engine/prompt"
|
|
"github.com/d1nch8g/jules/engine/trace"
|
|
"github.com/d1nch8g/jules/engine/user"
|
|
)
|
|
|
|
func (e *Engine) defaultProcessMessage(ctx context.Context, msg chat.Message) {
|
|
span := trace.FromMessage(ctx, msg)
|
|
|
|
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, &database.Action{
|
|
UserID: u.ID,
|
|
ExecutedAt: time.Now(),
|
|
Payload: actions.Raw(actions.UserAction{
|
|
Type: "user_message",
|
|
Content: 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, &database.Action{
|
|
UserID: u.ID,
|
|
ExecutedAt: time.Now(),
|
|
Payload: actions.Raw(actions.UserAction{
|
|
Type: "activated_notificaion",
|
|
Content: notif.Content,
|
|
}),
|
|
})
|
|
if err != nil {
|
|
span.Error("failed to record action", err)
|
|
return
|
|
}
|
|
|
|
e.process(ctx, span, u, database.DatabaseSource, notif.Content)
|
|
}
|
|
|
|
func (e *Engine) process(ctx context.Context, span *trace.Span, user *user.User, source, content string) {
|
|
var errs []error
|
|
|
|
for range e.Parameters.LLMRetryAttempts {
|
|
p := prompt.Build(user, source, content, errs...)
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, e.LLMResponseTimeout)
|
|
result, err := e.LLM.Process(ctx, p)
|
|
cancel()
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
span.LLMResponse(result)
|
|
span.Warn("failed to receive LLM response", errs...)
|
|
continue
|
|
}
|
|
|
|
actionSlice, err := actions.Parse(result, user.Timezone)
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
span.LLMResponse(result)
|
|
span.Warn("failed to parse actions received from LLM", errs...)
|
|
continue
|
|
}
|
|
|
|
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 {
|
|
errs = append(errs, err)
|
|
validationFailed = true
|
|
}
|
|
}
|
|
|
|
if validationFailed {
|
|
span.Actions(actionSlice)
|
|
span.Warn("failed to validate actions", errs...)
|
|
continue
|
|
}
|
|
|
|
for _, action := range actionSlice {
|
|
err = action.Execute(ctx, runtime)
|
|
if err != nil {
|
|
span.Actions(actionSlice)
|
|
errs = append(errs, err)
|
|
span.Warn("failed to execute actions", errs...)
|
|
}
|
|
|
|
err := e.Database.Actions().Log(ctx, &database.Action{
|
|
UserID: user.ID,
|
|
ExecutedAt: time.Now(),
|
|
Payload: actions.Raw(action),
|
|
})
|
|
if err != nil {
|
|
span.Actions(actionSlice)
|
|
errs = append(errs, err)
|
|
span.Warn("failed to record action", err)
|
|
}
|
|
}
|
|
|
|
span.Info("successfully processed")
|
|
return
|
|
}
|
|
|
|
span.Error("all attempts to process event have failed", errors.Join(errs...))
|
|
}
|