import os from typing import TypedDict, List, Dict, Any, Optional from langgraph.graph import StateGraph, START, END from agent.agent_graph.StateTasks import * from agent.llm.prompts import * from typing import get_type_hints import json from langchain_core.messages import HumanMessage,SystemMessage from agent.agent_graph.Graph_Utils import get_egp_to_usd from agent.rag.rag import * def answer_question(state:ProblemState): question = state["question"] if state["question_type"] == Available_Tasks.LAPTOP_CHOOSE.value: guide_prompt = Tasks_prompts.LAPTOP_THINK.value elif state["question_type"] == Available_Tasks.ROADMAP.value: guide_prompt = Tasks_prompts.ROADMAP.value elif ("node_output_article" in state.keys()): guide_prompt = Tasks_prompts.ROADMAP.value +"" + state["node_output_article"] +"" else: guide_prompt = Tasks_prompts.RAG.value state["answer"] = get_llm_answer(model_llm=state["llm"],messages=state["memory"] + [HumanMessage(content=(guide_prompt + "طلب المستخدم:\n" + question + "\nاهم معلومات المستخدم لاستخدامها ف الدلالة (بالنسبة للسعر هو نفس السعر لكن بالدولار فدائما ركز على السعر بالدولار\n)"+ str(state) + Route_prompts.FINALIZER_PROMPT.value))]) return state def update_context(state:ProblemState): # Control what keys can be modified to preven hullicination keys = get_type_hints(ProblemState).keys() keys_modifiable = [] # keep it inside the function to make sure append to it not affected by other calls _is_rag=False _rag_text_if_exist = "" for i in list(keys): # 1️⃣ استبعاد المفاتيح غير القابلة للتعديل نهائيًا if i in ["question", "answer", "node_output_article", "memory"]: continue # 2️⃣ منع تعديل question_type بعد بداية الشات # - question_type يُسمح به فقط في أول الشات # - لو موجود بالفعل في state → يبقى الشات بدأ → ممنوع التعديل # if exist question in data rag skip it if i == "question_type": if "question_type" in state.keys(): continue rag_text = state["rag_model"].get_relevant_question(state["question"]) if bool(rag_text): _is_rag = True _rag_text_if_exist = rag_text # store text to prevent calling twice continue # 3️⃣ أي key وصل هنا يبقى مسموح للـ LLM يرجّعه keys_modifiable.append(i) # Make the prompt prompt_llm_new_info = Route_prompts.Context_UPDATOR.value + "\n \n" +str(keys_modifiable) +"\n "+state["question"]+"" llm_new_info = get_llm_answer(model_llm=state["llm"],messages = [HumanMessage(prompt_llm_new_info)]) # Save and Process the returned json to prevent hallucination try: llm_new_info = json.loads(llm_new_info) for key in llm_new_info.keys(): if key in keys_modifiable: state[key] = llm_new_info[key] # check if rag if _is_rag: state["question_type"] = Available_Tasks.QUESTION.value state["node_output_article"] = _rag_text_if_exist # Check if all_ok can be answered now last_question = state.get("answer", "") check_finalized_prompt = ( "عندك ده اخر سوال واجابة"+ last_question + state["question"] + "هل معنى ذلك ان المستخدم اكد على الفهم الصحيح؟" + "رجع فقط BOOL (True/False)" ) check_finalized = get_llm_answer(model_llm=state["llm"], messages = [HumanMessage( check_finalized_prompt )]) state['all_ok'] = check_finalized.strip().lower() == "true" # If wrong parsed it's false except Exception as e: print("Context was not updated due to error : ",e) return state def convertPriceToDollar(state:ProblemState): if "price" in state.keys(): state["price"] = get_egp_to_usd(state["price"]) return state def step(state:ProblemState): next_topic = None if "question_type" not in state.keys(): next_topic = "question_type" else: for i in task_steps[state.get("question_type")]: if i not in state.keys(): next_topic = i break # Only after finishing the to do list of the question type we can ask for all_ok to confirm if (not next_topic) and ("all_ok" not in state.keys() or (state["all_ok"]==False)) and "question_type" in state.keys(): next_topic = "all_ok" step_prompt = (System_prompts.STATE_DESCRIBE.value + f"{next_topic} {state}" + Route_prompts.FINALIZER_PROMPT_STEP.value) state['answer'] = get_llm_answer(model_llm=state["llm"],messages = [HumanMessage(step_prompt)]) return state def search_knowledgebase(state:ProblemState): """ Search the vector database for relevant contexts. """ # fetch top 3 relevant docs state["node_output_article"] = state["rag_model"].get_relevant_question(state["question"]) return state def get_llm_answer(model_llm=None,messages=[HumanMessage(content="hi")]): return model_llm.invoke(messages).content