Add embedding generation APIs - #244
Conversation
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## trunk #244 +/- ##
============================================
- Coverage 88.16% 86.49% -1.67%
- Complexity 1217 1327 +110
============================================
Files 61 68 +7
Lines 3945 4295 +350
============================================
+ Hits 3478 3715 +237
- Misses 467 580 +113
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
I know we already have #132 open to add embedding support and while that PR is fairly old now, it has already gone through an initial review. I've not compared the code there to here but curious if the approach taken here is different than that and if so, what the benefits are here as compared to that? Ideally we proceed with a single approach to solve the same problem to not overtax contributors here. |
|
Thanks for pointing that out. I honestly missed #132 before opening this, and there's definitely overlap. My apologies for the duplicated effort. The main distinction I see is that this PR is provider-agnostic, while #132 includes OpenAI-specific implementation that I think belongs in the OpenAI provider package, not Happy to close this if maintainers want #132 to remain canonical, or take ownership here if this narrower direction makes more sense. |
I'd defer to @JasonTheAdams and/or @felixarntz on that, I think ideal to have a single PR we're working from in order to not duplicate effort but I also know that original PR has sat for months and likely needs work to get it across the finish line so may be worth closing that out and proceeding here instead. |
JasonTheAdams
left a comment
There was a problem hiding this comment.
This is great! Thanks for the PR, @chubes4! One key suggestion:
Let's introduce an Embedding Value Object that's iterable and immutable, takes a list<int|float> of values and an integer for dimensions. This will contain all the validations (cleaning up EmbeddingResult and make it a first-class type.
|
@JasonTheAdams Pushed some updates with the requested changes. Let me know if you'd like to see anything else, and thanks again! |
dkotter
left a comment
There was a problem hiding this comment.
Left a few minor comments but overall this is looking good to me.
A couple other things that ideally we see added here:
- We have a
cli.phpscript that is useful for quickly testing things. That currently doesn't support embeddings so would be nice to update that to make testing this PR easier - We should ensure our documentation is updated with this new functionality, in particular the
README.md
|
Shipped an update to address the latest review! Thank you |
| ModelInterface $model, | ||
| ?CapabilityEnum $capability, | ||
| GenerativeAiResult $result | ||
| ResultInterface $result |
There was a problem hiding this comment.
While I understand why we're switching from GenerativeAiResult to the more generic ResultInterface in this file, this does open up backward incompatibilities. I'd be inclined to be okay with that if this was just a stand-alone package but because the goal is to merge this back into WordPress core, not sure we can proceed with this approach.
As an example, if someone is using this after event hook and they run some code like this:
$event->getResult()->getCandidates()This will throw a fatal error if the result is an EmbeddingResult. Consumers would instead need to run a check like this first:
$result = $event->getResult();
if ($result instanceof GenerativeAiResult) {
// generative handling
} elseif ($result instanceof EmbeddingResult) {
// embedding handling
}I think ideally we need to figure out a way to handle the difference between a GenerativeAiResult and EmbeddingResult ourselves (if possible) to avoid that breakage.
Could potentially introduce new getGenerativeResult and getEmbeddingResult methods and keep getResult as is. Or maybe there's other, better options here
|
Thanks Darrin. I pushed updates to address the latest review feedback:
Also verified the updated client/provider branch pair in WP Codebox with a live OpenAI embeddings call. |
| ); | ||
| } | ||
|
|
||
| $this->dispatchEvent(new BeforeGenerateEmbeddingEvent($promptMessages, $model, $capability)); |
There was a problem hiding this comment.
Currently the WP AI Client will fire do_action calls for events (see https://github.com/WordPress/wordpress-develop/blob/52e3c3013a9635b45d66c4888f995ade320bc49e/src/wp-includes/ai-client/adapters/class-wp-ai-client-event-dispatcher.php#L56). Right now that means we have two events, wp_ai_client_before_generate_result and wp_ai_client_after_generate_result.
With these new event classes, I believe that will add two new hooks, wp_ai_client_before_generate_embedding and wp_ai_client_after_generate_embedding. This does mean anyone that uses those existing hooks will not capture these new events. Is that something to worry about here? Similar to my last comment about backwards incompatibility, technically this works and won't cause errors but also is a new thing that people will need to be aware of.
Is there anyway to continue to use the existing event system and make it work for both generative AI results and embedding results? Or is there a way to use the generative AI results for embeddings instead of introducing a new DTO specific to embedding results? Or some other middle ground approach?
And maybe we're fine with these changes and the implications, just want to ensure we're thinking through the various ways this will impact any consumers of these events
There was a problem hiding this comment.
And just to be clear, opening this as something to discuss, not necessarily something that needs changed. Just want to ensure we're okay with introducing new events
There was a problem hiding this comment.
I will have to look into it later this week. I'm about to be off grid camping for a few days. Get back to you asap.
There was a problem hiding this comment.
Thinking about this a bit more, I've landed on the approach in this PR being the right one, where we introduce new embedding only events instead of trying to force embeddings to use the existing event classes (especially since those are built around GenerativeAiResult).
Not needed for this PR but I do think there's a likely better future where we introduce a shared event interface that the existing events (and events in the future) can all use. A subscriber could then listen for all events there if desired.
In addition, I think a catch-all hook upstream in WordPress would also be useful, so someone wanting to track all events could use that instead of having to keep track of any new event hooks that may get added in the future.
Neither of those are blockers for this PR and I'd recommend those as a separate PR in this repo and one PR in the WordPress-develop repo.
JasonTheAdams
left a comment
There was a problem hiding this comment.
Thanks, @dkotter! I left a couple more comments.
…ew more places. Remove validation now as we force the input to be an EmbeddingList which only allows arrays with ints or floats
…st and have it iterate through all values. Keep the exception throwing in the constructor to match the others
…mptBuilder into this new class. Goal is to allow the creation of other Builder classes that can use the same model resolver functionality
…tionality into that and out of PromptBuilder
EmbeddingBuilder. Embedding inputs are now MessageParts (strings, Files, or parts) rather than Message conversations, matching how providers treat embeddings as a separate APIinor language tweaks
Resolve embeddings around modality-aware MessagePart inputs and a shared provider-level model resolver.
|
Reconciliation update: this branch now merges Darrin Kotter’s The combined API keeps the dedicated Verification on the reconciled head:
The dependent provider update is in WordPress/ai-provider-for-openai#34. |
JasonTheAdams
left a comment
There was a problem hiding this comment.
Alright! We're close! Just a few small suggestions.
There was a problem hiding this comment.
I'm really happy to see this massive class get trimmed down quite a bit. 😄
|
@JasonTheAdams I don't know that anyone else is still around to resolve those final items and merge, but If you're able to do that then I can try and see about getting a release out later this evening (after which we'd still need someone to bump the library in WP core before too long into tomorrow). All that to say, maybe things are too late now? |
|
It's not too late for me @jeffpaul @JasonTheAdams let's cook this PR. I just pushed an update. |
JasonTheAdams
left a comment
There was a problem hiding this comment.
Ran the integration tests locally to confirm this is working against WordPress/ai-provider-for-openai#34, and it worked!
Darin worked on this directly after requesting changes. All requests have since been met or are no longer relevant.
Summary
EmbeddingBuilderand shares model/provider selection withPromptBuilderthrough a composedModelResolver.list<string>provider contract.Fixes #242.
Testing
composer phpcs.composer phpstan.php -d error_reporting='E_ALL & ~E_DEPRECATED' vendor/bin/phpunit --testsuite unit..envwithOPENAI_API_KEYand install an OpenAI provider branch implementing the embedding contract.composer test:integration -- --filter EmbeddingGenerationIntegrationTest.Local verification against current
trunk: 1,132 tests and 4,184 assertions passed. A live OpenAI cross-package check also verified one-input and two-input requests at 256 dimensions.AI assistance