The RAG section still described the pipeline as originally specced, not the
one that runs: fixed-size chunking, keyword-only retrieval, a schema without
kind or content_hash, and an `Indexer` type that does not exist. Someone
reading it to understand the retrieval path would have been wrong about every
part of it.
Section 4, rewritten:
- 4.1 documents both source kinds, the README skip, the frontmatter exclusion
and the ### sub-split, each with the failure that motivated it.
- 4.2 replaces the BM25-only pipeline with hybrid retrieval, and explains why
RRF rather than a weighted blend, what happens when the embedder is down,
and why vectors carry a content hash.
- 4.3 swaps the fictional code sketch for the real schema plus an API table.
- 4.4 documents prompt assembly in the order the code does it, why there is
exactly one system message, and the three attempts it took to get the
language right.
- The banner at the top listed four decisions as pending. All four are now
made and measured, including the one it got wrong: BM25 was strong on the
corpus but the questions arrive in Spanish, which is a translation problem
a trigram tokenizer does not solve.
- 4.5 claimed a ~3k-token system prompt leaving room for 2–3 turns. Measured,
the largest prompt is 1255 tokens and compaction never fired in the whole
benchmark.
Elsewhere:
- §2 adds the embeddings server and internal/embed; the stack table said
qwen2.5:1.5b while the config ships 3b.
- §6.1 and §8.1 did not deploy the architecture being described: no embedder,
no --device none, no --parallel 1, no sampling flags, and a systemd unit
that started only the bot. §8.1 now has all three units and the memory
budget.
- §9.4 lists the retrieval tests, since a regression there is silent.
- §11 marks the phases that are done, records where the widget deliberately
diverged from the plan (vanilla JS, not React + Tailwind), and keeps the
three known unfixed answer-quality issues.
- §12.1 replaces aspirational targets with measured numbers, and says plainly
that TTFT <500ms and end-to-end <3s are not met on 2 CPU cores and why that
is the accepted trade.
Both language editions updated in step.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the §4.5 Auto-compaction section to architecture.md and
architecture.es.md describing the trigger, fallback, persistence and
the new SSE 'compaction' event so consumers know how to react.
Includes the three placeholder projects used while exercising the
feature end to end (bot-onboarding, dashboard-metricas, tienda-ropa)
so /api/reindex picks them up without further setup.
Conversations survive page reloads and work for any frontend, not just
the widget. Server-side SQLite, conversation ID as bearer token, browser
identity via localStorage.
Backend
-------
- internal/portfolio/conversations.go: schema + CRUD. Conversations and
messages tables in the same SQLite DB as the RAG index, with
foreign-key cascade delete. Conv IDs are 16-byte random hex
(128 bits of entropy).
- internal/portfolio/indexer.go: applies conversation schema + enables
foreign_keys pragma in OpenStore.
- internal/server/handlers.go: POST /api/chat accepts an optional
conversation_id, mints one if absent, persists user message before
the LLM runs and assistant message (with sources) after the stream
completes. New handlers: GetConversation, ListConversations,
DeleteConversation.
- internal/server/server.go: routes for GET /api/conversations,
GET/DELETE /api/conversations/{id}.
- internal/server/conversations_test.go: 6 tests (round-trip, continue,
list, 404, delete, streaming).
Widget
------
- web/chat-widget.js: stores conv_id in localStorage["rony-chat-conv"],
includes it in the chat request body, captures new IDs from the
server's 'start' SSE event, and calls GET /api/conversations/{id} on
load to restore history. On 404 it clears the stored ID and starts
fresh.
Docs
----
- docs/architecture.md: §3.1 documents the conversation_id field and
new REST endpoints; new §3.4 covers persistence lifecycle, schema,
client responsibilities, and auth model. §5.6 updated; filetree
reflects the new files.
- web/README.md: new 'Conversation persistence' section explains the
browser-scoped behavior and how to opt out or persist across devices.
The library was renamed to rony-llm-agent but some docs still referenced
the old go-llm-agent name. Also fixed 'chat-bot/' → 'rony-chat-bot/' in
project structure diagrams to match the actual directory name.
- README.md: full English translation, .es.md preserved
- docs/architecture.md: full translation (1039 lines)
- configs/portfolio-bot.yaml: full English translation, .es.yaml preserved
- data/projects/README.md + example-project.md: translated with banners
Default language is now English (standard for OSS). Spanish remains
available via .es.* suffix files.