File size: 9,148 Bytes
e122f2f
979d861
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# این فایل server.py یا app.py در Hugging Face Space شما قرار می گیرد
# این کد شامل:
# - بارگذاری مدل Sentence Transformer پارسی 'PartAI/Tooka-SBERT'
# - تعریف اپلیکیشن Flask
# - اندپوینت /get_embedding برای دریافت سوال و ارسال بردار آن
# - لاگ گیری برای عیب یابی در لاگ های Space و یک فایل جداگانه
# - مدیریت خطاهای اولیه در بارگذاری مدل و پردازش درخواست ها
# - رفع خطای 'SentenceTransformer' object has no attribute 'args' با اصلاح لاگ بارگذاری مدل

from sentence_transformers import SentenceTransformer
from flask import Flask, request, jsonify
import numpy as np
import logging
import os
# import torch # اگر از GPU استفاده می کنید و torch نصب کرده اید، این خط را فعال نگه دارید.

# تنظیمات اولیه لاگینگ برای نمایش در لاگ های استاندارد Space
# این لاگ ها معمولاً در تب "Container logs" دیده می شوند.
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# مسیر فایل لاگ جداگانه در فضای Hugging Face (برای عیب یابی بیشتر)
# نکته: نوشتن در فایل در برخی محیط های Space ممکن است نیاز به تنظیمات مجوز داشته باشد.
# خطای [Errno 13] Permission denied در لاگ قبلی به همین دلیل بود.
log_file_path = "app_log.txt"

# تابع برای افزودن پیام به فایل لاگ
def log_message(message):
    try:
        # حالت 'a' برای اضافه کردن به انتهای فایل است.
        # مطمئن شوید که دایرکتوری برای فایل قابل نوشتن است.
        # اگر خطا مجوز می گیرید، می توانید این تابع را موقتا غیرفعال کنید یا مسیر دیگری را امتحان کنید.
        with open(log_file_path, "a", encoding="utf-8") as f:
            f.write(message + "\n")
    except Exception as e:
        # اگر نوشتن در فایل لاگ هم با خطا مواجه شد، در لاگ استاندارد Space بنویسید.
        logging.error(f"Error writing to log file {log_file_path}: {e}")

# ****** نام مدل Sentence Transformer که می خواهیم در این سرور استفاده کنیم ******
# این مدل باید همان مدلی باشد که برای بردارسازی خاطرات در فایل های JSON استفاده کرده اید.
model_name_to_load = 'PartAI/Tooka-SBERT'
# ***************************************************************************


# ****** بارگذاری مدل Sentence Transformer در هنگام راه اندازی سرور ******
logging.info(f"Attempting to load Sentence Transformer model: {model_name_to_load}...") # لاگ قبل از بارگذاری (در لاگ های استاندارد Space)
log_message(f"Attempting to load Sentence Transformer model: {model_name_to_load}...") # لاگ قبل از بارگذاری (در فایل لاگ)

try:
    # بارگذاری مدل. اگر GPU دارید و torch نصب شده، می توانید device='cuda' را اضافه کنید.
    # اگر خطایی در بارگذاری رخ دهد، Exception گرفته می شود.
    # model = SentenceTransformer(model_name_to_load, device='cuda' if torch.cuda.is_available() else 'cpu')
    model = SentenceTransformer(model_name_to_load) # بارگذاری با استفاده از CPU یا تنظیمات پیش فرض

    # ****** افزودن لاگ تأیید پس از بارگذاری موفق مدل ******
    # این پیام ها در لاگ های Space به شما نشان می دهند که مدل با موفقیت بارگذاری شده و نام آن چیست.
    # ما نام مدلی که قصد بارگذاریش را داشتیم لاگ می کنیم تا از خطای Attribute Error جلوگیری شود.
    logging.info(f"SUCCESS: Model '{model_name_to_load}' loaded successfully.") # لاگ موفقیت (در لاگ های استاندارد Space)
    log_message(f"SUCCESS: Model '{model_name_to_load}' loaded successfully.") # لاگ موفقیت (در فایل لاگ)
    # ******************************************************

