Skip to content

Python: [Bug]: Vector upsert tools reject null auto-generated keys #8625

Open

Description

@Shy7777

Describe the bug

An upsert tool rejects an explicit null key on a collection whose key is auto-generated. The same record works through collection.upsert, and omitting the key works through the tool. This is particularly easy to hit when a record is serialized from a model with an optional key defaulting to None.

Steps to reproduce

import asyncio
from agent_framework import (
    InMemoryCollection,
    VectorStoreCollectionDefinition,
    VectorStoreField,
    create_upsert_tool,
)

async def main():
    collection = InMemoryCollection(
        dict,
        definition=VectorStoreCollectionDefinition(
            [
                VectorStoreField("key", name="id", type_="str", is_auto_generated=True),
                VectorStoreField("data", name="text", type_="str"),
            ],
            collection_name="auto_keys",
        ),
    )
    await collection.ensure_collection_exists()
    print(await collection.upsert([{"id": None, "text": "direct"}], generate_vectors=False))
    tool = create_upsert_tool(collection, generate_vectors=False)
    print(await tool.invoke(arguments={"records": [{"text": "omitted"}]}, skip_parsing=True))
    print(await tool.invoke(arguments={"records": [{"id": None, "text": "explicit null"}]}, skip_parsing=True))

asyncio.run(main())

The first two calls generate keys. The last raises TypeError: Key value must match the declared 'str' key type. The upsert tool also exposes the key as {"type": "string"} without allowing null.

Expected behavior

For a dictionary or a model with a nullable auto-generated key, an explicit null key should preserve the collection's existing key-generation behavior. Supplied non-null keys should still be validated normally, and non-auto-generated keys should still reject null.

Environment

  • Reproduced on main at a8acb4e0e9d7e4712b01b9c888cf943a2dfbee91.
  • agent-framework-core 1.19.0, Python 3.12, macOS arm64.
  • Uses the in-memory collection; no external service or credentials are involved.

Proposed fix

I plan to fix the upsert tool's auto-generated-key handling and add regression coverage for both dictionary and nullable typed records. The collection's serializer already treats an auto-generated None key as missing; the tool's decoder rejects it before that behavior can run. This would not change get/delete key validation or the function-calling loop.

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 22, 2026
  2. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Sep 22, 2026
  3. github-actions commented on Sep 22, 2026

    @github-actions
    Contributor

    🤖 Automated triage reproduction notes (agent-authored — trust but verify)

    Agent analysis

    Repro: python/packages/core/agent_framework/_vectors.py::_decode_vector_tool_records rejects explicit None auto-generated keys at line 2149 by calling key_from_json(None) before serialization; _vector_tool_record_schema also emits a non-nullable key schema. This affects dictionary and nullable typed records passed through create_upsert_tool. Minimal repro: create an InMemoryCollection with an auto-generated string key and invoke its upsert tool with {"records": [{"id": None, "text": "generated"}]}.

    • Failing test: python/packages/core/tests/core/test_vectors.py
    • Files examined: python/packages/core/agent_framework/_vectors.py, python/packages/core/agent_framework/_in_memory.py, python/packages/core/tests/core/test_vectors.py, python/packages/core/pyproject.toml
    • Tests run: focused auto-generated-key tests in python/packages/core/tests/core/test_vectors.py
    • Reported version: 1.19.0
    • Current version: 1.19.0
  4. added
    agentsUsage: [Issues, PRs], Target: Single agent
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 22, 2026
  5. eavanvalkenburg commented on Nov 3, 2026

    @eavanvalkenburg
    Member

    /plan Plan the bounded schema/decoder repair for #8625 using historical #8631, without reopening it or starting competing implementation. Current main _serialize_mapping_to_store deliberately omits an auto-generated None key, while _decode_vector_tool_records sends every present key to key_from_json before that path and _vector_tool_record_schema never allows its null form. This source-backed inconsistency affects the supported dictionary example; include typed records only where their actual construction contract accepts None. No new runtime reproduction is claimed here. Use one agreed nullability rule for schema and decode, preserve supplied-key validation and non-auto/nonnullable/get/delete behavior, and reuse model registration/annotation discovery rather than introducing a divergent policy or silent failure. Read the actual #8631 closure: the later blocker was supported Python 3.10 constructor Annotated key discovery, not the earlier annotation-source/Mongo assertion findings reported addressed in 2400cbe. Recheck that constructor path on current main before assuming the historical failure persists, including conflicting class versus constructor annotations. Cover ordinary public FunctionTool.invoke and stored results for omitted/null/valid/invalid keys across dictionary, nullable/nonnullable dataclass/Pydantic/constructor models, and Mongo ObjectId; report current exact supported-version results rather than the historical 573-test claim. The former production patch is 24 additions/1 deletion before exclusions; establish the smallest correct executable scope and compatibility boundary before any minor-readiness or assignment decision. Preserve the existing issue assignee and keep #8631 closed pending agreed continuation.

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

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions