Skip to content

feature: adds agentic memory decorator#2533

Open
hajdul88 wants to merge 31 commits intodevfrom
feature/cog-4514-minimal-productionization-of-the-simple-agentic-poc-feature
Open

feature: adds agentic memory decorator#2533
hajdul88 wants to merge 31 commits intodevfrom
feature/cog-4514-minimal-productionization-of-the-simple-agentic-poc-feature

Conversation

@hajdul88
Copy link
Copy Markdown
Collaborator

@hajdul88 hajdul88 commented Apr 1, 2026

Description

Adds cognee.agent_memory as a minimal productionized agent-memory decorator for async agent entrypoints.

This PR promotes the simple agentic decorator POC into a supported Cognee feature with a narrow first version:

  • optional memory retrieval before execution
  • transparent memory injection into LLMGateway.acreate_structured_output(...)
  • optional bounded AgentTrace persistence after execution
  • explicit user + dataset_name scope handling with combined read/write permission checks
  • support for both fixed memory queries and method-derived memory queries
  • focused quickstart, unit tests, integration tests, and e2e coverage

Related issue:

  • COG-4514

Acceptance Criteria

  • A supported cognee.agent_memory decorator exists on the public Cognee surface
  • The decorator wraps async functions and establishes execution-scoped context
  • Memory retrieval can be enabled or disabled per use case
  • Trace persistence can be enabled or disabled per use case
  • Downstream LLMGateway calls can consume memory context without custom agent logic
  • Invalid usage fails clearly
  • Persisted traces are structured and bounded
  • The feature includes logging/observability and test coverage across unit, integration, and e2e paths

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Code refactoring
  • Other (please specify):

Screenshots

Pre-submission Checklist

  • I have tested my changes thoroughly before submitting this PR (See CONTRIBUTING.md)
  • This PR contains minimal changes necessary to address the issue/feature
  • My code follows the project's coding standards and style guidelines
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if applicable)
  • All new and existing tests pass
  • I have searched existing PRs to ensure this change hasn't been submitted already
  • I have linked any relevant issues in the description
  • My commits have clear and descriptive messages

DCO Affirmation

I affirm that all code in every commit of this pull request conforms to the terms of the Topoteretes Developer Certificate of Origin.

Summary by CodeRabbit

  • New Features
    • Public agent_memory API for per-call memory context, configurable retrieval, decorator usage, optional trace persistence, and automatic prepending of retrieved memory to LLM prompts.
  • Examples
    • Quickstart script demonstrating agent-memory usage patterns, dynamic vs fixed queries, and trace behaviors.
  • Tests
    • Extensive unit, integration, and end-to-end tests covering access control, retrieval logic, decorator semantics, persistence of success/error traces, concurrency, and LLM integration.
  • Chores
    • CI job added to run agent-memory end-to-end tests.

@pull-checklist
Copy link
Copy Markdown

pull-checklist bot commented Apr 1, 2026

Please make sure all the checkboxes are checked:

  • I have tested these changes locally.
  • I have reviewed the code changes.
  • I have added end-to-end and unit tests (if applicable).
  • I have updated the documentation and README.md file (if necessary).
  • I have removed unnecessary code and debug statements.
  • PR title is clear and follows the convention.
  • I have tagged reviewers or team members for feedback.

@hajdul88 hajdul88 self-assigned this Apr 1, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Walkthrough

Adds a new agent-memory subsystem: decorator, runtime, models, LLM input injection, persistence, example, CI job, and unit/integration/e2e tests; exposes agent_memory and memory-context accessors at package level.

Changes

Cohort / File(s) Summary
Package exports
cognee/__init__.py, cognee/modules/agent_memory/__init__.py
Expose agent_memory and get_current_agent_memory_context from cognee.modules.agent_memory at package level.
Runtime & decorator
cognee/modules/agent_memory/runtime.py, cognee/modules/agent_memory/decorator.py
New async agent-memory runtime and decorator: dataclasses (AgentMemoryConfig, AgentScope, AgentMemoryContext), task-local context accessors, config validation, scope resolution, query derivation, retrieval (retrieve_memory_context), persistence (persist_trace), serialization helpers, and agent_memory decorator with lifecycle and trace persistence.
Data model
cognee/modules/agent_memory/models.py
Add AgentTrace DataPoint and private _agent_traces_nodeset() helper with deterministic id and post-init normalization.
LLM integration
cognee/infrastructure/llm/LLMGateway.py
Add _inject_agent_memory(text_input) and apply it inside LLMGateway.acreate_structured_output so structured-output LLM calls receive augmented input when memory context exists.
Examples
examples/guides/agent_memory_quickstart.py
New quickstart demonstrating decorator usage (fixed query, method-derived query, disabled memory), setup routine, and sample LLM call flow.
CI workflow
.github/workflows/e2e_tests.yml
Add test-agent-memory job to run e2e tests (cognee/tests/test_agent_memory_e2e.py) with backend access control and required LLM/embedding env vars.
Integration & e2e tests
cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py, cognee/tests/test_agent_memory_e2e.py
New integration and e2e tests validating permission gating, memory retrieval, trace persistence (success/error), and LLM injection behavior.
Unit tests
cognee/tests/unit/modules/agent_memory/test_agent_memory.py
Large unit test suite covering decorator validation, context lifecycle, query precedence, scope resolution, persistence semantics, concurrency/isolation, and LLM injection tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • dexters1
  • lxobr
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feature: adds agentic memory decorator' clearly and concisely summarizes the main change—introduction of a new agent memory decorator feature for async functions.
Description check ✅ Passed The PR description comprehensively covers the feature with a clear overview, explicit acceptance criteria, proper type classification, full pre-submission checklist completion, and DCO affirmation. All required sections are well-populated with meaningful content.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cog-4514-minimal-productionization-of-the-simple-agentic-poc-feature

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
cognee/modules/agent_memory/models.py (2)

