Spaces:
Sleeping
Sleeping
Commit
·
da91f13
1
Parent(s):
8aef1af
sellme_v2
Browse files- algorithms.py +6 -4
- app.py +68 -0
algorithms.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
-
def bellman_ford_list(graph, start_node, visited_nodes=None, client_type="B2B"):
|
| 2 |
"""
|
| 3 |
Advanced Bellman-Ford Algorithm.
|
| 4 |
|
| 5 |
Features:
|
| 6 |
1. Dynamic Weights based on Client Type (B2B prefers logic, B2C prefers speed).
|
| 7 |
2. Penalty for re-visiting nodes (avoid loops).
|
|
|
|
| 8 |
"""
|
| 9 |
|
| 10 |
# Ініціалізація
|
|
@@ -19,6 +20,9 @@ def bellman_ford_list(graph, start_node, visited_nodes=None, client_type="B2B"):
|
|
| 19 |
"B2C": {"logic": 1.5, "emotion": 0.7, "speed": 0.5}
|
| 20 |
}
|
| 21 |
modifiers = type_modifier.get(client_type, {"logic": 1.0, "emotion": 1.0, "speed": 1.0})
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
# Основний цикл релаксації
|
| 24 |
for _ in range(num_vertices - 1):
|
|
@@ -26,9 +30,7 @@ def bellman_ford_list(graph, start_node, visited_nodes=None, client_type="B2B"):
|
|
| 26 |
for v, weight in graph.adj_list[u]:
|
| 27 |
|
| 28 |
# --- ПОКРАЩЕННЯ 1: Динамічна вага ---
|
| 29 |
-
|
| 30 |
-
# Поки що просто емулюємо:
|
| 31 |
-
current_weight = weight
|
| 32 |
|
| 33 |
# --- ПОКРАЩЕННЯ 2: Штраф за повторення ---
|
| 34 |
if visited_nodes and v in visited_nodes:
|
|
|
|
| 1 |
+
def bellman_ford_list(graph, start_node, visited_nodes=None, client_type="B2B", sentiment_score=0.0):
|
| 2 |
"""
|
| 3 |
Advanced Bellman-Ford Algorithm.
|
| 4 |
|
| 5 |
Features:
|
| 6 |
1. Dynamic Weights based on Client Type (B2B prefers logic, B2C prefers speed).
|
| 7 |
2. Penalty for re-visiting nodes (avoid loops).
|
| 8 |
+
3. Sentiment adjustment (-1 angry to +1 happy affects aggressive paths).
|
| 9 |
"""
|
| 10 |
|
| 11 |
# Ініціалізація
|
|
|
|
| 20 |
"B2C": {"logic": 1.5, "emotion": 0.7, "speed": 0.5}
|
| 21 |
}
|
| 22 |
modifiers = type_modifier.get(client_type, {"logic": 1.0, "emotion": 1.0, "speed": 1.0})
|
| 23 |
+
|
| 24 |
+
# Sentiment modifier: negative sentiment increases costs, positive decreases
|
| 25 |
+
sentiment_factor = 1.0 - (sentiment_score * 0.3) # Range: 0.7 (happy) to 1.3 (angry)
|
| 26 |
|
| 27 |
# Основний цикл релаксації
|
| 28 |
for _ in range(num_vertices - 1):
|
|
|
|
| 30 |
for v, weight in graph.adj_list[u]:
|
| 31 |
|
| 32 |
# --- ПОКРАЩЕННЯ 1: Динамічна вага ---
|
| 33 |
+
current_weight = weight * sentiment_factor
|
|
|
|
|
|
|
| 34 |
|
| 35 |
# --- ПОКРАЩЕННЯ 2: Штраф за повторення ---
|
| 36 |
if visited_nodes and v in visited_nodes:
|
app.py
CHANGED
|
@@ -21,6 +21,7 @@ if "lead_info" not in st.session_state: st.session_state.lead_info = {}
|
|
| 21 |
if "visited_history" not in st.session_state: st.session_state.visited_history = [] # Track visited nodes
|
| 22 |
if "current_archetype" not in st.session_state: st.session_state.current_archetype = "UNKNOWN"
|
| 23 |
if "reasoning" not in st.session_state: st.session_state.reasoning = ""
|
|
|
|
| 24 |
# Checklist status based on your screenshot
|
| 25 |
if "checklist" not in st.session_state:
|
| 26 |
st.session_state.checklist = {
|
|
@@ -361,6 +362,73 @@ elif st.session_state.page == "chat":
|
|
| 361 |
draw_graph(graph_data, st.session_state.current_node, path),
|
| 362 |
use_container_width=True # Розтягує граф на всю ширину колонки
|
| 363 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
|
| 365 |
with col_chat:
|
| 366 |
for msg in st.session_state.messages:
|
|
|
|
| 21 |
if "visited_history" not in st.session_state: st.session_state.visited_history = [] # Track visited nodes
|
| 22 |
if "current_archetype" not in st.session_state: st.session_state.current_archetype = "UNKNOWN"
|
| 23 |
if "reasoning" not in st.session_state: st.session_state.reasoning = ""
|
| 24 |
+
if "current_sentiment" not in st.session_state: st.session_state.current_sentiment = 0.0
|
| 25 |
# Checklist status based on your screenshot
|
| 26 |
if "checklist" not in st.session_state:
|
| 27 |
st.session_state.checklist = {
|
|
|
|
| 362 |
draw_graph(graph_data, st.session_state.current_node, path),
|
| 363 |
use_container_width=True # Розтягує граф на всю ширину колонки
|
| 364 |
)
|
| 365 |
+
|
| 366 |
+
# --- BELLMAN-FORD ALGORITHM LOGS ---
|
| 367 |
+
st.markdown("---")
|
| 368 |
+
with st.expander("🧮 Алгоритм Беллмана-Форда (Live Logs)", expanded=False):
|
| 369 |
+
st.markdown("""
|
| 370 |
+
**Математика прийняття рішень:**
|
| 371 |
+
Алгоритм шукає шлях $P$, де сума ваг $W$ є мінімальною:
|
| 372 |
+
$$ D[v] = \\min(D[v], D[u] + W(u, v)) $$
|
| 373 |
+
""")
|
| 374 |
+
|
| 375 |
+
# 1. Calculate real data
|
| 376 |
+
visited_ids = [node_to_id[n] for n in st.session_state.get('visited_history', []) if n in node_to_id]
|
| 377 |
+
client_type = st.session_state.lead_info.get('type', 'B2B')
|
| 378 |
+
current_sentiment = st.session_state.get("current_sentiment", 0.0)
|
| 379 |
+
|
| 380 |
+
# Call algorithm to get distance array
|
| 381 |
+
raw_dist = bellman_ford_list(
|
| 382 |
+
graph,
|
| 383 |
+
curr_id,
|
| 384 |
+
visited_nodes=visited_ids,
|
| 385 |
+
client_type=client_type,
|
| 386 |
+
sentiment_score=current_sentiment
|
| 387 |
+
)
|
| 388 |
+
|
| 389 |
+
# 2. Build beautiful table for humans
|
| 390 |
+
debug_data = []
|
| 391 |
+
target_path_set = set(path) # Path we already found for graph
|
| 392 |
+
|
| 393 |
+
for i, d in enumerate(raw_dist):
|
| 394 |
+
node_name = id_to_node[i]
|
| 395 |
+
|
| 396 |
+
# Format infinity
|
| 397 |
+
cost_display = "∞" if d == float('inf') else round(d, 2)
|
| 398 |
+
|
| 399 |
+
# Node status
|
| 400 |
+
status = "⬜"
|
| 401 |
+
if node_name == st.session_state.current_node: status = "📍 Start"
|
| 402 |
+
elif node_name in target_path_set: status = "✨ Path"
|
| 403 |
+
elif d == float('inf'): status = "🚫 Unreachable"
|
| 404 |
+
|
| 405 |
+
debug_data.append({
|
| 406 |
+
"Node": node_name,
|
| 407 |
+
"Cost (Weight)": cost_display,
|
| 408 |
+
"Status": status
|
| 409 |
+
})
|
| 410 |
+
|
| 411 |
+
# Convert to DataFrame
|
| 412 |
+
df_log = pd.DataFrame(debug_data)
|
| 413 |
+
|
| 414 |
+
# Sort: path first, then cheap, then expensive
|
| 415 |
+
df_log["sort_key"] = df_log["Cost (Weight)"].apply(lambda x: 9999 if x == "∞" else float(x))
|
| 416 |
+
df_log = df_log.sort_values(by="sort_key").drop(columns=["sort_key"])
|
| 417 |
+
|
| 418 |
+
# Display
|
| 419 |
+
st.dataframe(
|
| 420 |
+
df_log,
|
| 421 |
+
use_container_width=True,
|
| 422 |
+
hide_index=True
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# 3. Explanation "Why?"
|
| 426 |
+
st.info(f"""
|
| 427 |
+
**Фактори впливу:**
|
| 428 |
+
- 🎭 **Емоція:** {current_sentiment} (впливає на вартість агресивних кроків)
|
| 429 |
+
- 🏢 **Тип:** {client_type} (змінює пріоритет швидкості)
|
| 430 |
+
- 🔄 **Повтори:** Вузли, де ми вже були, мають штраф x50.
|
| 431 |
+
""")
|
| 432 |
|
| 433 |
with col_chat:
|
| 434 |
for msg in st.session_state.messages:
|