Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -1881,90 +1881,85 @@ def get_price_trends():
|
|
| 1881 |
|
| 1882 |
@app.route("/api/ai/chat", methods=["POST"])
|
| 1883 |
def ai_chat():
|
| 1884 |
-
auth_header = request.headers.get("Authorization", "")
|
| 1885 |
uid = None
|
| 1886 |
-
|
| 1887 |
-
intent = "unknown"
|
| 1888 |
try:
|
| 1889 |
if not FIREBASE_INITIALIZED or not gemini_client:
|
| 1890 |
return jsonify({'error': 'Server or AI service not ready.'}), 503
|
| 1891 |
|
| 1892 |
uid = verify_token(auth_header)
|
| 1893 |
-
|
| 1894 |
data = request.get_json()
|
| 1895 |
if not data:
|
| 1896 |
return jsonify({"error": "Invalid request data."}), 400
|
| 1897 |
-
|
| 1898 |
user_message = data.get("message", "").strip()
|
| 1899 |
if not user_message:
|
| 1900 |
return jsonify({"error": "Message cannot be empty."}), 400
|
| 1901 |
|
|
|
|
| 1902 |
classify_prompt = f"""
|
| 1903 |
-
|
| 1904 |
-
|
| 1905 |
-
|
| 1906 |
-
|
| 1907 |
-
|
| 1908 |
-
|
| 1909 |
-
|
| 1910 |
-
|
| 1911 |
-
|
| 1912 |
-
|
| 1913 |
-
|
| 1914 |
-
|
| 1915 |
-
|
| 1916 |
-
|
| 1917 |
-
|
| 1918 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1919 |
valid_intents = ["platform_data_query", "price_trend_query", "general_agri_info", "other"]
|
| 1920 |
-
if
|
| 1921 |
-
|
|
|
|
|
|
|
| 1922 |
intent = "general_agri_info"
|
| 1923 |
-
|
| 1924 |
-
context_for_gemini = ""
|
| 1925 |
-
platform_data_summary = ""
|
| 1926 |
-
trend_analysis_summary = ""
|
| 1927 |
|
|
|
|
|
|
|
| 1928 |
if intent == "platform_data_query":
|
| 1929 |
platform_data_summary, _ = _fetch_platform_data_for_chat(user_message)
|
| 1930 |
if platform_data_summary:
|
| 1931 |
-
context_for_gemini += f"Current Platform Data Context
|
| 1932 |
-
|
| 1933 |
elif intent == "price_trend_query":
|
| 1934 |
-
|
| 1935 |
-
|
| 1936 |
-
if crop_match:
|
| 1937 |
-
trend_crop = crop_match.group(1).strip()
|
| 1938 |
-
if crop_match.group(2):
|
| 1939 |
-
trend_location = crop_match.group(2).strip()
|
| 1940 |
-
|
| 1941 |
-
trend_analysis_summary, _ = _get_price_trend_analysis_for_chat(crop_type=trend_crop, location=trend_location)
|
| 1942 |
if trend_analysis_summary:
|
| 1943 |
-
context_for_gemini += f"Price Trend Analysis Context
|
| 1944 |
|
|
|
|
| 1945 |
main_prompt = f"""
|
| 1946 |
-
You are Tunasonga Agri Assistant
|
| 1947 |
-
Your goal is to provide helpful, concise, and accurate information. Your persona is professional, friendly, and supportive of farmers and agri-businesses.
|
| 1948 |
-
The user's original query intent was classified as: {intent}
|
| 1949 |
-
{context_for_gemini}
|
| 1950 |
-
Based on the user's query and any provided context above, please formulate your answer to the user.
|
| 1951 |
User Query: "{user_message}"
|
| 1952 |
-
|
| 1953 |
-
|
| 1954 |
-
|
| 1955 |
-
- For 'general_agri_info': Use your broad agricultural knowledge. Focus on practices relevant to the SADC region, smallholder farmers, climate-smart agriculture, market access, and agri-business development. Provide actionable advice if possible.
|
| 1956 |
-
- If the query is unclear, classified as "other", or if the context is insufficient for a specific query: Provide a polite general response, ask for clarification, or gently guide the user on how you can help (e.g., "I can help with finding produce, getting price trends, or general farming advice. What would you like to know?").
|
| 1957 |
-
Keep your answers clear and easy to understand. Avoid overly technical jargon unless necessary and explain it.
|
| 1958 |
-
Answer:
|
| 1959 |
"""
|
| 1960 |
-
|
| 1961 |
-
|
| 1962 |
-
|
| 1963 |
-
|
| 1964 |
-
|
| 1965 |
-
|
| 1966 |
-
|
| 1967 |
-
|
|
|
|
|
|
|
|
|
|
| 1968 |
try:
|
| 1969 |
db.reference(f'ai_chat_history/{uid}/{str(uuid.uuid4())}', app=db_app).set({
|
| 1970 |
'user_message': user_message,
|
|
@@ -1974,15 +1969,19 @@ Answer:
|
|
| 1974 |
})
|
| 1975 |
except Exception as chat_history_error:
|
| 1976 |
logger.error(f"Failed to store chat history for UID {uid}: {chat_history_error}")
|
| 1977 |
-
|
|
|
|
| 1978 |
return jsonify({"response": ai_response_text, "intent": intent})
|
| 1979 |
|
| 1980 |
-
except AttributeError as ae:
|
| 1981 |
-
|
| 1982 |
-
|
| 1983 |
-
|
| 1984 |
-
|
| 1985 |
-
|
|
|
|
|
|
|
|
|
|
| 1986 |
|
| 1987 |
@app.route('/api/user/ai-chat-history', methods=['GET'])
|
| 1988 |
def get_ai_chat_history():
|
|
|
|
| 1881 |
|
| 1882 |
@app.route("/api/ai/chat", methods=["POST"])
|
| 1883 |
def ai_chat():
|
| 1884 |
+
auth_header = request.headers.get("Authorization", "")
|
| 1885 |
uid = None
|
| 1886 |
+
intent = "general_agri_info" # start with a valid default rather than "unknown"
|
|
|
|
| 1887 |
try:
|
| 1888 |
if not FIREBASE_INITIALIZED or not gemini_client:
|
| 1889 |
return jsonify({'error': 'Server or AI service not ready.'}), 503
|
| 1890 |
|
| 1891 |
uid = verify_token(auth_header)
|
| 1892 |
+
|
| 1893 |
data = request.get_json()
|
| 1894 |
if not data:
|
| 1895 |
return jsonify({"error": "Invalid request data."}), 400
|
| 1896 |
+
|
| 1897 |
user_message = data.get("message", "").strip()
|
| 1898 |
if not user_message:
|
| 1899 |
return jsonify({"error": "Message cannot be empty."}), 400
|
| 1900 |
|
| 1901 |
+
# 1) Build a more constrained classification prompt
|
| 1902 |
classify_prompt = f"""
|
| 1903 |
+
Classify the user’s query into exactly one of these categories (and nothing else):
|
| 1904 |
+
platform_data_query
|
| 1905 |
+
price_trend_query
|
| 1906 |
+
general_agri_info
|
| 1907 |
+
other
|
| 1908 |
+
|
| 1909 |
+
User Query: "{user_message}"
|
| 1910 |
+
|
| 1911 |
+
Return exactly one of the four category names above, with no extra words or punctuation.
|
| 1912 |
+
"""
|
| 1913 |
+
# 2) Call Gemini for classification, with explicit logging
|
| 1914 |
+
try:
|
| 1915 |
+
response_obj_gemini = gemini_client.generate_content(
|
| 1916 |
+
model='gemini-2.0-flash',
|
| 1917 |
+
contents=[{'parts': [{'text': classify_prompt}]}]
|
| 1918 |
+
)
|
| 1919 |
+
raw_intent = response_obj_gemini.text or ""
|
| 1920 |
+
except Exception as classify_ex:
|
| 1921 |
+
logger.error(f"Classification call failed: {classify_ex}")
|
| 1922 |
+
raw_intent = ""
|
| 1923 |
+
# 3) Normalize and validate
|
| 1924 |
+
normalized = raw_intent.strip().replace('"', '').lower()
|
| 1925 |
valid_intents = ["platform_data_query", "price_trend_query", "general_agri_info", "other"]
|
| 1926 |
+
if normalized in valid_intents:
|
| 1927 |
+
intent = normalized
|
| 1928 |
+
else:
|
| 1929 |
+
logger.warning(f"Unexpected or empty classification '{raw_intent}'. Defaulting intent to general_agri_info.")
|
| 1930 |
intent = "general_agri_info"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1931 |
|
| 1932 |
+
# 4) Build context based on intent...
|
| 1933 |
+
context_for_gemini = ""
|
| 1934 |
if intent == "platform_data_query":
|
| 1935 |
platform_data_summary, _ = _fetch_platform_data_for_chat(user_message)
|
| 1936 |
if platform_data_summary:
|
| 1937 |
+
context_for_gemini += f"Current Platform Data Context:\n{platform_data_summary}\n\n"
|
|
|
|
| 1938 |
elif intent == "price_trend_query":
|
| 1939 |
+
# … trend logic …
|
| 1940 |
+
trend_analysis_summary, _ = _get_price_trend_analysis_for_chat(...)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1941 |
if trend_analysis_summary:
|
| 1942 |
+
context_for_gemini += f"Price Trend Analysis Context:\n{trend_analysis_summary}\n\n"
|
| 1943 |
|
| 1944 |
+
# 5) Main answer prompt
|
| 1945 |
main_prompt = f"""
|
| 1946 |
+
You are Tunasonga Agri Assistant…
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1947 |
User Query: "{user_message}"
|
| 1948 |
+
The classified intent is: {intent}
|
| 1949 |
+
{context_for_gemini}
|
| 1950 |
+
Answer accordingly.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1951 |
"""
|
| 1952 |
+
try:
|
| 1953 |
+
response_obj_gemini = gemini_client.generate_content(
|
| 1954 |
+
model='gemini-2.0-flash',
|
| 1955 |
+
contents=[{'parts': [{'text': main_prompt}]}]
|
| 1956 |
+
)
|
| 1957 |
+
ai_response_text = response_obj_gemini.text.strip() or "I’m having trouble generating a response right now."
|
| 1958 |
+
except Exception as answer_ex:
|
| 1959 |
+
logger.error(f"Answer generation failed: {answer_ex}")
|
| 1960 |
+
ai_response_text = "I’m having trouble generating a response right now."
|
| 1961 |
+
|
| 1962 |
+
# 6) Save to Firebase history
|
| 1963 |
try:
|
| 1964 |
db.reference(f'ai_chat_history/{uid}/{str(uuid.uuid4())}', app=db_app).set({
|
| 1965 |
'user_message': user_message,
|
|
|
|
| 1969 |
})
|
| 1970 |
except Exception as chat_history_error:
|
| 1971 |
logger.error(f"Failed to store chat history for UID {uid}: {chat_history_error}")
|
| 1972 |
+
|
| 1973 |
+
# 7) Return JSON with valid intent
|
| 1974 |
return jsonify({"response": ai_response_text, "intent": intent})
|
| 1975 |
|
| 1976 |
+
except AttributeError as ae:
|
| 1977 |
+
# In this branch, always return a valid default intent
|
| 1978 |
+
logger.error(f"AttributeError in ai_chat (UID: {uid}): {ae}")
|
| 1979 |
+
ai_response_text = "I’m having a little trouble understanding that. Could you try rephrasing?"
|
| 1980 |
+
return jsonify({"response": ai_response_text, "intent": "general_agri_info", "error_detail": "AI_RESPONSE_FORMAT_ISSUE"}), 200
|
| 1981 |
+
except Exception as e:
|
| 1982 |
+
# For any other exception, ensure we return a valid intent instead of "unknown"
|
| 1983 |
+
logger.error(f"Unhandled exception in ai_chat (UID: {uid}): {e}")
|
| 1984 |
+
return jsonify({"error": str(e), "intent": "general_agri_info"}), 500
|
| 1985 |
|
| 1986 |
@app.route('/api/user/ai-chat-history', methods=['GET'])
|
| 1987 |
def get_ai_chat_history():
|