crewlayer 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.
- crewlayer-0.1.0/PKG-INFO +232 -0
- crewlayer-0.1.0/README.md +187 -0
- crewlayer-0.1.0/crewlayer/__init__.py +54 -0
- crewlayer-0.1.0/crewlayer/_actions.py +153 -0
- crewlayer-0.1.0/crewlayer/_client.py +87 -0
- crewlayer-0.1.0/crewlayer/_context.py +107 -0
- crewlayer-0.1.0/crewlayer/_exceptions.py +71 -0
- crewlayer-0.1.0/crewlayer/_http.py +101 -0
- crewlayer-0.1.0/crewlayer/_memory.py +192 -0
- crewlayer-0.1.0/crewlayer/_types.py +242 -0
- crewlayer-0.1.0/crewlayer/integrations/__init__.py +6 -0
- crewlayer-0.1.0/crewlayer/integrations/autogen.py +538 -0
- crewlayer-0.1.0/crewlayer/integrations/crewai.py +215 -0
- crewlayer-0.1.0/crewlayer/integrations/langchain.py +404 -0
- crewlayer-0.1.0/crewlayer/integrations/llamaindex.py +527 -0
- crewlayer-0.1.0/crewlayer/py.typed +0 -0
- crewlayer-0.1.0/pyproject.toml +87 -0
crewlayer-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: crewlayer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Open source memory & context backend for AI agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/GerardSole/CrewLayer
|
|
6
|
+
Project-URL: Repository, https://github.com/GerardSole/CrewLayer
|
|
7
|
+
Project-URL: Documentation, https://github.com/GerardSole/CrewLayer/blob/main/sdk/README.md
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/GerardSole/CrewLayer/issues
|
|
9
|
+
Author-email: Gerard Solé <gesoca2003@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: agents,ai,autogen,context,crewai,crewlayer,langchain,llamaindex,llm,memory
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Requires-Dist: httpx<1.0,>=0.27.0
|
|
24
|
+
Provides-Extra: all-integrations
|
|
25
|
+
Requires-Dist: crewai>=0.28.0; extra == 'all-integrations'
|
|
26
|
+
Requires-Dist: langchain-core<1.0,>=0.2.0; extra == 'all-integrations'
|
|
27
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'all-integrations'
|
|
28
|
+
Requires-Dist: pyautogen>=0.2.0; extra == 'all-integrations'
|
|
29
|
+
Provides-Extra: autogen
|
|
30
|
+
Requires-Dist: pyautogen>=0.2.0; extra == 'autogen'
|
|
31
|
+
Provides-Extra: build
|
|
32
|
+
Requires-Dist: build>=1.2; extra == 'build'
|
|
33
|
+
Requires-Dist: twine>=6.0; extra == 'build'
|
|
34
|
+
Provides-Extra: crewai
|
|
35
|
+
Requires-Dist: crewai>=0.28.0; extra == 'crewai'
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: httpx<1.0,>=0.27.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
40
|
+
Provides-Extra: langchain
|
|
41
|
+
Requires-Dist: langchain-core<1.0,>=0.2.0; extra == 'langchain'
|
|
42
|
+
Provides-Extra: llamaindex
|
|
43
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'llamaindex'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# CrewLayer Python SDK
|
|
47
|
+
|
|
48
|
+
Open source memory & context backend for AI agents.
|
|
49
|
+
Persistent memory, action logging, and shared blackboard — all in one REST API.
|
|
50
|
+
|
|
51
|
+
[](https://pypi.org/project/crewlayer/)
|
|
52
|
+
[](https://pypi.org/project/crewlayer/)
|
|
53
|
+
[](https://github.com/GerardSole/CrewLayer/blob/main/LICENSE)
|
|
54
|
+
|
|
55
|
+
**Source & docs:** [github.com/GerardSole/CrewLayer](https://github.com/GerardSole/CrewLayer)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install crewlayer
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Requires Python 3.12+. Runtime dependency: `httpx` only.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Quick start
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from crewlayer import CrewLayerClient
|
|
73
|
+
|
|
74
|
+
client = CrewLayerClient(api_key="crwl_...", base_url="http://localhost:8000")
|
|
75
|
+
|
|
76
|
+
# Persist a message to short-term memory
|
|
77
|
+
client.memory.append(agent_id="agent-uuid", role="user", content="I prefer dark mode")
|
|
78
|
+
|
|
79
|
+
# Semantic recall from long-term memory
|
|
80
|
+
results = client.memory.recall(agent_id="agent-uuid", query="UI preferences", limit=5)
|
|
81
|
+
for item in results.results:
|
|
82
|
+
print(f"[{item.similarity:.2f}] {item.content}")
|
|
83
|
+
|
|
84
|
+
# Log an action (full audit trail)
|
|
85
|
+
client.actions.log(agent_id="agent-uuid", tool_name="web_search",
|
|
86
|
+
input_params={"q": "crewlayer"}, status="success", duration_ms=120)
|
|
87
|
+
|
|
88
|
+
# Shared blackboard between agents
|
|
89
|
+
client.context.write(namespace="project-42", key="phase", value={"stage": "planning"})
|
|
90
|
+
entry = client.context.read("project-42", "phase")
|
|
91
|
+
print(entry.value) # {"stage": "planning"}
|
|
92
|
+
|
|
93
|
+
client.close()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Async client
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
from crewlayer import CrewLayerAsyncClient
|
|
103
|
+
|
|
104
|
+
async def main():
|
|
105
|
+
async with CrewLayerAsyncClient(api_key="crwl_...") as client:
|
|
106
|
+
await client.memory.append(agent_id="agent-uuid", role="user", content="Hello")
|
|
107
|
+
result = await client.memory.recall(agent_id="agent-uuid", query="greeting")
|
|
108
|
+
print(result.results)
|
|
109
|
+
|
|
110
|
+
asyncio.run(main())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Integrations
|
|
116
|
+
|
|
117
|
+
Optional extras bring first-class support for popular AI frameworks.
|
|
118
|
+
Each integration falls back gracefully when the framework is not installed.
|
|
119
|
+
|
|
120
|
+
| Extra | Install | What you get |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `langchain` | `pip install crewlayer[langchain]` | `AgentLayerMemory`, `AgentLayerVectorStore`, `AgentLayerCallbackHandler` |
|
|
123
|
+
| `crewai` | `pip install crewlayer[crewai]` | `AgentLayerMemoryProvider`, `AgentLayerTaskLogger` |
|
|
124
|
+
| `llamaindex` | `pip install crewlayer[llamaindex]` | `CrewLayerMemoryBuffer`, `CrewLayerVectorIndex`, `CrewLayerQueryEngine`, `CrewLayerCallbackManager` |
|
|
125
|
+
| `autogen` | `pip install crewlayer[autogen]` | `CrewLayerConversableAgent`, `CrewLayerGroupChatManager`, `CrewLayerAgentMemory`, `sync_agent_status` |
|
|
126
|
+
| `all-integrations` | `pip install crewlayer[all-integrations]` | All of the above |
|
|
127
|
+
|
|
128
|
+
### LangChain
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from crewlayer import CrewLayerClient
|
|
132
|
+
from crewlayer.integrations.langchain import AgentLayerMemory
|
|
133
|
+
from langchain.chains import ConversationChain
|
|
134
|
+
from langchain_openai import ChatOpenAI
|
|
135
|
+
|
|
136
|
+
client = CrewLayerClient(api_key="crwl_...")
|
|
137
|
+
memory = AgentLayerMemory(client=client, agent_id="agent-uuid", session_id="user-123")
|
|
138
|
+
chain = ConversationChain(llm=ChatOpenAI(), memory=memory)
|
|
139
|
+
chain.predict(input="What's my name?")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### CrewAI
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from crewlayer.integrations.crewai import AgentLayerMemoryProvider, AgentLayerTaskLogger
|
|
146
|
+
from crewai.memory import LongTermMemory
|
|
147
|
+
from crewai import Task
|
|
148
|
+
|
|
149
|
+
storage = AgentLayerMemoryProvider(client=client, agent_id="agent-uuid")
|
|
150
|
+
ltm = LongTermMemory(storage=storage)
|
|
151
|
+
|
|
152
|
+
logger = AgentLayerTaskLogger(client=client, agent_id="agent-uuid")
|
|
153
|
+
task = Task(description="Summarize feedback", expected_output="...", agent=agent, callback=logger)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### LlamaIndex
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from crewlayer.integrations.llamaindex import CrewLayerVectorIndex
|
|
160
|
+
from llama_index.core.schema import Document
|
|
161
|
+
|
|
162
|
+
index = CrewLayerVectorIndex(client=client, agent_id="agent-uuid", similarity_top_k=4)
|
|
163
|
+
index.insert(Document(text="User prefers dark mode"))
|
|
164
|
+
engine = index.as_query_engine()
|
|
165
|
+
response = engine.query("UI preferences")
|
|
166
|
+
print(response.response)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### AutoGen (multi-agent blackboard)
|
|
170
|
+
|
|
171
|
+
The killer feature: `CrewLayerGroupChatManager` writes every turn to a shared blackboard.
|
|
172
|
+
Any agent — or external observer — can read live group state without being in the chat.
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from crewlayer.integrations.autogen import (
|
|
176
|
+
CrewLayerConversableAgent, CrewLayerGroupChatManager, CrewLayerAgentMemory,
|
|
177
|
+
)
|
|
178
|
+
import autogen
|
|
179
|
+
|
|
180
|
+
client = CrewLayerClient(api_key="crwl_...")
|
|
181
|
+
researcher = CrewLayerConversableAgent(name="researcher", client=client, agent_id="uuid-r",
|
|
182
|
+
llm_config={"config_list": [...]})
|
|
183
|
+
writer = CrewLayerConversableAgent(name="writer", client=client, agent_id="uuid-w",
|
|
184
|
+
llm_config={"config_list": [...]})
|
|
185
|
+
|
|
186
|
+
groupchat = autogen.GroupChat(agents=[researcher, writer], messages=[], max_round=10)
|
|
187
|
+
manager = CrewLayerGroupChatManager(client=client, group_id="project-alpha", groupchat=groupchat)
|
|
188
|
+
CrewLayerAgentMemory(client=client, agent_id="uuid-r").apply(researcher)
|
|
189
|
+
|
|
190
|
+
researcher.initiate_chat(manager, message="Let's plan the release.")
|
|
191
|
+
|
|
192
|
+
# From anywhere — see who spoke last
|
|
193
|
+
latest = client.context.read("project-alpha", "latest_turn")
|
|
194
|
+
print(latest.value) # {"agent": "writer", "content": "...", "turn": 3}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Error handling
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from crewlayer import CrewLayerError, AuthError, NotFoundError, ConflictError, RateLimitError
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
client.memory.recall(agent_id="bad-id", query="test")
|
|
206
|
+
except AuthError:
|
|
207
|
+
print("Invalid API key")
|
|
208
|
+
except NotFoundError:
|
|
209
|
+
print("Agent not found")
|
|
210
|
+
except ConflictError as e:
|
|
211
|
+
print(f"Version conflict: {e}")
|
|
212
|
+
except RateLimitError:
|
|
213
|
+
print("Rate limited")
|
|
214
|
+
except CrewLayerError as e:
|
|
215
|
+
print(f"HTTP {e.status_code}: {e}")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
All exceptions expose `.status_code` (int | None) and `.response` (dict | None).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Self-hosting
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
git clone https://github.com/GerardSole/CrewLayer
|
|
226
|
+
cd CrewLayer
|
|
227
|
+
docker compose up -d # starts PostgreSQL + Redis
|
|
228
|
+
alembic upgrade head
|
|
229
|
+
uvicorn main:app --reload # API at http://localhost:8000
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Full documentation: [github.com/GerardSole/CrewLayer](https://github.com/GerardSole/CrewLayer)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# CrewLayer Python SDK
|
|
2
|
+
|
|
3
|
+
Open source memory & context backend for AI agents.
|
|
4
|
+
Persistent memory, action logging, and shared blackboard — all in one REST API.
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/crewlayer/)
|
|
7
|
+
[](https://pypi.org/project/crewlayer/)
|
|
8
|
+
[](https://github.com/GerardSole/CrewLayer/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
**Source & docs:** [github.com/GerardSole/CrewLayer](https://github.com/GerardSole/CrewLayer)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install crewlayer
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires Python 3.12+. Runtime dependency: `httpx` only.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from crewlayer import CrewLayerClient
|
|
28
|
+
|
|
29
|
+
client = CrewLayerClient(api_key="crwl_...", base_url="http://localhost:8000")
|
|
30
|
+
|
|
31
|
+
# Persist a message to short-term memory
|
|
32
|
+
client.memory.append(agent_id="agent-uuid", role="user", content="I prefer dark mode")
|
|
33
|
+
|
|
34
|
+
# Semantic recall from long-term memory
|
|
35
|
+
results = client.memory.recall(agent_id="agent-uuid", query="UI preferences", limit=5)
|
|
36
|
+
for item in results.results:
|
|
37
|
+
print(f"[{item.similarity:.2f}] {item.content}")
|
|
38
|
+
|
|
39
|
+
# Log an action (full audit trail)
|
|
40
|
+
client.actions.log(agent_id="agent-uuid", tool_name="web_search",
|
|
41
|
+
input_params={"q": "crewlayer"}, status="success", duration_ms=120)
|
|
42
|
+
|
|
43
|
+
# Shared blackboard between agents
|
|
44
|
+
client.context.write(namespace="project-42", key="phase", value={"stage": "planning"})
|
|
45
|
+
entry = client.context.read("project-42", "phase")
|
|
46
|
+
print(entry.value) # {"stage": "planning"}
|
|
47
|
+
|
|
48
|
+
client.close()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Async client
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from crewlayer import CrewLayerAsyncClient
|
|
58
|
+
|
|
59
|
+
async def main():
|
|
60
|
+
async with CrewLayerAsyncClient(api_key="crwl_...") as client:
|
|
61
|
+
await client.memory.append(agent_id="agent-uuid", role="user", content="Hello")
|
|
62
|
+
result = await client.memory.recall(agent_id="agent-uuid", query="greeting")
|
|
63
|
+
print(result.results)
|
|
64
|
+
|
|
65
|
+
asyncio.run(main())
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Integrations
|
|
71
|
+
|
|
72
|
+
Optional extras bring first-class support for popular AI frameworks.
|
|
73
|
+
Each integration falls back gracefully when the framework is not installed.
|
|
74
|
+
|
|
75
|
+
| Extra | Install | What you get |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `langchain` | `pip install crewlayer[langchain]` | `AgentLayerMemory`, `AgentLayerVectorStore`, `AgentLayerCallbackHandler` |
|
|
78
|
+
| `crewai` | `pip install crewlayer[crewai]` | `AgentLayerMemoryProvider`, `AgentLayerTaskLogger` |
|
|
79
|
+
| `llamaindex` | `pip install crewlayer[llamaindex]` | `CrewLayerMemoryBuffer`, `CrewLayerVectorIndex`, `CrewLayerQueryEngine`, `CrewLayerCallbackManager` |
|
|
80
|
+
| `autogen` | `pip install crewlayer[autogen]` | `CrewLayerConversableAgent`, `CrewLayerGroupChatManager`, `CrewLayerAgentMemory`, `sync_agent_status` |
|
|
81
|
+
| `all-integrations` | `pip install crewlayer[all-integrations]` | All of the above |
|
|
82
|
+
|
|
83
|
+
### LangChain
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from crewlayer import CrewLayerClient
|
|
87
|
+
from crewlayer.integrations.langchain import AgentLayerMemory
|
|
88
|
+
from langchain.chains import ConversationChain
|
|
89
|
+
from langchain_openai import ChatOpenAI
|
|
90
|
+
|
|
91
|
+
client = CrewLayerClient(api_key="crwl_...")
|
|
92
|
+
memory = AgentLayerMemory(client=client, agent_id="agent-uuid", session_id="user-123")
|
|
93
|
+
chain = ConversationChain(llm=ChatOpenAI(), memory=memory)
|
|
94
|
+
chain.predict(input="What's my name?")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### CrewAI
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from crewlayer.integrations.crewai import AgentLayerMemoryProvider, AgentLayerTaskLogger
|
|
101
|
+
from crewai.memory import LongTermMemory
|
|
102
|
+
from crewai import Task
|
|
103
|
+
|
|
104
|
+
storage = AgentLayerMemoryProvider(client=client, agent_id="agent-uuid")
|
|
105
|
+
ltm = LongTermMemory(storage=storage)
|
|
106
|
+
|
|
107
|
+
logger = AgentLayerTaskLogger(client=client, agent_id="agent-uuid")
|
|
108
|
+
task = Task(description="Summarize feedback", expected_output="...", agent=agent, callback=logger)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### LlamaIndex
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from crewlayer.integrations.llamaindex import CrewLayerVectorIndex
|
|
115
|
+
from llama_index.core.schema import Document
|
|
116
|
+
|
|
117
|
+
index = CrewLayerVectorIndex(client=client, agent_id="agent-uuid", similarity_top_k=4)
|
|
118
|
+
index.insert(Document(text="User prefers dark mode"))
|
|
119
|
+
engine = index.as_query_engine()
|
|
120
|
+
response = engine.query("UI preferences")
|
|
121
|
+
print(response.response)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### AutoGen (multi-agent blackboard)
|
|
125
|
+
|
|
126
|
+
The killer feature: `CrewLayerGroupChatManager` writes every turn to a shared blackboard.
|
|
127
|
+
Any agent — or external observer — can read live group state without being in the chat.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from crewlayer.integrations.autogen import (
|
|
131
|
+
CrewLayerConversableAgent, CrewLayerGroupChatManager, CrewLayerAgentMemory,
|
|
132
|
+
)
|
|
133
|
+
import autogen
|
|
134
|
+
|
|
135
|
+
client = CrewLayerClient(api_key="crwl_...")
|
|
136
|
+
researcher = CrewLayerConversableAgent(name="researcher", client=client, agent_id="uuid-r",
|
|
137
|
+
llm_config={"config_list": [...]})
|
|
138
|
+
writer = CrewLayerConversableAgent(name="writer", client=client, agent_id="uuid-w",
|
|
139
|
+
llm_config={"config_list": [...]})
|
|
140
|
+
|
|
141
|
+
groupchat = autogen.GroupChat(agents=[researcher, writer], messages=[], max_round=10)
|
|
142
|
+
manager = CrewLayerGroupChatManager(client=client, group_id="project-alpha", groupchat=groupchat)
|
|
143
|
+
CrewLayerAgentMemory(client=client, agent_id="uuid-r").apply(researcher)
|
|
144
|
+
|
|
145
|
+
researcher.initiate_chat(manager, message="Let's plan the release.")
|
|
146
|
+
|
|
147
|
+
# From anywhere — see who spoke last
|
|
148
|
+
latest = client.context.read("project-alpha", "latest_turn")
|
|
149
|
+
print(latest.value) # {"agent": "writer", "content": "...", "turn": 3}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Error handling
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from crewlayer import CrewLayerError, AuthError, NotFoundError, ConflictError, RateLimitError
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
client.memory.recall(agent_id="bad-id", query="test")
|
|
161
|
+
except AuthError:
|
|
162
|
+
print("Invalid API key")
|
|
163
|
+
except NotFoundError:
|
|
164
|
+
print("Agent not found")
|
|
165
|
+
except ConflictError as e:
|
|
166
|
+
print(f"Version conflict: {e}")
|
|
167
|
+
except RateLimitError:
|
|
168
|
+
print("Rate limited")
|
|
169
|
+
except CrewLayerError as e:
|
|
170
|
+
print(f"HTTP {e.status_code}: {e}")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
All exceptions expose `.status_code` (int | None) and `.response` (dict | None).
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Self-hosting
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
git clone https://github.com/GerardSole/CrewLayer
|
|
181
|
+
cd CrewLayer
|
|
182
|
+
docker compose up -d # starts PostgreSQL + Redis
|
|
183
|
+
alembic upgrade head
|
|
184
|
+
uvicorn main:app --reload # API at http://localhost:8000
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Full documentation: [github.com/GerardSole/CrewLayer](https://github.com/GerardSole/CrewLayer)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""CrewLayer SDK — official Python client for the CrewLayer AI agent backend."""
|
|
2
|
+
from crewlayer._client import CrewLayerAsyncClient, CrewLayerClient
|
|
3
|
+
from crewlayer._exceptions import (
|
|
4
|
+
AuthError,
|
|
5
|
+
ConflictError,
|
|
6
|
+
CrewLayerError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
RateLimitError,
|
|
9
|
+
ServerError,
|
|
10
|
+
)
|
|
11
|
+
from crewlayer._types import (
|
|
12
|
+
ActionPage,
|
|
13
|
+
ActionRecord,
|
|
14
|
+
ActionStats,
|
|
15
|
+
ContextEntry,
|
|
16
|
+
ContextNamespace,
|
|
17
|
+
ExtractResult,
|
|
18
|
+
MemoryItem,
|
|
19
|
+
MemoryPage,
|
|
20
|
+
Message,
|
|
21
|
+
RecallResult,
|
|
22
|
+
ShortMemory,
|
|
23
|
+
ToolStat,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Clients
|
|
28
|
+
"CrewLayerClient",
|
|
29
|
+
"CrewLayerAsyncClient",
|
|
30
|
+
# Exceptions
|
|
31
|
+
"CrewLayerError",
|
|
32
|
+
"AuthError",
|
|
33
|
+
"NotFoundError",
|
|
34
|
+
"ConflictError",
|
|
35
|
+
"RateLimitError",
|
|
36
|
+
"ServerError",
|
|
37
|
+
# Types — memory
|
|
38
|
+
"Message",
|
|
39
|
+
"ShortMemory",
|
|
40
|
+
"MemoryItem",
|
|
41
|
+
"RecallResult",
|
|
42
|
+
"ExtractResult",
|
|
43
|
+
"MemoryPage",
|
|
44
|
+
# Types — actions
|
|
45
|
+
"ActionRecord",
|
|
46
|
+
"ActionPage",
|
|
47
|
+
"ToolStat",
|
|
48
|
+
"ActionStats",
|
|
49
|
+
# Types — context
|
|
50
|
+
"ContextEntry",
|
|
51
|
+
"ContextNamespace",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Actions resource clients — sync and async."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from crewlayer._http import AsyncTransport, SyncTransport
|
|
7
|
+
from crewlayer._types import ActionPage, ActionRecord, ActionStats
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ActionsClient:
|
|
11
|
+
"""Synchronous action log operations."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, http: SyncTransport) -> None:
|
|
14
|
+
self._http = http
|
|
15
|
+
|
|
16
|
+
def log(
|
|
17
|
+
self,
|
|
18
|
+
agent_id: str,
|
|
19
|
+
tool_name: str,
|
|
20
|
+
input_params: dict[str, Any],
|
|
21
|
+
output_result: dict[str, Any],
|
|
22
|
+
status: str,
|
|
23
|
+
*,
|
|
24
|
+
session_id: str | None = None,
|
|
25
|
+
duration_ms: int | None = None,
|
|
26
|
+
error_msg: str | None = None,
|
|
27
|
+
metadata: dict[str, Any] | None = None,
|
|
28
|
+
) -> ActionRecord:
|
|
29
|
+
"""Record an immutable action entry for the agent."""
|
|
30
|
+
data = self._http.request(
|
|
31
|
+
"POST",
|
|
32
|
+
f"/v1/agents/{agent_id}/actions",
|
|
33
|
+
json={
|
|
34
|
+
"tool_name": tool_name,
|
|
35
|
+
"input_params": input_params,
|
|
36
|
+
"output_result": output_result,
|
|
37
|
+
"status": status,
|
|
38
|
+
"session_id": session_id,
|
|
39
|
+
"duration_ms": duration_ms,
|
|
40
|
+
"error_msg": error_msg,
|
|
41
|
+
"metadata": metadata or {},
|
|
42
|
+
},
|
|
43
|
+
)
|
|
44
|
+
return ActionRecord._from(data)
|
|
45
|
+
|
|
46
|
+
def get(self, agent_id: str, action_id: str) -> ActionRecord:
|
|
47
|
+
"""Retrieve a single action by ID."""
|
|
48
|
+
data = self._http.request("GET", f"/v1/agents/{agent_id}/actions/{action_id}")
|
|
49
|
+
return ActionRecord._from(data)
|
|
50
|
+
|
|
51
|
+
def list(
|
|
52
|
+
self,
|
|
53
|
+
agent_id: str,
|
|
54
|
+
*,
|
|
55
|
+
tool: str | None = None,
|
|
56
|
+
status: str | None = None,
|
|
57
|
+
since: str | None = None,
|
|
58
|
+
until: str | None = None,
|
|
59
|
+
limit: int = 50,
|
|
60
|
+
cursor: str | None = None,
|
|
61
|
+
) -> ActionPage:
|
|
62
|
+
"""List actions with optional filters. Paginated via cursor."""
|
|
63
|
+
params: dict[str, Any] = {"limit": limit}
|
|
64
|
+
if tool is not None:
|
|
65
|
+
params["tool"] = tool
|
|
66
|
+
if status is not None:
|
|
67
|
+
params["status"] = status
|
|
68
|
+
if since is not None:
|
|
69
|
+
params["since"] = since
|
|
70
|
+
if until is not None:
|
|
71
|
+
params["until"] = until
|
|
72
|
+
if cursor is not None:
|
|
73
|
+
params["cursor"] = cursor
|
|
74
|
+
data = self._http.request("GET", f"/v1/agents/{agent_id}/actions", params=params)
|
|
75
|
+
return ActionPage._from(data)
|
|
76
|
+
|
|
77
|
+
def stats(self, agent_id: str) -> ActionStats:
|
|
78
|
+
"""Aggregate statistics: totals, error rate, average duration, per-tool breakdown."""
|
|
79
|
+
data = self._http.request("GET", f"/v1/agents/{agent_id}/actions/stats")
|
|
80
|
+
return ActionStats._from(data)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AsyncActionsClient:
|
|
84
|
+
"""Asynchronous action log operations."""
|
|
85
|
+
|
|
86
|
+
def __init__(self, http: AsyncTransport) -> None:
|
|
87
|
+
self._http = http
|
|
88
|
+
|
|
89
|
+
async def log(
|
|
90
|
+
self,
|
|
91
|
+
agent_id: str,
|
|
92
|
+
tool_name: str,
|
|
93
|
+
input_params: dict[str, Any],
|
|
94
|
+
output_result: dict[str, Any],
|
|
95
|
+
status: str,
|
|
96
|
+
*,
|
|
97
|
+
session_id: str | None = None,
|
|
98
|
+
duration_ms: int | None = None,
|
|
99
|
+
error_msg: str | None = None,
|
|
100
|
+
metadata: dict[str, Any] | None = None,
|
|
101
|
+
) -> ActionRecord:
|
|
102
|
+
"""Record an immutable action entry for the agent."""
|
|
103
|
+
data = await self._http.request(
|
|
104
|
+
"POST",
|
|
105
|
+
f"/v1/agents/{agent_id}/actions",
|
|
106
|
+
json={
|
|
107
|
+
"tool_name": tool_name,
|
|
108
|
+
"input_params": input_params,
|
|
109
|
+
"output_result": output_result,
|
|
110
|
+
"status": status,
|
|
111
|
+
"session_id": session_id,
|
|
112
|
+
"duration_ms": duration_ms,
|
|
113
|
+
"error_msg": error_msg,
|
|
114
|
+
"metadata": metadata or {},
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
return ActionRecord._from(data)
|
|
118
|
+
|
|
119
|
+
async def get(self, agent_id: str, action_id: str) -> ActionRecord:
|
|
120
|
+
"""Retrieve a single action by ID."""
|
|
121
|
+
data = await self._http.request("GET", f"/v1/agents/{agent_id}/actions/{action_id}")
|
|
122
|
+
return ActionRecord._from(data)
|
|
123
|
+
|
|
124
|
+
async def list(
|
|
125
|
+
self,
|
|
126
|
+
agent_id: str,
|
|
127
|
+
*,
|
|
128
|
+
tool: str | None = None,
|
|
129
|
+
status: str | None = None,
|
|
130
|
+
since: str | None = None,
|
|
131
|
+
until: str | None = None,
|
|
132
|
+
limit: int = 50,
|
|
133
|
+
cursor: str | None = None,
|
|
134
|
+
) -> ActionPage:
|
|
135
|
+
"""List actions with optional filters. Paginated via cursor."""
|
|
136
|
+
params: dict[str, Any] = {"limit": limit}
|
|
137
|
+
if tool is not None:
|
|
138
|
+
params["tool"] = tool
|
|
139
|
+
if status is not None:
|
|
140
|
+
params["status"] = status
|
|
141
|
+
if since is not None:
|
|
142
|
+
params["since"] = since
|
|
143
|
+
if until is not None:
|
|
144
|
+
params["until"] = until
|
|
145
|
+
if cursor is not None:
|
|
146
|
+
params["cursor"] = cursor
|
|
147
|
+
data = await self._http.request("GET", f"/v1/agents/{agent_id}/actions", params=params)
|
|
148
|
+
return ActionPage._from(data)
|
|
149
|
+
|
|
150
|
+
async def stats(self, agent_id: str) -> ActionStats:
|
|
151
|
+
"""Aggregate statistics: totals, error rate, average duration, per-tool breakdown."""
|
|
152
|
+
data = await self._http.request("GET", f"/v1/agents/{agent_id}/actions/stats")
|
|
153
|
+
return ActionStats._from(data)
|