Project: Project AI Chatbot Memstore Version: qwen_memproxy.py 1.4.1, stages/contextdef_stage.py 1.0.1, stages/hotwords_rag_stage.py 1.0.0, stages/hotwords_store_stage.py 1.0.0, QwenMemProxy.sh 1.0.2 Date: 2026-10-27 Another collaboration between Andrew.human and Claude.ai
Written to be read by people and by AI models.
The llama-server web page ignores the system text the server offers it, so memory set at server start never reached the page. qwen_memproxy.py is a go-between that sits in front of a llama-server. It forwards every request unchanged (the web page, the API, streamed replies) except chat requests, to which it adds the operator-approved memstore entries as background facts. It never changes the memory store.
| Profile | Open this | Forwards to |
|---|---|---|
4b |
http://clone:8090/ |
Qwen3-4B server on 8080 |
05b |
http://clone:8091/ |
Qwen2.5-0.5B server on 8081 |
The Qwen servers and their ports are unchanged. Anything that talks to port 8080 or 8081 directly (such as qwen_memchat.py) works as before, without the go-between.
sh Qwen25_05B.sh (start the Qwen server first)
sh QwenMemProxy.sh 05b (start the go-between; open http://clone:8091/)
sh QwenMemProxy.sh stop 05b (ordered stop; do this before stopping the Qwen server)
QwenMemProxy.sh uses python3.8 for the go-between and python3.6 to read memstore (the Clone's python3.8 has no SQLite), and the database at $MEMSTORE_DB or else /root/memstore/memstore.db. Its log goes in /root/qwen_logs/memproxy_PROFILE_DATE.log and records start, stop and the entry count whenever it changes.
--place)| Setting | What it does | Default for |
|---|---|---|
user |
Puts the facts in front of the latest question only, as "Facts about me ... My question: ...". Earlier turns are untouched, and the page's own chat view shows only what you typed. | 05b |
system |
Adds the facts to the end of the system message. | 4b |
Why: a very small model (the 0.5B) gave little weight to system text and answered "Windows" despite the facts being present. Text next to the question is weighed more. For the 4B the system message is better because that part of the prompt is cached by the server, while facts in the question add about 100 tokens of reading (roughly 20 seconds at the 4B's speed) to every message. These defaults are untested guesses on real models; change with --place if the other works better.
--refresh), so a new approve or add applies to the next chat with no restart.--place system the facts go at the end of the system message, or in a new one if the page sent none. With --place user they go in front of the latest user message.POST /v1/chat/completions is changed. Everything else passes through byte for byte.chat request: memory added in front of the latest user message, ...to the page's system message, ...as a new system message, no memory to add ..., or not a normal chat body .... If the log shows no chat request line after you ask something, the page is not going through the go-between (check the port in the address bar).A stage is a small program in the stages folder (--stages DIR, default stages next to qwen_memproxy.py, normally /root/memstore/stages). The proxy scans the folder when it starts and again for every chat query, so a program added or removed later is hooked in or out without a restart. Each change is written to the log once.
The program says where it hooks in. Its header (first 80 lines) carries tag lines, in the same style as @version and @date:
* @hook before-model
* @order 10
@hook is required. A .py or .sh program without a valid @hook line is listed in the log as not hooked in, and is not run.@order is optional (default 50). Programs at the same hook point run lowest number first, then by file name.@version is read for the log.@needs FILE (optional, may be repeated) names a file that must sit in the same folder as qwen_memproxy.py. At start-up, and whenever the folder changes, the proxy warns WARNING: <stage> needs <file>, not found in <folder> instead of letting the stage fail on the first query. The stage is still hooked in.. or _, and files that are not .py or .sh, are ignored. .py programs run with --stage-python (default: the Python running the proxy), .sh programs with sh.| Hook point | When it runs | What it may return |
|---|---|---|
before-model |
Before the query is forwarded to the model | context (text added after the question, under "[About this question, worked out automatically and possibly imperfect]"); memory (text that replaces the memory entries for later stages and for what is sent) |
after-reply |
After the whole reply has gone to the page, in a separate thread so the page is not kept waiting | Nothing is used; the output is only logged |
each-word |
Planned, for the work done one next-word candidate at a time. A program tagged with it is reported as not runnable yet |
A query that starts with remember: runs no stages.
version (1), hook, proxy (its version), home (the folder holding qwen_memproxy.py), sql_python (the Python given with --python, the one that can read SQLite; the Clone's 3.8 cannot), profile (4b or 05b), query (the newest user message as typed), messages (the whole chat so far, as the page sent it), memory (the memory entries text), results (what earlier stages returned, by file name). after-reply stages also get reply.context and memory are kept in results for later stages and shown in the log.--stage-timeout seconds (the stage is asked to stop, and forced only if it ignores that), or output that is not a JSON object. The stage is logged as SKIPPED and the query goes on without it.A minimal stage:
# @hook before-model
import json, sys
order = json.load(sys.stdin)
print(json.dumps({"context": "The question has %d words." % len(order["query"].split())}))
stages.control in the stages folder (or any file given with --control FILE) is optional. One line per program; # starts a comment:
contextdef_stage.py order=10 timeout=120
hotwords.py enabled=no
Keys: enabled=yes|no, order=N, timeout=S. A line overrides the program's own header for those keys; the hook point can only be set by the program. A line that names a program not in the folder, or that the proxy cannot read, is reported in the log. The file is read again for every chat query. One folder with several control files (--control) is a convenient way to keep a set of experiments.
stages/hotwords_rag_stage.py hooks in at before-model, order 20, and needs hotwords.py next to the proxy.
hotwords.py pseudo on the query: the query's own hot-term array plus the related terms RAG draws from the stored arrays. pseudo stores nothing.results (query_terms, related_terms, rag_arrays, fuzzy_matches, and line, which is the pseudo query as hotwords.py prints it). Putting retrieved terms into the prompt would be a different experiment and would be a separate stage.hotwords.py on its standard input. hotwords.py runs with sql_python. The database is whichever hotwords.py uses ($HOTWORDS_DB, else $MEMDIR/hotwords.db).stages/hotwords_store_stage.py hooks in at after-reply, order 50, and needs hotwords.py next to the proxy.
hotwords.py add, labelled proxy YYYYmmdd-HHMMSS, so the stored arrays grow as you chat.hotwords_store_stage.py enabled=no in stages.control, and feed the store only what you approve or correct, with hotwords.py add by hand.stored (true or false), why, and the lines hotwords.py printed about what it added.stages/contextdef_stage.py hooks in at before-model, order 10, and needs contextdef_05b.py next to the proxy (the start-up warning says if it is missing). It runs contextdef_05b.py unchanged on the query and returns the subject, intent and terms.
contextdef_05b.py must sit in the same folder as qwen_memproxy.py, and the 0.5B server must be running on 8081, which is where contextdef_05b.py looks, whichever profile the proxy has.Subject: ..., Key terms: ...). The intent and the terms the model added itself ([+]) are in the log and in results, and are not sent.run folder: line that contextdef_05b.py prints, then reads the JSON file there whose index is 1 and which has a parsed section. It was written from sample output, not from that script's source. If the output changes, the stage says what it could not find and the proxy skips it.qwen_memproxy.py)| Option | Meaning |
|---|---|
--profile 4b|05b |
Ports as in the table. Default 4b. |
--listen-port N, --upstream-port N |
Override the profile. |
--host ADDR |
Address to listen on. Default 0.0.0.0. |
--memstore PATH, --python PATH, --db FILE, --limit N |
As for qwen_memchat.py. |
--place system|user |
Where the facts go (see above). |
--drop-tools WORDS |
The llama-server web page offers the model its own tools. A tool whose name or description contains one of these comma-separated words is removed before the request is forwarded. Default none for both profiles (every tool passes through). runtime removes the page's get_info tool ("Get runtime info (OS name)"; the page's other tool is get_datetime): it reports the browser PC's operating system, and the 0.5B answered "Windows" from it instead of using the memory. On the 0.5B even the harmless get_datetime made some answers wander or start writing a tool call; --drop-tools all removes both if that happens again. none lets every tool through; all removes every tool. The log lists the tools the page offers (once per set) and what was dropped, so the real names can be checked. |
--refresh S |
Seconds between memory re-reads. Default 5. |
--stages DIR |
Folder of stage programs. Default stages next to qwen_memproxy.py. none switches stages off, which is the way to get a plain run for comparison. |
--control FILE |
Control file for the stages. Default stages.control inside the stages folder. |
--stage-timeout S |
Seconds a stage may take before it is stopped. Default 120. |
--stage-python PATH |
Python used to run .py stages. Default: the one running the proxy. |
--quiet |
One log line per chat query, instead of the full detail. Verbose is the default. |
It prints qwen_memproxy 1.1.0 (2026-10-26) once at start, to its log.
--quiet is given its log shows every query, the text actually sent to the model, each stage's output and the reply. The log files therefore hold whatever you type. Use qwen_memchat.py when you want transcripts for the proposer.0 stopped normally, 2 the port could not be opened (often because one is already running).
sh QwenMemProxy.sh stop 05b (or 4b) asks the program to stop itself and finish cleanly. Stop the go-between first, then the Qwen server in your usual way.
remember:)Start a message with remember: and the text after it is saved to the memory store.
Example: remember: my name is Andrew
stated. The whole line you typed is kept as its quote, and the source is memproxy.chat request: remember: saved ... and memstore.py list.remember: lines in the chat history never save again.add approves immediately, as the manual for memstore.py says.| Version | Date | Change |
|---|---|---|
| 1.4.1 | 2026-10-27 | qwen_memproxy.py: @needs header line with a start-up warning for missing files; work order gains sql_python and profile. New stages/hotwords_rag_stage.py 1.0.0 and stages/hotwords_store_stage.py 1.0.0 (around hotwords.py 2.1.0). contextdef_stage.py 1.0.1 (adds @needs). stages.control is no longer in the package, so an edited copy is not overwritten. |
| 1.4.0 | 2026-10-27 | qwen_memproxy.py: stages. The proxy scans a folder for .py and .sh programs and hooks each in at the point its @hook header line names (before-model or after-reply; each-word is reserved), ordered by @order. Optional control file. Verbose log by default (--quiet to reduce). New stages/contextdef_stage.py 1.0.0 runs contextdef_05b.py. |
| 1.3.1 | 2026-10-27 | qwen_memproxy.py: --drop-tools default is now none for both profiles, so every page tool passes through (was all for 05b, runtime for 4b). |
| 1.3.0 | 2026-10-27 | qwen_memproxy.py: a message starting with remember: saves the rest as an approved fact. |
| 1.2.1 | 2026-10-27 | qwen_memproxy.py: --drop-tools default is now per profile: all for 05b, runtime for 4b. |
| 1.2.0 | 2026-10-27 | qwen_memproxy.py: added --drop-tools (default runtime) to remove page tools before forwarding; the tools the page offers are logged once. |
| 1.1.0 | 2026-10-26 | qwen_memproxy.py: added --place system|user. QwenMemProxy.sh 1.0.2: reports FAILED and exits 1 when the program never starts listening. |
| 1.0.1 | 2026-10-26 | Per-request log line. QwenMemProxy.sh waits up to 30 s for start-up. |
| 1.0.0 | 2026-10-26 | First release of qwen_memproxy.py and QwenMemProxy.sh. |