Romanchello-bit commited on
Commit
3519f60
·
1 Parent(s): 4919e19

first big update lmao

Browse files
Files changed (4) hide show
  1. engine.py +110 -0
  2. sales_script.json +70 -0
  3. sellme_demo.py +157 -0
  4. sellme_pro.py +270 -0
engine.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from graph_module import Graph
3
+ from algorithms import bellman_ford_list
4
+
5
+
6
+ class SalesEngine:
7
+ def __init__(self, json_file='sales_script.json'):
8
+ """Initialize the SalesEngine by loading the sales script from JSON."""
9
+ # Load the sales script
10
+ with open(json_file, 'r') as f:
11
+ data = json.load(f)
12
+
13
+ self.nodes_data = data['nodes']
14
+ self.edges_data = data['edges']
15
+
16
+ # Create mapping from string IDs to integer IDs
17
+ self.str_to_int = {}
18
+ self.int_to_str = {}
19
+ self.node_text = {}
20
+
21
+ for idx, node_name in enumerate(self.nodes_data.keys()):
22
+ self.str_to_int[node_name] = idx
23
+ self.int_to_str[idx] = node_name
24
+ self.node_text[node_name] = self.nodes_data[node_name]
25
+
26
+ # Build the Graph object
27
+ num_nodes = len(self.nodes_data)
28
+ self.graph = Graph(num_nodes, directed=True)
29
+
30
+ # Add edges with weights
31
+ for edge in self.edges_data:
32
+ from_node = self.str_to_int[edge['from']]
33
+ to_node = self.str_to_int[edge['to']]
34
+ weight = edge['weight']
35
+ self.graph.add_edge(from_node, to_node, weight)
36
+
37
+ def get_best_next_step(self, current_step_name):
38
+ """
39
+ Find the best next step from the current position to reach 'close_deal'.
40
+
41
+ Args:
42
+ current_step_name: String name of the current step
43
+
44
+ Returns:
45
+ Tuple of (next_step_name, next_step_text) for the optimal next step
46
+ """
47
+ # Find integer ID of current step
48
+ current_id = self.str_to_int[current_step_name]
49
+
50
+ # Run Bellman-Ford from current step
51
+ distances = bellman_ford_list(self.graph, current_id)
52
+
53
+ if distances is None:
54
+ raise ValueError("Negative cycle detected in the sales script graph!")
55
+
56
+ # Find the close_deal node ID
57
+ close_deal_id = self.str_to_int['close_deal']
58
+
59
+ # If we're already at close_deal, return it
60
+ if current_id == close_deal_id:
61
+ return current_step_name, self.node_text[current_step_name]
62
+
63
+ # Find the best immediate next step
64
+ # Look at all neighbors of current node and pick the one with shortest total distance to close_deal
65
+ adj_list = self.graph.get_list()
66
+ best_next_id = None
67
+ best_total_distance = float('inf')
68
+
69
+ for neighbor_id, edge_weight in adj_list[current_id]:
70
+ # Total distance = edge weight + distance from neighbor to close_deal
71
+ total_distance = edge_weight + distances[close_deal_id]
72
+ if total_distance < best_total_distance:
73
+ best_total_distance = total_distance
74
+ best_next_id = neighbor_id
75
+
76
+ if best_next_id is None:
77
+ raise ValueError(f"No path found from '{current_step_name}' to 'close_deal'")
78
+
79
+ # Convert back to string name and get text
80
+ next_step_name = self.int_to_str[best_next_id]
81
+ next_step_text = self.node_text[next_step_name]
82
+
83
+ return next_step_name, next_step_text
84
+
85
+
86
+ if __name__ == "__main__":
87
+ # Simulate a conversation path
88
+ engine = SalesEngine()
89
+
90
+ current_step = "start"
91
+ print(f"Starting sales conversation at: {current_step}")
92
+ print(f">>> {engine.node_text[current_step]}\n")
93
+
94
+ step_count = 0
95
+ max_steps = 10 # Safety limit to prevent infinite loops
96
+
97
+ while current_step != "close_deal" and step_count < max_steps:
98
+ # Get the best next step
99
+ next_step, next_text = engine.get_best_next_step(current_step)
100
+
101
+ print(f"Best next move: {next_step}")
102
+ print(f">>> {next_text}\n")
103
+
104
+ current_step = next_step
105
+ step_count += 1
106
+
107
+ if current_step == "close_deal":
108
+ print("[SUCCESS] Successfully reached the deal closure!")
109
+ else:
110
+ print("[WARNING] Reached maximum steps without closing the deal.")
sales_script.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nodes": {
3
+ "start": "Привіт! Це AI-асистент SellMe. Маєте хвилинку?",
4
+ "qualification": "Скажіть, ви використовуєте CRM систему?",
5
+ "pitch_crm": "Круто! Наш AI інтегрується з вашою CRM і сам заповнює картки.",
6
+ "pitch_no_crm": "Зрозумів. Наш AI може працювати навіть без CRM, в Телеграмі.",
7
+ "price_question": "Скільки це коштує? - Це залежить від кількості менеджерів.",
8
+ "objection_expensive": "Дорого? А скільки ви втрачаєте на незакритих угодах?",
9
+ "discount_offer": "Можемо запропонувати тестовий період за 1$.",
10
+ "close_deal": "Домовились! Висилаю посилання на оплату.",
11
+ "exit_bad": "Добре, вибачте за турботу. Гарного дня."
12
+ },
13
+ "edges": [
14
+ {
15
+ "from": "start",
16
+ "to": "qualification",
17
+ "weight": 1
18
+ },
19
+ {
20
+ "from": "start",
21
+ "to": "exit_bad",
22
+ "weight": 100
23
+ },
24
+ {
25
+ "from": "qualification",
26
+ "to": "pitch_crm",
27
+ "weight": 2
28
+ },
29
+ {
30
+ "from": "qualification",
31
+ "to": "pitch_no_crm",
32
+ "weight": 2
33
+ },
34
+ {
35
+ "from": "pitch_crm",
36
+ "to": "price_question",
37
+ "weight": 2
38
+ },
39
+ {
40
+ "from": "pitch_no_crm",
41
+ "to": "price_question",
42
+ "weight": 3
43
+ },
44
+ {
45
+ "from": "price_question",
46
+ "to": "objection_expensive",
47
+ "weight": 5
48
+ },
49
+ {
50
+ "from": "price_question",
51
+ "to": "close_deal",
52
+ "weight": 10
53
+ },
54
+ {
55
+ "from": "objection_expensive",
56
+ "to": "exit_bad",
57
+ "weight": 50
58
+ },
59
+ {
60
+ "from": "objection_expensive",
61
+ "to": "discount_offer",
62
+ "weight": 1
63
+ },
64
+ {
65
+ "from": "discount_offer",
66
+ "to": "close_deal",
67
+ "weight": 1
68
+ }
69
+ ]
70
+ }
sellme_demo.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import google.generativeai as genai
4
+ from graph_module import Graph
5
+ from algorithms import bellman_ford_list
6
+
7
+
8
+ def main():
9
+ # Configuration: Get API Key from user
10
+ print("=" * 60)
11
+ print("SellMe AI Sales Demo - Powered by Gemini")
12
+ print("=" * 60)
13
+ api_key = input("\nEnter your Gemini API Key: ").strip()
14
+
15
+ # Configure Gemini
16
+ genai.configure(api_key=api_key)
17
+ model = genai.GenerativeModel('gemini-1.5-flash')
18
+
19
+ print("\n[INFO] Gemini configured successfully!")
20
+ print("[INFO] Loading sales script...\n")
21
+
22
+ # Load sales_script.json
23
+ with open('sales_script.json', 'r', encoding='utf-8') as f:
24
+ data = json.load(f)
25
+
26
+ nodes_data = data['nodes']
27
+ edges_data = data['edges']
28
+
29
+ # Create mappings: string IDs <-> integer IDs
30
+ str_to_int = {}
31
+ int_to_str = {}
32
+
33
+ for idx, node_name in enumerate(nodes_data.keys()):
34
+ str_to_int[node_name] = idx
35
+ int_to_str[idx] = node_name
36
+
37
+ # Build Graph object
38
+ num_nodes = len(nodes_data)
39
+ graph = Graph(num_nodes, directed=True)
40
+
41
+ # Add edges with weights
42
+ for edge in edges_data:
43
+ from_node = str_to_int[edge['from']]
44
+ to_node = str_to_int[edge['to']]
45
+ weight = edge['weight']
46
+ graph.add_edge(from_node, to_node, weight)
47
+
48
+ print("[INFO] Sales graph built successfully!")
49
+ print(f"[INFO] Nodes: {num_nodes}, Edges: {len(edges_data)}\n")
50
+ print("=" * 60)
51
+ print("Starting Sales Conversation")
52
+ print("=" * 60)
53
+ print("(Type 'quit' to exit)\n")
54
+
55
+ # Start conversation
56
+ current_step = "start"
57
+ conversation_count = 0
58
+ max_steps = 20 # Safety limit
59
+
60
+ while current_step not in ["close_deal", "exit_bad"] and conversation_count < max_steps:
61
+ # Get current node ID
62
+ current_id = str_to_int[current_step]
63
+
64
+ # Calculate best path to close_deal using Bellman-Ford
65
+ distances = bellman_ford_list(graph, current_id)
66
+
67
+ if distances is None:
68
+ print("[ERROR] Negative cycle detected in sales graph!")
69
+ break
70
+
71
+ # Get close_deal node ID
72
+ close_deal_id = str_to_int['close_deal']
73
+
74
+ # Find the best immediate next step
75
+ adj_list = graph.get_list()
76
+ neighbors = adj_list[current_id]
77
+
78
+ if not neighbors:
79
+ print(f"[ERROR] No path forward from '{current_step}'")
80
+ break
81
+
82
+ # Pick the neighbor with shortest total distance to close_deal
83
+ best_next_id = None
84
+ best_total_distance = float('inf')
85
+
86
+ for neighbor_id, edge_weight in neighbors:
87
+ # Run Bellman-Ford from this neighbor to find distance to close_deal
88
+ neighbor_distances = bellman_ford_list(graph, neighbor_id)
89
+ if neighbor_distances and neighbor_distances[close_deal_id] != float('inf'):
90
+ total_distance = edge_weight + neighbor_distances[close_deal_id]
91
+ if total_distance < best_total_distance:
92
+ best_total_distance = total_distance
93
+ best_next_id = neighbor_id
94
+
95
+ if best_next_id is None:
96
+ print(f"[ERROR] No path found from '{current_step}' to 'close_deal'")
97
+ break
98
+
99
+ # Get next step name and script text
100
+ next_step_name = int_to_str[best_next_id]
101
+ script_text = nodes_data[next_step_name]
102
+
103
+ # Get user input (simulating client)
104
+ print(f"\n[CURRENT STEP: {current_step}]")
105
+ print(f"[NEXT TARGET: {next_step_name}]")
106
+ user_input = input("\nYou (Client): ").strip()
107
+
108
+ if user_input.lower() == 'quit':
109
+ print("\n[INFO] Exiting demo. Goodbye!")
110
+ break
111
+
112
+ # Create prompt for Gemini
113
+ prompt = f"""You are a professional sales representative for SellMe, an AI sales assistant platform.
114
+
115
+ Your goal is to move the conversation toward this step: '{next_step_name}'.
116
+ The sales script for this step says: '{script_text}'.
117
+ The client just said: '{user_input}'.
118
+
119
+ Generate a natural, conversational response in Ukrainian that:
120
+ 1. Acknowledges what the client said
121
+ 2. Smoothly guides toward the script message
122
+ 3. Sounds human and friendly, not robotic
123
+ 4. Keep it brief (1-2 sentences max)
124
+
125
+ Response:"""
126
+
127
+ # Get Gemini's response
128
+ print("\n[AI is thinking...]")
129
+ try:
130
+ response = model.generate_content(prompt)
131
+ ai_response = response.text.strip()
132
+
133
+ print(f"\nSellMe AI: {ai_response}")
134
+
135
+ except Exception as e:
136
+ print(f"\n[ERROR] Gemini API error: {e}")
137
+ print(f"[FALLBACK] Using script: {script_text}")
138
+
139
+ # Move to next step
140
+ current_step = next_step_name
141
+ conversation_count += 1
142
+
143
+ # End of conversation
144
+ print("\n" + "=" * 60)
145
+ if current_step == "close_deal":
146
+ print("[SUCCESS] Deal closed! 🎉")
147
+ print(f"Final message: {nodes_data[current_step]}")
148
+ elif current_step == "exit_bad":
149
+ print("[EXIT] Client not interested.")
150
+ print(f"Final message: {nodes_data[current_step]}")
151
+ else:
152
+ print(f"[INFO] Conversation ended at step: {current_step}")
153
+ print("=" * 60)
154
+
155
+
156
+ if __name__ == "__main__":
157
+ main()
sellme_pro.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import google.generativeai as genai
4
+ from graph_module import Graph
5
+ from algorithms import bellman_ford_list
6
+
7
+
8
+ def get_sentiment(user_text, model):
9
+ """
10
+ Analyze user sentiment using Gemini.
11
+
12
+ Args:
13
+ user_text: The user's message
14
+ model: Gemini model instance
15
+
16
+ Returns:
17
+ Float score from -1 (Angry) to +1 (Happy)
18
+ """
19
+ prompt = f"""Analyze the sentiment of this message and return ONLY a number between -1 and +1.
20
+ -1 = Very angry, hostile, frustrated
21
+ -0.5 = Slightly negative, uncertain
22
+ 0 = Neutral
23
+ +0.5 = Slightly positive, interested
24
+ +1 = Very happy, enthusiastic, eager
25
+
26
+ Message: "{user_text}"
27
+
28
+ Return only the number, nothing else:"""
29
+
30
+ try:
31
+ response = model.generate_content(prompt)
32
+ sentiment_text = response.text.strip()
33
+ # Extract number from response
34
+ sentiment_score = float(sentiment_text)
35
+ # Clamp to [-1, 1] range
36
+ sentiment_score = max(-1.0, min(1.0, sentiment_score))
37
+ return sentiment_score
38
+ except Exception as e:
39
+ print(f"[WARNING] Sentiment analysis failed: {e}. Defaulting to neutral (0.0)")
40
+ return 0.0
41
+
42
+
43
+ def update_weights(graph, str_to_int, original_edges, sentiment_score):
44
+ """
45
+ Dynamically update graph edge weights based on user sentiment.
46
+
47
+ Args:
48
+ graph: The Graph object
49
+ str_to_int: Mapping from string node names to integer IDs
50
+ original_edges: Original edge data from JSON
51
+ sentiment_score: Float from -1 to +1
52
+ """
53
+ # Strategy mapping
54
+ close_deal_id = str_to_int.get('close_deal')
55
+ discount_offer_id = str_to_int.get('discount_offer')
56
+ exit_bad_id = str_to_int.get('exit_bad')
57
+ pitch_crm_id = str_to_int.get('pitch_crm')
58
+ pitch_no_crm_id = str_to_int.get('pitch_no_crm')
59
+
60
+ # Rebuild graph with adjusted weights
61
+ for edge in original_edges:
62
+ from_id = str_to_int[edge['from']]
63
+ to_id = str_to_int[edge['to']]
64
+ original_weight = edge['weight']
65
+ adjusted_weight = original_weight
66
+
67
+ # Negative sentiment (< -0.3): Customer is unhappy/frustrated
68
+ if sentiment_score < -0.3:
69
+ # INCREASE weights for aggressive moves (hard selling is bad now)
70
+ if to_id == close_deal_id or to_id in [pitch_crm_id, pitch_no_crm_id]:
71
+ adjusted_weight = original_weight * 2.0 # Make these paths less attractive
72
+
73
+ # DECREASE weights for relationship-saving moves
74
+ if to_id == discount_offer_id or to_id == exit_bad_id:
75
+ adjusted_weight = original_weight * 0.5 # Make these paths more attractive
76
+
77
+ # Positive sentiment (> 0.3): Customer is happy/interested
78
+ elif sentiment_score > 0.3:
79
+ # DECREASE weights for close_deal (strike while iron is hot!)
80
+ if to_id == close_deal_id:
81
+ adjusted_weight = original_weight * 0.3 # Make closing much more attractive
82
+
83
+ # Also boost pitch effectiveness when customer is positive
84
+ if to_id in [pitch_crm_id, pitch_no_crm_id]:
85
+ adjusted_weight = original_weight * 0.7 # Make pitches more attractive
86
+
87
+ # Update the graph (we need to rebuild adjacency structures)
88
+ # Since Graph doesn't have update_edge, we'll handle this in the main loop
89
+ graph.adj_matrix[from_id][to_id] = adjusted_weight
90
+
91
+ # Update adjacency list
92
+ for i, (neighbor, _) in enumerate(graph.adj_list[from_id]):
93
+ if neighbor == to_id:
94
+ graph.adj_list[from_id][i] = (neighbor, adjusted_weight)
95
+ break
96
+
97
+
98
+ def main():
99
+ # Configuration: Get API Key from user
100
+ print("=" * 60)
101
+ print("SellMe PRO - Dynamic Sentiment-Based Sales AI")
102
+ print("=" * 60)
103
+ api_key = input("\nEnter your Gemini API Key: ").strip()
104
+
105
+ # Configure Gemini
106
+ genai.configure(api_key=api_key)
107
+ model = genai.GenerativeModel('gemini-1.5-pro')
108
+
109
+ print("\n[INFO] Gemini configured successfully!")
110
+ print("[INFO] Loading sales script...\n")
111
+
112
+ # Load sales_script.json
113
+ with open('sales_script.json', 'r', encoding='utf-8') as f:
114
+ data = json.load(f)
115
+
116
+ nodes_data = data['nodes']
117
+ edges_data = data['edges']
118
+
119
+ # Create mappings: string IDs <-> integer IDs
120
+ str_to_int = {}
121
+ int_to_str = {}
122
+
123
+ for idx, node_name in enumerate(nodes_data.keys()):
124
+ str_to_int[node_name] = idx
125
+ int_to_str[idx] = node_name
126
+
127
+ # Build Graph object
128
+ num_nodes = len(nodes_data)
129
+ graph = Graph(num_nodes, directed=True)
130
+
131
+ # Add edges with original weights
132
+ for edge in edges_data:
133
+ from_node = str_to_int[edge['from']]
134
+ to_node = str_to_int[edge['to']]
135
+ weight = edge['weight']
136
+ graph.add_edge(from_node, to_node, weight)
137
+
138
+ print("[INFO] Sales graph built successfully!")
139
+ print(f"[INFO] Nodes: {num_nodes}, Edges: {len(edges_data)}")
140
+ print("[INFO] Sentiment-based dynamic weighting enabled!\n")
141
+ print("=" * 60)
142
+ print("Starting Sales Conversation")
143
+ print("=" * 60)
144
+ print("(Type 'quit' to exit)\n")
145
+
146
+ # Start conversation
147
+ current_step = "start"
148
+ conversation_count = 0
149
+ max_steps = 20 # Safety limit
150
+
151
+ while current_step not in ["close_deal", "exit_bad"] and conversation_count < max_steps:
152
+ # Get current node ID
153
+ current_id = str_to_int[current_step]
154
+
155
+ # Get user input first
156
+ print(f"\n[CURRENT STEP: {current_step}]")
157
+ user_input = input("\nYou (Client): ").strip()
158
+
159
+ if user_input.lower() == 'quit':
160
+ print("\n[INFO] Exiting demo. Goodbye!")
161
+ break
162
+
163
+ # === SENTIMENT ANALYSIS ===
164
+ print("\n[AI is analyzing sentiment...]")
165
+ sentiment_score = get_sentiment(user_input, model)
166
+
167
+ # Determine sentiment category
168
+ if sentiment_score < -0.3:
169
+ sentiment_label = "NEGATIVE"
170
+ elif sentiment_score > 0.3:
171
+ sentiment_label = "POSITIVE"
172
+ else:
173
+ sentiment_label = "NEUTRAL"
174
+
175
+ print(f">>> Detected Sentiment: {sentiment_score:.2f} [{sentiment_label}]")
176
+
177
+ # === DYNAMIC WEIGHT UPDATE ===
178
+ if abs(sentiment_score) > 0.3:
179
+ print(">>> Strategy Changed! Adjusting conversation path...")
180
+ update_weights(graph, str_to_int, edges_data, sentiment_score)
181
+
182
+ # Calculate best path with updated weights
183
+ distances = bellman_ford_list(graph, current_id)
184
+
185
+ if distances is None:
186
+ print("[ERROR] Negative cycle detected in sales graph!")
187
+ break
188
+
189
+ # Get close_deal node ID
190
+ close_deal_id = str_to_int['close_deal']
191
+
192
+ # Find the best immediate next step
193
+ adj_list = graph.get_list()
194
+ neighbors = adj_list[current_id]
195
+
196
+ if not neighbors:
197
+ print(f"[ERROR] No path forward from '{current_step}'")
198
+ break
199
+
200
+ # Pick the neighbor with shortest total distance to close_deal
201
+ best_next_id = None
202
+ best_total_distance = float('inf')
203
+
204
+ for neighbor_id, edge_weight in neighbors:
205
+ # Run Bellman-Ford from this neighbor to find distance to close_deal
206
+ neighbor_distances = bellman_ford_list(graph, neighbor_id)
207
+ if neighbor_distances and neighbor_distances[close_deal_id] != float('inf'):
208
+ total_distance = edge_weight + neighbor_distances[close_deal_id]
209
+ if total_distance < best_total_distance:
210
+ best_total_distance = total_distance
211
+ best_next_id = neighbor_id
212
+
213
+ if best_next_id is None:
214
+ print(f"[ERROR] No path found from '{current_step}' to 'close_deal'")
215
+ break
216
+
217
+ # Get next step name and script text
218
+ next_step_name = int_to_str[best_next_id]
219
+ script_text = nodes_data[next_step_name]
220
+
221
+ print(f"[NEXT TARGET: {next_step_name}]")
222
+
223
+ # Create prompt for Gemini
224
+ prompt = f"""You are a professional sales representative for SellMe, an AI sales assistant platform.
225
+
226
+ Your goal is to move the conversation toward this step: '{next_step_name}'.
227
+ The sales script for this step says: '{script_text}'.
228
+ The client just said: '{user_input}'.
229
+ Client sentiment: {sentiment_score:.2f} ({sentiment_label})
230
+
231
+ Generate a natural, conversational response in Ukrainian that:
232
+ 1. Acknowledges what the client said and their emotional state
233
+ 2. Smoothly guides toward the script message
234
+ 3. Adjusts tone based on sentiment (softer if negative, enthusiastic if positive)
235
+ 4. Sounds human and friendly, not robotic
236
+ 5. Keep it brief (1-2 sentences max)
237
+
238
+ Response:"""
239
+
240
+ # Get Gemini's response
241
+ print("\n[AI is generating response...]")
242
+ try:
243
+ response = model.generate_content(prompt)
244
+ ai_response = response.text.strip()
245
+
246
+ print(f"\nSellMe AI: {ai_response}")
247
+
248
+ except Exception as e:
249
+ print(f"\n[ERROR] Gemini API error: {e}")
250
+ print(f"[FALLBACK] Using script: {script_text}")
251
+
252
+ # Move to next step
253
+ current_step = next_step_name
254
+ conversation_count += 1
255
+
256
+ # End of conversation
257
+ print("\n" + "=" * 60)
258
+ if current_step == "close_deal":
259
+ print("[SUCCESS] Deal closed!")
260
+ print(f"Final message: {nodes_data[current_step]}")
261
+ elif current_step == "exit_bad":
262
+ print("[EXIT] Client not interested.")
263
+ print(f"Final message: {nodes_data[current_step]}")
264
+ else:
265
+ print(f"[INFO] Conversation ended at step: {current_step}")
266
+ print("=" * 60)
267
+
268
+
269
+ if __name__ == "__main__":
270
+ main()