erdo 0.1.8__py3-none-any.whl → 0.1.10__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.

Potentially problematic release.


This version of erdo might be problematic. Click here for more details.

erdo/types.py CHANGED
@@ -1264,7 +1264,6 @@ class Prompt(BaseModel):
1264
1264
  return prompts
1265
1265
 
1266
1266
 
1267
- # Test system classes for agent testing
1268
1267
  class ConditionDefinition(BaseModel):
1269
1268
  """Condition definition for test expectations."""
1270
1269
 
@@ -1303,310 +1302,6 @@ class APIConditionDefinition(BaseModel):
1303
1302
  return "{}"
1304
1303
 
1305
1304
 
1306
- class TestExpectation(BaseModel):
1307
- """Expected outcome for test validation."""
1308
-
1309
- type: (
1310
- str # "output_contains", "step_success", "invocation_status", "condition", etc.
1311
- )
1312
- step_key: Optional[str] = None
1313
- field: Optional[str] = None
1314
- operator: str # "contains", "equals", "greater_than", etc.
1315
- value: Any
1316
- description: str
1317
- condition: Optional[Union[ConditionDefinition, APIConditionDefinition]] = None
1318
-
1319
- @classmethod
1320
- def output_contains(
1321
- cls, step_key: str, text: str, description: Optional[str] = None
1322
- ) -> "TestExpectation":
1323
- """Expect step output to contain specific text."""
1324
- return cls(
1325
- type="output_contains",
1326
- step_key=step_key,
1327
- operator="contains",
1328
- value=text,
1329
- description=description
1330
- or f"Step '{step_key}' output should contain '{text}'",
1331
- )
1332
-
1333
- @classmethod
1334
- def step_succeeds(
1335
- cls, step_key: str, description: Optional[str] = None
1336
- ) -> "TestExpectation":
1337
- """Expect step to complete successfully."""
1338
- return cls(
1339
- type="step_success",
1340
- step_key=step_key,
1341
- operator="equals",
1342
- value=True,
1343
- description=description or f"Step '{step_key}' should succeed",
1344
- )
1345
-
1346
- @classmethod
1347
- def invocation_status(
1348
- cls, status: str, description: Optional[str] = None
1349
- ) -> "TestExpectation":
1350
- """Expect overall invocation to have specific status."""
1351
- return cls(
1352
- type="invocation_status",
1353
- operator="equals",
1354
- value=status,
1355
- description=description or f"Invocation should have status '{status}'",
1356
- )
1357
-
1358
- @classmethod
1359
- def from_condition_type(
1360
- cls,
1361
- condition_type: str,
1362
- path: str,
1363
- parameters: Dict[str, Any],
1364
- description: Optional[str] = None,
1365
- ) -> "TestExpectation":
1366
- """Create a condition-based expectation using backend condition system."""
1367
- return cls(
1368
- type="condition",
1369
- operator="evaluate",
1370
- value="true",
1371
- description=description
1372
- or f"Condition {condition_type} should evaluate to true",
1373
- condition=ConditionDefinition(
1374
- type=condition_type, path=path, parameters=parameters
1375
- ),
1376
- )
1377
-
1378
- @classmethod
1379
- def text_contains(
1380
- cls, path: str, text: str, description: Optional[str] = None
1381
- ) -> "TestExpectation":
1382
- """Expect value at path to contain specific text."""
1383
- return cls(
1384
- type="condition",
1385
- operator="evaluate",
1386
- value="true",
1387
- description=description or f"Value at '{path}' should contain '{text}'",
1388
- condition=ConditionDefinition(
1389
- type="TextContains", path=path, parameters={"text": text}
1390
- ),
1391
- )
1392
-
1393
- @classmethod
1394
- def is_success(
1395
- cls, path: str = "$.Status", description: Optional[str] = None
1396
- ) -> "TestExpectation":
1397
- """Expect value at path to indicate success."""
1398
- return cls(
1399
- type="condition",
1400
- operator="evaluate",
1401
- value="true",
1402
- description=description or "Operation should succeed",
1403
- condition=ConditionDefinition(type="IsSuccess", path=path, parameters={}),
1404
- )
1405
-
1406
- @classmethod
1407
- def from_condition(
1408
- cls, condition_obj: Any, description: Optional[str] = None
1409
- ) -> "TestExpectation":
1410
- """Create expectation from condition object with parameter hydration support."""
1411
- # Convert condition object to dict format
1412
- condition_dict = condition_obj.to_dict()
1413
- condition_type = condition_dict.get("type", "Unknown")
1414
-
1415
- # Extract leaf parameters from the condition object - these support template strings
1416
- leaf_params = {}
1417
- if "leaf" in condition_dict:
1418
- leaf_params = condition_dict["leaf"]
1419
-
1420
- # Create APIConditionDefinition object that matches backend expectations
1421
- api_condition = APIConditionDefinition(
1422
- type=condition_type,
1423
- conditions=[], # Empty for leaf conditions
1424
- leaf=leaf_params, # Parameters go in leaf field for backend
1425
- )
1426
-
1427
- return cls(
1428
- type="condition",
1429
- operator="evaluate",
1430
- value="true",
1431
- description=description
1432
- or f"Condition {condition_type} should evaluate to true",
1433
- condition=api_condition, # Use APIConditionDefinition object
1434
- )
1435
-
1436
-
1437
- class TestMessage(BaseModel):
1438
- """Test message for agent input."""
1439
-
1440
- role: str # "user" or "assistant"
1441
- content: str
1442
-
1443
- @classmethod
1444
- def user(cls, content: str) -> "TestMessage":
1445
- """Create a user message."""
1446
- return cls(role="user", content=content)
1447
-
1448
- @classmethod
1449
- def assistant(cls, content: str) -> "TestMessage":
1450
- """Create an assistant message."""
1451
- return cls(role="assistant", content=content)
1452
-
1453
-
1454
- class Test(BaseModel):
1455
- """Test definition for agent validation."""
1456
-
1457
- name: str
1458
- description: str
1459
- agent: Optional[Any] = None # Reference to agent being tested
1460
- messages: List[TestMessage] = Field(default_factory=list)
1461
- session_messages: List[TestMessage] = Field(default_factory=list)
1462
- dataset_ids: List[str] = Field(
1463
- default_factory=list
1464
- ) # Reference real datasets by ID
1465
- parameters: Dict[str, str] = Field(default_factory=dict) # Bot parameters
1466
- expectations: List[TestExpectation] = Field(default_factory=list)
1467
- resume_from_state_id: Optional[str] = None
1468
- test_suite: Optional[str] = None # Name of the test suite this test belongs to
1469
-
1470
- def __init__(
1471
- self,
1472
- name: Optional[str] = None,
1473
- description: str = "",
1474
- agent: Optional[Any] = None,
1475
- expect: Optional[List[TestExpectation]] = None,
1476
- **kwargs: Any,
1477
- ):
1478
- """Initialize a Test with improved syntax support.
1479
-
1480
- Args:
1481
- name: Test name (auto-generated from agent name if not provided)
1482
- description: Test description
1483
- agent: Agent to test
1484
- expect: List of expectations (alternative to using .expect() method)
1485
- **kwargs: Additional test configuration
1486
- """
1487
- # Auto-generate name from agent if not provided
1488
- if name is None and agent is not None:
1489
- agent_name = getattr(agent, "name", "agent")
1490
- name = f"{agent_name}_test"
1491
- elif name is None:
1492
- name = "test"
1493
-
1494
- # Set expectations from constructor if provided
1495
- if expect is not None:
1496
- kwargs["expectations"] = expect
1497
-
1498
- super().__init__(name=name, description=description, agent=agent, **kwargs)
1499
-
1500
- def message(self, role: str, content: str) -> "Test":
1501
- """Add a message to the test conversation."""
1502
- self.messages.append(TestMessage(role=role, content=content))
1503
- return self
1504
-
1505
- def user_message(self, content: str) -> "Test":
1506
- """Add a user message."""
1507
- return self.message("user", content)
1508
-
1509
- def assistant_message(self, content: str) -> "Test":
1510
- """Add an assistant message."""
1511
- return self.message("assistant", content)
1512
-
1513
- def dataset(self, dataset_id: str) -> "Test":
1514
- """Add a dataset to the test by ID."""
1515
- self.dataset_ids.append(dataset_id)
1516
- return self
1517
-
1518
- def expect(self, expectation: TestExpectation) -> "Test":
1519
- """Add an expectation to the test."""
1520
- self.expectations.append(expectation)
1521
- return self
1522
-
1523
- def to_dict(self) -> Dict[str, Any]:
1524
- """Convert to dict format expected by backend."""
1525
- return {
1526
- "name": self.name,
1527
- "description": self.description,
1528
- "test_data": {
1529
- "messages": [msg.model_dump() for msg in self.messages],
1530
- "session_messages": [msg.model_dump() for msg in self.session_messages],
1531
- "dataset_ids": self.dataset_ids,
1532
- "parameters": self.parameters,
1533
- "resume_from_state_id": self.resume_from_state_id,
1534
- },
1535
- "expectations": [exp.model_dump() for exp in self.expectations],
1536
- }
1537
-
1538
-
1539
- def Expect(condition: Any, description: Optional[str] = None) -> TestExpectation:
1540
- """Create a test expectation from a condition object with clean syntax.
1541
-
1542
- Usage:
1543
- Expect(IsSuccess())
1544
- Expect(TextContains(text=TemplateString("{{output.content}}"), value="success"))
1545
- """
1546
- return TestExpectation.from_condition(condition, description)
1547
-
1548
-
1549
- class TestSuite(BaseModel):
1550
- """Test suite for grouping related tests together."""
1551
-
1552
- name: str
1553
- description: str
1554
- tests: List[Test] = Field(default_factory=list)
1555
- tags: List[str] = Field(default_factory=list)
1556
-
1557
- def __init__(
1558
- self,
1559
- name: str,
1560
- description: str = "",
1561
- tests: Optional[List[Test]] = None,
1562
- tags: Optional[List[str]] = None,
1563
- **kwargs: Any,
1564
- ):
1565
- """Initialize a TestSuite.
1566
-
1567
- Args:
1568
- name: Suite name
1569
- description: Suite description
1570
- tests: List of tests to include in the suite
1571
- tags: Tags for categorizing the test suite
1572
- **kwargs: Additional suite configuration
1573
- """
1574
- super().__init__(
1575
- name=name,
1576
- description=description,
1577
- tests=tests or [],
1578
- tags=tags or [],
1579
- **kwargs,
1580
- )
1581
-
1582
- def add_test(self, test: Test) -> "TestSuite":
1583
- """Add a test to the suite."""
1584
- self.tests.append(test)
1585
- return self
1586
-
1587
- def add_tests(self, *tests: Test) -> "TestSuite":
1588
- """Add multiple tests to the suite."""
1589
- for test in tests:
1590
- self.tests.append(test)
1591
- return self
1592
-
1593
- def tag(self, *tags: str) -> "TestSuite":
1594
- """Add tags to the test suite."""
1595
- for tag in tags:
1596
- if tag not in self.tags:
1597
- self.tags.append(tag)
1598
- return self
1599
-
1600
- def to_dict(self) -> Dict[str, Any]:
1601
- """Convert to dict format expected by backend."""
1602
- return {
1603
- "name": self.name,
1604
- "description": self.description,
1605
- "tests": [test.to_dict() for test in self.tests],
1606
- "tags": self.tags,
1607
- }
1608
-
1609
-
1610
1305
  # Re-export commonly used types for convenience
