Spaces:
Running
Running
Commit Β·
dc3dc12
1
Parent(s): ce74a97
update on topic_for_story #1
Browse files- Agent_Chat_Classification.py +5 -1
- Agent_Chat_Classification_Helper.py +9 -63
- Classification_parameters.py +5 -0
- Prompt_building.py +1 -0
- Retrieve.py +41 -2
- app_nn.py +9 -1
- ask_llm_final_prompt.py +2 -0
- config.py +8 -0
Agent_Chat_Classification.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from Agent_Chat_Classification_Helper import call_classification_llm
|
| 2 |
-
from Classification_parameters import TRACKED_FIELDS
|
| 3 |
from supabase_ie import load_user_info, save_user_info, update_country
|
| 4 |
from db_5_process_session import _load_history
|
| 5 |
|
|
@@ -28,6 +28,9 @@ def analyze_message(user_id: str, query: str):
|
|
| 28 |
relevant_missing = analysis.get("relevant_missing_fields", [])
|
| 29 |
topic = analysis.get("topic", "personal")
|
| 30 |
response_mode = analysis.get("response_mode", "dialogic")
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
print(f"[DEBUG][CLASSIFICATION_RAW] analysis={analysis}")
|
| 33 |
# --- Update user_info in Supabase if new info found ---
|
|
@@ -56,6 +59,7 @@ def analyze_message(user_id: str, query: str):
|
|
| 56 |
"response_mode": response_mode,
|
| 57 |
"user_info": user_info,
|
| 58 |
"relevant_missing": relevant_missing,
|
|
|
|
| 59 |
"chunks": [], # retrieval results can be appended later
|
| 60 |
"needs_news_fetch": analysis.get("needs_news_fetch", False),
|
| 61 |
"news_topic": analysis.get("news_topic", ""),
|
|
|
|
| 1 |
from Agent_Chat_Classification_Helper import call_classification_llm
|
| 2 |
+
from Classification_parameters import TRACKED_FIELDS, ALLOWED_STORY_TOPICS
|
| 3 |
from supabase_ie import load_user_info, save_user_info, update_country
|
| 4 |
from db_5_process_session import _load_history
|
| 5 |
|
|
|
|
| 28 |
relevant_missing = analysis.get("relevant_missing_fields", [])
|
| 29 |
topic = analysis.get("topic", "personal")
|
| 30 |
response_mode = analysis.get("response_mode", "dialogic")
|
| 31 |
+
topic_for_story = analysis.get("topic_for_story", "none")
|
| 32 |
+
if topic_for_story not in ALLOWED_STORY_TOPICS:
|
| 33 |
+
topic_for_story = "none"
|
| 34 |
|
| 35 |
print(f"[DEBUG][CLASSIFICATION_RAW] analysis={analysis}")
|
| 36 |
# --- Update user_info in Supabase if new info found ---
|
|
|
|
| 59 |
"response_mode": response_mode,
|
| 60 |
"user_info": user_info,
|
| 61 |
"relevant_missing": relevant_missing,
|
| 62 |
+
"topic_for_story": topic_for_story,
|
| 63 |
"chunks": [], # retrieval results can be appended later
|
| 64 |
"needs_news_fetch": analysis.get("needs_news_fetch", False),
|
| 65 |
"news_topic": analysis.get("news_topic", ""),
|
Agent_Chat_Classification_Helper.py
CHANGED
|
@@ -4,7 +4,7 @@ from config import OPENAI_CHAT_MODEL
|
|
| 4 |
from openai import OpenAI
|
| 5 |
import json
|
| 6 |
from supabase_ie import load_user_info
|
| 7 |
-
from Classification_parameters import TOPIC_DESCRIPTIONS, RESPONSE_MODE_DESCRIPTIONS, TRACKED_FIELDS, FIELD_DESCRIPTIONS
|
| 8 |
client = OpenAI()
|
| 9 |
|
| 10 |
|
|
@@ -35,6 +35,7 @@ def call_classification_llm(query: str, recent_history: list, user_info: dict, m
|
|
| 35 |
"topic": "",
|
| 36 |
"response_mode": "",
|
| 37 |
"emotion": "",
|
|
|
|
| 38 |
"needs_news_fetch": False,
|
| 39 |
"news_topic": [],
|
| 40 |
"news_question": ""
|
|
@@ -63,6 +64,12 @@ def call_classification_llm(query: str, recent_history: list, user_info: dict, m
|
|
| 63 |
* topic: one of {list(TOPIC_DESCRIPTIONS.keys())}
|
| 64 |
* response_mode: one of {list(RESPONSE_MODE_DESCRIPTIONS.keys())}
|
| 65 |
* emotion: one of ["curious","happy","sad","angry","neutral"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
- needs_news_fetch:
|
| 68 |
* true if the user is clearly asking for news, current events, or external information lookup.
|
|
@@ -106,71 +113,10 @@ def call_classification_llm(query: str, recent_history: list, user_info: dict, m
|
|
| 106 |
"topic": "",
|
| 107 |
"response_mode": "",
|
| 108 |
"emotion": "",
|
|
|
|
| 109 |
"needs_news_fetch": False,
|
| 110 |
"news_topic": [],
|
| 111 |
"news_question": ""
|
| 112 |
}
|
| 113 |
|
| 114 |
|
| 115 |
-
# def summarize_user_info(user_id: str) -> str:
|
| 116 |
-
# """Fetch user profile from Supabase, create a natural language summary, and return it as a string."""
|
| 117 |
-
|
| 118 |
-
# data = load_user_info(user_id)
|
| 119 |
-
# profile = data.get("user_profile", {})
|
| 120 |
-
|
| 121 |
-
# parts = []
|
| 122 |
-
|
| 123 |
-
# # Location
|
| 124 |
-
# if profile.get("living_country"):
|
| 125 |
-
# parts.append(f"lives in {profile['living_country']}")
|
| 126 |
-
# if profile.get("origin_country"):
|
| 127 |
-
# parts.append(f"was born in {profile['origin_country']}")
|
| 128 |
-
|
| 129 |
-
# # Kids
|
| 130 |
-
# kids = profile.get("kids")
|
| 131 |
-
# if kids:
|
| 132 |
-
# try:
|
| 133 |
-
# if isinstance(kids, str): # sometimes JSONB comes back as string
|
| 134 |
-
# kids = json.loads(kids)
|
| 135 |
-
# kids_list = [f"{k.get('name','?')} ({k.get('age','?')} yrs)" for k in kids if isinstance(k, dict)]
|
| 136 |
-
# if kids_list:
|
| 137 |
-
# parts.append("has kids: " + ", ".join(kids_list))
|
| 138 |
-
# except Exception as e:
|
| 139 |
-
# print("β οΈ Could not parse kids:", kids, e)
|
| 140 |
-
|
| 141 |
-
# # Arrays: hobbies, interests, languages, sports
|
| 142 |
-
# for field, label in [
|
| 143 |
-
# ("hobbies", "enjoys"),
|
| 144 |
-
# ("interests", "is interested in"),
|
| 145 |
-
# ("sports_played", "plays"),
|
| 146 |
-
# ("sports_watched", "watches"),
|
| 147 |
-
# ("language_spoken", "speaks")
|
| 148 |
-
# ]:
|
| 149 |
-
# values = profile.get(field)
|
| 150 |
-
# if values:
|
| 151 |
-
# if isinstance(values, str):
|
| 152 |
-
# values = [values]
|
| 153 |
-
# if isinstance(values, list) and values:
|
| 154 |
-
# parts.append(f"{label} " + ", ".join(values))
|
| 155 |
-
|
| 156 |
-
# # Misc single fields
|
| 157 |
-
# if profile.get("profession"):
|
| 158 |
-
# parts.append(f"works as {profile['profession']}")
|
| 159 |
-
# if profile.get("favorite_team"):
|
| 160 |
-
# parts.append(f"supports {profile['favorite_team']}")
|
| 161 |
-
# if profile.get("favorite_food"):
|
| 162 |
-
# parts.append(f"likes {profile['favorite_food']}")
|
| 163 |
-
# if profile.get("favorite_book"):
|
| 164 |
-
# parts.append(f"favorite book is {profile['favorite_book']}")
|
| 165 |
-
# if profile.get("favorite_movie"):
|
| 166 |
-
# parts.append(f"favorite movie is {profile['favorite_movie']}")
|
| 167 |
-
# if profile.get("pets"):
|
| 168 |
-
# parts.append(f"has pets: {profile['pets']}")
|
| 169 |
-
# if profile.get("education"):
|
| 170 |
-
# parts.append(f"studied {profile['education']}")
|
| 171 |
-
|
| 172 |
-
# # Final summary
|
| 173 |
-
# summary = "The user " + "; ".join(parts) + "." if parts else "No profile information available."
|
| 174 |
-
# print(f"β
User profile summary generated:\n{summary}")
|
| 175 |
-
|
| 176 |
-
# return summary
|
|
|
|
| 4 |
from openai import OpenAI
|
| 5 |
import json
|
| 6 |
from supabase_ie import load_user_info
|
| 7 |
+
from Classification_parameters import TOPIC_DESCRIPTIONS, RESPONSE_MODE_DESCRIPTIONS, TRACKED_FIELDS, FIELD_DESCRIPTIONS, ALLOWED_STORY_TOPICS
|
| 8 |
client = OpenAI()
|
| 9 |
|
| 10 |
|
|
|
|
| 35 |
"topic": "",
|
| 36 |
"response_mode": "",
|
| 37 |
"emotion": "",
|
| 38 |
+
"topic_for_story": "",
|
| 39 |
"needs_news_fetch": False,
|
| 40 |
"news_topic": [],
|
| 41 |
"news_question": ""
|
|
|
|
| 64 |
* topic: one of {list(TOPIC_DESCRIPTIONS.keys())}
|
| 65 |
* response_mode: one of {list(RESPONSE_MODE_DESCRIPTIONS.keys())}
|
| 66 |
* emotion: one of ["curious","happy","sad","angry","neutral"]
|
| 67 |
+
|
| 68 |
+
- topic_for_story:
|
| 69 |
+
* Choose exactly one label from this list (closed set):
|
| 70 |
+
[{allowed_str}]
|
| 71 |
+
* Return "none" if no fitting label is clearly supported.
|
| 72 |
+
* Never invent new labels.
|
| 73 |
|
| 74 |
- needs_news_fetch:
|
| 75 |
* true if the user is clearly asking for news, current events, or external information lookup.
|
|
|
|
| 113 |
"topic": "",
|
| 114 |
"response_mode": "",
|
| 115 |
"emotion": "",
|
| 116 |
+
"topic_for_story": "none",
|
| 117 |
"needs_news_fetch": False,
|
| 118 |
"news_topic": [],
|
| 119 |
"news_question": ""
|
| 120 |
}
|
| 121 |
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Classification_parameters.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
| 1 |
# ==============================
|
| 2 |
# Descriptions for Classification
|
| 3 |
# ==============================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
TRACKED_FIELDS = [
|
| 5 |
"name", "living_country", "origin_country","living_place",
|
| 6 |
"origin_place","number_of_brothers_and_sisters","kids",
|
|
|
|
| 1 |
# ==============================
|
| 2 |
# Descriptions for Classification
|
| 3 |
# ==============================
|
| 4 |
+
ALLOWED_STORY_TOPICS = [
|
| 5 |
+
"fortune","confidence","mental_strength","bullying","fear_phobias",
|
| 6 |
+
"friendship","love","sex","meaning_of_life","justice_fairness","none"
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
TRACKED_FIELDS = [
|
| 10 |
"name", "living_country", "origin_country","living_place",
|
| 11 |
"origin_place","number_of_brothers_and_sisters","kids",
|
Prompt_building.py
CHANGED
|
@@ -180,6 +180,7 @@ def build_socratic_prompt(
|
|
| 180 |
style_examples_path: str = "style_examples.json",
|
| 181 |
relevant_missing: list = None,
|
| 182 |
topic: str = None,
|
|
|
|
| 183 |
response_mode: str = None,
|
| 184 |
emotion: dict = None,
|
| 185 |
) -> str:
|
|
|
|
| 180 |
style_examples_path: str = "style_examples.json",
|
| 181 |
relevant_missing: list = None,
|
| 182 |
topic: str = None,
|
| 183 |
+
topic_for_story: str = None,
|
| 184 |
response_mode: str = None,
|
| 185 |
emotion: dict = None,
|
| 186 |
) -> str:
|
Retrieve.py
CHANGED
|
@@ -3,14 +3,15 @@
|
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
import numpy as np
|
| 6 |
-
from typing import List, Dict
|
|
|
|
| 7 |
from langchain_community.vectorstores import FAISS
|
| 8 |
import streamlit as st
|
| 9 |
import numpy as np
|
| 10 |
#from db_paths import (PERSONAL_INFO_CHUNKS_PATH,CHAT_HISTORY_CHUNKS_PATH)
|
| 11 |
import json
|
| 12 |
from supabase_ie import load_user_info, load_history_for_display, download_faiss_from_supabase
|
| 13 |
-
|
| 14 |
|
| 15 |
#used in embed_query
|
| 16 |
def normalize(v):
|
|
@@ -193,3 +194,41 @@ def compute_metadata_boost(metadata: Dict, query: str, filter_keywords: List[str
|
|
| 193 |
boost += weight_filter
|
| 194 |
return boost
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
import numpy as np
|
| 6 |
+
from typing import List, Dict, Optional, Any
|
| 7 |
+
import requests
|
| 8 |
from langchain_community.vectorstores import FAISS
|
| 9 |
import streamlit as st
|
| 10 |
import numpy as np
|
| 11 |
#from db_paths import (PERSONAL_INFO_CHUNKS_PATH,CHAT_HISTORY_CHUNKS_PATH)
|
| 12 |
import json
|
| 13 |
from supabase_ie import load_user_info, load_history_for_display, download_faiss_from_supabase
|
| 14 |
+
from config import SUPABASE_URL, SUPABASE_HEADERS
|
| 15 |
|
| 16 |
#used in embed_query
|
| 17 |
def normalize(v):
|
|
|
|
| 194 |
boost += weight_filter
|
| 195 |
return boost
|
| 196 |
|
| 197 |
+
PERSONAL_BUCKET = {"personal", "advice"}
|
| 198 |
+
|
| 199 |
+
def get_story_from_supabase(
|
| 200 |
+
user_id: str,
|
| 201 |
+
username: str,
|
| 202 |
+
conversation_type: str,
|
| 203 |
+
topic_for_story: str | None,
|
| 204 |
+
) -> dict | None:
|
| 205 |
+
"""
|
| 206 |
+
If conversation_type is personal/advice and topic_for_story is set,
|
| 207 |
+
call a Supabase RPC that:
|
| 208 |
+
- selects a story with your rules (unseen first; else seen<=1 and >90d ago),
|
| 209 |
+
- logs usage,
|
| 210 |
+
- returns a compact JSON payload for the prompt builder.
|
| 211 |
+
|
| 212 |
+
Returns None if no suitable story.
|
| 213 |
+
"""
|
| 214 |
+
if conversation_type not in PERSONAL_BUCKET:
|
| 215 |
+
return None
|
| 216 |
+
if not topic_for_story or topic_for_story == "none":
|
| 217 |
+
return None
|
| 218 |
+
|
| 219 |
+
fn = "pick_and_log_story_with_history_rpc"
|
| 220 |
+
payload = {
|
| 221 |
+
"p_user_id": user_id,
|
| 222 |
+
"p_topic": topic_for_story,
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
url = f"{SUPABASE_URL}/rest/v1/rpc/{fn}"
|
| 226 |
+
r = requests.post(url, headers=SUPABASE_HEADERS, json=payload, timeout=20)
|
| 227 |
+
|
| 228 |
+
if r.status_code == 404 or not r.text or r.text == "null":
|
| 229 |
+
return None
|
| 230 |
+
r.raise_for_status()
|
| 231 |
+
story = r.json()
|
| 232 |
+
return story
|
| 233 |
+
|
| 234 |
+
#return r.json()
|
app_nn.py
CHANGED
|
@@ -28,7 +28,7 @@ from Agent_Emotional_Evaluation import analyze_emotions_from_history
|
|
| 28 |
from Agent_Chat_Classification import analyze_message
|
| 29 |
#from Agent_Chat_Classification_Helper import load_user_info
|
| 30 |
|
| 31 |
-
from Retrieve import retrieve_all_chunks
|
| 32 |
from Prompt_building import TOPIC_TO_DBS
|
| 33 |
from ask_llm_final_prompt import ask_socrates
|
| 34 |
|
|
@@ -207,12 +207,20 @@ def run_chat_app(user_id: str, username:str, profile: Dict[str, Any],ui_lang:str
|
|
| 207 |
topic_to_dbs=TOPIC_TO_DBS,
|
| 208 |
)
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
# ---- Compose final answer ----
|
| 211 |
final_reply_en = ask_socrates(
|
| 212 |
user_input=user_msg_en,
|
| 213 |
retrieved_chunks=chunks,
|
| 214 |
relevant_missing=analysis.get("relevant_missing"),
|
| 215 |
topic=analysis.get("topic"),
|
|
|
|
| 216 |
response_mode=analysis.get("response_mode"),
|
| 217 |
emotion=emotion_result,
|
| 218 |
user_id=user_id,
|
|
|
|
| 28 |
from Agent_Chat_Classification import analyze_message
|
| 29 |
#from Agent_Chat_Classification_Helper import load_user_info
|
| 30 |
|
| 31 |
+
from Retrieve import retrieve_all_chunks, get_story_from_supabase
|
| 32 |
from Prompt_building import TOPIC_TO_DBS
|
| 33 |
from ask_llm_final_prompt import ask_socrates
|
| 34 |
|
|
|
|
| 207 |
topic_to_dbs=TOPIC_TO_DBS,
|
| 208 |
)
|
| 209 |
|
| 210 |
+
socratic_story = get_story_from_supabase(
|
| 211 |
+
user_id=user_id,
|
| 212 |
+
username=username,
|
| 213 |
+
topic_for_story=analysis.get("topic_for_story"),
|
| 214 |
+
conversation_type=analysis.get("topic"),
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
# ---- Compose final answer ----
|
| 218 |
final_reply_en = ask_socrates(
|
| 219 |
user_input=user_msg_en,
|
| 220 |
retrieved_chunks=chunks,
|
| 221 |
relevant_missing=analysis.get("relevant_missing"),
|
| 222 |
topic=analysis.get("topic"),
|
| 223 |
+
topic_for_story=analysis.get("topic_for_story"),
|
| 224 |
response_mode=analysis.get("response_mode"),
|
| 225 |
emotion=emotion_result,
|
| 226 |
user_id=user_id,
|
ask_llm_final_prompt.py
CHANGED
|
@@ -19,6 +19,7 @@ def ask_socrates(
|
|
| 19 |
model=OPENAI_CHAT_MODEL,
|
| 20 |
relevant_missing=None,
|
| 21 |
topic=None,
|
|
|
|
| 22 |
response_mode=None,
|
| 23 |
emotion=None
|
| 24 |
):
|
|
@@ -29,6 +30,7 @@ def ask_socrates(
|
|
| 29 |
user_id=user_id,
|
| 30 |
relevant_missing=relevant_missing,
|
| 31 |
topic=topic,
|
|
|
|
| 32 |
response_mode=response_mode,
|
| 33 |
emotion=emotion
|
| 34 |
)
|
|
|
|
| 19 |
model=OPENAI_CHAT_MODEL,
|
| 20 |
relevant_missing=None,
|
| 21 |
topic=None,
|
| 22 |
+
topic_for_story=None,
|
| 23 |
response_mode=None,
|
| 24 |
emotion=None
|
| 25 |
):
|
|
|
|
| 30 |
user_id=user_id,
|
| 31 |
relevant_missing=relevant_missing,
|
| 32 |
topic=topic,
|
| 33 |
+
topic_for_story=topic_for_story,
|
| 34 |
response_mode=response_mode,
|
| 35 |
emotion=emotion
|
| 36 |
)
|
config.py
CHANGED
|
@@ -28,6 +28,7 @@ HF_EMBEDDING_MODEL = _get("HF_EMBEDDING_MODEL", required=False, default="s
|
|
| 28 |
GNEWS_KEY = _get("GNEWS_KEY", required=False, default="")
|
| 29 |
|
| 30 |
|
|
|
|
| 31 |
# ββ Models βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 32 |
OPENAI_CHAT_MODEL = os.getenv("OPENAI_CHAT_MODEL", "gpt-4.1-mini")
|
| 33 |
OPENAI_CLASSIFIER_MODEL = os.getenv("OPENAI_CLASSIFIER_MODEL", "gpt-4.1-mini")
|
|
@@ -77,3 +78,10 @@ def get_username() -> str | None:
|
|
| 77 |
st.session_state.get("username")
|
| 78 |
or username_from_email(st.session_state.get("email"))
|
| 79 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
GNEWS_KEY = _get("GNEWS_KEY", required=False, default="")
|
| 29 |
|
| 30 |
|
| 31 |
+
|
| 32 |
# ββ Models βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 33 |
OPENAI_CHAT_MODEL = os.getenv("OPENAI_CHAT_MODEL", "gpt-4.1-mini")
|
| 34 |
OPENAI_CLASSIFIER_MODEL = os.getenv("OPENAI_CLASSIFIER_MODEL", "gpt-4.1-mini")
|
|
|
|
| 78 |
st.session_state.get("username")
|
| 79 |
or username_from_email(st.session_state.get("email"))
|
| 80 |
)
|
| 81 |
+
|
| 82 |
+
# Reusable headers for PostgREST / RPC calls
|
| 83 |
+
SUPABASE_HEADERS = {
|
| 84 |
+
"apikey": SUPABASE_SERVICE_KEY,
|
| 85 |
+
"Authorization": f"Bearer {SUPABASE_SERVICE_KEY}",
|
| 86 |
+
"Content-Type": "application/json",
|
| 87 |
+
}
|