Skip to content

Start when the host has no embedding model configured yet - #77

Open
jasperblues wants to merge 1 commit into
mainfrom
fix/719-tolerate-absent-embedding-service
Open

Start when the host has no embedding model configured yet#77
jasperblues wants to merge 1 commit into
mainfrom
fix/719-tolerate-absent-embedding-service

Conversation

@jasperblues

Copy link
Copy Markdown
Contributor

Problem

Both graph-backend beans resolved the default embedding service while being created:

DrivinePropositionRepository(..., ai.withDefaultEmbeddingService(), ...)   // :90
val embeddingService = ai.withDefaultEmbeddingService()                    // :167

withDefaultEmbeddingService() throws when no embedding model is registered. A host whose provider key arrives at first run rather than at boot therefore could not start — and so could not reach the setup flow that would have supplied the key.

Change

drivinePropositionRepository prefers the application's own EmbeddingService bean where there is an unambiguous one (a @Primary bean counts), falling back to ai.withDefaultEmbeddingService() as before.

This matters because a host that supports late configuration registers a service that reports its own absence and can be switched on later. DrivinePropositionRepository only touches the service when it actually embeds — never in its constructor — so holding an absent-tolerant one is safe.

getIfUnique() is deliberate: it returns the @Primary bean when there is one, null when the choice is ambiguous, and the fallback then preserves today's behaviour rather than failing on ambiguity.

propositionVectorIndexSchema registers nothing when there is no model, instead of guessing a dimension.

Why skip rather than guess

An index built at the wrong dimension is worse than no index, because writes to it succeed — a real model configured later silently disagrees with everything already stored. Skipping is recoverable; a corrupted index is not obviously broken until someone notices bad results.

The catalog is rebuilt on the next boot, by which time a model configured at first run is registered. Deployments that restart after first-run setup get this for free.

Compatibility

Unchanged for any deployment that has an embedding model — which is every existing one. The new path is only reachable when resolution would previously have thrown and taken the context down.

The in-memory backend (inMemoryPropositionRepository) still calls withDefaultEmbeddingService() directly. It is only active when the graph store is not, and was left alone to keep this change to the failing path.

Testing

  • dice-storage + dice-storage-autoconfigure: 100 tests pass, 0 failures
  • Verified in situ by booting a consuming application with zero provider keys, which previously died here

🤖 Generated with Claude Code

Both graph-backend beans resolved the default embedding service while they
were being created, via ai.withDefaultEmbeddingService(). That throws when no
embedding model is registered, so a host whose provider key arrives at first
run rather than at boot could not start — and could not reach the setup flow
that would have supplied the key.

drivinePropositionRepository now prefers the application's own EmbeddingService
bean where there is an unambiguous one (a @primary bean counts), falling back
to the platform default. A host that supports late configuration registers a
service that reports its own absence and can be switched on later;
DrivinePropositionRepository only touches it when it actually embeds, so
holding an absent-tolerant one is safe.

propositionVectorIndexSchema registers nothing when there is no model, rather
than guessing a dimension. An index built at the wrong dimension is worse than
no index, because writes to it succeed and a real model later disagrees with
everything already stored. The catalog is rebuilt on the next boot, by which
time a model configured at first run is registered.

Behaviour is unchanged for any deployment that has an embedding model.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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