|
import gradio as gr |
|
import re |
|
import inspect |
|
|
|
from sentence_transformers import SentenceTransformer |
|
from sentence_transformers.util import cos_sim |
|
|
|
|
|
codes = """001 - Vehicle Registration (New) |
|
002 - Vehicle Registration Renewal |
|
003 - Vehicle Ownership Transfer |
|
004 - Vehicle De-registration |
|
005 - Lost Registration Certificate Replacement |
|
006 - Address Change Update |
|
007 - Vehicle Data Correction |
|
008 - Ownership Name Correction |
|
009 - Vehicle Tax Payment |
|
010 - Late Payment Fee Processing |
|
011 - Vehicle Type/Specification Update |
|
012 - BBNKB (Transfer Fee of Vehicle Ownership) |
|
013 - STNK Issuance (Vehicle Registration Certificate) |
|
014 - STNK Renewal |
|
015 - Motor Vehicle Roadworthiness Inspection |
|
016 - Plate Number Renewal |
|
017 - Lost Plate Replacement |
|
018 - Vehicle Export Registration |
|
019 - Vehicle Import Registration |
|
020 - Fleet Vehicle Registration |
|
021 - Bulk Vehicle Registration Update |
|
022 - Vehicle Insurance Assistance |
|
023 - Vehicle Accident Reporting |
|
024 - Vehicle Usage Change Declaration (e.g., personal to commercial) |
|
025 - Legal Document Verification |
|
026 - Ownership Transfer for Inherited Vehicle |
|
027 - STNK Temporary Suspension |
|
028 - Proof of Ownership Document Update |
|
029 - Vehicle Ownership History Check |
|
030 - Vehicle Tax Recalculation Request |
|
031 - Tax Exemption Application (for special cases) |
|
032 - Deceased Owner’s Vehicle Ownership Transfer""".split("\n") |
|
|
|
undetected = "099 - Other/Undetected" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
codes = """001 - Pendaftaran Kendaraan |
|
002 - Pembaruan Data Kendaraan |
|
003 - Alih Kepemilikan (Balik Nama) |
|
004 - Pelaporan Dokumen atau Plat yang Hilang |
|
005 - Pembayaran dan Pengelolaan Pajak Kendaraan |
|
006 - Pemeriksaan dan Verifikasi Kendaraan |
|
007 - Pendaftaran Kendaraan Ekspor, Impor, atau Armada |
|
008 - Pelaporan dan Bantuan Terkait Kendaraan |
|
009 - Penangguhan atau Deklarasi Perubahan Penggunaan Kendaraan""".split("\n") |
|
|
|
|
|
vehicle_tax_info = { |
|
"B 1234 BCA": { |
|
"no_rangka": "1237191234", |
|
"type": "SUV", |
|
"tanggal": "23 Desember 2024", |
|
"status": "Belum Bayar", |
|
"harga_jual": 500_000_000 |
|
}, |
|
"B 5678 XYZ": { |
|
"no_rangka": "9876543210", |
|
"type": "Sedan", |
|
"tanggal": "15 Januari 2025", |
|
"status": "Belum Bayar", |
|
"harga_jual": 375_800_000 |
|
}, |
|
"D 3456 DEF": { |
|
"no_rangka": "4561237890", |
|
"type": "MPV", |
|
"tanggal": "10 Februari 2025", |
|
"status": "Sudah Bayar", |
|
"harga_jual": 400_000_000 |
|
} |
|
} |
|
|
|
|
|
detail_perhitungan = { |
|
"001": { |
|
"name": "Pendaftaran Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.1, |
|
|
|
}, |
|
"002": { |
|
"name": "Pembaruan Data Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.05, |
|
|
|
}, |
|
"003": { |
|
"name": "Alih Kepemilikan Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.1, |
|
|
|
}, |
|
"004": { |
|
"name": "Penggantian Dokumen atau Plat yang Hilang", |
|
"formula": lambda harga_jual: harga_jual * 0.03, |
|
|
|
}, |
|
"005": { |
|
"name": "Pembayaran dan Pengelolaan Pajak Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.12, |
|
|
|
}, |
|
"006": { |
|
"name": "Pemeriksaan dan Verifikasi Kendaraan", |
|
"formula": lambda harga_jual: 100000, |
|
|
|
}, |
|
"007": { |
|
"name": "Pendaftaran Kendaraan Ekspor, Impor, atau Armada", |
|
"formula": lambda harga_jual: harga_jual * 0.15, |
|
|
|
}, |
|
"008": { |
|
"name": "Pelaporan dan Bantuan Terkait Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.04, |
|
|
|
}, |
|
"009": { |
|
"name": "Penangguhan atau Deklarasi Perubahan Penggunaan Kendaraan", |
|
"formula": lambda harga_jual: harga_jual * 0.06, |
|
|
|
} |
|
} |
|
|
|
undetected = "099 - Lainnya/Tidak Terdeteksi" |
|
|
|
model_ids = [ |
|
"BAAI/bge-m3", |
|
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2", |
|
"intfloat/multilingual-e5-small", |
|
"sentence-transformers/distiluse-base-multilingual-cased-v2", |
|
"Alibaba-NLP/gte-multilingual-base", |
|
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", |
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
model_id = model_ids[-1] |
|
model = SentenceTransformer(model_id, trust_remote_code=True) |
|
|
|
codes_emb = model.encode([x[6:] for x in codes]) |
|
|
|
|
|
def censor_middle(number, num_to_hide=4): |
|
number_str = str(number) |
|
middle_index = len(number_str) // 2 |
|
start_index = middle_index - num_to_hide // 2 |
|
end_index = middle_index + num_to_hide // 2 |
|
|
|
censored_part = "*" * num_to_hide |
|
censored_number = number_str[:start_index] + censored_part + number_str[end_index:] |
|
|
|
return censored_number |
|
|
|
|
|
def get_calculation(request_code, plate_number): |
|
print(request_code, plate_number, "GET CALC") |
|
calc = detail_perhitungan.get(request_code) |
|
vehicle = vehicle_tax_info.get(plate_number) |
|
|
|
if vehicle != None and calc != None: |
|
harga_jual = vehicle.get("harga_jual") |
|
formula = calc.get("formula") |
|
result = formula(harga_jual) |
|
description = inspect.getsource(formula).split(":", 2)[-1].strip() |
|
result_detail = request_code + " - " + calc.get("name") |
|
return result, str(description), result_detail |
|
|
|
elif calc != None: |
|
formula = calc.get("formula") |
|
description = inspect.getsource(formula).split(":", 2)[-1].strip() |
|
result_detail = request_code + " - " + calc.get("name") |
|
return None, str(description), result_detail |
|
|
|
else: |
|
return None, None, None |
|
|
|
|
|
def build_output_formula(descriptions, result_details): |
|
out = "----------------------------------------------------\n\n" |
|
out = "Daftar Kode Permohonan:\n" |
|
for i, (desc,detail) in enumerate(zip(descriptions, result_details)): |
|
|
|
out += f"{i+1}. {detail}\nRumus: {desc}\n" |
|
return out |
|
|
|
|
|
def build_output_vehicle(plate_number): |
|
vehicle = vehicle_tax_info.get(plate_number) |
|
out = "----------------------------------------------------\n\n" |
|
out = "Nomor Polisi: " + plate_number + "\n" |
|
vehicle["no_rangka"] = censor_middle(vehicle["no_rangka"]) |
|
vehicle["harga_jual"] = "{:,}".format(vehicle["harga_jual"]) |
|
out += "\n".join([k + " : " + str(v) for k,v in vehicle.items()]) |
|
return out |
|
|
|
def build_output(result, description, result_detail, plate_number): |
|
return build_outputs([result], [description], [result_detail], plate_number) |
|
|
|
|
|
def build_outputs(results, descriptions, result_details, plate_number): |
|
vehicle = vehicle_tax_info.get(plate_number) |
|
vehicle["harga_jual"] = "{:,}".format(vehicle["harga_jual"]) |
|
|
|
out = "----------------------------------------------------\n\n" |
|
out = "Nomor Polisi: " + plate_number + "\n" |
|
out += "\n".join([k + " : " + str(v) if k != "no_rangka" else k + " : " + censor_middle(v) for k,v in vehicle.items()]) |
|
|
|
|
|
|
|
out += "\n\nDaftar Kode Permohonan:\n" |
|
|
|
for i, (res,desc,detail) in enumerate(zip(results, descriptions, result_details)): |
|
harga_jual = vehicle["harga_jual"] |
|
out += f"{i+1}. {detail}\nRumus: {desc}\nDetail perhitungan: {desc.replace('harga_jual', harga_jual)} = {res}\n" |
|
|
|
|
|
out += "\n\n\nEstimasi Biaya: " |
|
|
|
if len(results) > 1: |
|
out += " + ".join([f"Rp{x}" for x in results]) |
|
out += f" = {sum(results)}" |
|
else: |
|
out += str(results[0]) |
|
|
|
out += "\n\n----------------------------" |
|
|
|
return out |
|
|
|
|
|
def respond( |
|
message, |
|
history: list[tuple[str, str]], |
|
threshold, |
|
is_multiple |
|
): |
|
global codes_emb |
|
global undetected |
|
|
|
undetected_code = undetected[:3] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pattern = r'\b([A-Za-z]{1,2})\s?(\d{4})\s?([A-Za-z]{1,3})\b' |
|
|
|
matches = re.findall(pattern, message) |
|
|
|
plates = [" ".join(x).upper() for i,x in enumerate(matches)] |
|
|
|
plate_numbers = ", ".join(plates) |
|
|
|
text_emb = model.encode(message) |
|
scores = cos_sim(codes_emb, text_emb)[:,0] |
|
|
|
if is_multiple: |
|
request_details = [] |
|
request_numbers = [] |
|
request_scores = [] |
|
|
|
for i,score in enumerate(scores): |
|
if score > threshold: |
|
request_details.append(codes[i][6:]) |
|
request_numbers.append(codes[i][:3]) |
|
request_scores.append(str( round(score.tolist(), 3) ) ) |
|
|
|
if not request_details: |
|
request_details.append(undetected[6:]) |
|
request_numbers.append(undetected_code) |
|
|
|
|
|
request_numbers_copy = request_numbers |
|
request_details_copy = request_details |
|
|
|
request_numbers = "\n".join(request_numbers) |
|
request_details = "\n".join(request_details) |
|
request_scores = "\n".join(request_scores) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out = "" |
|
for plate in plates: |
|
results, descriptions, result_details = [], [], [] |
|
for code in request_numbers_copy: |
|
result, description, result_detail = get_calculation(code, plate) |
|
|
|
if result != None: |
|
results.append(result) |
|
if descriptions != None: |
|
descriptions.append(description) |
|
result_details.append(result_detail) |
|
|
|
if results: |
|
out += "\n\n" + build_outputs(results, descriptions, result_details, plate) |
|
elif vehicle_tax_info.get(plate): |
|
out += "\n\n" + build_output_vehicle(plate) |
|
|
|
if out == "": |
|
descriptions, result_details = [], [] |
|
for code in request_numbers_copy: |
|
result, description, result_detail = get_calculation(code, "") |
|
if description != None: |
|
descriptions.append(description) |
|
result_details.append(result_detail) |
|
if descriptions: |
|
out += "\n\n" + build_output_formula(descriptions, result_details) |
|
else: |
|
return "Request code number: " + request_numbers + "\nRequest detail: " + request_details + "\nPlate numbers: " + plate_numbers |
|
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
s_max = scores.argmax() |
|
|
|
if scores[s_max] < threshold: |
|
|
|
request_code = undetected |
|
else: |
|
request_code = codes[scores.argmax()] |
|
|
|
|
|
|
|
|
|
out = "" |
|
for plate in plates: |
|
results, descriptions, result_details = [], [], [] |
|
result, description, result_detail = get_calculation(request_code[:3], plate) |
|
|
|
if result != None: |
|
results.append(result) |
|
out += "\n\n" + build_outputs(results, descriptions, result_details, plate) |
|
elif vehicle_tax_info.get(plate): |
|
out += "\n\n" + build_output_vehicle(plate) |
|
|
|
|
|
|
|
|
|
if out == "": |
|
if request_code[:3] == undetected_code: |
|
return "Request code number: " + request_code[:3] + "\nRequest detail: " + request_code[6:] + "\nPlate numbers: " + plate_numbers |
|
else: |
|
result, description, result_detail = get_calculation(request_code[:3], "") |
|
out = build_output_formula([description], [result_detail]) |
|
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def reload(chosen_model_id): |
|
global model |
|
global model_id |
|
global codes_emb |
|
|
|
if chosen_model_id != model_id: |
|
model = SentenceTransformer(chosen_model_id, trust_remote_code=True) |
|
model_id = chosen_model_id |
|
codes_emb = model.encode([x[6:] for x in codes]) |
|
return f"Model {chosen_model_id} has been succesfully loaded!" |
|
return f"Model {chosen_model_id} is ready!" |
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
|
gr.Markdown("# List of Request Numbers") |
|
gr.Markdown("<br>".join(codes) + "<br>" + undetected) |
|
gr.Markdown("# Valid License Plate Number Criteria:") |
|
gr.Markdown("(1-2 letters) (4 numbers) (1-3 letters)") |
|
gr.Markdown("# Example Valid Plate Numbers") |
|
gr.Markdown("<br>".join(vehicle_tax_info.keys())) |
|
|
|
|
|
gr.Markdown("# Choose & Load Model:") |
|
reload_model = gr.Interface( |
|
fn=reload, |
|
inputs=[gr.Dropdown(choices=model_ids, value=model_id)], |
|
outputs="text", |
|
|
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
|
gr.Markdown("# Chatbot Interface:") |
|
chat_interface = gr.ChatInterface( |
|
respond, |
|
additional_inputs=[ |
|
gr.Number(0.5, label="confidence threshold", show_label=True, minimum=0., maximum=1.0, step=0.1), |
|
gr.Checkbox(label="multiple", info="Allow multiple request code numbers"), |
|
|
|
], |
|
|
|
chatbot=gr.Chatbot(height=800), |
|
|
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |
|
|