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