#ad_discordbot (Fork of Fork of xNul's bot)
1 messages · Page 9 of 1
With Variable assignment, you probably could
in combination with the Flows system
so you are automatically deleting the NPC and all the info about it aftewards?
hmm
If I don't store the NPC information in a lorebook, then information in history about it would be gone as soon as it's no longer within the context window.
You can already use responses from the LLM as input for tag parameters.
The LLMs response can also trigger Tag triggers.
assigning what to what ?
You can also use the LLMs response already as a variable such as {llm_0} is most recent response
so the llm output will trigger facts?
A fact is something you predefined, yes?
I got you, so it's about the context window
Or is it something the LLM defined that we're just going to assume is a fact
yes
I guess both
Yes, you can make a tag definition that can be triggered only by the LLMs response
if I would extract facts from LLM responses, I will count them as facts
search_mode: llm means it only searches the LLM's response for matching triggers
and update the db
what do you think is a better approach llm only, user only, or both
Depends entirely on the situation / use case
I need to stop chatting about this or I'll never finish enhancing Regenerate and Continue 😛
Currently, Regen / Continue are only working one time before failing - thats a bug.
Second, they are currently based entirely on the most recent interaction without any ability to manipulate
What I am doing is making is so that the message you right click on to use the App Command, will be found in the History and used as the input
And also, not error when using it repeatedly
you mean inseting a predefined message when clicking regenerate?
Stop asking questions and let the man cook! 😄
currently, you right click on any message and hit Regenerate - what happens is that it uses the current value of History as input.
The message content isn't doing anything to sway it
ok ok
What needs to happen is to jump back in History (enhancement)
But importantly, also not break after one use (fix the bug)
you mean editing the history?
With this enhancement, it will also have the framework to edit history
wait, Regenerate doesn't go back in history by default?
Regenerate will not edit the history, it will temporarily jump in history without modifying
It goes back in history by default - to the most recent exchange
go cook, and make it delicious chef
What I am going to do is simulatneously collect message.id in lists at the same time as History
When the appcommand is used, the message.id is accessible.
I can match the message.id and use it's index to fetch the appropriate history
retrieving another msg from history?
The message
This will be much more effective than trying to match the full text string in the history
why more effective?
You're here to sabotage me 😛
You ask the LLM a question and it says Yes.
10 messages later you ask a question and it says Yes.
You decide to regenerate a response which was Yes.
Where is it going to find it in history eh?
And it's going to take more computer to match a 1500 character text in history than by a 13 digit number
GO COOK US SOMETH
Hey there 👀
OI!
That's a lot of processing for possibly random results (in my experiences 7b can't make such decisions very well)
if your only scope is characters, then creating a character object that can store attributes like "teacher" flag.
then you could use character.get_is('teacher') -> bool
These tags could be extracted by an llm, or through more strict logic.
I think extracting would be more reliable because asking "is john a teacher" and john is not a teacher, the llm might say yes, because you mentioned teacher in the first place giving it a bias.
Yup yup, the best solution!
If the llm started saying "lol" to every message, and you hit regenerate, it wouldn't know which instance if you were only selecting by text.
character.get_is('teacher') -> bool
this is about checking data that's already saved?
sure
my suggestion was having an llm extract data it observes rather than asking if the llm thinks something
I didn't get it 🙂
Conversation:
John: okay classroom, today we're discussing my starchy nemesis, the tuber!
based on the following conversion, describe john:
- Possibly a teacher
- doesn't like potatos.
saving these facts might work better than asking an llm each time
🥔
asking llm: do you think john is a teacher?
I feel like the result could be pretty random.
because the question is kind of guiding it towards saying yes by mentioning he could be a teacher.
on the other hand, if the llm is just asked to collect info, it's not being guided by the question, and Might™️ give more truthful information
Should try that out with the logit probabilities in tgwi
see what it thinks
Possibly a teacher doesn't like potatos.
so this is the llm response?
yes sir, potatos
It's my response,
I don't know what an llm would say, will start one up
I mean you meant llm response?
yes, ideally
yup yup
Even multishot prompting can risk contaminating your data
giving it examples, the llm might think those examples directly apply to John and repeat them
I've run into this problem with the tools experiments
send_long_message() function will now return a list of message IDs it sent.
Then, the message ID from user message and the LLM Response(s) will be appended to lists.
Simultaneously, a 1 or 0 will be appended to another list representing whether it was saved to history or not
yes I run into those problems too
providing examples will cause those examples to bleed in the actual answer
and it confuses the LLM
specially if there is words that are the same in an example and also in the prompt
so many lists!
be careful with that.
Might forget a .pop somewhere and get uneven lists and missaligned indexes.
If possible, I would have one list that contains custom classes that hold the message id information as well as if it has been written to history.
Maybe even a reference to the actual text in history!
for example
one example having classroom and the prompt having classroom too, the llm will link the example's classroom to another topic which will lead the llm to reference or put focus on that unrelated topic and the llm will get confused and the reply wlll be weird
I can take a stab at that if you push it
If I have one accidental pop somewhere - just need to patch it 🙂
I have faith in this
I wouldn't include the memory lookup thing in the info extration
: |
maybe some different wording :P
had to mention evil for it to recognise that
so you used the tool right
Solar10B is THE AI
And a random llama2-7B model also is pretty confident.
Great!
That might be more viable than I was thinking!
But he could also be a student for the same reasons.
like I said, be careful what you ask 😸
that's a good answer
any idea on planing? it will be good to mix facts with priorities and make aplan then execute on it somehow without the llm getting confused
this is not the llm answer per say, but the llm answer will make you imagine that the llm is thinking
are we going to school or the mall what this human is talking about? I am tired of dummy humans that can't talk LLM language
John is a teacher at morning and a student at night, lol
Maybe the students switch roles and teach him how to properly prepare a dish with potatoes and change his outlook on life
and a chef in weekends
low confidence in answers, but interesting a weekday was chosen
lol
I don't have much experience with planning and self reflection.
The closest I've gone is internal thoughts.
Text inside (parentheses would be the bot thinking to itself and not visible to the user)
.. this often led to the bot getting depressed. interesting.
It would generate lots of self doubts and question the user about everything internally.
it was for science!
in that case...
yea haha
I tested that, and after a number of chats the internal thoughts becomes either repetitive or useless or both, basically not having any real effects on the conversatoin
one way to solve that is to NOT include internal thoughsts
I mean yes prompting them BUT not saving them
they will be like a temporary cache
but not sure how to optimally do that
Mhmm,
I originally did this as
X's internal thought:
But perhaps the llm interprets this as an entirely different character.
So I later began using () so its in the same line.
at the time I was limited to 2048tokens,
so temporary was not an issue haha
oh, it doesnt mix it up with the character reply?
Yea,
originally, it was x: reply then run another llm generation x's thoughts: reply
Sometimes through random chance it would generate thoughts before reply.
And there was a periodic loop that just generated thoughts randomly after nothing has been sent in a while.
maybe to help start a new conversation
maybe this can work
- first making a todo list
- the LLM will think about the todo list, it will generate ###Thinking and ###Reply
- remove the ###Thinking section and saving only ###Reply
what would the point of ### thinking be if it doesnt influence the next generation?
is paranthees finetuned/trined into the LLM to act like internal thoughts?
my goal was to give the bot it's own unspoken ideas/emotions that would stay present throughout and change as things go
yeah I don''t know how to do it right, since the plan is supposed to be for long term
no, llms just intuit that pretty well because it happens in roleplay.
like () in roleplay would be used to signify it's outside of the character's world, a narration or meta commentary on something to the other human
oh okk
making things coherant is tricky
imagine it like going back in time, You are interferring with history, that event of history should change, but not change to a point of being confusing or glitchy
the event still happens even that You are interfering by being a time traveler
but the event and everything still happens in a way that makes sense
that how I think about it, the plan is a foreign thing to the current scene, so it might mess things up
i'm confused, going back in history as a time traveler?
Also I found a screenshot of the internal monologue being used by the LLM properly.
I think it's funny it has it's own different opinion than what it's saying.
Which then influences later replies to take on a sarcasm like tone I believe is the best way to put it.
"of course not! im just a robot."
that was an example, a metaphore
that adds quite a touch
I am kind of working on something like this.
with real time voice conversation with an LLM, it is seeing my messages in the past.
So it has to do time travel actually!
but it got it coherent by generating slow and taking it's time to let the user input messages
did you prompt it to sarcasm"?
I love seeing it start updating it's reply, steering it in a new direction as new information comes into the past
I don't think so.
I think it had a pretty simple character prompt
would have to revisit that with more modern models and see how it goes.
This one often got confused who it was
that might be what am describing, do you have to handle a mess of the user interupting the llm etc..?
give the poor llm an identity already
don't let it suffer 🥹
it's not that it didn't have one, Just older 7b models could get confused by roles and switch them often.
#screenshots message
there's a little conversation here about it.
woow it's correcting itself
and updating on real time
this is running at 6tps on purpose
Yes, I love that it is coherent when reading back the chat log!
The text you see on the right is the live results from the llm.
That is the actual prompt being fed in
The llm is able to predict what you're saying and start forming a reply ahead of time
like me starting to say "why did it"
The llm already understood there's some technical issue, because I talked about voice chats
Also, I mark incomplete messages with --
and in the system prompt tell if that this means the user/bot is still typing
You are talking to me in a voice channel.
The user will end unfinished sentences end with: --
This indicates they are still talking while you reply.
Keep it short.
is pretty much it
but do you regenerate each time or how?
how you mark the LLM response as permanant
by LLM response I mean as far as it went, since it's live
I think I should do some regeneration.
maybe hide all the llm responses and regenerate the reply and have another prompt compare if it should say that or continue on the last track.
what were you doing before?
I check if the sentence has ended using regex, and set a "finished" flag
Just continuing the chat.
the llm makes the decision of flagging it as finished?
you set a special token for that?
What I mean is,
because chat from the user is broken up, the llm might overlook something.
So i should also hide the llm responses and treat the last few user messages as a new input and add that to the history.
I store the messages in a dataclass
most importantly:
user, text, typing:bool, spoken:bool, trimmed:bool
typing indicates the message is still being added to.
Spoken means the AI ran it through TTS already
Trimmed means the message will be in a permanent state of typing, because the bot cut off the user midway.
and this influences how the string is formated
so you get bunch of chunks like this then you add them to the queue/fix them then add them?
Message:
user: str
text: str
typing: bool
trimmed: bool
spoken: bool
History:
msgs: list[Message]
current_typing: dict[username: Message]
When user input comes in, it checks if there is an existing message in history.current_typing[user]
if so, Message.text += chunk.
If that chunk ends the sentence, Message.mark_complete()
if more sentences are present in the chunk, add new messages to history.msgs.
And if last message is incomplete, add it to current_typing!
this is all the prompt you used? that set?
This way of storing messages is something I want to eventually implement with Ad_discordbot
the history class would have a render method that outputs the message dict for tgwi.
and easier manipulation of messages/sorting!
yes
nani!
was using a smaller prompt so I could ensure everything was being formatted correctly with verbose mode
<s> Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
You are talking to me in a voice channel.
The user will end unfinished sentences end with: --
This indicates they are still talking while you reply.
Keep it short.
### Response:
# Voice chat:
User: "Hello this is a test!"
AI: "Hey there!"
AI: " How can I help?"
User: " I am a second message."
AI: "No problem, let's see--"
AI: "--if we can<continue here>
I added this spacing in for you
the messages above are the ones in memory, the last one is a guide so the LLM tries to continue its last
AI: "What's the plan if we can, though?"
this was the final output for me with the llama2-7b model I was expirementing with earlier
with <continue here> was better
huh interesting, are you using chat mode or default?
with <continue here>
without
I tried on https://labs.perplexity.ai/
so idk what they set it on
oh the model is llama-3-8b-instruct
I didn't pay attention
that looks like it uses chat instruct, which is different from executing the raw prompt
yes, for instruct, you probably want to tell it <continue here>
else it will start creating a new response like the issue I was having.
I am sending the prompt to v1/completions
all it does is continue the next token ^^
you can see that on one response it uses capital while the other not
lower case/upper case as a start
mhm, because it's getting a prompt like this internally:
### instruction
continue this:
AI: "No problem, let's see--"
AI: "--if we can
### Response:
prompt inseption?
compared to doing it yourself as:
### instruction
continue this:
AI: "No problem, let's see--"
### Response:
AI: "--if we can
no, this is useful for chat based assistants such as chatgpt
you don't have to think about prompting for text completion, rather you can talk to it like a person!
Do you know of the setting in the TGWUI for "always start your reply with X"
yes
if the model is censored, it might be beneficial to make the ### response section always start with "Sure!"
that's all :)
I force the LLM to start with something when using llama.cpp
here, I force the llm to start with its last reply
the cli
mhm mhm
Sure! but still no
some models do that , lol
meta, they gotta cover what left of their reputation
mhm, it makes sense, censor the instruct model that businesses would use.
But still release the base version without censoring (at least they did with llama2)
because now it's the people's fault if they finetune it
why did they require the name TOS thing?
for llama3
what if someone finetuned to do harm
I imagine so people don't steal credit or maybe use that to bypass tos?
By saying "well you can't prove this is your model"
that's the thing, if you modify your car in a really dangerous way and end up in an accident, I don't think you can sue the car manufacturer saying "why didn't you prevent this"
a bloody surgery
yeah but people will do people stuff
and one of people stuff is not understanding AI
mhms
google seems done (AI wise), they mess things up lately a lot
I've been using DDG for the past few years, it works perfectly fine for all my research needs!
People just need to know of alternatives.
Actually a few days ago, they were having network issues possibly?
and I had to use Google a few times, was surprized to see the Ai feature
I havent been following that much
yes bcz of bing
I see I see
Interesting, didn't know they were so closely tied together
DDG is based on bing search engine/API or something
I found it weird when I knew that
and it gave me some uncomfortable feelings : /
mhm, odd for a search engine meant to not spy on you
DDG and microsoft,,, what ? how that works?
my internal thoughts was back then ^
and I find it weird why mc would allow that.. maybe solely for financial reasons?
I assume they pay mc millions if not billions to keep search up and runing
🫡
@terse folio Easter egg found
Lol
I removed the line of code that tests interaction.author
But interesting to see it is actually being called!
Ahh I see, that's for the regenerate command, yup yup
You can remove that line 😸
Making more progress... there are things I did not take into consideration with this approach 😐
Like what?
i'm working on some new classes for history, if you have any ideas that might make it easier
Please don't make me merge them 😭
it's in its own file, i would take my time
Before I started, I tried moving History into a separate module but couldn't figure out how to also access bot_settings from there
initialize the class in the main file, just define the class somewhere else
Anyway - I'll have to reconsider my approach to assigning variables for the Flows system, because with this approach (linking message IDs to history prompts / LLM responses), there are internal messages that obviously do not have message.ids
Furthermore
I can't just initialize the message.id lists as empty while loading history
maybe start it with None, None for now
I'm going to use "I" on all this because I am making a mess of things 😛
ohno
So - I could additionally write and load these lists to the log files.
Which I think will be ideal solution.
The floating problem would then be Flows system
I kinda ran outta time but what’s wierd is that I’m fairly certain I sliced the history and updated the payload param… but it still regenerated based on most recent exchange
the Regenerate param may be hardcoded, maybe TGWUI remembers or something
May need to manipulate history then just send it like a normal payload rather than with regenerate
yea, i'd send new payloads
The logic of trying to match the message IDs to the history even though the history could persist between sessions wireless message IDs resets this may still work out if searching for matches in reverse and then slicing the index in reverse or something
I think the idea has merit but I may be biting something too big to swallow
how would you match the ids to what's in history?
The way I have it there’s basically 3 lists.
History (user, llm)
Message IDs (user, llm)
History tracker (if message was stored to history or not)
If a message is not saved to history, it appends 1 to the tracker.
I made a function that will filter the Message IDs list based on the tracker list
It is correctly slicing the history based on where it matches the current message ID to the index in the stored message ID list
But this is on a fresh history. Anyway, it’s not possible for the filtered message.id list to be longer than the history list. But history will be longer if bot loads up with existing history
I think I can resolve the issue of Flows by just inserting dummy IDs when it won’t be sending a message to channel
Idk. Too complicated
I think what I’ll just fix Regen and Continue for now without trying to enhance them, need a mental break from this idea
I see, yea that is complicated
hope my update will make things easier to work on
I just need to figure out saving with recursive references
I think I can make something happen if I just dial it down a bit
- I could abandon the idea of caring about messages not saved in history.
- instead of collecting message.id for both user and llm resp after the send_long_message() I could instead just capture the user msg ID
the point of this idea is to be able to edit history by right clicking a message in the discord window and using an AppCommand
will that update a message in the middle or start a new branch at that point?
I could have it send ephemeral msg if it’s used on a bot reply, and say it’s intended only for user msgs
Well the first step is finding the corresponding msg in history
I can do that ^^
also would per channel saves be fine? I think it would be easier to implement
I was thinking of an AppCommand like edit history - after you do it if it finds the match in history, it asks a followup on what to relace it with
you could also listen for the on_edit event
I’m trying to make this less complicated lol
trust me, it will be easy!
history = historymanager.get_for(channel.id)
entry = history.get(message.id)
if entry:
entry.update_text(message.content)```
would be something like this
Also I think I have a solution for importing history to tgwi
just save 2 files
if the user opens the history in tgwui and continues chat there, it won't match the other log.
Maybe a converter could be written.
Or the new history could be ignored and only use what the bot knows.
The multi chan history logs are all saved with a _multiple-channels suffix
If loading in multi can mode could be a good idea to try filtering on that
what I mean is, there could be a normal version of the logs that only contain internal/visible for the user to view in tgwi if they wish
Or filtering it out if boooting up in single chan mode
Ultimately both multi and single mode will need the extra logs to hold data such as message_ids
so no translation is needed if the bot will be the only one really using those files
Even if there’s just 2 channels having conversations, the merged version would be all over the place
hmm hmmm
new idea!
Store uuids for each object,
Each message, each history branch.
And save the references to eachother as uuids that will be loaded to the real objects on load.
this isn't a json save or anything, just printed the objects converted to dicts.
interesting, everything is stored as a pair
how does chat mode handle multiple users talking to the bot?
On the todo list is that experimental feature I discussed before
Currently, name1 is set as the user. So for each interaction the message log appears to the character as they’re chatting with that user
I see I see,
when I do chat with bots, I create the prompt myself which lets me insert my own names
i can add a history renderer for the different modes if you ever want to try that
I think it could be beneficial to use the server name as name1, and prefix each message with the username
I tested it briefly and it seemed to help
I plan to have a few things like that wrapped into a “server_mode” setting thats true/false
I’m going to avoid toying with History any further until you finish up what you’re doing 😛
In the meantime, will just fix Regen and Continue to actually work
How difficult will it be to implement the context injection idea? Looking forward to trying that out
the role could be set to context, a custom category to filter by in a future history handler!
using that you could delete all instances of context, store them in another list or do whatever
Maybe tonight... lemme see how fast I can work...
Will feel good to accomplish something instead of spinning my wheels
since visible does this to the text i'll stick to using internal for custom rendering!
almost done with it - had to take a break
testing
Works.
Note that this does not do do anything for instruct mode
@keen palm Those tags are now available for use
😸
In the process of installing / trying this out
https://github.com/TMElyralab/MusePose
well, it probably wont work. Guy in video says 24gb vram minimum >:\
Welp, so far I can at least generate one of those snazzy openpose videos lol.
Now to see if I can infer the actual video gen
Are they described in dict_tags?
Yes, below state in the LLM section
I also added them in the wiki
So I'm able to render musepose video except only @ 200 x 350 it seems 😛
[[prefix_context: 'Bob is a piece of toast']] -- Correct?
yes, the quotes are optional though
it can detect strings / floats / bool (true/false), lists, dictionaries pretty reliably
lol
I wrote it correct and then it told me a different solution that borked it.
My test succeeded because I had 2 things. It is failing on just one.
Pushed the bug fix
Oh yeah, push it.
Ahem, 'scuse me
Doesn't look like /reset_conversation is getting rid of history
per-channel history?
Right
Had to merge into his code revamp
I have the fix, pretty sure.
Pushed the bug fix
I noticed something didn't look right today when I was working on the other thing - but I did not commit that change.
I think I inadvertently rolled back something when merging our 2 branches together. The channel ID was not matching the dictionary value unless it was first converted to a string. This was addressed but apparently was reverted
Alright, reloading. LEt's see if I can break something else
So far so good
Now to redo all the tags in my gamemaster character file
Oh, actually, continue might not be working. The bot repeated the same paragraph like 8 times, and nothing new was saved in history
Regenerate is actually working now right?
I only had a moment to test it earlier and it seemed to be working, repeatedly. Continue looked like it was not.
Haven't tried yet
It is working slightly, though history still isn't changed to reflect the new generation. Also, there's a bit of an issue with regeneration whenever the initial message was split across multiple responses in discord
If I can finish what I started and make it work, it should handle the split messages as well
I’m going to wait for Reality to wrap up what they’re working on regarding History, though
it's all in a separate file, feel free to push anything to dev
Ok! I’ve had some time to think about it and I think all issues with the id pairing can be resolved.
There’s just likely to be a few headaches between now and then is all
i'd keep the dev branch up to date.
It seems when the same things are worked on, it causes the most issues.
when merging branches on Git, it likes to merge code that doesn't conflict, but that doesn't necessarily make it correct
It’s deleted at the moment ao if/when either of us has commits just open again
If we have another big merge situation I’ll do a more thorough file comparison rather than just reviewing conflicts
I think those merges need to be done manually.
Because a few lines of code got added from the old branch that used outdated variables that were removed in the new
yup
Would incorporating delete/replace last response commands be easier than sprucing up regenerate?
This system would make it easy to manipulate history in any way
May add those commands beforehand though
history saving/loading works ^^
History(
manager='9b6e9b7f1fde4988b1bdc8cfc9c9e5b1',
id=100,
_last={},
_items=[<Message (user) by 'Kat': 'Hey!', Replies(1)>,
<Message (assistant) by 'Skye': 'Hello Kat'>],
uuid='3e14fea7f35f478e821fb3f438b092f2')
Is this a new logging format?
this is an internal reprosentation of the classes used for history
Got it
you can query different formats through render functions
{'internal': ['Hey!', 'Hello Kat'], 'visible': ['Hey!', 'Hello Kat']}
( I know this isn't correct yet)
Kat: Hey!
Skye: Hello Kat
Looking forward to playing with that
Is this just user inputs?
those are some confusing lines!
ohno
Does this mean you were switching the whole self.session_history every time someone sends a message in a new channel!
I am having some success with this method I tried lately:
- Already having a structured kinda big yaml data about characters, places, and other details etc...
- the python script checks what places and characters has been mentioned in the past like 6 chats and puts them into a
characters = []andplaces = []lists - those two lists >
charactersandplacesgets put to a prompt that says something along the lines ofThis is the yaml data #I put yaml data here. Extract all informations about the following characters #characters-list and the following places #places-list, all the yaml data is included in prompt too, the prompt also instructs and foces the llm to make connections betweencharacters and other characters&places and other places&places and characterslike a how you Reality doing the similarity search using verctor dbs, but I used an llm for that, so the prompt have like this instructionYou should make connect a character with other relavent characters, places to places, and characters to places and put the results into sections, in this format ##Characters, ##Places - the result of step 3. is each section have "sub sections" of character A B and Places X Y Z etc..
- that result/analysis will be included in the user prompt, pre prompt, so when the LLM (main chat) answers it will have quite a lot of juiced, narrowed down and clear informations about the relavent characters/places to the current discussion
- the user prompt gets cleaned, so ONLY the user prompt gets saved to the chat history, not all that bloted amount of info, if all that info is saved to the history chat it will cause an insane amount of repetitions in the future and it will cause the llm to be dumb or confused
@terse folio thank you, for the similarity/relavency concepts you explained to me when we were discussing about the vector db
so using this, in theory the yaml/json data can be huge or infinite but you still will be able to narrow it down to only relavent info to the last 6 chats or whatever number you choose
if you have noticed any loopholes or bugs in this method please tell me, I am still testing things out
it's a bit much to wrap my head around at the moment, working on history stuff for now.
I'll try to take a deeper look tomorrow!
Btw!
you might benefit from the history code i'm working on too.
I'll put it on github as it's own module with some examples of how to use it.
And would be happy to get collaboration from others there to improve it!
just explain it in a clear language on github please 😅
Yup
we need to follow the typehints, otherwise they'll confuse us even more!
@halcyon quarry
@halcyon quarry question about this:
This is used in regeneration/continuation.
Was this meant to trim from the right to regenerate a specific message in the middle of the history?
test for sanity check
i'll keep the original behavior for now as im unsure what it does
No 😛
Yea, I remember now ^^
it dumps the channel ids + internals to the file
The cp_list / collected prompts is not implemented yet
You can implement it later because it gets saved anyway with the new history 😸
Its for the event that manage_history is called before LLM GEN rather than after, with only a user prompt and no last resp
Once it finally does receive both items, if multiple user msgs accumulated, join them as one prompt
I see
For the new behaviors where it may delay responding, or cancel a response to handle multiple user prompts at once
Hopefully we can do something similar with the new History 🙂
I would setup an event for "wait_to_gather_msgs" in each history so the bot can wait maybe a second before starting to generate.
If no one starts typing in that time, generate
All you need to do is watch the on_typing event.
Grab the history for that channel and set the flag
The cancelling part had not yet been worked out quite yet
Didn’t try 😛 (not yet)
I just wanted to get that cp list and function started to remind me to make whatever changes I do work for it
Got it in there right before you started blessing this project I think
i've kinda redone everything over the past few hours :P
I love it
You are a breathe of fresh air my friend
Don’t overwork yourself though lol
You were on when I thought I was on too late and now I’m up too early bc son didn’t sleep well, and here you are both times 😛 And your in Maryland?
Yea, sometimes just in that mood to get something done
Been there. Still there but now burdened with a few other responsibilities lol
why would you cancel a response?
I think some people want to have the bot behave like a human instead of a computer
I want to offer that option
A setting will make such an action more or less likely to happen
the filenames are timestamps by default so 0 will be sorted as the most recent
unique_id is datetime now when it is assigned
I had to modify tgwui load_history function bc it errored on the per channel log format
It also made sense to modify the get latest history function
Im afk forget exactly what the other function was called
Nothing anyone is actually using I don’t think
Er
This function replaces {user_2} variable with 3rd most recent user message, {llm_1} with second most recent LLM resp, etc
Those 2 variables are very useful for the Flows
The last one {history_X} I added in case anyone thought of something useful to do with it
It works similarly but returns the pair
makes sense, okay
This is something that needs to be reworked a bit if using the id pairing
Also the history no longer is stored in pairs, but it can be converted using the renderer
or history.last_exchange()
which I could add more of to query different indexes.
But the way that works is taking the last message (assumed to be assistant) and getting it's parent message, which is usually the one before it.
The message that trigged the llm
for now ill comment that function out and start getting the bot running
How I was planning to address the issue of when it generates from a Flow (prompt is internal and not from on_message) was to store that text value instead of the message.id, while adding another number type to the history tracker like 2 = internal prompt
If the function tris to get a message of type 2, it gets that text string instead of expecting a message.if
after a few fixes such as circular imports, the bot has awoken!
As in, actually appending the text string to the msg ids list
you can leave the message id empty, or set it to 0
also you can set a custom role on messages, like "flows" so you can select by them later
should history change with character changes?
I think so right?
Since tgwi saves logs to different files per characters, I will follow that
There is a setting in the config file to do one or the other
what's it called? how does it change how the tgwi save function works?
Min….
In change char task, if its per chan history it will reset history everywhere every time. If single history mode it will check the config setting and honor it
Its the setting with values “keep” and “new”
I just experienced that haha,
it deleted my saves as I switched characters
Until if when we figure out per channel characters, it doesn’t make any sense to keep history I don’t think
Although I suppose we could leave that decision to the users
How about we just print a warning in the CMD window if it is per channel histories and the mode is to keep history
i thought that was the intended behavior
to keep history across multiple channels
so it picks up where it left off for everyone
I’m on the road but I think I may have arbitrarily just forced history to reset but could just as easily keep it
The condition is in change care task
One nice thing about having a jailbroken phone is being able to use Discord with Apple CarPlay
you mean the bot will act like "typing..." then it will not send the msg?
It may generate, then wait to show "typing..." then wait to send message
depending on how loose the responsiveness setting is
responsiveness is going to be something like
1.0 default (bot is computer, like now)
0.0 is busy human that may be afk for a bit depending on the max delay setting
Woo history works over reloads now!
had a small bug
Red line = character change yes?
and switching characters, it remembers last conversations before the restart
for "new" + character change, i'm thinking of adding a function to create a new history file and unload the old ones instead of deleting them
er
because when you run clear that triggers it to clear the file as well
because I'm sure people would want to keep old chats
yes
Yes
that looks cool, simulating human behaviors
it's with the new history manager
when you update the classes it will push updates to the files.
If you clear the history in the bot it will clear them on disk
nice touch
I'm excited to get around to the new behaviors, but I think we need to polish up our history management first
Understand that in current version (Main), history from files is never deleted
yes, i'll have to look at the original code, what it was clearing
I think it just deleted the whole self.session_history
It clears the variables - however, the history is written to file on every interaction
So clearing the variables does not delete anything
okay I see, i'll replace .clear() with .fresh()
this will create a new instance of history and start a new file
after I write that function
One thing the bot lacks but I may never get around to, is better settings management via slash commands.
Using command groups, etc
that .clear() function could be nice if we do like a /history command with a number of other things within it
Or, yet another config option
this is where stuff used to get reset
but it doesn't change the "unique_id"
so what does it do with conflicting file names?
i tested out the branch
bot_history.load_bot_history()
When switching characters, it's okay to unload the internal history and not change the ID because you're switching to another folder anyway and it creates a new file
But if I run /reset_conversation
it forgets temporarly, but still writes to the same logs
and if I had load history "keep" it would probably load everything that was reset the next run
I mentioned earlier that I had arbitrarily forced reset on per-channel mode, on character changes, regardless of the setting
But that we should change this to instead honor it - but print a warning
im just trying to figure out the desired behavior
Current
# Set history
if bot_history.autoload_history and (bot_history.change_char_history_method == 'keep' and source != 'reset'):
bot_history.load_bot_history()
else:
if source == 'reset':
await bot_history.reset_session_history(ictx)
else:
await bot_history.reset_session_history()
How we should do it...
erm... hmm...
Ok it is more confusing than I thought lol
you are trying to manage history in a away without deleting it?
what's the current issue?
There's a setting in config.yaml to keep or refresh the chat history on character changes
some people want to keep the history even though new character is coming
By refresh I mean it starts a new log, does not delete the old
using the old char's history with a new char?
Using /reset_conversation causes the previous history to be deleted, actually
make it a setting, people can choose to turn it on or off
It is a setting 😛
but either ways the history shoudnlt be deletwd
We're discussing how it isn't working for per-channel history
a char/bot that interact with mutilple channels?
Where have you been?
We're all popping champagne and throwing confetti here the past few days because the bot now maintains chat history for all separate channels
I could say it again maybe
so the problem is that's hard to manage the reset history feature for multiple channels ?
there's no need to reset anything.
I missinterpreted the code
It's not a big problem, this is small potatoes
let John at ease man
Thank you for spelling "potatoes" correctly!
Ideally:
On character change:
- regardless of
per_channel_historysettings:change_char_history_method: keep- the chat log / variables will not change in any way.change_char_history_method: new- a new chat log is initialized (empty), all variables are cleared, etc.
On reset_conversation:
per_channel_history: true
- a new chat log is initialized, but only the history for that channel is cleared in the new log file.
- variables pertaining to that channel are cleared.
per_channel_history: false
- new chat log initialized, all variables cleared
Right. Currently, /reset_conversation will overwrite the previous chat log, as soon as new history is put in.
understood - definitely an oversight
I could work it out but hopefully Reality will just do it so I don't have to 🙏
They've been pretty good at dissecting other spaghetti
got reset_conversation working. had an issue with how I defined __copy__
Maybe you should push what you have to some branch so I can get a sneak peek
time to do some merging
woo almost 1000 additions :P
pushed to dev
per channel, and single works fine
👍
I can say one thing for sure - you are using so much code that is a foreign language to me.
But that's not too big of a problem
haha, my original plan was to write as little as possible.
Hence why I used libraries like data class and data class json to automate so much.
But had to make little workarounds.
I plan to make this it's own library that will get some cleaning and updates to be more generalized.
But that would only touch the internals.
For example it's common in python to mark a function/variable as private by prefixing it with "_"
So by only exposing certain tools outside the class, theres less ways to break it
I did not try running it yet - if dataclasses_json is not installed, what happens?
use the requirements.txt
there should be an update script that runs pip install -r requirements.txt
as well as pip install -U pip (before)
Is Ctrl+C a standard shutdown combination that you are just adding awareness for?
Thats typically the shortcut for Copy (obviously) and I could imagine folks trying to copy text from the window using that
Ctrl C is used to interrupt terminals, yes it's common
ok, just checking
If you have text selected on windows terminal and use ctrl c, it will copy it
but on linux it wont, just kills it
So I got in the habit of using ctrl x (cut) to copy out of terminals
Are there some things that happen when pressing Ctl+C verses hitting X?
As in, functions that save data etc?
yea, it runs save history incase you want to do it immediately.
I added a save interval to the settings, which you could set to 0 if you wish.
But it will always have your file saved within 30 seconds, even if you dont add any new inputs
It does so by creating an event for the future and checking if it has been saved between that time, if not, do it now and move the time forward
With the larger classes, a lot more data is being stored compared to before
SQL would be great for chat logs, but I'm not up for reorganizing that haha.
The data is all going to new files in /internal/ ?
(aside from history log in /logs/
Anyway, there are some functions similar to sql.
History.search(lambda m: m.name == Reality)
Would return all messages by me for example
they are both saved to the same dir in logs
you could also use this to search by any other attribute, like ID, or time, or time delta since then, ...
How big of a PITA would it be to save the extra data somewhere in internal?
I think the 'per-channel' history logs are about as invasive as I want to get to native TGWUI files
If you're using keep history, it will use the same amount of files as there are channels multiplied by the characters you use ofc
(And x2 for the class storage)
But I dont imagine most people to be running the bot on 100s of channels
But if they do, saving data will be more efficient by saving only the active channels!
in smaller chunks
Are you saying that there is now a separate chat log file for each channel?
I could look into it later, maybe a simple path.replace to move it
yes, it was easier to implement then putting everything in one from this approach
and I think it's more worth it in the long run
You could load up any channel/character chat into tgwui and view it without the bot.
I see that the channel ID is not being converted to a string.
With the new handling, there's no issues 'getting' it?
It is being converted to a string internally,
At first I had encoders and decoders convert it to string and back to int when saving to json.
But then I was thinking about generalizing the code to other uses, so make it a str.
And that worked out for the best anyway, because now the channel Id is (channelid+charactername+chatmode)
Same matching the log files so that you get a new history class and new tgwi history
when per_channel_history: False, is it still collecting both internal/visible?
Is it adding empty strings to visible in both cases? In any?
it collects whatever llm_gen outputs
which currently is filtering visible for the tts audio file. I didnt look too much into it
it acts the same with or without channels, because secretly "single channel" is just channel "0"
last_user_message, last_assistant_message
Dya think last_bot_message is more suitable?
yeah its only used 2x Ill update that to be uniform with the other variables using 'bot'
Sure,
Also I use bot_message and user_mesaage when referencing the history items.
I named them this way not to be confused with 'message' of the discord.Message class
could also call them hmessages, that's what I renamed the class to, to make it more clear whats what
else:
if source == 'reset':
# create a clone with same settings but empty, and replace it in the manager
bot_history.get_history_for(ictx.channel.id).fresh().replace()
else:
logging.warning('This originally cleared all history. No need, just unload maybe?')
# bot_history.clear_all_history()
bot_history.unload_history()
What does unload_history() do?
it just runs .clear on the internal channels dict.
This would clean up the classes from memory.
it's not important since the way channels are handled already deals with "resetting" things
oh that reminds me, I need to test character switching on single channel mode.
I think since it ignores the channel input, it will still be the same history
easy fix though
I'll remove this warning since I think things are working expectedly
Yes I think the logic checks out for the settings combos...
Tell me about this 🙂
self._bot_id = 0
For future use, or in use here?
Trying to make an update script but it's not quite so simple since we rely on using the cmd_X.bat scripts in textgenwebui folder
to activate the environment
This is because I made the author_id a requirement for the message class
Normally its populated with message.author.id from discord users, but I was a little lazy and didnt want to bother getting the bot's id
I have code for that, I'll share in a few
OK - I got some suggestions from ChatGPT that didn't quite work
@echo off
echo Updating from Git...
git pull
rem Set the directory one level up
set PARENT_DIR=..\
rem Check if the script is running on Windows
if "%OS%"=="Windows_NT" (
call %PARENT_DIR%cmd_windows.bat && pip install -r %PARENT_DIR%ad_discordbot\requirements.txt
) else (
rem Check if the script is running on WSL (Windows Subsystem for Linux)
if exist %SYSTEMROOT%\System32\wsl.exe (
call %PARENT_DIR%cmd_wsl.bat && pip install -r %PARENT_DIR%ad_discordbot/requirements.txt
) else (
rem Check if the script is running on MacOS or Linux
if exist /usr/bin/uname (
for /f "tokens=*" %%i in ('uname') do set UNAME=%%i
if "%UNAME%"=="Darwin" (
sh -c "%PARENT_DIR%cmd_macos.sh && pip install -r %PARENT_DIR%ad_discordbot/requirements.txt"
) else (
sh -c "%PARENT_DIR%cmd_linux.sh && pip install -r %PARENT_DIR%ad_discordbot/requirements.txt"
)
)
)
)
echo Update complete.
pause
This does activate the venv but does not execute the pip install -r requirements
This is 100% outside my realm of knowledge
Just told chatgpt that I want an update script that does git pull, that can activate one of the files depending on OS, and to run the pip install ir requirements.txt
@echo off
cd text-generation-webui
set INSTALL_ENV_DIR=%cd%\installer_files\env
call installer_files\conda\condabin\conda.bat activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end )
cd ad_discordbot
@REM git switch dev
git pull
cp bot.py ../bot.py
python -m pip install -U pip
python -m pip install -r requirements.txt
cd ..
@REM call python bot.py... -flags...
:end
pause
I use something like this, you'd have to adjust the paths to match your environment
whereever you put the script
like if you put in in a utils folder under ad_discord bot
the first line could be cd ../.. instead of cd textgenwebui
I think ideally we would not only have UPDATE.bat in the main folder, but also bot.py with like a LAUNCH.bat
(so no file has to be manually moved)
But this is a pipe dream - nothing I can do at all
take out all the manual tgwi startup functions and turn it into an extension!
never have to move another file
instead of client.run to start the bot
you'd have a script.py file with setup()
that runs client.run
in a different thread so tgwi could run
that's something to worry about later, it works fine as is
If you find yourself so dedicated to this project at any point, I would not feel bad if you forked it and I became the contributor lol
There's a lot of big brain ideas that are restricted by my amateur knowledge
I pushed a quick hotfix for /speak command, which was broke by recent updates
huh, also found it interesting it was creating history [text, text], [text, text]
Was this just to make it speak what you type?
Exactly
I added 'source' as a positional arg for llm_gen so only run manage_history() if source != 'speak'
did not check dev to address the issue yet
yea, it doesn't manage the history for speak, well it shouldn't
It creates the bot_message, but wont commit it to history
I accidentally ran pip install r -requirements.txt instead of pip install -r ad_discordbot\requirements.txt
it's possible to also use pip from within python
using sys.executable as the path to the python exe
but im not sure this works properly with activated conda environments
only tested with venvs
You might have to fork it at this point because man, I am really not a fan of the logging method here
I did not like the idea of having separate channel log files.
TWO log files for each channel is bananas
i'll move them to internal
It's quite obvious how much work you put into there's an overwhelming amoutn of data being captured
This isn't necessarily a bad thing but yes, this data is excessive for the TGWUI log folder
I was very thrilled to accidentally start logging all chanells to a single file, I was initially trying to do something similar
the log files for tgwi are completely unnecessary, just there if the user wants to load them in the webui without the bot.
That could be a toggleable setting.
Or have a command to convert a conversation to a tgwi file.
This amount of data would be staggering in one file, for sure
i know i've said this a lot,
but computers can work faster with many small files compared to one big one.
Especially when the task at hand only requires a small sub section of that file (the few histories actually in use)
but I also understand the appeal of that, I just can't imagine saving all those threads of history every time a message is sent!
This would be perfect for sql
that's one file, and it does loads in chunks and handles caching internally
And it fits perfectly with the data structure of history
manager: [channels]
channel: [messages]
message: {attributes}
in _internal_post_save you can comment out that line and it will cut the files saved in half.
it's not needed by anything else
actually no, it is needed sort of
I'm relying on tgwi's "all_history" function to get the unique ids
We were saving:
Guild name, channel name, the text exchange. Mostly ignoring 'visible'
I was working on also capturing: message IDs, and a separate list of 0's and 1's to map an ID to a message.
Now it's saving assloads of information for everything
you can turn off what you don't need in the history file
but I use some of the attributes, like "spoken"
so maybe have to dig around and check what to comment out
replies and reply_to is going to be useful for multi user chat
I'd say whatever we can comment out is probably best, it's just so much
until we need to uncheck them one at a time as we open new features
we'll be writing 50% data being unused
yea, wish there was a way to set "don't output if default"
i need to read more about dataclass_json and do expirements
The individual log files are not too shabby if you could get them to save into a subdirectory, and ideally those log files would include the guild name and channel name
they're not meant to be explored by the user, it's all internal data
For anyone bopping back and forth between the WebUI and this bot, it will be quite a shock for these to start amassing there
the webui doesn't see them :)
only the normal history files
I noticed then when there was a bug saving the normal history files, that tgwui didn't output the "unique_id" for the save i was trying to load
Assuming it will be reduced to one file for channel, this still would be absolutely stuffed with files if its a popular server
oh maybe my tests with dataclass_json exclude failed because my environment is set to py3.9
but tgwi uses 3.11 or 10
I need to get around to update all my code to 3.11!
okay, I guess it wasn't bugged
odd that it needs a function input, and "True" doesn't work on its own
Oh!
maybe it takes the class as an input?
and I could decide to exclude a value on a condition.
yes!
it passes the value
This update is a lot to digest.
There's two things that will be holding this back from my consent on comitting to main:
- The logging, as I've mentioned. Our logs are going to default TGWUI location so we just can't overwhelm this directory with potentially dozens/hundres of new files per day which could easily happen with 1 file per channel, and not organizing them to subdirectory based on unique_id.
- I need to come up with a solution for
format_prompt_with_recent_output()which is one of the driving forces for the Flows feature.
think sorting into folders based off channel id would work?
Ideally... to me... something like how I split the multiple channel logs
{unique_id}_{server_name}_{channel_name}
or, server_name, channel name - and use unique ID in the subdir
Got one of my 2 working again.
what do you think of zip files?
as history.fresh() is called, I could add an arg: archive
This would put it in a queue to be added into a zip file because that history file is no longer going to be used in conversation.
I think they should go in a subdirectory, without zipping
Also, it wouldn't be easy creating a file system that uses guild/channel names for folders/file names.
Because those names could change, and the files would no longer be found.
Then we'd have to have a database of the channels and their names when the folders were created?
maybe at the bottom level for the files, they could use guild/channel names in them
because the bot would be looping over all the files to get the latest time anyway
Thats a good point
but the path leading to that needs to stay the same
I am looking forward to understanding the new system as it seems very versatile
Does the tts play more efficiently now? I didn’t look into it much but saw that you did change it
Let me know if you have a better idea for skipping history management from /speak command (could be more reasons in the future) other than if source != 'speak':
Can't seem to figure out how to avoid adding a /speak request to message history without also changing upload_tts_file() worst case, just add it to history 🤷♂️
Yo
So there's essentially 3 types of messages that we need to track in order for the current features to work expectedly
- the ones that have message IDs that will be written to history
- the ones that have message IDs that will not be written to history
- the ones without message IDs (internal prompts)
you can assign roles to messages and filter them
Well, this is a moving forward thing. Currently in main we don't care about message ID's - all messages are captured in text form
the message ids are useful for tracking edits
Got a few mins to just discuss the new classes?
mhm
Trying to see where is the best part to start off...
Let's start with local_history
(I know, not a class 😛 )
What does the fp variable stand for? in get_history_for()?
file path
I could write a custom typehint for it ^-^
similar to what I did with ChannelID
I see, sort of 😛
Seems like it goes out of the way to avoid including the type hint
you skip writing ChannelID:str by importing ChannelID from typing
the interpreter would evaluate these for you.
Yes I see what you mean, you don't know what it's expecting as input now
Hmm, I wonder how I could solve that while also keeping the name
Not a particular big deal
i know some programs have fancy typehints that take types as inputs
How about, could you explain how the typing module helps
Wanted to organise types into there own place that could be used by other modules
it ultimately could go into utils_shared
just update the imports ^^
you'll notice what seems to be copies of functions sometimes.
they are inheriting from eachother
search (in the bot)
will search the logs folder for the latest log that matches character/mode/channel
so you don't have to pass your own file path every time you want to import history
huh, also for me, channelID shows the type
wonder if I have some other addon
hmm... I did open the folder in Explorer
I'll write up some notes in the internals
So at the end of get_history_for it does a recursive call after setting a few params depending on the initial input
Yes?
this is the internal get_history_for
If there's no history matched for the given id, check if the user passed a file path.
if not, check if search enabled.
if still no matches, create new history
nope, also the search code only runs once
Thats updated code you're showing? Or are there two functions
Sorry it's a little confusing to follow, here's an explination:
my original design was to make an external library you could import through pip for example
So I couldn't include the internals of the bot and I want to keep it as general as possible
So in bot.py, I import the history and history manager classes
and create subclasses
I overwrite a few functions
for example get_history_for
which adds code to get the bot's state, character name and all
then at the end of the overwritten get_history_for
it does super() (which is a reference to the base class version)
super().get_history_for(...) calls the original version of the function with the new paramaters that have already been processed
so if you shift click this, it takes you to the actual logic
this approach lets you change how the classes behave to an extent without needing to modify internal code! (as if it were a library)
the point of the subclass version is to create the custom channel_id
that includes character/mode information as well
Then it just works as normally 😸
i'm going to cleanup my config mess with the dataclasses now that I know how to exclude data to export
The reason things werent looking correct were because I opened the repo in explorer, without moving bot.py
in my work space bot.py is in the same dir as ad_discordbot
using top level imports like
from ad_discordbot.modules.history helps with that ambiguity
Yes, same - my work space is still Main version
ahh
Just packed it up and set up Dev in working space
do you use an external tool to switch branches?
Alright so after asking ChatGPT what super() is, let me know if I understand this right...
get_history_for() has all the attributes etc from the parent class, and can modify some specifics before getting into the whole package deal
Overall, super() is used here to build on the existing functionality of the parent class's get_history_for method while incorporating additional logic specific to the subclass, such as modifying the search parameter and formatting the id_ parameter.
yea, that's exactly what the function does: only modify the id, and update search based on the bot's state.
then pass it to the parent
something that might slightly confuse the typehints is this
I'm also replacing the class used for History, with Custom history
because the history manager can create history classes
If we're going to work together in harmony - I recommend trying to limit the complexity a bit if possible.
In this case, it seems like the code in the child class could be done in a previous step (skipping the need for the child class), except if self.change_char_history_method == 'new': would be I think if bot_history.change_char_history_method == 'new':
Im saying this could all be passed to the parent function, skipping the need for the child one
state_dict = bot_settings.settings['llmstate']['state']
mode = state_dict['mode']
character_menu = state_dict["character_menu"]
Seems like the only line the child is inherting is the change_char_history_method
i'll draw it out
I don't mean to complain, I just want to be real with you that I don't have any hand on knowledge with subclasses, and have trouble tracing where things are going
(currently)
Much of what you've set up previously I don't really need to understand all too much, or it's just not too complex that I get it 'enough'
Yea, the plan was to separate it from the bot
Like how you don't change the internals of discord.py, but rather use it's tools or subclass it (using a bot class)
I don't have a god damned clue what you two are talking about like 90% of the time
i think this drawing could help, brb
I'm finger painting and they're Rembrant
You're learning 😸 , I didn't get there in one day either
You'll be stuck doing everything that has anything to do with History if I can't get this 😛
And we're at that point where History is starting to be the focus on planned features
it's still the same class.
it's the same bot_history object.
it just has extra modifications to work with your bot instead of general purpose
what I actually should have done in the beginning is this:
move those custom attributes to the custom version, but at the time I was testing in a standalone python file so wanted it all at once.
the great thing is, I could make this change and it wouldn't change how it works at all!
the classes could also be combined into history.py if you wish.
I just wouldn't be able to update them as easily when I continue it as it's own standalone project
Assuming that you are making some more changes - after you do, we need to regroup and go over it real quick
Currently, I'm a bit paralyzed
sorry about that!
yea, I need to make one of those "readthedocs" liek websites
Or I need to read Python: For Dummies
this is what you were originally doing
this can get really out of hand as you add more and more attributes
this is more cleanly recreated using classes
instead of all the attributes being held seperately, put them into one object!
Now that it's one variable, you don't need to change many lines of code if you want to add a new attribute!
And you will never have mismatch index errors if you accidentally forget to add to one of the lists.
Classes are just another form of structuing data, focused on "related things stay together"
I understand this 🙂
now for a subclass example
the subclass extends the behavior of the previous Item
but now includes the added "custom_attribute"
you can do the same with adding functions or anything you want
CustomItem('pqr', 57196, False, 'Hello!')
this works too, I just used keywordArg assignment to make it easier to see the change ^^
In your illustrations,
"CustomHistoryManager" seems to not be the Subclass - correct?
(the parent class)
I draw it this way to show that "CustomHistoryManager" is inheriting ALL the contents of HistoryManager plus its own
so it wraps it and is bigger
but yes, it's called a subclass
Class animal
- legs ? unknown
- noise ? None
- has function "make noise" that prints noise attribute.
Class dog (type animal) is a subclass
- it can auto-fill that the dog has 4 legs.
- auto-fill that the noise is "bark"
- can add a custom function: play fetch.
The parent of Dog is Animal, because dog takes the functions/attributes from animal and modifies them.
in this example, even though "dog" class doesn't define a "make noise" function.
You can still run dog.make_noise and it will print bark
since 'animal' had it
It can be useful to use a common base class like this.
For example if you are sorting through a list of items and want to extract which ones are animals.
You could do if isinstance(item, animal)
All dogs, all cats, all birds... would return True.
But the other items would not.
I appreciate your holding my hand here and walking me along
So I do understand that by making the Class, this is an easy way to make a broad definition for all items that will be processed as History.
The CustomHistory subclass inherits those broad definitions, and adds more specialized attributes
yes,
the thing with CustomHistory is.
The history manager doesn't know about this new class, it is still using the parent version.
So I pass "history_factory=CustomHistory"
to the manager, so it creates them with the one we want ^-^
So rather than getting a return from the sublass, we update the subclass which passes to the parent class, then we get a return from the parent class
no classes are being updated when you call the get_history_for function.
It's just HistoryManager.get_history_for takes an ID for which history to get.
But in your bot, we need more than just the ID, we need character and mode info to decide which history we use.
CustomHistoryManager.get_history_for
Also uses ID
But it customises it, then continues as normally with
HistoryManager.get_history_for(New Custom ID)
this solution uses less code because
you would have to pass character and mode information each time you get history without it.
But here we do so automatically!
interesting, parents are also called superclasses.
that's where the super() function comes from
also little bit about why I try to use dataclasses as much as possible now!
When creating subclasses, you often need to super().__init__(all the args!)
to run init with the parent's code
But this is tedious to keep adding attributes
and having to remember where they all are.
Dataclasses kinda automate this nicely 😸
PS: a dataclass is just someone's library, it's still the same classes in the back, just a tool to make them easier
Let's talk about this...
bot_history = CustomHistoryManager(class_builder_history=CustomHistory, **config.get('textgenwebui', {}).get('chat_history', {}))
the ** part?
That's key word args, yes?
yes, i'm unpacking a dict into autosave=true, autoload=true, change_char_method='new'...etc
So it will basically dump whatever is in that dictionary as individual params
Now about the rest of that line...
class_builder we talked about that,
the manager doesn't know about the new history class we created for the bot, so lets tell it to use it
I'm not sure that we talked enough about class_builder
there we just pass a type
in the code it uses
new_history = self.class_builder_history()
I called it "factory" earlier because I forgot what I named it
Are we using class_builder for anything else besides history atm?
But we may expand
this was the message about customHistory
you can expand CustomHistory, and that will expand what's used by the bot, yes
the history class is what's created for each channel, holding the messages and other info
I meant like, we may start making a similar variable for another element of the bot like class_builder_behavior
remember, this is only for bot_history
hmmm
hmm, this is complicated to explain
ok so the name is supposed to be a hint that this is building the class
yes
example:
i'll call history manager HM for short
when you call function:
HM.get_history_for(id)
and the ID doesn't exist, it creates a new history class.
by default, that new history class is History which is found in history.py.
But I modified the history class (CustomHistory)
To change how saving works for tgwi
But HM is still creating the old histoy type, because it's programmed to do so.
So I gave HM an attribute, called class_builder_history = History by default.
when initialising HM, i can set it to a custom class
and HM will use that custom class every time you need a new history from HM.get_history_for
and the ID doesn't exist, it creates a new history class.
Do you mean it creates a new instance of the history class?
yes, it creates a new history Object*
is the correct term
a class is just a blueprint to objects.
that's what I meant 😸
Ok - that word helps a lot (object)
so, that should make more sense that multiple Objects aren't there when you create a subclass.
that it's actually just making a larger blueprint that combines all the information like a merge, then creates your object out of that.
Alright so it creates a new history object, and without customizing it it would just be called "History" but it gets a custom value assigned to it from the character menu, mode and uniqueID
yes, some values I can only get from the bot.py file.
So I had to define some parts in there to access them.
you can't do
bot.py:
import history
history.py:
import bot
to access eachother
I was wondering how to get around that
I could ask chatgpt this but maybe its a quick one, what is field?
limit_history: bool = field(default=True)
here in utils_discord.py
I import history.py only for typehints.
in history.py
I import utils_discord.py for something I forgot.
it's from your config.yaml
remember we are unpacking this dict into the HM class with **
I need to reorganise some of those variables because they are from when I was getting the base code working.
Stuff like limit_history should be defined in CustomHistoryManager.
not the parent.
Because the parent never uses it anyway.
if you could explain your idea more in depth I could give some thoughts on it ^-^
What I think you're saying is giving the bot different behaviour per something.
maybe per channel.
If tgwi supports this, yes absolutely!
And yes you could use the CustomHistory class to contain bot behaviour if you want!
maybe some custom per channel stuff like: don't save history to file for this channel
oh yea, this absolutely could be per channel!
lets do that in a future update!
It just didn't jump out at me what the reason for the variable name was.
As I think I understand now, you used that label to signify that CustomHistory is building the Class
actually would have to sort out the multiple files thing here.
bot behavior could be saved inside the history json files.
And i would write it in a way where it doesn't save default values.
That might be a little bigger of a change than I thought
added a hidden attribute to HMessage.
When a message is hidden it wont be rendered.
This could be useful for regenerating a reply, but also keeping it in the history save just hidden.
internal history is now significantly smaller
still a few more changes I could make
Is there currently any benefit/use for uuid?
that's for it to load which message is being replied to.
i'll change the uuid to use an incrementing number soon.
these are reply pairs
in the future, when handling multiple messages from different people.
you might want to be able to select which message the bot is replying to
as other messages would find their way inbetween the history
I could do this by storing the index on save.
But that means I need to overwrite the .to_dict() function and figure out how that all works.
it's doable!
hmm
if i "dont save" the replies in messages.
But rather as a dict in the history class.
i could write an encoder function that reduces everything to indexes as they currently are and uses those.
perhaps.
would look like
{
"1":"0",
"3":"2"
}
for the above screenshot
Commented out the hidden code.
A better solution for cycling possible regeneration would be to hold the messages in another list.
And use something like HMessage.replace() not implemented yet.
to save the one you want to in its place in history.
Could even have the bot edit its message!
But it would act the same as History.replace() swapping it in place of the old history in the manager.
Sorry for lack of response - I'm not quite going to understand it too much until I can get hands on with it after you make your adjustments.
I like how there is much less information written to file now.
As I said earlier, we just need to be sure that we can get the text for the 3 different types of messages.
For the 3rd type (internal message) - where there is no message.id, the user author is internal, the text is not written to history - we need to capture that text in one place or another, and be able to recall it
you already can!
For instance with the Flows feature, on Flow Step 5 we may want to use the output from Flow Step 1
currently, this would be done with either {llm_4} or {user_4}
set name=None (so it's not saved)
set role='internal'
id = None
You can use this to autofill that name='internal' if you need.
and that's it!
It will not appear in chat history because the tgwi renderer will only output pairs of user/assistant.
Where are such text strings being stored?
Are we retaining all text to RAM then?
if you want to collect all the internal messages, you can do
History.role_messages('internal') -> list
if it's in the history class, it's in ram.
But you can delete it if you want.
The point of the message ID's I think was mainly to not retain the text to RAM - be able to just go fetch it from discord
Well, I mean that's how it was in my head
that's a huge waste!
Discord prefers if you keep cached data as much as possible.
because calling channel.fetch_message makes discord do database lookups
also, your discord bot already caches messages,
if you keep the bot running forever, it will keep up to a week of messages in memory.