antonlytics 2.0.0__tar.gz → 2.2.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,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ *.egg
6
+ build/
7
+ dist/
8
+ .eggs/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .coverage
12
+ .coverage.*
13
+ htmlcov/
14
+ .tox/
15
+ .venv/
16
+ venv/
17
+ env/
18
+ .env
19
+ .DS_Store
@@ -0,0 +1,37 @@
1
+ # Antonlytics Python SDK — release helpers.
2
+ #
3
+ # Quick reference:
4
+ # make build build wheel + sdist into dist/
5
+ # make publish upload dist/ to PyPI (uses uv publish)
6
+ # make release build + publish in one shot
7
+ # make clean wipe dist/ build/ egg-info/
8
+ #
9
+ # Auth: set UV_PUBLISH_TOKEN to your PyPI API token (starts with `pypi-`).
10
+ # export UV_PUBLISH_TOKEN=pypi-AgEIcHlw...
11
+ # Or pass it inline:
12
+ # make publish UV_PUBLISH_TOKEN=pypi-...
13
+ # Get a token at: https://pypi.org/manage/account/token/
14
+
15
+ .PHONY: build publish release clean version
16
+
17
+ clean:
18
+ rm -rf dist build *.egg-info
19
+
20
+ build: clean
21
+ uv build
22
+
23
+ publish:
24
+ @if [ -z "$$UV_PUBLISH_TOKEN" ]; then \
25
+ echo "error: UV_PUBLISH_TOKEN is not set."; \
26
+ echo " get a token at https://pypi.org/manage/account/token/"; \
27
+ echo " then: export UV_PUBLISH_TOKEN=pypi-..."; \
28
+ exit 1; \
29
+ fi
30
+ uv publish
31
+
32
+ release: build publish
33
+ @VERSION=$$(grep -E '^version' pyproject.toml | head -1 | cut -d'"' -f2); \
34
+ echo "✓ released antonlytics $$VERSION to PyPI"
35
+
36
+ version:
37
+ @grep -E '^version' pyproject.toml | head -1 | cut -d'"' -f2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: antonlytics
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Memory for AI Agents - Simple natural language SDK
5
5
  Home-page: https://github.com/Voidback-Inc/antonlytics-python-sdk
6
6
  Author: Voidback
@@ -31,6 +31,9 @@ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
31
  Requires-Dist: black>=23.0.0; extra == "dev"
32
32
  Requires-Dist: flake8>=6.0.0; extra == "dev"
33
33
  Requires-Dist: mypy>=1.0.0; extra == "dev"
34
+ Provides-Extra: langchain
35
+ Requires-Dist: langchain-core>=0.2.0; extra == "langchain"
36
+ Requires-Dist: pydantic>=2.0.0; extra == "langchain"
34
37
  Dynamic: author
35
38
  Dynamic: home-page
36
39
  Dynamic: license-file
@@ -7,5 +7,5 @@ Simple SDK for giving your AI agent persistent memory.
7
7
  from .agent import Agent
8
8
  from .exceptions import AntonlyticsError, APIError, AuthenticationError
9
9
 
10
- __version__ = "2.0.0"
10
+ __version__ = "2.2.0"
11
11
  __all__ = ["Agent", "AntonlyticsError", "APIError", "AuthenticationError"]
@@ -2,7 +2,7 @@
2
2
  Agent class for interacting with Antonlytics API.
3
3
  """
4
4
 
5
- from typing import Dict, Any, Optional, List
5
+ from typing import Dict, Any, Optional, List, Iterator
6
6
  from .http_client import HTTPClient
7
7
  from .exceptions import AntonlyticsError
8
8
 
@@ -121,41 +121,89 @@ class Agent:
121
121
 
122
122
  return self.client.post('/api/v1/memory/chat/', payload)
123
123
 
124
- def get_memory(self, query: Optional[str] = None) -> Dict[str, Any]:
124
+ def get_memory(
125
+ self,
126
+ query: Optional[str] = None,
127
+ *,
128
+ max_entities: int = 10_000,
129
+ page_size: int = 500,
130
+ ) -> Dict[str, Any]:
125
131
  """
126
132
  Get memory context for your own agent/model.
127
- Returns structured knowledge graph data.
128
-
133
+
134
+ Two modes:
135
+ - ``query`` provided: semantic top-K retrieval (single shot, server-ranked).
136
+ Returns the most relevant entities and their relationships.
137
+ - ``query=None``: enumerates the full project graph via cursor pagination,
138
+ auto-iterating pages internally. Stops at ``max_entities`` for safety.
139
+
129
140
  Args:
130
- query: Optional natural language query to filter context
131
-
141
+ query: Optional natural language query for ranked retrieval.
142
+ max_entities: Safety ceiling for full-graph mode (default 10000).
143
+ page_size: Pagination page size for full-graph mode (1-1000, default 500).
144
+
132
145
  Returns:
133
- Dict with entities and relationships
134
-
146
+ Dict with entities and relationships.
147
+
135
148
  Example:
136
- >>> # Get all memory
137
- >>> memory = agent.get_memory()
138
- >>>
139
- >>> # Use with your own model
140
- >>> your_model.chat(
141
- ... system="You are a sales assistant",
142
- ... context=memory,
143
- ... message="Who to follow up?"
144
- ... )
149
+ >>> # Top-K retrieval for prompt context
150
+ >>> memory = agent.get_memory(query="What did Sarah say?")
151
+ >>>
152
+ >>> # Full project dump (auto-paginated)
153
+ >>> all_memory = agent.get_memory()
154
+ >>>
155
+ >>> # For very large projects, stream pages:
156
+ >>> for page in agent.iter_memory(page_size=500):
157
+ ... process(page)
145
158
  """
146
159
  if query:
147
160
  response = self.client.post('/api/v1/memory/query/', {
148
161
  'question': query,
149
- 'project_id': self.project_id
150
- })
151
- return response.get('graph_context', {})
152
- else:
153
- # Get all memory
154
- response = self.client.post('/api/v1/memory/query/', {
155
- 'question': 'What do you know?',
156
- 'project_id': self.project_id
162
+ 'project_id': self.project_id,
157
163
  })
158
164
  return response.get('graph_context', {})
165
+
166
+ # Full-graph mode: auto-paginate via /memory/list/.
167
+ entities: List[Dict[str, Any]] = []
168
+ relationships: List[Dict[str, Any]] = []
169
+ for page in self.iter_memory(page_size=page_size):
170
+ entities.extend(page.get('entities', []))
171
+ relationships.extend(page.get('relationships', []))
172
+ if len(entities) >= max_entities:
173
+ entities = entities[:max_entities]
174
+ break
175
+ return {'entities': entities, 'relationships': relationships}
176
+
177
+ def iter_memory(self, page_size: int = 500) -> Iterator[Dict[str, Any]]:
178
+ """
179
+ Stream the full project graph one page at a time.
180
+
181
+ Yields a dict ``{entities, relationships, next_cursor, has_more}`` per page.
182
+ Iteration stops automatically when the server reports no more pages.
183
+
184
+ Args:
185
+ page_size: Rows per page (1-1000, default 500).
186
+
187
+ Example:
188
+ >>> for page in agent.iter_memory(page_size=200):
189
+ ... for entity in page["entities"]:
190
+ ... print(entity["name"])
191
+ """
192
+ cursor: Optional[str] = None
193
+ while True:
194
+ payload: Dict[str, Any] = {
195
+ 'project_id': self.project_id,
196
+ 'limit': page_size,
197
+ }
198
+ if cursor:
199
+ payload['cursor'] = cursor
200
+ page = self.client.post('/api/v1/memory/list/', payload)
201
+ yield page
202
+ if not page.get('has_more'):
203
+ break
204
+ cursor = page.get('next_cursor')
205
+ if not cursor:
206
+ break
159
207
 
160
208
  def set_system_prompt(self, prompt: str) -> Dict[str, Any]:
161
209
  """
