|
|
from flask import Flask, request, jsonify, render_template |
|
|
import os |
|
|
from flask_cors import CORS |
|
|
from openai import OpenAI |
|
|
from translate import Translator |
|
|
|
|
|
|
|
|
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") or os.getenv("GEMINI_API_KEY") |
|
|
if not OPENAI_API_KEY: |
|
|
raise ValueError("OpenAI API Key is missing. Set it in environment variables.") |
|
|
|
|
|
OPENAI_API_BASE = os.getenv("OPENAI_API_BASE", "https://generativelanguage.googleapis.com/v1beta/openai/") |
|
|
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gemini-2.5-flash") |
|
|
|
|
|
|
|
|
client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_API_BASE) |
|
|
|
|
|
|
|
|
STATIC_FOLDER = os.path.join(os.path.dirname(__file__), "static") |
|
|
|
|
|
|
|
|
CONTEXT_FILE = os.path.join(STATIC_FOLDER, "context.txt") |
|
|
try: |
|
|
with open(CONTEXT_FILE, "r", encoding="utf-8") as file: |
|
|
CONTEXT_DATA = file.read() |
|
|
except FileNotFoundError: |
|
|
CONTEXT_DATA = "No context available." |
|
|
|
|
|
SYSTEM_INSTRUCTION = """ |
|
|
You are Hal, an AI assistant created to help farmers. |
|
|
Your goal is to analyze the crop data provided in the context file and assist farmers by answering their queries and solving their problems. |
|
|
|
|
|
Response Rules: |
|
|
1. Use the context file to answer farmer-related questions. |
|
|
2. Do not share any personal information. |
|
|
3. Provide only the information available in the context file. |
|
|
4. If not found, generate helpful answers from agricultural knowledge. |
|
|
5. Keep responses simple, clear, and useful. |
|
|
""" |
|
|
|
|
|
|
|
|
app = Flask(__name__, template_folder="templates", static_folder="static") |
|
|
CORS(app) |
|
|
|
|
|
|
|
|
def detect_language(text): |
|
|
""" |
|
|
Detect language of text using multiple methods |
|
|
""" |
|
|
try: |
|
|
|
|
|
try: |
|
|
from langdetect import detect |
|
|
detected = detect(text) |
|
|
return detected |
|
|
except ImportError: |
|
|
pass |
|
|
except: |
|
|
pass |
|
|
|
|
|
|
|
|
common_words = { |
|
|
'en': ['the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'is', 'are', 'was', 'were'], |
|
|
'es': ['el', 'la', 'de', 'que', 'y', 'en', 'un', 'es', 'se', 'no', 'te', 'lo', 'por', 'con', 'del', 'las'], |
|
|
'fr': ['le', 'de', 'et', 'à', 'un', 'il', 'être', 'en', 'avoir', 'que', 'pour', 'du', 'ce', 'son', 'une'], |
|
|
'de': ['der', 'die', 'und', 'in', 'den', 'von', 'zu', 'das', 'mit', 'sich', 'des', 'auf', 'für', 'ist'], |
|
|
'it': ['il', 'di', 'che', 'e', 'la', 'per', 'in', 'un', 'è', 'con', 'non', 'da', 'del', 'sono'], |
|
|
'pt': ['de', 'a', 'o', 'que', 'e', 'do', 'da', 'em', 'um', 'para', 'é', 'com', 'os', 'uma', 'ser'], |
|
|
'ru': ['в', 'и', 'не', 'на', 'я', 'быть', 'с', 'а', 'как', 'его', 'к', 'он', 'что', 'то'], |
|
|
'hi': ['है', 'का', 'के', 'में', 'और', 'को', 'से', 'पर', 'यह', 'वह', 'एक', 'जो'], |
|
|
'ar': ['في', 'من', 'إلى', 'على', 'هذا', 'هذه', 'التي', 'الذي', 'أن', 'كان', 'مع'], |
|
|
'zh': ['的', '是', '在', '了', '和', '有', '人', '我', '你', '他', '她', '它'], |
|
|
} |
|
|
|
|
|
text_lower = text.lower() |
|
|
scores = {} |
|
|
|
|
|
for lang, words in common_words.items(): |
|
|
score = sum(1 for word in words if word in text_lower) |
|
|
if score > 0: |
|
|
scores[lang] = score / len(words) |
|
|
|
|
|
if scores: |
|
|
return max(scores, key=scores.get) |
|
|
|
|
|
return 'en' |
|
|
|
|
|
except Exception: |
|
|
return 'en' |
|
|
|
|
|
|
|
|
def translate_text(text, target_lang, source_lang=None): |
|
|
""" |
|
|
Translate text to desired language using Microsoft Translator |
|
|
|
|
|
Args: |
|
|
text (str): Text to translate |
|
|
target_lang (str): Target language code (e.g., 'es', 'fr', 'de', 'hi', 'zh') |
|
|
source_lang (str): Source language code (if None, will auto-detect) |
|
|
|
|
|
Returns: |
|
|
str: Translated text or original text if translation fails |
|
|
""" |
|
|
try: |
|
|
|
|
|
if target_lang.lower() == 'en': |
|
|
return text |
|
|
|
|
|
|
|
|
if source_lang is None or source_lang == 'auto': |
|
|
source_lang = detect_language(text) |
|
|
|
|
|
|
|
|
if source_lang.lower() == target_lang.lower(): |
|
|
return text |
|
|
|
|
|
|
|
|
translator = Translator(to_lang=target_lang, from_lang=source_lang) |
|
|
translated = translator.translate(text) |
|
|
|
|
|
|
|
|
if translated and translated.strip(): |
|
|
return translated |
|
|
else: |
|
|
return text |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Translation error: {str(e)}") |
|
|
|
|
|
try: |
|
|
fallback_translator = Translator(to_lang=target_lang, from_lang='en') |
|
|
fallback_result = fallback_translator.translate(text) |
|
|
if fallback_result and fallback_result.strip(): |
|
|
return fallback_result |
|
|
except: |
|
|
pass |
|
|
return text |
|
|
|
|
|
|
|
|
LANGUAGE_CODES = { |
|
|
'english': 'en', |
|
|
'spanish': 'es', |
|
|
'french': 'fr', |
|
|
'german': 'de', |
|
|
'italian': 'it', |
|
|
'portuguese': 'pt', |
|
|
'russian': 'ru', |
|
|
'chinese': 'zh', |
|
|
'japanese': 'ja', |
|
|
'korean': 'ko', |
|
|
'arabic': 'ar', |
|
|
'hindi': 'hi', |
|
|
'dutch': 'nl', |
|
|
'swedish': 'sv', |
|
|
'norwegian': 'no', |
|
|
'danish': 'da', |
|
|
'finnish': 'fi', |
|
|
'polish': 'pl', |
|
|
'czech': 'cs', |
|
|
'hungarian': 'hu', |
|
|
'romanian': 'ro', |
|
|
'turkish': 'tr', |
|
|
'greek': 'el', |
|
|
'hebrew': 'he', |
|
|
'thai': 'th', |
|
|
'vietnamese': 'vi', |
|
|
'indonesian': 'id', |
|
|
'malay': 'ms' |
|
|
} |
|
|
|
|
|
@app.route("/", methods=["GET"]) |
|
|
def home(): |
|
|
return render_template("index.html") |
|
|
|
|
|
@app.route("/chat", methods=["POST"]) |
|
|
def chat(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
user_message = data.get("message") |
|
|
target_lang = data.get("lang", "en") |
|
|
|
|
|
if not user_message: |
|
|
return jsonify({"error": "Message is required"}), 400 |
|
|
|
|
|
|
|
|
target_lang = target_lang.lower() |
|
|
if target_lang in LANGUAGE_CODES: |
|
|
target_lang = LANGUAGE_CODES[target_lang] |
|
|
|
|
|
|
|
|
processed_message = user_message |
|
|
if target_lang != "en": |
|
|
try: |
|
|
|
|
|
detected_source = detect_language(user_message) |
|
|
print(f"Detected user language: {detected_source}") |
|
|
|
|
|
|
|
|
if detected_source != 'en': |
|
|
english_translator = Translator(to_lang='en', from_lang=detected_source) |
|
|
processed_message = english_translator.translate(user_message) |
|
|
if not processed_message or not processed_message.strip(): |
|
|
processed_message = user_message |
|
|
print(f"Translated user message to English: {processed_message}") |
|
|
except Exception as e: |
|
|
print(f"User message translation error: {str(e)}") |
|
|
processed_message = user_message |
|
|
|
|
|
messages = [ |
|
|
{"role": "system", "content": SYSTEM_INSTRUCTION}, |
|
|
{"role": "user", "content": f"Context Data:\n{CONTEXT_DATA}\n\nUser Query: {processed_message}"} |
|
|
] |
|
|
|
|
|
|
|
|
response = client.chat.completions.create( |
|
|
model=OPENAI_MODEL, |
|
|
messages=messages, |
|
|
temperature=0.7 |
|
|
) |
|
|
ai_response = response.choices[0].message.content.strip() |
|
|
|
|
|
|
|
|
if target_lang.lower() != "en": |
|
|
ai_response = translate_text(ai_response, target_lang) |
|
|
|
|
|
return jsonify({"response": ai_response}) |
|
|
|
|
|
except Exception as e: |
|
|
error_message = f"Error: {str(e)}" |
|
|
|
|
|
if 'target_lang' in locals() and target_lang.lower() != "en": |
|
|
try: |
|
|
error_message = translate_text(error_message, target_lang) |
|
|
except: |
|
|
pass |
|
|
|
|
|
return jsonify({"error": error_message}), 500 |
|
|
|
|
|
@app.route("/languages", methods=["GET"]) |
|
|
def get_languages(): |
|
|
"""Endpoint to get available language codes""" |
|
|
return jsonify({"languages": LANGUAGE_CODES}) |
|
|
|
|
|
@app.route("/translate", methods=["POST"]) |
|
|
def translate_endpoint(): |
|
|
"""Standalone translation endpoint""" |
|
|
try: |
|
|
data = request.get_json() |
|
|
text = data.get("text") |
|
|
target_lang = data.get("target_lang", "en") |
|
|
source_lang = data.get("source_lang") |
|
|
|
|
|
if not text: |
|
|
return jsonify({"error": "Text is required"}), 400 |
|
|
|
|
|
|
|
|
target_lang = target_lang.lower() |
|
|
if target_lang in LANGUAGE_CODES: |
|
|
target_lang = LANGUAGE_CODES[target_lang] |
|
|
|
|
|
|
|
|
if not source_lang or source_lang.lower() == 'auto': |
|
|
source_lang = detect_language(text) |
|
|
|
|
|
translated = translate_text(text, target_lang, source_lang) |
|
|
|
|
|
return jsonify({ |
|
|
"original_text": text, |
|
|
"translated_text": translated, |
|
|
"detected_source_lang": source_lang, |
|
|
"target_lang": target_lang |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({"error": str(e)}), 500 |
|
|
|
|
|
if __name__ == '__main__': |
|
|
print("Starting Flask app with translation support...") |
|
|
print("Available language codes:", list(LANGUAGE_CODES.keys())) |
|
|
print("Install dependencies: pip install translate") |
|
|
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True) |