34-37: Consider documenting the model_post_init behavior.

The method overrides both id and belongs_to_set unconditionally, which means user-provided values are silently discarded. This is likely intentional for deterministic tracing, but a brief docstring would clarify this design choice.

📝 Suggested docstring
     def model_post_init(self, __context: Any) -> None:
+        """Recompute deterministic id from text and reset belongs_to_set to agent_traces."""
         super().model_post_init(__context)
         self.id = uuid5(NAMESPACE_OID, f"AgentTrace:{self.text}")
         self.belongs_to_set = [_agent_traces_nodeset()]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/models.py` around lines 34 - 37, Add a concise
docstring to the model_post_init method explaining that it intentionally
overrides any user-provided id and belongs_to_set values to produce
deterministic tracing identifiers (it sets self.id via uuid5 with NAMESPACE_OID
and self.text and sets self.belongs_to_set to [_agent_traces_nodeset()]), and
note the side-effect that incoming values are discarded so callers should not
rely on passing those fields before post-init; reference the method name
model_post_init and the attributes id and belongs_to_set as well as
uuid5/NAMESPACE_OID/_agent_traces_nodeset to make the behavior discoverable.

32-32: Mutable class-level default may cause shared state issues.

The metadata dict is defined as a class attribute with a mutable default. If any code modifies instance.metadata, it will affect all instances. Consider using Field(default_factory=...) for safety.

♻️ Proposed fix
-    metadata: dict = {"index_fields": ["text"]}
+    metadata: dict = Field(default_factory=lambda: {"index_fields": ["text"]})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/models.py` at line 32, The class-level mutable
default for the attribute `metadata` can cause shared-state bugs across
instances; change the attribute to use a Field default factory (e.g.,
Field(default_factory=lambda: {"index_fields": ["text"]})) so each instance gets
its own dict; locate the `metadata` attribute in the model class in this file
and replace the class-level dict with a Field(default_factory=...) import from
pydantic (or the appropriate model library) and ensure tests/usage still expect
the same structure.
cognee/modules/agent_memory/runtime.py (2)

84-130: Consider validating memory_top_k bounds.

The validation checks types for most parameters but doesn't validate that memory_top_k is a positive integer. Negative or zero values could cause unexpected behavior in the search call.

🛡️ Proposed validation
     if dataset_name is not None and (not isinstance(dataset_name, str) or not dataset_name.strip()):
         raise CogneeValidationError(
             "dataset_name must be a non-empty string when provided.",
             log=False,
         )