@@ -0,0 +1 @@
1
+ """Optional framework adapters. Import directly: `from antonlytics.integrations.langchain import AntonlyticsMemory`."""
@@ -0,0 +1,124 @@
1
+ """
2
+ LangChain memory adapter.
3
+
4
+ Usage:
5
+ from antonlytics import Agent
6
+ from antonlytics.integrations.langchain import AntonlyticsMemory
7
+
8
+ agent = Agent(api_key="...", project_id="...")
9
+ memory = AntonlyticsMemory(agent=agent)
10
+
11
+ # Drop into any LangChain chain that accepts a memory.
12
+
13
+ Install the LangChain extras first:
14
+ pip install antonlytics[langchain]
15
+ """
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ try:
19
+ from langchain_core.memory import BaseMemory
20
+ from pydantic import Field
21
+ except ImportError as exc: # pragma: no cover
22
+ raise ImportError(
23
+ "AntonlyticsMemory requires langchain-core. Install with: "
24
+ "pip install antonlytics[langchain]"
25
+ ) from exc
26
+
27
+ from ..agent import Agent
28
+
29
+
30
+ def _format_graph(graph: Dict[str, Any]) -> str:
31
+ entities = graph.get("entities") or []
32
+ rels = graph.get("relationships") or []
33
+ if not entities and not rels:
34
+ return ""
35
+
36
+ lines: List[str] = []
37
+ if entities:
38
+ lines.append("Known facts:")
39
+ for e in entities[:50]:
40
+ props = e.get("properties") or {}
41
+ name = props.get("name") or e.get("name") or e.get("external_id") or ""
42
+ extras = ", ".join(f"{k}: {v}" for k, v in props.items() if k != "name")
43
+ lines.append(f"- [{e.get('type', '?')}] {name}" + (f" — {extras}" if extras else ""))
44
+
45
+ if rels:
46
+ # Build id → name map from entities for readable relationships.
47
+ id_to_name = {e.get("id"): (e.get("properties") or {}).get("name", "") for e in entities}
48
+ lines.append("Relationships:")
49
+ for r in rels[:50]:
50
+ src = id_to_name.get(r.get("source_id"), r.get("source_id", "?"))
51
+ tgt = id_to_name.get(r.get("target_id"), r.get("target_id", "?"))
52
+ lines.append(f"- {src} --[{r.get('type', '?')}]--> {tgt}")
53
+
54
+ return "\n".join(lines)
55
+
56
+
57
+ class AntonlyticsMemory(BaseMemory):
58
+ """LangChain BaseMemory backed by an Antonlytics project.
59
+
60
+ `load_memory_variables` fetches a question-scoped slice of the knowledge
61
+ graph and formats it into a string suitable for a prompt.
62
+ `save_context` ingests the new turn so the next call sees it.
63
+ """
64
+
65
+ agent: Agent
66
+ memory_key: str = Field(default="history")
67
+ input_key: Optional[str] = Field(default="input")
68
+ output_key: Optional[str] = Field(default=None)
69
+ return_messages: bool = Field(default=False)
70
+
71
+ class Config:
72
+ arbitrary_types_allowed = True
73
+
74
+ @property
75
+ def memory_variables(self) -> List[str]:
76
+ return [self.memory_key]
77
+
78
+ def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
79
+ query = ""
80
+ if self.input_key and self.input_key in inputs:
81
+ query = str(inputs[self.input_key])
82
+ elif inputs:
83
+ # Fall back to the first stringy value.
84
+ for v in inputs.values():
85
+ if isinstance(v, str):
86
+ query = v
87
+ break
88
+ try:
89
+ graph = self.agent.get_memory(query=query or None)
90
+ except Exception:
91
+ graph = {}
92
+ return {self.memory_key: _format_graph(graph)}
93
+
94
+ def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, Any]) -> None:
95
+ user_msg = ""
96
+ if self.input_key and self.input_key in inputs:
97
+ user_msg = str(inputs[self.input_key])
98
+ ai_msg = ""
99
+ if self.output_key and self.output_key in outputs:
100
+ ai_msg = str(outputs[self.output_key])
101
+ elif outputs:
102
+ for v in outputs.values():
103
+ if isinstance(v, str):
104
+ ai_msg = v
105
+ break
106
+
107
+ turn = ""
108
+ if user_msg:
109
+ turn += f"User: {user_msg}\n"
110
+ if ai_msg:
111
+ turn += f"Assistant: {ai_msg}"
112
+ turn = turn.strip()
113
+ if not turn:
114
+ return
115
+
116
+ try:
117
+ self.agent.ingest(turn)
118
+ except Exception:
119
+ # Never break the chain just because ingest hiccuped.
120
+ pass
121
+
122
+ def clear(self) -> None:
123
+ # Project-level wipe is not exposed by the API yet — intentional no-op.
124
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: antonlytics
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Memory for AI Agents - Simple natural language SDK
5
5
  Home-page: https://github.com/Voidback-Inc/antonlytics-python-sdk
6
6
  Author: Voidback
@@ -31,6 +31,9 @@ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
31
  Requires-Dist: black>=23.0.0; extra == "dev"
32
32
  Requires-Dist: flake8>=6.0.0; extra == "dev"
33
33
  Requires-Dist: mypy>=1.0.0; extra == "dev"
34
+ Provides-Extra: langchain
35
+ Requires-Dist: langchain-core>=0.2.0; extra == "langchain"
36
+ Requires-Dist: pydantic>=2.0.0; extra == "langchain"
34
37
  Dynamic: author
