Skip to content

Commit 2663d8c

Browse files
committed
Add template engine for conversation flow API fetch
- Add template_engine.go with support for: - Variables: {{name}}, {{user.profile.name}} - Conditionals: {{if condition}}...{{else}}...{{endif}} - Comparisons: {{if amount > 100}}, {{if status == 'active'}} - Loops: {{for item in items}}...{{endfor}} - Add response_mapping to API config for extracting data from API responses - Store mapped data in session for use in subsequent steps - Update frontend with response mapping UI and template syntax documentation - Remove legacy response_path field from flow step UI
1 parent 8d4efae commit 2663d8c

File tree

3 files changed

+619
-85
lines changed

3 files changed

+619
-85
lines changed

frontend/src/views/chatbot/ChatbotFlowsView.vue

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ interface ApiConfig {
5858
method: string
5959
headers: Record<string, string>
6060
body: string
61-
response_path: string
6261
fallback_message: string
62+
response_mapping: Record<string, string> // Maps session variables to API response paths
6363
}
6464
6565
interface ButtonConfig {
@@ -156,8 +156,8 @@ const defaultApiConfig: ApiConfig = {
156156
method: 'GET',
157157
headers: {},
158158
body: '',
159-
response_path: '',
160-
fallback_message: ''
159+
fallback_message: '',
160+
response_mapping: {}
161161
}
162162
163163
const defaultTransferConfig: TransferConfig = {
@@ -274,7 +274,8 @@ async function openEditDialog(flow: ChatbotFlow) {
274274
api_config: {
275275
...defaultApiConfig,
276276
...(s.api_config || s.ApiConfig || {}),
277-
headers: (s.api_config || s.ApiConfig || {}).headers || {}
277+
headers: (s.api_config || s.ApiConfig || {}).headers || {},
278+
response_mapping: (s.api_config || s.ApiConfig || {}).response_mapping || {}
278279
},
279280
buttons: s.buttons || s.Buttons || [],
280281
transfer_config: {
@@ -350,6 +351,28 @@ function removeStepHeader(stepIndex: number, key: string) {
350351
delete formData.value.steps[stepIndex].api_config.headers[key]
351352
}
352353
354+
// Step API response mapping helpers
355+
function addStepResponseMapping(stepIndex: number) {
356+
const step = formData.value.steps[stepIndex]
357+
if (!step.api_config.response_mapping) {
358+
step.api_config.response_mapping = {}
359+
}
360+
const mappingNum = Object.keys(step.api_config.response_mapping).length + 1
361+
step.api_config.response_mapping[`var_${mappingNum}`] = ''
362+
}
363+
364+
function updateStepResponseMappingKey(stepIndex: number, oldKey: string, newKey: string) {
365+
if (oldKey === newKey) return
366+
const step = formData.value.steps[stepIndex]
367+
const value = step.api_config.response_mapping[oldKey]
368+
delete step.api_config.response_mapping[oldKey]
369+
step.api_config.response_mapping[newKey] = value
370+
}
371+
372+
function removeStepResponseMapping(stepIndex: number, key: string) {
373+
delete formData.value.steps[stepIndex].api_config.response_mapping[key]
374+
}
375+
353376
function removeStep(index: number) {
354377
formData.value.steps.splice(index, 1)
355378
// Reorder remaining steps
@@ -767,14 +790,17 @@ function removeButton(step: FlowStep, index: number) {
767790
</Select>
768791
</div>
769792

770-
<!-- Static Message (for text type) -->
771-
<div v-if="step.message_type !== 'api_fetch'" class="space-y-2">
793+
<!-- Static Message (for text, buttons, whatsapp_flow types - not api_fetch which has its own template section) -->
794+
<div v-if="step.message_type !== 'api_fetch' && step.message_type !== 'transfer'" class="space-y-2">
772795
<Label>Message to Send *</Label>
773796
<Textarea
774797
v-model="step.message"
775798
placeholder="What is your name?"
776799
:rows="2"
777800
/>
801+
<p v-if="step.message_type === 'text' || step.message_type === 'buttons'" class="text-xs text-muted-foreground">
802+
Use <code v-pre class="bg-muted px-1 rounded">{{variable}}</code> for dynamic values from session data
803+
</p>
778804
</div>
779805

780806
<!-- Buttons Configuration (for buttons type) -->
@@ -845,7 +871,7 @@ function removeButton(step: FlowStep, index: number) {
845871
v-model="step.api_config.url"
846872
placeholder="https://api.example.com/data/{{customer_id}}"
847873
/>
848-
<p class="text-xs text-muted-foreground">Use {{variable}} to include session data</p>
874+
<p class="text-xs text-muted-foreground">Use <code v-pre class="text-xs bg-muted px-1 rounded">{{variable}}</code> to include session data</p>
849875
</div>
850876
</div>
851877

@@ -881,7 +907,7 @@ function removeButton(step: FlowStep, index: number) {
881907
</div>
882908
</div>
883909
<p class="text-xs text-muted-foreground">
884-
Add custom headers like Authorization, API keys. Use {{variable}} for dynamic values.
910+
Add custom headers like Authorization, API keys.
885911
</p>
886912
</div>
887913

@@ -894,23 +920,76 @@ function removeButton(step: FlowStep, index: number) {
894920
/>
895921
</div>
896922

897-
<div class="grid grid-cols-2 gap-4">
898-
<div class="space-y-2">
899-
<Label>Response Path</Label>
900-
<Input
901-
v-model="step.api_config.response_path"
902-
placeholder="data.message"
903-
/>
904-
<p class="text-xs text-muted-foreground">Dot notation path to extract message (e.g., data.message)</p>
923+
<!-- Response Mapping -->
924+
<div class="space-y-2 border-t pt-4">
925+
<div class="flex items-center justify-between">
926+
<Label>Response Mapping (extract API data)</Label>
927+
<Button variant="outline" size="sm" @click="addStepResponseMapping(index)">
928+
<Plus class="h-3 w-3 mr-1" />
929+
Add Mapping
930+
</Button>
905931
</div>
906-
<div class="space-y-2">
907-
<Label>Fallback Message</Label>
908-
<Input
909-
v-model="step.api_config.fallback_message"
910-
placeholder="Sorry, we couldn't fetch your data."
911-
/>
912-
<p class="text-xs text-muted-foreground">Sent if API call fails</p>
932+
<div v-if="step.api_config.response_mapping && Object.keys(step.api_config.response_mapping).length > 0" class="space-y-2">
933+
<div
934+
v-for="(value, key) in step.api_config.response_mapping"
935+
:key="key"
936+
class="flex items-center gap-2"
937+
>
938+
<Input
939+
:model-value="key"
940+
placeholder="Variable name"
941+
class="flex-1"
942+
@update:model-value="updateStepResponseMappingKey(index, key as string, $event)"
943+
/>
944+
<span class="text-muted-foreground">=</span>
945+
<Input
946+
v-model="step.api_config.response_mapping[key as string]"
947+
placeholder="API path (e.g., data.user.name)"
948+
class="flex-1"
949+
/>
950+
<Button variant="ghost" size="icon" @click="removeStepResponseMapping(index, key as string)">
951+
<Trash2 class="h-4 w-4 text-destructive" />
952+
</Button>
953+
</div>
913954
</div>
955+
<p class="text-xs text-muted-foreground">
956+
Map API response fields to session variables for use in the message template below.
957+
<br />
958+
Example: <code v-pre class="text-xs bg-muted px-1 rounded">name</code> = <code class="text-xs bg-muted px-1 rounded">data.client.name</code>,
959+
<code v-pre class="text-xs bg-muted px-1 rounded">positions</code> = <code class="text-xs bg-muted px-1 rounded">data.portfolio.items</code>
960+
</p>
961+
</div>
962+
963+
<!-- Message Template -->
964+
<div class="space-y-2 border-t pt-4">
965+
<Label>Message Template *</Label>
966+
<Textarea
967+
v-model="step.message"
968+
placeholder="Hi {{name}}, here are your positions:
969+
970+
{{for pos in positions}}
971+
- {{pos.symbol}}: {{pos.quantity}} @ ₹{{pos.price}}
972+
{{if pos.is_loss}} ⚠️ Loss: ₹{{pos.loss}}
973+
{{else}} ✅ Profit: ₹{{pos.profit}}
974+
{{endif}}{{endfor}}"
975+
:rows="6"
976+
/>
977+
<div class="text-xs text-muted-foreground space-y-1">
978+
<p><strong>Template Syntax:</strong></p>
979+
<p>• Variables: <code v-pre class="bg-muted px-1 rounded">{{name}}</code>, <code v-pre class="bg-muted px-1 rounded">{{user.profile.email}}</code></p>
980+
<p>• Conditionals: <code v-pre class="bg-muted px-1 rounded">{{if is_premium}}...{{else}}...{{endif}}</code></p>
981+
<p>• Comparisons: <code v-pre class="bg-muted px-1 rounded">{{if amount > 100}}</code>, <code v-pre class="bg-muted px-1 rounded">{{if status == 'active'}}</code></p>
982+
<p>• Loops: <code v-pre class="bg-muted px-1 rounded">{{for item in items}}...{{endfor}}</code></p>
983+
</div>
984+
</div>
985+
986+
<div class="space-y-2 border-t pt-4">
987+
<Label>Fallback Message</Label>
988+
<Input
989+
v-model="step.api_config.fallback_message"
990+
placeholder="Sorry, we couldn't fetch your data."
991+
/>
992+
<p class="text-xs text-muted-foreground">Sent if API call fails</p>
914993
</div>
915994
</div>
916995

0 commit comments

Comments
 (0)