58 Downloads
58 Downloads
README
README
Model dependent has the advantage of performing snappier, but it's trade off is that the model has more control over the process, so depending on behavior it's reliability may vary. I've taken extra steps in this latest release to take a compromising approach to addressing this unreliableness beyond trying to tune prompts.
EXPLICIT version (I recommend this one for reliability) - GithHub - Explicit | LMStudio
Optional Companion Plugin - GithHub - Context Cleanup | LMStudio - Useful at renumbering the message number appended at the end of assistant responses. Especially if the assistant loses track and refuses to correct itself.
Persisting Memories Plugin is an LM Studio plugin that lets users preserve selected assistant responses as reusable memory seeds and inject those memories into future conversations. It stores memories as local JSON files, organizes them by category, and uses prompt preprocessing to add selected memories to the active prompt when needed.
Tested working on Windows 11 Pro 25H2 - LM Studio 0.4.24
I was in the process of making a compact a summary plugin that would automatically compact/summarize a chat session after a certain threshold was met but I found unreliableness from the model to prepare jsons that accurately summarized the entire chat being stashed and truncated, I dislike unreliableness so I put the idea on hiatus for now. Maybe I'll come to terms with it eventually. Long story short, I decided to bring some of those ideas to this persisting memories plugin. If you think about it my persisting memories plugin and context cleanup paired together somewhat achieves the same concept. The real difference is that it's not automatic, you have to "summarize" yourself by saving the memory seeds you deemed important, choose which seeds to inject, and configure the truncation with my context cleanup.
This project gives LM Studio conversations a lightweight persistent memory layer. Instead of manually copying useful answers, users can select important assistant responses, save them as memory seeds, and later choose which memories should influence a new conversation.
A memory seed captures:
The plugin keeps the available memory pool in memory, injects selected memories into prompts, and can remove previously injected memories from the stored conversation file when the user no longer wants them.
From LM Studio Website: Install from the LM Studio Hub then enable the plugin.
From GitHub Source Code: Open PowerShell/terminal, navigate to root folder of the plugin you downloaded where you see the README, package, and manifest. Enter in lms dev -i -y . Plugin should now be available in LM Studio.
Make sure the persist_seed tool is enabled in the plugin's Tools section (this is mandatory — if it's off, the model can't use the plugin at all).
To use, while the plugin is enabled, type to the model, "save memory <message _#>; category <category_name>; name <memory_name>".
(example: save memory 4; category cats; name some_information)
If you forget the category or name the model should ask for it before running the tool. The plugin will ask the model to visually identify each message # to you, just base your # provided off what you're shown.

Through the use of prompt preprocessing, the memory seed is injected alongside the user's message on the newest user's turn.

When the user see's a message they wish to keep as a memory, they can call on the save_memory tool by saying this to the assistant

Removing and deleting are two distinct actions. Remove means your intention is to remove the memory from the current chat session's context. Deleting means to permanently discard the memory.
To remove: Just press the x on the memory bubble under the Selected Memories section. Memories not listed in Selected Memories are either not in context or were removed form context.

| Field | Purpose |
|---|---|
| Delete Memory | Full name of the memory to delete. Deletion is permanent. Bubbles will not update until reinitialization, this is a UI limitation of LM Studio. |
| Available Memories | Display-Only: a list of available memories. Users can copy names from this list into Memories to Inject or Delete Memory. |
| Memories to Inject | Memories that should be injected into the current session. Only the listed memories persist through turns. |

While enabled the plugin will force the assistant to append a message # after each of it's responses. This numbering makes it easier for users to refer to a specific exchange when asking to save a memory.
Save memory regex patterns (Example: svmem4 will be accepted and so will with store mmry msg4. the category is apple). Batch memory save requires a variation of through/to as a trigger and ending message number


