Skip to content

12 18 test env fix#205

Closed
thestumonkey wants to merge 2 commits intoSimpleOpenSoftware:mainfrom
Ushadow-io:12-18-test-env_fix
Closed

12 18 test env fix#205
thestumonkey wants to merge 2 commits intoSimpleOpenSoftware:mainfrom
Ushadow-io:12-18-test-env_fix

Conversation

@thestumonkey
Copy link
Contributor

@thestumonkey thestumonkey commented Dec 18, 2025

Fixed the robot test-env not using right .env file
Added a method that was missing on the chronicle mem implementation. The memory abstraction model still needs some work though, will be done in later PR

Summary by CodeRabbit

  • New Features
    • Added ability to retrieve individual memory entries by ID with optional ownership verification
  • Chores
    • Updated environment configuration loading mechanism

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

A new public method get_memory() is added to MemoryService for retrieving individual memory entries with optional ownership validation. Timestamp handling is normalized across memory operations by converting created_at from string to integer epoch seconds. Test environment setup is refactored to use the standard load_dotenv library instead of a custom loader.

Changes

Cohort / File(s) Summary
Memory retrieval and timestamp normalization
backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py
Added public get_memory(memory_id, user_id=None) method that retrieves and validates memory entries. Changed created_at type from string to integer (epoch seconds) in _create_memory_entries and _apply_memory_actions methods.
Test environment setup
tests/setup/test_env.py
Replaced custom load_env_file function with standard load_dotenv library. Original function body commented out; environment variables now loaded via load_dotenv with override disabled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Type signature changes from string to integer for created_at fields require verification of impact across memory operations
  • New public API method should be reviewed for proper error handling, authorization logic, and consistency with existing retrieval patterns
  • Env loading refactoring should confirm backward compatibility and behavior parity with the original implementation

Poem

🐰 A memory keeper awakens new sight—
Now retrieving chronicles, shining so bright!
Timestamps transform from words into time,
While setup sequences settle sublime.
The rabbit hops forth, all systems align! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title '12 18 test env fix' is vague and lacks clarity. While it references 'test env fix', the numeric prefix is unclear and the title fails to convey the actual changes: fixing the test environment .env file loading and adding a get_memory method to MemoryService. Use a more descriptive title like 'Fix test environment .env loading and add get_memory method' that clearly conveys the main changes in the pull request.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link
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 (2)
tests/setup/test_env.py (1)

8-23: Remove commented-out code.

The custom load_env_file function and its call are no longer needed. Since version control preserves the history, this dead code should be removed entirely rather than left as comments.

🔎 Apply this diff to remove the commented code:
-# def load_env_file():
-#     """Load environment variables from .env file if it exists."""
-#     # Look for .env in backends/advanced directory
-#     env_file = Path(__file__).parent.parent.parent / "backends" / "advanced" / ".env"
-#     if env_file.exists():
-#         with open(env_file) as f:
-#             for line in f:
-#                 line = line.strip()
-#                 if line and not line.startswith('#') and '=' in line:
-#                     key, value = line.split('=', 1)
-#                     # Only set if not already in environment (CI takes precedence)
-#                     if key not in os.environ:
-#                         os.environ[key] = value
-
-# Load .env file (CI environment variables take precedence)
-# load_env_file()
-
backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py (1)

293-347: Add a method to VectorStoreBase for single memory retrieval by ID.

The get_memory method directly accesses self.vector_store.client.retrieve() and self.vector_store.collection_name, bypassing the VectorStoreBase interface. Add a get_memory_by_id method to VectorStoreBase that QdrantVectorStore can implement, allowing other vector store providers to be added in the future while maintaining clean abstraction boundaries.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81297d7 and 4303b2a.

📒 Files selected for processing (2)
  • backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py (3 hunks)
  • tests/setup/test_env.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use Black formatter with 100-character line length for Python code
Use isort for import sorting in Python code
ALL imports must be at the top of the file after the docstring. NEVER import modules in the middle of functions or files. Use lazy imports sparingly and only when absolutely necessary for circular import issues.
Group imports in order: standard library, third-party, local imports
Always raise errors, never silently ignore. Use explicit error handling with proper exceptions rather than silent failures.
Do not add defensive hasattr() checks. Research and understand input/response or class structure instead.
Use uv run python or uv run python3 instead of direct python or python3 commands for Python execution

Files:

  • backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py
  • tests/setup/test_env.py
🧠 Learnings (2)
📚 Learning: 2025-12-16T14:24:28.301Z
Learnt from: CR
Repo: chronicler-ai/chronicle PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T14:24:28.301Z
Learning: Applies to **/{memory,provider}*.py : Implement pluggable memory providers supporting both Chronicle native (individual facts) and OpenMemory MCP delegation

Applied to files:

  • backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py
📚 Learning: 2025-12-08T23:52:34.959Z
Learnt from: AnkushMalaker
Repo: chronicler-ai/chronicle PR: 178
File: backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py:218-223
Timestamp: 2025-12-08T23:52:34.959Z
Learning: In Python code (chronicle project), prefer logging.exception() inside except blocks to automatically log the full stack trace. When re-raising exceptions, always chain with 'raise ... from e' to preserve the original context; use 'raise ... from None' only if you explicitly want to suppress the context. This improves debuggability across Python files.

Applied to files:

  • backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py
  • tests/setup/test_env.py
🧬 Code graph analysis (1)
backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py (1)
backends/advanced/src/advanced_omi_backend/services/memory/base.py (1)
  • MemoryEntry (26-63)
🪛 Ruff (0.14.8)
backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py

343-343: Consider moving this statement to an else block

(TRY300)

