Compare commits
4 Commits
feat/char-
...
feat/serve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e42eb96371 | ||
|
|
46a33baabb | ||
|
|
875de679cf | ||
|
|
3b542421e3 |
@@ -8,6 +8,7 @@ made with use of [tview](https://github.com/rivo/tview)
|
|||||||
- tts/stt (run make commands to get deps);
|
- tts/stt (run make commands to get deps);
|
||||||
- image input;
|
- image input;
|
||||||
- function calls (function calls are implemented natively, to avoid calling outside sources);
|
- function calls (function calls are implemented natively, to avoid calling outside sources);
|
||||||
|
- [character specific context (unique feature)](char-specific-context.md)
|
||||||
|
|
||||||
#### how it looks
|
#### how it looks
|
||||||

|

|
||||||
|
|||||||
1
bot.go
1
bot.go
@@ -874,6 +874,7 @@ out:
|
|||||||
// Process the new message to check for known_to tags in LLM response
|
// Process the new message to check for known_to tags in LLM response
|
||||||
newMsg = *processMessageTag(&newMsg)
|
newMsg = *processMessageTag(&newMsg)
|
||||||
chatBody.Messages = append(chatBody.Messages, newMsg)
|
chatBody.Messages = append(chatBody.Messages, newMsg)
|
||||||
|
stopTTSIfNotForUser(&newMsg)
|
||||||
}
|
}
|
||||||
cleanChatBody()
|
cleanChatBody()
|
||||||
refreshChatDisplay()
|
refreshChatDisplay()
|
||||||
|
|||||||
@@ -113,16 +113,7 @@ When `AutoTurn` is enabled, the system can automatically trigger responses from
|
|||||||
## Cardmaking with multiple characters
|
## Cardmaking with multiple characters
|
||||||
|
|
||||||
So far only json format supports multiple characters.
|
So far only json format supports multiple characters.
|
||||||
Card example:
|
[card example](sysprompts/alice_bob_carl.json)
|
||||||
```
|
|
||||||
{
|
|
||||||
"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"],
|
|
||||||
"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
|
## Limitations & Caveats
|
||||||
|
|
||||||
@@ -131,7 +122,7 @@ 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.
|
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
|
### TTS
|
||||||
Although text message might be hidden from user character. If TTS is enabled it will be read.
|
Although text message might be hidden from user character. If TTS is enabled it will be read until tags are parsed. If message should not be viewed by user, tts will stop.
|
||||||
|
|
||||||
### Tag Parsing
|
### Tag Parsing
|
||||||
|
|
||||||
|
|||||||
12
helpfuncs.go
12
helpfuncs.go
@@ -45,6 +45,18 @@ func refreshChatDisplay() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stopTTSIfNotForUser(msg *models.RoleMsg) {
|
||||||
|
viewingAs := cfg.UserRole
|
||||||
|
if cfg.WriteNextMsgAs != "" {
|
||||||
|
viewingAs = cfg.WriteNextMsgAs
|
||||||
|
}
|
||||||
|
// stop tts if msg is not for user
|
||||||
|
if cfg.CharSpecificContextEnabled &&
|
||||||
|
!slices.Contains(msg.KnownTo, viewingAs) && cfg.TTS_ENABLED {
|
||||||
|
TTSDoneChan <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func colorText() {
|
func colorText() {
|
||||||
text := textView.GetText(false)
|
text := textView.GetText(false)
|
||||||
quoteReplacer := strings.NewReplacer(
|
quoteReplacer := strings.NewReplacer(
|
||||||
|
|||||||
@@ -167,7 +167,6 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *RoleMsg) ToText(i int) string {
|
func (m *RoleMsg) ToText(i int) string {
|
||||||
icon := fmt.Sprintf("(%d)", i)
|
|
||||||
// Convert content to string representation
|
// Convert content to string representation
|
||||||
var contentStr string
|
var contentStr string
|
||||||
if !m.hasContentParts {
|
if !m.hasContentParts {
|
||||||
@@ -193,7 +192,7 @@ func (m *RoleMsg) ToText(i int) string {
|
|||||||
// since icon and content are separated by \n
|
// since icon and content are separated by \n
|
||||||
contentStr, _ = strings.CutPrefix(contentStr, m.Role+":")
|
contentStr, _ = strings.CutPrefix(contentStr, m.Role+":")
|
||||||
// if !strings.HasPrefix(contentStr, m.Role+":") {
|
// if !strings.HasPrefix(contentStr, m.Role+":") {
|
||||||
icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role)
|
icon := fmt.Sprintf("(%d) <%s>: ", i, m.Role)
|
||||||
// }
|
// }
|
||||||
textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, contentStr)
|
textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, contentStr)
|
||||||
return strings.ReplaceAll(textMsg, "\n\n", "\n")
|
return strings.ReplaceAll(textMsg, "\n\n", "\n")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"sys_prompt": "This is a chat between Alice, Bob and Carl. Normally all message are public (seen by everyone). But characters also able to make messages intended to specific targets using '@' tag. Usually tag is provided inside of out of character clause: (ooc: @charname@), but will be parsed if put anywhere in the message.\nTO SEND A PRIVATE MESSAGE:\n- Include a recipient tag in this exact format: @CharacterName@\n- The tag can be anywhere in your message\n- Example: \"Don't tell others this secret. (ooc: @Bob@)\"\n- For immersion sake it is better if private messages are given in context of whispering, passing notes, or being alone in some space: Alice: *leans closer to Carl and whispers* \"I forgot to turn off the car, could you watch my bag for a cuple of minutes? (ooc: @Carl@)\"\n- Only the sender and tagged recipients will see that message.\nRECEIVING MESSAGES:\n- You only see messages where you are the sender OR you are tagged in the recipient tag\n- Public messages (without tags) are seen by everyone.\nEXAMPLE FORMAT:\nAlice: \"Public message everyone sees\"\nAlice: \"Private message only for Bob @Bob@\"\n(if Diana joins the conversation, and Alice wants to exclude her) Alice: *Grabs Bob and Carl, and pulls them away* \"Listen boys, let's meet this friday again!\" (ooc: @Bob,Carl@; Diana is not trustworthy)\nWHEN TO USE:\n- Most of the time public messages (no tag) are the best choice. Private messages (with tag) are mostly for the passing secrets or information that is described or infered as private.\n- Game of 20 questions. Guys are putting paper sickers on the forehead with names written on them. So in this case only person who gets the sticker put on them does not see the writting on it.\nBob: *Puts sticker with 'JACK THE RIPPER' written on it, on Alices forehead* (ooc: @Carl).\nCarl: \"Alright, we're ready.\"\nAlice: \"Good. So, am I a fictional character or a real one?\"",
|
"sys_prompt": "This is a chat between Alice, Bob and Carl. Normally all message are public (seen by everyone). But characters also able to make messages intended to specific targets using '@' tag. Usually tag is provided inside of out of character clause: (ooc: @charname@), but will be parsed if put anywhere in the message.\nTO SEND A PRIVATE MESSAGE:\n- Include a recipient tag in this exact format: @CharacterName@\n- The tag can be anywhere in your message\n- Example: \"(ooc: @Bob@) Don't tell others this secret.\"\n- For immersion sake it is better if private messages are given in context of whispering, passing notes, or being alone in some space: Alice: (ooc: @Carl@) *leans closer to Carl and whispers* \"I forgot to turn off the car, could you watch my bag for a cuple of minutes?\"\n- Only the sender and tagged recipients will see that message.\nRECEIVING MESSAGES:\n- You only see messages where you are the sender OR you are tagged in the recipient tag\n- Public messages (without tags) are seen by everyone.\nEXAMPLE FORMAT:\nAlice: \"Public message everyone sees\"\nAlice: (ooc: @Bob@)\n\"Private message only for Bob\"\n(if Diana joins the conversation, and Alice wants to exclude her) Alice: (ooc: @Bob,Carl@; Diana is not trustworthy)\n*Grabs Bob and Carl, and pulls them away* \"Listen boys, let's meet this friday again!\"\nWHEN TO USE:\n- Most of the time public messages (no tag) are the best choice. Private messages (with tag) are mostly for the passing secrets or information that is described or infered as private.\n- Game of 20 questions. Guys are putting paper sickers on the forehead with names written on them. So in this case only person who gets the sticker put on them does not see the writting on it.\nBob: *Puts sticker with 'JACK THE RIPPER' written on it, on Alices forehead* (ooc: @Carl).\nCarl: \"Alright, we're ready.\"\nAlice: \"Good. So, am I a fictional character or a real one?\"",
|
||||||
"role": "Alice",
|
"role": "Alice",
|
||||||
"filepath": "sysprompts/alice_bob_carl.json",
|
"filepath": "sysprompts/alice_bob_carl.json",
|
||||||
"chars": ["Alice", "Bob", "Carl"],
|
"chars": ["Alice", "Bob", "Carl"],
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table {
|
|||||||
chatList[i] = name
|
chatList[i] = name
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// Sort chatList by UpdatedAt field in descending order (most recent first)
|
||||||
|
for i := 0; i < len(chatList)-1; i++ {
|
||||||
|
for j := i + 1; j < len(chatList); j++ {
|
||||||
|
if chatMap[chatList[i]].UpdatedAt.Before(chatMap[chatList[j]].UpdatedAt) {
|
||||||
|
// Swap chatList[i] and chatList[j]
|
||||||
|
chatList[i], chatList[j] = chatList[j], chatList[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Add 1 extra row for header
|
// Add 1 extra row for header
|
||||||
rows, cols := len(chatMap)+1, len(actions)+4 // +2 for name, +2 for timestamps
|
rows, cols := len(chatMap)+1, len(actions)+4 // +2 for name, +2 for timestamps
|
||||||
chatActTable := tview.NewTable().
|
chatActTable := tview.NewTable().
|
||||||
|
|||||||
1
tools.go
1
tools.go
@@ -330,6 +330,7 @@ func memorise(args map[string]string) []byte {
|
|||||||
Topic: args["topic"],
|
Topic: args["topic"],
|
||||||
Mind: args["data"],
|
Mind: args["data"],
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
if _, err := store.Memorise(memory); err != nil {
|
if _, err := store.Memorise(memory); err != nil {
|
||||||
logger.Error("failed to save memory", "err", err, "memoory", memory)
|
logger.Error("failed to save memory", "err", err, "memoory", memory)
|
||||||
|
|||||||
8
tui.go
8
tui.go
@@ -1120,12 +1120,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// I need keybind for tts to shut up
|
// I need keybind for tts to shut up
|
||||||
if event.Key() == tcell.KeyCtrlA {
|
if event.Key() == tcell.KeyCtrlA && cfg.TTS_ENABLED {
|
||||||
// textArea.SetText("pressed ctrl+A", true)
|
TTSDoneChan <- true
|
||||||
if cfg.TTS_ENABLED {
|
|
||||||
// audioStream.TextChan <- chunk
|
|
||||||
TTSDoneChan <- true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyCtrlW {
|
if event.Key() == tcell.KeyCtrlW {
|
||||||
// INFO: continue bot/text message
|
// INFO: continue bot/text message
|
||||||
|
|||||||
Reference in New Issue
Block a user