+    if not isinstance(memory_top_k, int) or memory_top_k < 1:
+        raise CogneeValidationError(
+            "memory_top_k must be a positive integer.",
+            log=False,
+        )

     return AgentMemoryConfig(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 84 - 130, The
validate_agent_memory_config function lacks bounds checking for memory_top_k;
add a validation that memory_top_k is an integer greater than 0 and raise
CogneeValidationError with a clear message (e.g., "memory_top_k must be a
positive integer") if not; update validate_agent_memory_config (and its
signature handling of memory_top_k) so the check happens before constructing
AgentMemoryConfig, referencing memory_top_k, CogneeValidationError,
validate_agent_memory_config, and AgentMemoryConfig to locate the change.

289-290: Redundant asyncio.create_task wrapping.

await asyncio.create_task(coro) is equivalent to await coro but adds unnecessary overhead. If the intent is fire-and-forget persistence, remove the await. If synchronous completion is required (current behavior), call the coroutine directly.

♻️ Proposed fix for synchronous execution
     try:
-        await asyncio.create_task(_persist_trace_in_task())
+        await _persist_trace_in_task()
     except Exception as error:

Or for true fire-and-forget (trace persists in background):

♻️ Alternative for fire-and-forget
     try:
-        await asyncio.create_task(_persist_trace_in_task())
+        asyncio.create_task(_persist_trace_in_task())
     except Exception as error:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 289 - 290, The current
call wraps the coroutine _persist_trace_in_task() in asyncio.create_task and
immediately awaits it, which is redundant; either (A) if you need synchronous
completion, replace await asyncio.create_task(_persist_trace_in_task()) with
awaiting the coroutine directly by calling await _persist_trace_in_task(), or
(B) if you want fire-and-forget persistence, remove the await and just call
asyncio.create_task(_persist_trace_in_task()) so the task runs in
background—update the invocation accordingly in runtime.py where
_persist_trace_in_task is called.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/guides/agent_memory_quickstart.py`:
- Around line 34-39: Update the type hints so primitive types are allowed:
change the `response_model` parameter type in
`LLMGateway.acreate_structured_output` and the interface signature in
`llm_interface.py` from `Type[BaseModel]` to `Union[Type[BaseModel], type]`, and
update adapter method return annotations from `BaseModel` to `Union[BaseModel,
Any]` (or `Union[BaseModel, Any]` where used) so implementations that return
primitives (e.g., `str`, `int`, `bool`) are correctly typed; adjust all adapter
implementations' signatures to match (references:
`LLMGateway.acreate_structured_output`, the interface method in
`llm_interface.py`, and each adapter class/method that declares `response_model`
or returns parsed output).

---

Nitpick comments:
In `@cognee/modules/agent_memory/models.py`:
- Around line 34-37: Add a concise docstring to the model_post_init method
explaining that it intentionally overrides any user-provided id and
belongs_to_set values to produce deterministic tracing identifiers (it sets
self.id via uuid5 with NAMESPACE_OID and self.text and sets self.belongs_to_set
to [_agent_traces_nodeset()]), and note the side-effect that incoming values are
discarded so callers should not rely on passing those fields before post-init;
reference the method name model_post_init and the attributes id and
belongs_to_set as well as uuid5/NAMESPACE_OID/_agent_traces_nodeset to make the
behavior discoverable.
- Line 32: The class-level mutable default for the attribute `metadata` can
cause shared-state bugs across instances; change the attribute to use a Field
default factory (e.g., Field(default_factory=lambda: {"index_fields":
["text"]})) so each instance gets its own dict; locate the `metadata` attribute
in the model class in this file and replace the class-level dict with a
Field(default_factory=...) import from pydantic (or the appropriate model
library) and ensure tests/usage still expect the same structure.

In `@cognee/modules/agent_memory/runtime.py`:
- Around line 84-130: The validate_agent_memory_config function lacks bounds
checking for memory_top_k; add a validation that memory_top_k is an integer
greater than 0 and raise CogneeValidationError with a clear message (e.g.,
"memory_top_k must be a positive integer") if not; update
validate_agent_memory_config (and its signature handling of memory_top_k) so the
check happens before constructing AgentMemoryConfig, referencing memory_top_k,
CogneeValidationError, validate_agent_memory_config, and AgentMemoryConfig to
locate the change.
- Around line 289-290: The current call wraps the coroutine
_persist_trace_in_task() in asyncio.create_task and immediately awaits it, which
is redundant; either (A) if you need synchronous completion, replace await
asyncio.create_task(_persist_trace_in_task()) with awaiting the coroutine
directly by calling await _persist_trace_in_task(), or (B) if you want
fire-and-forget persistence, remove the await and just call
asyncio.create_task(_persist_trace_in_task()) so the task runs in
background—update the invocation accordingly in runtime.py where
_persist_trace_in_task is called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1e904c33-9b49-440e-8143-2b8b664a3278

📥 Commits

Reviewing files that changed from the base of the PR and between 413a0f9 and 745cc72.

📒 Files selected for processing (7)
  • cognee/__init__.py
  • cognee/infrastructure/llm/LLMGateway.py
  • cognee/modules/agent_memory/__init__.py
  • cognee/modules/agent_memory/decorator.py
  • cognee/modules/agent_memory/models.py
  • cognee/modules/agent_memory/runtime.py
  • examples/guides/agent_memory_quickstart.py

@hajdul88 hajdul88 marked this pull request as draft April 1, 2026 12:07
@hajdul88 hajdul88 marked this pull request as ready for review April 1, 2026 13:07
@hajdul88 hajdul88 changed the title feature: adds agentic memory decorator (WIP) feature: adds agentic memory decorator Apr 1, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
cognee/tests/test_agent_memory_e2e.py (1)

1-2: Move this end-to-end test under a supported cognee/tests/ subdirectory.

Keeping cognee/tests/test_agent_memory_e2e.py at the top level breaks the repo's test layout convention and forces a one-off workflow target. Moving it under cognee/tests/integration/ (or another approved subdirectory) will keep discovery and CI wiring consistent. As per coding guidelines, cognee/tests/**/*.py: "Organize tests in cognee/tests/ directory with subdirectories: unit/, integration/, cli_tests/, tasks/."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/tests/test_agent_memory_e2e.py` around lines 1 - 2, Move the
end-to-end test file from cognee/tests/test_agent_memory_e2e.py into an approved
subdirectory under cognee/tests (e.g.,
cognee/tests/integration/test_agent_memory_e2e.py); update any imports or test
discovery references that point to the old module path (search for references to
test_agent_memory_e2e or the top-level path cognee.tests.test_agent_memory_e2e)
and ensure the test module lives under the cognee/tests/** hierarchy so CI and
pytest discovery follow the project's convention.
cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py (1)

146-167: Either remove the LLMGateway monkeypatch or route the assertion through it.

shared_memory_agent() returns context.memory_context directly, so this monkeypatch never affects the test outcome. As written, the test validates retrieval only; a regression in the downstream memory-injection path would still pass.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py`
around lines 146 - 167, The test currently monkeypatches
LLMGateway.acreate_structured_output but returns context.memory_context directly
in shared_memory_agent(), so the monkeypatch is unused; either remove the
monkeypatch block entirely, or change shared_memory_agent() to route the memory
through the gateway (call LLMGateway.acreate_structured_output with
text_input=context.memory_context and assert on the gateway's return value) so
the monkeypatched acreate_structured_output is exercised; update references to
get_current_agent_memory_context(), shared_memory_agent, and the
LLMGateway.acreate_structured_output call accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 137-140: The resolve_agent_scope function currently falls back to
get_default_user() when neither config.user nor session_user.get() exists;
change this to fail closed: in resolve_agent_scope (working with
AgentMemoryConfig and returning AgentScope) check config.user or
session_user.get(), and if neither is present, raise an explicit exception
(e.g., PermissionError or a custom UnauthorizedError) instead of calling
get_default_user(); remove or gate the get_default_user() fallback so
access-controlled flows never inherit a different principal.
- Around line 87-134: The current validate_agent_memory_config lets blank memory
queries, invalid memory_top_k, and partial user-like objects slip through and
cause later failures; update validate_agent_memory_config to (1) require
memory_query_fixed and memory_query_from_method when provided to be non-empty
after .strip(), (2) enforce memory_top_k is an int >= 1 (and optionally bound it
to a sensible max), and (3) require the user argument to be a real User model
(use isinstance(user, User)) rather than merely having an id; when normalizing,
strip and store the trimmed query strings and raise CogneeValidationError with
clear messages on violations so resolve_agent_scope and search receive validated
inputs.
- Around line 180-184: The code currently uses sanitize_value() (which only
truncates) in build_method_params, allowing secrets/PII (tokens, passwords,
emails, raw user content) to be persisted in AgentTrace; update
build_method_params to perform redaction instead of just truncation: detect
sensitive parameter names (e.g., password, pass, pwd, token, secret, api_key,
authorization, email, ssn, creditcard) and mask or replace their values with a
constant like "[REDACTED]" and also apply pattern-based redaction for values
that look like emails, JWTs, credit-card numbers, or long opaque tokens;
likewise apply the same redaction step to the code that produces
method_return_value (the return-value serialization around lines 324-335) so
return payloads are scrubbed before writing to AgentTrace. Ensure you centralize
the logic (e.g., a redact_sensitive_value helper) and call it from
build_method_params and the return-value handling code so all persisted traces
are redacted.

In
`@cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py`:
- Around line 78-92: The fixture agent_memory_integration_env mutates global
state (os.environ["ENABLE_BACKEND_ACCESS_CONTROL"] and
cognee.config.data_root_directory / cognee.config.system_root_directory) but
never restores it; update the fixture to snapshot the prior values (store
previous os.environ value or absence and previous data/system root values)
before mutating, then in the teardown (after yield, before/after calling
_reset_engines_and_prune()) restore the original environment key and call
cognee.config.data_root_directory(...) and
cognee.config.system_root_directory(...) with the previous values (or unset the
env var if it was absent); alternatively replace these mutations with pytest
monkeypatch.setenv and monkeypatch.setattr to scope changes automatically around
the yield while keeping engine_setup() and _reset_engines_and_prune() calls
unchanged.

In `@cognee/tests/test_agent_memory_e2e.py`:
- Around line 70-85: The fixture agent_memory_e2e_env mutates global state
(os.environ["ENABLE_BACKEND_ACCESS_CONTROL"] and
cognee.config.data_root_directory/system_root_directory) but doesn't restore it;
update the fixture to snapshot and restore those values (store previous
os.environ value or absence and previous data/system root directory values, then
restore them after the yield) or accept and use pytest's monkeypatch fixture
(use monkeypatch.setenv and monkeypatch.setenv/monkeypatch.setattr for
cognee.config.*) so that state is reverted in teardown alongside the existing
_reset_engines_and_prune/engine_setup calls.

---

Nitpick comments:
In
`@cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py`:
- Around line 146-167: The test currently monkeypatches
LLMGateway.acreate_structured_output but returns context.memory_context directly
in shared_memory_agent(), so the monkeypatch is unused; either remove the
monkeypatch block entirely, or change shared_memory_agent() to route the memory
through the gateway (call LLMGateway.acreate_structured_output with
text_input=context.memory_context and assert on the gateway's return value) so
the monkeypatched acreate_structured_output is exercised; update references to
get_current_agent_memory_context(), shared_memory_agent, and the
LLMGateway.acreate_structured_output call accordingly.

In `@cognee/tests/test_agent_memory_e2e.py`:
- Around line 1-2: Move the end-to-end test file from
cognee/tests/test_agent_memory_e2e.py into an approved subdirectory under
cognee/tests (e.g., cognee/tests/integration/test_agent_memory_e2e.py); update
any imports or test discovery references that point to the old module path
(search for references to test_agent_memory_e2e or the top-level path
cognee.tests.test_agent_memory_e2e) and ensure the test module lives under the
cognee/tests/** hierarchy so CI and pytest discovery follow the project's
convention.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f3cf6b5-6f12-4a40-b33d-2673a44a249f

📥 Commits

Reviewing files that changed from the base of the PR and between 745cc72 and 0a53d3c.

📒 Files selected for processing (8)
  • .github/workflows/e2e_tests.yml
  • cognee/modules/agent_memory/decorator.py
  • cognee/modules/agent_memory/models.py
  • cognee/modules/agent_memory/runtime.py
  • cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py
  • cognee/tests/test_agent_memory_e2e.py
  • cognee/tests/unit/modules/agent_memory/test_agent_memory.py
  • examples/guides/agent_memory_quickstart.py
✅ Files skipped from review due to trivial changes (1)
  • examples/guides/agent_memory_quickstart.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • cognee/modules/agent_memory/models.py
  • cognee/modules/agent_memory/decorator.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 356-369: The normalize_search_results function currently treats
Pydantic SearchResult instances as generic objects and falls back to
str(results), losing the nested search_result field; update
normalize_search_results to detect Pydantic models (e.g., check hasattr(results,
"search_result") or isinstance(results, BaseModel)) before the final fallback
and call normalize_search_results(results.search_result) so the structured
search_result content is preserved when normalizing outputs from search() which
returns List[SearchResult].
- Around line 305-308: set_database_global_context_variables is being called
with context.scope.dataset_owner_id which is Optional[UUID] but the function
signature requires a non-optional UUID; replace the optional with a guaranteed
user id fallback by passing context.scope.user.id when
context.scope.dataset_owner_id is None (i.e., call
set_database_global_context_variables with context.scope.dataset_id and
context.scope.dataset_owner_id or context.scope.user.id as the user_id argument)
to avoid the type mismatch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a2f32f7-4d71-4f5e-8b87-47b9989797d7

📥 Commits

Reviewing files that changed from the base of the PR and between dfd8c6d and 7742e4d.

📒 Files selected for processing (2)
  • cognee/modules/agent_memory/runtime.py
  • cognee/tests/unit/modules/agent_memory/test_agent_memory.py
✅ Files skipped from review due to trivial changes (1)
  • cognee/tests/unit/modules/agent_memory/test_agent_memory.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
cognee/modules/agent_memory/runtime.py (2)

305-308: ⚠️ Potential issue | 🟠 Major

Type mismatch: dataset_owner_id is Optional[UUID] but the function expects UUID.

context.scope.dataset_owner_id is typed as Optional[UUID] (line 44), but set_database_global_context_variables expects user_id: UUID without Optional. Passing None would cause a runtime error when access control is enabled.

Proposed fix using user.id as fallback
         await set_database_global_context_variables(
             context.scope.dataset_id,
-            context.scope.dataset_owner_id,
+            context.scope.dataset_owner_id or context.scope.user.id,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 305 - 308,
context.scope.dataset_owner_id is Optional[UUID] but
set_database_global_context_variables requires a non-optional UUID; update the
call in runtime.py to pass a guaranteed UUID (e.g., use
context.scope.dataset_owner_id if not None else context.user.id) or raise a
clear error if neither is available. Locate the call to
set_database_global_context_variables and change the argument for user_id to a
validated/fallback value derived from context.scope.dataset_owner_id or
context.user.id so a None is never passed at runtime.

358-371: ⚠️ Potential issue | 🟡 Minor

Handle Pydantic SearchResult models to preserve structured data.

The search() function returns List[SearchResult] (Pydantic BaseModel). Since Pydantic models are not dict instances, they fall through to str(results) (line 371), losing the structured search_result field content.

Proposed fix to handle objects with search_result attribute
     if isinstance(results, dict):
         if "search_result" in results:
             return normalize_search_results(results["search_result"])
         return json.dumps(sanitize_value(results), ensure_ascii=False)
+    if hasattr(results, "search_result"):
+        return normalize_search_results(results.search_result)
     return str(results)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 358 - 371,
normalize_search_results currently treats Pydantic SearchResult models as
generic objects and falls back to str(...), losing structured fields; update
normalize_search_results to detect Pydantic models or any object with a
"search_result" attribute (or a .dict() method) and extract/normalize that field
instead of stringifying the model. Specifically, in normalize_search_results add
a branch that checks for hasattr(results, "search_result") and returns
normalize_search_results(results.search_result), and/or for objects with a
.dict() method call sanitize_value(results.dict()) and json.dumps(...) to
preserve structured data; keep existing handling for dict, list, str, None, and
the final fallback.
🧹 Nitpick comments (1)
cognee/modules/agent_memory/runtime.py (1)

311-312: Redundant await asyncio.create_task() pattern.

await asyncio.create_task(coro()) creates a task then immediately awaits it, adding overhead without benefit. If the goal is isolated ContextVar scope, consider adding a comment explaining this. Otherwise, simplify to a direct await.

Option 1: Direct await (if blocking is acceptable)
     try:
-        await asyncio.create_task(_persist_trace_in_task())
+        await _persist_trace_in_task()
     except Exception as error:
Option 2: Fire-and-forget (if non-blocking is intended)
-    try:
-        await asyncio.create_task(_persist_trace_in_task())
-    except Exception as error:
-        logger.warning(
-            "Agent trace persistence failed for %s: %s",
-            context.origin_function,
-            error,
-            exc_info=False,
-        )
+    task = asyncio.create_task(_persist_trace_in_task())
+    task.add_done_callback(
+        lambda t: logger.warning(
+            "Agent trace persistence failed for %s: %s",
+            context.origin_function,
+            t.exception(),
+            exc_info=False,
+        ) if t.exception() else None
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 311 - 312, Replace the
redundant await asyncio.create_task(_persist_trace_in_task()) pattern in
runtime.py: either directly await the coroutine by calling await
_persist_trace_in_task() if you intend to wait for completion, or call
asyncio.create_task(_persist_trace_in_task()) without awaiting it for
fire-and-forget behavior; if you used create_task solely to isolate ContextVar
scope, keep create_task but add a short comment referencing that intent near the
call and ensure the task is not immediately awaited.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 305-308: context.scope.dataset_owner_id is Optional[UUID] but
set_database_global_context_variables requires a non-optional UUID; update the
call in runtime.py to pass a guaranteed UUID (e.g., use
context.scope.dataset_owner_id if not None else context.user.id) or raise a
clear error if neither is available. Locate the call to
set_database_global_context_variables and change the argument for user_id to a
validated/fallback value derived from context.scope.dataset_owner_id or
context.user.id so a None is never passed at runtime.
- Around line 358-371: normalize_search_results currently treats Pydantic
SearchResult models as generic objects and falls back to str(...), losing
structured fields; update normalize_search_results to detect Pydantic models or
any object with a "search_result" attribute (or a .dict() method) and
extract/normalize that field instead of stringifying the model. Specifically, in
normalize_search_results add a branch that checks for hasattr(results,
"search_result") and returns normalize_search_results(results.search_result),
and/or for objects with a .dict() method call sanitize_value(results.dict()) and
json.dumps(...) to preserve structured data; keep existing handling for dict,
list, str, None, and the final fallback.

---

Nitpick comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 311-312: Replace the redundant await
asyncio.create_task(_persist_trace_in_task()) pattern in runtime.py: either
directly await the coroutine by calling await _persist_trace_in_task() if you
intend to wait for completion, or call
asyncio.create_task(_persist_trace_in_task()) without awaiting it for
fire-and-forget behavior; if you used create_task solely to isolate ContextVar
scope, keep create_task but add a short comment referencing that intent near the
call and ensure the task is not immediately awaited.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 170e99f5-e7e8-4d66-a2a5-c3547394a3b8

📥 Commits

Reviewing files that changed from the base of the PR and between 7742e4d and 726553d.

📒 Files selected for processing (1)
  • cognee/modules/agent_memory/runtime.py

@hajdul88 hajdul88 requested review from dexters1 and lxobr April 1, 2026 15:33
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
cognee/modules/agent_memory/runtime.py (2)

331-345: ⚠️ Potential issue | 🟠 Major

Persisted traces still retain potentially sensitive payloads.

The TODO is acknowledged, but current persistence still stores method_params and method_return_value without redaction (Lines 339-340). Truncation alone does not mitigate secrets/PII retention risk.

Proposed direction
+def redact_sensitive_value(key: Optional[str], value: Any) -> Any:
+    # mask by sensitive key names + known token/email patterns, then sanitize/truncate
+    ...
+
 def build_agent_trace(context: AgentMemoryContext) -> AgentTrace:
@@
-    return AgentTrace(
+    redacted_params = {
+        key: redact_sensitive_value(key, value)
+        for key, value in context.method_params.items()
+    }
+    redacted_return = redact_sensitive_value(None, context.method_return_value)
+    return AgentTrace(
@@
-        method_params=context.method_params,
-        method_return_value=sanitize_value(context.method_return_value),
+        method_params=redacted_params,
+        method_return_value=redacted_return,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 331 - 345,
build_agent_trace is persisting raw method_params and method_return_value
(AgentTrace.method_params and method_return_value) which risks storing
secrets/PII; modify build_agent_trace to sanitize/redact these fields before
constructing the AgentTrace: run both context.method_params and
context.method_return_value through a dedicated redaction routine (e.g.,
redact_sensitive_data or extend sanitize_value) that removes or masks secrets
and PII and then truncate if needed (use
truncate_text/MAX_SERIALIZED_VALUE_LENGTH) before assigning to
AgentTrace.method_params and AgentTrace.method_return_value so only
redacted/truncated payloads are persisted.

84-141: ⚠️ Potential issue | 🟠 Major

Validate memory_top_k at the decorator boundary.

memory_top_k is accepted without type/range checks (Line 138). Invalid values fail later inside search/retrieval and can degrade into silent empty-memory behavior instead of a clear config error.

Proposed fix
 def validate_agent_memory_config(
@@
-    if user is not None and not hasattr(user, "id"):
+    if not isinstance(memory_top_k, int) or isinstance(memory_top_k, bool):
+        raise CogneeValidationError("memory_top_k must be an integer.", log=False)
+    if memory_top_k < 1:
+        raise CogneeValidationError("memory_top_k must be >= 1.", log=False)
+    if user is not None and not hasattr(user, "id"):
         raise CogneeValidationError("user must have an id attribute.", log=False)
@@
         memory_top_k=memory_top_k,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 84 - 141, The
validate_agent_memory_config function does not validate memory_top_k so invalid
types or non-positive values can propagate; add a check in
validate_agent_memory_config to ensure memory_top_k is an int and >= 1 (or your
minimum allowed), and raise CogneeValidationError with a clear message
(log=False) if it fails; then pass the validated/normalized memory_top_k into
the AgentMemoryConfig return so downstream code always receives a safe integer
value.
🧹 Nitpick comments (1)
cognee/modules/agent_memory/runtime.py (1)

187-191: Add a type annotation for func in build_method_params.

func is currently untyped (Line 187). Typing it as Callable[..., Any] keeps this public runtime helper consistent with project typing standards.

Proposed fix
-from typing import Any, Optional
+from typing import Any, Callable, Optional
@@
-def build_method_params(func, args: tuple[Any, ...], kwargs: dict[str, Any]) -> dict[str, Any]:
+def build_method_params(
+    func: Callable[..., Any], args: tuple[Any, ...], kwargs: dict[str, Any]
+) -> dict[str, Any]:
As per coding guidelines: "Add type hints to Python functions" and "Public Python APIs should be type-annotated where practical".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/modules/agent_memory/runtime.py` around lines 187 - 191, The function
build_method_params lacks a type annotation for its first parameter; update its
signature to type the func parameter as Callable[..., Any] (import Callable and
Any from typing) so the public runtime helper adheres to project typing
standards and typing checks; ensure you update any relevant imports in the
module if needed and run type checks to confirm no new issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 331-345: build_agent_trace is persisting raw method_params and
method_return_value (AgentTrace.method_params and method_return_value) which
risks storing secrets/PII; modify build_agent_trace to sanitize/redact these
fields before constructing the AgentTrace: run both context.method_params and
context.method_return_value through a dedicated redaction routine (e.g.,
redact_sensitive_data or extend sanitize_value) that removes or masks secrets
and PII and then truncate if needed (use
truncate_text/MAX_SERIALIZED_VALUE_LENGTH) before assigning to
AgentTrace.method_params and AgentTrace.method_return_value so only
redacted/truncated payloads are persisted.
- Around line 84-141: The validate_agent_memory_config function does not
validate memory_top_k so invalid types or non-positive values can propagate; add
a check in validate_agent_memory_config to ensure memory_top_k is an int and >=
1 (or your minimum allowed), and raise CogneeValidationError with a clear
message (log=False) if it fails; then pass the validated/normalized memory_top_k
into the AgentMemoryConfig return so downstream code always receives a safe
integer value.

---

Nitpick comments:
In `@cognee/modules/agent_memory/runtime.py`:
- Around line 187-191: The function build_method_params lacks a type annotation
for its first parameter; update its signature to type the func parameter as
Callable[..., Any] (import Callable and Any from typing) so the public runtime
helper adheres to project typing standards and typing checks; ensure you update
any relevant imports in the module if needed and run type checks to confirm no
new issues.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ddc8b5dd-1f0e-49bf-bbc9-f0da713865bf

📥 Commits

Reviewing files that changed from the base of the PR and between 7742e4d and baf99f0.

📒 Files selected for processing (3)
  • cognee/modules/agent_memory/runtime.py
  • cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py
  • cognee/tests/test_agent_memory_e2e.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • cognee/tests/integration/modules/agent_memory/test_agent_memory_integration.py
  • cognee/tests/test_agent_memory_e2e.py

@hajdul88 hajdul88 marked this pull request as draft April 3, 2026 07:35
@hajdul88 hajdul88 marked this pull request as ready for review April 3, 2026 08:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
cognee/tests/test_agent_memory_e2e.py (1)

70-82: ⚠️ Potential issue | 🟠 Major

Restore global config state mutated by the fixture.

Lines 73-74 mutate global Cognee root directories, but teardown at Line 81 does not restore previous values. This can leak state to later tests in the same process.

Proposed fix
+from collections.abc import AsyncIterator
+
 `@pytest_asyncio.fixture`
-async def agent_memory_e2e_env(tmp_path):
+async def agent_memory_e2e_env(tmp_path: Path) -> AsyncIterator[None]:
     """Create a clean backend-access-controlled environment for agent-memory e2e tests."""
     root = Path(tmp_path)
+    previous_data_root = cognee.config.data_root_directory()
+    previous_system_root = cognee.config.system_root_directory()
     cognee.config.data_root_directory(str(root / "data"))
     cognee.config.system_root_directory(str(root / "system"))
 
     await _reset_engines_and_prune()
     await engine_setup()
 
-    yield
-
-    await _reset_engines_and_prune()
+    try:
+        yield
+    finally:
+        await _reset_engines_and_prune()
+        cognee.config.data_root_directory(previous_data_root)
+        cognee.config.system_root_directory(previous_system_root)

Based on learnings: "Applies to cognee/tests/**/test_*.py : Avoid external state in Python tests; rely on test fixtures and CI-provided environment variables".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cognee/tests/test_agent_memory_e2e.py` around lines 70 - 82, The fixture
agent_memory_e2e_env mutates global Cognee config via
cognee.config.data_root_directory(...) and
cognee.config.system_root_directory(...) but never restores the previous values;
capture the original values (e.g., old_data_root =
cognee.config.data_root_directory() and old_system_root =
cognee.config.system_root_directory()) before setting the test paths, then after
the yield restore them (call cognee.config.data_root_directory(old_data_root)
and cognee.config.system_root_directory(old_system_root)) so the global state is
returned to its prior values; ensure these captures/restores wrap the existing
await _reset_engines_and_prune()/engine_setup() logic in the same async
generator fixture.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cognee/tests/test_agent_memory_e2e.py`:
- Around line 23-30: The cleanup block in the test is swallowing all exceptions;
instead, call get_vector_engine(), attempt disposal via
vector_engine.engine.dispose(close=True) but catch only expected exceptions
(e.g., AttributeError, RuntimeError, or the specific engine-dispose error type)
and log the exception details (or re-raise after logging) rather than using a
bare except: pass; ensure you reference get_vector_engine and
vector_engine.engine.dispose so the handler only suppresses known harmless
errors and surfaces or logs unexpected failures for debugging.

---

Duplicate comments:
In `@cognee/tests/test_agent_memory_e2e.py`:
- Around line 70-82: The fixture agent_memory_e2e_env mutates global Cognee
config via cognee.config.data_root_directory(...) and
cognee.config.system_root_directory(...) but never restores the previous values;
capture the original values (e.g., old_data_root =
cognee.config.data_root_directory() and old_system_root =
cognee.config.system_root_directory()) before setting the test paths, then after
the yield restore them (call cognee.config.data_root_directory(old_data_root)
and cognee.config.system_root_directory(old_system_root)) so the global state is
returned to its prior values; ensure these captures/restores wrap the existing
await _reset_engines_and_prune()/engine_setup() logic in the same async
generator fixture.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4ab5cb29-3906-4b65-a2fd-80e723b7b9da

📥 Commits

Reviewing files that changed from the base of the PR and between baf99f0 and eee0937.

📒 Files selected for processing (5)
  • cognee/modules/agent_memory/decorator.py
  • cognee/modules/agent_memory/runtime.py
  • cognee/tests/test_agent_memory_e2e.py
  • cognee/tests/unit/modules/agent_memory/test_agent_memory.py
  • examples/guides/agent_memory_quickstart.py
✅ Files skipped from review due to trivial changes (4)
  • cognee/modules/agent_memory/decorator.py
  • examples/guides/agent_memory_quickstart.py
  • cognee/tests/unit/modules/agent_memory/test_agent_memory.py
  • cognee/modules/agent_memory/runtime.py

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant