antonlytics 2.1.0__tar.gz → 2.3.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.
Files changed (24) hide show
  1. {antonlytics-2.1.0 → antonlytics-2.3.0}/PKG-INFO +1 -1
  2. antonlytics-2.3.0/antonlytics/__init__.py +22 -0
  3. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/agent.py +137 -25
  4. antonlytics-2.3.0/antonlytics/client.py +70 -0
  5. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/PKG-INFO +1 -1
  6. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/SOURCES.txt +1 -0
  7. {antonlytics-2.1.0 → antonlytics-2.3.0}/pyproject.toml +1 -1
  8. {antonlytics-2.1.0 → antonlytics-2.3.0}/setup.py +1 -1
  9. antonlytics-2.1.0/antonlytics/__init__.py +0 -11
  10. {antonlytics-2.1.0 → antonlytics-2.3.0}/.gitignore +0 -0
  11. {antonlytics-2.1.0 → antonlytics-2.3.0}/.pyre/pyre.stderr +0 -0
  12. {antonlytics-2.1.0 → antonlytics-2.3.0}/LICENSE +0 -0
  13. {antonlytics-2.1.0 → antonlytics-2.3.0}/MANIFEST.in +0 -0
  14. {antonlytics-2.1.0 → antonlytics-2.3.0}/Makefile +0 -0
  15. {antonlytics-2.1.0 → antonlytics-2.3.0}/README.md +0 -0
  16. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/exceptions.py +0 -0
  17. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/http_client.py +0 -0
  18. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/integrations/__init__.py +0 -0
  19. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/integrations/langchain.py +0 -0
  20. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/dependency_links.txt +0 -0
  21. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/requires.txt +0 -0
  22. {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/top_level.txt +0 -0
  23. {antonlytics-2.1.0 → antonlytics-2.3.0}/examples/langchain_quickstart.py +0 -0
  24. {antonlytics-2.1.0 → antonlytics-2.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: antonlytics
3
- Version: 2.1.0
3
+ Version: 2.3.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
@@ -0,0 +1,22 @@
1
+ """
2
+ Antonlytics Python SDK — Memory + Ontology infrastructure for AI agents
3
+ and SaaS applications.
4
+
5
+ Two entry points:
6
+
7
+ * ``Agent`` — project-scoped: ingest, chat, retrieval, system prompt.
8
+ * ``Antonlytics`` — account-level: project CRUD, plus ``.agent()`` factory.
9
+ """
10
+
11
+ from .agent import Agent
12
+ from .client import Antonlytics
13
+ from .exceptions import AntonlyticsError, APIError, AuthenticationError
14
+
15
+ __version__ = "2.3.0" # bumped: ingest_triplets + Antonlytics client
16
+ __all__ = [
17
+ "Agent",
18
+ "Antonlytics",
19
+ "AntonlyticsError",
20
+ "APIError",
21
+ "AuthenticationError",
22
+ ]
@@ -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
 
@@ -51,6 +51,70 @@ class Agent:
51
51
  self.base_url = base_url.rstrip('/')
52
52
  self.client = HTTPClient(api_key, base_url)
53
53
 
54
+ def ingest_triplets(self, triplets: List[Dict[str, Any]]) -> Dict[str, Any]:
55
+ """
56
+ Ingest pre-formed triplets directly — bypasses Claude extraction.
57
+
58
+ Use this from SaaS applications when your code already knows the
59
+ structured data (rows from a DB, parsed events, etc.) and you don't
60
+ want to pay LLM extraction cost / latency.
61
+
62
+ Each triplet is:
63
+ {
64
+ "subject": {"type": str, "id": str, "properties": dict},
65
+ "predicate": str, # e.g. "PURCHASED"
66
+ "object": {"type": str, "id": str, "properties": dict},
67
+ "relationship_properties": dict (optional)
68
+ }
69
+
70
+ Args:
71
+ triplets: list of triplet dicts.
72
+
73
+ Returns:
74
+ ``{"success": bool, "event_id": str, "async": bool, "results": {...}}``
75
+ Batches over 100 are processed asynchronously; poll
76
+ ``Agent.ingestion_event_status(event_id)``.
77
+ """
78
+ if not triplets:
79
+ raise AntonlyticsError("triplets must be a non-empty list")
80
+ return self.client.post('/api/v1/ingest/', {
81
+ 'project_id': self.project_id,
82
+ 'triplets': triplets,
83
+ })
84
+
85
+ def upsert_entity(
86
+ self,
87
+ type: str,
88
+ external_id: str,
89
+ properties: Optional[Dict[str, Any]] = None,
90
+ ) -> Dict[str, Any]:
91
+ """Convenience: create or update a single entity (no relationship).
92
+
93
+ Internally posts a self-referential triplet which the engine treats as
94
+ an entity upsert — same code path as direct ingestion."""
95
+ return self.ingest_triplets([{
96
+ 'subject': {'type': type, 'id': external_id, 'properties': properties or {}},
97
+ 'predicate': 'SELF',
98
+ 'object': {'type': type, 'id': external_id, 'properties': properties or {}},
99
+ }])
100
+
101
+ def add_relationship(
102
+ self,
103
+ source: Dict[str, Any],
104
+ predicate: str,
105
+ target: Dict[str, Any],
106
+ relationship_properties: Optional[Dict[str, Any]] = None,
107
+ ) -> Dict[str, Any]:
108
+ """Convenience: post a single (source --[predicate]--> target) edge.
109
+
110
+ Both endpoints are upserted if they don't already exist."""
111
+ return self.ingest_triplets([{
112
+ 'subject': source,
113
+ 'predicate': predicate,
114
+ 'object': target,
115
+ 'relationship_properties': relationship_properties or {},
116
+ }])
117
+
54
118
  def ingest(self, text: str) -> Dict[str, Any]:
55
119
  """
56
120
  Ingest text and extract entities/relationships.
@@ -121,41 +185,89 @@ class Agent:
121
185
 
122
186
  return self.client.post('/api/v1/memory/chat/', payload)
123
187
 
124
- def get_memory(self, query: Optional[str] = None) -> Dict[str, Any]:
188
+ def get_memory(
189
+ self,
190
+ query: Optional[str] = None,
191
+ *,
192
+ max_entities: int = 10_000,
193
+ page_size: int = 500,
194
+ ) -> Dict[str, Any]:
125
195
  """
126
196
  Get memory context for your own agent/model.
127
- Returns structured knowledge graph data.
128
-
197
+
198
+ Two modes:
199
+ - ``query`` provided: semantic top-K retrieval (single shot, server-ranked).
200
+ Returns the most relevant entities and their relationships.
201
+ - ``query=None``: enumerates the full project graph via cursor pagination,
202
+ auto-iterating pages internally. Stops at ``max_entities`` for safety.
203
+
129
204
  Args:
130
- query: Optional natural language query to filter context
131
-
205
+ query: Optional natural language query for ranked retrieval.
206
+ max_entities: Safety ceiling for full-graph mode (default 10000).
207
+ page_size: Pagination page size for full-graph mode (1-1000, default 500).
208
+
132
209
  Returns:
133
- Dict with entities and relationships
134
-
210
+ Dict with entities and relationships.
211
+
135
212
  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
- ... )
213
+ >>> # Top-K retrieval for prompt context
214
+ >>> memory = agent.get_memory(query="What did Sarah say?")
215
+ >>>
216
+ >>> # Full project dump (auto-paginated)
217
+ >>> all_memory = agent.get_memory()
218
+ >>>
219
+ >>> # For very large projects, stream pages:
220
+ >>> for page in agent.iter_memory(page_size=500):
221
+ ... process(page)
145
222
  """
146
223
  if query:
147
224
  response = self.client.post('/api/v1/memory/query/', {
148
225
  '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
226
+ 'project_id': self.project_id,
157
227
  })
158
228
  return response.get('graph_context', {})
229
+
230
+ # Full-graph mode: auto-paginate via /memory/list/.
231
+ entities: List[Dict[str, Any]] = []
232
+ relationships: List[Dict[str, Any]] = []
233
+ for page in self.iter_memory(page_size=page_size):
234
+ entities.extend(page.get('entities', []))
235
+ relationships.extend(page.get('relationships', []))
236
+ if len(entities) >= max_entities:
237
+ entities = entities[:max_entities]
238
+ break
239
+ return {'entities': entities, 'relationships': relationships}
240
+
241
+ def iter_memory(self, page_size: int = 500) -> Iterator[Dict[str, Any]]:
242
+ """
243
+ Stream the full project graph one page at a time.
244
+
245
+ Yields a dict ``{entities, relationships, next_cursor, has_more}`` per page.
246
+ Iteration stops automatically when the server reports no more pages.
247
+
248
+ Args:
249
+ page_size: Rows per page (1-1000, default 500).
250
+
251
+ Example:
252
+ >>> for page in agent.iter_memory(page_size=200):
253
+ ... for entity in page["entities"]:
254
+ ... print(entity["name"])
255
+ """
256
+ cursor: Optional[str] = None
257
+ while True:
258
+ payload: Dict[str, Any] = {
259
+ 'project_id': self.project_id,
260
+ 'limit': page_size,
261
+ }
262
+ if cursor:
263
+ payload['cursor'] = cursor
264
+ page = self.client.post('/api/v1/memory/list/', payload)
265
+ yield page
266
+ if not page.get('has_more'):
267
+ break
268
+ cursor = page.get('next_cursor')
269
+ if not cursor:
270
+ break
159
271
 
160
272
  def set_system_prompt(self, prompt: str) -> Dict[str, Any]:
161
273
  """
@@ -0,0 +1,70 @@
1
+ """
2
+ Account-level client. For SaaS apps that manage many projects with one API key.
3
+
4
+ from antonlytics import Antonlytics
5
+
6
+ client = Antonlytics(api_key="...")
7
+
8
+ # Create a project on the API key's team
9
+ project = client.create_project(name="customer_42", scope="Customer 42 dataset")
10
+
11
+ # Per-project work — same Agent surface as before
12
+ agent = client.agent(project_id=project["id"])
13
+ agent.ingest_triplets([...])
14
+
15
+ The Agent class is unchanged — Antonlytics is just a thin factory + project
16
+ management wrapper that reuses the same HTTPClient.
17
+ """
18
+ from typing import Dict, Any, List, Optional
19
+
20
+ from .http_client import HTTPClient
21
+ from .agent import Agent
22
+ from .exceptions import AntonlyticsError
23
+
24
+
25
+ class Antonlytics:
26
+ """Account-level entry point. Use this for project CRUD; use ``.agent()``
27
+ to get a project-scoped Agent for ingestion / chat / retrieval."""
28
+
29
+ def __init__(self, api_key: str, base_url: str = "https://api.antonlytics.com"):
30
+ if not api_key:
31
+ raise AntonlyticsError("API key is required")
32
+ self.api_key = api_key
33
+ self.base_url = base_url.rstrip("/")
34
+ self.client = HTTPClient(api_key, self.base_url)
35
+
36
+ # ── Project CRUD ─────────────────────────────────────────────────────
37
+
38
+ def list_projects(self) -> List[Dict[str, Any]]:
39
+ """Return all projects this API key has access to."""
40
+ r = self.client.get("/api/v1/graph/projects/")
41
+ return r.get("results", r) if isinstance(r, dict) else r
42
+
43
+ def create_project(self, name: str, scope: Optional[str] = None,
44
+ description: Optional[str] = None) -> Dict[str, Any]:
45
+ """Create a new project on the team this API key belongs to."""
46
+ if not name or not name.strip():
47
+ raise AntonlyticsError("Project name is required")
48
+ payload: Dict[str, Any] = {"name": name.strip()}
49
+ if scope: payload["scope"] = scope
50
+ if description: payload["description"] = description
51
+ return self.client.post("/api/v1/graph/projects/", payload)
52
+
53
+ def get_project(self, project_id: str) -> Dict[str, Any]:
54
+ return self.client.get(f"/api/v1/graph/projects/{project_id}/")
55
+
56
+ def delete_project(self, project_id: str) -> Dict[str, Any]:
57
+ return self.client.delete(f"/api/v1/graph/projects/{project_id}/")
58
+
59
+ def project_stats(self, project_id: str) -> Dict[str, Any]:
60
+ return self.client.get(f"/api/v1/graph/projects/{project_id}/stats/")
61
+
62
+ def project_ontology(self, project_id: str) -> Dict[str, Any]:
63
+ return self.client.get(f"/api/v1/graph/projects/{project_id}/ontology/")
64
+
65
+ # ── Agent factory ────────────────────────────────────────────────────
66
+
67
+ def agent(self, project_id: str) -> Agent:
68
+ """Return an Agent scoped to ``project_id``. Reuses this client's
69
+ API key + base URL — no extra config needed."""
70
+ return Agent(api_key=self.api_key, project_id=project_id, base_url=self.base_url)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: antonlytics
3
- Version: 2.1.0
3
+ Version: 2.3.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
@@ -8,6 +8,7 @@ setup.py
8
8
  .pyre/pyre.stderr
9
9
  antonlytics/__init__.py
10
10
  antonlytics/agent.py
11
+ antonlytics/client.py
11
12
  antonlytics/exceptions.py
12
13
  antonlytics/http_client.py
13
14
  antonlytics.egg-info/PKG-INFO
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "antonlytics"
7
- version = "2.1.0"
7
+ version = "2.3.0"
8
8
  description = "Memory for AI Agents - Simple natural language SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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.1.0",
12
+ version="2.3.0",
13
13
  author="Voidback",
14
14
  author_email="hello@voidback.com",
15
15
  description="Memory for AI Agents - Simple natural language SDK",
@@ -1,11 +0,0 @@
1
- """
2
- Antonlytics Python SDK - Memory for AI Agents
3
-
4
- Simple SDK for giving your AI agent persistent memory.
5
- """
6
-
7
- from .agent import Agent
8
- from .exceptions import AntonlyticsError, APIError, AuthenticationError
9
-
10
- __version__ = "2.1.0"
11
- __all__ = ["Agent", "AntonlyticsError", "APIError", "AuthenticationError"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes