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 =
"You answer questions about the user's knowledge-brain: a vault of notes,\nADRs, plans, commit history and docs, retrieved for you by a hybrid\nBM25 + cosine search. The retrieved chunks are numbered and included in\nthe user's message.\n\nAnswer only from those chunks. When the chunks do not contain the answer,\nsay so plainly instead of guessing. Cite every claim with the chunk\nnumber in square brackets, e.g. [1] or [2][3]. Keep the answer tight;\nquote file paths only via the citation numbers.\n"
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.message}" 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| " [\#{index + 1}] \#{chunk.location} (\#{chunk.source_type}/\#{chunk.repo})\n \#{chunk.text}\n CHUNK\n end.join(\"\\n\")\n\n <<~PROMPT\n Retrieved chunks:\n\n \#{body}\n\n Question: \#{question}\n PROMPT\nend\n" |