From f5d76eb60587564648e9f5084469a27cef5765b8 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 16 Jan 2026 10:11:01 +0300 Subject: Doc: feature concept --- char-specific-context.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 char-specific-context.md (limited to 'char-specific-context.md') diff --git a/char-specific-context.md b/char-specific-context.md new file mode 100644 index 0000000..d56a0cf --- /dev/null +++ b/char-specific-context.md @@ -0,0 +1,31 @@ +say we have a chat (system card) with three or more characters: +Alice, Bob and Carl. +the chat uses /completion endpoint (as oposed to /v1/chat/completion of openai) to the same llm on all chars. +Alice needs to pass info to Bob without Carl knowing the content (or perhaps even that communication occured at all). +Issue is that being in the same chat history (chatBody), llm shares context for each char. +Even if message passed through the tool calls, Carl can see a tool call with the arguments. +If we delete tool calls and their responses, then both Bob and Alice would have to re-request that secret info each time it is their turn, which is absurd. + +concept of char specific context: +let every message to have a `KnownTo` field (type []string); +which could be empty (to everyone) or have speicifc names ([]string{"Alice", "Bob"}) +so when that's character turn (which we track in `WriteNextMsgAsCompletionAgent`, then that message is injected at its proper index position (means every message should know it's index?) into chatBody (chat history). + +indexes are tricky. +what happens if msg is deleted? will every following message decrement their index? so far edit/copy functionality take in consideration position of existing messages in order. +how to avoid two messages with the same index? if Alices letter is send as secret and assigned index: 5. Then Carl's turn we have that secret message excluded, so his action would get also index 5. +Perhaps instead of indexes we should only keep message order by timestamps (time.Time)? + +so we need to think of some sort of tag that llm could add into the message, to make sure it is to be known by that specific target char, some weird string that would not occur naturally, that we could parse: +__known_to_chars__Alice,Bob__ + + +for ex. +Alice: __known_to_chars__Bob__ Can you keep a secret? +Bob: I also have a secret for you Alice __known_to_chars__Alice__ + +tag can be anywhere in the message. Sender should be also included in KnownTo, so we should parse sender name and add them to KnownTo. + +also need to consider user case (as in human chatting with llm). User also can assume any char identity to write the message and ideally the same rules should affect user's chars. + +Again, this is not going to work with openais /v1/chat endpoint since it converts all characters to user/assistant; so it is completion only feature. It also might cause unwanted effects, so we better have an option in config to switch this context editing on/off. -- cgit v1.2.3 From 12be6036902ba9fa1b403310422e5f6f3e6c1875 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 17 Jan 2026 10:23:06 +0300 Subject: Chore: add alice-bob-carl card --- char-specific-context.md | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'char-specific-context.md') diff --git a/char-specific-context.md b/char-specific-context.md index d56a0cf..93c08c5 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -27,5 +27,10 @@ Bob: I also have a secret for you Alice __known_to_chars__Alice__ tag can be anywhere in the message. Sender should be also included in KnownTo, so we should parse sender name and add them to KnownTo. also need to consider user case (as in human chatting with llm). User also can assume any char identity to write the message and ideally the same rules should affect user's chars. +user has "Writing as {char}" (vars: persona and cfg.UserRole) +on persona change we should update tui text view to have atual for that character chat history Again, this is not going to work with openais /v1/chat endpoint since it converts all characters to user/assistant; so it is completion only feature. It also might cause unwanted effects, so we better have an option in config to switch this context editing on/off. + + +alternative approach to the tag string would be to have a judge agent to determine after each message what characters should hae access to it. but it means to make an additional call to llm after each msg. -- cgit v1.2.3 From 8b162ef34f0755e2224c43499218def16d4b6845 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 17 Jan 2026 11:42:35 +0300 Subject: Enha: change textview chat history based on current user persona --- char-specific-context.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'char-specific-context.md') diff --git a/char-specific-context.md b/char-specific-context.md index 93c08c5..c1a7bd6 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -34,3 +34,6 @@ Again, this is not going to work with openais /v1/chat endpoint since it convert alternative approach to the tag string would be to have a judge agent to determine after each message what characters should hae access to it. but it means to make an additional call to llm after each msg. + + +need to update character card loader to support multiple characters -- cgit v1.2.3 From 65b4f01177a38497b0ecb82b09f9dcded55c5acb Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Tue, 3 Feb 2026 11:00:12 +0300 Subject: Doc: char context doc --- char-specific-context.md | 162 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 26 deletions(-) (limited to 'char-specific-context.md') diff --git a/char-specific-context.md b/char-specific-context.md index c1a7bd6..423572b 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -1,39 +1,149 @@ -say we have a chat (system card) with three or more characters: -Alice, Bob and Carl. -the chat uses /completion endpoint (as oposed to /v1/chat/completion of openai) to the same llm on all chars. -Alice needs to pass info to Bob without Carl knowing the content (or perhaps even that communication occured at all). -Issue is that being in the same chat history (chatBody), llm shares context for each char. -Even if message passed through the tool calls, Carl can see a tool call with the arguments. -If we delete tool calls and their responses, then both Bob and Alice would have to re-request that secret info each time it is their turn, which is absurd. +# Character-Specific Context -concept of char specific context: -let every message to have a `KnownTo` field (type []string); -which could be empty (to everyone) or have speicifc names ([]string{"Alice", "Bob"}) -so when that's character turn (which we track in `WriteNextMsgAsCompletionAgent`, then that message is injected at its proper index position (means every message should know it's index?) into chatBody (chat history). +**/completion only feature; won't work with /v1/chat** -indexes are tricky. -what happens if msg is deleted? will every following message decrement their index? so far edit/copy functionality take in consideration position of existing messages in order. -how to avoid two messages with the same index? if Alices letter is send as secret and assigned index: 5. Then Carl's turn we have that secret message excluded, so his action would get also index 5. -Perhaps instead of indexes we should only keep message order by timestamps (time.Time)? +## Overview -so we need to think of some sort of tag that llm could add into the message, to make sure it is to be known by that specific target char, some weird string that would not occur naturally, that we could parse: -__known_to_chars__Alice,Bob__ +Character-Specific Context is a feature that enables private communication between characters in a multi-character chat. When enabled, messages can be tagged with a special marker indicating which characters should "know" about (see) that message. This allows for secret conversations, private information sharing, and roleplaying scenarios where certain characters are not privy to all communications. +(This feature works by filtering the chat history for each character based on the `KnownTo` field associated with each message. Only messages that are intended for a particular character (or are public) are included in that character's view of the conversation.) -for ex. +## How It Works + +### 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).** + +**Example:** +``` Alice: __known_to_chars__Bob__ Can you keep a secret? -Bob: I also have a secret for you Alice __known_to_chars__Alice__ +``` + +**To avoid breaking immersion, it is better to place the tag in (ooc:)** +``` +Alice: (ooc: __known_to_chars__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`. + +Multiple tags can be used in a single message; all mentioned characters are combined into the `KnownTo` list. + +### Filtering Chat History + +When it's a character's turn to respond, the function `filterMessagesForCharacter` filters the full message list, returning only those messages where: + +- `KnownTo` is empty (message is public), OR +- `KnownTo` contains the character's name. + +System messages (`role == "system"`) are always visible to all characters. + +The filtered history is then used to construct the prompt sent to the LLM. This ensures each character only sees messages they are supposed to know about. + +### Configuration + +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__`. + +These are set in `config.toml` (see `config.example.toml` for the default values). + +### Processing Pipeline + +1. **Message Creation** – When a message is added to the chat (by a user or LLM), `processMessageTag` scans its content for the known‑to tag. +2. **Storage** – The parsed `KnownTo` list is stored with the message in the database. +3. **Filtering** – Whenever the chat history is needed (e.g., for an LLM request), `filterMessagesForCharacter` is called with the target character (the one whose turn it is). The filtered list is used for the prompt. +4. **Display** – The TUI also uses the same filtering when showing the conversation for a selected character (see “Writing as…”). + +## Usage Examples + +### Basic Private Message + +Alice wants to tell Bob something without Carl knowing: + +``` +Alice: __known_to_chars__Bob__ Meet me at the library tonight. +``` + +Result: +- Alice (sender) sees the message. +- Bob sees the message. +- Carl does **not** see the message in his chat history. + +### Multi‑recipient Secret + +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. +``` + +### Public Message + +A message without any tag (or with an empty `KnownTo`) is visible to all characters. + +``` +Alice: Hello everyone! +``` + +### User‑Role Considerations + +The human user can assume any character’s identity via the “Writing as…” feature (`cfg.UserRole` and `cfg.WriteNextMsgAs`). When the user writes as a character, the same filtering rules apply: the user will see only the messages that character would see. + +## Interaction with AutoTurn and WriteNextMsgAsCompletionAgent + +### WriteNextMsgAsCompletionAgent + +This configuration variable determines which character the LLM should respond as. It is used by `filterMessagesForCurrentCharacter` to select the target character for filtering. If `WriteNextMsgAsCompletionAgent` is set, the LLM will reply in the voice of that character, and only messages visible to that character will be included in the prompt. + +### AutoTurn + +Normally llm and user (human) take turns writting messages. With private messages there is an issue, where llm can write a private message that will not be visible for character who user controls, so for a human it would appear that llm did not respond. It is desirable in this case, for llm to answer to itself, larping as target character for that private message. + +When `AutoTurn` is enabled, the system can automatically trigger responses from llm as characters who have received a private message. The logic in `triggerPrivateMessageResponses` checks the `KnownTo` list of the last message and, for each recipient that is not the user (or the sender), queues a chat round for that character. This creates a chain of private replies without user intervention. + +**Example flow:** +1. Alice (llm) sends a private message to Bob (llm) (`KnownTo = ["Alice","Bob"]`). +2. Carl (user) sees nothing. +3. `AutoTurn` detects this and queues a response from Bob. +4. Bob replies (potentially also privately). +5. The conversation continues automatically until public message is made, or Carl (user) was included in `KnownTo`. + + +## Cardmaking with multiple characters + +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).", + "role": "Alice", + "filepath": "sysprompts/alice_bob_carl.json", + "chars": ["Alice", "Bob", "Carl"], + "first_msg": "Hey guys! Want to play Alias like game? I'll tell Bob a word and he needs to describe that word so Carl can guess what it was?" +} +``` + +## Limitations & Caveats + +### Endpoint Compatibility -tag can be anywhere in the message. Sender should be also included in KnownTo, so we should parse sender name and add them to KnownTo. +Character‑specific context relies on the `/completion` endpoint (or other completion‑style endpoints) where the LLM is presented with a raw text prompt containing the entire filtered history. It does **not** work with OpenAI‑style `/v1/chat/completions` endpoints, because those endpoints enforce a fixed role set (`user`/`assistant`/`system`) and strip custom role names and metadata. -also need to consider user case (as in human chatting with llm). User also can assume any char identity to write the message and ideally the same rules should affect user's chars. -user has "Writing as {char}" (vars: persona and cfg.UserRole) -on persona change we should update tui text view to have atual for that character chat history +### Tag Parsing -Again, this is not going to work with openais /v1/chat endpoint since it converts all characters to user/assistant; so it is completion only feature. It also might cause unwanted effects, so we better have an option in config to switch this context editing on/off. +- The tag is case‑sensitive. +- Whitespace around character names is trimmed. +- If the tag appears multiple times, all mentioned characters are combined. +### Database Storage -alternative approach to the tag string would be to have a judge agent to determine after each message what characters should hae access to it. but it means to make an additional call to llm after each msg. +The `KnownTo` field is stored as a JSON array in the database. Existing messages that were created before enabling the feature will have an empty `KnownTo` and thus be visible to all characters. +## Relevant Configuration -need to update character card loader to support multiple characters +```toml +CharSpecificContextEnabled = true +CharSpecificContextTag = "__known_to_chars__" +AutoTurn = false +``` -- cgit v1.2.3 From 79861e7c2bc6f2ed95309ca6e83577ddc4e2c63a Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 4 Feb 2026 11:22:17 +0300 Subject: Enha: privateMessageResp with resume --- char-specific-context.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'char-specific-context.md') diff --git a/char-specific-context.md b/char-specific-context.md index 423572b..f06fd75 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -130,6 +130,9 @@ Card example: Character‑specific context relies on the `/completion` endpoint (or other completion‑style endpoints) where the LLM is presented with a raw text prompt containing the entire filtered history. It does **not** work with OpenAI‑style `/v1/chat/completions` endpoints, because those endpoints enforce a fixed role set (`user`/`assistant`/`system`) and strip custom role names and metadata. +### TTS +Although text message might be hidden from user character. If TTS is enabled it will be read. + ### Tag Parsing - The tag is case‑sensitive. -- cgit v1.2.3 From 5e7ddea6827765ac56155577cf7dcc809fe1128c Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Mon, 9 Feb 2026 09:44:54 +0300 Subject: Enha: change __known_by_char tag to @ --- char-specific-context.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'char-specific-context.md') 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 ``` -- cgit v1.2.3