fix(bandit): skip reward updates for unknown arm names#12
Open
GrigoryEvko wants to merge 800 commits into
Open
fix(bandit): skip reward updates for unknown arm names#12GrigoryEvko wants to merge 800 commits into
GrigoryEvko wants to merge 800 commits into
Conversation
short id will generate based on full id when required
…regression feature weights
… and improve docstring clarity
The DedupDecision type is returned by dedup.process_incoming() but memory.py accesses its fields via attribute access without referencing the type by name. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: memory system modular refactor + adversarial hardening
…cstrings Phase 1 cleanup (completed): - Move A_mem/, GAM_root/ → _vendor/ (vendored MIT libs) - Move contrib licenses → _vendor/ - Move 3 example scripts → examples/ - Fix 15 broken vendored library imports (A_mem/GAM_root bare imports) - Update 8 consumer import paths to _vendor/ - Add _vendor/__init__.py docstring (vendored libs notice) - Add examples/__init__.py docstring (not production code) - Update shared_memory/__init__.py docstring - Update pyproject.toml (ruff/mypy exclude paths for vendors) Tests: 770 passed Lint: clean Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): Phase 1 — directory reorg (vendor/examples/docstrings)
- Delete 5 duplicated usage-merge functions from memory_write_example.py (_to_float, _median_or_none, _extract_usage_task_deltas, _build_usage_payload_from_task_deltas, _merge_usage_payloads) → import from card_update_dedup.py (canonical home) - Delete duplicate dedupe_keep_order from card_update_dedup.py → import from shared_memory/utils.py - Remove deprecated _apply_update_actions() from memory.py (dead wrapper) - Make memory_to_card private (_memory_to_card) — only used internally - Simplify single-iteration loop in _extract_json_object Net: ~120 lines deleted, zero behavior change. Tests: 770 passed | Lint: clean Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): deduplicate code and delete dead paths
Replace hand-written RetrievalWeights.from_mapping() and CardUpdateDedupConfig.from_mapping() dict parsers (~87 lines) with Pydantic v2 @model_validator(mode="before") — same behavior, idiomatic. Also: add docstrings to all functions in card_update_dedup.py, fix stale test references to deleted _apply_update_actions wrapper, add test for flat config format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): Pydantic-idiomatic config parsing
- memory_write_example.py → write_pipeline.py (it's production, not an example) - memory_write_config.py → write_pipeline_config.py - selected_ideas_6.py → origin_analysis.py (remove versioned filename) - Delete test_memory_write_example_extended.py (duplicate of test_write_pipeline.py) - Update all 8 import sites + 1 dynamic importlib.import_module call Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): rename write pipeline and analysis files
Extract from card_conversion.py (554 → 420 lines): - base.py: GigaEvoMemoryBase abstract class (20 lines) - card_search.py: format_search_results, search_cards_by_keyword, synthesize_search_results (115 lines) Update 4 import sites directly (no re-exports). card_conversion.py retains: normalization, conversion, GAM config, constants, protocols. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): split card_conversion into focused modules
Define MemoryError, MemoryRetrieverError, MemorySearchError, and MemoryStorageError in gigaevo/exceptions.py following the existing GigaEvoError hierarchy. Wire them into the memory subsystem: - gam_search.build() wraps all failures in MemoryRetrieverError - memory.py narrows two gam.build() catches from bare Exception - card_store._load() narrows to (json.JSONDecodeError, OSError) - card_dedup import block narrows to (ImportError, OSError) Resilience-critical catches (search fallback, merge loop, __exit__) remain broad by design. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): custom exception hierarchy and narrowed catches
…t base to ABC - concept_api.py: all 5 RuntimeError raises → MemoryStorageError (matches gigaevo/database pattern of wrapping I/O errors) - base.py: GigaEvoMemoryBase now uses ABC + @AbstractMethod (matches MutationOperator, Stage, LangGraphAgent pattern) - card_dedup.py: narrow two broad catches: - JSONL read fallback: except Exception → (json.JSONDecodeError, OSError) - GAM store build: except Exception → (MemoryRetrieverError, OSError) - Update 6 test assertions from RuntimeError to MemoryStorageError Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ormity refactor(memory): exception conformity + ABC base class
When write_pipeline.py passes MemoryCard/ProgramCard Pydantic models to memory_platform.save_card(), the dict() call on a Pydantic model doesn't properly flatten nested Pydantic objects like ConnectedIdea. This caused TypeError in _persist_index() when json.dumps() tried to serialize. Root cause: write_pipeline returns list[AnyCard] (Pydantic models) and both backends (memory_platform and memory/shared_memory) consume these cards via save_card(). memory_platform's normalize_memory_card() must explicitly call .model_dump() on Pydantic inputs to flatten nested objects. Fix verified: all 788 memory + integration tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests the exact bug path: Pydantic MemoryCard/ProgramCard with nested ConnectedIdea and MemoryCardExplanation objects must be properly flattened to plain dicts before JSON serialization. 6 tests covering: ProgramCard with ConnectedIdea, MemoryCard with MemoryCardExplanation, plain dict passthrough, JSON round-trips, None. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add gigaevo-memory Git dependency to pyproject.toml - Remove sys.path manipulation from memory_platform/memory.py and remote_gam_retriever.py (no longer needed with proper install) - Simplify test file to use direct imports instead of module mocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expands from 6 to 11 tests covering the complete save_card → _persist_index flow with Pydantic inputs. Tests verify: - normalize_memory_card: ConnectedIdea/MemoryCardExplanation → dict - save_card: Pydantic ProgramCard/MemoryCard → JSON-serializable index - _card_to_backend_content: API payload is clean dict - persist/reload roundtrip: index file survives write→read cycle Uses _make_platform_memory() factory with mocked API client to test memory_platform in isolation without network dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add docstrings to 15 public methods across 5 files (memory.py, concept_api.py, card_dedup.py, openai_inference.py, write_pipeline.py) - Add return type annotations to 4 functions in amem_gam_retriever.py - Fix 2 mypy errors: annotate retrievers dict, rename variable in api_sync.py - Extract magic numbers: _MAX_SUMMARY_CHARS, _MAX_DESCRIPTION_CHARS, _ENTITY_NAME_MAX_LENGTH, _MAX_CONNECTED_DESCRIPTIONS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(memory): type annotations, docstrings, constants, platform bug fix
on_mutation_outcome forwarded program.get_metadata('mutation_model')
directly to self._bandit.update_reward, which raised KeyError when
the metadata did not match a current arm (Redis snapshot replay,
custom operator, hand-built test fixture). Membership check at the
top of the function turns the crash into a debug-level skip.
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.
on_mutation_outcome forwards program.get_metadata('mutation_model') directly to self._bandit.update_reward, which indexes self.arms[arm_name]. The normal flow keeps producer and router in step, but the metadata can also arrive from a Program loaded out of a Redis snapshot written by an older router, from a custom mutation operator, or from a hand-built test fixture. In those cases update_reward raises KeyError and aborts the agent callback.
Minimal reproducer against current main:
The change adds a single membership check against self._bandit.arms at the top of on_mutation_outcome and skips with one debug note when the name is unknown. Three new tests cover the unknown-arm path on the ACCEPTED branch, on the REJECTED_ACCEPTOR branch, and combined with missing child fitness.