planar 0.10.0__py3-none-any.whl → 0.11.0__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.
Files changed (60) hide show
  1. planar/app.py +18 -6
  2. planar/routers/info.py +79 -36
  3. planar/scaffold_templates/pyproject.toml.j2 +1 -1
  4. planar/testing/fixtures.py +7 -4
  5. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/METADATA +9 -1
  6. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/RECORD +8 -60
  7. planar/ai/test_agent_serialization.py +0 -229
  8. planar/ai/test_agent_tool_step_display.py +0 -78
  9. planar/data/test_dataset.py +0 -358
  10. planar/files/storage/test_azure_blob.py +0 -435
  11. planar/files/storage/test_local_directory.py +0 -162
  12. planar/files/storage/test_s3.py +0 -299
  13. planar/files/test_files.py +0 -282
  14. planar/human/test_human.py +0 -385
  15. planar/logging/test_formatter.py +0 -327
  16. planar/modeling/mixins/test_auditable.py +0 -97
  17. planar/modeling/mixins/test_timestamp.py +0 -134
  18. planar/modeling/mixins/test_uuid_primary_key.py +0 -52
  19. planar/routers/test_agents_router.py +0 -174
  20. planar/routers/test_dataset_router.py +0 -429
  21. planar/routers/test_files_router.py +0 -49
  22. planar/routers/test_object_config_router.py +0 -367
  23. planar/routers/test_routes_security.py +0 -168
  24. planar/routers/test_rule_router.py +0 -470
  25. planar/routers/test_workflow_router.py +0 -564
  26. planar/rules/test_data/account_dormancy_management.json +0 -223
  27. planar/rules/test_data/airline_loyalty_points_calculator.json +0 -262
  28. planar/rules/test_data/applicant_risk_assessment.json +0 -435
  29. planar/rules/test_data/booking_fraud_detection.json +0 -407
  30. planar/rules/test_data/cellular_data_rollover_system.json +0 -258
  31. planar/rules/test_data/clinical_trial_eligibility_screener.json +0 -437
  32. planar/rules/test_data/customer_lifetime_value.json +0 -143
  33. planar/rules/test_data/import_duties_calculator.json +0 -289
  34. planar/rules/test_data/insurance_prior_authorization.json +0 -443
  35. planar/rules/test_data/online_check_in_eligibility_system.json +0 -254
  36. planar/rules/test_data/order_consolidation_system.json +0 -375
  37. planar/rules/test_data/portfolio_risk_monitor.json +0 -471
  38. planar/rules/test_data/supply_chain_risk.json +0 -253
  39. planar/rules/test_data/warehouse_cross_docking.json +0 -237
  40. planar/rules/test_rules.py +0 -1494
  41. planar/security/tests/test_auth_middleware.py +0 -162
  42. planar/security/tests/test_authorization_context.py +0 -78
  43. planar/security/tests/test_cedar_basics.py +0 -41
  44. planar/security/tests/test_cedar_policies.py +0 -158
  45. planar/security/tests/test_jwt_principal_context.py +0 -179
  46. planar/test_app.py +0 -142
  47. planar/test_cli.py +0 -394
  48. planar/test_config.py +0 -515
  49. planar/test_object_config.py +0 -527
  50. planar/test_object_registry.py +0 -14
  51. planar/test_sqlalchemy.py +0 -193
  52. planar/test_utils.py +0 -105
  53. planar/testing/test_memory_storage.py +0 -143
  54. planar/workflows/test_concurrency_detection.py +0 -120
  55. planar/workflows/test_lock_timeout.py +0 -140
  56. planar/workflows/test_serialization.py +0 -1203
  57. planar/workflows/test_suspend_deserialization.py +0 -231
  58. planar/workflows/test_workflow.py +0 -2005
  59. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/WHEEL +0 -0
  60. {planar-0.10.0.dist-info → planar-0.11.0.dist-info}/entry_points.txt +0 -0
