flow_chat 0.6.1 → 0.7.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +44 -0
  3. data/.gitignore +2 -1
  4. data/README.md +84 -1229
  5. data/docs/configuration.md +337 -0
  6. data/docs/flows.md +320 -0
  7. data/docs/images/simulator.png +0 -0
  8. data/docs/instrumentation.md +216 -0
  9. data/docs/media.md +153 -0
  10. data/docs/testing.md +475 -0
  11. data/docs/ussd-setup.md +306 -0
  12. data/docs/whatsapp-setup.md +162 -0
  13. data/examples/multi_tenant_whatsapp_controller.rb +9 -37
  14. data/examples/simulator_controller.rb +9 -18
  15. data/examples/ussd_controller.rb +32 -38
  16. data/examples/whatsapp_controller.rb +32 -125
  17. data/examples/whatsapp_media_examples.rb +68 -336
  18. data/examples/whatsapp_message_job.rb +5 -3
  19. data/flow_chat.gemspec +6 -2
  20. data/lib/flow_chat/base_processor.rb +48 -2
  21. data/lib/flow_chat/config.rb +5 -0
  22. data/lib/flow_chat/context.rb +13 -1
  23. data/lib/flow_chat/instrumentation/log_subscriber.rb +176 -0
  24. data/lib/flow_chat/instrumentation/metrics_collector.rb +197 -0
  25. data/lib/flow_chat/instrumentation/setup.rb +155 -0
  26. data/lib/flow_chat/instrumentation.rb +70 -0
  27. data/lib/flow_chat/prompt.rb +20 -20
  28. data/lib/flow_chat/session/cache_session_store.rb +73 -7
  29. data/lib/flow_chat/session/middleware.rb +37 -4
  30. data/lib/flow_chat/session/rails_session_store.rb +36 -1
  31. data/lib/flow_chat/simulator/controller.rb +6 -6
  32. data/lib/flow_chat/ussd/gateway/nalo.rb +30 -0
  33. data/lib/flow_chat/ussd/gateway/nsano.rb +33 -0
  34. data/lib/flow_chat/ussd/middleware/choice_mapper.rb +109 -0
  35. data/lib/flow_chat/ussd/middleware/executor.rb +24 -2
  36. data/lib/flow_chat/ussd/middleware/pagination.rb +87 -7
  37. data/lib/flow_chat/ussd/processor.rb +14 -0
  38. data/lib/flow_chat/ussd/renderer.rb +1 -1
  39. data/lib/flow_chat/version.rb +1 -1
  40. data/lib/flow_chat/whatsapp/client.rb +99 -12
  41. data/lib/flow_chat/whatsapp/configuration.rb +35 -4
  42. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +120 -34
  43. data/lib/flow_chat/whatsapp/middleware/executor.rb +24 -2
  44. data/lib/flow_chat/whatsapp/processor.rb +8 -0
  45. data/lib/flow_chat/whatsapp/renderer.rb +4 -9
  46. data/lib/flow_chat.rb +23 -0
  47. metadata +22 -11
  48. data/.travis.yml +0 -6
  49. data/app/controllers/demo_controller.rb +0 -101
  50. data/app/flow_chat/demo_restaurant_flow.rb +0 -889
  51. data/config/routes_demo.rb +0 -59
  52. data/examples/initializer.rb +0 -86
  53. data/examples/media_prompts_examples.rb +0 -27
  54. data/images/ussd_simulator.png +0 -0
@@ -0,0 +1,216 @@
1
+ # Instrumentation & Monitoring
2
+
3
+ FlowChat includes a comprehensive instrumentation system for observability, monitoring, and logging.
4
+
5
+ ## Quick Setup
6
+
7
+ Enable instrumentation in your Rails application:
8
+
9
+ ```ruby
10
+ # config/initializers/flowchat.rb
11
+ FlowChat.setup_instrumentation!
12
+ ```
13
+
14
+ This sets up:
15
+ - 📊 **Metrics Collection** - Performance and usage metrics
16
+ - 📝 **Structured Logging** - Event-driven logs with context
17
+ - 🔍 **Event Tracking** - All framework events instrumented
18
+ - ⚡ **Performance Monitoring** - Execution timing and bottleneck detection
19
+
20
+ ## Features
21
+
22
+ **🎯 Zero Configuration**
23
+ - Works out of the box with Rails applications
24
+ - Automatic ActiveSupport::Notifications integration
25
+ - Thread-safe metrics collection
26
+
27
+ **📈 Comprehensive Metrics**
28
+ - Flow execution counts and timing
29
+ - Session creation/destruction rates
30
+ - WhatsApp/USSD message volumes
31
+ - Cache hit/miss ratios
32
+ - Error tracking by type and flow
33
+
34
+ **🔍 Rich Event Tracking**
35
+ - 20+ predefined event types (see [Event Types](#event-types))
36
+ - Automatic context enrichment (session ID, flow name, gateway)
37
+ - Structured event payloads
38
+
39
+ **📊 Production Ready**
40
+ - Minimal performance overhead
41
+ - Thread-safe operations
42
+ - Graceful error handling
43
+
44
+ ## Event Types
45
+
46
+ FlowChat instruments the following events:
47
+
48
+ ### Flow Events
49
+ - `flow.execution.start.flow_chat`
50
+ - `flow.execution.end.flow_chat`
51
+ - `flow.execution.error.flow_chat`
52
+
53
+ ### Session Events
54
+ - `session.created.flow_chat`
55
+ - `session.destroyed.flow_chat`
56
+ - `session.data.get.flow_chat`
57
+ - `session.data.set.flow_chat`
58
+ - `session.cache.hit.flow_chat`
59
+ - `session.cache.miss.flow_chat`
60
+
61
+ ### WhatsApp Events
62
+ - `whatsapp.message.received.flow_chat`
63
+ - `whatsapp.message.sent.flow_chat`
64
+ - `whatsapp.webhook.verified.flow_chat`
65
+ - `whatsapp.api.request.flow_chat`
66
+ - `whatsapp.media.upload.flow_chat`
67
+
68
+ ### USSD Events
69
+ - `ussd.message.received.flow_chat`
70
+ - `ussd.message.sent.flow_chat`
71
+ - `ussd.pagination.triggered.flow_chat`
72
+
73
+ ## Usage Examples
74
+
75
+ ### Access Metrics
76
+
77
+ ```ruby
78
+ # Get current metrics snapshot
79
+ metrics = FlowChat.metrics.snapshot
80
+
81
+ # Flow execution metrics
82
+ flow_metrics = FlowChat.metrics.get_category("flows")
83
+ puts flow_metrics["flows.executed"] # Total flows executed
84
+ puts flow_metrics["flows.execution_time"] # Average execution time
85
+
86
+ # Session metrics
87
+ session_metrics = FlowChat.metrics.get_category("sessions")
88
+ puts session_metrics["sessions.created"] # Total sessions created
89
+ puts session_metrics["sessions.cache.hits"] # Cache hit count
90
+ ```
91
+
92
+ ### Custom Instrumentation in Flows
93
+
94
+ ```ruby
95
+ class PaymentFlow < FlowChat::Flow
96
+ def process_payment
97
+ # Instrument custom events in your flows
98
+ instrument("payment.started", {
99
+ amount: payment_amount,
100
+ currency: "USD",
101
+ payment_method: "mobile_money"
102
+ }) do
103
+ # Payment processing logic
104
+ result = process_mobile_money_payment
105
+
106
+ # Event automatically includes session_id, flow_name, gateway
107
+ result
108
+ end
109
+ end
110
+ end
111
+ ```
112
+
113
+ ### Event Subscribers
114
+
115
+ ```ruby
116
+ # config/initializers/flowchat_instrumentation.rb
117
+ FlowChat.setup_instrumentation!
118
+
119
+ # Subscribe to specific events
120
+ ActiveSupport::Notifications.subscribe("flow.execution.end.flow_chat") do |event|
121
+ duration = event.duration
122
+ flow_name = event.payload[:flow_name]
123
+
124
+ # Send to external monitoring service
125
+ ExternalMonitoring.track_flow_execution(flow_name, duration)
126
+ end
127
+
128
+ # Subscribe to all FlowChat events
129
+ ActiveSupport::Notifications.subscribe(/\.flow_chat$/) do |name, start, finish, id, payload|
130
+ CustomLogger.log_event(name, payload.merge(duration: finish - start))
131
+ end
132
+ ```
133
+
134
+ ### Integration with Monitoring Services
135
+
136
+ ```ruby
137
+ # config/initializers/flowchat_monitoring.rb
138
+ FlowChat.setup_instrumentation!
139
+
140
+ # Export metrics to Prometheus, StatsD, etc.
141
+ ActiveSupport::Notifications.subscribe("flow.execution.end.flow_chat") do |event|
142
+ StatsD.increment("flowchat.flows.executed")
143
+ StatsD.timing("flowchat.flows.duration", event.duration)
144
+ StatsD.increment("flowchat.flows.#{event.payload[:flow_name]}.executed")
145
+ end
146
+
147
+ # Track error rates
148
+ ActiveSupport::Notifications.subscribe("flow.execution.error.flow_chat") do |event|
149
+ StatsD.increment("flowchat.flows.errors")
150
+ StatsD.increment("flowchat.flows.errors.#{event.payload[:error_class]}")
151
+ end
152
+ ```
153
+
154
+ ## Performance Impact
155
+
156
+ The instrumentation system is designed for production use with minimal overhead:
157
+
158
+ - **Event Publishing**: ~0.1ms per event
159
+ - **Metrics Collection**: Thread-safe atomic operations
160
+ - **Memory Usage**: <1MB for typical applications
161
+ - **Storage**: Events are ephemeral, metrics are kept in memory
162
+
163
+ ## Debugging & Troubleshooting
164
+
165
+ ### Enable Debug Logging
166
+
167
+ ```ruby
168
+ # config/environments/development.rb
169
+ config.log_level = :debug
170
+ ```
171
+
172
+ ### Reset Metrics
173
+
174
+ ```ruby
175
+ # Clear all metrics (useful for testing)
176
+ FlowChat.metrics.reset!
177
+ ```
178
+
179
+ ### Check Event Subscribers
180
+
181
+ ```ruby
182
+ # See all active subscribers
183
+ ActiveSupport::Notifications.notifier.listeners_for("flow.execution.end.flow_chat")
184
+ ```
185
+
186
+ ## Testing Instrumentation
187
+
188
+ ```ruby
189
+ # test/test_helper.rb
190
+ class ActiveSupport::TestCase
191
+ setup do
192
+ # Reset metrics before each test
193
+ FlowChat::Instrumentation::Setup.reset! if FlowChat::Instrumentation::Setup.setup?
194
+ end
195
+ end
196
+
197
+ # In your tests
198
+ class FlowInstrumentationTest < ActiveSupport::TestCase
199
+ test "flow execution is instrumented" do
200
+ events = []
201
+
202
+ # Capture events
203
+ ActiveSupport::Notifications.subscribe(/flow_chat$/) do |name, start, finish, id, payload|
204
+ events << { name: name, payload: payload, duration: (finish - start) * 1000 }
205
+ end
206
+
207
+ # Execute flow
208
+ processor.run(WelcomeFlow, :main_page)
209
+
210
+ # Verify events
211
+ assert_equal 2, events.size
212
+ assert_equal "flow.execution.start.flow_chat", events[0][:name]
213
+ assert_equal "flow.execution.end.flow_chat", events[1][:name]
214
+ assert_equal "welcome_flow", events[0][:payload][:flow_name]
215
+ end
216
+ end
data/docs/media.md ADDED
@@ -0,0 +1,153 @@
1
+ # Media Support
2
+
3
+ FlowChat supports rich media attachments for enhanced conversational experiences with automatic cross-platform optimization.
4
+
5
+ ## Supported Media Types
6
+
7
+ - **📷 Images** (`type: :image`) - Photos, screenshots, diagrams
8
+ - **📄 Documents** (`type: :document`) - PDFs, forms, receipts
9
+ - **🎥 Videos** (`type: :video`) - Tutorials, demos, explanations
10
+ - **🎵 Audio** (`type: :audio`) - Voice messages, recordings
11
+ - **😊 Stickers** (`type: :sticker`) - Fun visual elements
12
+
13
+ ## Basic Usage
14
+
15
+ ```ruby
16
+ class ProductFlow < FlowChat::Flow
17
+ def main_page
18
+ # Text input with context image
19
+ feedback = app.screen(:feedback) do |prompt|
20
+ prompt.ask "What do you think of our new product?",
21
+ media: {
22
+ type: :image,
23
+ url: "https://cdn.example.com/products/new_product.jpg"
24
+ }
25
+ end
26
+
27
+ # Send informational media
28
+ app.say "Thanks for your feedback! Here's what's coming next:",
29
+ media: {
30
+ type: :video,
31
+ url: "https://videos.example.com/roadmap.mp4"
32
+ }
33
+
34
+ # Document with filename
35
+ app.say "Here's your receipt:",
36
+ media: {
37
+ type: :document,
38
+ url: "https://api.example.com/receipt.pdf",
39
+ filename: "receipt.pdf"
40
+ }
41
+ end
42
+ end
43
+ ```
44
+
45
+ ## Media Hash Format
46
+
47
+ ```ruby
48
+ {
49
+ type: :image, # Required: :image, :document, :audio, :video, :sticker
50
+ url: "https://...", # Required: URL to the media file OR WhatsApp media ID
51
+ filename: "doc.pdf" # Optional: Only for documents
52
+ }
53
+ ```
54
+
55
+ ## Using WhatsApp Media IDs
56
+
57
+ For better performance and to avoid external dependencies, you can upload files to WhatsApp and use the media ID:
58
+
59
+ ```ruby
60
+ # Upload a file first
61
+ client = FlowChat::Whatsapp::Client.new(config)
62
+ media_id = client.upload_media('path/to/image.jpg', 'image/jpeg')
63
+
64
+ # Then use the media ID in your flow
65
+ app.screen(:product_demo) do |prompt|
66
+ prompt.ask "What do you think?",
67
+ media: {
68
+ type: :image,
69
+ url: media_id # Use the media ID instead of URL
70
+ }
71
+ end
72
+ ```
73
+
74
+ ## WhatsApp Client Media Methods
75
+
76
+ The WhatsApp client provides methods for uploading and sending media:
77
+
78
+ ```ruby
79
+ client = FlowChat::Whatsapp::Client.new(config)
80
+
81
+ # Upload media and get media ID
82
+ media_id = client.upload_media('image.jpg', 'image/jpeg')
83
+ media_id = client.upload_media(file_io, 'image/jpeg', 'photo.jpg')
84
+
85
+ # Send media directly
86
+ client.send_image("+1234567890", "https://example.com/image.jpg", "Caption")
87
+ client.send_image("+1234567890", media_id, "Caption")
88
+
89
+ # Send document with MIME type and filename
90
+ client.send_document("+1234567890", "https://example.com/doc.pdf", "Your receipt", "receipt.pdf", "application/pdf")
91
+
92
+ # Send other media types
93
+ client.send_video("+1234567890", "https://example.com/video.mp4", "Demo video", "video/mp4")
94
+ client.send_audio("+1234567890", "https://example.com/audio.mp3", "audio/mpeg")
95
+ client.send_sticker("+1234567890", "https://example.com/sticker.webp", "image/webp")
96
+ ```
97
+
98
+ ## Cross-Platform Behavior
99
+
100
+ ### WhatsApp Experience
101
+ - Media is sent directly to the chat
102
+ - Prompt text becomes the media caption
103
+ - Rich, native messaging experience
104
+
105
+ ### USSD Experience
106
+ - Media URL is included in text message
107
+ - Graceful degradation with clear media indicators
108
+ - Users can access media via the provided link
109
+
110
+ ```ruby
111
+ # This code works on both platforms:
112
+ app.screen(:help) do |prompt|
113
+ prompt.ask "Describe your issue:",
114
+ media: {
115
+ type: :image,
116
+ url: "https://support.example.com/help_example.jpg"
117
+ }
118
+ end
119
+ ```
120
+
121
+ **WhatsApp Result:** Image sent with caption "Describe your issue:"
122
+
123
+ **USSD Result:**
124
+ ```
125
+ 📷 Image: https://support.example.com/help_example.jpg
126
+
127
+ Describe your issue:
128
+ ```
129
+
130
+ ## Advanced Examples
131
+
132
+ ### Dynamic Media Based on User Context
133
+
134
+ ```ruby
135
+ class SupportFlow < FlowChat::Flow
136
+ def show_help
137
+ user_level = determine_user_level
138
+
139
+ help_media = case user_level
140
+ when :beginner
141
+ { type: :video, url: "https://help.example.com/beginner_guide.mp4" }
142
+ when :advanced
143
+ { type: :document, url: "https://help.example.com/advanced_manual.pdf", filename: "advanced_manual.pdf" }
144
+ else
145
+ { type: :image, url: "https://help.example.com/quick_reference.jpg" }
146
+ end
147
+
148
+ app.say "Here's help tailored for you:", media: help_media
149
+ end
150
+ end
151
+ ```
152
+
153
+ See [examples/whatsapp_media_examples.rb](../examples/whatsapp_media_examples.rb) for complete media implementation examples.