soprano-sdk 0.2.1__tar.gz → 0.2.2__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.
Files changed (105) hide show
  1. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/PKG-INFO +1 -1
  2. soprano_sdk-0.2.2/examples/concert_booking/README.md +108 -0
  3. soprano_sdk-0.2.2/examples/concert_booking/TEST_RESULTS.md +179 -0
  4. soprano_sdk-0.2.2/examples/concert_booking/__init__.py +6 -0
  5. soprano_sdk-0.2.2/examples/concert_booking/booking_helpers.py +88 -0
  6. soprano_sdk-0.2.2/examples/concert_booking/concert_ticket_booking.yaml +244 -0
  7. soprano_sdk-0.2.2/examples/debit_card_block.yaml +608 -0
  8. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/pyproject.toml +1 -1
  9. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/authenticators/mfa.py +8 -7
  10. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/core/engine.py +26 -11
  11. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/nodes/collect_input.py +5 -1
  12. soprano_sdk-0.2.2/tests/test_mfa_error_handling.py +778 -0
  13. soprano_sdk-0.2.2/tests/test_mfa_scenarios.py +427 -0
  14. soprano_sdk-0.2.2/tests/test_mfa_validation.py +545 -0
  15. soprano_sdk-0.2.2/uv.lock +5165 -0
  16. soprano_sdk-0.2.1/uv.lock +0 -5163
  17. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/.github/workflows/test_build_and_publish.yaml +0 -0
  18. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/.gitignore +0 -0
  19. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/.python-version +0 -0
  20. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/CLAUDE.md +0 -0
  21. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/LICENSE +0 -0
  22. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/README.md +0 -0
  23. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/framework_example.yaml +0 -0
  24. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/greeting_functions.py +0 -0
  25. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/greeting_workflow.yaml +0 -0
  26. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/main.py +0 -0
  27. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/persistence/README.md +0 -0
  28. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/persistence/conversation_based.py +0 -0
  29. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/persistence/entity_based.py +0 -0
  30. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/persistence/mongodb_demo.py +0 -0
  31. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/return_functions.py +0 -0
  32. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/return_workflow.yaml +0 -0
  33. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/structured_output_example.yaml +0 -0
  34. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/README.md +0 -0
  35. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/crewai_supervisor_ui.py +0 -0
  36. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/langgraph_supervisor_ui.py +0 -0
  37. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/tools/__init__.py +0 -0
  38. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/tools/crewai_tools.py +0 -0
  39. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/tools/langgraph_tools.py +0 -0
  40. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/supervisors/workflow_tools.py +0 -0
  41. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/tools/__init__.py +0 -0
  42. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/tools/address.py +0 -0
  43. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/examples/validator.py +0 -0
  44. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/langgraph_demo.py +0 -0
  45. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/langgraph_selfloop_demo.py +0 -0
  46. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/langgraph_v.py +0 -0
  47. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/main.py +0 -0
  48. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/return_fsm.excalidraw +0 -0
  49. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/return_state_machine.png +0 -0
  50. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/legacy/ui.py +0 -0
  51. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/scripts/visualize_workflow.py +0 -0
  52. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/scripts/workflow_demo.py +0 -0
  53. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/scripts/workflow_demo_ui.py +0 -0
  54. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/__init__.py +0 -0
  55. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/agents/__init__.py +0 -0
  56. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/agents/adaptor.py +0 -0
  57. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/agents/factory.py +0 -0
  58. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/agents/structured_output.py +0 -0
  59. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/authenticators/__init__.py +0 -0
  60. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/core/__init__.py +0 -0
  61. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/core/constants.py +0 -0
  62. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/core/rollback_strategies.py +0 -0
  63. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/core/state.py +0 -0
  64. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/engine.py +0 -0
  65. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/nodes/__init__.py +0 -0
  66. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/nodes/base.py +0 -0
  67. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/nodes/call_function.py +0 -0
  68. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/nodes/factory.py +0 -0
  69. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/routing/__init__.py +0 -0
  70. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/routing/router.py +0 -0
  71. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/tools.py +0 -0
  72. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/__init__.py +0 -0
  73. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/function.py +0 -0
  74. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/logger.py +0 -0
  75. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/template.py +0 -0
  76. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/tool.py +0 -0
  77. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/utils/tracing.py +0 -0
  78. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/validation/__init__.py +0 -0
  79. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/validation/schema.py +0 -0
  80. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/soprano_sdk/validation/validator.py +0 -0
  81. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/debug_jinja2.py +0 -0
  82. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_agent_factory.py +0 -0
  83. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_collect_input_refactor.py +0 -0
  84. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_external_values.py +0 -0
  85. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_inputs_validation.py +0 -0
  86. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_jinja2_path.py +0 -0
  87. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_jinja2_standalone.py +0 -0
  88. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_persistence.py +0 -0
  89. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_structured_output.py +0 -0
  90. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/tests/test_transition_routing.py +0 -0
  91. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/todo.md +0 -0
  92. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/.eslintrc.cjs +0 -0
  93. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/.gitignore +0 -0
  94. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/README.md +0 -0
  95. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/index.html +0 -0
  96. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/package-lock.json +0 -0
  97. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/package.json +0 -0
  98. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/App.jsx +0 -0
  99. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/CustomNode.jsx +0 -0
  100. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/StepDetailsModal.jsx +0 -0
  101. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/WorkflowGraph.jsx +0 -0
  102. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/WorkflowInfoPanel.jsx +0 -0
  103. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/assets/react.svg +0 -0
  104. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/src/main.jsx +0 -0
  105. {soprano_sdk-0.2.1 → soprano_sdk-0.2.2}/workflow-visualizer/vite.config.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soprano-sdk
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: YAML-driven workflow engine with AI agent integration for building conversational SOPs
5
5
  Author: Arvind Thangamani
6
6
  License: MIT
@@ -0,0 +1,108 @@
1
+ # Concert Ticket Booking - MFA Example
2
+
3
+ This example demonstrates a concert ticket booking workflow with Multi-Factor Authentication (MFA) protection on critical operations.
4
+
5
+ ## Overview
6
+
7
+ The workflow allows users to book concert tickets through a conversational interface with the following features:
8
+
9
+ - **Customer name collection**
10
+ - **Concert selection**
11
+ - **Seat preference selection** (VIP, Premium, General)
12
+ - **Ticket quantity selection**
13
+ - **Seat availability checking**
14
+ - **Payment processing** (MFA-protected)
15
+ - **Booking confirmation** (MFA-protected)
16
+ - **Booking modification** (with loop-back to MFA)
17
+
18
+ ## MFA-Protected Steps
19
+
20
+ Two critical operations are protected with MFA:
21
+
22
+ 1. **`process_payment`** - Payment transaction processing
23
+ 2. **`send_confirmation`** - Sending booking confirmation
24
+
25
+ ## Key Features for MFA Testing
26
+
27
+ ### 1. Forward MFA Flow
28
+ Regular workflow progression through MFA-protected steps:
29
+ ```
30
+ collect_seat_preference → check_seat_availability → process_payment (MFA) → send_confirmation (MFA)
31
+ ```
32
+
33
+ ### 2. Loop Back Scenario
34
+ User can modify booking AFTER completion, which loops back through MFA:
35
+ ```
36
+ ask_modification_needed → collect_seat_preference → ... → process_payment (MFA)
37
+ ```
38
+
39
+ This tests that steps AFTER MFA-protected nodes can correctly reference them.
40
+
41
+ ### 3. Multiple MFA Steps
42
+ Sequential MFA-protected operations:
43
+ ```
44
+ process_payment (MFA) → send_confirmation (MFA)
45
+ ```
46
+
47
+ ### 4. Alternative Paths
48
+ When seats are unavailable, alternative paths also go through MFA:
49
+ ```
50
+ offer_alternative_seats → check_seat_availability → process_payment (MFA)
51
+ ```
52
+
53
+ ## Files
54
+
55
+ - **`concert_ticket_booking.yaml`** - Workflow definition with MFA configuration
56
+ - **`booking_helpers.py`** - Helper functions for workflow logic
57
+ - **`README.md`** - This file
58
+
59
+ ## MFA Configuration Example
60
+
61
+ ```yaml
62
+ - id: process_payment
63
+ description: Process payment transaction with MFA
64
+ action: call_function
65
+ function: booking_helpers.process_payment
66
+ output: payment_status
67
+ mfa:
68
+ model: gpt-4o-mini
69
+ type: REST
70
+ payload:
71
+ transactionType: CONCERT_TICKET_PAYMENT
72
+ businessKey:
73
+ customerName: "{{customer_name}}"
74
+ concertName: "{{concert_name}}"
75
+ seatPreference: "{{seat_preference}}"
76
+ ticketQuantity: "{{ticket_quantity}}"
77
+ ```
78
+
79
+ ## Testing
80
+
81
+ Run the comprehensive MFA test suite:
82
+
83
+ ```bash
84
+ python tests/test_mfa_scenarios.py
85
+ ```
86
+
87
+ The test suite includes:
88
+
89
+ 1. **MFA Node Creation** - Verifies MFA nodes are properly inserted
90
+ 2. **Previous Step Redirection** - Tests forward flow through MFA
91
+ 3. **Loop Back Redirection** - Tests backward references to MFA steps (critical)
92
+ 4. **Multiple MFA Steps** - Tests sequential MFA operations
93
+ 5. **MFA Data Fields** - Validates data schema updates
94
+ 6. **Alternative Paths** - Tests multiple paths to MFA steps
95
+ 7. **Comprehensive Flow** - End-to-end validation
96
+
97
+ ## Pricing
98
+
99
+ - **VIP**: ₹5,000 per ticket
100
+ - **Premium**: ₹3,000 per ticket
101
+ - **General**: ₹1,500 per ticket
102
+
103
+ ## Notes
104
+
105
+ - Maximum 8 tickets per booking
106
+ - Seat availability is simulated (80% success rate)
107
+ - Payment processing is simulated (90% success rate)
108
+ - Confirmation sending is simulated (95% success rate)
@@ -0,0 +1,179 @@
1
+ # MFA Test Results - Concert Ticket Booking
2
+
3
+ ## Test Execution Summary
4
+
5
+ **Date**: 2026-01-05
6
+ **Test Suite**: `tests/test_mfa_scenarios.py`
7
+ **Status**: ✅ **ALL TESTS PASSED** (7/7)
8
+
9
+ ## Test Cases
10
+
11
+ ### ✅ Test 1: MFA Node Creation
12
+ **Status**: PASSED
13
+ **Purpose**: Verify MFA nodes are properly created and injected into the workflow
14
+
15
+ **Validations**:
16
+ - MFA start nodes are inserted before MFA-protected steps
17
+ - MFA validate nodes are created for user input collection
18
+ - Original steps remain intact after MFA injection
19
+ - 2 MFA-protected steps → 4 MFA nodes created (2 per protected step)
20
+
21
+ ---
22
+
23
+ ### ✅ Test 2: Previous Step Redirection (Forward Flow)
24
+ **Status**: PASSED
25
+ **Purpose**: Verify steps BEFORE MFA-protected steps redirect correctly
26
+
27
+ **Validations**:
28
+ - `check_seat_availability` → `process_payment` becomes `check_seat_availability` → `process_payment_mfa_start`
29
+ - `process_payment` → `send_confirmation` becomes `process_payment` → `send_confirmation_mfa_start`
30
+ - All forward transitions to MFA-protected steps go through MFA authentication
31
+
32
+ ---
33
+
34
+ ### ✅ Test 3: Loop Back Redirection (CRITICAL BUG FIX TEST)
35
+ **Status**: PASSED
36
+ **Purpose**: Verify steps AFTER MFA-protected steps that loop back redirect correctly
37
+
38
+ **Critical Scenario**:
39
+ ```
40
+ User Flow:
41
+ 1. Complete booking (goes through MFA for payment & confirmation)
42
+ 2. Request modification (ask_modification_needed step)
43
+ 3. Change seat preference (loops back to collect_seat_preference)
44
+ 4. Re-process payment (must go through MFA again)
45
+ ```
46
+
47
+ **Validations**:
48
+ - `ask_modification_needed` comes AFTER MFA-protected steps
49
+ - Loop back to `collect_seat_preference` is preserved
50
+ - Path from `collect_seat_preference` eventually reaches `process_payment_mfa_start`
51
+ - No MFA bypass - modification requires re-authentication
52
+
53
+ **This test validates the fix implemented in** `soprano_sdk/core/engine.py:202-246`
54
+
55
+ ---
56
+
57
+ ### ✅ Test 4: Multiple MFA Steps in Sequence
58
+ **Status**: PASSED
59
+ **Purpose**: Verify multiple consecutive MFA-protected steps work correctly
60
+
61
+ **Scenario**:
62
+ ```
63
+ process_payment (MFA) → send_confirmation (MFA)
64
+ ```
65
+
66
+ **Validations**:
67
+ - Each MFA step has independent start and validate nodes
68
+ - Flow between MFA steps is preserved
69
+ - Both MFA processes execute sequentially without interference
70
+
71
+ ---
72
+
73
+ ### ✅ Test 5: MFA Data Fields Creation
74
+ **Status**: PASSED
75
+ **Purpose**: Verify MFA-specific data fields are automatically created
76
+
77
+ **Validations**:
78
+ - Data fields for MFA validate nodes are added to workflow schema
79
+ - Field names match the MFA validate node field names
80
+ - Data schema properly includes MFA authentication fields
81
+
82
+ ---
83
+
84
+ ### ✅ Test 6: Alternative Paths to MFA Steps
85
+ **Status**: PASSED
86
+ **Purpose**: Verify alternative/conditional paths to MFA steps are redirected
87
+
88
+ **Scenario**:
89
+ ```
90
+ offer_alternative_seats → check_seat_availability → process_payment (MFA)
91
+ ```
92
+
93
+ **Validations**:
94
+ - Alternative path (when seats unavailable) also goes through MFA
95
+ - Multiple entry points to MFA-protected steps all redirect correctly
96
+ - No bypass routes exist for MFA-protected operations
97
+
98
+ ---
99
+
100
+ ### ✅ Test 7: Comprehensive MFA Flow Validation
101
+ **Status**: PASSED
102
+ **Purpose**: End-to-end validation of entire workflow structure
103
+
104
+ **Validations**:
105
+ - All 15 workflow steps validated
106
+ - No step directly references MFA-protected steps (all go through MFA start)
107
+ - No MFA bypass violations found
108
+ - Complete workflow integrity maintained
109
+
110
+ **Workflow Statistics**:
111
+ - Total steps: 15
112
+ - MFA-protected steps: 2
113
+ - MFA nodes created: 4
114
+ - Regular steps: 11
115
+
116
+ ---
117
+
118
+ ## Technical Details
119
+
120
+ ### MFA Implementation
121
+ The framework automatically injects MFA authentication nodes for steps marked with `mfa` configuration:
122
+
123
+ ```yaml
124
+ - id: process_payment
125
+ action: call_function
126
+ function: booking_helpers.process_payment
127
+ mfa:
128
+ model: gpt-4o-mini
129
+ type: REST
130
+ payload:
131
+ transactionType: CONCERT_TICKET_PAYMENT
132
+ ```
133
+
134
+ ### Node Injection Pattern
135
+ For each MFA-protected step `X`, the framework creates:
136
+ 1. `X_mfa_start` - Initiates MFA authentication flow
137
+ 2. `X_mfa_validate` - Collects and validates user authentication input
138
+ 3. Original step `X` - Executes only after successful MFA
139
+
140
+ ### Bug Fix Validation
141
+ The critical test (Test 3) validates the fix in `core/engine.py` where:
142
+ - **Before**: Only steps BEFORE MFA-protected steps were updated
143
+ - **After**: ALL steps (both before AND after) that reference MFA-protected steps are updated
144
+
145
+ This ensures loop-back scenarios (like booking modifications) cannot bypass MFA authentication.
146
+
147
+ ---
148
+
149
+ ## Running the Tests
150
+
151
+ ```bash
152
+ # Using pytest (recommended)
153
+ cd /path/to/soprano_sdk
154
+ uv run pytest tests/test_mfa_scenarios.py -v
155
+
156
+ # Using direct Python execution
157
+ cd /path/to/soprano_sdk
158
+ uv run python tests/test_mfa_scenarios.py
159
+ ```
160
+
161
+ ## Dependencies
162
+
163
+ The tests require:
164
+ - `pytest` for test execution
165
+ - Environment variables for MFA endpoints (set via pytest fixtures)
166
+ - Concert booking workflow YAML and helper functions
167
+
168
+ ---
169
+
170
+ ## Conclusion
171
+
172
+ All comprehensive MFA test cases pass successfully, validating:
173
+ 1. Correct MFA node creation and injection
174
+ 2. Proper redirection of all paths (forward and backward)
175
+ 3. Support for multiple consecutive MFA steps
176
+ 4. Automatic data field management
177
+ 5. Complete workflow integrity with no MFA bypass routes
178
+
179
+ The implementation correctly handles both forward flows and loop-back scenarios, ensuring that MFA protection cannot be circumvented through any workflow path.
@@ -0,0 +1,6 @@
1
+ """
2
+ Concert Ticket Booking Example
3
+
4
+ This example demonstrates MFA (Multi-Factor Authentication) functionality
5
+ in a concert ticket booking workflow.
6
+ """
@@ -0,0 +1,88 @@
1
+ """
2
+ Helper functions for concert ticket booking workflow
3
+ """
4
+ import random
5
+ import uuid
6
+
7
+
8
+ def initialize_prices(state):
9
+ """Initialize ticket prices"""
10
+ return {
11
+ 'vip_price': 5000,
12
+ 'premium_price': 3000,
13
+ 'general_price': 1500
14
+ }
15
+
16
+
17
+ def check_availability(state):
18
+ """Check if requested seats are available"""
19
+ seat_preference = state.get('seat_preference')
20
+ ticket_quantity = state.get('ticket_quantity', 1)
21
+
22
+ # Simulate availability check - 80% chance of success
23
+ available = random.random() > 0.2
24
+
25
+ print(f"Checking availability: {ticket_quantity} x {seat_preference} seats")
26
+ print(f"Result: {'Available' if available else 'Not Available'}")
27
+
28
+ return available
29
+
30
+
31
+ def process_payment(state):
32
+ """Process payment for tickets"""
33
+ customer_name = state.get('customer_name')
34
+ concert_name = state.get('concert_name')
35
+ seat_preference = state.get('seat_preference')
36
+ ticket_quantity = state.get('ticket_quantity', 1)
37
+
38
+ # Calculate total amount
39
+ vip_price = state.get('vip_price', 5000)
40
+ premium_price = state.get('premium_price', 3000)
41
+ general_price = state.get('general_price', 1500)
42
+
43
+ prices = {
44
+ 'VIP': vip_price,
45
+ 'Premium': premium_price,
46
+ 'General': general_price
47
+ }
48
+
49
+ price_per_ticket = prices.get(seat_preference, general_price)
50
+ total_amount = price_per_ticket * ticket_quantity
51
+
52
+ # Simulate payment processing - 90% success rate
53
+ payment_success = random.random() > 0.1
54
+
55
+ print(f"Processing payment for {customer_name}")
56
+ print(f"Concert: {concert_name}")
57
+ print(f"Tickets: {ticket_quantity} x {seat_preference}")
58
+ print(f"Total: ₹{total_amount}")
59
+ print(f"Payment Status: {'Success' if payment_success else 'Failed'}")
60
+
61
+ # Generate booking reference on success
62
+ if payment_success:
63
+ state['booking_reference'] = f"BK{uuid.uuid4().hex[:8].upper()}"
64
+
65
+ return payment_success
66
+
67
+
68
+ def send_confirmation(state):
69
+ """Send booking confirmation"""
70
+ customer_name = state.get('customer_name')
71
+ booking_reference = state.get('booking_reference')
72
+ concert_name = state.get('concert_name')
73
+
74
+ # Simulate sending confirmation - 95% success rate
75
+ confirmation_sent = random.random() > 0.05
76
+
77
+ print(f"Sending confirmation to {customer_name}")
78
+ print(f"Booking Reference: {booking_reference}")
79
+ print(f"Concert: {concert_name}")
80
+ print(f"Status: {'Sent' if confirmation_sent else 'Failed'}")
81
+
82
+ return confirmation_sent
83
+
84
+
85
+ def handle_payment_failure(state):
86
+ """Handle payment failure"""
87
+ print("Payment failed. Cleaning up...")
88
+ return False
@@ -0,0 +1,244 @@
1
+ name: Concert Ticket Booking
2
+ description: Manages concert ticket booking with seat selection, payment processing, and confirmation
3
+ version: '1.0.0'
4
+
5
+ data:
6
+ - name: bearer_token
7
+ type: text
8
+ description: User authentication bearer token for MFA
9
+ - name: customer_name
10
+ type: text
11
+ description: Customer's full name
12
+ - name: concert_name
13
+ type: text
14
+ description: Name of the concert to book
15
+ - name: seat_preference
16
+ type: text
17
+ description: Preferred seating section (VIP, Premium, General)
18
+ - name: ticket_quantity
19
+ type: number
20
+ description: Number of tickets to book
21
+ - name: seat_available
22
+ type: boolean
23
+ description: Whether requested seats are available
24
+ - name: payment_status
25
+ type: boolean
26
+ description: Payment transaction status
27
+ - name: confirmation_sent
28
+ type: boolean
29
+ description: Whether confirmation email was sent
30
+ - name: modification_requested
31
+ type: boolean
32
+ description: Whether customer wants to modify booking
33
+ - name: booking_reference
34
+ type: text
35
+ description: Unique booking reference number
36
+ - name: vip_price
37
+ type: number
38
+ description: VIP ticket price
39
+ - name: premium_price
40
+ type: number
41
+ description: Premium ticket price
42
+ - name: general_price
43
+ type: number
44
+ description: General ticket price
45
+
46
+ steps:
47
+ - id: initialize_pricing
48
+ description: Initialize ticket pricing
49
+ action: call_function
50
+ function: booking_helpers.initialize_prices
51
+ output: vip_price
52
+ next: collect_customer_name
53
+
54
+ - id: collect_customer_name
55
+ description: Collect customer name
56
+ action: collect_input_with_agent
57
+ field: customer_name
58
+ max_attempts: 3
59
+ agent:
60
+ name: Customer Name Collector
61
+ model: gpt-4o-mini
62
+ initial_message: "Welcome to Concert Ticket Booking! Please provide your full name."
63
+ instructions: >
64
+ Extract the customer's full name from their message.
65
+ Return: NAME_CAPTURED: [customer_name] or INVALID_INPUT:
66
+ transitions:
67
+ - next: collect_concert_details
68
+ pattern: "NAME_CAPTURED:"
69
+ - next: booking_cancelled
70
+ pattern: "INVALID_INPUT:"
71
+
72
+ - id: collect_concert_details
73
+ description: Collect concert and seat preferences
74
+ action: collect_input_with_agent
75
+ field: concert_name
76
+ max_attempts: 3
77
+ agent:
78
+ name: Concert Details Collector
79
+ model: gpt-4o-mini
80
+ initial_message: "Hi {{customer_name}}! Which concert would you like to attend? Please specify the concert name."
81
+ instructions: >
82
+ Extract concert name from the user's message.
83
+ Return: CONCERT_CAPTURED: [concert_name] or CANCEL_REQUEST:
84
+ transitions:
85
+ - next: collect_seat_preference
86
+ pattern: "CONCERT_CAPTURED:"
87
+ - next: booking_cancelled
88
+ pattern: "CANCEL_REQUEST:"
89
+
90
+ - id: collect_seat_preference
91
+ description: Collect seating preference
92
+ action: collect_input_with_agent
93
+ field: seat_preference
94
+ max_attempts: 3
95
+ agent:
96
+ name: Seat Preference Collector
97
+ model: gpt-4o-mini
98
+ initial_message: >
99
+ Please choose your seating preference for {{concert_name}}:
100
+ - VIP (₹{{vip_price}})
101
+ - Premium (₹{{premium_price}})
102
+ - General (₹{{general_price}})
103
+ instructions: >
104
+ Identify seating preference (VIP, Premium, or General).
105
+ Return: SEAT_CAPTURED: [seat_preference] or CANCEL_REQUEST:
106
+ transitions:
107
+ - next: collect_ticket_quantity
108
+ pattern: "SEAT_CAPTURED:"
109
+ - next: booking_cancelled
110
+ pattern: "CANCEL_REQUEST:"
111
+
112
+ - id: collect_ticket_quantity
113
+ description: Collect number of tickets
114
+ action: collect_input_with_agent
115
+ field: ticket_quantity
116
+ max_attempts: 3
117
+ agent:
118
+ name: Ticket Quantity Collector
119
+ model: gpt-4o-mini
120
+ initial_message: "How many {{seat_preference}} tickets would you like to book? (Maximum 8 per booking)"
121
+ instructions: >
122
+ Extract the number of tickets (1-8).
123
+ Return: QUANTITY_CAPTURED: [ticket_quantity] or INVALID_QUANTITY:
124
+ transitions:
125
+ - next: check_seat_availability
126
+ pattern: "QUANTITY_CAPTURED:"
127
+ - next: booking_cancelled
128
+ pattern: "INVALID_QUANTITY:"
129
+
130
+ - id: check_seat_availability
131
+ description: Check if requested seats are available
132
+ action: call_function
133
+ function: booking_helpers.check_availability
134
+ output: seat_available
135
+ transitions:
136
+ - next: process_payment
137
+ condition: true
138
+ - next: offer_alternative_seats
139
+ condition: false
140
+
141
+ - id: offer_alternative_seats
142
+ description: Offer alternative seat options
143
+ action: collect_input_with_agent
144
+ field: seat_preference
145
+ max_attempts: 3
146
+ agent:
147
+ name: Alternative Seat Offer
148
+ model: gpt-4o-mini
149
+ initial_message: >
150
+ Sorry {{customer_name}}, {{seat_preference}} seats are not available for {{concert_name}}.
151
+ Would you like to try a different section?
152
+ instructions: >
153
+ Ask if user wants alternative seats.
154
+ Return: ALTERNATIVE_ACCEPTED: [new_preference] or BOOKING_DECLINED:
155
+ transitions:
156
+ - next: check_seat_availability
157
+ pattern: "ALTERNATIVE_ACCEPTED:"
158
+ - next: booking_cancelled
159
+ pattern: "BOOKING_DECLINED:"
160
+
161
+ # MFA-PROTECTED STEP: Payment processing requires authentication
162
+ - id: process_payment
163
+ description: Process payment transaction with MFA
164
+ action: call_function
165
+ function: booking_helpers.process_payment
166
+ output: payment_status
167
+ mfa:
168
+ model: gpt-4o-mini
169
+ type: REST
170
+ payload:
171
+ transactionType: CONCERT_TICKET_PAYMENT
172
+ businessKey:
173
+ customerName: "{{customer_name}}"
174
+ concertName: "{{concert_name}}"
175
+ seatPreference: "{{seat_preference}}"
176
+ ticketQuantity: "{{ticket_quantity}}"
177
+ transitions:
178
+ - next: send_confirmation
179
+ condition: true
180
+ - next: payment_failed
181
+ condition: false
182
+
183
+ # MFA-PROTECTED STEP: Sending confirmation requires authentication
184
+ - id: send_confirmation
185
+ description: Send booking confirmation with MFA
186
+ action: call_function
187
+ function: booking_helpers.send_confirmation
188
+ output: confirmation_sent
189
+ mfa:
190
+ model: gpt-4o-mini
191
+ type: REST
192
+ payload:
193
+ transactionType: SEND_TICKET_CONFIRMATION
194
+ businessKey:
195
+ bookingReference: "{{booking_reference}}"
196
+ customerName: "{{customer_name}}"
197
+ next: ask_modification_needed
198
+
199
+ - id: ask_modification_needed
200
+ description: Ask if customer wants to modify booking
201
+ action: collect_input_with_agent
202
+ field: modification_requested
203
+ max_attempts: 3
204
+ agent:
205
+ name: Modification Request Collector
206
+ model: gpt-4o-mini
207
+ initial_message: >
208
+ Booking confirmed for {{concert_name}}, {{customer_name}}!
209
+ Reference: {{booking_reference}}
210
+ Seats: {{ticket_quantity}} x {{seat_preference}}
211
+
212
+ Would you like to modify your booking details? (Yes/No)
213
+ instructions: >
214
+ Determine if user wants to modify the booking.
215
+ Return: MODIFICATION_REQUESTED: or NO_MODIFICATION:
216
+ transitions:
217
+ - next: collect_seat_preference
218
+ pattern: "MODIFICATION_REQUESTED:"
219
+ - next: booking_success
220
+ pattern: "NO_MODIFICATION:"
221
+
222
+ - id: payment_failed
223
+ description: Handle payment failure
224
+ action: call_function
225
+ function: booking_helpers.handle_payment_failure
226
+ output: payment_status
227
+ next: booking_failed
228
+
229
+ outcomes:
230
+ - id: booking_success
231
+ type: success
232
+ message: "Thank you {{customer_name}}! Your {{ticket_quantity}} {{seat_preference}} tickets for {{concert_name}} have been confirmed. Booking reference: {{booking_reference}}"
233
+
234
+ - id: booking_cancelled
235
+ type: failure
236
+ message: "Booking cancelled. Feel free to start a new booking anytime!"
237
+
238
+ - id: booking_failed
239
+ type: failure
240
+ message: "We couldn't complete your booking. Please try again or contact support."
241
+
242
+ - id: payment_failed
243
+ type: failure
244
+ message: "Payment failed. Please check your payment method and try again."