Add interactive chat REPL with persistent sessions#16
Closed
Conversation
Member
Author
Code reviewFound 2 issues:
OpenKB/openkb/agent/chat_session.py Lines 130 to 179 in e1207f4
Lines 337 to 346 in e1207f4 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Introduces `openkb chat`, a multi-turn conversation REPL that stores each session under `.openkb/chats/<id>.json` so conversations survive across invocations and can be resumed by id or prefix. Built on prompt_toolkit for input editing and a bottom toolbar, and reuses the existing query agent so tool calls and streaming behavior match `openkb query`. Supports `--resume`, `--list`, `--delete`, and `--no-color`, plus in-REPL slash commands (/exit, /clear, /save, /help) where /save exports a human-readable transcript to wiki/explorations/.
prompt_toolkit's ANSI color table has no ansibrightwhite — the bright white slot is named ansiwhite (and ansigray is the dim one). Use ansiwhite for the toolbar session id; boldness still distinguishes it from the surrounding toolbar text.
prompt_toolkit's default bottom-toolbar style is `reverse`, which flipped our `bg:ansiblue fg:ansiwhite` into the gray-background / dark-text we were seeing. Add `noreverse` to the toolbar classes so the intended blue background with white text renders correctly.
Drops the blue background from the bottom toolbar and tones it down to match the rest of the dim chrome. Replaces the terminal-dependent ansibrightblack (which rendered near-black in dark themes) with a fixed #8a8a8a so the dim gray stays readable across themes. Rebuilds the header to mirror the Claude Code style: a bold title with version suffix, followed by "<kb path> · <model> · session <id>" and the short help hint. Adds `_openkb_version` and `_display_kb_dir` helpers so the path collapses to `~/...` when under $HOME.
Use print_formatted_text with end="" so the `\n` in our own strings isn't doubled by the function's default newline, which was putting a blank line between every header row. Add explicit blank lines before and after the header block, and emit two newlines after each turn so the next `>>>` has breathing room instead of sitting flush against the response.
Switch the three chat accent styles (title, prompt symbol, resume turn numbers) to the brand blue #5fa0e0 so the REPL reads as an OpenKB surface rather than a generic cyan terminal prompt. Tighten the streaming cadence so tool call lines sit flush against the preceding text (they're part of the same "thought") while a blank line always separates a tool batch from the response text that follows.
Three related touch-ups to the three agent prompts: - Fix a copy-paste bug where the Q&A and lint agents were told to "Write all wiki content in X language" — the Q&A agent doesn't write wiki content, and the lint agent writes reports. Switch them to "Answer in X" and "Write the lint report in X" respectively. The compiler agent keeps its original wording since it actually writes wiki content. - Give all three agents an OpenKB identity in their opening line so the model introduces itself consistently when asked who it is. - In the Q&A search strategy, finish the thought on summaries (tell the model to follow the `full_text` path when a summary is too thin), trim step 5 so the get_image tool's "when to call" guidance lives in the tool docstring instead of the instructions template, and reword step 5 to refer to the tool by name with "the ... tool".
The Q&A agent had an odd naming wart: the helper in openkb.agent.tools was called get_page_content (no wiki_ prefix like its siblings read_wiki_file, list_wiki_files, read_wiki_image, write_wiki_file), so the @function_tool wrapper had to be named get_page_content_tool and do a lazy local import to avoid a name collision. The instructions template meanwhile referred to the tool as get_page_content — a third name — leaving three spellings for one concept. Rename the helper to get_wiki_page_content so it matches the wiki_ convention, rename the wrapper to get_page_content so the tool name the model sees matches what the instructions have always said, and drop the lazy-import workaround. Update the test imports, call sites, class name, and the one assertion in test_query that was still checking for the old wrapper name (that assertion was already broken by earlier work).
Previously openkb/__init__.py had a hand-written __version__ = "0.1.0" that drifted out of sync with pyproject.toml's version = "0.1.0.dev0", and the chat REPL had a three-level try/except fallback to paper over which string it would actually read. Make pyproject.toml the single source of truth by having __init__.py pull its __version__ from the installed package metadata via importlib.metadata, and simplify _openkb_version in chat.py to just import __version__.
Change the three exit paths (/exit command, Ctrl-C at empty prompt, Ctrl-D) from a curt "Bye." to "Bye. Thanks for using OpenKB." with a trailing blank line so the shell prompt isn't flush against the goodbye.
Bump the tool and tool.name styles from #8a8a8a to #a8a8a8 so streaming tool-call lines sit visually between the static dim chrome (header, slash help, resume previews — still #8a8a8a) and the default-colored response text. Gives the "something is happening" lines a touch more presence without making them compete with the answer.
A single stray Ctrl-C at the prompt now prints "(Press Ctrl-C again to exit)" and returns to the prompt. A second Ctrl-C within 2s (or Ctrl-D / /exit) actually quits. Matches the bash/zsh safety dance and avoids losing an open session to a fat-fingered interrupt. Typing anything at the prompt resets the window, so the warning only persists across consecutive lone interrupts.
list_sessions was sorting by filename (which is the session id, a timestamp of when the session was created). That means a session you resumed recently but created days ago would sink below sessions you created today but haven't touched since — the opposite of what you want in a "pick a session to resume" UI. Sort by updated_at instead with the session id as a tiebreaker so the most recently active session is always at the top of `openkb chat --list`.
Add a dedicated "Interactive chat" subsection under Usage that covers what chat is, how it differs from one-off query, the session management flags, and where to find the slash commands. Add the `openkb chat` row to the Commands table, add a chat step in the Quick start (replacing the lint step, which was already covered by its own bullet), and surface chat as its own feature bullet. While there, polish a few of the existing feature bullets: rename "Any format" to "Broad format support" to avoid overclaiming, tighten the "Auto wiki" bullet into "Compiled Wiki" with a single sentence that ends on the "kept in sync" value prop, and tag the "Query" bullet as one-off so it reads in contrast to chat.
Member
Author
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
12 tasks
Member
Author
|
Superseded by #18 (same content, rebased into logical commits, plus a new commit sanitizing base64 image payloads from persisted chat history). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
openkb chat, a multi-turn conversation REPL for the knowledge base. Conversations are automatically persisted to.openkb/chats/<id>.jsonand can be resumed across invocations. Also bundles a handful of adjacent cleanups that were surfaced while building it.Main feature
openkb chatCLI command with--resume [ID](latest or by id / unique prefix),--list,--delete ID, and--no-color./exitquit cleanly./exit,/clear(start a fresh session, previous is kept on disk),/save [name](export markdown transcript towiki/explorations/),/help.openkb query(dim tool-call lines flush against the preceding text, blank line separating a tool batch from the following response paragraph).<kb dir> · <model> · session <id>, then a short key hint.... N earlier turns omitted).build_query_agentso tool behavior is identical to single-shotquery.Prompt / agent polish bundled in
full_textwhen a summary is too thin), move theget_image"when to call" guidance into the tool docstring, and reword step 5 to refer to the tool by name.Refactor bundled in
get_page_contentnaming: rename the helper inopenkb.agent.toolstoget_wiki_page_content(matching the*_wiki_*sibling convention), rename the agent-side wrapper fromget_page_content_tooltoget_page_contentso the tool name the model sees matches what the instructions have always said, drop the lazy-import workaround, and fix a stale test assertion.openkb.__version__from installed package metadata (importlib.metadata) sopyproject.tomlis the single source of truth for the version string.Test plan
openkb chatfrom a kb root starts a new session, header renders, prompt works/savewrites a readable transcript towiki/explorations//clearstarts a fresh session; previous shows up inopenkb chat --list/exitquit with the friendly goodbye messageopenkb chat --resumepicks up the latest session;--resume <prefix>resolves a unique prefixopenkb chat --listand--delete <id>workopenkb querystill works (shared agent)pytest tests/test_agent_tools.py tests/test_query.pypasses (31/31 locally)