C:\Users\USERNAME\.lmstudio\conversationsModel dependent has the advantage of performing snappier, but it's trade off is that the model has more control over the process, so depending on behavior it's reliability may vary. I've taken extra steps in this latest release to take a compromising approach to addressing this unreliableness beyond trying to tune prompts.
EXPLICIT version (I recommend this one for reliability) - GithHub - Explicit | LMStudio
Optional Companion Plugin - GithHub - Context Cleanup | LMStudio - Useful at renumbering the message number appended at the end of assistant responses. Especially if the assistant loses track and refuses to correct itself.
Persisting Memories Plugin is an LM Studio plugin that lets users preserve selected assistant responses as reusable memory seeds and inject those memories into future conversations. It stores memories as local JSON files, organizes them by category, and uses prompt preprocessing to add selected memories to the active prompt when needed.
Tested working on Windows 11 Pro 25H2 - LM Studio 0.4.24
I was in the process of making a compact a summary plugin that would automatically compact/summarize a chat session after a certain threshold was met but I found unreliableness from the model to prepare jsons that accurately summarized the entire chat being stashed and truncated, I dislike unreliableness so I put the idea on hiatus for now. Maybe I'll come to terms with it eventually. Long story short, I decided to bring some of those ideas to this persisting memories plugin. If you think about it my persisting memories plugin and context cleanup paired together somewhat achieves the same concept. The real difference is that it's not automatic, you have to "summarize" yourself by saving the memory seeds you deemed important, choose which seeds to inject, and configure the truncation with my context cleanup.
This project gives LM Studio conversations a lightweight persistent memory layer. Instead of manually copying useful answers, users can select important assistant responses, save them as memory seeds, and later choose which memories should influence a new conversation.
A memory seed captures:
The plugin keeps the available memory pool in memory, injects selected memories into prompts, and can remove previously injected memories from the stored conversation file when the user no longer wants them.
From LM Studio Website: Install from the LM Studio Hub then enable the plugin.
From GitHub Source Code: Open PowerShell/terminal, navigate to root folder of the plugin you downloaded where you see the README, package, and manifest. Enter in lms dev -i -y . Plugin should now be available in LM Studio.
Make sure the persist_seed tool is enabled in the plugin's Tools section (this is mandatory — if it's off, the model can't use the plugin at all).
To use, while the plugin is enabled, type to the model, "save memory <message _#>; category <category_name>; name <memory_name>".
(example: save memory 4; category cats; name some_information)
If you forget the category or name the model should ask for it before running the tool. The plugin will ask the model to visually identify each message # to you, just base your # provided off what you're shown.

Through the use of prompt preprocessing, the memory seed is injected alongside the user's message on the newest user's turn.

When the user see's a message they wish to keep as a memory, they can call on the save_memory tool by saying this to the assistant

Removing and deleting are two distinct actions. Remove means your intention is to remove the memory from the current chat session's context. Deleting means to permanently discard the memory.
To remove: Just press the x on the memory bubble under the Selected Memories section. Memories not listed in Selected Memories are either not in context or were removed form context.

| Field | Purpose |
|---|---|
| Delete Memory | Full name of the memory to delete. Deletion is permanent. Bubbles will not update until reinitialization, this is a UI limitation of LM Studio. |
| Available Memories | Display-Only: a list of available memories. Users can copy names from this list into Memories to Inject or Delete Memory. |
| Memories to Inject | Memories that should be injected into the current session. Only the listed memories persist through turns. |

While enabled the plugin will force the assistant to append a message # after each of it's responses. This numbering makes it easier for users to refer to a specific exchange when asking to save a memory.
Save memory regex patterns (Example: svmem4 will be accepted and so will with store mmry msg4. the category is apple). Batch memory save requires a variation of through/to as a trigger and ending message number


C:\Users\USERNAME\.lmstudio\conversationsThe user controls the memory seeds they save, which is sorted by the user's choice too. The table of memories, when first initialized, will send every memory file through the model to extract keywords and provide a summary that best defines the contents of that memory file. Those values will then be added to one of three table of memories files living in the ~\.lmstudio\memories folder.

!! IMPORTANT !! Users are expected to send a one time user message with "Initialize Table of Memories" toggled ON. You must only do this once. This will create the TOM files and populate their fields with your memories created by older versions of this plugin. After this one time run, you should leave the option toggled OFF. The reason why is because the process can be expensive depending on how many memory files you have. You do not want to be initializing all your memories every turn. You do not have to worry that in the future the TOM becomes outdated as you add brand new memories or update old memories. The TOM will be maintained with the recent additions to the backend logic even while the "Initialize Table of Memories" remains off. The toggle will remain for future versions because it can still serve as a function to fully repair your TOM if you suspect it to be missing, corrupted, or inaccurate.


As your memory files grow you may forget what's in them. Users can now ask the model to use the plugin to help recall, find, or remember a memory based off of your fuzzy memory. In the backend the model will be given a collection of keywords tied to a memory file provided by the TOM files and asked to choose the top 8 best matches compared to the query. Then the best keyword matching candidates pass along their summaries for the model to determine up to 3 best matches to return to the user. From there once you determined which memory you were looking for, you can inject the memory normally through the "Memories to Inject" field.

