Compare commits

...

4 Commits

Author SHA1 Message Date
31da2788a3 nahj get metadata 2026-05-12 12:58:25 +00:00
447d37a274 add oss madule 2026-05-12 12:57:18 +00:00
61df642a34 nahj engine 2026-05-12 12:53:07 +00:00
d21fa4d2c5 api address edit 2026-05-06 14:57:53 +00:00
4 changed files with 522 additions and 2 deletions

View File

@ -80,7 +80,7 @@ def load_faiss_index(index_path: str, metadata_path: str):
def get_client(): def get_client():
url = "https://api.avalai.ir/v1" url = "https://api.avalapis.ir/v1" #avalapis #avalapis
client = OpenAI( client = OpenAI(
api_key=get_key(), api_key=get_key(),
base_url=url, base_url=url,

View File

@ -0,0 +1,309 @@
import json
from fastapi import FastAPI, Request
from pydantic import BaseModel
import requests
import logging
import uvicorn
import random
import time
import nahj_engine as nahj_chat
import data_model as dm
# ===========================
# پیکربندی اولیه
# ===========================
TOKEN = "602738113:OcVhjcsXqvE6D9FUytdoMZ096DPKYIUwnrk"
API_URL = f"https://tapi.bale.ai/bot{TOKEN}/"
# راه‌اندازی لاگر
logging.basicConfig(
filename="./bale_bot/bot.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
# ===========================
# define import model class
# ===========================
class Message(BaseModel):
chat: dict
text: str | None = None
class Update(BaseModel):
message: Message | None = None
async def get_latest_req_id(self):
latest_request = dm.get_last_request()
latest_req_id = latest_request['update_id']
if not latest_req_id:
latest_req_id = 0
return latest_req_id + 1
async def save_entery(self, update_item):
is_active = True
answer = ''
message = update_item['message']
fromm = message['from']
chat = message['chat']
username, first_name, last_name = '','',''
if 'username' in fromm:
username = fromm['username']
if 'first_name' in fromm:
first_name = fromm['first_name']
if 'last_name' in fromm:
last_name = fromm['last_name']
try:
dm.insert_request(update_item['update_id'],username,message['text'], answer, message['message_id'],fromm['id'],fromm['is_bot'],message['date'],chat['id'],chat['type'],first_name,last_name, is_active)
except:
return update_item['update_id']
return update_item['update_id']
async def update_request(self, update_id, answer):
dm.update_request(update_id= update_id, answer= answer)
async def split_text_into_chunks(self, text, max_length=4000):
"""
تقسیم یک متن به چانکهای حداکثر max_length کاراکتری، بدون خراب کردن معنا با رعایت انتهای جملهها.
:param text: متن ورودی
:param max_length: حداکثر طول هر چانک (پیشفرض 4000)
:return: لیستی از چانکهای متن
"""
chunks = [] # لیستی برای ذخیره چانک‌ها
start = 0 # شروع متن برای هر چانک
while start < len(text):
# اگر متن باقی‌مانده کوتاه‌تر از max_length باشد، کل آن را اضافه کنید
if len(text) - start <= max_length:
chunks.append(text[start:])
break
# پیدا کردن نقطه پایانی چانک (حداکثر تا max_length کاراکتر جلو بروید)
end = start + max_length
# اگر در وسط یک جمله هستیم، به عقب برگردید تا انتهای جمله پیدا شود
while end > start and text[end - 1] not in '.!?':
end -= 1
# اگر هیچ انتهای جمله پیدا نشد، متن را تا max_length ببرید
if end == start:
end = start + max_length
# اضافه کردن چانک به لیست
chunks.append(text[start:end])
# شروع چانک بعدی
start = end
return chunks
async def save_chat_data(self,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.post("/chat")
async def chat(request: Request):
"""
دریافت مستقیم آبجکت ورودی به صورت JSON
"""
# دریافت بدنه درخواست
body = await request.json()
# دسترسی به فیلدها
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)
if not answer:
reply = 'خطا در تولید پاسخ!'
if answer:
await update_request(update_id, answer)
# برگرداندن آبجکت خروجی (خودکار به JSON تبدیل می‌شود)
return {
"output": answer,
"status": "ok",
"input_received": user_input
}
print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
print(f'!!! NAHJ-RUNNER IS READY !!!')
print(f'%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
# ===========================
# (local execution)
# ===========================
# if __name__ == "__main__":
# import asyncio
# result = asyncio.run(chat())
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8010)
# uvicorn.run(
# "nahj_engine_general_runner:app",
# host="0.0.0.0",
# port=8010,
# reload=True, # فعال بودن reload برای دیباگ مفید است
# log_level="debug"
# )
# uvicorn nahj_engine_general_runner:app --reload --host 0.0.0.0 --port 8010

View File

@ -11,6 +11,11 @@ import asyncio
import traceback import traceback
from openai import AsyncOpenAI from openai import AsyncOpenAI
import copy, asyncio, traceback
from openai import OpenAI, AsyncOpenAI, LengthFinishReasonError
from typing import List, Union
from pydantic import BaseModel
today = f'{datetime.datetime.now().year}{datetime.datetime.now().month}{datetime.datetime.now().day}' today = f'{datetime.datetime.now().year}{datetime.datetime.now().month}{datetime.datetime.now().day}'
SYSTEM_PROMPT = """ SYSTEM_PROMPT = """
@ -148,6 +153,99 @@ async def single_simple_async_proccess_item(
traceback.print_exc() traceback.print_exc()
raise RuntimeError(f"⚠️ Error in API call: {str(e)}") raise RuntimeError(f"⚠️ Error in API call: {str(e)}")
class Result(BaseModel):
result : str
async def single_async_item(
api_url,
api_key,
item,
reasoning_effort,
temperature,
top_p,
semaphore_number,
model_name,
priority,
output_schema=None,
max_token=4096,
print_logs=False,
return_reason=False,
stop=None,
return_used_token=False,
timeout=300,
):
try:
async with AsyncOpenAI(
base_url=api_url, api_key=api_key
) as client:
semaphore = asyncio.Semaphore(semaphore_number)
async with semaphore:
messages = [{"role": "user", "content": item["user_prompt"]}]
if item.get("system_prompt"):
messages.insert(
0, {"role": "system", "content": item["system_prompt"]}
)
if item.get("assistant_prompt"):
messages.append(
{"role": "assistant", "content": item["assistant_prompt"]}
)
coro = 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},
)
response = await asyncio.wait_for(coro, timeout=timeout)
if print_logs:
print(f"parse response ---- {response}")
parsed_obj = response.choices[0].message.parsed
# print(f'parsed_obj {parsed_obj}')
if parsed_obj is None:
return {
"error": "Failed to parse response",
"raw": str(response),
}
parsed_obj = output_schema.model_validate(parsed_obj)
# Validate just in case (optional, چون .parse already does it)
if return_reason:
reasoning_content = response.choices[
0
].message.reasoning_content
if return_used_token:
_total_token = response.usage.total_tokens
item["llm_output"] = (
parsed_obj.model_dump(),
str(reasoning_content),
int(_total_token),
)
return item
item["llm_output"] = (
parsed_obj.model_dump(),
str(reasoning_content)
)
return item
item["llm_output"] = parsed_obj.model_dump()
return item
except asyncio.TimeoutError:
print(f"⏳ Timeout on item {item}")
return None
except Exception as e:
print(f"⚠️ Error __process_item {item}: {traceback.print_exc()}")
return None
async def main(): async def main():
with open('./leader_data/khamenei_messages_4.json', 'r', encoding='utf-8') as file: with open('./leader_data/khamenei_messages_4.json', 'r', encoding='utf-8') as file:
data = json.load(file) data = json.load(file)
@ -221,9 +319,33 @@ async def main():
print(f'all_paragraphs: {all_paragraphs}') print(f'all_paragraphs: {all_paragraphs}')
print('---------------------------------------------') print('---------------------------------------------')
async def oss_test():
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",
item=item,
reasoning_effort="medium",
temperature=0.1,
top_p=1,
semaphore_number=1,
model_name="gpt-oss-120b",
priority=1,
output_schema=Result,
max_token=None,
return_reason=True,
return_used_token=True,
timeout=300
)
print(response['llm_output'])
pass
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) # asyncio.run(main())
asyncio.run(oss_test())

