construct-labs-crm-env 0.1.1__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,24 @@
1
+
2
+ # React
3
+ node_modules/
4
+ dist/
5
+ build/
6
+ .next/
7
+ out/
8
+ .cache/
9
+ .eslintcache
10
+ npm-debug.log*
11
+ yarn-debug.log*
12
+ yarn-error.log*
13
+ pnpm-debug.log*
14
+ .npm-cache/
15
+
16
+ # Python
17
+ .venv/
18
+ __pycache__/
19
+
20
+ # macOS
21
+ .DS_Store
22
+
23
+ launch.json
24
+ .env
@@ -0,0 +1,42 @@
1
+ Construct Labs CRM Environment SDK - Proprietary License
2
+
3
+ Copyright (c) 2024 Construct Labs GmbH. All rights reserved.
4
+
5
+ TERMS AND CONDITIONS
6
+
7
+ 1. GRANT OF LICENSE
8
+ This software and associated documentation files (the "Software") are
9
+ proprietary to Construct Labs GmbH. Use of this Software requires a valid
10
+ commercial agreement with Construct Labs GmbH.
11
+
12
+ 2. RESTRICTIONS
13
+ Without a valid commercial agreement, you may NOT:
14
+ - Use the Software for any purpose
15
+ - Copy, modify, or distribute the Software
16
+ - Reverse engineer, decompile, or disassemble the Software
17
+ - Sublicense, sell, or transfer the Software to third parties
18
+
19
+ 3. COMMERCIAL LICENSE
20
+ To obtain a commercial license, contact:
21
+
22
+ Construct Labs GmbH
23
+ Email: hello@construct-labs.com
24
+ Website: https://construct-labs.com
25
+
26
+ 4. WARRANTY DISCLAIMER
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
30
+
31
+ 5. LIMITATION OF LIABILITY
32
+ IN NO EVENT SHALL CONSTRUCT LABS GMBH BE LIABLE FOR ANY CLAIM, DAMAGES OR
33
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
35
+ DEALINGS IN THE SOFTWARE.
36
+
37
+ 6. TERMINATION
38
+ This license is effective until terminated. Your rights under this license
39
+ will terminate automatically without notice if you fail to comply with any
40
+ of its terms or if your commercial agreement expires or is terminated.
41
+
42
+ For licensing inquiries: hello@construct-labs.com
@@ -0,0 +1,412 @@
1
+ Metadata-Version: 2.4
2
+ Name: construct-labs-crm-env
3
+ Version: 0.1.1
4
+ Summary: CRM Agent Environment SDK by Construct Labs - Train RL agents to interact with CRM systems
5
+ Project-URL: Homepage, https://construct-labs.com
6
+ Author-email: Construct Labs GmbH <hello@construct-labs.com>
7
+ License: Proprietary
8
+ License-File: LICENSE
9
+ Keywords: agent,ai,crm,environment,machine-learning,reinforcement-learning
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: Other/Proprietary License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Requires-Dist: websockets>=12.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Construct Labs CRM Agent Environment
32
+
33
+ Python SDK for the Construct Labs CRM Agent Environment - a reinforcement learning environment for training AI agents to interact with CRM systems.
34
+
35
+ ## License
36
+
37
+ This software requires a commercial license from Construct Labs GmbH.
38
+ Contact hello@construct-labs.com for licensing inquiries.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install construct-labs-crm-env
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```python
49
+ from construct_labs_crm_env import CrmAgentEnv, CrmAgentAction, CRMActionType
50
+
51
+ # Connect to the CRM environment
52
+ with CrmAgentEnv(
53
+ base_url="https://api.construct-labs.com",
54
+ api_key="your-api-key" # Issued by Construct Labs
55
+ ) as env:
56
+ # Reset the environment
57
+ result = env.reset()
58
+
59
+ # List companies
60
+ result = env.step(CrmAgentAction(
61
+ action_type=CRMActionType.LIST_COMPANIES,
62
+ limit=10
63
+ ))
64
+ print(result.observation.data)
65
+ ```
66
+
67
+ ## Environment Variables
68
+
69
+ You can set your API key via environment variable:
70
+
71
+ ```bash
72
+ export CRM_AGENT_API_KEY=your-api-key
73
+ ```
74
+
75
+ ```python
76
+ # API key is read from environment
77
+ env = CrmAgentEnv(base_url="https://api.construct-labs.com")
78
+ ```
79
+
80
+ ## LLM Integration Example
81
+
82
+ The SDK is designed to work with LLM-based agents. Here's how to parse LLM tool calls:
83
+
84
+ ```python
85
+ from construct_labs_crm_env import CrmAgentEnv
86
+
87
+ with CrmAgentEnv(
88
+ base_url="https://api.construct-labs.com",
89
+ api_key="your-api-key"
90
+ ) as env:
91
+ result = env.reset()
92
+
93
+ # Simulate an LLM generating a tool call
94
+ llm_tool_call = {
95
+ "name": "list_companies",
96
+ "arguments": {"limit": 5}
97
+ }
98
+
99
+ # Parse the tool call into a CrmAgentAction
100
+ parsed = env.parse_tool_call(llm_tool_call)
101
+
102
+ if parsed.is_valid:
103
+ result = env.step(parsed.action)
104
+ print(result.observation.model_dump_json(indent=2))
105
+ else:
106
+ print(f"Invalid tool call: {parsed.error_message}")
107
+ ```
108
+
109
+ ## Customization
110
+
111
+ Subclass `CrmAgentEnv` to customize agent behavior:
112
+
113
+ ### Custom System Prompt
114
+
115
+ ```python
116
+ class SalesAgent(CrmAgentEnv):
117
+ @property
118
+ def system_prompt(self) -> str:
119
+ return """You are a sales assistant AI.
120
+
121
+ Your goal is to help close deals by:
122
+ 1. Finding relevant companies and contacts
123
+ 2. Creating opportunities with accurate values
124
+ 3. Adding follow-up tasks
125
+
126
+ Be concise. Focus on high-value opportunities."""
127
+ ```
128
+
129
+ ### Restricted Tool Set
130
+
131
+ ```python
132
+ class ReadOnlyAgent(CrmAgentEnv):
133
+ """Agent that can only read data, not modify."""
134
+
135
+ @property
136
+ def tools(self) -> list[dict]:
137
+ read_only = {'list_companies', 'get_company', 'list_people',
138
+ 'get_person', 'list_opportunities', 'submit_answer'}
139
+ return [t for t in self._default_tools()
140
+ if t['function']['name'] in read_only]
141
+ ```
142
+
143
+ ### Custom Observation Formatting
144
+
145
+ ```python
146
+ class VerboseAgent(CrmAgentEnv):
147
+ def format_observation(self, observation):
148
+ base = super().format_observation(observation)
149
+ return f"=== CRM Response ===\n{base}\n=== End ==="
150
+ ```
151
+
152
+ ## Available Actions
153
+
154
+ ### Company Operations
155
+ - `LIST_COMPANIES` - List all companies
156
+ - `GET_COMPANY` - Get a specific company by ID
157
+ - `CREATE_COMPANY` - Create a new company
158
+ - `UPDATE_COMPANY` - Update an existing company
159
+ - `DELETE_COMPANY` - Delete a company
160
+
161
+ ### Contact Operations
162
+ - `LIST_PEOPLE` - List all contacts
163
+ - `GET_PERSON` - Get a specific contact by ID
164
+ - `CREATE_PERSON` - Create a new contact
165
+ - `UPDATE_PERSON` - Update an existing contact
166
+ - `DELETE_PERSON` - Delete a contact
167
+
168
+ ### Opportunity Operations
169
+ - `LIST_OPPORTUNITIES` - List all opportunities
170
+ - `GET_OPPORTUNITY` - Get a specific opportunity by ID
171
+ - `CREATE_OPPORTUNITY` - Create a new opportunity
172
+ - `UPDATE_OPPORTUNITY` - Update an existing opportunity
173
+ - `DELETE_OPPORTUNITY` - Delete an opportunity
174
+
175
+ ### Note Operations
176
+ - `LIST_NOTES` - List all notes
177
+ - `CREATE_NOTE` - Create a note attached to a record
178
+
179
+ ### Task Operations
180
+ - `LIST_TASKS` - List all tasks
181
+ - `CREATE_TASK` - Create a new task
182
+ - `UPDATE_TASK` - Update an existing task
183
+ - `COMPLETE_TASK` - Mark a task as complete
184
+
185
+ ### Submit Answer
186
+ - `SUBMIT_ANSWER` - Submit the final answer and end the session
187
+
188
+ ## Integration with Training Frameworks
189
+
190
+ ### Collecting Rollouts for RL Training
191
+
192
+ The SDK is designed for reinforcement learning. Here's how to collect rollouts with rewards:
193
+
194
+ ```python
195
+ from dataclasses import dataclass, field
196
+ from construct_labs_crm_env import CrmAgentEnv, CrmAgentObservation
197
+
198
+ @dataclass
199
+ class Rollout:
200
+ """A single episode rollout for training."""
201
+ observations: list[CrmAgentObservation] = field(default_factory=list)
202
+ actions: list[dict] = field(default_factory=list) # Raw tool calls
203
+ rewards: list[float] = field(default_factory=list)
204
+ done: bool = False
205
+ total_reward: float = 0.0
206
+
207
+ def collect_rollout(env: CrmAgentEnv, agent, seed: int | None = None) -> Rollout:
208
+ """Collect a single rollout from the environment."""
209
+ rollout = Rollout()
210
+
211
+ # Reset environment
212
+ result = env.reset(seed=seed)
213
+ rollout.observations.append(result.observation)
214
+
215
+ while not result.done:
216
+ # Get action from agent (returns tool call dict)
217
+ tool_call = agent.get_action(
218
+ system_prompt=env.system_prompt,
219
+ tools=env.tools,
220
+ observation=result.observation,
221
+ )
222
+
223
+ # Parse and execute
224
+ parsed = env.parse_tool_call(tool_call)
225
+
226
+ if parsed.is_valid:
227
+ result = env.step(parsed.action)
228
+ reward = result.reward if result.reward is not None else 0.0
229
+ else:
230
+ # Invalid action penalty
231
+ reward = -1.0
232
+ result.done = True
233
+
234
+ # Store transition
235
+ rollout.actions.append(tool_call)
236
+ rollout.rewards.append(reward)
237
+ rollout.observations.append(result.observation)
238
+
239
+ rollout.done = True
240
+ rollout.total_reward = sum(rollout.rewards)
241
+ return rollout
242
+
243
+ # Collect multiple rollouts for training
244
+ def collect_rollouts(
245
+ env: CrmAgentEnv,
246
+ agent,
247
+ num_rollouts: int,
248
+ seed_offset: int = 0,
249
+ ) -> list[Rollout]:
250
+ """Collect multiple rollouts for batch training."""
251
+ rollouts = []
252
+ for i in range(num_rollouts):
253
+ rollout = collect_rollout(env, agent, seed=seed_offset + i)
254
+ rollouts.append(rollout)
255
+ return rollouts
256
+
257
+ # Example usage
258
+ with CrmAgentEnv(
259
+ base_url="https://api.construct-labs.com",
260
+ api_key="your-api-key"
261
+ ) as env:
262
+ # Collect 10 rollouts
263
+ rollouts = collect_rollouts(env, your_agent, num_rollouts=10)
264
+
265
+ # Compute statistics
266
+ avg_reward = sum(r.total_reward for r in rollouts) / len(rollouts)
267
+ avg_length = sum(len(r.actions) for r in rollouts) / len(rollouts)
268
+
269
+ print(f"Average reward: {avg_reward:.2f}")
270
+ print(f"Average episode length: {avg_length:.1f}")
271
+ ```
272
+
273
+ ### GRPO Training Integration
274
+
275
+ For Group Relative Policy Optimization (GRPO) training:
276
+
277
+ ```python
278
+ from construct_labs_crm_env import CrmAgentEnv
279
+
280
+ def collect_grpo_group(
281
+ env: CrmAgentEnv,
282
+ agent,
283
+ group_size: int = 8,
284
+ seed: int = 0,
285
+ ) -> list[Rollout]:
286
+ """Collect a group of rollouts with the same seed for GRPO."""
287
+ group = []
288
+ for _ in range(group_size):
289
+ # Same seed = same initial state, different agent samples
290
+ rollout = collect_rollout(env, agent, seed=seed)
291
+ group.append(rollout)
292
+ return group
293
+
294
+ def compute_grpo_advantages(group: list[Rollout]) -> list[float]:
295
+ """Compute relative advantages within a group."""
296
+ rewards = [r.total_reward for r in group]
297
+ mean_reward = sum(rewards) / len(rewards)
298
+ std_reward = (sum((r - mean_reward) ** 2 for r in rewards) / len(rewards)) ** 0.5
299
+
300
+ if std_reward < 1e-8:
301
+ return [0.0] * len(rewards)
302
+
303
+ return [(r - mean_reward) / std_reward for r in rewards]
304
+
305
+ # Training loop
306
+ with CrmAgentEnv(
307
+ base_url="https://api.construct-labs.com",
308
+ api_key="your-api-key"
309
+ ) as env:
310
+ for step in range(num_training_steps):
311
+ # Collect group of rollouts
312
+ group = collect_grpo_group(env, agent, group_size=8, seed=step)
313
+
314
+ # Compute advantages
315
+ advantages = compute_grpo_advantages(group)
316
+
317
+ # Update policy using advantages
318
+ agent.update(group, advantages)
319
+ ```
320
+
321
+ ### Basic Training Loop
322
+
323
+ ```python
324
+ from construct_labs_crm_env import CrmAgentEnv
325
+
326
+ env = CrmAgentEnv(
327
+ base_url="https://api.construct-labs.com",
328
+ api_key="your-api-key"
329
+ )
330
+
331
+ with env:
332
+ result = env.reset(seed=42)
333
+
334
+ while not result.done:
335
+ # Get action from your agent/LLM
336
+ tool_call = your_agent.get_action(
337
+ env.system_prompt,
338
+ env.tools,
339
+ result.observation
340
+ )
341
+
342
+ # Parse and execute
343
+ parsed = env.parse_tool_call(tool_call)
344
+ if parsed.is_valid:
345
+ result = env.step(parsed.action)
346
+ else:
347
+ # Handle invalid action
348
+ print(f"Invalid action: {parsed.error_message}")
349
+ break
350
+ ```
351
+
352
+ ## API Reference
353
+
354
+ ### CrmAgentEnv
355
+
356
+ Main client class for interacting with the CRM environment.
357
+
358
+ **Constructor:**
359
+ - `base_url` (str): Base URL of the CRM environment server
360
+ - `api_key` (str, optional): API key for authentication
361
+ - `connect_timeout_s` (float): Connection timeout in seconds (default: 10)
362
+ - `message_timeout_s` (float): Message timeout in seconds (default: 60)
363
+
364
+ **Methods:**
365
+ - `reset(seed=None)` - Reset the environment
366
+ - `step(action)` - Execute an action
367
+ - `state()` - Get current environment state
368
+ - `close()` - Close the connection
369
+ - `parse_tool_call(tool_call)` - Parse LLM tool call to action
370
+ - `format_observation(observation)` - Format observation for LLM
371
+
372
+ **Properties (overridable):**
373
+ - `system_prompt` - System prompt for the agent
374
+ - `tools` - Available tool definitions
375
+
376
+ ### CrmAgentAction
377
+
378
+ Pydantic model for CRM actions.
379
+
380
+ ```python
381
+ action = CrmAgentAction(
382
+ action_type=CRMActionType.CREATE_COMPANY,
383
+ company_name="Acme Corp",
384
+ company_domain="acme.com",
385
+ company_employees=100
386
+ )
387
+ ```
388
+
389
+ ### CrmAgentObservation
390
+
391
+ Pydantic model for CRM observations.
392
+
393
+ - `success` (bool): Whether the action succeeded
394
+ - `error` (str | None): Error message if failed
395
+ - `data` (dict): Raw response data
396
+ - `done` (bool): Whether episode has ended
397
+ - `reward` (float | None): Reward signal
398
+
399
+ Use `observation.model_dump_json()` to get JSON representation.
400
+
401
+ ## Support
402
+
403
+ For licensing, technical support, or questions:
404
+
405
+ **Email:** hello@construct-labs.com
406
+
407
+ ## License
408
+
409
+ Copyright (c) 2024 Construct Labs GmbH. All rights reserved.
410
+
411
+ This software is proprietary and requires a commercial license.
412
+ See [LICENSE](LICENSE) for details.