Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
GROQ_API_KEY=your_groq_key_here
COHERE_API_KEY=your_cohere_key_here

# Vector DB
# Vector DB (Qdrant remains the default local backend)
VECTOR_SEARCH_BACKEND=qdrant

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire the backend selector into retrieval construction

Setting VECTOR_SEARCH_BACKEND=pinecone has no runtime effect because the setting is never read anywhere; ai/graph/langgraph_flow.py still imports and unconditionally constructs QdrantSearch at lines 2 and 12. Consequently, every user following the new opt-in instructions continues querying Qdrant instead of the configured Pinecone index, so the advertised backend cannot be activated.

Useful? React with 👍 / 👎.

QDRANT_HOST=localhost
QDRANT_PORT=6333

# Optional Pinecone backend
PINECONE_API_KEY=
PINECONE_INDEX_NAME=trojanchat
PINECONE_NAMESPACE=trojanchat

# Inference Cache
REDIS_URL=redis://localhost:6379
CACHE_ENABLED=true
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<img src="https://img.shields.io/badge/Streamlit-frontend-FF4B4B?logo=streamlit&logoColor=white" alt="Streamlit">
<img src="https://img.shields.io/badge/Docker-non--root-2496ED?logo=docker&logoColor=white" alt="Docker">
<img src="https://img.shields.io/badge/SBOM-CycloneDX-2D9CDB" alt="CycloneDX SBOM">
<img src="https://img.shields.io/badge/Pinecone-Optional%20Semantic%20Search-00A98F?logo=pinecone&logoColor=white" alt="Pinecone optional semantic search">
</p>

<p align="center">
Expand Down Expand Up @@ -95,6 +96,20 @@ The intended use, excluded use, data/credential handling, model or algorithm lim
Use the linked production-readiness issue for this repository as the checklist. Resolve missing tests, deployment instructions, observability, supply-chain controls, and release evidence before attaching a production claim.


## Optional Pinecone semantic search

TrojanChat keeps Qdrant as the default local vector backend and provides an opt-in Pinecone adapter for hosted retrieval.

```bash
python -m pip install -r requirements-pinecone.txt
export VECTOR_SEARCH_BACKEND=pinecone
export PINECONE_API_KEY=***
export PINECONE_INDEX_NAME=trojanchat
export PINECONE_NAMESPACE=trojanchat
```

Pinecone quality and operations must be benchmarked independently from the existing bounded in-process storage benchmark: record embedding model, corpus revision, top-k, recall@k, p95/p99 latency, error rate, and cost. Keep credentials in deployment secrets and retain Qdrant/local fallback for offline tests.

## Engineering evidence

| Evidence | Current result | Enforcement |
Expand Down
52 changes: 52 additions & 0 deletions ai/retrieval/pinecone_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

import os
from typing import Any

from ai.embeddings.cohere_embedder import CohereEmbedder


class PineconeSearch:
"""Pinecone alternative to the existing Qdrant semantic-search path."""

def __init__(self, index_name: str | None = None):
api_key = os.getenv("PINECONE_API_KEY", "").strip()
self.index_name = (
index_name or os.getenv("PINECONE_INDEX_NAME", "trojanchat")
).strip()
self.namespace = os.getenv("PINECONE_NAMESPACE", "trojanchat").strip()

if not api_key:
raise RuntimeError(
"PINECONE_API_KEY is required when VECTOR_SEARCH_BACKEND=pinecone."
)
if not self.index_name or not self.namespace:
raise RuntimeError("PINECONE_INDEX_NAME and PINECONE_NAMESPACE are required.")

from pinecone import Pinecone

self.index = Pinecone(api_key=api_key).Index(self.index_name)
self.embedder = CohereEmbedder()

def search(
self,
query: str,
top_k: int = 5,
filter_condition: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
vector = self.embedder.embed_text(query)
response = self.index.query(
vector=vector,
top_k=top_k,
include_metadata=True,
namespace=self.namespace,
filter=filter_condition,
)
return [
{
"id": match.id,
"score": match.score,
"payload": match.metadata or {},
}
for match in response.matches
]
2 changes: 2 additions & 0 deletions requirements-pinecone.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Optional hosted semantic-search backend.
pinecone>=6.0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install the adapter's Cohere dependency

In a clean environment following the documented install command, importing PineconeSearch fails with ModuleNotFoundError: cohere: this file installs only pinecone, while pinecone_search.py imports CohereEmbedder, whose module imports cohere at module load time. The base requirements.txt also does not declare cohere, so installing both declared requirement files still leaves the new adapter unusable.

Useful? React with 👍 / 👎.

Loading