commit - 830d8191936320c0ae569b373d61df7a307efae8
commit + d0d125e7ac0a5cc3e9924691160301dae740bcc0
blob - 28cc2e8d33bdf6da8ef2122058b7babb82a09beb
blob + 6ffc0ca8fa7694b4627f0701462b98f3b70b378d
--- rag_backend.py
+++ rag_backend.py
import argparse
+import logging
+from chromadb import Settings
from langchain_chroma import Chroma
from langchain.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
class RagBackend:
def __init__(self, db_path: str = None):
self.db_path = db_path if db_path else DB_PATH
- embedding_function = embeddings()
- self.db = Chroma(persist_directory=self.db_path, embedding_function=embedding_function)
+ self.db = Chroma(
+ persist_directory=self.db_path,
+ embedding_function=embeddings(),
+ client_settings=Settings(anonymized_telemetry=False)
+ )
self.model = OllamaLLM(model="llama3")
- def query(self, query_text: str, history: str) -> list[list[str]]:
+ def query(self, query_text: str, history: str) -> (str, list[str]):
# look up possible context from the index
context_docs = self.db.similarity_search_with_score(query_text, k=5)
if __name__ == "__main__":
- # Create CLI.
+ logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s %(name)s %(message)s'
+ )
+
parser = argparse.ArgumentParser()
parser.add_argument("--db", default=DB_PATH, help="path to the database")
parser.add_argument("query_text", type=str, help="The query text.")
blob - bf9f1c65f4f8d39eed574808bd702857278f938d
blob + 28da3f54b65e1e8b1cb46f9c296e7ae2e2e40e0b
--- rag_indexer.py
+++ rag_indexer.py
from langchain_community.document_loaders.text import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
-from configuration import embeddings
+from configuration import embeddings, DB_PATH
class RagIndexer:
self._db.add_documents(batch_chunks, ids=batch_chunk_ids)
logging.info(f"new chunk batch {start_idx + 1} to {end_idx} added")
else:
- logging.warn("no new or updated chunks found")
+ logging.warning("no new or updated chunks found")
def calculate_chunk_ids(self, chunks: list[Document]) -> list[Document]:
# This will create IDs like "source.ext:6:2"
blob - fde7de1e6fc84adcd3fa7758a0ffcb7516fa09ea
blob + 54f332f4b361ede4044c6272cdf3d3da1c1fa3d1
--- rag_interface.py
+++ rag_interface.py
+import logging
+
from flask import Flask, request, jsonify, render_template
from rag_backend import RagBackend
app = Flask(__name__)
rag = RagBackend()
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s %(name)s %(message)s'
+)
+
+
@app.route('/')
def home():
return render_template("page.html")
-@app.route('/chat', methods=['POST'])
-def chat():
- user_message = request.json.get('message', '')
- history_message = request.json.get('history', '')
- llm_response, references = rag.query(user_message, history_message)
- return jsonify({
- 'reply': {
- 'text': llm_response,
- 'references': references
- }
- })
+# MCP endpoint
+@app.route('/mcp', methods=['POST'])
+def handle_mcp():
+ data = request.get_json()
+ method = data.get('method')
+ params = data.get('params', {})
+ # Dispatch to the correct tool
+ if method == "llm_chat":
+ result, refs = rag.query(params.get('query', ''), params.get('history', ''))
+ return jsonify({
+ "jsonrpc": "2.0",
+ "result": {
+ 'text': result,
+ 'ref': refs},
+ "id": data.get("id")
+ })
+ # Add more tool handlers as needed
+ return jsonify({"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": data.get("id")})
if __name__ == '__main__':
blob - 5ea01edd5c332347a36464da1aa3f7e359924abe
blob + c51160a44bcbdf3037f850b08e4a5a7d08efbc4b
--- requirements.txt
+++ requirements.txt
flask
chromadb
pytest
+mcp[cli]
\ No newline at end of file
blob - 1cc031ac6eccb63d792a719f289a4ec552b2d5a4
blob + 9aa19b8971ac34dacc009c9b1ac133747ce7d5e0
--- templates/page.html
+++ templates/page.html
input.disabled = isBusy;
}
- // Helper to construct file link from reference object
- function referenceToLink(refObj) {
- // refObj: { ref: "filename.pdf:12:34", title: "Title" }
- const match = refObj.ref.match(/^(.+?):(\d+):(\d+)$/);
- if (!match) return refObj.ref;
- const filename = match[1];
- const page = match[2];
- const url = `/static/files/${encodeURIComponent(filename)}#page=${page}`;
- // Show the title (bold), then the link
- return `<a href="${url}" target="_blank" rel="noopener">${refObj.title}, p.${page}</a>`;
+ async function sendMCPMessage(query, historyText) {
+ const requestId = Date.now();
+ const requestPayload = {
+ jsonrpc: "2.0",
+ method: "llm_chat",
+ params: { query: query, history: historyText },
+ id: requestId
+ };
+
+ const response = await fetch('/mcp', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(requestPayload)
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ if (data.error) {
+ throw new Error(data.error.message || 'Unknown MCP error');
+ }
+
+ return data.result;
}
- send.onclick = function() {
+ send.onclick = async function() {
const text = input.value.trim();
if (!text) return;
appendMessage('user', text);
history.forEach(entry => {
context_prompt += `Question: ${entry.question}\nAnswer: ${entry.answer}\n`;
});
- fetch('/chat', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ message: text, history: context_prompt })
- })
- .then(response => response.json())
- .then(data => {
- appendMessage('bot', data.reply.text);
- // Save to history
- history.push({ question: text, answer: data.reply.text });
- setBusy(false);
- })
- .catch(() => {
- appendMessage('bot', "Sorry, there was an error.");
+
+ try {
+ const result = await sendMCPMessage(text, context_prompt);
+ appendMessage('bot', result.text);
+ history.push({ question: text, answer: result.text });
+ } catch (error) {
+ appendMessage('bot', `Error: ${error.message}`);
+ } finally {
setBusy(false);
- });
+ }
};
input.addEventListener("keyup", function(event) {