Update app.py
Browse files
app.py
CHANGED
|
@@ -39,6 +39,7 @@ GROQ_API_KEY_2 = os.getenv("GROQ_API_KEY_2") # Reserved for STT
|
|
| 39 |
GROQ_API_KEY_3 = os.getenv("GROQ_API_KEY_3") # Reserved for TTS
|
| 40 |
GROQ_API_KEY_4 = os.getenv("GROQ_API_KEY_4") # Additional Key (Fallback)
|
| 41 |
SERPAPI_KEY = os.getenv("SERPAPI_KEY") # Search
|
|
|
|
| 42 |
|
| 43 |
# List of API Keys for the Chat function
|
| 44 |
GROQ_CHAT_KEYS = [
|
|
@@ -722,6 +723,104 @@ def chat():
|
|
| 722 |
prompt = data.get("prompt", "")
|
| 723 |
history = data.get("history", [])
|
| 724 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 725 |
# Flags
|
| 726 |
user_timezone_str = data.get("user_timezone", "Asia/Jakarta")
|
| 727 |
current_username = data.get("current_username")
|
|
@@ -738,10 +837,10 @@ def chat():
|
|
| 738 |
# SUPER GTE FLAG
|
| 739 |
super_gte_active = data.get("super_gte", False)
|
| 740 |
|
| 741 |
-
# Rate limit logic
|
| 742 |
|
| 743 |
|
| 744 |
-
# LIMIT CHECK
|
| 745 |
|
| 746 |
|
| 747 |
print(f"[CHAT] π¬ User Prompt (Text Mode): {prompt}")
|
|
@@ -771,13 +870,9 @@ def chat():
|
|
| 771 |
return Response("Deep research requires a question.", mimetype="text/plain")
|
| 772 |
|
| 773 |
def gen_deep():
|
| 774 |
-
|
| 775 |
-
|
| 776 |
final_answer = deep_research_mode(deep_query, history, num_sources=15)
|
| 777 |
yield final_answer
|
| 778 |
|
| 779 |
-
|
| 780 |
-
|
| 781 |
response = Response(gen_deep(), mimetype="text/plain")
|
| 782 |
|
| 783 |
return response
|
|
@@ -808,12 +903,9 @@ def chat():
|
|
| 808 |
# Note: If agent_active is True, the Agent logic is handled inside stream_chat
|
| 809 |
|
| 810 |
# ======================
|
| 811 |
-
# π¬ 4. STANDARD CHAT (
|
| 812 |
# ======================
|
| 813 |
|
| 814 |
-
# --- FIX: decrement MUST be OUTSIDE generator ---
|
| 815 |
-
|
| 816 |
-
|
| 817 |
def generate():
|
| 818 |
for chunk in stream_chat(
|
| 819 |
prompt,
|
|
@@ -827,9 +919,6 @@ def chat():
|
|
| 827 |
):
|
| 828 |
yield chunk
|
| 829 |
|
| 830 |
-
# Ambil sisa limit setelah decrement
|
| 831 |
-
|
| 832 |
-
|
| 833 |
response = Response(generate(), mimetype="text/plain")
|
| 834 |
|
| 835 |
return response
|
|
|
|
| 39 |
GROQ_API_KEY_3 = os.getenv("GROQ_API_KEY_3") # Reserved for TTS
|
| 40 |
GROQ_API_KEY_4 = os.getenv("GROQ_API_KEY_4") # Additional Key (Fallback)
|
| 41 |
SERPAPI_KEY = os.getenv("SERPAPI_KEY") # Search
|
| 42 |
+
COHERE_API_KEY = os.getenv("COHERE_KEY")
|
| 43 |
|
| 44 |
# List of API Keys for the Chat function
|
| 45 |
GROQ_CHAT_KEYS = [
|
|
|
|
| 723 |
prompt = data.get("prompt", "")
|
| 724 |
history = data.get("history", [])
|
| 725 |
|
| 726 |
+
# ======================
|
| 727 |
+
# πΌοΈ VISION MODE (AUTO DETECT - BASE64 ONLY)
|
| 728 |
+
# ======================
|
| 729 |
+
image_base64 = data.get("image_base64")
|
| 730 |
+
if image_base64:
|
| 731 |
+
print("[VISION] πΌοΈ Image detected β using c4ai-aya-vision-32b (Cohere)")
|
| 732 |
+
|
| 733 |
+
# Validate base64 quickly
|
| 734 |
+
try:
|
| 735 |
+
# If user sends full data URI like "data:image/png;base64,....", strip prefix for decoding test
|
| 736 |
+
test_b64 = image_base64
|
| 737 |
+
if test_b64.startswith("data:"):
|
| 738 |
+
test_b64 = test_b64.split(",", 1)[1]
|
| 739 |
+
base64.b64decode(test_b64, validate=True)
|
| 740 |
+
except Exception as e:
|
| 741 |
+
print(f"[VISION] β Invalid base64: {e}")
|
| 742 |
+
return jsonify({"error": "Invalid base64 image"}), 400
|
| 743 |
+
|
| 744 |
+
# Build payload for Cohere v2 chat (vision)
|
| 745 |
+
cohere_url = "https://api.cohere.ai/v2/chat"
|
| 746 |
+
payload = {
|
| 747 |
+
"model": "c4ai-aya-vision-32b",
|
| 748 |
+
"messages": [
|
| 749 |
+
{
|
| 750 |
+
"role": "user",
|
| 751 |
+
"content": [
|
| 752 |
+
{
|
| 753 |
+
"type": "input_text",
|
| 754 |
+
"text": prompt if prompt else "Describe this image."
|
| 755 |
+
},
|
| 756 |
+
{
|
| 757 |
+
# Cohere accepts data URI as image url - keep the data URI if present
|
| 758 |
+
"type": "input_image",
|
| 759 |
+
"image": image_base64
|
| 760 |
+
}
|
| 761 |
+
]
|
| 762 |
+
}
|
| 763 |
+
],
|
| 764 |
+
# optional tuning
|
| 765 |
+
"temperature": 0.6,
|
| 766 |
+
"max_tokens": 800
|
| 767 |
+
}
|
| 768 |
+
|
| 769 |
+
headers = {
|
| 770 |
+
"Authorization": f"Bearer {COHERE_API_KEY}",
|
| 771 |
+
"Content-Type": "application/json"
|
| 772 |
+
}
|
| 773 |
+
|
| 774 |
+
try:
|
| 775 |
+
res = requests.post(cohere_url, json=payload, headers=headers, timeout=60)
|
| 776 |
+
try:
|
| 777 |
+
res_json = res.json()
|
| 778 |
+
except Exception:
|
| 779 |
+
print("[VISION] β Cohere returned non-json response")
|
| 780 |
+
return jsonify({"error": "Cohere returned non-json response", "status_code": res.status_code, "text": res.text}), 500
|
| 781 |
+
|
| 782 |
+
# Robust extraction of text reply (support several possible shapes)
|
| 783 |
+
ai_reply = None
|
| 784 |
+
# Try common v2 shape: res_json["messages"][0]["content"][0]["text"]
|
| 785 |
+
try:
|
| 786 |
+
ai_reply = res_json.get("messages", [])[0].get("content", [])[0].get("text")
|
| 787 |
+
except Exception:
|
| 788 |
+
ai_reply = None
|
| 789 |
+
|
| 790 |
+
# Fallback: sometimes Cohere returns 'output' or 'text' fields
|
| 791 |
+
if not ai_reply:
|
| 792 |
+
# Try to find any 'text' in nested dicts
|
| 793 |
+
def find_text(obj):
|
| 794 |
+
if isinstance(obj, dict):
|
| 795 |
+
for k, v in obj.items():
|
| 796 |
+
if k == "text" and isinstance(v, str):
|
| 797 |
+
return v
|
| 798 |
+
else:
|
| 799 |
+
found = find_text(v)
|
| 800 |
+
if found:
|
| 801 |
+
return found
|
| 802 |
+
elif isinstance(obj, list):
|
| 803 |
+
for item in obj:
|
| 804 |
+
found = find_text(item)
|
| 805 |
+
if found:
|
| 806 |
+
return found
|
| 807 |
+
return None
|
| 808 |
+
ai_reply = find_text(res_json) or ""
|
| 809 |
+
|
| 810 |
+
return jsonify({
|
| 811 |
+
"mode": "vision",
|
| 812 |
+
"reply": ai_reply,
|
| 813 |
+
"raw": res_json
|
| 814 |
+
})
|
| 815 |
+
|
| 816 |
+
except requests.exceptions.RequestException as e:
|
| 817 |
+
print(f"[VISION] β RequestException: {e}")
|
| 818 |
+
return jsonify({"error": "Failed to call Cohere", "details": str(e)}), 500
|
| 819 |
+
|
| 820 |
+
# =====================================================
|
| 821 |
+
# π§© ποΈ (VISION DONE) β LANJUTKAN MODE TEXT SEPERTI BIASA
|
| 822 |
+
# =====================================================
|
| 823 |
+
|
| 824 |
# Flags
|
| 825 |
user_timezone_str = data.get("user_timezone", "Asia/Jakarta")
|
| 826 |
current_username = data.get("current_username")
|
|
|
|
| 837 |
# SUPER GTE FLAG
|
| 838 |
super_gte_active = data.get("super_gte", False)
|
| 839 |
|
| 840 |
+
# Rate limit logic (kept placeholder as in your original)
|
| 841 |
|
| 842 |
|
| 843 |
+
# LIMIT CHECK (kept placeholder)
|
| 844 |
|
| 845 |
|
| 846 |
print(f"[CHAT] π¬ User Prompt (Text Mode): {prompt}")
|
|
|
|
| 870 |
return Response("Deep research requires a question.", mimetype="text/plain")
|
| 871 |
|
| 872 |
def gen_deep():
|
|
|
|
|
|
|
| 873 |
final_answer = deep_research_mode(deep_query, history, num_sources=15)
|
| 874 |
yield final_answer
|
| 875 |
|
|
|
|
|
|
|
| 876 |
response = Response(gen_deep(), mimetype="text/plain")
|
| 877 |
|
| 878 |
return response
|
|
|
|
| 903 |
# Note: If agent_active is True, the Agent logic is handled inside stream_chat
|
| 904 |
|
| 905 |
# ======================
|
| 906 |
+
# π¬ 4. STANDARD STREAM CHAT (unchanged)
|
| 907 |
# ======================
|
| 908 |
|
|
|
|
|
|
|
|
|
|
| 909 |
def generate():
|
| 910 |
for chunk in stream_chat(
|
| 911 |
prompt,
|
|
|
|
| 919 |
):
|
| 920 |
yield chunk
|
| 921 |
|
|
|
|
|
|
|
|
|
|
| 922 |
response = Response(generate(), mimetype="text/plain")
|
| 923 |
|
| 924 |
return response
|