@@ -1,470 +0,0 @@
1
- from decimal import Decimal
2
-
3
- import pytest
4
- from pydantic import BaseModel
5
-
6
- from planar.app import PlanarApp
7
- from planar.config import sqlite_config
8
- from planar.rules import rule
9
- from planar.testing.planar_test_client import PlanarTestClient
10
-
11
-
12
- class ExpenseRuleInput(BaseModel):
13
- title: str
14
- amount: float
15
- description: str
16
- status: str
17
- category: str
18
-
19
-
20
- class RuleOutput(BaseModel):
21
- reason: str
22
- approved: bool
23
-
24
-
25
- class TransactionVolumeRow(BaseModel):
26
- period: str
27
- country: str
28
- currency: str
29
- completed_count: int
30
- rejected_count: int
31
-
32
-
33
- class TransactionVolume(BaseModel):
34
- rows: list[TransactionVolumeRow]
35
- total_completed_count: int
36
- total_rejected_count: int
37
-
38
-
39
- class PricingInput(BaseModel):
40
- rows: list[TransactionVolumeRow]
41
-
42
-
43
- class TransactionPricingLine(TransactionVolumeRow):
44
- completed_price_per_transaction_usd: Decimal
45
- rejected_price_per_transaction_usd: Decimal
46
-
47
-
48
- class PricingRuleOutput(BaseModel):
49
- line_items: list[TransactionPricingLine]
50
-
51
-
52
- class PricingRuleOutputWrongType(BaseModel):
53
- line_items: list[TransactionPricingLine]
54
- some_other_field: str
55
-
56
-
57
- @rule(description="Complex business rule")
58
- def complex_business_rule(input: ExpenseRuleInput) -> RuleOutput:
59
- """
60
- A complex business rule that determines if the expense should be approved
61
- """
62
- # input and output must be json serializable objects for the zen / gorules lib to work
63
- return RuleOutput(reason="The widgets look fantastic", approved=True)
64
-
65
-
66
- @rule(description="Calculates fees based on tiered, total transaction volume.")
67
- def pricing_rule(
68
- input: PricingInput,
69
- ) -> PricingRuleOutput:
70
- """
71
- Calculates fees based on country, currency, and tiered volume.
72
- """
73
- return PricingRuleOutput(line_items=[])
74
-
75
-
76
- @rule(
77
- description="Calculates fees based on tiered, total transaction volume with wrong type"
78
- )
79
- def pricing_rule_with_wrong_type(
80
- input: PricingInput,
81
- ) -> PricingRuleOutputWrongType:
82
- return PricingRuleOutputWrongType(line_items=[], some_other_field="test")
83
-
84
-
85
- @pytest.fixture(name="app")
86
- def app_fixture(tmp_db_path: str):
87
- app = PlanarApp(
88
- config=sqlite_config(tmp_db_path),
89
- title="Test app for agent router",
90
- description="Testing agent endpoints",
91
- )
92
-
93
- app.register_rule(complex_business_rule)
94
- app.register_rule(pricing_rule_with_wrong_type)
95
- app.register_rule(pricing_rule)
96
- return app
97
-
98
-
99
- EXPENSE_RULE_JDM = {
100
- "nodes": [
101
- {
102
- "id": "7e51efb8-7463-4775-ad69-180442a34444",
103
- "type": "inputNode",
104
- "name": "Input",
105
- "content": {
106
- "schema": '{"properties": {"title": {"title": "Title", "type": "string"}, "amount": {"title": "Amount", "type": "number"}, "description": {"title": "Description", "type": "string"}, "status": {"title": "Status", "type": "string"}, "category": {"title": "Category", "type": "string"}}, "required": ["title", "amount", "description", "status", "category"], "title": "ExpenseRuleInput", "type": "object"}'
107
- },
108
- "position": {"x": 100, "y": 100},
109
- },
110
- {
111
- "id": "abf8c265-da42-4b81-b7bf-349d3e248294",
112
- "type": "decisionTableNode",
113
- "name": "decisionTable1",
114
- "content": {
115
- "hitPolicy": "first",
116
- "rules": [
117
- {
118
- "_id": "9fc59e78-58be-412b-9d2b-79c22bcfefe4",
119
- "2a5ac809-24db-431b-a228-7bc318cd0a3f": "",
120
- "25f2e0b6-c02b-43a2-a9cd-d40e5c1ca709": '"default value"',
121
- "4b09e532-bb42-453b-9c00-26af89f70a03": "true",
122
- }
123
- ],
124
- "inputs": [
125
- {
126
- "id": "2a5ac809-24db-431b-a228-7bc318cd0a3f",
127
- "name": "Input",
128
- "field": "",
129
- }
130
- ],
131
- "outputs": [
132
- {
133
- "id": "25f2e0b6-c02b-43a2-a9cd-d40e5c1ca709",
134
- "field": "reason",
135
- "name": "reason",
136
- },
137
- {
138
- "id": "4b09e532-bb42-453b-9c00-26af89f70a03",
139
- "field": "approved",
140
- "name": "approved",
141
- },
142
- ],
143
- "passThrough": True,
144
- "passThorough": False,
145
- "inputField": None,
146
- "outputPath": None,
147
- "executionMode": "single",
148
- },
149
- "position": {"x": 405, "y": 120},
150
- },
151
- {
152
- "id": "40abc689-6e0e-40ee-bc76-df51065e6ff5",
153
- "type": "outputNode",
154
- "name": "Output",
155
- "content": {
156
- "schema": '{"properties": {"reason": {"title": "Reason", "type": "string"}, "approved": {"title": "Approved", "type": "boolean"}}, "required": ["reason", "approved"], "title": "RuleOutput", "type": "object"}'
157
- },
158
- "position": {"x": 885, "y": 130},
159
- },
160
- {
161
- "id": "a5385f35-5ba7-4cbf-a5b8-f87bca6fd95c",
162
- "type": "expressionNode",
163
- "name": "expression1",
164
- "content": {
165
- "expressions": [],
166
- "passThrough": True,
167
- "inputField": None,
168
- "outputPath": None,
169
- "executionMode": "single",
170
- },
171
- "position": {"x": 590, "y": 350},
172
- },
173
- {
174
- "id": "0689381e-0650-4ba5-b4ba-1a1800f035ca",
175
- "type": "decisionTableNode",
176
- "name": "decisionTable2",
177
- "content": {
178
- "hitPolicy": "first",
179
- "rules": [],
180
- "inputs": [
181
- {
182
- "id": "4caf4578-a643-4a3c-bc6e-2bdf8559e601",
183
- "name": "Input",
184
- "field": "",
185
- }
186
- ],
187
- "outputs": [
188
- {
189
- "id": "4ec07a83-70ca-46ec-aee7-331c44a8da76",
190
- "field": "output",
191
- "name": "Output",
192
- }
193
- ],
194
- "passThrough": True,
195
- "passThorough": None,
196
- "inputField": None,
197
- "outputPath": None,
198
- "executionMode": "single",
199
- },
200
- "position": {"x": 885, "y": 500},
201
- },
202
- ],
203
- "edges": [
204
- {
205
- "id": "cd19ba68-3f39-4b50-8014-85f01258fbe3",
206
- "type": "edge",
207
- "sourceId": "7e51efb8-7463-4775-ad69-180442a34444",
208
- "targetId": "abf8c265-da42-4b81-b7bf-349d3e248294",
209
- },
210
- {
211
- "id": "7c26024c-0f02-4393-8cf0-0f5097cd21d0",
212
- "type": "edge",
213
- "sourceId": "abf8c265-da42-4b81-b7bf-349d3e248294",
214
- "targetId": "40abc689-6e0e-40ee-bc76-df51065e6ff5",
215
- },
216
- {
217
- "id": "66d1a27e-2cf2-4b2e-862d-b65dc554c320",
218
- "type": "edge",
219
- "sourceId": "abf8c265-da42-4b81-b7bf-349d3e248294",
220
- "targetId": "a5385f35-5ba7-4cbf-a5b8-f87bca6fd95c",
221
- },
222
- {
223
- "id": "abf1329e-c27d-4655-b1a7-d410ea03b998",
224
- "type": "edge",
225
- "sourceId": "a5385f35-5ba7-4cbf-a5b8-f87bca6fd95c",
226
- "targetId": "40abc689-6e0e-40ee-bc76-df51065e6ff5",
227
- },
228
- {
229
- "id": "d56e19e6-2303-4272-b7df-49e3df75c62f",
230
- "type": "edge",
231
- "sourceId": "a5385f35-5ba7-4cbf-a5b8-f87bca6fd95c",
232
- "targetId": "0689381e-0650-4ba5-b4ba-1a1800f035ca",
233
- },
234
- ],
235
- }
236
-
237
-
238
- async def test_save_rule_endpoints(client: PlanarTestClient, app: PlanarApp):
239
- response = await client.get("/planar/v1/rules/complex_business_rule")
240
-
241
- assert response.status_code == 200
242
-
243
- data = response.json()
244
-
245
- assert len(data["configs"]) == 1
246
-
247
- # save the rule
248
- response = await client.post(
249
- "/planar/v1/rules/complex_business_rule", json=EXPENSE_RULE_JDM
250
- )
251
-
252
- assert response.status_code == 200, response.text
253
-
254
- data = response.json()
255
-
256
- assert len(data["configs"]) == 2
257
-
258
-
259
- PRICING_RULE_JDM = {
260
- "nodes": [
261
- {
262
- "id": "6cc036d3-3350-449e-9b2c-1569b8f86ffc",
263
- "type": "inputNode",
264
- "name": "Input",
265
- "content": {
266
- "schema": '{"$defs": {"TransactionVolumeRow": {"properties": {"period": {"title": "Period", "type": "string"}, "country": {"title": "Country", "type": "string"}, "currency": {"title": "Currency", "type": "string"}, "completed_count": {"title": "Completed Count", "type": "integer"}, "rejected_count": {"title": "Rejected Count", "type": "integer"}}, "required": ["period", "country", "currency", "completed_count", "rejected_count"], "title": "TransactionVolumeRow", "type": "object"}}, "properties": {"rows": {"items": {"$ref": "#/$defs/TransactionVolumeRow"}, "title": "Rows", "type": "array"}}, "required": ["rows"], "title": "PricingInput", "type": "object"}'
267
- },
268
- "position": {"x": 100, "y": 100},
269
- },
270
- {
271
- "id": "3921e9d3-02e3-4a72-b74d-037c80f97eaa",
272
- "type": "decisionTableNode",
273
- "name": "decisionTable1",
274
- "content": {
275
- "hitPolicy": "first",
276
- "rules": [
277
- {
278
- "_id": "15d4429c-39bc-448d-a7f4-187eaea4493a",
279
- "e5688083-30b9-449e-adaf-bf8ff69eb2ac": '"ARS"',
280
- "42c29309-9aa4-4441-bd1a-b1b57d1b628e": "<= 6000",
281
- "71b9d121-5b37-4b0b-b4c2-d29a868fed35": '"Argentina"',
282
- "662e29e1-d0b8-4cf4-a443-3e92f2157054": "100",
283
- "_description": "",
284
- },
285
- {
286
- "_id": "9ef47884-004d-4314-a61c-38778ed7b7d7",
287
- "e5688083-30b9-449e-adaf-bf8ff69eb2ac": "",
288
- "42c29309-9aa4-4441-bd1a-b1b57d1b628e": "",
289
- "71b9d121-5b37-4b0b-b4c2-d29a868fed35": "",
290
- "662e29e1-d0b8-4cf4-a443-3e92f2157054": "1.00",
291
- },
292
- ],
293
- "inputs": [
294
- {
295
- "id": "e5688083-30b9-449e-adaf-bf8ff69eb2ac",
296
- "name": "Currency",
297
- "field": "currency",
298
- },
299
- {
300
- "id": "42c29309-9aa4-4441-bd1a-b1b57d1b628e",
301
- "name": "Completed Count",
302
- "field": "completed_count",
303
- },
304
- {
305
- "id": "71b9d121-5b37-4b0b-b4c2-d29a868fed35",
306
- "name": "Country",
307
- "field": "country",
308
- },
309
- ],
310
- "outputs": [
311
- {
312
- "id": "662e29e1-d0b8-4cf4-a443-3e92f2157054",
313
- "field": "completed_price_per_transaction_usd",
314
- "name": "Completed Price Per Transaction (USD)",
315
- }
316
- ],
317
- "passThrough": True,
318
- "passThorough": None,
319
- "inputField": "rows",
320
- "outputPath": "line_items",
321
- "executionMode": "loop",
322
- },
323
- "position": {"x": 350, "y": 95},
324
- },
325
- {
326
- "id": "a9a82683-5dbb-4eed-8326-83dea36c1d53",
327
- "type": "outputNode",
328
- "name": "Output",
329
- "content": {
330
- "schema": '{"$defs": {"TransactionPricingLine": {"properties": {"period": {"title": "Period", "type": "string"}, "country": {"title": "Country", "type": "string"}, "currency": {"title": "Currency", "type": "string"}, "completed_count": {"title": "Completed Count", "type": "integer"}, "rejected_count": {"title": "Rejected Count", "type": "integer"}, "completed_price_per_transaction_usd": {"title": "Completed Price Per Transaction Usd", "type": "number"}, "rejected_price_per_transaction_usd": {"title": "Rejected Price Per Transaction Usd", "type": "number"}}, "required": ["period", "country", "currency", "completed_count", "rejected_count", "completed_price_per_transaction_usd", "rejected_price_per_transaction_usd"], "title": "TransactionPricingLine", "type": "object"}}, "properties": {"line_items": {"items": {"$ref": "#/$defs/TransactionPricingLine"}, "title": "Line Items", "type": "array"}}, "required": ["line_items"], "title": "PricingRuleOutput", "type": "object"}'
331
- },
332
- "position": {"x": 1195, "y": 60},
333
- },
334
- {
335
- "id": "b384c91d-dbc3-4043-a0f0-a3adef9ac340",
336
- "type": "expressionNode",
337
- "name": "expression1",
338
- "content": {
339
- "expressions": [
340
- {
341
- "id": "b0e3f514-c109-43e4-91ad-3007110d0a35",
342
- "key": "rejected_price_per_transaction_usd",
343
- "value": "200",
344
- }
345
- ],
346
- "passThrough": True,
347
- "inputField": "line_items",
348
- "outputPath": "line_items",
349
- "executionMode": "loop",
350
- },
351
- "position": {"x": 670, "y": 100},
352
- },
353
- {
354
- "id": "b89b13b8-526f-4db2-a524-faeeca0e78d7",
355
- "type": "expressionNode",
356
- "name": "expression2",
357
- "content": {
358
- "expressions": [
359
- {
360
- "id": "b6a9570b-7ea1-4ebb-b5eb-b693fe14ca47",
361
- "key": "line_items",
362
- "value": "line_items",
363
- }
364
- ],
365
- "passThrough": False,
366
- "inputField": None,
367
- "outputPath": None,
368
- "executionMode": "single",
369
- },
370
- "position": {"x": 950, "y": 100},
371
- },
372
- ],
373
- "edges": [
374
- {
375
- "id": "a9c02fbb-3ad6-4f65-a718-16c0e02d7551",
376
- "type": "edge",
377
- "sourceId": "6cc036d3-3350-449e-9b2c-1569b8f86ffc",
378
- "targetId": "3921e9d3-02e3-4a72-b74d-037c80f97eaa",
379
- },
380
- {
381
- "id": "c2959035-5f0d-4317-8ccf-2f885450b669",
382
- "type": "edge",
383
- "sourceId": "3921e9d3-02e3-4a72-b74d-037c80f97eaa",
384
- "targetId": "b384c91d-dbc3-4043-a0f0-a3adef9ac340",
385
- },
386
- {
387
- "id": "6513b9bf-7016-4776-bf84-81418caf7b74",
388
- "type": "edge",
389
- "sourceId": "b384c91d-dbc3-4043-a0f0-a3adef9ac340",
390
- "targetId": "b89b13b8-526f-4db2-a524-faeeca0e78d7",
391
- },
392
- {
393
- "id": "b75e295e-8fb4-45b6-9040-06fdd8d6723e",
394
- "type": "edge",
395
- "sourceId": "b89b13b8-526f-4db2-a524-faeeca0e78d7",
396
- "targetId": "a9a82683-5dbb-4eed-8326-83dea36c1d53",
397
- },
398
- ],
399
- }
400
-
401
-
402
- async def test_save_rule_endpoints_with_jdm(client: PlanarTestClient, app: PlanarApp):
403
- response = await client.get("/planar/v1/rules/pricing_rule")
404
-
405
- assert response.status_code == 200
406
-
407
- data = response.json()
408
-
409
- assert len(data["configs"]) == 1
410
-
411
- # save the rule
412
- response = await client.post("/planar/v1/rules/pricing_rule", json=PRICING_RULE_JDM)
413
-
414
- assert response.status_code == 200
415
-
416
- data = response.json()
417
-
418
- assert len(data["configs"]) == 2
419
-
420
-
421
- async def test_save_rule_endpoints_with_jdm_wrong_type(
422
- client: PlanarTestClient, app: PlanarApp
423
- ):
424
- response = await client.get("/planar/v1/rules/pricing_rule_with_wrong_type")
425
-
426
- assert response.status_code == 200
427
-
428
- data = response.json()
429
-
430
- assert len(data["configs"]) == 1
431
-
432
- # save the rule
433
- response = await client.post(
434
- "/planar/v1/rules/pricing_rule_with_wrong_type",
435
- json=PRICING_RULE_JDM,
436
- )
437
-
438
- assert response.status_code == 400
439
- response_json = response.json()
440
- assert response_json["detail"]["error"] == "ValidationError"
441
- assert response_json["detail"]["object_name"] == "pricing_rule_with_wrong_type"
442
- assert response_json["detail"]["object_type"] == "rule"
443
- assert response_json["detail"]["diagnostics"]["is_valid"] is False
444
- assert (
445
- response_json["detail"]["diagnostics"]["suggested_fix"]["jdm"]["nodes"][0][
446
- "content"
447
- ]["schema"]
448
- == PRICING_RULE_JDM["nodes"][0]["content"]["schema"]
449
- )
450
- # check contains some_other_field
451
- assert (
452
- "some_other_field"
453
- in response_json["detail"]["diagnostics"]["suggested_fix"]["jdm"]["nodes"][2][
454
- "content"
455
- ]["schema"]
456
- )
457
-
458
- assert response_json["detail"]["diagnostics"]["issues"] == [
459
- {
460
- "error_code": "MISSING_FIELD",
461
- "field_path": "some_other_field",
462
- "message": "Field 'some_other_field' is missing in current node",
463
- "reference_value": {
464
- "title": "Some Other Field",
465
- "type": "string",
466
- },
467
- "current_value": None,
468
- "for_object": "outputNode",
469
- }
470
- ]