Skip to content

multi2vec_* factories accept vectorize_collection_name and silently drop it #2106

Open

Description

@toolshedlabs-hash

Every multi2vec_* factory accepts vectorize_collection_name and then drops it.

The text factories forward it. The multimodal ones take the same argument and never use it.
Nothing is logged either, so vectorize_collection_name=False has no effect and the collection name is still folded
into the vectors.

Reproduced on 4.22.0 with no server, since these factories are pure config builders. Asking every
factory for vectorize_collection_name=False and reading the config it emits:

text2vec_cohere      vectorizeClassName=False     honoured=True
text2vec_openai      vectorizeClassName=False     honoured=True
multi2vec_cohere     vectorizeClassName=<absent>  honoured=False
multi2vec_clip       vectorizeClassName=<absent>  honoured=False
multi2vec_voyageai   vectorizeClassName=<absent>  honoured=False
multi2vec_jinaai     vectorizeClassName=<absent>  honoured=False

The key is absent rather than wrong, so the server applies its own default. The docstring on these
same methods says that default is True.

While confirming it I found something that probably decides which fix you want. None of the eight
_Multi2Vec*Config classes has a vectorizeClassName field at all. Both text config classes do. So
the factories are accepting an argument the config model below them has no slot to hold, which
reads less like a missed line and more like the parameter was copied into signatures where it was
never supported.

That leaves a question I cannot answer from the client, and it changes the fix:

  • If Weaviate does support it for multimodal, the field needs adding to those config classes and
    passing through.
  • If it does not, the parameter cannot ever work there, and the fix is to drop it from those
    signatures or raise on it.

Happy to write either patch once you say which.

A note on severity. Not a security hole. It is just quiet: someone turning it off
for a multimodal collection gets vectors they did not ask for, and nothing tells them.

What I did not check. multi2vec_google needs location and project_id to build, so it is
covered by reading the code and not by the run above. I also did not check the async client or the
non-named-vector Configure.Vectorizer paths, which look like they have the same shape.

