From a1375a09567f4180d8b05688d415cc3da4eec750 Mon Sep 17 00:00:00 2001 From: ajokar Date: Sat, 16 May 2026 14:29:40 +0000 Subject: [PATCH] edit nahj general runner and some changes --- nahj_engine.py | 23 +++- nahj_engine_general_runner.py | 215 ++++++++-------------------------- nahj_get_metadata_oss.py | 98 ++++++++++++++-- 3 files changed, 156 insertions(+), 180 deletions(-) diff --git a/nahj_engine.py b/nahj_engine.py index a0eb104..2aebdd2 100644 --- a/nahj_engine.py +++ b/nahj_engine.py @@ -1,5 +1,5 @@ -# nahj engine - +# nahj engine started ... +print('nahj engine started ...') import json import os import numpy as np @@ -693,14 +693,24 @@ def bale_chat(query): print(f'len messages SIMPLE question: {len(messages)}') print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') - return llm_answer + + + + result = { + "output" : llm_answer, + "status" : 'OK', + "similarity_result" : retrived_sections_list, + "reference_ids" : [], + } + return result # uvicorn src.app:app --reload if __name__ == "__main__": # query = 'در قانون حمایت از خانواده و جوانی جمعیت چه خدماتی در نظر گرفته شده است؟' while True: - query = input('enter your qustion:') + # query = input('enter your qustion:') + query = "انسان در فتنه باید چگونه رفتار کند؟" if query == '': # should write in DATABASE with open('./nahj-answer/chat-leader.txt', 'r', encoding='utf-8') as file: @@ -714,8 +724,9 @@ if __name__ == "__main__": print(f'retrive duration: {(end_retrive - start).total_seconds()}') # prompt = f'برای پرسش "{query}" از میان متن های "{result_passages}" .پاسخ مناسب را استخراج کن. پاسخ از زبان رهبر انقلاب اسلامی -حضرت آیت الله خامنه‌ای- بیان می شود. پاسخ به صورت تحلیلی باشد و ابعاد مختلف پرسش را در نظر بگیرد. پاسخ تولید شده، باید متن های مرتبط با پرسش را به صورت علمی بازنویسی کند. برای هر بخش از متن که در تولید پاسخ استفاده می کنی، عنوان آن بخش و تاریخ را نیز حتما در متن اضافه کن. درصورتی که مطلبی مرتبط با پرسش در متن پیدا نشد، فقط پاسخ بده: "متاسفانه در منابع، پاسخی پیدا نشد!"' - prompt = f'''برای پرسش "{query}" از میان بیانات رهبر معظم انقلاب، پاسخ مناسب را استخراج کن. پاسخ به صورت تحلیلی باشد و ابعاد مختلف پرسش را در نظر بگیرد. پاسخ تولید شده، باید محتوای بیانات مرتبط با پرسش را به صورت علمی بازنویسی کند. برای هر بخش از بیانات که در تولید پاسخ استفاده می کنی، عنوان آن و تاریخ را نیز در پاسخ اضافه کن. بیانات رهبر معظم انقلاب اسلامی: "{result_passages}"''' - + # prompt = f'''برای پرسش "{query}" از میان بیانات رهبر معظم انقلاب، پاسخ مناسب را استخراج کن. پاسخ به صورت تحلیلی باشد و ابعاد مختلف پرسش را در نظر بگیرد. پاسخ تولید شده، باید محتوای بیانات مرتبط با پرسش را به صورت علمی بازنویسی کند. برای هر بخش از بیانات که در تولید پاسخ استفاده می کنی، عنوان آن و تاریخ را نیز در پاسخ اضافه کن. بیانات رهبر معظم انقلاب اسلامی: "{result_passages}"''' + prompt = f'''برای پرسش "{query}" از میان متن های زیر که از نهج‌البلاغه استخراج شده است، پاسخ مناسب را استخراج کن. متن خروجی «بدون مارک داون» و «بدون استایل» باشد. برای هر کدام از بخش‌ها که در تولید پاسخ استفاده می کنی، عنوان آن بخش را نیز در پاسخ اضافه کن. متن‌های مرتبط از کتاب نهج‌البلاغه "{related_paragraphs.strip()}"''' + llm_answer = llm_request(prompt)# "deepseek-reasoner" print('-'*40) diff --git a/nahj_engine_general_runner.py b/nahj_engine_general_runner.py index a0199fe..5c876df 100644 --- a/nahj_engine_general_runner.py +++ b/nahj_engine_general_runner.py @@ -1,5 +1,9 @@ +""" +این سرویس، هسته اصلی نهج البلاغه را اجرا می کند +""" import json from fastapi import FastAPI, Request +from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import requests import logging @@ -8,6 +12,7 @@ import random import time import nahj_engine as nahj_chat import data_model as dm +from typing import Optional, Dict # =========================== # پیکربندی اولیه # =========================== @@ -25,14 +30,13 @@ logging.basicConfig( # define import model class # =========================== class Message(BaseModel): - chat: dict - text: str | None = None + user_query: str class Update(BaseModel): message: Message | None = None -async def get_latest_req_id(self): +async def get_latest_req_id(): latest_request = dm.get_last_request() latest_req_id = latest_request['update_id'] @@ -41,7 +45,7 @@ async def get_latest_req_id(self): return latest_req_id + 1 -async def save_entery(self, update_item): +async def save_entery(update_item): is_active = True answer = '' message = update_item['message'] @@ -61,11 +65,11 @@ async def save_entery(self, update_item): return update_item['update_id'] -async def update_request(self, update_id, answer): +async def update_request(update_id, answer): dm.update_request(update_id= update_id, answer= answer) -async def split_text_into_chunks(self, text, max_length=4000): +async def split_text_into_chunks(text, max_length=4000): """ تقسیم یک متن به چانک‌های حداکثر max_length کاراکتری، بدون خراب کردن معنا با رعایت انتهای جمله‌ها. @@ -99,190 +103,67 @@ async def split_text_into_chunks(self, text, max_length=4000): return chunks -async def save_chat_data(self,query, answer, first_name, username): +async def save_chat_data(query, answer, first_name, username): chat_data = f'''username: {username}\nfirstname: {first_name}\nquery: {query}\nanswer:{answer}\n+ + + + + + + + + + + + + + + + + + + + \n+ + + + + + + + + + + + + + + + + + + + \n\n''' # # should write in DATABASE with open('./bale_bot/chat-data.txt', 'a+', encoding='utf-8') as file: file.write(chat_data) -async def handle_update(self, update_reqs: dict): - print(f"handle update ...") - data = update_reqs - if "message" not in data: - return - message = data["message"] - chat_id = message["chat"]["id"] - text = message.get("text", "").strip() - - fromm = message['from'] - # first_name = fromm['first_name'] - # username = fromm['username'] - - logging.info(f"Received message from {chat_id}: {text}") - - keyboard = { - "keyboard": [["جستجو","پرسش","پرسش عمیق"]],# ,"شبکه معنایی" - "resize_keyboard": True, - "one_time_keyboard": True - } - - if text == "/start": - reply = "سلام، من دستیار هوشمند نهج‌البلاغه هستم. لطفا یکی از گزینه‌های زیر را انتخاب نمائید ..." - - await self.send_message(chat_id, reply, keyboard) - return - - elif text == "پرسش": - # حذف نوع درخواست قبلی کاربر - self.user_states.pop(chat_id, None) - # ایجاد وضعیت پرسش برای کاربر جاری - self.user_states[chat_id] = "simple_question" - reply = "لطفا متن «پرسش» را وارد نمائید ..." - await self.send_message(chat_id, reply, keyboard) - return - - elif text == "جستجو": - # حذف نوع درخواست قبلی کاربر - self.user_states.pop(chat_id, None) - # ایجاد وضعیت جستجو برای کاربر جاری - self.user_states[chat_id] = "search" - reply = "لطفا متن موردنظر جهت «جستجو» را وارد نمائید ..." - await self.send_message(chat_id, reply, keyboard) - return - - elif text == "شبکه معنایی": - # حذف نوع درخواست قبلی کاربر - self.user_states.pop(chat_id, None) - # ایجاد وضعیت شبکه معنایی برای کاربر جاری - self.user_states[chat_id] = "semantic-network" - reply = "لطفا کلمه موردنظر جهت ترسیم «شبکه معنایی» را وارد نمائید ..." - await self.send_message(chat_id, reply, keyboard) - return - - elif text == "پرسش عمیق": - # حذف نوع درخواست قبلی کاربر - self.user_states.pop(chat_id, None) - # ایجاد وضعیت پرسش برای کاربر جاری - self.user_states[chat_id] = "deep_question" - reply = "لطفا متن «پرسش عمیق» را وارد نمائید ..." - await self.send_message(chat_id, reply, keyboard) - return - - # elif text == "/help": - # reply = ( - # "دستورهای موجود:\n" - # "/start - شروع ربات\n" - # "/chat - گفت‌گو با دستیار هوشمند نهج البلاغه\n" - # "/status - وضعیت ربات" - # ) - # self.send_message(chat_id, reply) - - elif text == "ربات": - reply = "ربات فعال است ✅" - await self.send_message(chat_id, reply, keyboard) - return - - elif self.user_states.get(chat_id) == "semantic-network": - await self.send_message(chat_id, f"⏳ در حال ایجاد شبکه معنایی برای کلمه «{text}» ...") - reply = 'با عرض پوزش؛ این امکان، در حال حاضر در دسترس نیست' - - elif self.user_states.get(chat_id) == "search": - await self.send_message(chat_id, f"⏳ در حال جستجو برای «{text}» ...") - - answer = nahj_chat.bale_search(text) - - if answer: - reply = answer - else: - reply = 'خطا در تولید پاسخ!' - - elif self.user_states.get(chat_id) == "simple_question": - - await self.send_message(chat_id, f"⏳ در حال آماده‌سازی پاسخ به «{text}» ...") - - answer = nahj_chat.bale_chat(text) - - if answer: - reply = answer - else: - reply = 'خطا در تولید پاسخ!' - - elif self.user_states.get(chat_id) == "deep_question": - - await self.send_message(chat_id, f"⏳ در حال آماده‌سازی پاسخ به «{text}» ...") - - # answer = nahj_chat.bale_chat(text) - final_result = await nahj_chat.bale_complex_chat(text) - - if final_result: - - sub_questions = 'سوالات جزئی مرتبط با سوال کاربر:\n' - for i, q in enumerate(final_result.get('sub_qa'),1): - sub_questions += f'{i}. {q.get("question")}\n' - - sub_qa_text = '' - for i, qa in enumerate(final_result.get('sub_qa'),1): - sub_qa_text += f'{i}. {qa.get("question")}\n{qa.get("answer")}\n\n' - # reply_content = f'''سوال اصلی: {text}\n\n{sub_questions}\n\n* * * * *سوالات جزئی:\n{sub_qa_text.strip()}\n\nپاسخ نهائی:\n{final_result.get('final_answer',0)}''' - reply_content = f'''{final_result.get('final_answer',0)}''' - reply = reply_content.strip() - - else: - reply = 'خطا در تولید پاسخ!' - - else: - reply = "لطفا یکی از گزینه‌های زیر را انتخاب نمائید" - await self.send_message(chat_id, reply, keyboard) - return - - reply_len = len(reply.split()) - print(f"len answer: {reply_len}") - print(f"ready for next ...") - print('+'*20) - print('+'*20) - reply_chuncs = [] - - reply_chuncs = await self.split_text_into_chunks(reply) - - for i, paragraph in enumerate(reply_chuncs): - await self.send_message(chat_id, paragraph, keyboard) - - # await self.save_chat_data(text, reply, first_name, username) - return reply - # =========================== # ساخت اپلیکیشن FastAPI # =========================== app = FastAPI() +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # یا دامنه فرانت‌اند + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + @app.post("/chat") -async def chat(request: Request): +async def chat(request: Message): """ دریافت مستقیم آبجکت ورودی به صورت JSON """ + # دریافت بدنه درخواست - body = await request.json() + body = dict(request) + user_query = body.get('user_query') + # دسترسی به فیلدها - user_input = body.get("message") - metadata = body.get("metadata", {}) + # user_input = body.get("message") + # metadata = body.get("metadata", {}) # update_id = await save_entery(item) update_id = random.randint(1, 10) - answer = nahj_chat.bale_chat(user_input) + answer = nahj_chat.bale_chat(user_query) if not answer: - reply = 'خطا در تولید پاسخ!' + answer['output'] = 'خطا در تولید پاسخ!' + answer['status'] = 'Failed' + answer['similarity_result'] = [] + answer['reference_ids'] = [] if answer: - await update_request(update_id, answer) + try: + await update_request(update_id = update_id, answer = answer['output']) + except: + pass + + print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') + print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') + retrived_sections_list = answer['similarity_result'] + print(type(retrived_sections_list)) + print(retrived_sections_list) + + print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') + print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') # برگرداندن آبجکت خروجی (خودکار به JSON تبدیل می‌شود) - return { - "output": answer, - "status": "ok", - "input_received": user_input - } + return answer print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%') print(f'!!! NAHJ-RUNNER IS READY !!!') @@ -297,7 +178,8 @@ print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%') # result = asyncio.run(chat()) if __name__ == "__main__": - uvicorn.run(app, host="127.0.0.1", port=8010) + pass + # uvicorn.run(app, host="127.0.0.1", port=8010) # uvicorn.run( # "nahj_engine_general_runner:app", # host="0.0.0.0", @@ -306,4 +188,5 @@ if __name__ == "__main__": # log_level="debug" # ) -# uvicorn nahj_engine_general_runner:app --reload --host 0.0.0.0 --port 8010 \ No newline at end of file +# uvicorn nahj_engine_general_runner:app --reload --host 0.0.0.0 --port 8010 +# uvicorn main:app --reload --host 0.0.0.0 --port 8000 >> runs llm for pajoohesh \ No newline at end of file diff --git a/nahj_get_metadata_oss.py b/nahj_get_metadata_oss.py index e918ee2..84c6793 100644 --- a/nahj_get_metadata_oss.py +++ b/nahj_get_metadata_oss.py @@ -201,7 +201,27 @@ async def single_async_item( reasoning_effort=reasoning_effort, extra_body={"priority": priority}, ) - response = await asyncio.wait_for(coro, timeout=timeout) + + # response = await asyncio.wait_for(coro, timeout=timeout) + response = await asyncio.wait_for( + client.chat.completions.parse( + model=model_name, + messages=messages, + temperature=temperature, + top_p=top_p, + max_tokens=max_token, + stop=stop, + response_format=output_schema, + reasoning_effort=reasoning_effort, + extra_body={"priority": priority}, + ), + timeout=timeout + ) + print('----------------------------------------------------') + print('----------------------------------------------------') + print(response) + print('----------------------------------------------------') + print('----------------------------------------------------') if print_logs: print(f"parse response ---- {response}") @@ -319,11 +339,73 @@ async def main(): print(f'all_paragraphs: {all_paragraphs}') print('---------------------------------------------') -async def oss_test(): - item = {} - item['assistant_prompt'] = "تو یک دستیار خبره در زمینه تدوین متون علمی هستی" - item['system_prompt'] = "پاسخ ها فقط باید علمی باشند و سبک نگارش طنز، سرگرمی، ادبی،احساسی و ... قابل قبول نیست." - item['user_prompt'] = "ابعاد مختلف علوم اجتماعی محاسباتی کدام است؟" +def process_item( + item:dict, + output_schema:BaseModel, + model_name:str="gpt-oss-120b", + api_url:str="http://172.16.29.102:8001/v1/", + api_key:str="EMPTY", + temperature:float=0.1, + top_p:float=1, + reasoning_effort:str="medium", + max_token:int=1024, + priority:int=1, + ): + try: + client = OpenAI( + base_url=api_url, + api_key=api_key, + ) + messages = [ + {"role": "user", "content": item["user_prompt"]}, + ] + if item.get("system_prompt"): + messages.append({"role": "system", "content": item["system_prompt"]}) + if item.get("assistant_prompt"): + messages.append( + {"role": "assistant", "content": item["assistant_prompt"]} + ) + response = client.chat.completions.parse( + model=model_name, + messages=messages, + temperature=temperature, + top_p=top_p, + reasoning_effort=reasoning_effort, + max_tokens=max_token, + stop=None, + response_format=output_schema, + extra_body={"priority": priority}, + ) + + parsed = ( + response.choices[0].message.parsed + if response and response.choices and response.choices[0].message.parsed + else {"raw_text": str(response)} + ) + + parsed = output_schema.model_validate(parsed) + + item["llm_output"] = dict(parsed.model_dump()) + + return item, 200 + + except Exception as e: + print(f"⚠️ Error __process_item {item['id']}: {traceback.print_exc()}") + return None, 400 + + +# res = process_item( +# item={"user_prompt":"سلام خوبی"}, +# output_schema=Result, +# ) + +async def oss_test(SYSTEM_PROMPT,USER_PROMPT,Dictt): + item['system_prompt'] = SYSTEM_PROMPT + item['user_prompt'] = f"{USER_PROMPT}\n{Dictt}" + # item = {} + # item['assistant_prompt'] = "تو یک دستیار خبره در زمینه تدوین متون علمی هستی" + # item['system_prompt'] = "پاسخ ها فقط باید علمی باشند و سبک نگارش طنز، سرگرمی، ادبی،احساسی و ... قابل قبول نیست. پاسخ فقط به زبان فارسی باشد و فقط اصطلاحات علمی را در صورت نیاز به زبان انگلیسی بیاور." + # item['user_prompt'] = "ابعاد مختلف علوم اجتماعی محاسباتی کدام است؟" response = await single_async_item( api_url="http://2.188.15.102:8001/v1/", api_key="EMPTY", @@ -338,10 +420,10 @@ async def oss_test(): max_token=None, return_reason=True, return_used_token=True, - timeout=300 + timeout=1000 ) print(response['llm_output']) - pass + return response['llm_output'] if __name__ == "__main__":