Another method to find your memories is to look at the table yourself. You can ask the assistant to look up, fetch, get the memories table. It technically should ask you to specify which table before actually fetching it, but some models might just make assumptions and grab any table. Your options are TOM_A-I, TOM_J-R, TOM_S-Z. All non-alphabet characters will be stored in TOM_S-Z. You do not have to type it exactly, a model "should" understand tomjr refers to TOM_J-R. Just rememeber doing so brings all entries of the TOM request into context, this can consume a large amount of your context window depending on how many unique memory files you have stored. I recommend leaving the tool table_of_memories_lookup OFF if you do not actively needed it. Handing a misbehaving model a tool could mean it spams it and gives you any excuse it wants to avoid accountability.

Added Summarize All Memories Injected feature. This will summarized/condense any future memories you inject (does not apply retroactively), rather than injecting the memory seed verbatum. Results may vary, all memory seeds in the same file will most likely be consolidated into one big narrative unless they're distinctly different. Regardless it should save some context window compared to the verbatum approach. Just consider the feature as "there if you want it" you may find it useful for your needs. The feature does cause a delay to process and formulate the summaries before it's injection so it is not as quick as injecting the memory seed verbatum.
The added text will now be able to be referenced by the assistant.
Inject all memory files within that category folder with <category_folder>/*.json.
Users will not be allowed to create a fle named * or *.json to protect this feature. (memory/file names like few*many will be allowed)

User says: save memory #, category <category_name>, name <memory_name>
save memory message # works too.; or , or .category and/or name.Batch memory save. save memory <message #> to <message #>; category <category>; name <name>.
User messages that are just save memory commands will be filtered out and not saved to the file during the batch process.

If category and/or name are not provided during the initial user message, the user will be asked in the following assistant message.
To delete: Copy and paste the exact memory name into the Delete Memory text field. It should instantly delete the memory. The plugin will not update Available Memories unless it's reinitialized. But for all future purposes the deleted memory will no longer be usable.
To delete a whole category: <category_folder>/*.json. Be warned, the moment you complete that name to delete up to the last n of .json and it is a valid existing folder, it's gone, there is no recovery.

Instead of letting the model gather all the parameters and hope it registers it as the correct parameters, I've taken a new middle ground approach. When the backend sees that the user is trying to save a memory, it will wait to gather all the parameters. Once all the parameters (message #, category name, memory name) are known, the plugin will reconstruct the exact save memory command string to feed to the model. It will give all the gathered parameters in the correct format.
During an active save memory request if the user fails to provide any parameters there is a hard thrown error to the model. This will inform the model of the missing parameters or prevent the model from trying to invoke the tool when the user never asked for it. You may see a failed tool call, but nothing should be able to proceed beyond that point. My previous approach left a door open for the model to succesfully call the full tool function with parameters it hallucinated, this approach leaves no tolerance for that behavior.
Users are now barred from performing memory injections in the middle of a save memoroy request. This was done so that users would not muddle up the reconstructed memory command provided to the model. If you forget this rule it doesn't matter, the memories will just be injected on your first non-save memory turn.
Message # appending has been created into a "reminder" for the model. Rather than append tags on each user message to make sure the model always labels each message correctly, or only sending the formatting instructions once and hoping the model doesn't forget or start repeating numbers. I've taken an inbetween approach, I now have the formatting instructions sent one time if it hasn't already been given, and it will send the instructions again when the backend notices a pattern that the assistant has clearly disregarded the instructions. This has tested well. Less overhead than the repeated tags, yet able to recover if the model misbehaves or loses it's turn count. Still more inherent overhead compared to my explicit version though.
So far these new approaches seem better than before, less reliance on hoping the model gets it right, but there's always the inherient unreliableness of the model, which is eliminated by my explicit version.
Acquire Lock file has been reworked. The plugin now will not release it's lock until all the functions it needs to run are completed, similar to my approach on my explicit version. However I wanted to try a different method I forgone on my explicit version. In short my explicit version gathers all the functions trying to run, and does it in one go with a single .lock file controlled by a coordinator. This model dependent version offloads some of that to the model so I went with the less complex version of making a queue. The faster function creates it's lock first and each function who needs to run it's logic will join the queue after and pass along it's function to execute. Once the queue has been cleared then the lock file will be released.
If you start discussing things within the assistant's no-go-zones and don't relent, your model may get tied up into an intolerant-reject-your-demands/requests pattern, there is a high chance it will start disregarding even simple demands like the formatting instructions even if it's sent every user turn. What I have seen that was mostly successful is if the assistant has been refusing to append the message number. You directly tell it to start following the formatting instructions again. It will resume once it's out of it's rejection mood.
A memories folder will be created at C:\Users\USERNAME\.lmstudio, and a .json file that retains the relationship between the chat session and its conversation file will be stored in C:\Users\USERNAME\.lmstudio\conversations.
Injection markers: memories will be injected within blocks of BEGIN and END markers containing the memory seed category/memory_name. These markers allow for later removal of the memory.
Internal chat ID: the preprocessor will append a one-time InternalChatID [ICID] to mark the chat session. This marker helps to later identify the session and tie it to the corresponding conversation file. Some dumb overcautious models will think the tag is a jailbreak attempt to manipulate their behavior.
Conversation mapping: the plugin stores a relationship file that maps internal chat IDs to conversation file names. It keeps only the newest 15 relationships.
Removal polling: when triggered memory removal, polls the conversation file every 800 ms until the assistant finishes responding. Then it will remove the memories from the conversation after 2 seconds. These 2 seconds were mandatory otherwise LM Studio would just overwrite it again with some cached version prior to the removal of the memory seeds.
LM Studio also reinitializes the plugins whenever it decides too, so reliable long term storage of variables outside the scope is unreliable and just used temporarily. That includes storing current values in the config Schematics.
Path safety: memory names are normalized, but not to correct misspellings. It is easiest to copy and paste the memory name from the list displayed in Available Memories into the text field of Memories to Inject.
In-memory pool: the available memory pool is kept in memory and updated when files are deleted. Plugin UI updates may be delayed because of LM Studio plugin behavior, but on the backend these values are properly updated.
The user controls the memory seeds they save, which is sorted by the user's choice too. The table of memories, when first initialized, will send every memory file through the model to extract keywords and provide a summary that best defines the contents of that memory file. Those values will then be added to one of three table of memories files living in the ~\.lmstudio\memories folder.

!! IMPORTANT !! Users are expected to send a one time user message with "Initialize Table of Memories" toggled ON. You must only do this once. This will create the TOM files and populate their fields with your memories created by older versions of this plugin. After this one time run, you should leave the option toggled OFF. The reason why is because the process can be expensive depending on how many memory files you have. You do not want to be initializing all your memories every turn. You do not have to worry that in the future the TOM becomes outdated as you add brand new memories or update old memories. The TOM will be maintained with the recent additions to the backend logic even while the "Initialize Table of Memories" remains off. The toggle will remain for future versions because it can still serve as a function to fully repair your TOM if you suspect it to be missing, corrupted, or inaccurate.


As your memory files grow you may forget what's in them. Users can now ask the model to use the plugin to help recall, find, or remember a memory based off of your fuzzy memory. In the backend the model will be given a collection of keywords tied to a memory file provided by the TOM files and asked to choose the top 8 best matches compared to the query. Then the best keyword matching candidates pass along their summaries for the model to determine up to 3 best matches to return to the user. From there once you determined which memory you were looking for, you can inject the memory normally through the "Memories to Inject" field.

Another method to find your memories is to look at the table yourself. You can ask the assistant to look up, fetch, get the memories table. It technically should ask you to specify which table before actually fetching it, but some models might just make assumptions and grab any table. Your options are TOM_A-I, TOM_J-R, TOM_S-Z. All non-alphabet characters will be stored in TOM_S-Z. You do not have to type it exactly, a model "should" understand tomjr refers to TOM_J-R. Just rememeber doing so brings all entries of the TOM request into context, this can consume a large amount of your context window depending on how many unique memory files you have stored. I recommend leaving the tool table_of_memories_lookup OFF if you do not actively needed it. Handing a misbehaving model a tool could mean it spams it and gives you any excuse it wants to avoid accountability.

Added Summarize All Memories Injected feature. This will summarized/condense any future memories you inject (does not apply retroactively), rather than injecting the memory seed verbatum. Results may vary, all memory seeds in the same file will most likely be consolidated into one big narrative unless they're distinctly different. Regardless it should save some context window compared to the verbatum approach. Just consider the feature as "there if you want it" you may find it useful for your needs. The feature does cause a delay to process and formulate the summaries before it's injection so it is not as quick as injecting the memory seed verbatum.
The added text will now be able to be referenced by the assistant.
Inject all memory files within that category folder with <category_folder>/*.json.
Users will not be allowed to create a fle named * or *.json to protect this feature. (memory/file names like few*many will be allowed)

User says: save memory #, category <category_name>, name <memory_name>
save memory message # works too.; or , or .category and/or name.Batch memory save. save memory <message #> to <message #>; category <category>; name <name>.
User messages that are just save memory commands will be filtered out and not saved to the file during the batch process.

If category and/or name are not provided during the initial user message, the user will be asked in the following assistant message.
To delete: Copy and paste the exact memory name into the Delete Memory text field. It should instantly delete the memory. The plugin will not update Available Memories unless it's reinitialized. But for all future purposes the deleted memory will no longer be usable.
To delete a whole category: <category_folder>/*.json. Be warned, the moment you complete that name to delete up to the last n of .json and it is a valid existing folder, it's gone, there is no recovery.

Instead of letting the model gather all the parameters and hope it registers it as the correct parameters, I've taken a new middle ground approach. When the backend sees that the user is trying to save a memory, it will wait to gather all the parameters. Once all the parameters (message #, category name, memory name) are known, the plugin will reconstruct the exact save memory command string to feed to the model. It will give all the gathered parameters in the correct format.
During an active save memory request if the user fails to provide any parameters there is a hard thrown error to the model. This will inform the model of the missing parameters or prevent the model from trying to invoke the tool when the user never asked for it. You may see a failed tool call, but nothing should be able to proceed beyond that point. My previous approach left a door open for the model to succesfully call the full tool function with parameters it hallucinated, this approach leaves no tolerance for that behavior.
Users are now barred from performing memory injections in the middle of a save memoroy request. This was done so that users would not muddle up the reconstructed memory command provided to the model. If you forget this rule it doesn't matter, the memories will just be injected on your first non-save memory turn.
Message # appending has been created into a "reminder" for the model. Rather than append tags on each user message to make sure the model always labels each message correctly, or only sending the formatting instructions once and hoping the model doesn't forget or start repeating numbers. I've taken an inbetween approach, I now have the formatting instructions sent one time if it hasn't already been given, and it will send the instructions again when the backend notices a pattern that the assistant has clearly disregarded the instructions. This has tested well. Less overhead than the repeated tags, yet able to recover if the model misbehaves or loses it's turn count. Still more inherent overhead compared to my explicit version though.
So far these new approaches seem better than before, less reliance on hoping the model gets it right, but there's always the inherient unreliableness of the model, which is eliminated by my explicit version.
Acquire Lock file has been reworked. The plugin now will not release it's lock until all the functions it needs to run are completed, similar to my approach on my explicit version. However I wanted to try a different method I forgone on my explicit version. In short my explicit version gathers all the functions trying to run, and does it in one go with a single .lock file controlled by a coordinator. This model dependent version offloads some of that to the model so I went with the less complex version of making a queue. The faster function creates it's lock first and each function who needs to run it's logic will join the queue after and pass along it's function to execute. Once the queue has been cleared then the lock file will be released.
If you start discussing things within the assistant's no-go-zones and don't relent, your model may get tied up into an intolerant-reject-your-demands/requests pattern, there is a high chance it will start disregarding even simple demands like the formatting instructions even if it's sent every user turn. What I have seen that was mostly successful is if the assistant has been refusing to append the message number. You directly tell it to start following the formatting instructions again. It will resume once it's out of it's rejection mood.
A memories folder will be created at C:\Users\USERNAME\.lmstudio, and a .json file that retains the relationship between the chat session and its conversation file will be stored in C:\Users\USERNAME\.lmstudio\conversations.
Injection markers: memories will be injected within blocks of BEGIN and END markers containing the memory seed category/memory_name. These markers allow for later removal of the memory.
Internal chat ID: the preprocessor will append a one-time InternalChatID [ICID] to mark the chat session. This marker helps to later identify the session and tie it to the corresponding conversation file. Some dumb overcautious models will think the tag is a jailbreak attempt to manipulate their behavior.
Conversation mapping: the plugin stores a relationship file that maps internal chat IDs to conversation file names. It keeps only the newest 15 relationships.
Removal polling: when triggered memory removal, polls the conversation file every 800 ms until the assistant finishes responding. Then it will remove the memories from the conversation after 2 seconds. These 2 seconds were mandatory otherwise LM Studio would just overwrite it again with some cached version prior to the removal of the memory seeds.
LM Studio also reinitializes the plugins whenever it decides too, so reliable long term storage of variables outside the scope is unreliable and just used temporarily. That includes storing current values in the config Schematics.
Path safety: memory names are normalized, but not to correct misspellings. It is easiest to copy and paste the memory name from the list displayed in Available Memories into the text field of Memories to Inject.
In-memory pool: the available memory pool is kept in memory and updated when files are deleted. Plugin UI updates may be delayed because of LM Studio plugin behavior, but on the backend these values are properly updated.