Skip to content
This repository was archived by the owner on Apr 4, 2026. It is now read-only.

fix: memory dashboard delete + model picker empty list (#23, #24)#25

Merged
Nafania merged 5 commits into
mainfrom
fix/issues-23-24
Mar 27, 2026
Merged

fix: memory dashboard delete + model picker empty list (#23, #24)#25
Nafania merged 5 commits into
mainfrom
fix/issues-23-24

Conversation

@Nafania
Copy link
Copy Markdown
Owner

@Nafania Nafania commented Mar 27, 2026

Summary

Changes

File What
python/helpers/memory.py Added _read_data_item_content(), fixed delete_documents_by_ids and _delete_data_by_id to read file content
python/api/memory_dashboard.py Reuse shared _read_data_item_content from memory module
python/helpers/connected_providers.py Extended list_models() with API-key fallback via _list_models_via_api()
python/api/connected_providers.py New /connected_providers endpoint returning all providers with valid credentials
webui/js/model-picker.js Rewrote to load from all connected providers; added init(), toggle()
webui/components/chat/top-section/chat-top.html Removed model picker
webui/components/chat/input/bottom-actions.html Added model picker with upward dropdown

Test plan

  • tests/helpers/test_connected_providers.py — 11 passed
  • tests/api/test_provider_models.py — 3 passed
  • Manual: open Memory Dashboard → select memory → delete → should succeed
  • Manual: bottom bar → model picker dropdown → should show models from configured provider
  • Manual: select a model override → verify it persists per chat

Closes #23
Closes #24

Made with Cursor

Nafania added 2 commits March 27, 2026 11:42
- Fix memory delete: read file content instead of path to match IDs
- Fix model picker: load from all connected providers, not just OAuth
- Add /connected_providers API endpoint for API-key-based providers
- Move model picker from top bar to bottom actions bar

Closes #23
Closes #24

Made-with: Cursor
When the Cognee data item file cannot be read (e.g. path is not a real
file), fall back to returning raw_data_location as the searchable
string. This preserves backward compatibility with tests and older data
while still reading file content when available (fixing #23).

Made-with: Cursor
Copy link
Copy Markdown
Owner Author

@Nafania Nafania left a comment

Choose a reason for hiding this comment

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

Code Review: PR #25 — Fix Memory Delete + Model Picker (#23, #24)

Strengths

  1. Correct root cause fix for #23. _read_data_item_content() properly reads the file at raw_data_location to find the [META:{"id":"..."}] header, fixing the mismatch between where IDs live (file content) and where the old code searched (file path).

  2. Good DRY refactor. Duplicate file-reading logic extracted into a single shared function in memory.py — all three call sites (delete_documents_by_ids, _delete_data_by_id, dashboard listing) are now consistent.

  3. Graceful fallback chain. _read_data_item_content tries: file read → raw_data_location as string → item.name. Handles missing/unreadable files without breaking.

  4. Clean model-listing architecture. OAuth-first with API-key fallback is logical. Parallel model fetching with Promise.all in the frontend is efficient.

  5. Good UI implementation. Upward-opening dropdown for bottom-bar placement, loading state, empty state messaging, context length display, vision tags, and "Reset to default".


Issues Summary

Severity ID Issue
🟥 Critical C1 aiohttp is an undeclared dependency — runtime crash risk
🟧 Important I1 No tests for _read_data_item_content (the core #23 fix)
🟧 Important I2 No tests for _list_models_via_api
🟧 Important I3 No tests for ProviderPool.list_models API-key path
🟧 Important I4 No tests for ConnectedProviders API handler
🟧 Important I5 Underscore-prefixed function exported as public API
🟧 Important I6 Vision detection only works for OpenRouter
🟨 Minor M1 Synchronous file I/O in async context
🟨 Minor M2 _API_BASES dict recreated on every call
🟨 Minor M3 Model list only loaded once on init() — no refresh without page reload
🟨 Minor M4 Inline styles mixed with CSS classes in bottom-actions.html

Recommendations

  1. Before merge: fix C1 — replace aiohttp with httpx.AsyncClient (already a declared dependency).
  2. Before or immediately after merge: add tests for I1–I4. At minimum:
    • 5 tests for _read_data_item_content (file found, file:// URI, file missing, no raw_data_location, exception)
    • 4 tests for _list_models_via_api (success, non-200, network error, empty data)
    • 2 tests for list_models API-key path
    • 2 tests for ConnectedProviders handler
  3. Consider extending provider config (providers.yaml) for API base URLs instead of the hardcoded _API_BASES dict.

Assessment

Ready to merge? ❌ No — fix C1 first.

Core logic for both bug fixes is correct and well-reasoned. The one blocker is aiohttp being an undeclared dependency — it's a simple swap to httpx which is already available. After that fix, the PR is functionally ready. Test gaps (I1–I4) are significant but shouldn't block merge given active user impact.

Comment thread python/helpers/connected_providers.py Outdated
async def _list_models_via_api(provider_id: str, api_key: str) -> list[ModelInfo]:
"""List models from a provider using its OpenAI-compatible /models endpoint."""
from python.helpers.providers import get_provider_config
import aiohttp
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟥 C1 — Critical: aiohttp is not in requirements.txt and is not used anywhere else in the codebase. In a clean install without Cognee's transitive deps, this will raise ModuleNotFoundError the first time any API-key provider tries to list models.

The project already declares httpx>=0.27.0. Replace with:

import httpx

async with httpx.AsyncClient(timeout=15.0) as client:
    resp = await client.get(models_url, headers={"Authorization": f"Bearer {api_key}"})
    if resp.status_code != 200:
        logger.warning("Models API returned %d for %s", resp.status_code, provider_id)
        return []
    data = resp.json()

Comment thread python/api/memory_dashboard.py Outdated

from python.helpers.api import ApiHandler, Request, Response
from python.helpers.memory import Memory, get_existing_memory_subdirs, get_context_memory_subdir
from python.helpers.memory import Memory, get_existing_memory_subdirs, get_context_memory_subdir, _read_data_item_content
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟧 I5 — Important: _read_data_item_content uses the underscore convention indicating it's module-private, but it's imported here as a cross-module dependency. Either:

  • Rename to read_data_item_content (no underscore) to acknowledge its public status, or
  • Route the call through a public method on Memory (e.g. Memory.read_data_item_content(item))

continue
name = m.get("name") or mid
ctx = m.get("context_length") or m.get("context_window") or 0
vision = False
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟧 I6 — Important: The architecture.modality field is OpenRouter-specific. OpenAI, Mistral, Groq, xAI, DeepSeek don't return this in their /models response. supports_vision will always be False for non-OpenRouter providers.

At minimum document this limitation. Ideally, add provider-specific parsing (e.g. checking model ID patterns like gpt-4o, gemini-*-pro-vision) or just skip the vision flag for non-OpenRouter providers.

Comment thread python/helpers/connected_providers.py Outdated
kwargs = config.get("kwargs", {}) or {}
api_base = kwargs.get("api_base", "")

_API_BASES = {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟨 M2 — Minor: This constant dict is recreated on every function call. Move to module level. Also consider pulling API base URLs from providers.yaml / get_provider_config instead of maintaining a parallel hardcoded list that can drift.

Comment thread python/helpers/memory.py Outdated
return text, {"area": Memory.Area.MAIN.value}


def _read_data_item_content(item) -> str:
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟧 I1 — Important: This function is the core fix for #23 and has zero test coverage. Needs at least 4–5 unit tests:

  • Valid file at raw_data_location → returns file content
  • file:// URI scheme → correctly resolves and reads
  • File doesn't exist → falls back to raw_data_location string
  • raw_data_location is None → falls back to item.name
  • File exists but unreadable → falls back gracefully

🟨 M1 — Minor: Synchronous open() + read() in an async call path. For large datasets with many items, this could block the event loop. Consider asyncio.to_thread() wrapping if performance becomes an issue.

Comment thread webui/js/model-picker.js
open: false,
loading: false,

init() {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟨 M3 — Minor: Models are loaded once on init() and never refreshed. If the user configures a new provider's API key in Settings, they'd need to reload the page to see new models. A refresh button in the dropdown or periodic re-fetch (e.g. on each toggle when data is stale) would improve UX.

Nafania added 2 commits March 27, 2026 12:23
- Replace aiohttp with httpx in _list_models_via_api (C1)
- Rename _read_data_item_content → read_data_item_content for public API (I5)
- Move _API_BASES to module-level _KNOWN_API_BASES, extract _resolve_api_base helper (M2)
- Add 5 unit tests for read_data_item_content (I1)
- Refresh models on toggle when data is stale (>5 min) (M3)

Made-with: Cursor
- Add tests for _list_models_via_api (success, non-200, network error,
  empty data, no api base, OpenRouter vision detection)
- Add tests for ProviderPool.list_models API-key fallback path
  (no credential, happy path, caching, stale cache fallback)
- Add tests for ConnectedProviders API handler
- Document vision detection as OpenRouter-specific
- Add read_data_item_content_async to avoid blocking the event loop
- Move inline styles to CSS classes in bottom-actions.html

Made-with: Cursor
@Nafania
Copy link
Copy Markdown
Owner Author

Nafania commented Mar 27, 2026

Review Update — All Issues Addressed

Commits f75acc08 + defea3ca address all review comments. Summary:

Issue Status Fix
C1 aiohttp undeclared dep ✅ Fixed in f75acc08 Replaced with httpx.AsyncClient
I1 No tests for read_data_item_content ✅ Fixed in f75acc08 5 tests added
I2 No tests for _list_models_via_api ✅ Fixed in defea3ca 6 tests (success, non-200, network error, empty data, no api base, vision detection)
I3 No tests for list_models API-key path ✅ Fixed in defea3ca 4 tests (no cred, happy path, caching, stale cache fallback)
I4 No tests for ConnectedProviders handler ✅ Fixed in defea3ca 3 tests (happy path, empty, get_methods)
I5 Underscore-prefixed public function ✅ Fixed in f75acc08 Renamed to read_data_item_content
I6 Vision detection OpenRouter-only ✅ Fixed in defea3ca Documented limitation in code comment
M1 Sync file I/O in async context ✅ Fixed in defea3ca Added read_data_item_content_async via asyncio.to_thread
M2 _API_BASES inside function ✅ Fixed in f75acc08 Module-level _KNOWN_API_BASES + _resolve_api_base() helper
M3 No model refresh ✅ Fixed in f75acc08 Staleness check (5min) on toggle
M4 Inline styles ✅ Fixed in defea3ca Moved to CSS classes

All 32 tests pass. Ready to merge.

Pre-existing tests still referenced _read_data_item_content (with
underscore). Updated to read_data_item_content and fixed assertion
for missing-file fallback (returns raw_data_location, not name).

Made-with: Cursor
@Nafania Nafania merged commit 41be641 into main Mar 27, 2026
4 checks passed
Nafania added a commit that referenced this pull request Mar 31, 2026
- Add max_agent_depth to AgentConfig (default 5)
- Refuse delegation when depth limit reached
- Detect same-profile delegation loops (streak >= 2)
- Add depth awareness to agent info prompt
- Show subordinate status in agent info

Made-with: Cursor
Nafania added a commit that referenced this pull request Mar 31, 2026
Mock agents need explicit max_agent_depth=5 on config to avoid
MagicMock vs int comparison errors in depth/loop checks.
Update agent info test assertion to include new prompt parameters.

Made-with: Cursor
Nafania added a commit that referenced this pull request Mar 31, 2026
feat(#25): Subordinate delegation loop prevention and depth limits
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug + Enhancement: Model selector dropdown is empty and should be relocated Bug: Memory dashboard delete fails with 'Memory with ID not found'

1 participant