Skip to content

perf(#2442): cache ticket embeddings in storage to bypass startup inference - #2530

Merged
riteshbonthalakoti merged 3 commits into
riteshbonthalakoti:gssocfrom
suhaniiz:perf/cache-embeddings-2442
Jun 9, 2026
Merged

perf(#2442): cache ticket embeddings in storage to bypass startup inference#2530
riteshbonthalakoti merged 3 commits into
riteshbonthalakoti:gssocfrom
suhaniiz:perf/cache-embeddings-2442

Conversation

@suhaniiz

@suhaniiz suhaniiz commented Jun 9, 2026

Copy link
Copy Markdown

Description

Addresses issue #2442 by updating the storage schema and hydration layer. Previously, the DuplicateService performed an expensive self.model.encode(text) operation for every cached historical ticket sequentially during load(), inducing significant service availability latency on startup.

This PR shifts the overhead into an $O(1)$ look-up pattern. The generated PyTorch embedding array is now serialized as a standard list of floats directly inside the JSON database when a ticket is created. On service reboot, load() reads these float lists and maps them straight back into memory as float32 tensors, avoiding redundant deep learning inference entirely.

Changes Made

  • Updated save_to_disk to convert embedding arrays into standard Python lists for valid JSON serialization.
  • Passed the active tensor from add_ticket directly into the disk-writing mechanism.
  • Updated load() to read pre-compiled lists from cache and convert them back to tensors with runtime device parity checks.
  • Included a defensive fallback logic routine to cleanly encode tickets on startup if they were cached prior to this schema migration.

Testing Checklist

  • Confirmed successful initial generation and serialization of embedding lists to JSON cache.
  • Verified near-instant hydration of service when starting up with a pre-populated cache file.
  • Validated that cosine similarity scans still operate correctly against reconstituted cache tensors.

Summary by CodeRabbit

  • New Features

    • Duplicate detection now caches and reuses embeddings from historical ticket data, reducing reprocessing overhead.
  • Chores

    • Updated model file management configuration.

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

@suhaniiz is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes Git LFS pointers for two model files and updates the DuplicateService to cache and reuse embeddings from disk. Previously, embeddings were discarded; now they are serialized to the history cache and deserialized on load to avoid recomputation.

Changes

Embedding Caching and Model Cleanup

Layer / File(s) Summary
Model file LFS pointer removal
backend/models/classifier/model.safetensors, backend/models/ner/model.safetensors
Git LFS pointer contents are deleted from both model files; no replacement pointer is provided.
Embedding caching in duplicate service
backend/services/duplicate_service.py
load() now reuses cached embeddings from case_history_cache.json when present, converting JSON lists back to PyTorch tensors and moving them to the model device; otherwise re-encodes from text. save_to_disk() signature expanded with optional embedding_tensor parameter; serializes tensors to JSON-friendly lists under an "embedding" field. add_ticket() passes computed embeddings into save_to_disk() for persistence.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • ritesh-1918/HELPDESK.AI#2035: Both PRs modify backend/services/duplicate_service.py's persistence layer and cache flow to serialize and reuse embeddings in JSON format.

Suggested labels

type:refactor, quality:clean

Suggested reviewers

  • ritesh-1918

Poem

🐰 A model fine, now LFS-free,
embeddings cached for all to see,
No recompute on every call—
the fuzzy one optimized it all! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: caching ticket embeddings during storage to avoid recomputing them at startup, which directly aligns with the PR's core objective of reducing startup latency.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/services/duplicate_service.py (1)

111-112: ⚡ Quick win

Consider catching more specific exceptions.

The broad except Exception catch here could mask unexpected errors. Consider catching more specific exceptions like IOError, json.JSONDecodeError, or OSError to handle known failure modes while allowing unexpected errors to surface during development.

♻️ Proposed refactor for more specific exception handling
             with open(self.storage_file, "w") as f:
                 json.dump(data, f, indent=2)
             print(f"[DuplicateService] Indexed ticket {ticket_id} to case history.")
-        except Exception as e:
+        except (IOError, OSError, json.JSONDecodeError) as e:
             print(f"[DuplicateService] Failed to save to disk: {e}")
🤖 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 `@backend/services/duplicate_service.py` around lines 111 - 112, The broad
except in DuplicateService that currently does "except Exception as e:
print(...)" should be replaced with targeted handlers: catch OSError/IOError
(for filesystem write errors) and json.JSONDecodeError (if JSON
parsing/serialization is involved) and log each specific error in the same
print/processLogger call; for any truly unexpected errors re-raise them so they
surface during development. Locate the try/except block inside DuplicateService
(the save-to-disk routine) and replace the blanket except Exception with
specific except OSError/IOError as e: ... and except json.JSONDecodeError as e:
... and then optionally a final bare "except Exception:" that re-raises the
exception.

Source: Linters/SAST tools

🤖 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 `@backend/services/duplicate_service.py`:
- Around line 53-61: When deserializing cached embeddings (item["embedding"])
validate the vector length/shape before converting to a tensor: retrieve the
expected embedding dimension from the model (e.g., via a getter like
get_sentence_embedding_dimension() or by inspecting a known embedding shape),
check that len(item["embedding"]) == expected_dim, and if it mismatches or is
malformed log a warning and fall back to recomputing with
self.model.encode(text, convert_to_tensor=True) (then move to self.model.device
if available); ensure the validated tensor is the one passed into util.cos_sim()
so corrupted cached vectors cannot cause shape errors or silent accuracy issues.

---

Nitpick comments:
In `@backend/services/duplicate_service.py`:
- Around line 111-112: The broad except in DuplicateService that currently does
"except Exception as e: print(...)" should be replaced with targeted handlers:
catch OSError/IOError (for filesystem write errors) and json.JSONDecodeError (if
JSON parsing/serialization is involved) and log each specific error in the same
print/processLogger call; for any truly unexpected errors re-raise them so they
surface during development. Locate the try/except block inside DuplicateService
(the save-to-disk routine) and replace the blanket except Exception with
specific except OSError/IOError as e: ... and except json.JSONDecodeError as e:
... and then optionally a final bare "except Exception:" that re-raises the
exception.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a235e7b-cb1c-4fc5-8c0b-ed65305806fe

📥 Commits

Reviewing files that changed from the base of the PR and between da8faf2 and 82937bb.

📒 Files selected for processing (3)
  • backend/models/classifier/model.safetensors
  • backend/models/ner/model.safetensors
  • backend/services/duplicate_service.py
💤 Files with no reviewable changes (2)
  • backend/models/classifier/model.safetensors
  • backend/models/ner/model.safetensors

Comment on lines +53 to +61
# Fixed: Check if an embedding is cached, fallback safely if not found
if "embedding" in item and item["embedding"] is not None:
embedding = torch.tensor(item["embedding"], dtype=torch.float32)
# Move to model device if available
if hasattr(self.model, 'device'):
embedding = embedding.to(self.model.device)
else:
embedding = self.model.encode(text, convert_to_tensor=True)

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

Validate embedding shape when deserializing from cache.

When reconstructing embeddings from JSON (line 55), there's no validation that the embedding list has the correct length or shape. The all-MiniLM-L6-v2 model produces 384-dimensional embeddings, but if the cached data is corrupted or comes from a different model version, the reconstructed tensor could have the wrong shape, leading to errors in util.cos_sim() at line 167 or silent accuracy degradation.

🛡️ Proposed fix to validate embedding dimensions
                         for item in data:
                             text = item["text"]
                             # Fixed: Check if an embedding is cached, fallback safely if not found
                             if "embedding" in item and item["embedding"] is not None:
-                                embedding = torch.tensor(item["embedding"], dtype=torch.float32)
+                                embedding_list = item["embedding"]
+                                # Validate embedding dimension (all-MiniLM-L6-v2 produces 384-dim vectors)
+                                if not isinstance(embedding_list, list) or len(embedding_list) != 384:
+                                    print(f"[DuplicateService] Invalid cached embedding for {item['ticket_id']}, re-encoding...")
+                                    embedding = self.model.encode(text, convert_to_tensor=True)
+                                else:
+                                    embedding = torch.tensor(embedding_list, dtype=torch.float32)
                                 # Move to model device if available
                                 if hasattr(self.model, 'device'):
                                     embedding = embedding.to(self.model.device)
🤖 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 `@backend/services/duplicate_service.py` around lines 53 - 61, When
deserializing cached embeddings (item["embedding"]) validate the vector
length/shape before converting to a tensor: retrieve the expected embedding
dimension from the model (e.g., via a getter like
get_sentence_embedding_dimension() or by inspecting a known embedding shape),
check that len(item["embedding"]) == expected_dim, and if it mismatches or is
malformed log a warning and fall back to recomputing with
self.model.encode(text, convert_to_tensor=True) (then move to self.model.device
if available); ensure the validated tensor is the one passed into util.cos_sim()
so corrupted cached vectors cannot cause shape errors or silent accuracy issues.

@riteshbonthalakoti
riteshbonthalakoti changed the base branch from main to gssoc June 9, 2026 19:40
@riteshbonthalakoti

Copy link
Copy Markdown
Owner

Hi @suhaniiz! Thanks for the amazing contribution. I've successfully merged your PR! 🚀

Please make sure to sign up under the company Ritesh PVT Limited when testing your features!

⚠️ Also, please complete the mandatory onboarding steps so your points register correctly:

⚠️ MANDATORY GSSOC ONBOARDING STEPS:
Before your PR points are finalized on the leaderboard, you MUST complete these steps:

  1. Star this repository: https://github.com/ritesh-1918/HELPDESK.AI (Mandatory)
  2. 👤 Follow the Project Admin: https://github.com/ritesh-1918 (Mandatory - manual step)
  3. 💼 Connect on LinkedIn: https://www.linkedin.com/in/ritesh1908/ (Mandatory)

Let's build something epic! 🔥

@riteshbonthalakoti
riteshbonthalakoti merged commit e1d5daf into riteshbonthalakoti:gssoc Jun 9, 2026
2 of 3 checks passed
@suhaniiz

Copy link
Copy Markdown
Author

@ritesh-1918 , this pr was under GSSoC 2026, so kindly add the appropriate labels

@suhaniiz

Copy link
Copy Markdown
Author

@ritesh-1918 ...

@suhaniiz

Copy link
Copy Markdown
Author

@ritesh-1918 , please add appropriate labels

@suhaniiz

Copy link
Copy Markdown
Author

@ritesh-1918 ....

@suhaniiz

Copy link
Copy Markdown
Author

@ritesh-1918 , this pr is under gssoc 2026, please add appropriate labels otherwise it wont count to me
thank you!!

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.

2 participants