From 765bd9debec1d08e9e2f930a6df03029e505a4bf Mon Sep 17 00:00:00 2001 From: Corey Leath Date: Sat, 25 Jul 2026 20:21:30 -0400 Subject: [PATCH 1/4] feat: add opt-in Pinecone semantic search --- ai/retrieval/pinecone_search.py | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 ai/retrieval/pinecone_search.py diff --git a/ai/retrieval/pinecone_search.py b/ai/retrieval/pinecone_search.py new file mode 100644 index 0000000..5a1dbe7 --- /dev/null +++ b/ai/retrieval/pinecone_search.py @@ -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 + ] From 02b0a6da8d0270e22fa8455fd08af03ff1d85ad6 Mon Sep 17 00:00:00 2001 From: Corey Leath Date: Sat, 25 Jul 2026 20:21:36 -0400 Subject: [PATCH 2/4] build: add optional Pinecone dependency --- requirements-pinecone.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 requirements-pinecone.txt diff --git a/requirements-pinecone.txt b/requirements-pinecone.txt new file mode 100644 index 0000000..f00dbab --- /dev/null +++ b/requirements-pinecone.txt @@ -0,0 +1,2 @@ +# Optional hosted semantic-search backend. +pinecone>=6.0.0 From cc14b448242fe0115b9b5d7db4788994345085dd Mon Sep 17 00:00:00 2001 From: Corey Leath Date: Sat, 25 Jul 2026 20:21:43 -0400 Subject: [PATCH 3/4] config: document optional Pinecone search settings --- .env.example | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 0166e67..615bd7b 100644 --- a/.env.example +++ b/.env.example @@ -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 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 From d420d40e7930e8bd5a8df22ca51ce3986ca8c31a Mon Sep 17 00:00:00 2001 From: Corey Leath Date: Sat, 25 Jul 2026 20:21:53 -0400 Subject: [PATCH 4/4] docs: add Pinecone badge and semantic-search metrics --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 7230cbb..8a0cf85 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Streamlit Docker CycloneDX SBOM + Pinecone optional semantic search

@@ -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 |