diff options
| -rw-r--r-- | bot.go | 6 | ||||
| -rw-r--r-- | bot_test.go | 70 | ||||
| -rw-r--r-- | char-specific-context.md | 16 | ||||
| -rw-r--r-- | config.example.toml | 2 | ||||
| -rw-r--r-- | main.go | 21 | ||||
| -rw-r--r-- | popups.go | 9 | ||||
| -rw-r--r-- | sysprompts/alice_bob_carl.json | 2 |
7 files changed, 62 insertions, 64 deletions
@@ -76,10 +76,10 @@ func parseKnownToTag(content string) []string { } tag := cfg.CharSpecificContextTag if tag == "" { - tag = "__known_to_chars__" + tag = "@" } - // Pattern: tag + list + "__" - pattern := regexp.QuoteMeta(tag) + `(.*?)__` + // Pattern: tag + list + "@" + pattern := regexp.QuoteMeta(tag) + `(.*?)@` re := regexp.MustCompile(pattern) matches := re.FindAllStringSubmatch(content, -1) if len(matches) == 0 { diff --git a/bot_test.go b/bot_test.go index 1710003..01c3e2c 100644 --- a/bot_test.go +++ b/bot_test.go @@ -299,81 +299,81 @@ func TestParseKnownToTag(t *testing.T) { }{ { name: "feature disabled returns original", - content: "Hello __known_to_chars__Alice__", + content: "Hello @Alice@", enabled: false, - tag: "__known_to_chars__", - wantCleaned: "Hello __known_to_chars__Alice__", + tag: "@", + wantCleaned: "Hello @Alice@", wantKnownTo: nil, }, { name: "no tag returns original", content: "Hello Alice", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Hello Alice", wantKnownTo: nil, }, { name: "single tag with one char", - content: "Hello __known_to_chars__Alice__", + content: "Hello @Alice@", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Hello", wantKnownTo: []string{"Alice"}, }, { name: "single tag with two chars", - content: "Secret __known_to_chars__Alice,Bob__ message", + content: "Secret @Alice,Bob@ message", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Secret message", wantKnownTo: []string{"Alice", "Bob"}, }, { name: "tag at beginning", - content: "__known_to_chars__Alice__ Hello", + content: "@Alice@ Hello", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Hello", wantKnownTo: []string{"Alice"}, }, { name: "tag at end", - content: "Hello __known_to_chars__Alice__", + content: "Hello @Alice@", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Hello", wantKnownTo: []string{"Alice"}, }, { name: "multiple tags", - content: "First __known_to_chars__Alice__ then __known_to_chars__Bob__", + content: "First @Alice@ then @Bob@", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "First then", wantKnownTo: []string{"Alice", "Bob"}, }, { name: "custom tag", - content: "Secret __secret__Alice,Bob__ message", + content: "Secret @Alice,Bob@ message", enabled: true, - tag: "__secret__", + tag: "@", wantCleaned: "Secret message", wantKnownTo: []string{"Alice", "Bob"}, }, { name: "empty list", - content: "Secret __known_to_chars____", + content: "Secret @@@", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "Secret", wantKnownTo: nil, }, { name: "whitespace around commas", - content: "__known_to_chars__ Alice , Bob , Carl __", + content: "@ Alice , Bob , Carl @", enabled: true, - tag: "__known_to_chars__", + tag: "@", wantCleaned: "", wantKnownTo: []string{"Alice", "Bob", "Carl"}, }, @@ -415,13 +415,13 @@ func TestProcessMessageTag(t *testing.T) { name: "feature disabled returns unchanged", msg: models.RoleMsg{ Role: "Alice", - Content: "Secret __known_to_chars__Bob__", + Content: "Secret @Bob@", }, enabled: false, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", - Content: "Secret __known_to_chars__Bob__", + Content: "Secret @Bob@", KnownTo: nil, }, }, @@ -432,7 +432,7 @@ func TestProcessMessageTag(t *testing.T) { Content: "Hello everyone", }, enabled: true, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", Content: "Hello everyone", @@ -443,10 +443,10 @@ func TestProcessMessageTag(t *testing.T) { name: "tag with Bob, adds Alice automatically", msg: models.RoleMsg{ Role: "Alice", - Content: "Secret __known_to_chars__Bob__", + Content: "Secret @Bob@", }, enabled: true, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", Content: "Secret", @@ -457,10 +457,10 @@ func TestProcessMessageTag(t *testing.T) { name: "tag already includes sender", msg: models.RoleMsg{ Role: "Alice", - Content: "__known_to_chars__Alice,Bob__", + Content: "@Alice,Bob@", }, enabled: true, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", Content: "", @@ -471,11 +471,11 @@ func TestProcessMessageTag(t *testing.T) { name: "knownTo already set (from DB), tag still processed", msg: models.RoleMsg{ Role: "Alice", - Content: "Secret __known_to_chars__Bob__", + Content: "Secret @Bob@", KnownTo: []string{"Alice"}, // from previous processing }, enabled: true, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", Content: "Secret", @@ -486,14 +486,14 @@ func TestProcessMessageTag(t *testing.T) { name: "example from real use", msg: models.RoleMsg{ Role: "Alice", - Content: "I'll start with a simple one! The word is 'banana'. (ooc: __known_to_chars__Bob__)", + Content: "I'll start with a simple one! The word is 'banana'. (ooc: @Bob@)", KnownTo: []string{"Alice"}, // from previous processing }, enabled: true, - tag: "__known_to_chars__", + tag: "@", wantMsg: models.RoleMsg{ Role: "Alice", - Content: "I'll start with a simple one! The word is 'banana'. (ooc: __known_to_chars__Bob__)", + Content: "I'll start with a simple one! The word is 'banana'. (ooc: @Bob@)", KnownTo: []string{"Bob", "Alice"}, }, }, @@ -588,7 +588,7 @@ func TestFilterMessagesForCharacter(t *testing.T) { t.Run(tt.name, func(t *testing.T) { testCfg := &config.Config{ CharSpecificContextEnabled: tt.enabled, - CharSpecificContextTag: "__known_to_chars__", + CharSpecificContextTag: "@", } cfg = testCfg @@ -640,7 +640,7 @@ func TestKnownToFieldPreservationScenario(t *testing.T) { // Test the specific scenario from the log where KnownTo field was getting lost originalMsg := models.RoleMsg{ Role: "Alice", - Content: `Alice: "Okay, Bob. The word is... **'Ephemeral'**. (ooc: __known_to_chars__Bob__)"`, + Content: `Alice: "Okay, Bob. The word is... **'Ephemeral'**. (ooc: @Bob@)"`, KnownTo: []string{"Bob"}, // This was detected in the log } diff --git a/char-specific-context.md b/char-specific-context.md index f06fd75..54fa24e 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -12,16 +12,16 @@ Character-Specific Context is a feature that enables private communication betwe ### Tagging Messages -Messages can be tagged with a special string (by default `__known_to_chars__`) followed by a comma-separated list of character names. The tag can appear anywhere in the message content. **After csv of characters tag should be closed with `__` (for regexp to know where it ends).** +Messages can be tagged with a special string (by default `@`) followed by a comma-separated list of character names. The tag can appear anywhere in the message content. **After csv of characters tag should be closed with `@` (for regexp to know where it ends).** **Example:** ``` -Alice: __known_to_chars__Bob__ Can you keep a secret? +Alice: @Bob@ Can you keep a secret? ``` **To avoid breaking immersion, it is better to place the tag in (ooc:)** ``` -Alice: (ooc: __known_to_chars__Bob__) Can you keep a secret? +Alice: (ooc: @Bob@) Can you keep a secret? ``` This message will be visible only to Alice (the sender) and Bob. The tag is parsed by `parseKnownToTag` and the resulting list of character names is stored in the `KnownTo` field of the message (`RoleMsg`). The sender is automatically added to the `KnownTo` list (if not already present) by `processMessageTag`. @@ -44,7 +44,7 @@ The filtered history is then used to construct the prompt sent to the LLM. This Two configuration settings control this feature: - `CharSpecificContextEnabled` – boolean; enables or disables the feature globally. -- `CharSpecificContextTag` – string; the tag used to mark private messages. Default is `__known_to_chars__`. +- `CharSpecificContextTag` – string; the tag used to mark private messages. Default is `@`. These are set in `config.toml` (see `config.example.toml` for the default values). @@ -62,7 +62,7 @@ These are set in `config.toml` (see `config.example.toml` for the default values Alice wants to tell Bob something without Carl knowing: ``` -Alice: __known_to_chars__Bob__ Meet me at the library tonight. +Alice: @Bob@ Meet me at the library tonight. ``` Result: @@ -75,7 +75,7 @@ Result: Alice shares a secret with Bob and Carl, but not David: ``` -Alice: (ooc: __known_to_chars__Bob,Carl__) The treasure is hidden under the old oak. +Alice: (ooc: @Bob,Carl@) The treasure is hidden under the old oak. ``` ### Public Message @@ -116,7 +116,7 @@ So far only json format supports multiple characters. Card example: ``` { - "sys_prompt": "This is a chat between Alice, Bob and Carl. Normally what is said by any character is seen by all others. But characters also might write messages intended to specific targets if their message contain string tag '__known_to_chars__{CharName1,CharName2,CharName3}__'.\nFor example:\nAlice:\n\"Hey, Bob. I have a secret for you... (ooc: __known_to_chars__Bob__)\"\nThis message would be seen only by Bob and Alice (sender always sees their own message).", + "sys_prompt": "This is a chat between Alice, Bob and Carl. Normally what is said by any character is seen by all others. But characters also might write messages intended to specific targets if their message contain string tag '@{CharName1,CharName2,CharName3}@'.\nFor example:\nAlice:\n\"Hey, Bob. I have a secret for you... (ooc: @Bob@)\"\nThis message would be seen only by Bob and Alice (sender always sees their own message).", "role": "Alice", "filepath": "sysprompts/alice_bob_carl.json", "chars": ["Alice", "Bob", "Carl"], @@ -147,6 +147,6 @@ The `KnownTo` field is stored as a JSON array in the database. Existing messages ```toml CharSpecificContextEnabled = true -CharSpecificContextTag = "__known_to_chars__" +CharSpecificContextTag = "@" AutoTurn = false ``` diff --git a/config.example.toml b/config.example.toml index 82aa5f5..1b466eb 100644 --- a/config.example.toml +++ b/config.example.toml @@ -46,5 +46,5 @@ FilePickerExts = "png,jpg,jpeg,gif,webp" # Comma-separated list of allowed file EnableMouse = false # Enable mouse support in the UI # character specific context CharSpecificContextEnabled = true -CharSpecificContextTag = "__known_to_chars__" +CharSpecificContextTag = "@" AutoTurn = true @@ -8,18 +8,15 @@ import ( ) var ( - boolColors = map[bool]string{true: "green", false: "red"} - botRespMode = false - editMode = false - roleEditMode = false - injectRole = true - selectedIndex = int(-1) - currentAPIIndex = 0 // Index to track current API in ApiLinks slice - currentORModelIndex = 0 // Index to track current OpenRouter model in ORFreeModels slice - currentLocalModelIndex = 0 // Index to track current llama.cpp model - shellMode = false - indexLineCompletion = "F12 to show keys help | llm turn: [%s:-:b]%v[-:-:-] (F6) | chat: [orange:-:b]%s[-:-:-] (F1) | toolUseAdviced: [%s:-:b]%v[-:-:-] (ctrl+k) | model: [orange:-:b]%s[-:-:-] (ctrl+l) | skip LLM resp: [%s:-:b]%v[-:-:-] (F10)\nAPI: [orange:-:b]%s[-:-:-] (ctrl+v) | recording: [%s:-:b]%v[-:-:-] (ctrl+r) | writing as: [orange:-:b]%s[-:-:-] (ctrl+q) | bot will write as [orange:-:b]%s[-:-:-] (ctrl+x) | role injection (alt+7) [%s:-:b]%v[-:-:-]" - focusSwitcher = map[tview.Primitive]tview.Primitive{} + boolColors = map[bool]string{true: "green", false: "red"} + botRespMode = false + editMode = false + roleEditMode = false + injectRole = true + selectedIndex = int(-1) + shellMode = false + indexLineCompletion = "F12 to show keys help | llm turn: [%s:-:b]%v[-:-:-] (F6) | chat: [orange:-:b]%s[-:-:-] (F1) | toolUseAdviced: [%s:-:b]%v[-:-:-] (ctrl+k) | model: [orange:-:b]%s[-:-:-] (ctrl+l) | skip LLM resp: [%s:-:b]%v[-:-:-] (F10)\nAPI: [orange:-:b]%s[-:-:-] (ctrl+v) | recording: [%s:-:b]%v[-:-:-] (ctrl+r) | writing as: [orange:-:b]%s[-:-:-] (ctrl+q) | bot will write as [orange:-:b]%s[-:-:-] (ctrl+x) | role injection (alt+7) [%s:-:b]%v[-:-:-]" + focusSwitcher = map[tview.Primitive]tview.Primitive{} ) func main() { @@ -28,12 +28,13 @@ func showModelSelectionPopup() { // Check for empty options list if len(modelList) == 0 { logger.Warn("empty model list for", "api", cfg.CurrentAPI, "localModelsLen", len(LocalModels), "orModelsLen", len(ORFreeModels)) - message := "No models available for selection" - if strings.Contains(cfg.CurrentAPI, "openrouter.ai") { + var message string + switch { + case strings.Contains(cfg.CurrentAPI, "openrouter.ai"): message = "No OpenRouter models available. Check token and connection." - } else if strings.Contains(cfg.CurrentAPI, "api.deepseek.com") { + case strings.Contains(cfg.CurrentAPI, "api.deepseek.com"): message = "DeepSeek models should be available. Please report bug." - } else { + default: message = "No llama.cpp models loaded. Ensure llama.cpp server is running with models." } if err := notifyUser("Empty list", message); err != nil { diff --git a/sysprompts/alice_bob_carl.json b/sysprompts/alice_bob_carl.json index d575f93..b822b99 100644 --- a/sysprompts/alice_bob_carl.json +++ b/sysprompts/alice_bob_carl.json @@ -1,5 +1,5 @@ { - "sys_prompt": "This is a chat between Alice, Bob and Carl. Normally what is said by any character is seen by all others. But characters also might write messages intended to specific targets if their message contain string tag '__known_to_chars__{CharName1,CharName2,CharName3}__'.\nFor example:\nAlice:\n\"Hey, Bob. I have a secret for you... (ooc: __known_to_chars__Bob__)\"\nThis message would be seen only by Bob and Alice (sender always sees their own message).", + "sys_prompt": "This is a chat between Alice, Bob and Carl. Normally what is said by any character is seen by all others. But characters also might write messages intended to specific targets if their message contain string tag '@{CharName1,CharName2,CharName3}@'.\nFor example:\nAlice:\n\"Hey, Bob. I have a secret for you... (ooc: @Bob@)\"\nThis message would be seen only by Bob and Alice (sender always sees their own message).", "role": "Alice", "filepath": "sysprompts/alice_bob_carl.json", "chars": ["Alice", "Bob", "Carl"], |