🔇 Additional comments (1)
tests/setup/test_env.py (1)

4-4: LGTM!

Using the standard load_dotenv library is a good improvement over the custom loader.

},
embedding=embedding,
created_at=str(int(time.time())),
created_at=int(time.time()),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "chronicle.py" -path "*/memory/providers/*" | head -20

Repository: chronicler-ai/chronicle

Length of output: 150


🏁 Script executed:

fd -t f "chronicle.py" | grep -E "memory/providers"

Repository: chronicler-ai/chronicle

Length of output: 148


🏁 Script executed:

wc -l backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py

Repository: chronicler-ai/chronicle

Length of output: 152


🏁 Script executed:

# Find MemoryEntry base class definition and its created_at field
rg -n "class MemoryEntry" backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py

Repository: chronicler-ai/chronicle

Length of output: 49


🏁 Script executed:

# Search for MemoryEntry imports and definition
rg -n "MemoryEntry" backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py | head -20

Repository: chronicler-ai/chronicle

Length of output: 1013


🏁 Script executed:

# Find the base.py file and examine MemoryEntry definition
fd -t f "base.py" backends/advanced/src/advanced_omi_backend/services/memory/ | head -5

Repository: chronicler-ai/chronicle

Length of output: 133


🏁 Script executed:

# Check the size of base.py first
wc -l backends/advanced/src/advanced_omi_backend/services/memory/base.py

Repository: chronicler-ai/chronicle

Length of output: 137


🏁 Script executed:

# Find MemoryEntry class definition in base.py
rg -n "class MemoryEntry" backends/advanced/src/advanced_omi_backend/services/memory/base.py -A 20

Repository: chronicler-ai/chronicle

Length of output: 935


🏁 Script executed:

# Check if there's a __post_init__ method in MemoryEntry
rg -n "__post_init__" backends/advanced/src/advanced_omi_backend/services/memory/base.py -A 10

Repository: chronicler-ai/chronicle

Length of output: 578


🏁 Script executed:

# Check lines around 487 in chronicle.py
sed -n '480,495n' backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py

Repository: chronicler-ai/chronicle

Length of output: 49


🏁 Script executed:

# Check lines around 487 in chronicle.py using head/tail
head -495 backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py | tail -20

Repository: chronicler-ai/chronicle

Length of output: 788


🏁 Script executed:

# Check line 693 area
head -700 backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py | tail -20

Repository: chronicler-ai/chronicle

Length of output: 930


🏁 Script executed:

# Check line 338 area for get_memory
head -345 backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py | tail -20

Repository: chronicler-ai/chronicle

Length of output: 921


🏁 Script executed:

# Check the imports and dataclass decorator in base.py
head -50 backends/advanced/src/advanced_omi_backend/services/memory/base.py

Repository: chronicler-ai/chronicle

Length of output: 1617


🏁 Script executed:

# Verify what happens when int is passed to created_at field
# Let's trace through the issue: what does __post_init__ actually do with an int value?
cat > /tmp/test_dataclass.py << 'EOF'
import time
from dataclasses import dataclass
from typing import Optional

@dataclass
class TestEntry:
    content: str
    created_at: Optional[str] = None
    
    def __post_init__(self):
        if self.created_at is None:
            self.created_at = str(int(time.time()))

# Test 1: Pass None - __post_init__ converts to string
entry1 = TestEntry(content="test", created_at=None)
print(f"Test 1 (None): type={type(entry1.created_at).__name__}, value={entry1.created_at}")

# Test 2: Pass int - __post_init__ does NOT convert
entry2 = TestEntry(content="test", created_at=int(time.time()))
print(f"Test 2 (int): type={type(entry2.created_at).__name__}, value={entry2.created_at}")

# Test 3: Pass string - remains string
entry3 = TestEntry(content="test", created_at="1234567890")
print(f"Test 3 (str): type={type(entry3.created_at).__name__}, value={entry3.created_at}")
EOF
python /tmp/test_dataclass.py

Repository: chronicler-ai/chronicle

Length of output: 153


Ensure created_at field uses consistent types across creation and retrieval.

The code passes int(time.time()) for created_at at lines 487 and 693, but the base class MemoryEntry defines this field as Optional[str]. The __post_init__ method only converts None to a string, so passing an integer directly creates a type violation.

Additionally, the get_memory method (line 338) retrieves created_at without normalizing the type, risking mixed types (string or int) depending on when the memory was stored.

Verify the intended type and ensure consistency:

  1. Either convert int(time.time()) to str(int(time.time())) when creating instances
  2. Or update the field type hint to Optional[int | str] and normalize on retrieval
  3. Update __post_init__ or retrieval logic to handle both types consistently
🤖 Prompt for AI Agents
In
backends/advanced/src/advanced_omi_backend/services/memory/providers/chronicle.py
around lines 487 and 693 (and referencing retrieval at ~338), created_at is
being set with int(time.time()) while the base MemoryEntry defines created_at as
Optional[str], and get_memory does not normalize types; fix by choosing one
approach and implementing it consistently: either (A) convert timestamps to
strings when creating entries (use str(int(time.time()))) and ensure
__post_init__ still enforces a string, or (B) widen the type hint to
Optional[int | str], update __post_init__ to normalize both int and str into a
single canonical type, and modify get_memory to always return the canonical
type; apply the chosen change to all creation sites (lines 487 and 693), the
MemoryEntry type hint/__post_init__, and the retrieval path around line 338 so
created_at is consistent across create and read.

Copy link
Collaborator

@AnkushMalaker AnkushMalaker left a comment

Choose a reason for hiding this comment

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

Please keep this PR focused, address the get_memory in a separate PR. Closing this.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants