Skip to content

Commit ec804d6

Browse files
committed
Chromadb client-server flow
1 parent 73657bb commit ec804d6

File tree

10 files changed

+128
-111
lines changed

10 files changed

+128
-111
lines changed

deploy/docker/docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ services:
268268
image: 'chromadb/chroma:latest'
269269
environment:
270270
IS_PERSISTENT: 'TRUE'
271+
healthcheck:
272+
test: [ "CMD", "/bin/bash", "-c", "cat < /dev/null > /dev/tcp/localhost/8000" ]
273+
interval: 15s
274+
timeout: 15s
275+
retries: 15
276+
start_period: 20s
271277
volumes:
272278
- chromadb-data:/data
273279
# ports:

deploy/helm/values.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@ apiGatewayService:
327327
serviceSelectorLabels:
328328
app: gateway-service
329329

330-
331330
chromadb:
332331
name: chromadb
333332
image: chromadb/chroma

services/chatbot/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ faiss-cpu==1.11.0
1919
psycopg2-binary
2020
uvicorn==0.35.0
2121
fastmcp==2.10.2
22-
chromadb-client==1.0.15
22+
chromadb-client==1.0.15
23+
langchain-chroma==0.2.4

services/chatbot/src/chatbot/chat_service.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from uuid import uuid4
22
from langgraph.graph.message import Messages
3-
from services.chatbot.src.chatbot.retrieverutils import add_to_chroma_collection
3+
from .vectordb import update_collection
44
from .extensions import db
55
from .langgraph_agent import execute_langgraph_agent
66

@@ -37,9 +37,7 @@ async def process_user_message(session_id, user_message, api_key, model_name, us
3737
history.append(
3838
{"id": response_message_id, "role": "assistant", "content": reply.content}
3939
)
40-
await add_to_chroma_collection(
41-
api_key, session_id, [{"user": user_message}, {"assistant": reply.content}]
42-
)
40+
await update_collection(api_key, session_id, {"user": user_message, "assistant": reply.content})
4341
# Limit chat history to last 20 messages
4442
history = history[-20:]
4543
await update_chat_history(session_id, history)

services/chatbot/src/chatbot/langgraph_agent.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
1-
import os
21
import textwrap
3-
from typing import Annotated, Sequence, TypedDict
4-
5-
from langchain.agents.agent_toolkits import create_retriever_tool
6-
from langchain.chains import LLMChain, RetrievalQA
7-
from langchain.prompts import PromptTemplate
8-
from langchain.schema import BaseMessage
9-
from langchain.tools import Tool
102
from langchain_community.agent_toolkits import SQLDatabaseToolkit
11-
from langchain_community.agent_toolkits.sql.base import create_sql_agent
12-
from langchain_community.document_loaders import DirectoryLoader, TextLoader
13-
from langchain_community.embeddings import OpenAIEmbeddings
14-
from langchain_community.vectorstores import FAISS # or Chroma, Weaviate, etc.
153
from langchain_openai import ChatOpenAI
16-
from langgraph.graph import MessageGraph, StateGraph
17-
from langgraph.graph.message import add_messages
184
from langgraph.prebuilt import create_react_agent
19-
from chromadb.config import DEFAULT_TENANT, DEFAULT_DATABASE, Settings
20-
21-
225
from .extensions import postgresdb
23-
from .config import Config
246
from .mcp_client import get_mcp_client
25-
import chromadb
267

278

289
async def build_langgraph_agent(api_key, model_name, user_jwt):
@@ -69,8 +50,6 @@ async def build_langgraph_agent(api_key, model_name, user_jwt):
6950
mcp_tools = await mcp_client.get_tools()
7051
db_tools = toolkit.get_tools()
7152
tools = mcp_tools + db_tools
72-
retriever_tool = await get_retriever_tool(api_key)
73-
tools.append(retriever_tool)
7453
agent_node = create_react_agent(model=llm, tools=tools, prompt=system_prompt)
7554
return agent_node
7655

services/chatbot/src/chatbot/retrieverutils.py

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from langchain.agents.agent_toolkits import create_retriever_tool
2+
from .config import Config
3+
import chromadb
4+
from uuid import uuid4
5+
from langchain_core.documents import Document
6+
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
7+
8+
async def get_collection(api_key):
9+
chroma_client = await chromadb.AsyncHttpClient(
10+
host=Config.CHROMA_HOST,
11+
port=Config.CHROMA_PORT,
12+
)
13+
embeddings = OpenAIEmbeddingFunction(api_key=api_key, model_name="text-embedding-3-large")
14+
collection = await chroma_client.get_or_create_collection(
15+
name="chats",
16+
embedding_function=embeddings,
17+
)
18+
return collection
19+
20+
async def update_collection(api_key, session_id, new_messages):
21+
docs = []
22+
ids = []
23+
for role, content in new_messages.items():
24+
if content:
25+
doc_id = str(uuid4())
26+
doc = Document(
27+
page_content=content,
28+
metadata={"session_id": session_id, "role": role}
29+
)
30+
docs.append(doc)
31+
ids.append(doc_id)
32+
33+
if docs:
34+
collection = await get_collection(api_key)
35+
await collection.add(
36+
documents=[d.page_content for d in docs],
37+
metadatas=[d.metadata for d in docs],
38+
ids=ids
39+
)
40+
41+
# async def get_retriever_tool(api_key):
42+
# collection = await get_chroma_collection(api_key)
43+
# retriever = collection.as_retriever()
44+
# retriever_tool = create_retriever_tool(
45+
# retriever,
46+
# name="chat_rag",
47+
# description="""
48+
# Use this to answer questions based on user chat history (summarized and semantically indexed).
49+
# Use this when the user asks about prior chats, what they asked earlier, or wants a summary of past conversations.
50+
51+
# Use this tool when the user refers to anything mentioned before, asks for a summary of previous messages or sessions,
52+
# or references phrases like 'what I said earlier', 'things we discussed', 'my earlier question', 'until now', 'till date', 'all my conversations' or 'previously mentioned'.
53+
# The chat history is semantically indexed and summarized using vector search.
54+
# """,
55+
# )
56+
# return retriever_tool

services/chatbot/src/mcpserver/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ class Config:
1010
CHROMA_PERSIST_DIRECTORY = os.getenv("CHROMA_PERSIST_DIRECTORY", "/app/vectorstore")
1111
OPENAPI_SPEC = os.getenv("OPENAPI_SPEC", "/app/resources/crapi-openapi-spec.json")
1212
API_USER = os.getenv("API_USER", "[email protected]")
13-
API_PASSWORD = os.getenv("API_PASSWORD", "Admin!123")
13+
API_PASSWORD = os.getenv("API_PASSWORD", "Admin!123")
14+
CHROMA_HOST = os.environ.get("CHROMA_HOST", "chromadb")
15+
CHROMA_PORT = os.environ.get("CHROMA_PORT", "8000")

services/chatbot/src/mcpserver/server.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import httpx
2-
from fastmcp import FastMCP, settings
2+
from fastmcp import FastMCP
33
import json
44
import os
55
from .config import Config
66
import logging
77
import time
88
from .tool_helpers import (
99
get_any_api_key,
10-
get_chat_history_retriever,
10+
build_retrieverQA,
1111
)
1212

1313
# Configure logging
@@ -78,6 +78,27 @@ def get_http_client():
7878
openapi_spec=openapi_spec, client=get_http_client(), name="My crAPI MCP Server"
7979
)
8080