89
oss.py Normal file
View File

@ -0,0 +1,89 @@
async def single_async_item(
api_url,
api_key,
item,
reasoning_effort,
temperature,
top_p,
semaphore_number,
model_name,
priority,
output_schema=None,
max_token=4096,
print_logs=False,
return_reason=False,
stop=None,
return_used_token=False,
timeout=300,
):
try:
async with AsyncOpenAI(
base_url=api_url, api_key=api_key
) as client:
semaphore = asyncio.Semaphore(semaphore_number)
async with semaphore:
messages = [{"role": "user", "content": item["user_prompt"]}]
if item.get("system_prompt"):
messages.insert(
0, {"role": "system", "content": item["system_prompt"]}
)
if item.get("assistant_prompt"):
messages.append(
{"role": "assistant", "content": item["assistant_prompt"]}
)
coro = 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},
)
response = await asyncio.wait_for(coro, timeout=timeout)
if print_logs:
print(f"parse response ---- {response}")
parsed_obj = response.choices[0].message.parsed
# print(f'parsed_obj {parsed_obj}')
if parsed_obj is None:
return {
"error": "Failed to parse response",
"raw": str(response),
}
parsed_obj = output_schema.model_validate(parsed_obj)
# Validate just in case (optional, چون .parse already does it)
if return_reason:
reasoning_content = response.choices[
0
].message.reasoning_content
if return_used_token:
_total_token = response.usage.total_tokens
item["llm_output"] = (
parsed_obj.model_dump(),
str(reasoning_content),
int(_total_token),
)
return item
item["llm_output"] = (
parsed_obj.model_dump(),
str(reasoning_content)
)
return item
item["llm_output"] = parsed_obj.model_dump()
return item
except asyncio.TimeoutError:
print(f"⏳ Timeout on item {item}")
return None
except Exception as e:
print(f"⚠️ Error __process_item {item}: {traceback.print_exc()}")
return None