refactoring in prompt module and renamed jtime into timeconv - more obvious naming

This commit is contained in:
d1nch8g
2026-04-26 16:26:36 +03:00
parent c817b53a14
commit 5947c5c0e8
5 changed files with 73 additions and 100 deletions
-6
View File
@@ -9,9 +9,3 @@ Life-changing AI-assistant.
шутки про отправку дикпиков, специфические мемы
Ошибки:
- упростить системное уведомление, сделать что бы оно было всегда вечером, сделать что бы оно отталкивалось от ачивок пользака, сделать что бы первый системный пинг был через 2 дня после регистрации пользака - и был всегда был про написать самому дорогому человеку комплимент, просто так, потом эти пинги должны варироваться - мб тоже триггерить что-то хорошее, мб узнавать по прогрессу на текущих моментах, говорит о пинге когда не надо...
+3 -3
View File
@@ -10,7 +10,7 @@ import (
"github.com/d1nch8g/jules/chat"
"github.com/d1nch8g/jules/database"
"github.com/d1nch8g/jules/engine/jtime"
"github.com/d1nch8g/jules/engine/timeconv"
"github.com/d1nch8g/jules/engine/user"
"github.com/d1nch8g/jules/search"
"github.com/google/uuid"
@@ -378,7 +378,7 @@ func (a AddNotification) Validate(_ context.Context, rt *Runtime) error {
if a.Content == "" {
return errors.New("add_notification: content is required")
}
if _, err := jtime.ToUTC(a.Time, rt.User.Timezone); err != nil {
if _, err := timeconv.ToUTC(a.Time, rt.User.Timezone); err != nil {
return fmt.Errorf("add_notification: invalid time %q: %w", a.Time, err)
}
if a.Target == "" {
@@ -393,7 +393,7 @@ func (a AddNotification) Validate(_ context.Context, rt *Runtime) error {
}
func (a AddNotification) Execute(ctx context.Context, rt *Runtime) error {
scheduledAt, _ := jtime.ToUTC(a.Time, rt.User.Timezone)
scheduledAt, _ := timeconv.ToUTC(a.Time, rt.User.Timezone)
initiatorID := rt.User.ID
targetID := rt.User.ID
if a.Target != "" {
+68 -89
View File
@@ -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
@@ -1,4 +1,4 @@
package jtime
package timeconv
import (
"fmt"
@@ -1,4 +1,4 @@
package jtime
package timeconv
import (
"testing"