cortexdb-agno 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ # Rust
2
+ /target
3
+ **/*.rs.bk
4
+
5
+ # Environment / secrets
6
+ .env
7
+ .env.local
8
+ .env*.local
9
+ *.pem
10
+ *.key
11
+ .npmrc
12
+
13
+ # SQLite database
14
+ *.sqlite
15
+ *.sqlite-wal
16
+ *.sqlite-shm
17
+
18
+ # OS
19
+ .DS_Store
20
+ Thumbs.db
21
+ desktop.ini
22
+
23
+ # IDE
24
+ .idea/
25
+ .vscode/
26
+ *.swp
27
+ *.swo
28
+
29
+ # Data directories
30
+ cortexdb_data*/
31
+ /data/
32
+ # Per-bench tenant stores (RocksDB + Tantivy + HNSW state; regeneratable per run)
33
+ /data_*/
34
+ # Experimental per-branch stores (not tracked on this branch but left gitignored
35
+ # so checkout from other branches doesn't surface them in git status)
36
+ /event_memory_store/
37
+ /llm_cache/
38
+
39
+ # Benchmark inputs and per-run outputs (kept local, regenerated each run)
40
+ benchmarks/longmemeval/data/
41
+ benchmarks/longmemeval/server_results/
42
+ benchmarks/longmemeval/fast_results/
43
+ benchmarks/longmemeval/micro_results/
44
+ benchmarks/longmemeval/server_logs/
45
+ benchmarks/longmemeval/*.log
46
+ benchmarks/locomo/locomo_results*.json
47
+ benchmarks/locomo/server_results/
48
+ benchmarks/locomo/*.log
49
+ /answer_out.json
50
+
51
+ # Local Claude Code state
52
+ .claude/
53
+ .tmp/
54
+
55
+ # Python
56
+ __pycache__/
57
+ *.pyc
58
+ .venv/
59
+ venv/
60
+
61
+ # Node
62
+ node_modules/
63
+ dist/
64
+ .next/
65
+
66
+ # Egg info
67
+ *.egg-info/
68
+
69
+ # Scratch/debug text files at root
70
+ /*.txt
71
+ /*.log
72
+
73
+ # Local debug / marketing / private content (not for repo)
74
+ harness/.reports/
75
+ harness_data_*/
76
+ blog/
77
+ sales/
78
+ videos/
79
+ local-instance/
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.4
2
+ Name: cortexdb-agno
3
+ Version: 0.1.0
4
+ Summary: Agno integration for CortexDB — long-term memory for AI systems
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: agno>=1.0
8
+ Requires-Dist: cortexdbai>=0.1.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
11
+ Requires-Dist: pytest>=7.0; extra == 'dev'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # cortexdb-agno
15
+
16
+ Agno integration for CortexDB long-term memory.
17
+
18
+ > **LLM provider note (audit BLK-2):** the canonical example uses
19
+ > `from agno.models.openai import OpenAIChat`. CortexDB itself is
20
+ > LLM-agnostic — Agno picks the model. Swap providers with one line:
21
+ >
22
+ > ```python
23
+ > # OpenAI (default in the docs)
24
+ > from agno.models.openai import OpenAIChat
25
+ > model = OpenAIChat(id="gpt-4o")
26
+ >
27
+ > # Anthropic
28
+ > from agno.models.anthropic import Claude
29
+ > model = Claude(id="claude-sonnet-4-6")
30
+ >
31
+ > # Google
32
+ > from agno.models.google import Gemini
33
+ > model = Gemini(id="gemini-1.5-pro")
34
+ > ```
35
+ >
36
+ > Nothing in `cortexdb-agno` reads `OPENAI_API_KEY`.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install cortexdb-agno
42
+ ```
43
+
44
+ ## API base URL
45
+
46
+ Defaults to `https://api-v1.cortexdb.ai` (audit FRI-8). Override with
47
+ `CORTEXDB_API_URL`.
@@ -0,0 +1,34 @@
1
+ # cortexdb-agno
2
+
3
+ Agno integration for CortexDB long-term memory.
4
+
5
+ > **LLM provider note (audit BLK-2):** the canonical example uses
6
+ > `from agno.models.openai import OpenAIChat`. CortexDB itself is
7
+ > LLM-agnostic — Agno picks the model. Swap providers with one line:
8
+ >
9
+ > ```python
10
+ > # OpenAI (default in the docs)
11
+ > from agno.models.openai import OpenAIChat
12
+ > model = OpenAIChat(id="gpt-4o")
13
+ >
14
+ > # Anthropic
15
+ > from agno.models.anthropic import Claude
16
+ > model = Claude(id="claude-sonnet-4-6")
17
+ >
18
+ > # Google
19
+ > from agno.models.google import Gemini
20
+ > model = Gemini(id="gemini-1.5-pro")
21
+ > ```
22
+ >
23
+ > Nothing in `cortexdb-agno` reads `OPENAI_API_KEY`.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install cortexdb-agno
29
+ ```
30
+
31
+ ## API base URL
32
+
33
+ Defaults to `https://api-v1.cortexdb.ai` (audit FRI-8). Override with
34
+ `CORTEXDB_API_URL`.
@@ -0,0 +1,13 @@
1
+ """CortexDB integration for Agno.
2
+
3
+ Provides a memory backend and agent tools that allow Agno agents
4
+ to use CortexDB as their long-term memory system.
5
+ """
6
+
7
+ from cortexdb_agno.memory import CortexDBMemory
8
+ from cortexdb_agno.tools import CortexDBTools
9
+
10
+ __all__ = [
11
+ "CortexDBMemory",
12
+ "CortexDBTools",
13
+ ]
@@ -0,0 +1,113 @@
1
+ """Agno memory backend powered by CortexDB.
2
+
3
+ Provides a memory implementation compatible with Agno's memory system,
4
+ backed by CortexDB's long-term memory. This enables Agno agents to
5
+ persist and recall knowledge across sessions and conversations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Optional
11
+
12
+ from cortexdb import Cortex
13
+
14
+
15
+ class CortexDBMemory:
16
+ """Agno memory backend that stores and retrieves context via CortexDB.
17
+
18
+ Implements Agno's ``Memory`` interface backed by CortexDB's semantic
19
+ storage. This enables agents to share knowledge across sessions,
20
+ remember outcomes from previous runs, and build up persistent
21
+ context over time.
22
+
23
+ Args:
24
+ client: An initialized CortexDB client instance.
25
+ scope: The hierarchical scope path for memory isolation.
26
+
27
+ Example::
28
+
29
+ from cortexdb import Cortex
30
+ from cortexdb_agno import CortexDBMemory
31
+
32
+ client = Cortex("http://localhost:3141")
33
+ memory = CortexDBMemory(client=client, scope="user:default")
34
+
35
+ # Store a memory
36
+ memory.add("The deployment window is 2-4am UTC on Tuesdays.")
37
+
38
+ # Search memories
39
+ results = memory.search("deployment schedule")
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ client: Cortex,
45
+ scope: str = "user:default",
46
+ ) -> None:
47
+ """Initialize CortexDBMemory with a CortexDB client.
48
+
49
+ Args:
50
+ client: An initialized CortexDB client instance.
51
+ scope: The hierarchical scope path for memory isolation.
52
+ """
53
+ self._client = client
54
+ self._scope = scope
55
+
56
+ def add(self, content: str, metadata: Optional[dict[str, Any]] = None) -> dict[str, Any]:
57
+ """Store a memory in CortexDB.
58
+
59
+ Args:
60
+ content: The text content to store.
61
+ metadata: Optional metadata to associate with the memory.
62
+
63
+ Returns:
64
+ The experience response dict from CortexDB.
65
+ """
66
+ return self._client.experience(self._scope, text=content)
67
+
68
+ def search(self, query: str) -> dict[str, Any]:
69
+ """Search for relevant memories in CortexDB.
70
+
71
+ Args:
72
+ query: The search query string.
73
+
74
+ Returns:
75
+ A dict with a ``context`` key holding the recalled context block.
76
+ """
77
+ result = self._client.recall(self._scope, query=query)
78
+
79
+ return {
80
+ "context": result.get("context_block", "") if result else "",
81
+ }
82
+
83
+ def clear(self) -> dict[str, Any]:
84
+ """Clear all memories for the current scope.
85
+
86
+ Issues a forget command to CortexDB to remove all stored memories.
87
+
88
+ Returns:
89
+ The forget response dict from CortexDB.
90
+ """
91
+ return self._client.forget(
92
+ self._scope,
93
+ confirm_all=True,
94
+ cascade="redact_events",
95
+ reason="Agno memory clear requested",
96
+ )
97
+
98
+ def get_context(self, query: str) -> str:
99
+ """Retrieve formatted context for an agent prompt.
100
+
101
+ Convenience method that fetches relevant memories and formats them
102
+ as a single context string suitable for injection into an agent's
103
+ system prompt or message history.
104
+
105
+ Args:
106
+ query: The query to find relevant context for.
107
+
108
+ Returns:
109
+ A formatted string of relevant memories.
110
+ """
111
+ result = self._client.recall(self._scope, query=query)
112
+
113
+ return result.get("context_block", "") if result else ""
@@ -0,0 +1,97 @@
1
+ """Agno tools for interacting with CortexDB.
2
+
3
+ Provides a toolkit of tools that Agno agents can use to store, search,
4
+ and forget memories in CortexDB's long-term memory system.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from agno.tools import Toolkit
12
+
13
+ from cortexdb import Cortex
14
+
15
+
16
+ class CortexDBTools(Toolkit):
17
+ """Toolkit that creates Agno-compatible tools for CortexDB operations.
18
+
19
+ Provides search, store, and forget tools that can be passed directly
20
+ to an Agno ``Agent``.
21
+
22
+ Args:
23
+ client: An initialized CortexDB client instance.
24
+ scope: The hierarchical scope path for memory isolation.
25
+
26
+ Example::
27
+
28
+ from agno.agent import Agent
29
+ from cortexdb import Cortex
30
+ from cortexdb_agno import CortexDBTools
31
+
32
+ client = Cortex("https://api-v1.cortexdb.ai", actor="user:app", bearer="v4.public...")
33
+ toolkit = CortexDBTools(client=client, scope="user:default")
34
+
35
+ agent = Agent(
36
+ name="Research Assistant",
37
+ tools=[toolkit],
38
+ )
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ client: Cortex,
44
+ scope: str = "user:default",
45
+ ) -> None:
46
+ super().__init__(name="cortexdb")
47
+ self._client = client
48
+ self._scope = scope
49
+ self.register(self.search_memory)
50
+ self.register(self.store_memory)
51
+ self.register(self.forget_memory)
52
+
53
+ def search_memory(self, query: str) -> str:
54
+ """Search CortexDB for relevant memories and past context.
55
+
56
+ Args:
57
+ query: A natural language query describing what to search for.
58
+
59
+ Returns:
60
+ The retrieved context string from CortexDB.
61
+ """
62
+ result = self._client.recall(self._scope, query=query)
63
+
64
+ context = result.get("context_block", "") if result else ""
65
+ if not context:
66
+ return "No relevant memories found in CortexDB."
67
+
68
+ return context
69
+
70
+ def store_memory(self, content: str) -> str:
71
+ """Store information in CortexDB's long-term memory.
72
+
73
+ Args:
74
+ content: The information to store as a memory.
75
+
76
+ Returns:
77
+ A confirmation message.
78
+ """
79
+ self._client.experience(self._scope, text=content)
80
+ return "Memory stored successfully in CortexDB."
81
+
82
+ def forget_memory(self, reason: str) -> str:
83
+ """Forget all memories in this scope from CortexDB.
84
+
85
+ Args:
86
+ reason: The reason for forgetting these memories.
87
+
88
+ Returns:
89
+ A confirmation message.
90
+ """
91
+ self._client.forget(
92
+ self._scope,
93
+ confirm_all=True,
94
+ cascade="redact_events",
95
+ reason=reason,
96
+ )
97
+ return f"Memories in scope '{self._scope}' have been forgotten."
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cortexdb-agno"
7
+ version = "0.1.0"
8
+ description = "Agno integration for CortexDB — long-term memory for AI systems"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "Apache-2.0"
12
+ dependencies = [
13
+ "cortexdbai>=0.1.0",
14
+ "agno>=1.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=7.0",
20
+ "pytest-asyncio>=0.21",
21
+ ]
File without changes
@@ -0,0 +1,150 @@
1
+ """Tests for Agno CortexDB integration.
2
+
3
+ Validates the CortexDBMemory backend and CortexDBTools toolkit against
4
+ the v1 SDK surface, without requiring a live CortexDB or Agno install.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ import unittest
11
+ from unittest.mock import MagicMock, patch
12
+
13
+
14
+ # Build mock modules before importing integration code.
15
+ mock_agno_tools = MagicMock()
16
+
17
+
18
+ class _MockToolkit:
19
+ """Stand-in for agno.tools.Toolkit."""
20
+
21
+ def __init__(self, name="", **kwargs):
22
+ self.name = name
23
+ self.registered = []
24
+
25
+ def register(self, fn):
26
+ self.registered.append(fn)
27
+ return fn
28
+
29
+
30
+ mock_agno_tools.Toolkit = _MockToolkit
31
+
32
+ mock_agno = MagicMock()
33
+ mock_agno.tools = mock_agno_tools
34
+ mock_agno.agent = MagicMock()
35
+
36
+ mock_cortexdb = MagicMock()
37
+
38
+ with patch.dict(sys.modules, {
39
+ "agno": mock_agno,
40
+ "agno.tools": mock_agno_tools,
41
+ "agno.agent": mock_agno.agent,
42
+ "cortexdb": mock_cortexdb,
43
+ }):
44
+ from cortexdb_agno.memory import CortexDBMemory
45
+ from cortexdb_agno.tools import CortexDBTools
46
+
47
+
48
+ class TestCortexDBMemory(unittest.TestCase):
49
+ """Tests for the CortexDBMemory class."""
50
+
51
+ def setUp(self) -> None:
52
+ self.client = MagicMock()
53
+ self.memory = CortexDBMemory(
54
+ client=self.client,
55
+ scope="user:test",
56
+ )
57
+
58
+ def test_init_stores_config(self) -> None:
59
+ self.assertIs(self.memory._client, self.client)
60
+ self.assertEqual(self.memory._scope, "user:test")
61
+
62
+ def test_init_defaults(self) -> None:
63
+ m = CortexDBMemory(client=self.client)
64
+ self.assertEqual(m._scope, "user:default")
65
+
66
+ def test_add_calls_experience(self) -> None:
67
+ self.memory.add("test content")
68
+ self.client.experience.assert_called_once_with(
69
+ "user:test", text="test content",
70
+ )
71
+
72
+ def test_search_calls_recall(self) -> None:
73
+ self.client.recall.return_value = {"context_block": "result"}
74
+ results = self.memory.search("query")
75
+ self.client.recall.assert_called_once_with("user:test", query="query")
76
+ self.assertEqual(results["context"], "result")
77
+
78
+ def test_search_empty(self) -> None:
79
+ self.client.recall.return_value = {"context_block": ""}
80
+ results = self.memory.search("nothing")
81
+ self.assertEqual(results["context"], "")
82
+
83
+ def test_clear_calls_forget(self) -> None:
84
+ self.memory.clear()
85
+ self.client.forget.assert_called_once_with(
86
+ "user:test",
87
+ confirm_all=True,
88
+ cascade="redact_events",
89
+ reason="Agno memory clear requested",
90
+ )
91
+
92
+ def test_get_context_returns_string(self) -> None:
93
+ self.client.recall.return_value = {"context_block": "first second"}
94
+ context = self.memory.get_context("query")
95
+ self.assertIn("first", context)
96
+
97
+ def test_get_context_empty(self) -> None:
98
+ self.client.recall.return_value = {"context_block": ""}
99
+ context = self.memory.get_context("query")
100
+ self.assertEqual(context, "")
101
+
102
+
103
+ class TestCortexDBTools(unittest.TestCase):
104
+ """Tests for the CortexDBTools toolkit."""
105
+
106
+ def setUp(self) -> None:
107
+ self.client = MagicMock()
108
+ self.toolkit = CortexDBTools(
109
+ client=self.client,
110
+ scope="user:test",
111
+ )
112
+
113
+ def test_registers_three_tools(self) -> None:
114
+ self.assertEqual(len(self.toolkit.registered), 3)
115
+
116
+ def test_search_tool_calls_recall(self) -> None:
117
+ self.client.recall.return_value = {"context_block": "found"}
118
+ result = self.toolkit.search_memory("test query")
119
+ self.client.recall.assert_called_once_with("user:test", query="test query")
120
+ self.assertIn("found", result)
121
+
122
+ def test_search_tool_no_results(self) -> None:
123
+ self.client.recall.return_value = {"context_block": ""}
124
+ result = self.toolkit.search_memory("nothing")
125
+ self.assertIn("No relevant memories", result)
126
+
127
+ def test_store_tool_calls_experience(self) -> None:
128
+ result = self.toolkit.store_memory("important info")
129
+ self.client.experience.assert_called_once_with(
130
+ "user:test", text="important info",
131
+ )
132
+ self.assertIn("stored successfully", result)
133
+
134
+ def test_forget_tool_calls_forget(self) -> None:
135
+ result = self.toolkit.forget_memory("cleanup")
136
+ self.client.forget.assert_called_once_with(
137
+ "user:test",
138
+ confirm_all=True,
139
+ cascade="redact_events",
140
+ reason="cleanup",
141
+ )
142
+ self.assertIn("forgotten", result)
143
+
144
+ def test_default_config(self) -> None:
145
+ toolkit = CortexDBTools(client=self.client)
146
+ self.assertEqual(toolkit._scope, "user:default")
147
+
148
+
149
+ if __name__ == "__main__":
150
+ unittest.main()