81+
@mcp.tool(tags={"history", "search", "summary", "context"},)
82+
async def retriever_tool(question: str) -> str:
83+
"""Answer questions based on user chat history (summarized and semantically indexed).
84+
Use this when the user asks about prior chats, what they asked earlier, or wants a summary of past conversations.
85+
Answer questions based on the user's prior chat history.
86+
87+
Use this tool when the user refers to anything mentioned before, asks for a summary of previous messages or sessions,
88+
or references phrases like 'what I said earlier', 'things we discussed', 'my earlier question', 'until now', 'till date', 'all my conversations' or 'previously mentioned'.
89+
The chat history is semantically indexed and summarized using vector search."""
90+
91+
logger.info(f"search_chat_history called with: {question}")
92+
api_key=await get_any_api_key()
93+
if not api_key:
94+
logger.error("API key is not available. Cannot search chat history.")
95+
return "OpenAI API key is not available. Cannot search chat history."
96+
retrieverQA = build_retrieverQA(api_key=api_key)
97+
response = await retrieverQA.ainvoke({"query": question})
98+
result = response["result"]
99+
logger.info(f"RESULT: {result}")
100+
return result
101+
81102
if __name__ == "__main__":
82103
mcp_server_port = int(os.environ.get("MCP_SERVER_PORT", 5500))
83104
mcp.run(

services/chatbot/src/mcpserver/tool_helpers.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import os
2+
import chromadb
23
from langchain_community.embeddings import OpenAIEmbeddings
3-
from langchain_community.vectorstores import Chroma
44
from langchain.prompts import PromptTemplate
55
from chatbot.extensions import db
66
from .config import Config
77
from langchain.chains import RetrievalQA
88
from langchain_openai import ChatOpenAI
9+
from langchain_chroma import Chroma
10+
911

1012
async def get_any_api_key():
1113
if os.environ.get("CHATBOT_OPENAI_API_KEY"):
@@ -16,3 +18,36 @@ async def get_any_api_key():
1618
if doc and "openai_api_key" in doc:
1719
return doc["openai_api_key"]
1820
return None
21+
22+
23+
def build_retrieverQA(api_key: str):
24+
prompt_template = PromptTemplate.from_template(
25+
"""You are an assistant that summarizes chat history across sessions.
26+
27+
Given the following chat excerpts:
28+
{context}
29+
Answer the user's question: {question}
30+
31+
If the user asks for a summary, provide a coherent, high-level summary of the conversations in natural language.
32+
If the user asks a specific question, extract and answer it from the chats.
33+
Be detailed, accurate, and neutral."""
34+
)
35+
36+
chroma_client = chromadb.HttpClient(
37+
host=Config.CHROMA_HOST,
38+
port=Config.CHROMA_PORT,
39+
)
40+
embeddings = OpenAIEmbeddings(api_key=api_key, model="text-embedding-3-large")
41+
vectorstore = Chroma(
42+
collection_name="chats",
43+
embedding_function=embeddings,
44+
client=chroma_client,
45+
)
46+
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 5})
47+
return RetrievalQA.from_chain_type(
48+
llm=ChatOpenAI(api_key=api_key, model="gpt-4o"),
49+
retriever=retriever,
50+
chain_type="stuff",
51+
chain_type_kwargs={"prompt": prompt_template, "document_variable_name": "context"},
52+
return_source_documents=False,
53+
)

0 commit comments

Comments
 (0)