Activity

  1. Anai-Guo commented on Aug 3, 2026

    @Anai-Guo
    Contributor

    The question this issue is blocked on ("does Weaviate support vectorizeClassName for multimodal?") is answerable from the server source, and the answer is per-module, not one way for all of them.

    Checking weaviate/weaviate for vectorizeClassName under each modules/multi2vec-*:

    module server reads vectorizeClassName where
    multi2vec-aws yes modules/multi2vec-aws/ent/class_settings.go
    multi2vec-cohere yes modules/multi2vec-cohere/ent/class_settings.go
    multi2vec-nvidia yes modules/multi2vec-nvidia/ent/class_settings.go
    multi2vec-google yes modules/multi2vec-google/vectorizer/class_settings.go
    multi2vec-jinaai yes modules/multi2vec-jinaai/config.go
    multi2vec-twelvelabs yes modules/multi2vec-twelvelabs/config.go
    multi2vec-voyageai yes modules/multi2vec-voyageai/config.go
    multi2vec-clip no no reference anywhere under modules/multi2vec-clip/
    multi2vec-bind no no reference anywhere under modules/multi2vec-bind/

    multi2vec-clip/vectorizer/class_settings.go defines InferenceURL, ImageField, ImageFieldsWeights, TextField, TextFieldsWeights, Properties, Validate — and no class-name setting. Same shape for bind.

    So neither branch in the issue applies uniformly:

    • For seven of the nine modules the field is supported server-side, and the client dropping it is a plain pass-through bug — _Multi2VecBase in config_vectorizers.py has no vectorizeClassName field for any of them to carry.
    • For multi2vec_clip and multi2vec_bind the parameter genuinely cannot ever work, so those two need the drop-or-raise treatment — and that part is a maintainer call about the public signature, since removing a keyword argument is a breaking change and raising on a previously-silent argument changes behaviour for anyone passing it today.

    Not opening a PR for this — the clip/bind half needs your decision on the signature, and splitting the fix across the two halves seems worse than doing it once you've picked. Posting the server-side evidence so the design question isn't the thing blocking it.

    (AI-assisted, human reviewed. Every row above is from reading the current weaviate/weaviate source, not inferred from the client.)

  2. toolshedlabs-hash commented on Aug 4, 2026

    @toolshedlabs-hash
    Author

    I went to check this against the server source before acting on it, and the per-module split does not hold up. Posting what I found so nobody removes a parameter that works.

    multi2vec-clip and multi2vec-cohere build the same settings object:

    base: basesettings.NewBaseClassMultiModalSettingsWithAltNames(cfg, false, "multi2vec-clip", nil, nil)

    BaseClassMultiModalSettings embeds *BaseClassSettings, and that is where VectorizeClassName() lives, reading vectorizeClassName straight out of GetSettings(). Every multi2vec module that constructs this base inherits the reader, clip and bind included. The false in that call is lowerCaseInput, not a vectorizeClassName default.

    What a grep under each module directory actually finds is a DefaultVectorizeClassName constant declared in some of them and not others. That is a declaration, not a read, so it does not tell you which modules honour the setting.

    I have not checked whether each module's vectorizer prepends the class name at embedding time. Still unverified. The settings layer is shared either way, so if clip and bind do behave differently, the difference lives in the vectorizer and not in config, which is a different fix from the one described above.

    Either way the client-side half looks unchanged: _Multi2VecBase has no field to carry the value, so the factories still accept it and drop it.

  3. Anai-Guo commented on Aug 4, 2026

    @Anai-Guo
    Contributor

    You're right, and my per-module table was wrong. Thanks for checking it against the source rather than acting on it — I'll walk through what I get now, including the piece you flagged as unverified, plus one wrinkle neither of us had.

    Reader layer — uniform, exactly as you said.

    BaseClassMultiModalSettings embeds *BaseClassSettings (usecases/modulecomponents/settings/base_class_multimodal_settings.go:33-34), and VectorizeClassName() is defined only there (base_class_settings.go:116), reading vectorizeClassName straight out of GetSettings(). Searching modules/multi2vec-* for any override returns nothing — no module redefines it. All of them inherit the same reader.

    My table came from grepping DefaultVectorizeClassName per module directory, which is the declaration-vs-read mistake you named. And in two of the modules I scored on that basis the constant is outright dead: in multi2vec-cohere/ent/class_settings.go:29 and multi2vec-google/vectorizer/class_settings.go:36, DefaultVectorizeClassName appears exactly once in the file — its own declaration — and is referenced nowhere else in the module.

    Defaults layer — there is a real split, but it is not the one I described.

    Two modules wire their local constant into the schema defaults:

    • multi2vec-jinaai/config.go:28 — "vectorizeClassName": ent.DefaultVectorizeClassName, with ent.DefaultVectorizeClassName = false (ent/class_settings.go:32)
    • multi2vec-voyageai/config.go:26 — same shape, = false (ent/class_settings.go:29)

    So when the user leaves the setting unset, those two resolve to false via ClassConfigDefaults(), while every other multi2vec module falls through to the shared settings.DefaultVectorizeClassName = true (base_class_settings.go:27). That is a two-module split at the defaults layer, not the reader split across many modules that I posted.

    Embedding layer — uniform, and this is the part that settles it.

    The only production code branching on icheck.VectorizeClassName() is ObjectVectorizer.TextsWithTitleProperty (usecases/modulecomponents/vectorizer/object_texts.go:82), which prepends the camel-split class name to the corpus. Every caller of it is a text2vec-* module. No multi2vec vectorizer reaches it:

    • multi2vec-clip/vectorizer/vectorizer.go declares an objectVectorizer field (line 28) and initializes it (line 34), but object() never calls it — it builds texts purely from properties passing ichek.TextField(propName) (lines 70-99). The field is unused on that path.
    • multi2vec-google/vectorizer/vectorizer.go and multi2vec-jinaai/vectorizer/vectorizer.go contain no reference to VectorizeClassName, objectVectorizer, or object.Class at all.

    So for multi2vec the setting is accepted, stored, and readable — and nothing downstream consumes it. The class name is never prepended to a multi2vec embedding. The defaults split above is therefore moot in practice: both true and false produce the same vector.

    Which leaves the client-side half, where your reading holds.

    _Multi2VecBase has no field to carry the value, so the factories accept vectorize_collection_name and drop it. The thing worth fixing is the silence, not the drop — the parameter reads as effective and isn't, at any layer. Rejecting it at the factory signature seems better than threading it through to a server that ignores it, but that's a maintainer call.

    One caveat on scope: I checked the clip, google, and jinaai vectorizers directly. I have not read all twelve. Given the settings layer is shared and none of the three consults the class name, I'd expect the rest to match, but I'm flagging that rather than generalizing from three again.

  4. toolshedlabs-hash commented on Aug 4, 2026

    @toolshedlabs-hash
    Author

    Confirmed your embedding-layer reading, and closed the scope caveat you flagged.

    VectorizeClassName() appears twice in object_texts.go: the interface method at line 33 and the single branch at line 82. That branch is the only thing in production that acts on the setting.

    I then walked every .go file in all ten multi2vec-* modules, 112 files, looking for VectorizeClassName, TextsWithTitleProperty or object.Class, and discounting bare DefaultVectorizeClassName declarations since those are what produced the original table.

    multi2vec-aws 11   multi2vec-bind 16   multi2vec-clip 17   multi2vec-cohere 7
    multi2vec-dummy 3  multi2vec-google 14 multi2vec-jinaai 13 multi2vec-nvidia 11
    multi2vec-twelvelabs 8                 multi2vec-voyageai 12
    

    Zero consumers, in all ten. Two of them (cohere, twelvelabs) have no vectorizer/ directory at all, so there was nowhere for it to hide.

    So the setting is stored and readable, and nothing in multi2vec ever reads it back. It is inert. The jinaai and voyageai defaults split has no observable effect, since both values produce the same vector.

    For the client that leaves one question, which is yours to answer: reject vectorize_collection_name at the factory signature, or carry it through to a server that ignores it. Rejecting looks more honest to me, though it is a breaking change for anyone passing it today, which is precisely the group getting nothing from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions