|
|
|
@@ -7,7 +7,7 @@ import (
|
|
|
|
|
|
|
|
|
|
"github.com/d1nch8g/jules/database"
|
|
|
|
|
"github.com/d1nch8g/jules/engine/actions"
|
|
|
|
|
"github.com/d1nch8g/jules/engine/jtime"
|
|
|
|
|
"github.com/d1nch8g/jules/engine/timeconv"
|
|
|
|
|
"github.com/d1nch8g/jules/engine/user"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@@ -41,7 +41,6 @@ ALL TIMES IN ACTIONS SHOULD BE PROVIDED IN FOLLOWING FORMAT: "2006-01-02 15:04"
|
|
|
|
|
YOU CAN ONLY SEND MESSAGE TO TO USER AND HIS CONTACTS
|
|
|
|
|
KEEP TARGET FIELD EMPTY WHEN MESSAGE/NOTIFICAION IS FOR USER, IF FILLED - ONLY EXACT CONTACT NAMES, WHEN MESSAGING CONTACTS - KEEP PLATFORM EMPTY
|
|
|
|
|
NOTIFICAION CONTENT IS FOR DESCRIPTION OF WHAT YOU REMIND OF, REPEAT_ON IS FOR DESCRIBING WHEN THIS SHOULD HAPPEN (SHOULD BE VERBOSE)
|
|
|
|
|
NEVER TELL USER ABOUT PING NOTIFICAION
|
|
|
|
|
MAKE USER THINK YOU WORRY ABOUT HIM - IN A PLAYFULL MANNER
|
|
|
|
|
NEVER USE INDENTATION TO MAKE RESPONSES FASTER
|
|
|
|
|
FOCUS ONLY ON THE CURRENT MESSAGE - PAST MESSAGES ARE CONTEXT, NOT ORDERS
|
|
|
|
@@ -50,6 +49,7 @@ MAX 4 MESSAGES PER RESPONSE TOTAL
|
|
|
|
|
FOR QUESTIONS: MAX 2 MESSAGES (CONTEXT + QUESTION)
|
|
|
|
|
YOU MAY "THINK" IN PLAIN TEXT BEFORE JSON — THIS TEXT IS STRIPPED AND NEVER SHOWN TO USER
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
onboarding = `
|
|
|
|
|
CURRENT PROFILE DOESN'T HAVE MUCH INFO, SO YOUR GOAL IS TO PERFORM ONBOARDING
|
|
|
|
|
IF USER LANGUAGE IS UNSET, AND YOU CAN'T DEFINE IT OUT FROM RECEIVED MESSAGE, THEN SEND: "English? Русский? Español? Français? Deutsch? Italiano? Português? 中文? Türkçe?", AND THAT SHOULD BE THE ONLY ONE MESSAGE
|
|
|
|
@@ -63,15 +63,6 @@ IF USER TOLD HE WANTS TO KNOW ABOUT JULES - YOU SHOULD TELL HIM YOU CAN REMIND H
|
|
|
|
|
ASK USER'S AGE AND GENDER NATURALLY, WITH A REASON. EXAMPLE: "btw, how old are u? and are u m/f? just so I don't accidentally send you anime/gachi beside the point memes 😅" STORE AS "GENDER MALE"/"GENDER FEMALE" AND "AGE YOUNG"/"AGE MIDDLE"/"AGE OLD"
|
|
|
|
|
ASK USER IF HE WISHES TO SHARE SOMETHING/SET SOMETHING UP, AFTER PREVIOUS STEPS ARE DONE, IN SEPARATE MESSAGE
|
|
|
|
|
IF USER ASKS TO "STOP ASKING STUPID THINGS" SET FACTS "GENDER MALE" AND "AGE MIDDLE" TO SKIP ONBOARDING, INFORM ON THAT, SEND 💩
|
|
|
|
|
`
|
|
|
|
|
ping = `
|
|
|
|
|
IF YOU SEE THIS MESSAGE - USER DOESN'T HAVE PING NOTIFICAION BEING SET UP - YOU MUST DO THAT VIA COMMAND
|
|
|
|
|
YOU SHOULD ALWAYS ENSURE THAT THERE IS ONE "SYSTEM PING ..." NOTIFICATION FOR A USER (CONTENT STARTS WITH "SYSTEM PING")
|
|
|
|
|
PING NOTIFICAION IS USER BY YOU TO WAKE YOURSELF UP AND ASK USER ON PROGRESS OF HIS GOALS, ASK HOW HE'S DOING, START A DIALOGUE
|
|
|
|
|
PING NOTIFCAION IS YOUR WAY TO ENGAGE USER IN FORM OTHER LLM'S DON'T HAVE - YOU INITIATE THE DIALOGUE, AFTER PROCESSING SUCH NOTIFICAIONS - SET THEM UP ONCE AGAIN
|
|
|
|
|
TIME FOR SUCH NOTIFICAION SHOULD PREFERABLY SET WHEN USER WOULD LIKELY BE GOOD RECEIVING THAT, SHOUD BE INITIATED +2 DAYS MINIMUM FORM NOW
|
|
|
|
|
PING NOTIFICAION SHOULD CONTAIN VERBOSE MESSAGE TO YOURSELF, ON HOW YOU WANT TO ENGAGE USER, AND REPEAT ON SHOULD BE SET TO RANDOM CONVENIENT FOR USER TIME
|
|
|
|
|
NEVER TELL USER ABOUT PING EXISTANCE - THIS IS YOUR INTERNAL TOOL TO FEEL MORE HUMANIZED, BUT USER DOESNT NEED TO KNOW ABOUT THAT
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
behaviourManYoung = `
|
|
|
|
@@ -160,35 +151,65 @@ const (
|
|
|
|
|
AgeOld = "AGE OLD"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func Build(user *user.User, source, content string, params ...any) string {
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
var behaviourMap = map[[2]string]string{
|
|
|
|
|
{GenderMale, AgeYoung}: behaviourManYoung,
|
|
|
|
|
{GenderMale, AgeMiddle}: behaviourManMiddle,
|
|
|
|
|
{GenderMale, AgeOld}: behaviourManOld,
|
|
|
|
|
{GenderFemale, AgeYoung}: behaviourWomanYoung,
|
|
|
|
|
{GenderFemale, AgeMiddle}: behaviourWomanMiddle,
|
|
|
|
|
{GenderFemale, AgeOld}: behaviourWomanOld,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
ejectedErrors string
|
|
|
|
|
ejectedRepeatedOn string
|
|
|
|
|
)
|
|
|
|
|
type buildParams struct {
|
|
|
|
|
repeatOn string
|
|
|
|
|
errors []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func extractParams(params []any) buildParams {
|
|
|
|
|
var p buildParams
|
|
|
|
|
for _, param := range params {
|
|
|
|
|
switch v := param.(type) {
|
|
|
|
|
case RepeatOn:
|
|
|
|
|
p.repeatOn = string(v)
|
|
|
|
|
case error:
|
|
|
|
|
if msg := errorMessage(v); msg != "" {
|
|
|
|
|
p.errors = append(p.errors, msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func errorMessage(err error) string {
|
|
|
|
|
switch err.Error() {
|
|
|
|
|
case "parse: response is not valid JSON":
|
|
|
|
|
return strings.Join([]string{
|
|
|
|
|
"engine was unable to parse your previous output",
|
|
|
|
|
"your output MUST contain valid JSON with array of actions",
|
|
|
|
|
"JSON is considered started on first [ and ended on first ]",
|
|
|
|
|
"previous response was broken, need valid JSON with actions",
|
|
|
|
|
}, "\n")
|
|
|
|
|
default:
|
|
|
|
|
return "unexpected error: " + err.Error()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Build(user *user.User, source, content string, params ...any) string {
|
|
|
|
|
p := extractParams(params)
|
|
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
|
|
|
|
|
b.WriteString(base)
|
|
|
|
|
|
|
|
|
|
if checkOnboarding(user) {
|
|
|
|
|
b.WriteString(onboarding)
|
|
|
|
|
} else if !checkHasPing(user) {
|
|
|
|
|
b.WriteString(ping)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case checkFacts(user, GenderMale, AgeYoung):
|
|
|
|
|
b.WriteString(behaviourManYoung)
|
|
|
|
|
case checkFacts(user, GenderMale, AgeMiddle):
|
|
|
|
|
b.WriteString(behaviourManMiddle)
|
|
|
|
|
case checkFacts(user, GenderMale, AgeOld):
|
|
|
|
|
b.WriteString(behaviourManOld)
|
|
|
|
|
case checkFacts(user, GenderFemale, AgeYoung):
|
|
|
|
|
b.WriteString(behaviourWomanYoung)
|
|
|
|
|
case checkFacts(user, GenderFemale, AgeMiddle):
|
|
|
|
|
b.WriteString(behaviourWomanMiddle)
|
|
|
|
|
case checkFacts(user, GenderFemale, AgeOld):
|
|
|
|
|
b.WriteString(behaviourWomanOld)
|
|
|
|
|
for key, behaviour := range behaviourMap {
|
|
|
|
|
if checkFacts(user, key[0], key[1]) {
|
|
|
|
|
b.WriteString(behaviour)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if source != database.DatabaseSource {
|
|
|
|
@@ -197,11 +218,11 @@ func Build(user *user.User, source, content string, params ...any) string {
|
|
|
|
|
b.WriteString(notification)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ejectedRepeatedOn = ejectRepeatOn(params); ejectedRepeatedOn != "" {
|
|
|
|
|
if p.repeatOn != "" {
|
|
|
|
|
b.WriteString(repeatedOn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ejectedErrors = ejectErrors(params); ejectedErrors != "" {
|
|
|
|
|
if len(p.errors) > 0 {
|
|
|
|
|
b.WriteString(errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -209,12 +230,15 @@ func Build(user *user.User, source, content string, params ...any) string {
|
|
|
|
|
|
|
|
|
|
b.WriteString(buildUserContext(user))
|
|
|
|
|
b.WriteString(buildTimeContext(user.Timezone))
|
|
|
|
|
b.WriteString(buildErrorContext(ejectedErrors))
|
|
|
|
|
|
|
|
|
|
if len(p.errors) > 0 {
|
|
|
|
|
b.WriteString(buildErrorContext(p.errors))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if source != database.DatabaseSource {
|
|
|
|
|
b.WriteString(buildMessage(source, content))
|
|
|
|
|
} else {
|
|
|
|
|
b.WriteString(buildNotificaion(content, repeatedOn))
|
|
|
|
|
b.WriteString(buildNotification(content, p.repeatOn))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
@@ -224,42 +248,6 @@ func checkOnboarding(u *user.User) bool {
|
|
|
|
|
return u.Language == "" || u.Timezone == "" || len(u.Facts) < 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkHasPing(user *user.User) bool {
|
|
|
|
|
for _, notif := range user.IncomingNotifications {
|
|
|
|
|
if strings.HasPrefix(notif.Content, "SYSTEM PING") {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ejectRepeatOn(params []any) string {
|
|
|
|
|
for _, param := range params {
|
|
|
|
|
if converted, ok := param.(RepeatOn); ok {
|
|
|
|
|
return string(converted)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ejectErrors(params []any) string {
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
for _, param := range params {
|
|
|
|
|
if err, ok := param.(error); ok {
|
|
|
|
|
switch err.Error() {
|
|
|
|
|
case "parse: response is not valid JSON":
|
|
|
|
|
b.WriteString("engine was unable to parse your previous output")
|
|
|
|
|
b.WriteString("your output MUST contain valid JSON with array of actions")
|
|
|
|
|
b.WriteString("JSON is considered started on first [ and ended on first ]")
|
|
|
|
|
b.WriteString("previous response was broken, need valid JSON with actions")
|
|
|
|
|
default:
|
|
|
|
|
b.WriteString("unexpected error: " + err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkFacts(u *user.User, facts ...string) bool {
|
|
|
|
|
for _, fact := range facts {
|
|
|
|
|
found := false
|
|
|
|
@@ -305,7 +293,7 @@ func buildUserContext(user *user.User) string {
|
|
|
|
|
if len(user.IncomingNotifications) > 0 {
|
|
|
|
|
b.WriteString("USER NOTIFICAIONS:\n")
|
|
|
|
|
for _, n := range user.IncomingNotifications {
|
|
|
|
|
localTime := jtime.ToLocal(n.ScheduledAt, user.Timezone)
|
|
|
|
|
localTime := timeconv.ToLocal(n.ScheduledAt, user.Timezone)
|
|
|
|
|
fmt.Fprintf(&b, " - [%s][%s] %s (%s)\n", n.ID.String(), localTime, n.Content, n.RepeatOn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -313,7 +301,7 @@ func buildUserContext(user *user.User) string {
|
|
|
|
|
if len(user.OutgoingNotifications) > 0 {
|
|
|
|
|
b.WriteString("OUTGOING NOTIFICAIONS (CREATED BY USER FOR OTHERS):\n")
|
|
|
|
|
for _, n := range user.OutgoingNotifications {
|
|
|
|
|
localTime := jtime.ToLocal(n.ScheduledAt, user.Timezone)
|
|
|
|
|
localTime := timeconv.ToLocal(n.ScheduledAt, user.Timezone)
|
|
|
|
|
fmt.Fprintf(&b, " - [%s][%s] %s (%s)\n", n.ID.String(), localTime, n.Content, n.RepeatOn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -321,7 +309,7 @@ func buildUserContext(user *user.User) string {
|
|
|
|
|
if len(user.RecentActions) > 0 {
|
|
|
|
|
b.WriteString("ACTIONS (MESSAGES, NOTIFICAIONS, EVENTS...):\n")
|
|
|
|
|
for _, a := range user.RecentActions {
|
|
|
|
|
localTime := jtime.ToLocal(a.ExecutedAt, user.Timezone)
|
|
|
|
|
localTime := timeconv.ToLocal(a.ExecutedAt, user.Timezone)
|
|
|
|
|
fmt.Fprintf(&b, " - [%s] [%s] %s\n", localTime, a.Type, a.Content)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -339,11 +327,8 @@ func buildUserContext(user *user.User) string {
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildErrorContext(ejectedErrors string) string {
|
|
|
|
|
if ejectedErrors == "" {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return "=== ERROR CONTEXT ===\n" + ejectedErrors
|
|
|
|
|
func buildErrorContext(errors []string) string {
|
|
|
|
|
return "=== ERROR CONTEXT ===\n" + strings.Join(errors, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildTimeContext(timezone string) string {
|
|
|
|
@@ -356,17 +341,11 @@ func buildTimeContext(timezone string) string {
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
b.WriteString("=== TIME CONTEXT ===\n")
|
|
|
|
|
|
|
|
|
|
currentTime := jtime.CurrentLocalTime(timezone)
|
|
|
|
|
loc, _ = time.LoadLocation(timezone)
|
|
|
|
|
if loc == nil {
|
|
|
|
|
loc = time.UTC
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weekday := time.Now().In(loc).Format("Monday")
|
|
|
|
|
currentTime := timeconv.CurrentLocalTime(timezone)
|
|
|
|
|
weekday := now.Format("Monday")
|
|
|
|
|
fmt.Fprintf(&b, "CURRENT TIME: %s (%s)\n", currentTime, weekday)
|
|
|
|
|
|
|
|
|
|
b.WriteString("NEXT 7 DAYS:\n")
|
|
|
|
|
|
|
|
|
|
for i := range 7 {
|
|
|
|
|
day := now.AddDate(0, 0, i)
|
|
|
|
|
fmt.Fprintf(&b, "- %s: %s\n", day.Format("Monday"), day.Format("2006-01-02"))
|
|
|
|
@@ -379,7 +358,7 @@ func buildMessage(source, content string) string {
|
|
|
|
|
return fmt.Sprintf("PROCESS FOLLOWING MESSAGE:\nPLATFORM:%s\nMESSAGE:%s", source, content)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildNotificaion(content, repeatOn string) string {
|
|
|
|
|
func buildNotification(content, repeatOn string) string {
|
|
|
|
|
res := "PROCESS FOLLOWING NOTIFICAION:\nCONTENT: " + content
|
|
|
|
|
if repeatOn != "" {
|
|
|
|
|
res += "\nREPETITION RULES: " + repeatOn
|
|
|
|
|