Repository navigation
Conversation
Add an embedder hook beside the two built-in providers. With `provider: custom`, `embedding.embedder` names a `"module.path:callable"` factory that cocoindex-code imports and calls as `factory(settings, indexing_params)`; the result must satisfy cocoindex's `VectorSchemaProvider` and expose an async `embed()`. This mirrors the `module.path:callable` form the chunker registry already uses. Why: the stock cloud path routes every embedding through litellm, which is a large dependency tree (it pulls aiohttp, boto3, openai, tiktoken and some forty others) and the subject of two PyPI supply-chain compromises in 2026. Users who talk to a single provider can now install a small adapter of their own beside the tool (`uv tool install cocoindex-code --with my-embedders`) and leave litellm out of the environment entirely. - settings: `EmbeddingSettings.embedder`, validated in `__post_init__` (required with `custom`, rejected elsewhere), round-tripped by the YAML (de)serialisers, with a comment template for `ccc init` output. - embedder_params: a custom embedder owns its kwargs, so `validate_params` passes them through. - shared: `load_custom_embedder_factory()` with errors that name the spec for a malformed value, an import failure, a missing attribute or a non-callable; `create_embedder()` dispatches to it and checks the returned object against the contract. - tests/test_custom_embedder.py: settings validation and round trip, params pass-through, loading a factory from a temporary module, contract rejection, every loader error, and an embed/schema call. - EMBEDDINGS.md: a "Custom Embedder" section; CLAUDE.md: one line in the embedding flow notes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a third embedder path beside
sentence-transformersandlitellm:provider: customwithembedding.embedder: "module.path:callable". cocoindex-code imports the factory and calls it once at daemon start asfactory(settings, indexing_params); the result must expose an asyncembed()and cocoindex's__coco_vector_schema__()(checked at load against a small runtime protocol,CustomEmbedder). The form mirrors themodule.path:callablestrings the chunker registry already accepts.Why
The cloud path routes every embedding through litellm. That is the right default for a tool that wants to support a hundred providers out of the box, but it is also a large dependency tree (aiohttp, boto3, openai, tiktoken, tokenizers and about forty more), and litellm has had two PyPI supply-chain compromises this year (1.82.7/1.82.8 in March, 1.107.0 on October 21). A user who talks to one provider can now install a small adapter of their own next to the tool (
uv tool install cocoindex-code --with my-embedders, with a uv override to leave litellm out) and keep the rest of cocoindex-code unchanged. We run the index this way against Voyage AI; the adapter is a single-filehttpxclient.Changes
settings.py:EmbeddingSettings.embedder, validated in__post_init__(required withcustom, rejected with any other provider), round-tripped by the YAML (de)serialisers; a comment template for thecustomprovider in generated settings.embedder_params.py:validate_paramspasses a custom embedder's kwargs through; the embedder owns them.shared.py:CustomEmbedderprotocol,load_custom_embedder_factory()with errors that name the spec (malformed value, import failure, missing attribute, non-callable), and thecustombranch increate_embedder()that checks the returned object against the protocol.tests/test_custom_embedder.py: settings validation and round trip, params pass-through, loading a factory from a temporary module, contract rejection, every loader error, and an embed + schema call through the loaded object.EMBEDDINGS.md: a "Custom Embedder" section with the YAML shape and the contract;CLAUDE.md: one line in the embedding-flow notes.ccc initis unchanged and does not offer the provider; it is a settings-file feature.Testing
tests/test_custom_embedder.py,tests/test_settings.py,tests/test_embedder_params.py: 100 passed.sentence-transformers: 30 failures, identical tomainrun the same way in the same environment (all needsentence_transformers/torchor a runningccc); no new failures on this branch.ruff checkandruff format --checkclean on the touched files;mypy --strictclean on them apart from the pre-existingyamlstubs note.🤖 Generated with Claude Code