Repository navigation
feat(web): add an Ask chat panel backed by Ollama - #36
Merged
Merged
Conversation
Adds an "Ask" button to the top bar that slides out a chat panel on the right. It answers questions about the user's repositories with a model on an Ollama server, which can be another machine on the network. Ollama is optional: without it the panel says it cannot reach the server and nothing else changes. Core (packages/core/src/ai): - ChatBackend interface and an OllamaBackend over plain fetch (NDJSON streaming, abort, stall timeout, readable errors), injected like SearchBackend so tests use a fake. - [ollama] config table with GITLIB_OLLAMA_HOST, _MODEL and _NUM_CTX overrides. - A token budget that cuts history to fit the context window, because Ollama truncates silently, and a system prompt built from the repository list plus the current repository's notes and README, each with a fixed cap. - isGitLibError, a brand check for errors that cross a module-copy boundary: the dev server can load core twice, which made instanceof fail and turned an expected error into a 500. Web: - POST /api/chat streams NDJSON events. server.mjs now aborts the request when the browser disconnects, so closing the panel stops the model. - A non-modal Sheet panel mounted in the root layout, with a model picker, Stop, and states for turned off, unreachable and no models. Model output is rendered without images, which closes an exfiltration path through image URLs in text shaped by a hostile README. - The top bar's link list scrolls sideways so Ask stays visible on a phone; it already overflowed at 375px before this change. Docs: README Chat section, runbook troubleshooting rows, env and compose settings, and the research behind the design (docs/ollama-integration.md and docs/dependabot-watch.md, the latter not implemented here). Tested against a fake Ollama only (streaming, Stop, mid-stream errors, unreachable server, phone width, production server); not yet run with a real model. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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
Adds an Ask button to the top bar that slides out a chat panel on the right. It answers questions about your repositories with a model on an Ollama server, which can be another machine on the network. Ollama is optional: without it the panel says it cannot reach the server and nothing else changes.
The goal is to run local models against the data gitlib already holds, with a single 12 GB GPU in mind. The research behind the design is included in this PR (
docs/ollama-integration.md), along with a second note on a scheduled Dependabot watch that is not built here.What changed
Core (
packages/core/src/ai)ChatBackendinterface and anOllamaBackendover plainfetch: NDJSON streaming, abort, a stall timeout, and readable errors. It is injected intoGitLibApplikeSearchBackend, so tests use a fake.[ollama]config table withGITLIB_OLLAMA_HOST,GITLIB_OLLAMA_MODELandGITLIB_OLLAMA_NUM_CTXoverrides, mirroring[search].isGitLibError, a brand check for errors that cross a module-copy boundary (see reviewer notes).Web
POST /api/chatstreams NDJSON events.server.mjsnow aborts the request when the browser disconnects, so closing the panel or pressing Stop stops the model.Sheetpanel in the root layout (conversation survives navigation), with a model picker, Stop, a scope chip, and states for turned off, unreachable and no models installed.MarkdownBodygets anuntrustedmode used for model output: no images, and link targets are shown. An image URL in text shaped by a hostile README could otherwise carry data out.Docs and config: README "Chat" section, runbook troubleshooting rows,
.env.examplefiles anddocker-compose.yml.For reviewers
@ts-expect-errorinroutes/api.chat.ts.@tanstack/react-routerpinsrouter-core1.171.38 while Start pins 1.171.39, so Start's type for a route'sserveroption lands on a different copy and is not applied. The build findsserverby its shape in the object literal, so it has to stay written out there. Whennpm ls @tanstack/router-coreshows one copy the directive becomes an error, which is the cue to delete it. Realigning those versions is worth a separate change.isGitLibError. In the dev serverinstanceofagainst core's error classes failed (an expected error came back as a 500). There is a regression test.min-w-0and a scrolling link list.apps/web/src/routeTree.gen.tsis regenerated for the new route.main; a dry-run merge is clean.Not in this PR
Tool calling and retrieval beyond the capped prompt, a shared work queue for the GPU, and the Dependabot watch (research only, in
docs/dependabot-watch.md).Test plan
npm run typechecknpm test: 278 passing, 126 of them new (config, token budget, prompt building, Ollama client over a real local HTTP server, route handler, protocol parsing)npm run build, and no server code in the browser bundledocs/ollama-integration.md, ask a question, and confirmollama psshows the model fully on the GPU🤖 Generated with Claude Code