* Enable chromium semantic history search with our own embedder
* Replace history_embeddings patch with template override
Replace the trivial patch that commented out scored_url_rows.clear()
with a BraveHistoryEmbeddingsService template subclass (per
docs/gni_sources.md) that overrides OnPassageVisibilityCalculated to
always synthesize passing visibility scores (1.0), bypassing upstream's
PageContentAnnotationsService-based filtering which Brave doesn't use.
The template pattern avoids circular deps between
//brave/browser/history_embeddings and //chrome/browser:browser —
the template is instantiated at the factory site which already has
the required dep.
* Add unit tests for BraveHistoryEmbeddingsService
Run all upstream HistoryEmbeddingsService unit tests with the Brave
template subclass via chromium_src override, ensuring our visibility
bypass doesn't break any existing behavior. Add a Brave-specific test
verifying search results are preserved when the visibility model is
unavailable (upstream clears them).
* Add BraveEmbedder::Observer to notify when idle
Replace the JS-side connection tracking (boundCount /
notifyPassageEmbedderIdle) with a C++ observer pattern.
BravePassageEmbeddingsServiceController observes the embedder
and calls NotifyPassageEmbedderIdle via mojom when all jobs
complete, avoiding the renderer round-trip.
* Run LocalAIService BackgroundWebContents on guest OTR profile
The WASM embedding model doesn't need user profile data, so run
BackgroundWebContents on the guest OTR profile to isolate it from
user data.
- Make BackgroundWebContentsFactory async so it can call
CreateProfileAsync to obtain the guest OTR profile, then
construct BackgroundWebContentsImpl in the callback
- Inject the factory via SetBackgroundWebContentsFactory from
browser_context_keyed_service_factories.cc (which has
ProfileManager access) to avoid adding sources to sources.gni
- Add a static WebContents bind callback registry on
LocalAIServiceFactory so UntrustedLocalAIUI::BindInterface can
route mojo requests from the guest OTR WebContents back to the
owning LocalAIService without profile-based lookup
- Simplify BravePassageEmbeddingsServiceController to use a single
shared BraveEmbedder instead of per-profile map
* Add BravePassageEmbeddingsService in-process mojo impl
New BravePassageEmbeddingsService implements upstream
passage_embeddings::mojom::PassageEmbeddingsService in the browser
process. It hosts the guest-OTR BackgroundWebContents that runs the
WASM EmbeddingGemma renderer and owns the PassageEmbedderFactory
registration path (via local_ai::mojom::LocalAIService, which it also
implements so UntrustedLocalAIUI can route the renderer binding).
An internal BraveBatchPassageEmbedder translates upstream's batch
mojom (array<string> + priority) to the renderer's one-passage-at-a-
time interface, processing passages sequentially so callbacks resolve
with results in order.
Not yet wired to any caller — the follow-up commit switches
BravePassageEmbeddingsServiceController over to using this service
instead of the per-profile LocalAIService keyed service.
* Wire controller to launch in-process BravePassageEmbeddingsService
BravePassageEmbeddingsServiceController now mirrors
ChromePassageEmbeddingsServiceController: MaybeLaunchService()
constructs a BravePassageEmbeddingsService and binds the base class's
service_remote_ to it via an in-process mojo pipe; ResetServiceRemote()
tears it down. The constructor fires EmbedderMetadataUpdated once,
since our metadata is static (EmbeddingGemma version 1, 768-dim, 0.45)
and doesn't arrive through optimization_guide.
GetEmbeddings() override replaces the base class's file-loading flow —
we have no tflite/sentencepiece files to open — and calls
service_remote_->LoadModels() with default-constructed params
(BravePassageEmbeddingsService ignores them) before routing
GenerateEmbeddings to the bound embedder_remote_. Upstream's
SchedulingEmbedder drives the job queue end-to-end: priority re-sort
between passages, partial-progress resumption, and performance-scenario
awareness all come along for free.
The chromium_src include shim virtualizes EmbedderReady,
GetEmbedderMetadata, and GetEmbeddings via `#define X virtual X` so
the subclass can override them, and injects
`friend class BravePassageEmbeddingsServiceController` via an
EmbedderRunning macro anchor so the override can reach private
members (observer_list_ to fire the initial metadata notification,
embedder_remote_ to drive LoadModels without the upstream
model-info path).
PageEmbeddingsServiceFactory and HistoryEmbeddingsServiceFactory
overrides drop the GetBraveEmbedder(profile) hook; both now use the
base class's GetEmbedder() which returns the SchedulingEmbedder.
* Delete BraveEmbedder, LocalAIService keyed service, and orphan code
BravePassageEmbeddingsService now owns everything the old setup needed:
the background WebContents, the PassageEmbedderFactory registration,
and the static WebContents→bind-callback registry used by
UntrustedLocalAIUI::BindInterface. That removes the last reason to
keep the LocalAIService keyed service and its factory around.
Removed:
- brave_embedder.{h,cc,_unittest.cc} — replaced by upstream's
SchedulingEmbedder + BravePassageEmbeddingsService.
- local_ai_service.{h,cc,_unittest.cc} — merged into the new service.
- brave/browser/local_ai/ entirely — factory and bind registry
relocated to BravePassageEmbeddingsService static methods.
- LocalAIService keyed service registration from
browser_context_keyed_service_factories.cc.
- GetPassageEmbedder / NotifyPassageEmbedderIdle from local_ai.mojom;
those flows are now handled via the upstream PassageEmbeddingsService
mojom and the controller's service_remote_ idle handler.
UntrustedLocalAIUI now routes binds through
BravePassageEmbeddingsService::BindForWebContents. BUILD.gn and
sources.gni are adjusted accordingly.
* Use direct BindPassageEmbedder instead of mojo LoadModels
Upstream's mojom::PassageEmbeddingsService::LoadModels requires
non-null ReadOnlyFile fields for the tflite model and sentencepiece
tokenizer; mojo's serializer DCHECKs that they are physical files
(IsPhysicalFile), which rules out /dev/null or NUL as placeholders.
Since we don't have upstream's model files at all — our renderer
loads its own EmbeddingGemma — there's no way to satisfy that
contract on the caller side.
Keep BravePassageEmbeddingsService's mojom interface for completeness
but expose a direct C++ BindPassageEmbedder(receiver, callback) entry
point and have the controller call it directly. The service_remote_
inherited from the base class is left unbound, and the embedder_remote_
idle handler now fires ResetServiceRemote so the WASM renderer is torn
down on idle.
* Add unit tests for BravePassageEmbeddingsService
Covers the core flows: BindPassageEmbedder creating the background
contents, the barrier-closure that waits for both component-installed
model files and the renderer factory registration, the batch embedder
fanning out passages to the renderer one at a time, Init failure
tearing things down, renderer crash triggering close, and the static
WebContents→bind-callback registry used by UntrustedLocalAIUI.