construct-labs-crm-env 0.1.2__py3-none-any.whl → 0.1.5__py3-none-any.whl
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.
- construct_labs_crm_env/__init__.py +7 -2
- construct_labs_crm_env/client.py +125 -569
- construct_labs_crm_env/tools.py +697 -0
- {construct_labs_crm_env-0.1.2.dist-info → construct_labs_crm_env-0.1.5.dist-info}/METADATA +1 -1
- construct_labs_crm_env-0.1.5.dist-info/RECORD +10 -0
- construct_labs_crm_env-0.1.5.dist-info/licenses/LICENSE +56 -0
- construct_labs_crm_env-0.1.2.dist-info/RECORD +0 -9
- construct_labs_crm_env-0.1.2.dist-info/licenses/LICENSE +0 -42
- {construct_labs_crm_env-0.1.2.dist-info → construct_labs_crm_env-0.1.5.dist-info}/WHEEL +0 -0
construct_labs_crm_env/client.py
CHANGED
|
@@ -35,6 +35,7 @@ from .models import (
|
|
|
35
35
|
CrmAgentState,
|
|
36
36
|
)
|
|
37
37
|
from .protocol import ParsedAction
|
|
38
|
+
from .tools import DEFAULT_TOOLS
|
|
38
39
|
|
|
39
40
|
# Type alias for JSON-serializable dictionaries
|
|
40
41
|
JsonDict = dict[str, Any]
|
|
@@ -105,7 +106,7 @@ class CrmAgentEnv(EnvClient[CrmAgentAction, CrmAgentObservation, CrmAgentState])
|
|
|
105
106
|
raise ValueError(
|
|
106
107
|
"API key is required. Pass api_key parameter or set "
|
|
107
108
|
"CRM_AGENT_API_KEY environment variable. "
|
|
108
|
-
"
|
|
109
|
+
"Contact hello@construct-labs.com to obtain an API key."
|
|
109
110
|
)
|
|
110
111
|
|
|
111
112
|
self._api_key = resolved_api_key
|
|
@@ -162,7 +163,7 @@ class CrmAgentEnv(EnvClient[CrmAgentAction, CrmAgentObservation, CrmAgentState])
|
|
|
162
163
|
if "401" in error_msg or "403" in error_msg or "4001" in error_msg:
|
|
163
164
|
raise ConnectionError(
|
|
164
165
|
"Authentication failed. Please verify your API key. "
|
|
165
|
-
"
|
|
166
|
+
"Contact hello@construct-labs.com if you need assistance."
|
|
166
167
|
) from e
|
|
167
168
|
raise ConnectionError(f"Failed to connect to {self._ws_url}: {e}") from e
|
|
168
169
|
finally:
|
|
@@ -194,12 +195,6 @@ class CrmAgentEnv(EnvClient[CrmAgentAction, CrmAgentObservation, CrmAgentState])
|
|
|
194
195
|
"""Parse server response into CrmAgentState."""
|
|
195
196
|
return CrmAgentState.model_validate(payload)
|
|
196
197
|
|
|
197
|
-
def _reset_payload(self, seed: int | None = None) -> JsonDict:
|
|
198
|
-
"""Create payload for reset request."""
|
|
199
|
-
if seed is not None:
|
|
200
|
-
return {"seed": seed}
|
|
201
|
-
return {}
|
|
202
|
-
|
|
203
198
|
# =========================================================================
|
|
204
199
|
# Extensible Properties - Override these in subclasses
|
|
205
200
|
# =========================================================================
|
|
@@ -233,48 +228,95 @@ class CrmAgentEnv(EnvClient[CrmAgentAction, CrmAgentObservation, CrmAgentState])
|
|
|
233
228
|
"""
|
|
234
229
|
return """You are a tool-using agent interacting with a CRM (Customer Relationship Management) system.
|
|
235
230
|
|
|
236
|
-
GOAL
|
|
231
|
+
## GOAL
|
|
232
|
+
|
|
233
|
+
Complete CRM tasks by creating, updating, retrieving, and managing business data.
|
|
234
|
+
|
|
235
|
+
## DATA MODEL
|
|
236
|
+
|
|
237
|
+
- Companies: Organizations you do business with
|
|
238
|
+
- People: Contacts who work at companies (linked via person_company_id)
|
|
239
|
+
- Opportunities: Sales deals linked to a company and a person (point of contact)
|
|
240
|
+
- Notes: Free-text records attached to any company, person, or opportunity
|
|
241
|
+
- Tasks: Action items with due dates, attached to any company, person, or opportunity
|
|
242
|
+
|
|
243
|
+
## AVAILABLE TOOLS
|
|
244
|
+
|
|
245
|
+
- Companies: list_companies, get_company, create_company, update_company, delete_company
|
|
246
|
+
- People: list_people, get_person, create_person, update_person, delete_person
|
|
247
|
+
- Opportunities: list_opportunities, get_opportunity, create_opportunity, update_opportunity, delete_opportunity
|
|
248
|
+
- Notes: list_notes, create_note
|
|
249
|
+
- Tasks: list_tasks, create_task, update_task, complete_task
|
|
250
|
+
- Answer: submit_answer
|
|
251
|
+
|
|
252
|
+
## FILTERING AND PAGINATION
|
|
253
|
+
|
|
254
|
+
Use the filter parameter to search records. Format: field[comparator]:value
|
|
255
|
+
|
|
256
|
+
Comparators: eq, neq, gt, gte, lt, lte, ilike (case-insensitive like), in, is, startsWith, containsAny
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
- name[ilike]:"%acme%" - names containing "acme"
|
|
260
|
+
- stage[eq]:"WON" - opportunities with stage WON
|
|
261
|
+
- amount[gte]:10000 - deals worth $10,000 or more
|
|
262
|
+
- createdAt[gte]:"2026-01-01" - records created this year
|
|
263
|
+
- deletedAt[is]:NULL - non-deleted records
|
|
264
|
+
|
|
265
|
+
Rules: Quote strings and dates. Do not quote numbers. Combine with comma for AND: field1[eq]:"a",field2[gt]:5
|
|
266
|
+
|
|
267
|
+
Pagination: Results return max 60 records. Use starting_after with the endCursor from pageInfo to get more.
|
|
268
|
+
|
|
269
|
+
## WORKFLOW
|
|
270
|
+
|
|
271
|
+
For complex tasks, make multiple tool calls:
|
|
272
|
+
1. First, list or search to find relevant records
|
|
273
|
+
2. Then, get details or create/update as needed
|
|
274
|
+
3. Finally, call submit_answer with your findings
|
|
237
275
|
|
|
238
|
-
|
|
239
|
-
- Companies: list, get, create, update, delete
|
|
240
|
-
- People/Contacts: list, get, create, update, delete
|
|
241
|
-
- Opportunities: list, get, create, update, delete
|
|
242
|
-
- Notes: list, create (attach to companies, people, or opportunities)
|
|
243
|
-
- Tasks: list, create, update, complete
|
|
276
|
+
## OUTPUT FORMAT
|
|
244
277
|
|
|
245
|
-
|
|
278
|
+
Think briefly about which tool to use, then output exactly one tool call:
|
|
279
|
+
<tool_call>
|
|
280
|
+
{"name": "tool_name", "arguments": {...}}
|
|
281
|
+
</tool_call>
|
|
246
282
|
|
|
247
|
-
|
|
283
|
+
## EXAMPLES
|
|
284
|
+
|
|
285
|
+
List companies:
|
|
248
286
|
<tool_call>
|
|
249
287
|
{"name": "list_companies", "arguments": {"limit": 10}}
|
|
250
288
|
</tool_call>
|
|
251
289
|
|
|
252
|
-
|
|
290
|
+
Find companies by name:
|
|
253
291
|
<tool_call>
|
|
254
|
-
{"name": "
|
|
292
|
+
{"name": "list_companies", "arguments": {"filter": "name[ilike]:\"%tech%\""}}
|
|
255
293
|
</tool_call>
|
|
256
294
|
|
|
257
|
-
|
|
295
|
+
Create a company:
|
|
258
296
|
<tool_call>
|
|
259
|
-
{"name": "
|
|
297
|
+
{"name": "create_company", "arguments": {"company_name": "Acme Corp", "company_domain": "acme.com"}}
|
|
260
298
|
</tool_call>
|
|
261
299
|
|
|
262
|
-
|
|
300
|
+
Create a contact linked to a company:
|
|
263
301
|
<tool_call>
|
|
264
|
-
{"name": "
|
|
302
|
+
{"name": "create_person", "arguments": {"person_first_name": "John", "person_last_name": "Doe", "person_email": "john@acme.com", "person_company_id": "company-uuid-here"}}
|
|
265
303
|
</tool_call>
|
|
266
304
|
|
|
267
|
-
|
|
305
|
+
Submit final answer:
|
|
306
|
+
<tool_call>
|
|
307
|
+
{"name": "submit_answer", "arguments": {"answer": "The total pipeline value is $1.5M across 12 open opportunities."}}
|
|
308
|
+
</tool_call>"""
|
|
268
309
|
|
|
269
310
|
@property
|
|
270
311
|
def tools(self) -> list[JsonDict]:
|
|
271
312
|
"""Tool definitions for the CRM environment.
|
|
272
313
|
|
|
273
|
-
|
|
274
|
-
|
|
314
|
+
Returns tool definitions formatted by `format_tools()`. Override
|
|
315
|
+
`format_tools()` to transform the tool schema for different providers
|
|
316
|
+
(e.g., Anthropic, Google).
|
|
275
317
|
|
|
276
318
|
Returns:
|
|
277
|
-
List of tool definitions
|
|
319
|
+
List of tool definitions (OpenAI format by default).
|
|
278
320
|
|
|
279
321
|
Example:
|
|
280
322
|
>>> class ReadOnlyAgent(CrmAgentEnv):
|
|
@@ -285,7 +327,34 @@ IMPORTANT: Output ONLY a tool_call, no other text."""
|
|
|
285
327
|
... return [t for t in self._default_tools()
|
|
286
328
|
... if any(op in t['function']['name'] for op in read_ops)]
|
|
287
329
|
"""
|
|
288
|
-
return self._default_tools()
|
|
330
|
+
return self.format_tools(self._default_tools())
|
|
331
|
+
|
|
332
|
+
def format_tools(self, tools: list[JsonDict]) -> list[JsonDict]:
|
|
333
|
+
"""Format tool definitions for the target LLM provider.
|
|
334
|
+
|
|
335
|
+
Override this method to transform tool schemas for different providers.
|
|
336
|
+
The default implementation returns OpenAI-compatible format unchanged.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
tools: List of tool definitions in OpenAI format.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Formatted tool definitions for your target provider.
|
|
343
|
+
|
|
344
|
+
Example (Anthropic format):
|
|
345
|
+
>>> class AnthropicCrmAgent(CrmAgentEnv):
|
|
346
|
+
... def format_tools(self, tools):
|
|
347
|
+
... # Convert OpenAI format to Anthropic format
|
|
348
|
+
... return [
|
|
349
|
+
... {
|
|
350
|
+
... "name": t["function"]["name"],
|
|
351
|
+
... "description": t["function"]["description"],
|
|
352
|
+
... "input_schema": t["function"]["parameters"],
|
|
353
|
+
... }
|
|
354
|
+
... for t in tools
|
|
355
|
+
... ]
|
|
356
|
+
"""
|
|
357
|
+
return tools
|
|
289
358
|
|
|
290
359
|
def _default_tools(self) -> list[JsonDict]:
|
|
291
360
|
"""Return the default tool definitions.
|
|
@@ -293,548 +362,9 @@ IMPORTANT: Output ONLY a tool_call, no other text."""
|
|
|
293
362
|
Subclasses can call this to get all default tools and filter/extend them.
|
|
294
363
|
|
|
295
364
|
Returns:
|
|
296
|
-
Complete list of CRM tool definitions.
|
|
365
|
+
Complete list of CRM tool definitions in OpenAI format.
|
|
297
366
|
"""
|
|
298
|
-
return
|
|
299
|
-
# =================================================================
|
|
300
|
-
# Company Tools
|
|
301
|
-
# =================================================================
|
|
302
|
-
{
|
|
303
|
-
"type": "function",
|
|
304
|
-
"function": {
|
|
305
|
-
"name": "list_companies",
|
|
306
|
-
"description": "List all companies in the CRM",
|
|
307
|
-
"parameters": {
|
|
308
|
-
"type": "object",
|
|
309
|
-
"properties": {
|
|
310
|
-
"limit": {
|
|
311
|
-
"type": "integer",
|
|
312
|
-
"default": 60,
|
|
313
|
-
"description": "Maximum number of companies to return (max 200)",
|
|
314
|
-
},
|
|
315
|
-
"starting_after": {
|
|
316
|
-
"type": "string",
|
|
317
|
-
"description": "Cursor for pagination - returns objects after this ID",
|
|
318
|
-
},
|
|
319
|
-
"ending_before": {
|
|
320
|
-
"type": "string",
|
|
321
|
-
"description": "Cursor for pagination - returns objects before this ID",
|
|
322
|
-
},
|
|
323
|
-
"order_by": {
|
|
324
|
-
"type": "string",
|
|
325
|
-
"description": "Order by: field_name[ASC|DESC]",
|
|
326
|
-
},
|
|
327
|
-
"filter": {
|
|
328
|
-
"type": "string",
|
|
329
|
-
"description": "Filter: field[eq|gt|lt|contains]:value",
|
|
330
|
-
},
|
|
331
|
-
"depth": {
|
|
332
|
-
"type": "integer",
|
|
333
|
-
"default": 1,
|
|
334
|
-
"description": "Relation depth: 0=primary only, 1=include relations",
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
"required": [],
|
|
338
|
-
},
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
"type": "function",
|
|
343
|
-
"function": {
|
|
344
|
-
"name": "get_company",
|
|
345
|
-
"description": "Get details of a specific company",
|
|
346
|
-
"parameters": {
|
|
347
|
-
"type": "object",
|
|
348
|
-
"properties": {
|
|
349
|
-
"record_id": {
|
|
350
|
-
"type": "string",
|
|
351
|
-
"description": "ID of the company to retrieve",
|
|
352
|
-
},
|
|
353
|
-
"depth": {
|
|
354
|
-
"type": "integer",
|
|
355
|
-
"default": 1,
|
|
356
|
-
"description": "Relation depth: 0=primary only, 1=include relations",
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
"required": ["record_id"],
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
{
|
|
364
|
-
"type": "function",
|
|
365
|
-
"function": {
|
|
366
|
-
"name": "create_company",
|
|
367
|
-
"description": "Create a new company in the CRM",
|
|
368
|
-
"parameters": {
|
|
369
|
-
"type": "object",
|
|
370
|
-
"properties": {
|
|
371
|
-
"company_name": {
|
|
372
|
-
"type": "string",
|
|
373
|
-
"description": "Name of the company",
|
|
374
|
-
},
|
|
375
|
-
"company_domain": {
|
|
376
|
-
"type": "string",
|
|
377
|
-
"description": "Domain/website of the company",
|
|
378
|
-
},
|
|
379
|
-
"company_address": {
|
|
380
|
-
"type": "string",
|
|
381
|
-
"description": "Address of the company",
|
|
382
|
-
},
|
|
383
|
-
"company_employees": {
|
|
384
|
-
"type": "integer",
|
|
385
|
-
"description": "Number of employees",
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
"required": ["company_name"],
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
"type": "function",
|
|
394
|
-
"function": {
|
|
395
|
-
"name": "update_company",
|
|
396
|
-
"description": "Update an existing company",
|
|
397
|
-
"parameters": {
|
|
398
|
-
"type": "object",
|
|
399
|
-
"properties": {
|
|
400
|
-
"record_id": {
|
|
401
|
-
"type": "string",
|
|
402
|
-
"description": "ID of the company to update",
|
|
403
|
-
},
|
|
404
|
-
"company_name": {"type": "string"},
|
|
405
|
-
"company_domain": {"type": "string"},
|
|
406
|
-
"company_address": {"type": "string"},
|
|
407
|
-
"company_employees": {"type": "integer"},
|
|
408
|
-
},
|
|
409
|
-
"required": ["record_id"],
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
"type": "function",
|
|
415
|
-
"function": {
|
|
416
|
-
"name": "delete_company",
|
|
417
|
-
"description": "Delete a company from the CRM",
|
|
418
|
-
"parameters": {
|
|
419
|
-
"type": "object",
|
|
420
|
-
"properties": {
|
|
421
|
-
"record_id": {
|
|
422
|
-
"type": "string",
|
|
423
|
-
"description": "ID of the company to delete",
|
|
424
|
-
},
|
|
425
|
-
},
|
|
426
|
-
"required": ["record_id"],
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
|
-
# =================================================================
|
|
431
|
-
# Person/Contact Tools
|
|
432
|
-
# =================================================================
|
|
433
|
-
{
|
|
434
|
-
"type": "function",
|
|
435
|
-
"function": {
|
|
436
|
-
"name": "list_people",
|
|
437
|
-
"description": "List all contacts/people in the CRM",
|
|
438
|
-
"parameters": {
|
|
439
|
-
"type": "object",
|
|
440
|
-
"properties": {
|
|
441
|
-
"limit": {
|
|
442
|
-
"type": "integer",
|
|
443
|
-
"default": 60,
|
|
444
|
-
"description": "Maximum number of contacts to return (max 200)",
|
|
445
|
-
},
|
|
446
|
-
"starting_after": {"type": "string"},
|
|
447
|
-
"ending_before": {"type": "string"},
|
|
448
|
-
"order_by": {"type": "string"},
|
|
449
|
-
"filter": {"type": "string"},
|
|
450
|
-
"depth": {"type": "integer", "default": 1},
|
|
451
|
-
},
|
|
452
|
-
"required": [],
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
"type": "function",
|
|
458
|
-
"function": {
|
|
459
|
-
"name": "get_person",
|
|
460
|
-
"description": "Get details of a specific contact",
|
|
461
|
-
"parameters": {
|
|
462
|
-
"type": "object",
|
|
463
|
-
"properties": {
|
|
464
|
-
"record_id": {
|
|
465
|
-
"type": "string",
|
|
466
|
-
"description": "ID of the contact to retrieve",
|
|
467
|
-
},
|
|
468
|
-
"depth": {"type": "integer", "default": 1},
|
|
469
|
-
},
|
|
470
|
-
"required": ["record_id"],
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
"type": "function",
|
|
476
|
-
"function": {
|
|
477
|
-
"name": "create_person",
|
|
478
|
-
"description": "Create a new contact/person in the CRM",
|
|
479
|
-
"parameters": {
|
|
480
|
-
"type": "object",
|
|
481
|
-
"properties": {
|
|
482
|
-
"person_first_name": {
|
|
483
|
-
"type": "string",
|
|
484
|
-
"description": "First name",
|
|
485
|
-
},
|
|
486
|
-
"person_last_name": {
|
|
487
|
-
"type": "string",
|
|
488
|
-
"description": "Last name",
|
|
489
|
-
},
|
|
490
|
-
"person_email": {
|
|
491
|
-
"type": "string",
|
|
492
|
-
"description": "Email address",
|
|
493
|
-
},
|
|
494
|
-
"person_phone": {
|
|
495
|
-
"type": "string",
|
|
496
|
-
"description": "Phone number",
|
|
497
|
-
},
|
|
498
|
-
"person_company_id": {
|
|
499
|
-
"type": "string",
|
|
500
|
-
"description": "ID of associated company",
|
|
501
|
-
},
|
|
502
|
-
"person_job_title": {
|
|
503
|
-
"type": "string",
|
|
504
|
-
"description": "Job title",
|
|
505
|
-
},
|
|
506
|
-
},
|
|
507
|
-
"required": ["person_first_name", "person_last_name"],
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
"type": "function",
|
|
513
|
-
"function": {
|
|
514
|
-
"name": "update_person",
|
|
515
|
-
"description": "Update an existing contact",
|
|
516
|
-
"parameters": {
|
|
517
|
-
"type": "object",
|
|
518
|
-
"properties": {
|
|
519
|
-
"record_id": {
|
|
520
|
-
"type": "string",
|
|
521
|
-
"description": "ID of the contact to update",
|
|
522
|
-
},
|
|
523
|
-
"person_first_name": {"type": "string"},
|
|
524
|
-
"person_last_name": {"type": "string"},
|
|
525
|
-
"person_email": {"type": "string"},
|
|
526
|
-
"person_phone": {"type": "string"},
|
|
527
|
-
"person_job_title": {"type": "string"},
|
|
528
|
-
},
|
|
529
|
-
"required": ["record_id"],
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
"type": "function",
|
|
535
|
-
"function": {
|
|
536
|
-
"name": "delete_person",
|
|
537
|
-
"description": "Delete a contact from the CRM",
|
|
538
|
-
"parameters": {
|
|
539
|
-
"type": "object",
|
|
540
|
-
"properties": {
|
|
541
|
-
"record_id": {
|
|
542
|
-
"type": "string",
|
|
543
|
-
"description": "ID of the contact to delete",
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
"required": ["record_id"],
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
|
-
},
|
|
550
|
-
# =================================================================
|
|
551
|
-
# Opportunity Tools
|
|
552
|
-
# =================================================================
|
|
553
|
-
{
|
|
554
|
-
"type": "function",
|
|
555
|
-
"function": {
|
|
556
|
-
"name": "list_opportunities",
|
|
557
|
-
"description": "List all opportunities/deals in the CRM",
|
|
558
|
-
"parameters": {
|
|
559
|
-
"type": "object",
|
|
560
|
-
"properties": {
|
|
561
|
-
"limit": {
|
|
562
|
-
"type": "integer",
|
|
563
|
-
"default": 60,
|
|
564
|
-
"description": "Maximum number to return (max 200)",
|
|
565
|
-
},
|
|
566
|
-
"starting_after": {"type": "string"},
|
|
567
|
-
"ending_before": {"type": "string"},
|
|
568
|
-
"order_by": {"type": "string"},
|
|
569
|
-
"filter": {"type": "string"},
|
|
570
|
-
"depth": {"type": "integer", "default": 1},
|
|
571
|
-
},
|
|
572
|
-
"required": [],
|
|
573
|
-
},
|
|
574
|
-
},
|
|
575
|
-
},
|
|
576
|
-
{
|
|
577
|
-
"type": "function",
|
|
578
|
-
"function": {
|
|
579
|
-
"name": "get_opportunity",
|
|
580
|
-
"description": "Get details of a specific opportunity",
|
|
581
|
-
"parameters": {
|
|
582
|
-
"type": "object",
|
|
583
|
-
"properties": {
|
|
584
|
-
"record_id": {
|
|
585
|
-
"type": "string",
|
|
586
|
-
"description": "ID of the opportunity",
|
|
587
|
-
},
|
|
588
|
-
"depth": {"type": "integer", "default": 1},
|
|
589
|
-
},
|
|
590
|
-
"required": ["record_id"],
|
|
591
|
-
},
|
|
592
|
-
},
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
"type": "function",
|
|
596
|
-
"function": {
|
|
597
|
-
"name": "create_opportunity",
|
|
598
|
-
"description": "Create a new opportunity/deal",
|
|
599
|
-
"parameters": {
|
|
600
|
-
"type": "object",
|
|
601
|
-
"properties": {
|
|
602
|
-
"opportunity_name": {
|
|
603
|
-
"type": "string",
|
|
604
|
-
"description": "Name of the opportunity",
|
|
605
|
-
},
|
|
606
|
-
"opportunity_amount": {
|
|
607
|
-
"type": "number",
|
|
608
|
-
"description": "Deal value",
|
|
609
|
-
},
|
|
610
|
-
"opportunity_stage": {
|
|
611
|
-
"type": "string",
|
|
612
|
-
"enum": ["NEW", "MEETING", "PROPOSAL", "WON", "LOST"],
|
|
613
|
-
"description": "Sales stage",
|
|
614
|
-
},
|
|
615
|
-
"opportunity_close_date": {
|
|
616
|
-
"type": "string",
|
|
617
|
-
"description": "Expected close date (ISO format)",
|
|
618
|
-
},
|
|
619
|
-
"opportunity_company_id": {
|
|
620
|
-
"type": "string",
|
|
621
|
-
"description": "Associated company ID",
|
|
622
|
-
},
|
|
623
|
-
"opportunity_person_id": {
|
|
624
|
-
"type": "string",
|
|
625
|
-
"description": "Point of contact ID",
|
|
626
|
-
},
|
|
627
|
-
},
|
|
628
|
-
"required": ["opportunity_name"],
|
|
629
|
-
},
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
{
|
|
633
|
-
"type": "function",
|
|
634
|
-
"function": {
|
|
635
|
-
"name": "update_opportunity",
|
|
636
|
-
"description": "Update an existing opportunity",
|
|
637
|
-
"parameters": {
|
|
638
|
-
"type": "object",
|
|
639
|
-
"properties": {
|
|
640
|
-
"record_id": {
|
|
641
|
-
"type": "string",
|
|
642
|
-
"description": "ID of the opportunity to update",
|
|
643
|
-
},
|
|
644
|
-
"opportunity_name": {"type": "string"},
|
|
645
|
-
"opportunity_amount": {"type": "number"},
|
|
646
|
-
"opportunity_stage": {
|
|
647
|
-
"type": "string",
|
|
648
|
-
"enum": ["NEW", "MEETING", "PROPOSAL", "WON", "LOST"],
|
|
649
|
-
},
|
|
650
|
-
"opportunity_close_date": {"type": "string"},
|
|
651
|
-
},
|
|
652
|
-
"required": ["record_id"],
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
},
|
|
656
|
-
{
|
|
657
|
-
"type": "function",
|
|
658
|
-
"function": {
|
|
659
|
-
"name": "delete_opportunity",
|
|
660
|
-
"description": "Delete an opportunity",
|
|
661
|
-
"parameters": {
|
|
662
|
-
"type": "object",
|
|
663
|
-
"properties": {
|
|
664
|
-
"record_id": {
|
|
665
|
-
"type": "string",
|
|
666
|
-
"description": "ID of the opportunity to delete",
|
|
667
|
-
},
|
|
668
|
-
},
|
|
669
|
-
"required": ["record_id"],
|
|
670
|
-
},
|
|
671
|
-
},
|
|
672
|
-
},
|
|
673
|
-
# =================================================================
|
|
674
|
-
# Note Tools
|
|
675
|
-
# =================================================================
|
|
676
|
-
{
|
|
677
|
-
"type": "function",
|
|
678
|
-
"function": {
|
|
679
|
-
"name": "list_notes",
|
|
680
|
-
"description": "List all notes in the CRM",
|
|
681
|
-
"parameters": {
|
|
682
|
-
"type": "object",
|
|
683
|
-
"properties": {
|
|
684
|
-
"limit": {
|
|
685
|
-
"type": "integer",
|
|
686
|
-
"default": 10,
|
|
687
|
-
"description": "Maximum number of notes to return",
|
|
688
|
-
},
|
|
689
|
-
},
|
|
690
|
-
"required": [],
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
},
|
|
694
|
-
{
|
|
695
|
-
"type": "function",
|
|
696
|
-
"function": {
|
|
697
|
-
"name": "create_note",
|
|
698
|
-
"description": "Create a note attached to a record",
|
|
699
|
-
"parameters": {
|
|
700
|
-
"type": "object",
|
|
701
|
-
"properties": {
|
|
702
|
-
"note_body": {
|
|
703
|
-
"type": "string",
|
|
704
|
-
"description": "Content of the note",
|
|
705
|
-
},
|
|
706
|
-
"note_target_id": {
|
|
707
|
-
"type": "string",
|
|
708
|
-
"description": "ID of record to attach note to",
|
|
709
|
-
},
|
|
710
|
-
"note_target_type": {
|
|
711
|
-
"type": "string",
|
|
712
|
-
"enum": ["company", "person", "opportunity"],
|
|
713
|
-
"description": "Type of record",
|
|
714
|
-
},
|
|
715
|
-
},
|
|
716
|
-
"required": ["note_body"],
|
|
717
|
-
},
|
|
718
|
-
},
|
|
719
|
-
},
|
|
720
|
-
# =================================================================
|
|
721
|
-
# Task Tools
|
|
722
|
-
# =================================================================
|
|
723
|
-
{
|
|
724
|
-
"type": "function",
|
|
725
|
-
"function": {
|
|
726
|
-
"name": "list_tasks",
|
|
727
|
-
"description": "List all tasks in the CRM",
|
|
728
|
-
"parameters": {
|
|
729
|
-
"type": "object",
|
|
730
|
-
"properties": {
|
|
731
|
-
"limit": {
|
|
732
|
-
"type": "integer",
|
|
733
|
-
"default": 10,
|
|
734
|
-
"description": "Maximum number of tasks to return",
|
|
735
|
-
},
|
|
736
|
-
},
|
|
737
|
-
"required": [],
|
|
738
|
-
},
|
|
739
|
-
},
|
|
740
|
-
},
|
|
741
|
-
{
|
|
742
|
-
"type": "function",
|
|
743
|
-
"function": {
|
|
744
|
-
"name": "create_task",
|
|
745
|
-
"description": "Create a task, optionally linked to a record",
|
|
746
|
-
"parameters": {
|
|
747
|
-
"type": "object",
|
|
748
|
-
"properties": {
|
|
749
|
-
"task_title": {
|
|
750
|
-
"type": "string",
|
|
751
|
-
"description": "Title of the task",
|
|
752
|
-
},
|
|
753
|
-
"task_body": {
|
|
754
|
-
"type": "string",
|
|
755
|
-
"description": "Description",
|
|
756
|
-
},
|
|
757
|
-
"task_due_date": {
|
|
758
|
-
"type": "string",
|
|
759
|
-
"description": "Due date (ISO format)",
|
|
760
|
-
},
|
|
761
|
-
"task_status": {
|
|
762
|
-
"type": "string",
|
|
763
|
-
"enum": ["TODO", "IN_PROGRESS", "DONE"],
|
|
764
|
-
"description": "Status",
|
|
765
|
-
},
|
|
766
|
-
"task_target_id": {
|
|
767
|
-
"type": "string",
|
|
768
|
-
"description": "ID of record to link task to",
|
|
769
|
-
},
|
|
770
|
-
"task_target_type": {
|
|
771
|
-
"type": "string",
|
|
772
|
-
"enum": ["company", "person", "opportunity"],
|
|
773
|
-
"description": "Type of record",
|
|
774
|
-
},
|
|
775
|
-
},
|
|
776
|
-
"required": ["task_title"],
|
|
777
|
-
},
|
|
778
|
-
},
|
|
779
|
-
},
|
|
780
|
-
{
|
|
781
|
-
"type": "function",
|
|
782
|
-
"function": {
|
|
783
|
-
"name": "update_task",
|
|
784
|
-
"description": "Update an existing task",
|
|
785
|
-
"parameters": {
|
|
786
|
-
"type": "object",
|
|
787
|
-
"properties": {
|
|
788
|
-
"record_id": {
|
|
789
|
-
"type": "string",
|
|
790
|
-
"description": "ID of the task to update",
|
|
791
|
-
},
|
|
792
|
-
"task_title": {"type": "string"},
|
|
793
|
-
"task_body": {"type": "string"},
|
|
794
|
-
"task_due_date": {"type": "string"},
|
|
795
|
-
},
|
|
796
|
-
"required": ["record_id"],
|
|
797
|
-
},
|
|
798
|
-
},
|
|
799
|
-
},
|
|
800
|
-
{
|
|
801
|
-
"type": "function",
|
|
802
|
-
"function": {
|
|
803
|
-
"name": "complete_task",
|
|
804
|
-
"description": "Mark a task as complete",
|
|
805
|
-
"parameters": {
|
|
806
|
-
"type": "object",
|
|
807
|
-
"properties": {
|
|
808
|
-
"record_id": {
|
|
809
|
-
"type": "string",
|
|
810
|
-
"description": "ID of the task to complete",
|
|
811
|
-
},
|
|
812
|
-
},
|
|
813
|
-
"required": ["record_id"],
|
|
814
|
-
},
|
|
815
|
-
},
|
|
816
|
-
},
|
|
817
|
-
# =================================================================
|
|
818
|
-
# Submit Answer Tool
|
|
819
|
-
# =================================================================
|
|
820
|
-
{
|
|
821
|
-
"type": "function",
|
|
822
|
-
"function": {
|
|
823
|
-
"name": "submit_answer",
|
|
824
|
-
"description": "Submit final answer and end the session",
|
|
825
|
-
"parameters": {
|
|
826
|
-
"type": "object",
|
|
827
|
-
"properties": {
|
|
828
|
-
"answer": {
|
|
829
|
-
"type": "string",
|
|
830
|
-
"description": "The final answer based on CRM data",
|
|
831
|
-
},
|
|
832
|
-
},
|
|
833
|
-
"required": ["answer"],
|
|
834
|
-
},
|
|
835
|
-
},
|
|
836
|
-
},
|
|
837
|
-
]
|
|
367
|
+
return list(DEFAULT_TOOLS)
|
|
838
368
|
|
|
839
369
|
# =========================================================================
|
|
840
370
|
# Tool Parsing and Observation Formatting
|
|
@@ -967,6 +497,32 @@ IMPORTANT: Output ONLY a tool_call, no other text."""
|
|
|
967
497
|
if field in arguments and arguments[field] is not None:
|
|
968
498
|
action_kwargs[field] = arguments[field]
|
|
969
499
|
|
|
500
|
+
# Convert generic note_target_id/type to specific fields
|
|
501
|
+
# Tool schema uses: note_target_id + note_target_type
|
|
502
|
+
# Server expects: note_target_person_id, note_target_company_id, etc.
|
|
503
|
+
note_target_id = action_kwargs.pop("note_target_id", None)
|
|
504
|
+
note_target_type = action_kwargs.pop("note_target_type", None)
|
|
505
|
+
if note_target_id and note_target_type:
|
|
506
|
+
target_type = str(note_target_type).lower()
|
|
507
|
+
if target_type == "person":
|
|
508
|
+
action_kwargs["note_target_person_id"] = note_target_id
|
|
509
|
+
elif target_type == "company":
|
|
510
|
+
action_kwargs["note_target_company_id"] = note_target_id
|
|
511
|
+
elif target_type == "opportunity":
|
|
512
|
+
action_kwargs["note_target_opportunity_id"] = note_target_id
|
|
513
|
+
|
|
514
|
+
# Same conversion for tasks
|
|
515
|
+
task_target_id = action_kwargs.pop("task_target_id", None)
|
|
516
|
+
task_target_type = action_kwargs.pop("task_target_type", None)
|
|
517
|
+
if task_target_id and task_target_type:
|
|
518
|
+
target_type = str(task_target_type).lower()
|
|
519
|
+
if target_type == "person":
|
|
520
|
+
action_kwargs["task_target_person_id"] = task_target_id
|
|
521
|
+
elif target_type == "company":
|
|
522
|
+
action_kwargs["task_target_company_id"] = task_target_id
|
|
523
|
+
elif target_type == "opportunity":
|
|
524
|
+
action_kwargs["task_target_opportunity_id"] = task_target_id
|
|
525
|
+
|
|
970
526
|
try:
|
|
971
527
|
action = CrmAgentAction(**action_kwargs)
|
|
972
528
|
return ParsedAction(action=action, is_valid=True)
|