Skip to content

Add embedding similarity calculations - #993

Open
dkotter wants to merge 6 commits into
WordPress:developfrom
dkotter:feature/embedding-calculations
Open

Add embedding similarity calculations#993
dkotter wants to merge 6 commits into
WordPress:developfrom
dkotter:feature/embedding-calculations

Conversation

@dkotter

@dkotter dkotter commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What?

Partially closes #962

Adds a Vector_Math class with various public methods that can be used to run similarity calculations against two vector embeddings. Also adds a Vector_Ranker class to score vectors against one another.

Why?

#892 and #975 brought in embedding code from the PHP AI Client. #976 brought in the data layer to store embeddings. This PR is one of the next steps in the overall embedding goal, allowing us to run comparisons from one vector to another.

How?

  • Adds a Vector_Math class with various public methods that can be used to run similarity calculations against two vector embeddings
  • Adds a Vector_Ranker class to score vectors against one another
  • Adds another command to our temporary embeddings WP-CLI command to test things

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Fable 5.1, Opus 5
Used for: Planning approach and executing on that. Review of approach and final review and testing by me

Testing Instructions

Easiest way to test is the custom WP-CLI command added as part of this PR:

  1. Open a terminal window
  2. Ensure you have embeddings generated for at least two posts
wp ai embeddings generate --post-id=1 --dry-run=false --chunk --provider=ollama --model=nomic-embed-text:latest
wp ai embeddings generate --post-id=2 --dry-run=false --chunk --provider=ollama --model=nomic-embed-text:latest
  1. Compare the same post against itself:
wp ai embeddings compare 1 1
  1. You should see a cosine similarity of 1 and euclidean distance of 0
  2. Compare the posts against each other:
wp ai embeddings compare 1 2
  1. You should see similarity numbers for each chunk as well as a single number for the overall comparison

Changelog Entry

Added - New Vector_Math and Vector_Ranker classes, allowing users to run similarity and ranking queries against vectors.

Open WordPress Playground Preview

@dkotter dkotter added this to the 1.4.0 milestone Sep 2, 2026
@dkotter dkotter self-assigned this Sep 2, 2026
@dkotter
dkotter requested a review from a team September 2, 2026 22:39
@dkotter
dkotter requested a review from jeffpaul as a code owner September 2, 2026 22:39
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: andreilupu <euthelup@git.wordpress.org>
Co-authored-by: ColinM-sys <colinmcdonough@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 32.17391% with 234 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.24%. Comparing base (813c81d) to head (d9990d5).

Files with missing lines Patch % Lines
includes/CLI/Embeddings_Command.php 0.00% 230 Missing ⚠️
includes/Embeddings/Vector_Ranker.php 90.32% 3 Missing ⚠️
includes/Embeddings/Vector_Math.php 98.79% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #993      +/-   ##
=============================================
- Coverage      75.37%   74.24%   -1.14%     
- Complexity      3357     3447      +90     
=============================================
  Files            138      140       +2     
  Lines          12976    13315     +339     
=============================================
+ Hits            9781     9886     +105     
- Misses          3195     3429     +234     
Flag Coverage Δ
unit 74.24% <32.17%> (-1.14%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andreilupu

Copy link
Copy Markdown

Great work on this 👏 To me, the math, classes, and tests are really solid. One thing I hit while reading through the compare command:

When two posts share more than one provider/model pair, there's currently no way to compare them.

In resolve_stored_model() (includes/CLI/Embeddings_Command.php#L708-L714), the multiple-shared-models branch prints the shared pairs formatted as flags:

Posts 42 and 99 share more than one model:
  --provider=openai --model=text-embedding-3-small
  --provider=ollama --model=nomic-embed-text:latest
Error: Posts have more than one model in common. Embed both with the same model, then compare.

Two issues with this:

  1. The --provider=... --model=... lines read as flags the user should pass to compare, but compare doesn't accept those flags — its synopsis only has --metric and --pairs.
  2. The suggested fix ("Embed both with the same model, then compare") doesn't resolve the situation: the posts already share a model — two of them — and since save() upserts on (object_type, object_id, provider, model, chunk_index), re-embedding with either model won't remove the other shared pair. The only way out today is deleting rows by hand (SQL or Embedding_Repository::delete_for_object() via wp eval), since no delete command is exposed.

This is easy to hit in practice: embed a couple of posts with Ollama while testing locally, later re-embed them with OpenAI, and compare becomes unusable for those posts.

Suggestion: add optional --provider=<provider> and --model=<model> flags to compare (mirroring generate) and use them to disambiguate in resolve_stored_model(). To be clear, this is still a same-model comparison on both sides; the flags only select which shared model's vectors to use, not a way to compare across models. Auto-detection can stay as the default for the common single-shared-model case, and the current error output already prints exactly the flag values the user would need to pass, so the UX would compose nicely.

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.

Tracking: Implement Embedding Support

2 participants