Skip to content

Commit f609e16

Browse files
committed
Add share_payload message type and book_feedback input type
Share payload: MessageNodeProcessor resolves share_payload_variable from session state, returning structured data the frontend can render as a shareable card. Book feedback: QuestionNodeProcessor supports input_type "book_feedback" with a book_source variable reference. Users can rate books (like/dislike/ read) and the structured feedback is stored in session state for downstream condition routing. Updated recommendation flow to v2.0.0 with feedback collection, share payload generation, and liked-book branching.
1 parent c5af7c4 commit f609e16

File tree

2 files changed

+97
-13
lines changed

2 files changed

+97
-13
lines changed

app/services/chat_runtime.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ async def process(
136136
message["delay"] = msg_config["delay"]
137137
messages.append(message)
138138

139+
# Handle share_payload_variable: resolve variable and emit share_payload message
140+
share_payload_var = node_content.get("share_payload_variable")
141+
if share_payload_var:
142+
payload = self.runtime.substitute_object(
143+
f"{{{{{share_payload_var}}}}}", session_state
144+
)
145+
if payload and isinstance(payload, dict):
146+
messages.append({"type": "share_payload", "content": payload})
147+
139148
# Fallback: check for direct "text" or "rich_text" in node content
140149
if not messages:
141150
if node_content.get("rich_text"):
@@ -407,7 +416,7 @@ async def process(
407416
state_updates={"system": {"_current_options": options}},
408417
)
409418

410-
return {
419+
result: Dict[str, Any] = {
411420
"type": "question",
412421
"question": question_message,
413422
"content_id": content_id,
@@ -418,6 +427,18 @@ async def process(
418427
"node_id": node.node_id,
419428
}
420429

430+
# For book_feedback questions, resolve the book source and include books
431+
if input_type == "book_feedback":
432+
book_source = node_content.get("book_source", "")
433+
if book_source:
434+
books = self.runtime.substitute_object(
435+
f"{{{{{book_source}}}}}", session_state
436+
)
437+
if books and isinstance(books, list):
438+
result["books"] = books
439+
440+
return result
441+
421442
async def _fetch_random_content(
422443
self,
423444
db: AsyncSession,
@@ -648,7 +669,15 @@ async def process_response(
648669

649670
# For choice-based inputs, try to store the full option object
650671
stored_value: Any = sanitized_input
651-
if input_type in ("choice", "image_choice", "button"):
672+
if input_type == "book_feedback":
673+
# Book feedback arrives as JSON with liked/disliked/read arrays
674+
import json
675+
676+
try:
677+
stored_value = json.loads(user_input)
678+
except (json.JSONDecodeError, TypeError):
679+
stored_value = sanitized_input
680+
elif input_type in ("choice", "image_choice", "button"):
652681
options = (session.state or {}).get("system", {}).get(
653682
"_current_options"
654683
) or []
@@ -1855,12 +1884,16 @@ async def get_initial_node(
18551884
@staticmethod
18561885
def _build_input_request(source: Dict[str, Any]) -> Dict[str, Any]:
18571886
"""Build an input_request dict from a question result or raw node content."""
1858-
return {
1887+
ir: Dict[str, Any] = {
18591888
"input_type": source.get("input_type", "text"),
18601889
"variable": source.get("variable", ""),
18611890
"options": source.get("options", []),
18621891
"question": source.get("question", {}),
18631892
}
1893+
# Include books for book_feedback questions
1894+
if source.get("books"):
1895+
ir["books"] = source["books"]
1896+
return ir
18641897

18651898
async def _resolve_question_node(
18661899
self, db: AsyncSession, question_node: FlowNode, session: ConversationSession

scripts/fixtures/huey-recommendation-flow.json

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"seed_key": "huey-recommendation",
33
"name": "Huey Recommendation",
4-
"description": "Queries for book recommendations with fallback, displays results.",
5-
"version": "1.0.0",
4+
"description": "Queries for book recommendations with fallback, collects per-book feedback, and displays liked books.",
5+
"version": "2.0.0",
66
"entry_node_id": "good_choices_msg",
77
"visibility": "wriveted",
88
"flow_data": {
@@ -71,14 +71,60 @@
7171
},
7272
{
7373
"id": "show_books",
74+
"type": "question",
75+
"content": {
76+
"question": {"text": "I found {{temp.book_count}} books for you! Swipe through and tell me what you think \ud83d\udcda"},
77+
"input_type": "book_feedback",
78+
"book_source": "temp.book_results",
79+
"variable": "temp.book_feedback"
80+
},
81+
"position": {"x": 900, "y": 150}
82+
},
83+
{
84+
"id": "build_share_payload",
85+
"type": "action",
86+
"content": {
87+
"actions": [
88+
{
89+
"type": "aggregate",
90+
"expression": "size(has(temp.book_feedback, 'liked') ? temp.book_feedback.liked : [])",
91+
"target": "temp.liked_count"
92+
}
93+
]
94+
},
95+
"position": {"x": 1100, "y": 150}
96+
},
97+
{
98+
"id": "check_liked",
99+
"type": "condition",
100+
"content": {
101+
"conditions": [
102+
{"if": "temp.liked_count > 0", "then": "$0"}
103+
],
104+
"default_path": "default"
105+
},
106+
"position": {"x": 1300, "y": 150}
107+
},
108+
{
109+
"id": "show_liked_msg",
74110
"type": "message",
75111
"content": {
76112
"messages": [
77-
{"type": "text", "text": "I found {{temp.book_count}} books for you! \ud83d\udcda"},
78-
{"type": "book_list", "source": "temp.book_results"}
113+
{"type": "text", "text": "Great taste! Here are the books you liked \ud83c\udf1f"},
114+
{"type": "book_list", "source": "temp.book_feedback.liked"}
79115
]
80116
},
81-
"position": {"x": 900, "y": 150}
117+
"position": {"x": 1500, "y": 50}
118+
},
119+
{
120+
"id": "no_liked_msg",
121+
"type": "message",
122+
"content": {
123+
"messages": [
124+
{"type": "text", "text": "No worries! Maybe next time we'll find something you love \ud83d\ude0a"}
125+
]
126+
},
127+
"position": {"x": 1500, "y": 250}
82128
},
83129
{
84130
"id": "fallback_query",
@@ -118,12 +164,12 @@
118164
},
119165
{
120166
"id": "show_fallback_books",
121-
"type": "message",
167+
"type": "question",
122168
"content": {
123-
"messages": [
124-
{"type": "text", "text": "I found {{temp.fallback_count}} books you might enjoy! \ud83d\udcda"},
125-
{"type": "book_list", "source": "temp.fallback_results"}
126-
]
169+
"question": {"text": "I found {{temp.fallback_count}} books you might enjoy! Swipe through and tell me what you think \ud83d\udcda"},
170+
"input_type": "book_feedback",
171+
"book_source": "temp.fallback_results",
172+
"variable": "temp.book_feedback"
127173
},
128174
"position": {"x": 1300, "y": 350}
129175
},
@@ -144,8 +190,13 @@
144190
{"source": "book_query", "target": "check_results", "type": "default"},
145191
{"source": "check_results", "target": "show_books", "type": "$0"},
146192
{"source": "check_results", "target": "fallback_query", "type": "default"},
193+
{"source": "show_books", "target": "build_share_payload", "type": "default"},
194+
{"source": "build_share_payload", "target": "check_liked", "type": "default"},
195+
{"source": "check_liked", "target": "show_liked_msg", "type": "$0"},
196+
{"source": "check_liked", "target": "no_liked_msg", "type": "default"},
147197
{"source": "fallback_query", "target": "check_fallback", "type": "default"},
148198
{"source": "check_fallback", "target": "show_fallback_books", "type": "$0"},
199+
{"source": "show_fallback_books", "target": "build_share_payload", "type": "default"},
149200
{"source": "check_fallback", "target": "no_books_msg", "type": "default"}
150201
]
151202
}

0 commit comments

Comments
 (0)