35
38
  Dynamic: home-page
36
39
  Dynamic: license-file
@@ -1,6 +1,7 @@
1
- .DS_Store
1
+ .gitignore
2
2
  LICENSE
3
3
  MANIFEST.in
4
+ Makefile
4
5
  README.md
5
6
  pyproject.toml
6
7
  setup.py
@@ -13,4 +14,7 @@ antonlytics.egg-info/PKG-INFO
13
14
  antonlytics.egg-info/SOURCES.txt
14
15
  antonlytics.egg-info/dependency_links.txt
15
16
  antonlytics.egg-info/requires.txt
16
- antonlytics.egg-info/top_level.txt
17
+ antonlytics.egg-info/top_level.txt
18
+ antonlytics/integrations/__init__.py
19
+ antonlytics/integrations/langchain.py
20
+ examples/langchain_quickstart.py
@@ -6,3 +6,7 @@ pytest-cov>=4.0.0
6
6
  black>=23.0.0
7
7
  flake8>=6.0.0
8
8
  mypy>=1.0.0
9
+
10
+ [langchain]
11
+ langchain-core>=0.2.0
12
+ pydantic>=2.0.0
@@ -0,0 +1,51 @@
1
+ """
2
+ LangChain + Antonlytics quickstart.
3
+
4
+ Install:
5
+ pip install antonlytics[langchain] langchain langchain-anthropic
6
+
7
+ Run:
8
+ export ANTONLYTICS_API_KEY=...
9
+ export ANTONLYTICS_PROJECT_ID=...
10
+ export ANTHROPIC_API_KEY=...
11
+ python examples/langchain_quickstart.py
12
+ """
13
+ import os
14
+
15
+ from antonlytics import Agent
16
+ from antonlytics.integrations.langchain import AntonlyticsMemory
17
+
18
+ from langchain_anthropic import ChatAnthropic
19
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
20
+ from langchain.chains import ConversationChain
21
+
22
+
23
+ def main() -> None:
24
+ agent = Agent(
25
+ api_key=os.environ["ANTONLYTICS_API_KEY"],
26
+ project_id=os.environ["ANTONLYTICS_PROJECT_ID"],
27
+ )
28
+
29
+ memory = AntonlyticsMemory(agent=agent, memory_key="history", input_key="input")
30
+
31
+ llm = ChatAnthropic(model="claude-sonnet-4-5", temperature=0)
32
+
33
+ prompt = ChatPromptTemplate.from_messages([
34
+ ("system",
35
+ "You are a helpful assistant. Use the prior knowledge below when relevant.\n\n"
36
+ "{history}"),
37
+ ("human", "{input}"),
38
+ ])
39
+
40
+ chain = ConversationChain(llm=llm, prompt=prompt, memory=memory, input_key="input")
41
+
42
+ # Teach it something.
43
+ chain.invoke({"input": "I had a sales call with Sarah from TechCorp about Enterprise pricing."})
44
+
45
+ # Ask it back with no keyword overlap. Hybrid retrieval should still pull Sarah/TechCorp.
46
+ out = chain.invoke({"input": "Anyone I should circle back with on a deal?"})
47
+ print(out["response"])
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "antonlytics"
7
- version = "2.0.0"
7
+ version = "2.2.0"
8
8
  description = "Memory for AI Agents - Simple natural language SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -37,6 +37,10 @@ dev = [
37
37
  "flake8>=6.0.0",
38
38
  "mypy>=1.0.0",
39
39
  ]
40
+ langchain = [
41
+ "langchain-core>=0.2.0",
42
+ "pydantic>=2.0.0",
43
+ ]
40
44
 
41
45
  [project.urls]
42
46
  Homepage = "https://antonlytics.com"
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
9
9
 
10
10
  setup(
11
11
  name="antonlytics",
12
- version="2.0.0",
12
+ version="2.2.0",
13
13
  author="Voidback",
14
14
  author_email="hello@voidback.com",
15
15
  description="Memory for AI Agents - Simple natural language SDK",
@@ -48,6 +48,10 @@ setup(
48
48
  "flake8>=6.0.0",
49
49
  "mypy>=1.0.0",
50
50
  ],
51
+ "langchain": [
52
+ "langchain-core>=0.2.0",
53
+ "pydantic>=2.0.0",
54
+ ],
51
55
  },
52
56
  keywords="ai agent memory llm knowledge-graph natural-language",
53
57
  )
Binary file
File without changes
File without changes
File without changes
File without changes