1611
1306
  __all__ = [
1612
1307
  "Agent",
@@ -1621,12 +1316,6 @@ __all__ = [
1621
1316
  "PythonFile",
1622
1317
  "Prompt",
1623
1318
  "TemplateString",
1624
- # Test system
1625
- "Test",
1626
- "TestSuite",
1627
- "TestExpectation",
1628
- "TestMessage",
1629
- "Expect",
1630
1319
  # Complex types
1631
1320
  "ExecutionCondition",
1632
1321
  "ConditionDefinition",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: erdo
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Python SDK for building workflow automation agents with Erdo
5
5
  Project-URL: Homepage, https://erdo.ai
6
6
  Project-URL: Documentation, https://docs.erdo.ai
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
22
23
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
25
  Requires-Python: >=3.9
@@ -292,20 +293,104 @@ main_step.on(
292
293
  )
293
294
  ```
294
295
 
295
- ## CLI Integration
296
+ ## Invoking Agents
297
+
298
+ Use the `invoke()` function to execute agents programmatically:
299
+
300
+ ```python
301
+ from erdo import invoke
302
+
303
+ # Invoke an agent
304
+ response = invoke(
305
+ "data-question-answerer",
306
+ messages=[{"role": "user", "content": "What were Q4 sales?"}],
307
+ datasets=["sales-2024"],
308
+ parameters={"time_period": "Q4"},
309
+ )
310
+
311
+ if response.success:
312
+ print(response.result)
313
+ else:
314
+ print(f"Error: {response.error}")
315
+ ```
316
+
317
+ ### Invocation Modes
318
+
319
+ - **Live Mode** (default): Runs against real backend with LLM API
320
+ - **Replay Mode**: Uses cached responses - free after first run (perfect for testing!)
321
+ - **Mock Mode**: Returns synthetic responses - always free
296
322
 
297
- Deploy your agents using the Erdo CLI:
323
+ ```python
324
+ # Replay mode - free after first run
325
+ response = invoke("my-agent", messages=[...], mode="replay")
326
+
327
+ # Mock mode - always free
328
+ response = invoke("my-agent", messages=[...], mode="mock")
329
+ ```
330
+
331
+ ## Testing Agents
332
+
333
+ Write fast, parallel agent tests using `agent_test_*` functions:
334
+
335
+ ```python
336
+ from erdo import invoke
337
+ from erdo.test import text_contains
338
+
339
+ def agent_test_csv_sales():
340
+ """Test CSV sales analysis."""
341
+ response = invoke(
342
+ "data-question-answerer",
343
+ messages=[{"role": "user", "content": "What were Q4 sales?"}],
344
+ datasets=["sales-q4-2024"],
345
+ mode="replay", # Free after first run!
346
+ )
347
+
348
+ assert response.success
349
+ result_text = str(response.result)
350
+ assert text_contains(result_text, "sales", case_sensitive=False)
351
+ ```
352
+
353
+ Run tests in parallel with the CLI:
298
354
 
299
355
  ```bash
300
- # Install the CLI
301
- pip install erdo
302
- erdo install-cli
356
+ # Run all tests
357
+ erdo agent-test tests/test_my_agent.py
358
+
359
+ # Verbose output
360
+ erdo agent-test tests/test_my_agent.py --verbose
361
+ ```
303
362
 
304
- # Login to your Erdo account
363
+ ### Test Helpers
364
+
365
+ The `erdo.test` module provides assertion helpers:
366
+
367
+ ```python
368
+ from erdo.test import (
369
+ text_contains, # Check if text contains substring
370
+ text_equals, # Check exact match
371
+ text_matches, # Check regex pattern
372
+ json_path_equals, # Check JSON path value
373
+ json_path_exists, # Check if JSON path exists
374
+ has_dataset, # Check if dataset is present
375
+ )
376
+ ```
377
+
378
+ ## CLI Integration
379
+
380
+ Deploy and manage your agents using the Erdo CLI:
381
+
382
+ ```bash
383
+ # Login to your account
305
384
  erdo login
306
385
 
307
- # Sync your agents
308
- erdo sync
386
+ # Sync your agents to the platform
387
+ erdo sync-agent my_agent.py
388
+
389
+ # Invoke an agent
390
+ erdo invoke my-agent --message "Hello!"
391
+
392
+ # Run agent tests
393
+ erdo agent-test tests/test_my_agent.py
309
394
  ```
310
395
 
311
396
  ## Examples
@@ -314,6 +399,8 @@ See the `examples/` directory for complete examples:
314
399
 
315
400
  - `agent_centric_example.py` - Comprehensive agent with multiple steps
316
401
  - `state_example.py` - State management and templating
402
+ - `invoke_example.py` - Agent invocation patterns
403
+ - `agent_test_example.py` - Agent testing examples
317
404
 
318
405
  ## API Reference
319
406
 
@@ -1,12 +1,12 @@
1
- erdo/__init__.py,sha256=gg-frEZXTdpawdIdEOpSJuHDG6w7OM-Fu-QhHmcSK7Y,952
1
+ erdo/__init__.py,sha256=ko4ZhPHlSJpvrnK5BI04qfGycBN9WqYL5Nh6SGk-Ysc,1082
2
2
  erdo/bot_permissions.py,sha256=NZSjjGURORpq3hRqv0tA0YvkxcTZ9MQqtmzrlmof29c,8261
3
3
  erdo/cli_entry.py,sha256=jz18bWz-D09rmkEvwLTAbcK1shHUqFCpiU7U7czEltY,2057
4
+ erdo/formatting.py,sha256=jIfQlqk1oKratM8Fkrlq1rRsnergthbSQFCfeEd6rqY,8346
4
5
  erdo/install_cli.py,sha256=jgRoSm-MIbLHW0Sr5m78q3htRwIh7JXo_wNmpVsiAcM,4454
5
6
  erdo/integrations.py,sha256=-xYBpjYmjhJtv_UnaIfBb46cJC4icLOx9P1kOf5lM-g,4219
6
- erdo/py.typed,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
7
7
  erdo/state.py,sha256=o89-SThDWYNjfwMZuDfier1Lx96_tLi5wYSth7dPI6I,14270
8
8
  erdo/template.py,sha256=gvlSpEN3whD4LEqY6ppeYYZuFeM9ndU68qzKP-ETRJ8,4641
9
- erdo/types.py,sha256=du_pogspPVSwxAe_w3pnt4_EpBxnhnQ-s4JH09L9UCk,63046
9
+ erdo/types.py,sha256=KQGaw42O-ltVUhZjv-MpiS6vrpMbMWkn3g1u2ZPkGlk,52564
10
10
  erdo/_generated/__init__.py,sha256=jFLoVFeswecK5mGp4-MPWwnygol2dYIsEpnmm9gubHY,506
11
11
  erdo/_generated/internal.py,sha256=ghd1g_9WF0kXO2MkFIBxcuHuRF00SlOeT-mjJcfQNyM,1569
12
12
  erdo/_generated/internal_actions.py,sha256=dbZgHNF5RQhjyB8VAJ-4eaV2XUlqHKv99m-DtNNx7rI,2661
@@ -29,17 +29,20 @@ erdo/_generated/actions/websearch.py,sha256=KGKeJwZDBUXg-fPkq8oe7vw1zsspSNJ_Ei-B
29
29
  erdo/_generated/condition/__init__.py,sha256=tWS9LUWbMy8Q4yYG2ZHJuQibNbmACAEupe1f9PPQ11c,14808
30
30
  erdo/actions/__init__.py,sha256=kb9vGSmhSGwxKNGacUCquu6z3d8u972uIw9x13aCrp0,1311
31
31
  erdo/conditions/__init__.py,sha256=xN7MS1aj4lh65CrE-94yFQXnQV8v8VjdEXMzPLTK0CU,394
32
- erdo/config/__init__.py,sha256=qUkApXToTQC88f5n6FDIuAfDM6na7ydOiKT1M5enbNo,121
32
+ erdo/config/__init__.py,sha256=lROKyMtaH5cMiF1RcIRA2NLR1Sp8AKWx4brcPT1Vwy8,147
33
33
  erdo/config/config.py,sha256=BSerQVQdbbp1KuARpIuKp6bM1M4Cqr-Bsf0nQJJTND8,4279
34
- erdo/invoke/__init__.py,sha256=UJ6MM90QWGyFLg6HqWDshNIekwnQ9G4jus0A_7bexEc,227
35
- erdo/invoke/client.py,sha256=h88WB5OQukt9KKQzGavmp_-A9o5R4ZvA49TffG1Xa2w,7582
36
- erdo/invoke/invoke.py,sha256=yMD65tQiBzrYw_Z_lKdxW2C0lyut9N4n_OmKDp0c6k8,8166
34
+ erdo/invoke/__init__.py,sha256=QhMBmUR8OXuyFS8fbNntjpRd7LNeKlV8Y8x6qSjgQ0I,249
35
+ erdo/invoke/client.py,sha256=bLQNlyRnXpAIJA9SXUn4kOIwWC4wXq5_hmjH262YBzA,8181
36
+ erdo/invoke/invoke.py,sha256=et5U83pV-LAVJD7shejm8o6nHLlgqg2AOVcPp6sqoTE,17689
37
37
  erdo/sync/__init__.py,sha256=f-7-9cYngOiLrTDAng3JxMC86JIE6jFk6-Vjz_dMybs,311
38
38
  erdo/sync/client.py,sha256=LrcyrsapuyrqmuPh-UwbocL7WUhHZ9zkDItXzon_AUs,3022
39
39
  erdo/sync/extractor.py,sha256=iAIlRJqHr4e69XYnir2mZ6F6cUjnXp0P_EKsG1jdDLc,17217
40
40
  erdo/sync/sync.py,sha256=KzuChW5ramY8rX6QM3emRda928A09CQzFLmLJ5PLztg,10421
41
- erdo-0.1.8.dist-info/METADATA,sha256=2gBPUH8bkBcY_RB9ohlnPWAdRqIr7WgSMyHsxgy0KJE,9127
42
- erdo-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- erdo-0.1.8.dist-info/entry_points.txt,sha256=KFGSp8-6IE3-8dSr-3Djqye3IdEY65Y4E8fABoFUCHg,45
44
- erdo-0.1.8.dist-info/licenses/LICENSE,sha256=9pdgUAuBAumY5tewMdJnx2Ozj8dS6gGKsSiY-SVInu4,1034
45
- erdo-0.1.8.dist-info/RECORD,,
41
+ erdo/test/__init__.py,sha256=_OevGagFDTykFcYzA__FARXiS5kgT9tK90r_0TmyGQQ,1114
42
+ erdo/test/evaluate.py,sha256=-6vX6ynW8ICYaoXZLl-xdzAALvx1aSl1CurIRkgSJ0s,7730
43
+ erdo/test/runner.py,sha256=vZR9PLKT2nDMzHyPm_jniGSA9eddGk4p1dQq0h1dRms,7299
44
+ erdo-0.1.10.dist-info/METADATA,sha256=QUWpKqNV_34ZpXZ_dLSChXDzH6pqqWy8WiocGmEa9Qs,11404
45
+ erdo-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
46
+ erdo-0.1.10.dist-info/entry_points.txt,sha256=KFGSp8-6IE3-8dSr-3Djqye3IdEY65Y4E8fABoFUCHg,45
47
+ erdo-0.1.10.dist-info/licenses/LICENSE,sha256=9pdgUAuBAumY5tewMdJnx2Ozj8dS6gGKsSiY-SVInu4,1034
48
+ erdo-0.1.10.dist-info/RECORD,,
erdo/py.typed DELETED
@@ -1 +0,0 @@
1
-
File without changes