except Exception as e:
    # ****** مدیریت خطا در صورت عدم بارگذاری مدل ******
    error_msg = f"ERROR: Failed to load model {model_name_to_load}: {e}"
    logging.error(error_msg) # لاگ خطا (در لاگ های استاندارد Space)
    log_message(error_msg) # لاگ خطا (در فایل لاگ)
    # در صورتی که بارگذاری مدل با خطا مواجه شد، متغیر model را None قرار می دهیم
    # تا در تابع get_embedding بتوانیم وضعیت خطا را چک کنیم.
    model = None
    # *************************************************


# تعریف اپلیکیشن Flask
app = Flask(__name__)

# تعریف اندپوینت اصلی برای تست سلامت سرور
@app.route('/')
def index():
    # چک کردن وضعیت مدل قبل از پاسخ به درخواست اصلی
    if model is None:
        # اگر مدل بارگذاری نشده، پیام خطا نمایش داده می شود.
        return "Sentiment Embedding Server is running, but model failed to load. Check Space logs for details.", 500
    # اگر مدل با موفقیت بارگذاری شده، پیام موفقیت نمایش داده می شود.
    # از نام مدلی که در متغیر ذخیره کردیم استفاده می کنیم.
    return f"Sentiment Embedding Server is running successfully with model: {model_name_to_load}"


# تعریف اندپوینت برای دریافت بردار (embedding) سوال
@app.route('/get_embedding', methods=['POST'])
def get_embedding():
    # قبل از پردازش درخواست، مطمئن شوید مدل بارگذاری شده است.
    if model is None:
        error_msg = "Model is not loaded on the server due to a previous error. Check Space logs."
        # این خطا احتمالا قبلا در لاگ ها ثبت شده، اما برای اطمینان مجدد ثبت می کنیم.
        # logging.error(error_msg) # لاگ تکراری در صورتیکه بالا هم خطا داده
        log_message(f"Received request but model is not loaded: {error_msg}") # ثبت در فایل لاگ
        return jsonify({"error": error_msg}), 500 # ارسال پاسخ خطا با کد 500

    try:
        # دریافت داده ها از درخواست POST (باید شامل فیلد 'query' باشد)
        data = request.get_json()
        query = data.get('query')

        # چک کردن اینکه فیلد query خالی نباشد
        if not query or not isinstance(query, str) or not query.strip():
            log_message(f"Received invalid or empty query in /get_embedding: {data}")
            return jsonify({"error": "Invalid or empty query text provided"}), 400 # ارسال پاسخ خطا با کد 400

        log_message(f"Received query in /get_embedding: '{query}'")
        logging.info(f"Processing query in /get_embedding: '{query[:50]}...'") # لاگ در کنسول Space برای درخواست های دریافتی

        embedding = model.encode(query, convert_to_numpy=True, pooling_mode='mean', normalize=True)

        # تبدیل بردار numpy به لیست Python برای ارسال در پاسخ JSON
        embedding_list = embedding.tolist()

        log_message(f"Successfully generated embedding for query in /get_embedding.") # لاگ موفقیت در فایل
        # logging.info(f"Embedding generated successfully for query.") # لاگ موفقیت در کنسول Space (اختیاری)

        # ارسال بردار به عنوان پاسخ JSON
        return jsonify({"embedding": embedding_list}), 200 # ارسال پاسخ موفقیت با کد 200

    except Exception as e:
        error_message = f"An error occurred during embedding generation in /get_embedding: {e}"
        logging.error(error_message) # لاگ خطا (در لاگ های استاندارد Space)
        log_message(error_message) # لاگ خطا (در فایل لاگ)
        return jsonify({"error": error_message}), 500 # ارسال پاسخ خطا با کد 500

# خط زیر فقط در صورتی که فایل server.py مستقیماً اجرا شود، سرور Flask را اجرا می کند.
# در Hugging Face Space، خود زیرساخت Space مسئول اجرای برنامه شماست و این بخش معمولاً غیرفعال است.
# if __name__ == '__main__':
#     # Hugging Face Spaces از پورت 7860 استفاده می کند.
#     # debug=True برای محیط توسعه مفید است اما در محیط پروداکشن توصیه نمی شود.
#     app.run(host='0.0.0.0', port=7860, debug=False)