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.
- {antonlytics-2.1.0 → antonlytics-2.3.0}/PKG-INFO +1 -1
- antonlytics-2.3.0/antonlytics/__init__.py +22 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/agent.py +137 -25
- antonlytics-2.3.0/antonlytics/client.py +70 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/PKG-INFO +1 -1
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/SOURCES.txt +1 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/pyproject.toml +1 -1
- {antonlytics-2.1.0 → antonlytics-2.3.0}/setup.py +1 -1
- antonlytics-2.1.0/antonlytics/__init__.py +0 -11
- {antonlytics-2.1.0 → antonlytics-2.3.0}/.gitignore +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/.pyre/pyre.stderr +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/LICENSE +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/MANIFEST.in +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/Makefile +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/README.md +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/exceptions.py +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/http_client.py +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/integrations/__init__.py +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics/integrations/langchain.py +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/dependency_links.txt +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/requires.txt +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/antonlytics.egg-info/top_level.txt +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/examples/langchain_quickstart.py +0 -0
- {antonlytics-2.1.0 → antonlytics-2.3.0}/setup.cfg +0 -0
|
@@ -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(
|
|
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
|
-
|
|
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
|
|
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
|
-
>>> #
|
|
137
|
-
>>> memory = agent.get_memory()
|
|
138
|
-
>>>
|
|
139
|
-
>>> #
|
|
140
|
-
>>>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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,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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|