Skip to content

fix(everos): voyage rerank + timezone import after EverCore rename#7

Merged
Ptah-CT merged 3 commits into
mainfrom
fix/everos-bug-cluster-2026-05-15
May 15, 2026
Merged

fix(everos): voyage rerank + timezone import after EverCore rename#7
Ptah-CT merged 3 commits into
mainfrom
fix/everos-bug-cluster-2026-05-15

Conversation

@Ptah-CT
Copy link
Copy Markdown

@Ptah-CT Ptah-CT commented May 15, 2026

Summary

Two custom patches survived the upstream rename methods/evermemos/methods/EverCore/ only in the production deployment but not in this fork:

  • rerank_voyage.py dropped on rename. With RERANK_PROVIDER=voyage in production .env and RERANK_FALLBACK_PROVIDER=none (fail-fast policy), every search raised Unsupported provider: voyage from the rerank-factory. Rewritten (252 LOC) using rerank_deepinfra.py as a template, adapted for Voyage's plain-query/documents request and data: [{index, relevance_score}] response shape.
  • timezone import missing in episodic_memory_qdrant_repository.py:247 (tz=timezone.utc without from datetime import timezone). One-line fix.
  • Factory branch in rerank_service.py for voyage provider added between deepinfra and the else: raise RerankError.

Symptom was HTTP 200 + episodes=[] (not a hang) — silent fail mode that took three days to notice.

Test plan

  • Live-verified on database after deployment: 60 ES candidates → Qdrant vector_search 12 ms → Voyage rerank 15 results @ 0.4492 top score → 15 episodes returned in 5050 ms total
  • Service-Restart of all 5 instances (everos, everos-thot/ptah/anubis/sachmet) clean
  • Optional next: contribute rerank_voyage.py upstream to EverMind-AI/EverOS so it doesn't get re-dropped on future renames

Related

  • Doc update: XInfty/XInfty_docs#2 (EWM Bug-Cluster 2026-05-15)

…verCore rename

Two defects survived the methods/evermemos/ → methods/EverCore/ rename
and made retrieval return empty results (HTTP 200 with episodes=[]):

1. rerank_voyage.py was dropped on rename — RERANK_PROVIDER=voyage in .env
   then raises "Unsupported provider: voyage" in the factory. Rewritten
   from scratch (252 LOC) using rerank_deepinfra.py as a template, with
   Voyage-specific request shape (plain query/documents, no Qwen wrapping)
   and response mapping (data: [{index, relevance_score}] → standard
   {results: [{index, score, rank}]}).

2. episodic_memory_qdrant_repository.py used timezone.utc in
   datetime.fromtimestamp() but only imported `datetime`. NameError on
   every vector search. One-line fix in the import statement.

3. rerank_service.py factory branch for "voyage" added between deepinfra
   and the raise. Defaults base_url to https://api.voyageai.com/v1/rerank
   when env override is empty.

Verified live: 60 ES candidates → Qdrant vector_search 12 ms → Voyage
rerank 15 results @ 0.4492 top score → 15 episodes returned in 5050 ms.

Backups on database:
- rerank_service.py.bak.1779028699
- episodic_memory_qdrant_repository.py.bak.1779028653
- .env*.bak.1779028576 (env LLM_BASE_URL drift; not in scope for this repo)
@Ptah-CT Ptah-CT requested a review from DerAuctor as a code owner May 15, 2026 16:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Review Change Stack

Warning

Rate limit exceeded

@Ptah-CT has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 30 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 12361f16-3860-47bd-9bbc-72729f252d73

📥 Commits

Reviewing files that changed from the base of the PR and between 7804ba3 and 8c8bbb6.

📒 Files selected for processing (2)
  • FORK_PATCHES.md
  • methods/EverCore/src/agentic_layer/rerank_voyage.py
📝 Walkthrough

Walkthrough

Die Pull Request integriert die Voyage AI Rerank-API als neuer Provider durch eine neue Service-Klasse (VoyageRerankService) mit Konfiguration, asynchronem HTTP-Handling, Batch-Reranking und Response-Normalisierung. Factory-Integration und Timezone-Import unterstützen das neue Feature.

Changes

Voyage Rerank Service Integration

Layer / File(s) Summary
Configuration und Service-Initialisierung
methods/EverCore/src/agentic_layer/rerank_voyage.py
VoyageRerankConfig Dataclass mit API-Schlüssel, Base-URL (https://api.voyageai.com/v1/rerank), Modell (rerank-2.5), Timeout, Retry-Limits und Batch-Konfiguration; Service speichert Konfiguration und initialisiert Semaphor für Parallelitätskontrolle auf 5 concurrent Requests.
HTTP-Session und Anfrage-Retry-Logik
methods/EverCore/src/agentic_layer/rerank_voyage.py
Lazy-Initialisierung der aiohttp-Session mit Timeout und Bearer-Token-Headers; POST-Anfragen mit Query/Documents/Modell-Shape, Semaphore-gesteuerte Parallelität, HTTP-Fehler und Exceptions mit exponentiellem Backoff über 3 Retry-Versuche.
Response-Parsing und Format-Konvertierung
methods/EverCore/src/agentic_layer/rerank_voyage.py
Normalisiert Voyage API-Response data[{index, relevance_score}] in lineares Scores-Array; extrahiert input_tokens aus usage; konvertiert in EverOS-Format results[{index, score, rank}] mit absteigender Score-Sortierung und Längen-Anpassung auf Original-Dokumentanzahl.
Document und Memory Reranking
methods/EverCore/src/agentic_layer/rerank_voyage.py
rerank_documents teilt in batch_size-Blöcke, führt Batch-Anfragen parallel aus, aggregiert Scores/Token-Nutzung, nutzt TokenUsageCollector für Token-Tracking; rerank_memories extrahiert Texte aus Hits, rerankt und schreibt Scores basierend auf index zurück mit optionaler Top-K-Einschränkung; Fehler-Fallback auf Sortierung nach vorhandenem score-Feld.
Factory-Integration und Provider-Registrierung
methods/EverCore/src/agentic_layer/rerank_service.py
Importiert VoyageRerankService und VoyageRerankConfig; erweitert _create_service_from_config um elif provider.lower() == "voyage"-Branch, der Config mit Default-Endpoint instanziiert und Service zurückgibt.
Timezone-Import für Timestamp-Normalisierung
methods/EverCore/src/infra_layer/adapters/out/search/repository/episodic_memory_qdrant_repository.py
Erweitert datetime-Import um timezone-Klasse für UTC-konformes Parsen von Qdrant-Timestamps mit datetime.fromtimestamp(..., tz=timezone.utc).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • DerAuctor

Poem

🐰 Ein neuer Reranker hoppelt herbei,
Voyage AI macht die Suche geschwind und frei,
Mit Batches und Retries, so zuverlässig und flott,
Parallel wie Kaninchen—nie verloren im Slot!
Timestamps im UTC—zeitlos und wahr,
Der Service ist fertig, die Integration wunderbar! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Der Titel beschreibt präzise die beiden Hauptänderungen: Wiederherstellung des Voyage-Rerank-Providers und Behebung des fehlenden Timezone-Imports nach dem EverCore-Umbenennung.
Description check ✅ Passed Die Beschreibung erläutert detailliert die zwei behobenen Fehler aus der Umbenennungsoperation, das Testplan und Symptome vor der Behebung – vollständig relevant für den Changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/everos-bug-cluster-2026-05-15

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Issues are disabled on this fork, so the tracking list lives in-repo.
Documents every patch that exists only in XInfty/EverOS_Qdrant and must
be re-applied after each upstream sync:

- Voyage rerank provider + factory branch
- timezone import in episodic Qdrant repo
- 13× response_format json_schema strict across LLM call-sites
- Profile extractor post-parse coercion
- Intentional opt-outs (tool_pre_compress stays on json_object)

Each section has a one-liner grep so a future contributor (or CI guard)
can verify the patch is still present after a merge from upstream.

Concrete history motivating this file:
- 2026-05-12: response_format=json_object patches got exposed by a model
  swap; migrated 13/15 sites to strict json_schema.
- 2026-05-15: rerank_voyage.py + factory branch silently dropped during
  the methods/evermemos/ → methods/EverCore/ rename. Retrieval returned
  empty for three days before being noticed. Restored in #7.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7804ba3e19

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

config = VoyageRerankConfig(
api_key=api_key,
base_url=base_url or 'https://api.voyageai.com/v1/rerank',
model=model,
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 Select a Voyage model when provider is voyage

When RERANK_PROVIDER=voyage, this branch still forwards the shared RERANK_MODEL value unchanged, but the repo default is Qwen/Qwen3-Reranker-4B (methods/EverCore/env.template:121), which is not a Voyage rerank model. In that common configuration, Voyage returns a request error and reranking degrades/fails (especially with fallback disabled), so switching providers is broken unless users manually discover and override the model. Set a provider-specific default (e.g., rerank-2.5) when the model is unset or still on the non-Voyage default.

Useful? React with 👍 / 👎.

coderabbitai[bot]
coderabbitai Bot previously requested changes May 15, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@methods/EverCore/src/agentic_layer/rerank_service.py`:
- Around line 177-187: The Voyage branch currently always forwards the shared
model value to VoyageRerankConfig, preventing VoyageRerankConfig's internal
default (rerank-2.5) from being used; modify the branch so that
VoyageRerankConfig is constructed without a model argument when no explicit
model was provided (i.e., only pass model=model when model is not None/empty),
or explicitly pass None so the config can apply its default; update the
instantiation that creates VoyageRerankConfig in the provider=="voyage" branch
(referencing VoyageRerankConfig and VoyageRerankService) accordingly.

In `@methods/EverCore/src/agentic_layer/rerank_voyage.py`:
- Around line 217-219: The debug log currently prints the full query string
(logger.debug with query and len(all_texts)), which can expose tenant data;
update the logging in the rerank_voyage logic (look for logger.debug and the
variable query in rerank_voyage or surrounding function) to avoid logging raw
query text and instead log only non-sensitive metadata such as query length and
a deterministic hash (e.g., SHA-256 of query) plus num_texts (len(all_texts));
ensure you compute the hash from query safely and include a short label like
"query_len" and "query_hash" in the logger.debug call rather than the full query
content.
- Around line 62-71: The _ensure_session method can create multiple
aiohttp.ClientSession objects concurrently and leak them; guard session creation
with a single async lock/semaphore so only one coroutine creates and assigns
self.session at a time (e.g., acquire an existing self._session_lock or the same
Semaphore used elsewhere before checking/creating). Inside the lock, re-check
"if self.session is None or self.session.closed" then create ClientSession; if
you must replace an existing session ensure the old session is properly closed
with await self.session.close() before overwriting. Update _ensure_session to
use the lock/semaphore and the re-check pattern to prevent races and leaks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 824f4c1b-b352-4ace-9b07-f36280bb91f7

📥 Commits

Reviewing files that changed from the base of the PR and between f25eba3 and 7804ba3.

📒 Files selected for processing (3)
  • methods/EverCore/src/agentic_layer/rerank_service.py
  • methods/EverCore/src/agentic_layer/rerank_voyage.py
  • methods/EverCore/src/infra_layer/adapters/out/search/repository/episodic_memory_qdrant_repository.py

Comment on lines +177 to +187
elif provider.lower() == "voyage":
config = VoyageRerankConfig(
api_key=api_key,
base_url=base_url or 'https://api.voyageai.com/v1/rerank',
model=model,
timeout=timeout,
max_retries=max_retries,
batch_size=batch_size,
max_concurrent_requests=max_concurrent,
)
return VoyageRerankService(config)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Der Voyage-Branch neutralisiert den provider-spezifischen Modell-Default.

Hier wird immer das geteilte model übergeben; dadurch greift VoyageRerankConfig-Default (rerank-2.5) nie. Für provider="voyage" sollte ohne explizites Modell auf den Voyage-Default zurückgefallen werden.

🔧 Vorschlag zur Behebung
     elif provider.lower() == "voyage":
+        resolved_model = model
+        if not resolved_model or resolved_model == HybridRerankConfig.model:
+            resolved_model = VoyageRerankConfig().model
         config = VoyageRerankConfig(
             api_key=api_key,
             base_url=base_url or 'https://api.voyageai.com/v1/rerank',
-            model=model,
+            model=resolved_model,
             timeout=timeout,
             max_retries=max_retries,
             batch_size=batch_size,
             max_concurrent_requests=max_concurrent,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@methods/EverCore/src/agentic_layer/rerank_service.py` around lines 177 - 187,
The Voyage branch currently always forwards the shared model value to
VoyageRerankConfig, preventing VoyageRerankConfig's internal default
(rerank-2.5) from being used; modify the branch so that VoyageRerankConfig is
constructed without a model argument when no explicit model was provided (i.e.,
only pass model=model when model is not None/empty), or explicitly pass None so
the config can apply its default; update the instantiation that creates
VoyageRerankConfig in the provider=="voyage" branch (referencing
VoyageRerankConfig and VoyageRerankService) accordingly.

Comment thread methods/EverCore/src/agentic_layer/rerank_voyage.py
Comment on lines +217 to +219
logger.debug(
f"Voyage reranking, query: {query!r}, num_texts={len(all_texts)}"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Roh-Query wird geloggt und kann Tenant-Daten exponieren.

Das Debug-Log enthält den kompletten Query-Text. In Multi-Tenant-Betrieb sollte hier nur Metadaten (z. B. Länge/Hash) geloggt werden.

🔒 Vorschlag zur Behebung
             logger.debug(
-                f"Voyage reranking, query: {query!r}, num_texts={len(all_texts)}"
+                "Voyage reranking gestartet: num_texts=%d, query_len=%d",
+                len(all_texts),
+                len(query),
             )
As per coding guidelines `EverCore is multi-tenant; data must remain tenant-scoped`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.debug(
f"Voyage reranking, query: {query!r}, num_texts={len(all_texts)}"
)
logger.debug(
"Voyage reranking gestartet: num_texts=%d, query_len=%d",
len(all_texts),
len(query),
)
🧰 Tools
🪛 Ruff (0.15.12)

[warning] 218-218: Logging statement uses f-string

(G004)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@methods/EverCore/src/agentic_layer/rerank_voyage.py` around lines 217 - 219,
The debug log currently prints the full query string (logger.debug with query
and len(all_texts)), which can expose tenant data; update the logging in the
rerank_voyage logic (look for logger.debug and the variable query in
rerank_voyage or surrounding function) to avoid logging raw query text and
instead log only non-sensitive metadata such as query length and a deterministic
hash (e.g., SHA-256 of query) plus num_texts (len(all_texts)); ensure you
compute the hash from query safely and include a short label like "query_len"
and "query_hash" in the logger.debug call rather than the full query content.

…Lock

CodeRabbit finding #3: _ensure_session() could be entered concurrently
by multiple coroutines (e.g. parallel rerank batches via gather), each
seeing self.session is None and creating its own aiohttp.ClientSession.
The losers of the race were never tracked and leaked.

Add self._session_lock = asyncio.Lock() in __init__ and use a
double-checked pattern inside _ensure_session: a fast path when the
session is already alive, a lock-protected slow path with re-check
before instantiation.

Findings #1 (Voyage model default) and #2 (raw query in debug log) are
intentionally deferred — production .env always sets RERANK_MODEL and
DEBUG-level logs are not enabled by default in this environment.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c8bbb6bb6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +147 to +148
"""Low-level reranking; ``instruction`` is ignored — Voyage uses the
query/documents pair directly."""
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve rerank instruction in Voyage requests

VoyageRerankService.rerank_documents explicitly ignores the instruction argument, so any call site that supplies custom ranking guidance loses that behavior when RERANK_PROVIDER=voyage. For example, search_mem_service passes a skill-specific instruction to bias domain/method matching, but this provider drops it and ranks only on raw query text, which can materially reduce retrieval precision and makes Voyage behavior inconsistent with the existing vLLM/DeepInfra implementations that honor instruction.

Useful? React with 👍 / 👎.

config = VoyageRerankConfig(
api_key=api_key,
base_url=base_url or 'https://api.voyageai.com/v1/rerank',
model=model,
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 Select a Voyage-compatible default model

The new voyage branch forwards the shared RERANK_MODEL unchanged, but the repository default remains Qwen/Qwen3-Reranker-4B in env.template, which is not a Voyage reranker model. With RERANK_PROVIDER=voyage and no explicit model override, requests will hit Voyage with an invalid model and fail with 4xx errors, breaking reranking in the default configuration for this provider.

Useful? React with 👍 / 👎.

Comment on lines +152 to +154
batch_size = self.config.batch_size or 100
batches = [
documents[i : i + batch_size] for i in range(0, len(documents), batch_size)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard against non-positive Voyage batch sizes

This implementation does not validate self.config.batch_size before building batches. If RERANK_BATCH_SIZE is set to a negative value, range(0, len(documents), batch_size) produces no batches, so no API calls are made and the code silently returns zero-filled scores, effectively disabling reranking without an explicit error.

Useful? React with 👍 / 👎.

Comment on lines +167 to +170
if isinstance(result, Exception):
logger.error(f"Voyage rerank batch {i} failed: {result}")
all_scores.extend([-100.0] * len(batches[i]))
continue
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 Raise on Voyage batch failures instead of fabricating scores

When a Voyage batch request errors out, the code logs it and appends synthetic -100.0 scores, then continues as if reranking succeeded. If all batches fail (e.g., invalid API key, outage, 429/5xx), callers still receive a normal-looking rerank result, so fallback/error paths are never triggered and production can silently serve degraded ordering instead of surfacing the failure.

Useful? React with 👍 / 👎.

@Ptah-CT Ptah-CT dismissed coderabbitai[bot]’s stale review May 15, 2026 17:02

Findings #1 (Voyage model default override) and #2 (raw query in debug log) are intentionally deferred — production .env always sets RERANK_MODEL and DEBUG-level logs are not enabled. Finding #3 (session race) addressed in 8c8bbb6.

@Ptah-CT Ptah-CT merged commit acbeb86 into main May 15, 2026
8 checks passed
@Ptah-CT Ptah-CT deleted the fix/everos-bug-cluster-2026-05-15 branch May 15, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant