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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +44 -0
- data/.gitignore +2 -1
- data/README.md +84 -1229
- data/docs/configuration.md +337 -0
- data/docs/flows.md +320 -0
- data/docs/images/simulator.png +0 -0
- data/docs/instrumentation.md +216 -0
- data/docs/media.md +153 -0
- data/docs/testing.md +475 -0
- data/docs/ussd-setup.md +306 -0
- data/docs/whatsapp-setup.md +162 -0
- data/examples/multi_tenant_whatsapp_controller.rb +9 -37
- data/examples/simulator_controller.rb +9 -18
- data/examples/ussd_controller.rb +32 -38
- data/examples/whatsapp_controller.rb +32 -125
- data/examples/whatsapp_media_examples.rb +68 -336
- data/examples/whatsapp_message_job.rb +5 -3
- data/flow_chat.gemspec +6 -2
- data/lib/flow_chat/base_processor.rb +48 -2
- data/lib/flow_chat/config.rb +5 -0
- data/lib/flow_chat/context.rb +13 -1
- data/lib/flow_chat/instrumentation/log_subscriber.rb +176 -0
- data/lib/flow_chat/instrumentation/metrics_collector.rb +197 -0
- data/lib/flow_chat/instrumentation/setup.rb +155 -0
- data/lib/flow_chat/instrumentation.rb +70 -0
- data/lib/flow_chat/prompt.rb +20 -20
- data/lib/flow_chat/session/cache_session_store.rb +73 -7
- data/lib/flow_chat/session/middleware.rb +37 -4
- data/lib/flow_chat/session/rails_session_store.rb +36 -1
- data/lib/flow_chat/simulator/controller.rb +6 -6
- data/lib/flow_chat/ussd/gateway/nalo.rb +30 -0
- data/lib/flow_chat/ussd/gateway/nsano.rb +33 -0
- data/lib/flow_chat/ussd/middleware/choice_mapper.rb +109 -0
- data/lib/flow_chat/ussd/middleware/executor.rb +24 -2
- data/lib/flow_chat/ussd/middleware/pagination.rb +87 -7
- data/lib/flow_chat/ussd/processor.rb +14 -0
- data/lib/flow_chat/ussd/renderer.rb +1 -1
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/client.rb +99 -12
- data/lib/flow_chat/whatsapp/configuration.rb +35 -4
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +120 -34
- data/lib/flow_chat/whatsapp/middleware/executor.rb +24 -2
- data/lib/flow_chat/whatsapp/processor.rb +8 -0
- data/lib/flow_chat/whatsapp/renderer.rb +4 -9
- data/lib/flow_chat.rb +23 -0
- metadata +22 -11
- data/.travis.yml +0 -6
- data/app/controllers/demo_controller.rb +0 -101
- data/app/flow_chat/demo_restaurant_flow.rb +0 -889
- data/config/routes_demo.rb +0 -59
- data/examples/initializer.rb +0 -86
- data/examples/media_prompts_examples.rb +0 -27
- 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.
|