be4 es-connect
This commit is contained in:
parent
2b6104bf51
commit
f062fba571
1
.gitignore
vendored
Normal file → Executable file
1
.gitignore
vendored
Normal file → Executable file
|
|
@ -7,3 +7,4 @@ app/lib
|
||||||
_temp.py
|
_temp.py
|
||||||
_data_json/
|
_data_json/
|
||||||
__pycache__/*
|
__pycache__/*
|
||||||
|
run_dev.bash
|
||||||
|
|
|
||||||
123
app.py
Executable file
123
app.py
Executable file
|
|
@ -0,0 +1,123 @@
|
||||||
|
# app.py
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from utils.workflow import ElasticHelper, Formatter, RequestManager
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from router.bale_bot import router as bale_router
|
||||||
|
from router.bale_bot import initialize_webhook
|
||||||
|
from utils.main import BaleBot, UserManager
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# --- Lifespan manager ---
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# 🚀 Startup — همه موارد مقداردهی اولیه باید اینجا باشند
|
||||||
|
print("🚀 Starting up Bale-Bot Backend system...")
|
||||||
|
|
||||||
|
# فقط یک بار در ابتدای برنامه `.env` را بارگذاری کنیم
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
TOKEN = os.getenv("BALE_TOKEN")
|
||||||
|
ES_URL = os.getenv("ES_URL")
|
||||||
|
ES_PASSWORD = os.getenv("ES_PASSWORD")
|
||||||
|
ES_USER_NAME = os.getenv("ES_USER_NAME")
|
||||||
|
ES_INDEX_NAME = os.getenv("ES_INDEX_NAME")
|
||||||
|
GLOBAL_DOMAIN = os.getenv("GLOBAL_DOMAIN")
|
||||||
|
BACK_END_HOST= os.getenv("BACK_END_HOST")
|
||||||
|
BACK_END_PORT= os.getenv("BACK_END_PORT")
|
||||||
|
BACK_END_URL = f"http://{BACK_END_HOST}:{BACK_END_PORT}"
|
||||||
|
|
||||||
|
# 🔍 گرفتن تنظیمات
|
||||||
|
required_vars = {
|
||||||
|
"TOKEN": TOKEN,
|
||||||
|
"ES_URL": ES_URL,
|
||||||
|
"ES_PASSWORD": ES_PASSWORD,
|
||||||
|
"ES_USER_NAME": ES_USER_NAME,
|
||||||
|
"ES_INDEX_NAME": ES_INDEX_NAME,
|
||||||
|
"GLOBAL_DOMAIN": GLOBAL_DOMAIN,
|
||||||
|
}
|
||||||
|
missing = [k for k, v in required_vars.items() if not v]
|
||||||
|
if missing:
|
||||||
|
raise EnvironmentError(
|
||||||
|
f"Missing required environment variables: {missing}\nPLZ add the required field in the .env file !"
|
||||||
|
)
|
||||||
|
|
||||||
|
app.state.es_helper = ElasticHelper(
|
||||||
|
es_url=ES_URL,
|
||||||
|
es_pass=ES_PASSWORD,
|
||||||
|
es_user=ES_USER_NAME,
|
||||||
|
)
|
||||||
|
|
||||||
|
app.state.base_url = f"https://tapi.bale.ai/bot{TOKEN}"
|
||||||
|
app.state.webhook_url = f"{GLOBAL_DOMAIN}/webhook/{TOKEN}"
|
||||||
|
app.state.set_webhook_url = f"https://tapi.bale.ai/bot{TOKEN}/setWebhook"
|
||||||
|
app.state.es_index_name = ES_INDEX_NAME
|
||||||
|
app.state.bale_token = TOKEN
|
||||||
|
|
||||||
|
# اجباری است برای ربات
|
||||||
|
initialize_webhook(
|
||||||
|
webhook_url=app.state.webhook_url,
|
||||||
|
set_webhook_url=app.state.set_webhook_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
app.state.user_manager = UserManager()
|
||||||
|
app.state.formatter = Formatter()
|
||||||
|
app.state.request_manager = RequestManager(
|
||||||
|
host_url=BACK_END_URL,
|
||||||
|
)
|
||||||
|
# بله بات
|
||||||
|
bale_bot = BaleBot(
|
||||||
|
user_manager=app.state.user_manager,
|
||||||
|
es_helper=app.state.es_helper,
|
||||||
|
es_index_name=app.state.es_index_name,
|
||||||
|
token=app.state.bale_token,
|
||||||
|
formatter= app.state.formatter,
|
||||||
|
back_end_url = BACK_END_URL,
|
||||||
|
request_manager = app.state.request_manager,
|
||||||
|
)
|
||||||
|
app.state.bale_bot = bale_bot
|
||||||
|
print("=== Bale-Bot Initialized ===")
|
||||||
|
|
||||||
|
yield # برنامه در این حالت اجرا میشود
|
||||||
|
|
||||||
|
print("🛑 Shutting down Bale-Bot Backend system...")
|
||||||
|
|
||||||
|
|
||||||
|
# --- Application factory ---
|
||||||
|
def create_app() -> FastAPI:
|
||||||
|
app = FastAPI(
|
||||||
|
title="Bale-Bot Backend",
|
||||||
|
version="0.1.0",
|
||||||
|
lifespan=lifespan,
|
||||||
|
# برای محیط production، میتوانید docs + redoc را غیرفعال کنید
|
||||||
|
# docs_url=None, redoc_url=None
|
||||||
|
)
|
||||||
|
|
||||||
|
# 🌐 CORS
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # فقط در dev — در prod لیست دقیق دامنهها را بگذارید
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# 🏠 Health routes
|
||||||
|
@app.get("/", include_in_schema=False)
|
||||||
|
async def health_check():
|
||||||
|
return {"status": "ok", "message": "Bale-Bot Backend is running"}
|
||||||
|
|
||||||
|
@app.get("/ping", include_in_schema=False)
|
||||||
|
async def ping():
|
||||||
|
return {"pong": True}
|
||||||
|
|
||||||
|
# 📦 روتها
|
||||||
|
app.include_router(bale_router)
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
############## Create app
|
||||||
|
app = create_app()
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
|
|
||||||
START_BUTTONS_INLINE = [
|
|
||||||
[{"text": "گفتگو", "callback_data": "law_chat"}],
|
|
||||||
[{"text": "جستجو", "callback_data": "law_search"}],
|
|
||||||
[
|
|
||||||
# {"text": "راهنما", "callback_data": "help"},
|
|
||||||
{"text": "درباره ما", "callback_data": "about"},
|
|
||||||
{"text": "تماس با ما", "callback_data": "contact"},
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
BUTTON_TEXT_TO_CALLBACK = {
|
|
||||||
"جستجو": "law_search",
|
|
||||||
"گفتگو": "law_chat",
|
|
||||||
# "راهنما": "help",
|
|
||||||
"درباره ما": "about",
|
|
||||||
"تماس با ما": "contact",
|
|
||||||
}
|
|
||||||
BUTTON_TEXT_TO_CALLBACK_LIST = [
|
|
||||||
[
|
|
||||||
"جستجو",
|
|
||||||
"گفتگو",
|
|
||||||
# "راهنما",
|
|
||||||
"درباره ما",
|
|
||||||
"تماس با ما",
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
MORE_BUTTON = [[{"text": "🔽 نمایش نتایج بیشتر", "callback_data": f"more"}]]
|
|
||||||
|
|
||||||
|
|
||||||
CHAT_EFFORT_BUTTONS = [
|
|
||||||
[
|
|
||||||
# {"text": "⚡ سریع", "callback_data": "chat_effort_low"},
|
|
||||||
{"text": "🧠 بررسی عمیق تر", "callback_data": "chat_effort_medium"},
|
|
||||||
# {"text": "⚖ نرمال", "callback_data": "chat_effort_medium"},
|
|
||||||
]
|
|
||||||
# [
|
|
||||||
# {"text": "➕ ادامه پاسخ", "callback_data": "chat_more"}
|
|
||||||
# ]
|
|
||||||
]
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
STARTMESSAGE = """👋 سلام دوست عزیز! 🤗
|
|
||||||
به دستیار هوشمند قانون یار خوش آمدید!
|
|
||||||
فقط کافیه به من بگید چه کمکی از دستم برمیاد!"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ABOUT = """
|
|
||||||
من ربات گفتگوگر حقوقی هستم که روی قوانین رسمی جمهوری اسلامی ایران از سامانه قانونیار مجلس شورای اسلامی توسعه یافتم.
|
|
||||||
لذا به هر سوال و گفتگویی طبق همان منابع پاسخ میدهم
|
|
||||||
نشانی سامانه منبع در زیر آمده است
|
|
||||||
[qanonyar.parliran.ir](https://qanonyar.parliran.ir)
|
|
||||||
|
|
||||||
کارفرما : مرکز فناوری مجلس شورای اسلامی ایران
|
|
||||||
|
|
||||||
"""
|
|
||||||
CONTACT_US = """لطفا برای ارتباط با ما از طریق مرکز فناوری مجلس شورای اسلامی ایران اقدام فرمایید"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
JOST_OJO_TEXT = """📚✨ هر پرسش حقوقی که از قوانین کشور دارید یا هر متن حقوقی که براتون مهم هست، همینجا بفرستید تا با استناد به قوانین با شما گفتگو کنم..
|
|
||||||
|
|
||||||
📝 میتونید:
|
|
||||||
• موضوع کلی رو بگید
|
|
||||||
• سؤال بپرسید
|
|
||||||
• یا متن حقوقی که داری برام بنویسید
|
|
||||||
|
|
||||||
🎯 هرچقدر دقیقتر و واضحتر توضیح بدید، بهتر میتوانم راهنمایی کنم.
|
|
||||||
"""
|
|
||||||
HOURGLASS = "⏳" # استیکر ساعت شنی
|
|
||||||
WAIT_TEXT = f"{HOURGLASS} لطفاً کمی صبر کنید...\nدر حال پردازش درخواست قبلی شما هستم."
|
|
||||||
|
|
||||||
|
|
||||||
GOFT_OGO_TEXT = "💬 آماده گفتگو هستم"
|
|
||||||
116
base_model.py
116
base_model.py
|
|
@ -1,116 +0,0 @@
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class BaleStartMessageForm(BaseModel):
|
|
||||||
id: int
|
|
||||||
is_bot: bool = False
|
|
||||||
first_name: str
|
|
||||||
last_name: Optional[str] = None
|
|
||||||
username: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class BaleStartMessageChat(BaseModel):
|
|
||||||
id: int
|
|
||||||
type: str
|
|
||||||
username: Optional[str] = None
|
|
||||||
first_name: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class BaleStartMessage(BaseModel):
|
|
||||||
message_id: int
|
|
||||||
from_user: BaleStartMessageForm = Field(..., alias="from")
|
|
||||||
date: int
|
|
||||||
chat: BaleStartMessageChat
|
|
||||||
text: str
|
|
||||||
entities: List[dict] = []
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
populate_by_name = True
|
|
||||||
|
|
||||||
|
|
||||||
class BaleCallbackFrom(BaseModel):
|
|
||||||
id: int
|
|
||||||
is_bot: bool
|
|
||||||
first_name: str
|
|
||||||
username: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class BaleCallbackMessage(BaseModel):
|
|
||||||
message_id: int
|
|
||||||
chat: BaleStartMessageChat
|
|
||||||
text: Optional[str]
|
|
||||||
|
|
||||||
|
|
||||||
class BaleCallbackQuery(BaseModel):
|
|
||||||
id: str
|
|
||||||
from_user: BaleCallbackFrom = Field(..., alias="from")
|
|
||||||
message: BaleCallbackMessage
|
|
||||||
data: str
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
populate_by_name = True
|
|
||||||
|
|
||||||
|
|
||||||
class BaleUpdate(BaseModel):
|
|
||||||
update_id: int
|
|
||||||
message: Optional[BaleStartMessage] = None
|
|
||||||
callback_query: Optional[BaleCallbackQuery] = None
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
exclude_none = True
|
|
||||||
|
|
||||||
|
|
||||||
class QaChatSingle(BaseModel):
|
|
||||||
id: str
|
|
||||||
chat_id: int
|
|
||||||
user_query: str
|
|
||||||
model_key: str
|
|
||||||
model_effort: str
|
|
||||||
|
|
||||||
retrived_passage: str
|
|
||||||
retrived_ref_ids: str
|
|
||||||
retrived_duration: Optional[int] = 0
|
|
||||||
prompt_type: str = "question"
|
|
||||||
llm_duration: int
|
|
||||||
full_duration: Optional[int] = 0
|
|
||||||
time_create: Optional[int] = 0
|
|
||||||
used_ref_ids: Optional[str] = ""
|
|
||||||
status_text: Optional[str] = ""
|
|
||||||
status: Optional[int] = 0
|
|
||||||
prompt_answer: str
|
|
||||||
other_info: dict | None
|
|
||||||
|
|
||||||
|
|
||||||
class QaChatBlock(BaseModel):
|
|
||||||
id: str
|
|
||||||
title: str
|
|
||||||
user_id: str
|
|
||||||
is_premium: bool
|
|
||||||
chat: QaChatSingle
|
|
||||||
total_token: int
|
|
||||||
is_end: bool
|
|
||||||
|
|
||||||
|
|
||||||
class QaChat(BaseModel):
|
|
||||||
id: str
|
|
||||||
chat_id: int
|
|
||||||
title: Optional[str] = ""
|
|
||||||
user_id: str
|
|
||||||
user_query: str
|
|
||||||
query_type: str = "question" # llm -> greeting, other, legal_question | rag -> question
|
|
||||||
full_duration: Optional[float] = 0
|
|
||||||
other_info: Optional[dict] = ""
|
|
||||||
|
|
||||||
ss_ref_ids: Optional[List[str]] = ""
|
|
||||||
ss_model_key: Optional[str] = ""
|
|
||||||
ss_duration: Optional[float] = 0
|
|
||||||
ss_answer: Optional[str] = ""
|
|
||||||
|
|
||||||
llm_ref_ids: Optional[List[str]] = []
|
|
||||||
llm_model_key: Optional[str] = ""
|
|
||||||
llm_duration: Optional[float] = 0
|
|
||||||
llm_answer: Optional[str] = ""
|
|
||||||
|
|
||||||
status_text: Optional[str] = ""
|
|
||||||
status: Optional[int] = 0
|
|
||||||
26
dependencies.py
Executable file
26
dependencies.py
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# dependencies.py
|
||||||
|
from fastapi import Depends, Request
|
||||||
|
from utils.workflow import ElasticHelper
|
||||||
|
|
||||||
|
def _get_es_helper(request: Request) -> ElasticHelper:
|
||||||
|
return request.app.state.es_helper
|
||||||
|
|
||||||
|
def _get_base_url(request: Request) -> str:
|
||||||
|
return request.app.state.base_url
|
||||||
|
|
||||||
|
def _get_webhook_url(request: Request) -> str:
|
||||||
|
return request.app.state.webhook_url
|
||||||
|
|
||||||
|
def _get_base_set_webhook_url(request: Request) -> str:
|
||||||
|
return request.app.state.set_webhook_url
|
||||||
|
|
||||||
|
def _get_base_es_index_name(request: Request) -> str:
|
||||||
|
return request.app.state.es_index_name
|
||||||
|
|
||||||
|
def _get_bale_token(request: Request) -> str:
|
||||||
|
return request.app.state.bale_token
|
||||||
|
|
||||||
|
def _get_bale_bot(request: Request) -> str:
|
||||||
|
return request.app.state.bale_bot
|
||||||
|
|
||||||
|
|
||||||
632
main.py
632
main.py
|
|
@ -1,632 +0,0 @@
|
||||||
#################
|
|
||||||
from fastapi import FastAPI, Request, HTTPException
|
|
||||||
import requests, logging, asyncio, httpx, os, uuid, traceback, orjson, copy, uvicorn, time, re
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from pathlib import Path
|
|
||||||
from time import sleep
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
#################
|
|
||||||
from base_model import BaleUpdate, QaChat
|
|
||||||
from bale_buttons import (
|
|
||||||
START_BUTTONS_INLINE,
|
|
||||||
BUTTON_TEXT_TO_CALLBACK,
|
|
||||||
BUTTON_TEXT_TO_CALLBACK_LIST,
|
|
||||||
MORE_BUTTON, CHAT_EFFORT_BUTTONS
|
|
||||||
)
|
|
||||||
from bale_massages import (
|
|
||||||
STARTMESSAGE,
|
|
||||||
ABOUT,
|
|
||||||
CONTACT_US,
|
|
||||||
JOST_OJO_TEXT,
|
|
||||||
HOURGLASS,
|
|
||||||
WAIT_TEXT,GOFT_OGO_TEXT
|
|
||||||
)
|
|
||||||
from utils import load_orjson, save_orjson, ElasticHelper, split_text_chunks
|
|
||||||
|
|
||||||
|
|
||||||
############## Create app
|
|
||||||
app = FastAPI()
|
|
||||||
##############
|
|
||||||
|
|
||||||
############## Global-Params
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
TOKEN = os.getenv("BALE_TOKEN")
|
|
||||||
ES_URL = os.getenv("ES_URL")
|
|
||||||
ES_PASSWORD = os.getenv("ES_PASSWORD")
|
|
||||||
ES_USER_NAME = os.getenv("ES_USER_NAME")
|
|
||||||
ES_INDEX_NAME = os.getenv("ES_INDEX_NAME")
|
|
||||||
|
|
||||||
BASE_URL = f"https://tapi.bale.ai/bot{TOKEN}"
|
|
||||||
DATA_DIR = os.path.join(".", "_data_json")
|
|
||||||
if not os.path.exists(DATA_DIR):
|
|
||||||
os.makedirs(DATA_DIR)
|
|
||||||
|
|
||||||
GLOBAL_DOMAIN = "https://bl.tavasi.ir" # f"https://YOUR_DOMAIN.com
|
|
||||||
WEBHOOK_URL = f"{GLOBAL_DOMAIN}/webhook/{TOKEN}"
|
|
||||||
SET_WEBHOOK_URL = f"https://tapi.bale.ai/bot{TOKEN}/setWebhook"
|
|
||||||
|
|
||||||
MAX_LIMIT_RAG = 100
|
|
||||||
STEP_RAG = 10
|
|
||||||
USER_STATE = {}
|
|
||||||
USER_LAST_QUERY = {}
|
|
||||||
USER_PAGINATION = {}
|
|
||||||
USER_CHAT_EFFORT = {}
|
|
||||||
USER_CHAT_LIMIT = {}
|
|
||||||
USER_LAST_CHAT_QUERY = {}
|
|
||||||
|
|
||||||
TIME_OUT = 60
|
|
||||||
MAX_LEN = 4000 # کمی کمتر از حد پایه امنتر است
|
|
||||||
|
|
||||||
ES_HELPER = ElasticHelper(
|
|
||||||
es_url=ES_URL,
|
|
||||||
es_pass=ES_PASSWORD,
|
|
||||||
es_user=ES_USER_NAME,
|
|
||||||
)
|
|
||||||
|
|
||||||
class UserState(str, Enum):
|
|
||||||
MAIN = "main"
|
|
||||||
LAW_SEARCH = "law_search"
|
|
||||||
LAW_CHAT = "law_chat"
|
|
||||||
BUSY = "busy"
|
|
||||||
|
|
||||||
|
|
||||||
class SessionManager:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.states = {}
|
|
||||||
self.pagination = {}
|
|
||||||
|
|
||||||
def init(self, chat_id: int):
|
|
||||||
if chat_id not in self.states:
|
|
||||||
self.states[chat_id] = UserState.MAIN
|
|
||||||
|
|
||||||
def get_state(self, chat_id: int):
|
|
||||||
return self.states.get(chat_id, UserState.MAIN)
|
|
||||||
|
|
||||||
def set_state(self, chat_id: int, state: UserState):
|
|
||||||
self.states[chat_id] = state
|
|
||||||
|
|
||||||
def set_query(self, chat_id: int, query: str):
|
|
||||||
self.pagination[chat_id] = {"query": query, "limit": 10, "step": 10}
|
|
||||||
|
|
||||||
def increase_limit(self, chat_id: int):
|
|
||||||
if chat_id in self.pagination:
|
|
||||||
self.pagination[chat_id]["limit"] += self.pagination[chat_id]["step"]
|
|
||||||
return self.pagination[chat_id]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_pagination(self, chat_id: int):
|
|
||||||
return self.pagination.get(chat_id)
|
|
||||||
|
|
||||||
def clear(self, chat_id: int):
|
|
||||||
self.pagination.pop(chat_id, None)
|
|
||||||
self.states[chat_id] = UserState.MAIN
|
|
||||||
|
|
||||||
|
|
||||||
class BaleBot:
|
|
||||||
|
|
||||||
def __init__(self, session: SessionManager):
|
|
||||||
self.session = session
|
|
||||||
|
|
||||||
async def handle_update(self, update: BaleUpdate):
|
|
||||||
|
|
||||||
if update.message:
|
|
||||||
return await self.handle_message(update)
|
|
||||||
|
|
||||||
if update.callback_query:
|
|
||||||
return await self.handle_callback(update)
|
|
||||||
|
|
||||||
async def handle_message(self, update: BaleUpdate):
|
|
||||||
chat_id = update.message.chat.id
|
|
||||||
text = (update.message.text or "").strip()
|
|
||||||
|
|
||||||
self.session.init(chat_id)
|
|
||||||
|
|
||||||
# /start
|
|
||||||
if text == "/start":
|
|
||||||
self.session.clear(chat_id)
|
|
||||||
send_message(chat_id, STARTMESSAGE, buttons=START_BUTTONS_INLINE)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
if text == "تماس با ما":
|
|
||||||
send_message(chat_id, CONTACT_US)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
if text == "درباره ما":
|
|
||||||
send_message(chat_id, ABOUT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
# ✅ اگر BUSY بود
|
|
||||||
if self.session.get_state(chat_id) == UserState.BUSY:
|
|
||||||
send_message(chat_id, WAIT_TEXT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
# ✅ شبیهسازی کلیک روی دکمهها با متن
|
|
||||||
if text == "جستجو":
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_SEARCH)
|
|
||||||
send_message(chat_id, JOST_OJO_TEXT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
if text == "گفتگو":
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_CHAT)
|
|
||||||
send_message(chat_id, GOFT_OGO_TEXT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
# وضعیت فعلی کاربر
|
|
||||||
state = self.session.get_state(chat_id)
|
|
||||||
|
|
||||||
if state == UserState.LAW_SEARCH:
|
|
||||||
return await self.handle_search(chat_id, update, text)
|
|
||||||
|
|
||||||
elif state == UserState.LAW_CHAT:
|
|
||||||
return await self.handle_chat(chat_id, text)
|
|
||||||
|
|
||||||
# اگر هیچکدوم نبود → پیشفرض بزن بره گفتگو
|
|
||||||
else:
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_CHAT)
|
|
||||||
return await self.handle_chat(chat_id, text)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_callback(self, update: BaleUpdate):
|
|
||||||
chat_id = update.callback_query.message.chat.id
|
|
||||||
data = update.callback_query.data
|
|
||||||
|
|
||||||
if data == "law_search":
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_SEARCH)
|
|
||||||
send_message(chat_id, JOST_OJO_TEXT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
if data.startswith("chat_effort_"):
|
|
||||||
level = data.replace("chat_effort_", "")
|
|
||||||
USER_CHAT_EFFORT[chat_id] = level
|
|
||||||
|
|
||||||
send_message(chat_id, f"✅ بله حتما، لطفا کمی منتظر بمانید ...")
|
|
||||||
|
|
||||||
# اجرای مجدد آخرین پرسش
|
|
||||||
last_query = USER_LAST_CHAT_QUERY.get(chat_id)
|
|
||||||
if last_query:
|
|
||||||
return await self.handle_chat(chat_id, last_query, again=True)
|
|
||||||
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
|
|
||||||
if data == "chat_more":
|
|
||||||
pag = self.session.increase_limit(chat_id)
|
|
||||||
|
|
||||||
if not pag:
|
|
||||||
send_message(chat_id, "❌ سوالی برای ادامه نیست")
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
last_query = USER_LAST_CHAT_QUERY.get(chat_id)
|
|
||||||
if last_query:
|
|
||||||
return await self.handle_chat(chat_id, last_query)
|
|
||||||
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if data == "law_chat":
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_CHAT)
|
|
||||||
send_message(chat_id, GOFT_OGO_TEXT)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
if data == "more":
|
|
||||||
return await self.handle_more(chat_id)
|
|
||||||
|
|
||||||
if data == "stop":
|
|
||||||
self.session.clear(chat_id)
|
|
||||||
send_message(chat_id, "✅ پایان نمایش")
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
async def handle_search(self, chat_id, update, text):
|
|
||||||
|
|
||||||
self.session.set_state(chat_id, UserState.BUSY)
|
|
||||||
|
|
||||||
self.session.set_query(chat_id, text)
|
|
||||||
|
|
||||||
send_message(chat_id, "⏳ در حال جستجو...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
pag = self.session.get_pagination(chat_id)
|
|
||||||
|
|
||||||
result = await result_semantic_search(
|
|
||||||
text=pag["query"],
|
|
||||||
limit=pag["limit"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# print(f'result rag {result} {type(result["ss_answer"])}')
|
|
||||||
chunked_text_ = get_in_form(
|
|
||||||
title=pag["query"],
|
|
||||||
sections=result["ss_answer"],
|
|
||||||
)
|
|
||||||
# print(f'chunked_text_ rag {chunked_text_}')
|
|
||||||
|
|
||||||
send_message(
|
|
||||||
chat_id,
|
|
||||||
chunked_text=chunked_text_,
|
|
||||||
buttons=MORE_BUTTON
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
send_message(chat_id, "❌ خطا در جستجو")
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
self.session.set_state(chat_id, UserState.MAIN)
|
|
||||||
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
async def handle_more(self, chat_id):
|
|
||||||
|
|
||||||
pag = self.session.increase_limit(chat_id)
|
|
||||||
|
|
||||||
if not pag:
|
|
||||||
send_message(chat_id, "❌ درخواستی یافت نشد")
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
self.session.set_state(chat_id, UserState.BUSY)
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = await result_semantic_search(
|
|
||||||
text=pag["query"],
|
|
||||||
limit=pag["limit"]
|
|
||||||
)
|
|
||||||
|
|
||||||
chunked_text_ = get_in_form(
|
|
||||||
title=pag["query"],
|
|
||||||
sections=result["ss_answer"],
|
|
||||||
)
|
|
||||||
send_message(
|
|
||||||
chat_id,
|
|
||||||
chunked_text=chunked_text_,
|
|
||||||
buttons=MORE_BUTTON
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
send_message(chat_id, "❌ خطا در افزایش نتایج")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
self.session.set_state(chat_id, UserState.MAIN)
|
|
||||||
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
async def handle_chat(self, chat_id: int, text: str, again=False):
|
|
||||||
|
|
||||||
# رفتن به حالت BUSY
|
|
||||||
self.session.set_state(chat_id, UserState.BUSY)
|
|
||||||
|
|
||||||
send_message(chat_id, "⏳ در حال پردازش...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# اگر اولین پیام است، آن را به عنوان query ذخیره کن
|
|
||||||
pag = self.session.get_pagination(chat_id)
|
|
||||||
if not pag:
|
|
||||||
self.session.set_query(chat_id, text)
|
|
||||||
pag = self.session.get_pagination(chat_id)
|
|
||||||
else:
|
|
||||||
# اگر query قبلا بوده ولی کاربر چیز جدیدی تایپ کرده
|
|
||||||
if text.strip():
|
|
||||||
self.session.set_query(chat_id, text)
|
|
||||||
pag = self.session.get_pagination(chat_id)
|
|
||||||
|
|
||||||
limit = pag["limit"]
|
|
||||||
|
|
||||||
# effort را از حافظه بیرونی بگیر (یا پیش فرض)
|
|
||||||
effort = USER_CHAT_EFFORT.get(chat_id, "low")
|
|
||||||
|
|
||||||
|
|
||||||
result = await result_chat(
|
|
||||||
text=pag["query"],
|
|
||||||
limit=limit,
|
|
||||||
effort=effort,
|
|
||||||
again=again
|
|
||||||
)
|
|
||||||
# print(f'result {result}')
|
|
||||||
text_ = result['llm_answer']
|
|
||||||
query_type = result['query_type']
|
|
||||||
if isinstance(text_, str):
|
|
||||||
text_ = format_answer_bale(answer_text=text_)
|
|
||||||
|
|
||||||
# print(f'text_ {text_}')
|
|
||||||
# ذخیره آخرین سوال برای دکمهها
|
|
||||||
USER_LAST_CHAT_QUERY[chat_id] = pag["query"]
|
|
||||||
|
|
||||||
print ( '-='*10, effort, query_type)
|
|
||||||
if query_type == 'legal_question' and effort != 'medium' :
|
|
||||||
send_message(
|
|
||||||
chat_id=chat_id,
|
|
||||||
chunked_text=text_,
|
|
||||||
buttons=CHAT_EFFORT_BUTTONS
|
|
||||||
)
|
|
||||||
else :
|
|
||||||
send_message(
|
|
||||||
chat_id=chat_id,
|
|
||||||
chunked_text=text_
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print("ERROR in handle_chat:", e)
|
|
||||||
send_message(chat_id, "❌ خطا در پردازش")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# بازگشت به حالت چت
|
|
||||||
self.session.set_state(chat_id, UserState.LAW_CHAT)
|
|
||||||
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WEB_LINK = "https://majles.tavasi.ir/entity/detail/view/qsection/"
|
|
||||||
|
|
||||||
def get_in_form(title: str, sections: list, max_len: int = 4000):
|
|
||||||
chunks = []
|
|
||||||
current = f"برای پرسش: {title}\n\n"
|
|
||||||
ref_text = "«منبع»"
|
|
||||||
|
|
||||||
for i, data in enumerate(sections, start=1):
|
|
||||||
sec_text = data.get("content", "")
|
|
||||||
idx = data.get("id")
|
|
||||||
|
|
||||||
# ساخت ref کامل
|
|
||||||
ref = f"[{ref_text}]({WEB_LINK}{idx})"
|
|
||||||
# متن کامل آیتم
|
|
||||||
block = f"{i}: {sec_text}\n{ref}\n\n"
|
|
||||||
|
|
||||||
# اگر با اضافه شدن این آیتم از حد مجاز عبور میکنیم → شروع چانک جدید
|
|
||||||
if len(current) + len(block) > max_len:
|
|
||||||
chunks.append(current.rstrip())
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
current += block
|
|
||||||
|
|
||||||
# آخرین چانک را هم اضافه کن
|
|
||||||
if current.strip():
|
|
||||||
chunks.append(current.rstrip())
|
|
||||||
|
|
||||||
return chunks
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def format_answer_bale(answer_text: str, max_len: int = 4000):
|
|
||||||
"""
|
|
||||||
answer_text: متن خروجی مدل که داخلش عبارتهای مثل (منبع: qs2117427) وجود دارد
|
|
||||||
sources: مثل ['qs2117427']
|
|
||||||
"""
|
|
||||||
ref_text = "«منبع»"
|
|
||||||
|
|
||||||
def make_link(src):
|
|
||||||
return f"[{ref_text}]({WEB_LINK}{src})"
|
|
||||||
|
|
||||||
# الگو برای تشخیص هر پرانتز که شامل یک یا چند کد باشد
|
|
||||||
# مثلا: (qs123) یا (qs123, qs456, qs789)
|
|
||||||
pattern = r"\((?:منبع[:: ]+)?([a-zA-Z0-9_, ]+)\)"
|
|
||||||
|
|
||||||
def replace_source(m):
|
|
||||||
content = m.group(1)
|
|
||||||
codes = [c.strip() for c in content.split(",")] # جداسازی چند کد
|
|
||||||
links = [make_link(code) for code in codes]
|
|
||||||
full_match = m.group(0)
|
|
||||||
# if "منبع" in full_match:
|
|
||||||
# print(f'Found explicit source(s): {links}')
|
|
||||||
# else:
|
|
||||||
# print(f'Found implicit source(s): {links}')
|
|
||||||
return ", ".join(links) # جایگزینی همه کدها با لینکهایشان
|
|
||||||
|
|
||||||
# جایگزینی در متن
|
|
||||||
answer_text = re.sub(pattern, replace_source, answer_text)
|
|
||||||
|
|
||||||
# اگر طول کمتر از max_len بود → تمام
|
|
||||||
if len(answer_text) <= max_len:
|
|
||||||
return [answer_text]
|
|
||||||
|
|
||||||
# تقسیم متن اگر طول زیاد شد
|
|
||||||
chunks = []
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
sentences = answer_text.split(". ")
|
|
||||||
for sentence in sentences:
|
|
||||||
st = sentence.strip()
|
|
||||||
if not st.endswith("."):
|
|
||||||
st += "."
|
|
||||||
|
|
||||||
if len(current) + len(st) > max_len:
|
|
||||||
chunks.append(current.strip())
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
current += st + " "
|
|
||||||
|
|
||||||
if current.strip():
|
|
||||||
chunks.append(current.strip())
|
|
||||||
|
|
||||||
return chunks
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def send_message(
|
|
||||||
chat_id: int,
|
|
||||||
text: str = None,
|
|
||||||
buttons=None, # inline buttons
|
|
||||||
chunked_text=None,
|
|
||||||
):
|
|
||||||
url = f"https://tapi.bale.ai/bot{TOKEN}/sendMessage"
|
|
||||||
|
|
||||||
if chunked_text is None:
|
|
||||||
chunks = split_text_chunks(text)
|
|
||||||
else:
|
|
||||||
chunks = chunked_text
|
|
||||||
|
|
||||||
reply_keyboard = [
|
|
||||||
[{"text": btn} for btn in row] for row in BUTTON_TEXT_TO_CALLBACK_LIST
|
|
||||||
]
|
|
||||||
|
|
||||||
total = len(chunks)
|
|
||||||
|
|
||||||
for i, chunk in enumerate(chunks):
|
|
||||||
is_last = (i == total - 1)
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"chat_id": chat_id,
|
|
||||||
"text": chunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
# فقط برای پیام آخر کیبورد بفرست
|
|
||||||
if is_last:
|
|
||||||
|
|
||||||
reply_markup = {
|
|
||||||
"keyboard": reply_keyboard,
|
|
||||||
"resize_keyboard": True,
|
|
||||||
"one_time_keyboard": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
if buttons:
|
|
||||||
reply_markup["inline_keyboard"] = buttons
|
|
||||||
|
|
||||||
payload["reply_markup"] = reply_markup
|
|
||||||
|
|
||||||
r = requests.post(url, json=payload)
|
|
||||||
|
|
||||||
# try:
|
|
||||||
# r.json()
|
|
||||||
# print("Send:", r.status_code)
|
|
||||||
# except:
|
|
||||||
# print("Send:", r.status_code)
|
|
||||||
|
|
||||||
|
|
||||||
async def save_to_es(data: QaChat):
|
|
||||||
# print("save_to_es data rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr")
|
|
||||||
try:
|
|
||||||
|
|
||||||
es_res = ES_HELPER.update_index_doc(
|
|
||||||
is_update_state=False,
|
|
||||||
index_name_o=ES_INDEX_NAME,
|
|
||||||
eid=data.id,
|
|
||||||
data=data.model_dump(),
|
|
||||||
)
|
|
||||||
# type_name, payload, request
|
|
||||||
# print(f"Saved {es_res}")
|
|
||||||
except:
|
|
||||||
print("save_to_es - 000000000000000000000000000000000000000000000")
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_webhook():
|
|
||||||
"""
|
|
||||||
این تابع برای ربات اجباری است
|
|
||||||
به سرور بله مشخص میکند که به چه server ایی درخواست ها را با این توکن داده شده ارسال کند
|
|
||||||
"""
|
|
||||||
params = {"url": WEBHOOK_URL}
|
|
||||||
r = requests.get(SET_WEBHOOK_URL, params=params)
|
|
||||||
print("Webhook set status:", r.status_code)
|
|
||||||
print("Webhook set response:", r.json())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def chunked_simple_text(answer_text, max_len=4000):
|
|
||||||
chunks = []
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
sentences = answer_text.split(". ")
|
|
||||||
for sentence in sentences:
|
|
||||||
st = sentence.strip()
|
|
||||||
if not st.endswith("."):
|
|
||||||
st += "."
|
|
||||||
|
|
||||||
if len(current) + len(st) > max_len:
|
|
||||||
chunks.append(current.strip())
|
|
||||||
current = ""
|
|
||||||
|
|
||||||
current += st + " "
|
|
||||||
|
|
||||||
if current.strip():
|
|
||||||
chunks.append(current.strip())
|
|
||||||
|
|
||||||
return chunks
|
|
||||||
|
|
||||||
|
|
||||||
async def result_chat(text, limit=10, effort="low", again=False):
|
|
||||||
url = "http://2.188.15.101:8009/run_chat"
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with httpx.AsyncClient(timeout=TIME_OUT) as client:
|
|
||||||
response = await client.post(
|
|
||||||
url,
|
|
||||||
json={
|
|
||||||
"query": text,
|
|
||||||
"limit": limit,
|
|
||||||
"effort": effort,
|
|
||||||
"again": again,
|
|
||||||
"mode_type":"bale"
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
result = data.get("result", "❌ پاسخی دریافت نشد")
|
|
||||||
|
|
||||||
# print('results_chat ',type(result))
|
|
||||||
return result
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ خطای RAG:\n{str(e)}")
|
|
||||||
return "❌ ارتباط با سرور قطع میباشد"
|
|
||||||
|
|
||||||
async def result_semantic_search(text, limit):
|
|
||||||
|
|
||||||
url = "http://2.188.15.101:8009/run_semantic_search"
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with httpx.AsyncClient(timeout=TIME_OUT) as client:
|
|
||||||
response = await client.post(url, json={"query": text, "limit": limit})
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json() # ⚠️ اینجا response.json() فقط داده میدهد، شیء نیست
|
|
||||||
result = data.get("result")
|
|
||||||
# result = chunked_simple_text(result)
|
|
||||||
return result
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ خطای RAG:\n{str(e)}")
|
|
||||||
return "ارتباط با بالا قطع می باشد❌"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
session = SessionManager()
|
|
||||||
bot = BaleBot(session)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(f"/webhook/{TOKEN}")
|
|
||||||
async def webhook(request: Request):
|
|
||||||
raw = await request.json()
|
|
||||||
|
|
||||||
try:
|
|
||||||
update = BaleUpdate(**raw)
|
|
||||||
except Exception as e:
|
|
||||||
print("❌ Parse Error", e)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
print(f'update {update}')
|
|
||||||
|
|
||||||
return await bot.handle_update(update)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("\n====== Bot Status: ======")
|
|
||||||
initialize_webhook()
|
|
||||||
|
|
||||||
print("\n====== Bot Webhook Server Running ======")
|
|
||||||
uvicorn.run("main:app", host="0.0.0.0", port=8005, reload=True)
|
|
||||||
|
|
||||||
|
|
||||||
# if __name__ == "__main__":
|
|
||||||
# # 1️⃣ ابتدا وبهوک را ست میکنیم
|
|
||||||
# print("\n====== Bot Status: ======")
|
|
||||||
# initialize_webhook()
|
|
||||||
|
|
||||||
# # 2️⃣ بعد سرور FastAPI را اجرا میکنیم تا پیامها را دریافت کند
|
|
||||||
# print("\n====== Bot Webhook Server Running ======")
|
|
||||||
# uvicorn.run("main:app", host="0.0.0.0", port=8005, reload=True)
|
|
||||||
|
|
||||||
|
|
||||||
2
requirements.txt → requierments.txt
Normal file → Executable file
2
requirements.txt → requierments.txt
Normal file → Executable file
|
|
@ -1 +1,3 @@
|
||||||
elasticsearch==8.13.2
|
elasticsearch==8.13.2
|
||||||
|
nltk
|
||||||
|
pydantic
|
||||||
40
router/bale_bot.py
Executable file
40
router/bale_bot.py
Executable file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# router.bale_bot.py
|
||||||
|
from fastapi import Depends, APIRouter, Request
|
||||||
|
import requests, traceback
|
||||||
|
from utils.main import BaleBot, BaleUpdate
|
||||||
|
from dependencies import _get_bale_token, _get_bale_bot
|
||||||
|
|
||||||
|
|
||||||
|
##############
|
||||||
|
router = APIRouter(tags=["bale-bot"])
|
||||||
|
##############
|
||||||
|
|
||||||
|
|
||||||
|
# @app.post(f"/webhook/{TOKEN}")
|
||||||
|
@router.post("/webhook/{token}", description="ربات قانون یار")
|
||||||
|
async def webhook(
|
||||||
|
request: Request,
|
||||||
|
token: str = Depends(_get_bale_token),
|
||||||
|
bale_bot: BaleBot = Depends(_get_bale_bot),
|
||||||
|
):
|
||||||
|
raw = await request.json()
|
||||||
|
|
||||||
|
try:
|
||||||
|
update = BaleUpdate(**raw)
|
||||||
|
except Exception as e:
|
||||||
|
print("❌ Parse Error", e)
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
# print(f"update {update}")
|
||||||
|
return await bale_bot.render_update(update)
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_webhook(webhook_url, set_webhook_url):
|
||||||
|
"""
|
||||||
|
این تابع برای ربات اجباری است
|
||||||
|
به سرور بله مشخص میکند که به چه server ایی درخواست ها را با این توکن داده شده ارسال کند
|
||||||
|
"""
|
||||||
|
params = {"url": webhook_url}
|
||||||
|
r = requests.get(set_webhook_url, params=params)
|
||||||
|
print("Webhook set status:", r.status_code)
|
||||||
|
print("Webhook set response:", r.json())
|
||||||
0
sample_env
Normal file → Executable file
0
sample_env
Normal file → Executable file
131
utils.py
131
utils.py
|
|
@ -1,131 +0,0 @@
|
||||||
from elasticsearch import Elasticsearch, helpers
|
|
||||||
import requests, logging, asyncio, httpx, os, uuid, traceback, orjson, copy, uvicorn, time
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
# ------------------------ Global-params
|
|
||||||
|
|
||||||
def load_orjson(path: str | Path):
|
|
||||||
path = Path(path)
|
|
||||||
with path.open("rb") as f: # باید باینری باز بشه برای orjson
|
|
||||||
return orjson.loads(f.read())
|
|
||||||
|
|
||||||
|
|
||||||
def save_orjson(path, data):
|
|
||||||
with open(path, "wb") as f:
|
|
||||||
f.write(
|
|
||||||
orjson.dumps(data, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def split_text_chunks(text: str, max_len: int = 4000):
|
|
||||||
"""Split a long text into safe chunks."""
|
|
||||||
return [text[i : i + max_len] for i in range(0, len(text), max_len)]
|
|
||||||
|
|
||||||
|
|
||||||
class ElasticHelper:
|
|
||||||
"""
|
|
||||||
کلاس ElasticHelper:
|
|
||||||
نوع ورودی: بدون ورودی مستقیم در تعریف کلاس
|
|
||||||
نوع خروجی: شیء از نوع ElasticHelper
|
|
||||||
عملیات:
|
|
||||||
- متغیرهای کلاسی برای شمارش و مدیریت عملیات تعریف میکند
|
|
||||||
- مسیر پیشفرض مپینگها را تنظیم میکند
|
|
||||||
"""
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
total = 0
|
|
||||||
id = ""
|
|
||||||
path_mappings = os.getcwd() + "/repo/_other/"
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
es_url="http://127.0.0.1:6900",
|
|
||||||
es_pass="",
|
|
||||||
es_user="elastic",
|
|
||||||
path_mappings="",
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
نوع ورودی:
|
|
||||||
- es_url: آدرس Elasticsearch (str) - پیشفرض "http://127.0.0.1:6900"
|
|
||||||
- es_pass: رمز عبور (str) - پیشفرض خالی
|
|
||||||
- es_user: نام کاربری (str) - پیشفرض "elastic"
|
|
||||||
- path_mappings: مسیر مپینگها (str) - پیشفرض خالی
|
|
||||||
نوع خروجی: شیء ElasticHelper
|
|
||||||
عملیات:
|
|
||||||
- اتصال به Elasticsearch را برقرار میکند
|
|
||||||
- در صورت وجود رمز عبور، از احراز هویت استفاده میکند
|
|
||||||
- تا 10 بار برای اتصال مجدد تلاش میکند (هر بار 5 ثانیه انتظار)
|
|
||||||
- در صورت عدم موفقیت، پیام خطا نمایش داده میشود
|
|
||||||
"""
|
|
||||||
if path_mappings:
|
|
||||||
self.path_mappings = path_mappings
|
|
||||||
|
|
||||||
if es_pass == "":
|
|
||||||
self.es = Elasticsearch(es_url)
|
|
||||||
else:
|
|
||||||
self.es = Elasticsearch(
|
|
||||||
es_url,
|
|
||||||
basic_auth=(es_user, es_pass),
|
|
||||||
verify_certs=False,
|
|
||||||
)
|
|
||||||
# print(es_url)
|
|
||||||
# print(self.es)
|
|
||||||
|
|
||||||
self.success_connect = False
|
|
||||||
for a in range(0, 10):
|
|
||||||
try:
|
|
||||||
if not self.es.ping():
|
|
||||||
print("Elastic Connection Not ping, sleep 30 s : ", a)
|
|
||||||
sleep(5)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
self.success_connect = True
|
|
||||||
break
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
break
|
|
||||||
if not self.success_connect:
|
|
||||||
print("******", "not access to elastic service")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.counter = 0
|
|
||||||
self.total = 0
|
|
||||||
self.id = ""
|
|
||||||
|
|
||||||
def search(self, **params):
|
|
||||||
try:
|
|
||||||
res = self.es.search(**params)
|
|
||||||
except:
|
|
||||||
return {"hits": {"hits": []}}
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_document(self, index_name, id):
|
|
||||||
res = self.es.get(index=index_name, id=id)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def exist_document(self, index_name, id):
|
|
||||||
res = self.es.exists(index=index_name, id=id)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def update_index_doc(self, is_update_state, index_name_o, eid, data):
|
|
||||||
"""
|
|
||||||
نوع ورودی:
|
|
||||||
- is_update_state: تعیین عملیات (update یا index) (bool)
|
|
||||||
- index_name_o: نام اندیس (str)
|
|
||||||
- eid: شناسه سند (str)
|
|
||||||
- data: دادههای سند (dict)
|
|
||||||
نوع خروجی: پاسخ Elasticsearch (dict)
|
|
||||||
عملیات:
|
|
||||||
- اگر is_update_state=True باشد: سند را آپدیت میکند
|
|
||||||
- در غیر این صورت: سند جدید ایجاد میکند
|
|
||||||
"""
|
|
||||||
if is_update_state:
|
|
||||||
resp = self.es.update(index=index_name_o, id=eid, doc=data)
|
|
||||||
# resp = self.es.update(index=index_name_o, id=eid, body={'doc':data})
|
|
||||||
else:
|
|
||||||
resp = self.es.index(index=index_name_o, id=eid, document=data)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
10
utils/bale_buttons.py
Executable file
10
utils/bale_buttons.py
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
BACK_BUTTON = {"text": "⬅️ مرحله قبل", "callback_data": "workflow_back"}
|
||||||
|
HOME_BUTTON = {"text": "🏠 خانه", "callback_data": "main"}
|
||||||
|
MORE_LIMIT_BUTTON = {"text": "بارگذاری نتایج بیشتر 10+", "callback_data": "more_limit"}
|
||||||
|
MORE_EFFORT_BUTTON = {"text": "🧠 بررسی عمیق تر", "callback_data": "more_effort"}
|
||||||
|
|
||||||
|
BUTTON_TEXT_TO_CALLBACK_LIST = [
|
||||||
|
{"text": "جستجو"},
|
||||||
|
{"text": "گفتگو"},
|
||||||
|
{"text": "بازگشت به خانه"},
|
||||||
|
]
|
||||||
39
utils/bale_massages.py
Executable file
39
utils/bale_massages.py
Executable file
|
|
@ -0,0 +1,39 @@
|
||||||
|
BASE_MESSAGE = {
|
||||||
|
"not_yet": """این قسمت در دست توسعه است...""",
|
||||||
|
"contact_us": """لطفا برای ارتباط با ما از طریق مرکز فناوری مجلس شورای اسلامی ایران اقدام فرمایید""",
|
||||||
|
"about_us": """
|
||||||
|
من ربات گفتگوگر حقوقی هستم که روی قوانین رسمی جمهوری اسلامی ایران از *سامانه قانونیار مجلس شورای اسلامی* توسعه یافتم.
|
||||||
|
لذا به هر سوال و گفتگویی طبق همان منابع پاسخ میدهم
|
||||||
|
نشانی سامانه منبع در زیر آمده است
|
||||||
|
[qanonyar.parliran.ir](https://qanonyar.parliran.ir)
|
||||||
|
|
||||||
|
*کارفرما : *مرکز فناوری مجلس شورای اسلامی ایران""",
|
||||||
|
"main": """👋 سلام دوست عزیز! 🤗
|
||||||
|
به دستیار هوشمند قانون یار خوش آمدید!
|
||||||
|
فقط کافیه به من بگید چه کمکی از دستم برمیاد!""",
|
||||||
|
"search_in_law": """📚✨ هر پرسش حقوقی که از قوانین کشور دارید یا هر متن حقوقی که براتون مهم هست، همینجا بفرستید تا با استناد به قوانین با شما گفتگو کنم..
|
||||||
|
|
||||||
|
📝 میتونید:
|
||||||
|
• موضوع کلی رو بگید
|
||||||
|
• سؤال بپرسید
|
||||||
|
• یا متن حقوقی که داری برام بنویسید
|
||||||
|
|
||||||
|
🎯 هرچقدر دقیقتر و واضحتر توضیح بدید، بهتر میتوانم راهنمایی کنم.
|
||||||
|
""",
|
||||||
|
"law_chat": """💬 با احترام، آماده گفتگو هستم""",
|
||||||
|
"law_chat_logical": """💬 با عرض ادب و احترام، آماده گفتگو حقوقی دقیق هستم""",
|
||||||
|
"law_writing_policy": """برای بررسی مغایرت با سیاست های قانون گذاری متن ورودی را ارسال نمایید.""",
|
||||||
|
"constitution": """برای بررسی مغایرت با اصول مهم قانون اساسی؛ لطفا ماده قانونی را وارد نمایید.""",
|
||||||
|
"general_policy": """برای بررسی مغایرت با سیاست های کلی نظام متن ورودی را ارسال نمایید.""",
|
||||||
|
"conflic_all_qq": """برای بررسی مغایرت با تمام قوانین؛ متن ورودی را ارسال نمایید.""",
|
||||||
|
"busy": """⏳ تا اتمام پردازش قبلی منتظر بمانید ⏳""",
|
||||||
|
"qanon_title_repeat": """لطفا عنوان قانون مورد نظر را ارسال نمایید:""",
|
||||||
|
"rule_making": """متن مورد نظر را وارد کنید :""",
|
||||||
|
}
|
||||||
|
|
||||||
|
END_BOT = "✅ پایان نمایش"
|
||||||
|
USER_WAIT = "✅ بله حتما، لطفا کمی منتظر بمانید ..."
|
||||||
|
ERROR_IN_PROCESS = "❌ خطا در پردازش"
|
||||||
|
NO_MORE_QUESTION = "❌ سوالی برای ادامه نیست"
|
||||||
|
HOURGLASS = "⏳" # استیکر ساعت شنی
|
||||||
|
WAIT_TEXT = f"{HOURGLASS} لطفاً کمی صبر کنید...\nدر حال پردازش درخواست قبلی شما هستم."
|
||||||
288
utils/base_model.py
Executable file
288
utils/base_model.py
Executable file
|
|
@ -0,0 +1,288 @@
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import List, Optional, Literal, Union, Dict, Any
|
||||||
|
from utils.bale_buttons import BUTTON_TEXT_TO_CALLBACK_LIST
|
||||||
|
import json
|
||||||
|
from typing import Optional, Callable, List, Any
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class BaleStartMessageForm(BaseModel):
|
||||||
|
id: int
|
||||||
|
is_bot: bool = False
|
||||||
|
first_name: str
|
||||||
|
last_name: Optional[str] = None
|
||||||
|
username: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BaleStartMessageChat(BaseModel):
|
||||||
|
id: int
|
||||||
|
type: str
|
||||||
|
username: Optional[str] = None
|
||||||
|
first_name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BaleStartMessage(BaseModel):
|
||||||
|
message_id: int
|
||||||
|
from_user: BaleStartMessageForm = Field(..., alias="from")
|
||||||
|
date: int
|
||||||
|
chat: BaleStartMessageChat
|
||||||
|
text: str
|
||||||
|
entities: List[dict] = []
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
populate_by_name = True
|
||||||
|
|
||||||
|
|
||||||
|
class BaleCallbackFrom(BaseModel):
|
||||||
|
id: int
|
||||||
|
is_bot: bool
|
||||||
|
first_name: str
|
||||||
|
username: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BaleCallbackMessage(BaseModel):
|
||||||
|
message_id: int
|
||||||
|
chat: BaleStartMessageChat
|
||||||
|
text: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class BaleCallbackQuery(BaseModel):
|
||||||
|
id: str
|
||||||
|
from_user: BaleCallbackFrom = Field(..., alias="from")
|
||||||
|
message: BaleCallbackMessage
|
||||||
|
data: str
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
populate_by_name = True
|
||||||
|
|
||||||
|
|
||||||
|
class BaleUpdate(BaseModel):
|
||||||
|
update_id: int
|
||||||
|
message: Optional[BaleStartMessage] = None
|
||||||
|
callback_query: Optional[BaleCallbackQuery] = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
exclude_none = True
|
||||||
|
|
||||||
|
|
||||||
|
class QaChatSingle(BaseModel):
|
||||||
|
id: str
|
||||||
|
chat_id: int
|
||||||
|
user_query: str
|
||||||
|
model_key: str
|
||||||
|
model_effort: str
|
||||||
|
|
||||||
|
retrived_passage: str
|
||||||
|
retrived_ref_ids: str
|
||||||
|
retrived_duration: Optional[int] = 0
|
||||||
|
prompt_type: str = "question"
|
||||||
|
llm_duration: int
|
||||||
|
full_duration: Optional[int] = 0
|
||||||
|
time_create: Optional[int] = 0
|
||||||
|
used_ref_ids: Optional[str] = ""
|
||||||
|
status_text: Optional[str] = ""
|
||||||
|
status: Optional[int] = 0
|
||||||
|
prompt_answer: str
|
||||||
|
other_info: dict | None
|
||||||
|
|
||||||
|
|
||||||
|
class QaChatBlock(BaseModel):
|
||||||
|
id: str
|
||||||
|
title: str
|
||||||
|
user_id: str
|
||||||
|
is_premium: bool
|
||||||
|
chat: QaChatSingle
|
||||||
|
total_token: int
|
||||||
|
is_end: bool
|
||||||
|
|
||||||
|
|
||||||
|
class QaChat(BaseModel):
|
||||||
|
time_stamp: int = 0
|
||||||
|
id: str
|
||||||
|
chat_id: int
|
||||||
|
title: Optional[str] = ""
|
||||||
|
user_id: str
|
||||||
|
user_query: str
|
||||||
|
query_type: str = (
|
||||||
|
"question" # llm -> greeting, other, legal_question | rag -> question
|
||||||
|
)
|
||||||
|
full_duration: Optional[float] = 0
|
||||||
|
other_info: Optional[dict] = ""
|
||||||
|
|
||||||
|
ss_ref_ids: Optional[List[str]] = ""
|
||||||
|
ss_model_key: Optional[str] = ""
|
||||||
|
ss_duration: Optional[float] = 0
|
||||||
|
ss_answer: Optional[str] = ""
|
||||||
|
|
||||||
|
llm_ref_ids: Optional[List[str]] = []
|
||||||
|
llm_model_key: Optional[str] = ""
|
||||||
|
llm_duration: Optional[float] = 0
|
||||||
|
llm_answer: Optional[str] = ""
|
||||||
|
|
||||||
|
status_text: Optional[str] = ""
|
||||||
|
status: Optional[int] = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ConflictDetection(BaseModel):
|
||||||
|
explanation_of_conflict: str
|
||||||
|
has_confict: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class ConflictTypeDetection(BaseModel):
|
||||||
|
conflict_type: Literal["Doctrinal_conflict", "Practical_conflict"]
|
||||||
|
explanation_of_type: str
|
||||||
|
|
||||||
|
|
||||||
|
class RelationIdentification(BaseModel):
|
||||||
|
reasoning: str
|
||||||
|
relation_type: Literal[
|
||||||
|
"مطلق مقدم، مقید موخر",
|
||||||
|
"مقید مقدم، مطلق موخر",
|
||||||
|
"تکرار حکم",
|
||||||
|
"تعارض مستقر",
|
||||||
|
"بدون تعارض",
|
||||||
|
]
|
||||||
|
class Evaluation(BaseModel):
|
||||||
|
is_subject_unity_assessment_correct: bool
|
||||||
|
is_conflict_detection_correct: bool
|
||||||
|
is_conflict_type_detection_correct: bool
|
||||||
|
is_relation_type_detection_correct: bool
|
||||||
|
valid_relation_type: str
|
||||||
|
comments: str
|
||||||
|
|
||||||
|
class SingleRuleRelation(BaseModel):
|
||||||
|
rule_id: str
|
||||||
|
rule_content: str
|
||||||
|
rule_type: str
|
||||||
|
|
||||||
|
section_id: str
|
||||||
|
section_content: str
|
||||||
|
|
||||||
|
qanon_etebar: Optional[str] = None
|
||||||
|
qanon_id: Optional[str] = None
|
||||||
|
qanon_title: Optional[str] = None
|
||||||
|
|
||||||
|
state_etebar: Optional[str] = None
|
||||||
|
|
||||||
|
date: Optional[str] = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
dict_ = {
|
||||||
|
# "rule_id": self.rule_id,
|
||||||
|
"متن حکم": self.rule_content,
|
||||||
|
"نوع حکم": self.rule_type,
|
||||||
|
"متن ماده قانونی": self.section_content,
|
||||||
|
}
|
||||||
|
# Return the dictionary as a formatted JSON string
|
||||||
|
return json.dumps(dict_, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
class SubjectUnity(BaseModel):
|
||||||
|
has_subject_unity: Literal["yes", "no", "yes_under_assumptions"]
|
||||||
|
required_assumptions: Optional[str] = None
|
||||||
|
reasoning: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class RuleRelation(BaseModel):
|
||||||
|
# ----- input rule
|
||||||
|
in_rule: SingleRuleRelation
|
||||||
|
|
||||||
|
# ----- semantic-search close rule
|
||||||
|
db_rule: SingleRuleRelation
|
||||||
|
|
||||||
|
# ----- subject-unity data
|
||||||
|
subject_unity: SubjectUnity | None = None
|
||||||
|
|
||||||
|
# ----- conflict-detection data
|
||||||
|
conflict_detection: ConflictDetection | None = None
|
||||||
|
|
||||||
|
# ----- conflict-type-detection data
|
||||||
|
conflict_type_detection: ConflictTypeDetection | None = None
|
||||||
|
|
||||||
|
# ----- relation-identification data
|
||||||
|
relation_identification: RelationIdentification | None = None
|
||||||
|
|
||||||
|
class StateDetail(BaseModel):
|
||||||
|
state: str
|
||||||
|
message: str
|
||||||
|
button_text: str
|
||||||
|
end_buttons: List = []
|
||||||
|
inline_buttons: Optional[List[List[dict]]] = None
|
||||||
|
|
||||||
|
handler : str = None
|
||||||
|
allow_empty_input: bool = True
|
||||||
|
|
||||||
|
class BaleUser(BaseModel):
|
||||||
|
uc_id: str
|
||||||
|
chat_id: int
|
||||||
|
user_id: str
|
||||||
|
update: BaleUpdate
|
||||||
|
username: str
|
||||||
|
is_bot: bool = False
|
||||||
|
first_name: str = ""
|
||||||
|
last_name: str = ""
|
||||||
|
|
||||||
|
rule_relation: RuleRelation | None = None
|
||||||
|
subject_unities:Dict = {}
|
||||||
|
|
||||||
|
# ---- defaults
|
||||||
|
effort: Literal["medium", "low"] = "low"
|
||||||
|
limit: int = 10
|
||||||
|
permission: str = "normal"
|
||||||
|
|
||||||
|
# ---- runtime
|
||||||
|
is_processing_lock : bool = False
|
||||||
|
is_call_back_query : bool = False
|
||||||
|
state_detail : StateDetail = None
|
||||||
|
last_message_id : int = 0
|
||||||
|
|
||||||
|
input_query: str = "" # ورودی کاربر
|
||||||
|
call_back_query: str = "" # ورودی کاربر
|
||||||
|
_query_type: str = "" # ورودی کاربر
|
||||||
|
sub_state: str = "" # برای روندی ها
|
||||||
|
|
||||||
|
all_qq: bool = False
|
||||||
|
|
||||||
|
# ---- memory
|
||||||
|
last_query: Dict[str, str] = {} # آخرین سوال کاربر
|
||||||
|
last_result: Dict[str, Any] = {} # آخرین نتایج
|
||||||
|
last_runtime: Dict[str, Any] = (
|
||||||
|
{}
|
||||||
|
) # مثلا {"GP": {"effort": "medium", "limit": 20}} برای بقیه چیزها
|
||||||
|
stack: Dict[str, List] = {} # پشته برای داده های مرحله ای
|
||||||
|
|
||||||
|
|
||||||
|
class KeyboardItem(BaseModel):
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
|
class InlineKeyboardItem(BaseModel):
|
||||||
|
text: str
|
||||||
|
callback_data: str
|
||||||
|
|
||||||
|
|
||||||
|
class ReplyMarkup(BaseModel):
|
||||||
|
keyboard: List[List[Union[KeyboardItem, Dict]]] = Field(
|
||||||
|
default_factory=lambda: BUTTON_TEXT_TO_CALLBACK_LIST
|
||||||
|
)
|
||||||
|
resize_keyboard: bool = True
|
||||||
|
one_time_keyboard: bool = False
|
||||||
|
inline_keyboard: List[List[Union[InlineKeyboardItem, Dict]]] | None = (
|
||||||
|
None # دکمه ها ی داخال صفحه
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BalePayload(BaseModel):
|
||||||
|
reply_markup: ReplyMarkup = ReplyMarkup()
|
||||||
|
chat_id: int
|
||||||
|
message_id: int = None
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
|
class Step(BaseModel):
|
||||||
|
name: str
|
||||||
|
level : int
|
||||||
|
data_input : str
|
||||||
|
data_output : str
|
||||||
|
bot_output : List
|
||||||
|
|
||||||
146
utils/config.py
Normal file
146
utils/config.py
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
from utils.base_model import StateDetail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BUSY_TEXT = ("""⏳ تا اتمام پردازش قبلی منتظر بمانید ⏳""",)
|
||||||
|
|
||||||
|
|
||||||
|
class StateRegistry:
|
||||||
|
def __init__(self, states):
|
||||||
|
self._states = {s.state: s for s in states}
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
return self._states.get(key, default)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
return list(self._states.values())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
STATE = [
|
||||||
|
StateDetail(
|
||||||
|
state="search_in_law",
|
||||||
|
button_text="جستجو معنایی در قوانین 🔎",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن حقوقی برای جستجو در قوانین را وارد نمایید""",
|
||||||
|
handler="handle_search_in_law",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="chat_in_law",
|
||||||
|
button_text="گفتگو طبق قوانین کشور",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""💬 با احترام، آماده گفتگو هستم""",
|
||||||
|
handler="handle_chat_in_law",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="logical_chat_in_law",
|
||||||
|
button_text="گفتگوی حقوقی دقیق تر 🧠💬",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""💬 با عرض ادب و احترام، آماده گفتگو حقوقی دقیق هستم""",
|
||||||
|
handler="handle_logical_chat_in_law",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="conflict_law_writing_policy",
|
||||||
|
button_text="بررسی مغایرت با سیاست های قانون گذاری 📜",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن مورد نظر برای بررسی مغایرت با سیاست های قانون گذاری را وارد کنید :""",
|
||||||
|
handler="handle_conflict_law_writing_policy",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="conflict_qanon_asasi",
|
||||||
|
button_text="بررسی مغایرت با اصول مهم قانون اساسی ⚖️",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن مورد نظر برای بررسی مغایرت با اصول مهم قانون اساسی را وارد کنید :""",
|
||||||
|
handler="handle_conflict_qanon_asasi",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="conflict_general_policy",
|
||||||
|
button_text="بررسی مغایرت با سیاست های کلی نظام 🏛️",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن مورد نظر برای بررسی مغایرت با سیاست های کلی نظام را وارد کنید :""",
|
||||||
|
handler="handle_conflict_general_policy",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="conflict_all_qavanin",
|
||||||
|
button_text="بررسی مغایرت در تمام قوانین",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن مورد نظر برای بررسی مغایرت در تمام قوانین جمهوری اسلامی ایران را وارد کنید :""",
|
||||||
|
handler="handle_conflict_all_qavanin",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="qanon_title_repeat",
|
||||||
|
button_text="بررسی تکرار عنوان قانون 🔁",
|
||||||
|
end_buttons=[],
|
||||||
|
handler="handle_qanon_title_repeat",
|
||||||
|
message="""لطفا عنوان قانون مورد نظر را برای بررسی ارسال نمایید:""",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="rule_making",
|
||||||
|
handler="handle_rule_making",
|
||||||
|
button_text="استخراج اجزاء حقوقی متن",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""متن مورد نظر برای استخراج اجزاء حقوقی را وارد کنید :""",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="beta",
|
||||||
|
handler="handle_beta",
|
||||||
|
button_text="BETA-Mode",
|
||||||
|
end_buttons=[],
|
||||||
|
message="""این قسمت در دست توسعه قرار دارد ...""",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="contact_us",
|
||||||
|
button_text="تماس با ما ☎️",
|
||||||
|
message="""لطفا برای ارتباط با ما از طریق مرکز فناوری مجلس شورای اسلامی ایران اقدام فرمایید""",
|
||||||
|
),
|
||||||
|
StateDetail(
|
||||||
|
state="about_us",
|
||||||
|
button_text="درباره ما ⚡",
|
||||||
|
message="""من ربات گفتگوگر حقوقی هستم که روی قوانین رسمی جمهوری اسلامی ایران از *سامانه قانونیار مجلس شورای اسلامی* توسعه یافتم.
|
||||||
|
لذا به هر سوال و گفتگویی طبق همان منابع پاسخ میدهم
|
||||||
|
نشانی سامانه منبع در زیر آمده است
|
||||||
|
[qanonyar.parliran.ir](https://qanonyar.parliran.ir)
|
||||||
|
|
||||||
|
*کارفرما : *مرکز فناوری مجلس شورای اسلامی ایران""",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
STATE_REGISTERY = StateRegistry(STATE)
|
||||||
|
STATE_CONFIG = {i.state: i for i in STATE}
|
||||||
|
|
||||||
|
|
||||||
|
def build_buttons_form(button_form):
|
||||||
|
buttons = []
|
||||||
|
|
||||||
|
for row in button_form:
|
||||||
|
row_buttons = []
|
||||||
|
for state_name in row:
|
||||||
|
state = STATE_REGISTERY.get(state_name)
|
||||||
|
if not state:
|
||||||
|
continue
|
||||||
|
|
||||||
|
row_buttons.append({
|
||||||
|
"text": state.button_text,
|
||||||
|
"callback_data": state.state
|
||||||
|
})
|
||||||
|
|
||||||
|
if row_buttons:
|
||||||
|
buttons.append(row_buttons)
|
||||||
|
|
||||||
|
return buttons
|
||||||
|
|
||||||
|
# Button-STYLE
|
||||||
|
main_button_form = [
|
||||||
|
["search_in_law"],
|
||||||
|
["chat_in_law"],
|
||||||
|
["logical_chat_in_law"],
|
||||||
|
["conflict_law_writing_policy"],
|
||||||
|
["conflict_qanon_asasi"],
|
||||||
|
["conflict_general_policy"],
|
||||||
|
["conflict_all_qavanin"],
|
||||||
|
["qanon_title_repeat"],
|
||||||
|
["rule_making"],
|
||||||
|
["contact_us", "about_us", "beta"]
|
||||||
|
]
|
||||||
|
MAIN_BUTTON = build_buttons_form(main_button_form)
|
||||||
1011
utils/main.py
Executable file
1011
utils/main.py
Executable file
File diff suppressed because it is too large
Load Diff
26
utils/static.py
Executable file
26
utils/static.py
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
################# modularity
|
||||||
|
### import from external-package
|
||||||
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
|
import requests, logging, asyncio, httpx, os, uuid, traceback, orjson, copy, uvicorn, time, re
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from pathlib import Path
|
||||||
|
from time import sleep
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Dict
|
||||||
|
from utils.base_model import RuleRelation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# پارامتر های کلی
|
||||||
|
#################################################
|
||||||
|
EFFORT = "low"
|
||||||
|
MAX_LIMIT_RAG = 100 # بیشینه تعدادی که rag برمی گرداند
|
||||||
|
STEP_RAG = 10 # مقداری که هر مرحله rag اضافه میکند با فشردن نمایش بیشتر more
|
||||||
|
TIME_OUT = 600 # هر درخواست با این مقدار time-out ارسال می شود
|
||||||
|
MAX_LEN = 4000 # کمی کمتر از حد پایه امنتر است برای بیشینه پیام در بله
|
||||||
|
QS_WEB_LINK = "https://majles.tavasi.ir/entity/detail/view/qsection/" # آدرس صفحه qs ها
|
||||||
|
QQ_WEB_LINK = (
|
||||||
|
"https://majles.tavasi.ir/entity/navigation/view/qasection/" # آدرس صفحه qq ها
|
||||||
|
)
|
||||||
|
REF_TEXT = "«منبع»" # برای نمایش منبع
|
||||||
1437
utils/workflow.py
Executable file
1437
utils/workflow.py
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user