refactor(memory): represent passed-in config types as TypedDicts - #2824
Merged
pgrayy merged 1 commit intoJun 16, 2026
Merged
Conversation
The memory config/options types (SearchOptions, MemorySearchOptions, MemoryAddOptions, MemoryToolConfig, MemoryAddToolConfig, MemoryInjectionConfig, MemoryManagerConfig) and InjectionConfig and MemoryStoreConfig were dataclasses. They are passed-in option bags, not value objects with behavior, so a TypedDict is the more idiomatic representation and lets callers pass plain dict literals (matching how the same configs are constructed elsewhere via Unpack TypedDicts, e.g. the model configs). - All seven memory configs + InjectionConfig + MemoryStoreConfig become TypedDicts (total=False; MemoryManagerConfig marks `stores` Required). Construction via the call form (MemoryToolConfig(...)) keeps working, and dict literals now type-check. - MemoryStore stays a Protocol (it carries methods) and re-declares the config fields as attributes, since a Protocol cannot extend a TypedDict. - MemoryEntry and the *Context types stay dataclasses (returned/callback value objects, not passed-in config). - Manager discrimination over the `Config | bool` unions switches from isinstance(<config-class>) (unusable on a TypedDict) to isinstance(x, dict) for selecting the config arm, and `is False` for the disabled-sentinel guards. - Attribute reads become .get(); previously-defaulted fields are preserved (wait_for_writes defaults at the read site; MemoryManagerConfig fields default via MemoryManager.__init__ since omitted keys are simply absent). No test changes required (call-form construction is still valid for TypedDicts). Full suite green: ruff + mypy (188 files), 3602 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
|
Assessment: Comment Clean, well-motivated refactor: converting these passed-in option bags from dataclasses to Review themes
Nicely scoped change with excellent docstrings — addressing the test gap would make it solid. |
opieter-aws
approved these changes
Jun 16, 2026
9 tasks
pgrayy
added a commit
to pgrayy/strands-harness-sdk
that referenced
this pull request
Jun 16, 2026
Follow-up to strands-agents#2824. ExtractionConfig is a passed-in option bag (trigger, extractor, filter, all optional) provided via the `bool | ExtractionConfig` shorthand, the same kind of config that PR converted, so this makes it a TypedDict too for consistency and to let callers pass plain dict literals. - ExtractionConfig becomes a TypedDict (total=False). Its sibling types stay as they are: ExtractionResult / ExtractorContext / ExtractionTriggerContext / MemoryMessageFilter are value objects, Extractor / ExtractionTrigger are a Protocol / ABC, and _ResolvedExtractionConfig is the internal resolved output (a value object) — none are passed-in config bags. - _resolve_extraction_config reads the fields via .get(). - Fix a falsiness regression the conversion exposes: the disabled check was `if not extraction`, but an empty ExtractionConfig is now `{}` (falsy), which would wrongly disable extraction. Check the disabled sentinels explicitly (`extraction is None or extraction is False`) so an empty config stays enabled. Construction via the call form (ExtractionConfig(trigger=...)) still works, so no test changes were needed. Full suite green: ruff + mypy (194 files), 3668 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 16, 2026
This was referenced Jun 25, 2026
This was referenced Jul 7, 2026
This was referenced Jul 10, 2026
This was referenced Jul 21, 2026
Merged
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.
Description
The memory config/option types were dataclasses, but they are passed-in option bags, not value objects with behavior. This converts them to
TypedDict, the more idiomatic representation for "a bag of optional fields a caller provides," and the one that matches how config bags are constructed elsewhere in the SDK. It also lets callers pass plain dict literals, which mirrors how these configs are written in the TypeScript SDK (object literals).Converted to
TypedDict(total=False):SearchOptions,MemorySearchOptions,MemoryAddOptions,MemoryToolConfig,MemoryAddToolConfig,MemoryInjectionConfig,MemoryManagerConfig, plusInjectionConfigandMemoryStoreConfig.MemoryManagerConfigmarksstoresasRequired.Left unchanged by design:
MemoryStorestays aProtocol(it carries methods) and re-declares the config fields as attributes, since aProtocolcannot extend aTypedDict.MemoryEntryand the*Contexttypes stay dataclasses, they are returned/callback value objects, not passed-in config.Public API impact
Construction is a superset of before, no breakage:
Internally, the manager's discrimination over the
Config | boolunions switches fromisinstance(<config-class>)(not usable on aTypedDict) toisinstance(x, dict)for selecting the config arm andis Falsefor the disabled-sentinel guards. Attribute reads become.get(...). All previously-defaulted fields are preserved:wait_for_writesdefaults at its read site, and theMemoryManagerConfigfields default viaMemoryManager.__init__(an omitted TypedDict key is simply absent, so the constructor's own defaults apply).Prior art in the SDK
Accepting a
TypedDictconfig as a single parameter is an established pattern:ConversationManager/proactive_compression(conversation_manager.py) —bool | ProactiveCompressionConfig | None, the closest precedent to this PR. It discriminates the union exactly as this PR does:if proactive_compression is True: ... elif isinstance(proactive_compression, dict): threshold = proactive_compression.get("compression_threshold", DEFAULT). Its docstring shows bothproactive_compression=Trueandproactive_compression={"compression_threshold": 0.8}.MCPClient/tasks_config: TasksConfig | None(mcp_client.py) — a config TypedDict passed as one parameter.OpenAIResponsesModel/bedrock_mantle_config: BedrockMantleConfig | None(openai_responses.py) — passed as one param and read directly (_resolve_region(config: BedrockMantleConfig)).default_audio: AudioConfig(nova_sonic.py,gemini_live.py,openai_realtime.py) — a TypedDict config with an inline dict-literal default.CitationsContentBlock/citations: CitationsConfig | None(types/media.py).So this PR's
MemoryToolConfig | bool/MemoryInjectionConfig | boolparameters, theisinstance(x, dict)/is Falsediscrimination, and the.get(key, default)reads all match the existingproactive_compressionprecedent.Related Issues
Documentation PR
Not applicable; no behavior change, construction is a superset of before.
Type of Change
Other (please describe): internal refactor of config type representation (no behavior change; construction is backward compatible)
Testing
How have you tested the change? Verify that the changes do not break functionality or introduce new warnings.
hatch run prepareRan the relevant checks (full
prepareincludes the multi-version test matrix):hatch run test-lint→ ruffAll checks passed;mypy ./srcno issues in 188 source fileshatch run test-format→ all files formattedhatch test) → 3602 passed. No test changes were needed, call-form construction stays valid for TypedDicts.Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.