Module: Brainchat::Chat
- Defined in:
- lib/brainchat/chat.rb,
sig/brainchat.rbs
Overview
Answers a question with an LLM, using retrieved knowledge-brain chunks as the only context. Chunks are numbered in the prompt and the model is asked to cite them as [n]; the CLI prints the [n] -> path:lines mapping after the answer, so every citation is resolvable to a file.
Constant Summary collapse
- OLLAMA_DEFAULT_BASE =
Ollama's conventional local endpoint, so --provider ollama needs no setup.
"http://localhost:11434/v1"- FAILURES =
A missing API key or an unknown model id raise off a different branch of RubyLLM's hierarchy than provider/HTTP faults, and they are the two most likely failures here, so both branches are caught.
[RubyLLM::Error, RubyLLM::ConfigurationError, RubyLLM::ModelNotFoundError, Faraday::Error].freeze
- SYSTEM_PROMPT =
<<~PROMPT You answer questions about the user's knowledge-brain: a vault of notes, ADRs, plans, commit history and docs, retrieved for you by a hybrid BM25 + cosine search. The retrieved chunks are numbered and included in the user's message. Answer only from those chunks. When the chunks do not contain the answer, say so plainly instead of guessing. Cite every claim with the chunk number in square brackets, e.g. [1] or [2][3]. Keep the answer tight; quote file paths only via the citation numbers. PROMPT
Class Method Summary collapse
-
.call(question, chunks, on_chunk: nil, **chat_options) ⇒ Object
Asks
questionwithchunksas context. -
.configure ⇒ Object
RubyLLM reads no provider credentials from the environment on its own, and a CLI has nowhere else to get them.
-
.prompt_for(question, chunks) ⇒ Object
Chunks go over as a numbered list; the citation number the model emits is the position in this list, and the CLI prints the same numbering.
Class Method Details
.call(question, chunks, on_chunk: nil, **chat_options) ⇒ Object
Asks question with chunks as context. Streams each content delta to
on_chunk when given. Returns the final RubyLLM::Message.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/brainchat/chat.rb', line 36 def call(question, chunks, on_chunk: nil, **) configure chat = RubyLLM.chat(**.compact) .with_instructions(SYSTEM_PROMPT) if on_chunk chat.ask(prompt_for(question, chunks)) { |chunk| on_chunk.call(chunk.content) } else chat.ask(prompt_for(question, chunks)) end rescue *FAILURES => e raise Error, "chat failed: #{e.class}: #{e.}" end |
.configure ⇒ Object
RubyLLM reads no provider credentials from the environment on its own, and a CLI has nowhere else to get them.
51 52 53 54 55 56 57 |
# File 'lib/brainchat/chat.rb', line 51 def configure RubyLLM.configure do |config| config.anthropic_api_key = ENV.fetch("ANTHROPIC_API_KEY", nil) config.openai_api_key = ENV.fetch("OPENAI_API_KEY", nil) config.ollama_api_base = ENV.fetch("OLLAMA_API_BASE", OLLAMA_DEFAULT_BASE) end end |
.prompt_for(question, chunks) ⇒ Object
Chunks go over as a numbered list; the citation number the model emits is the position in this list, and the CLI prints the same numbering.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/brainchat/chat.rb', line 61 def prompt_for(question, chunks) body = chunks.each_with_index.map do |chunk, index| <<~CHUNK [#{index + 1}] #{chunk.location} (#{chunk.source_type}/#{chunk.repo}) #{chunk.text} CHUNK end.join("\n") <<~PROMPT Retrieved chunks: #{body} Question: #{question} PROMPT end |