Spaces:
Sleeping
Sleeping
File size: 10,823 Bytes
7c7c363 c6e4140 42a5cc8 c6e4140 f05c774 a44cb1b f05c774 7c7c363 89dfc8f 7c7c363 c6e4140 89dfc8f 7c7c363 89dfc8f 7c7c363 89dfc8f 7c7c363 a6afbe2 7c7c363 c6e4140 7c7c363 89dfc8f 7c7c363 ab6c841 7c7c363 89dfc8f c6e4140 89dfc8f 7c7c363 c6e4140 7c7c363 9adc62a 7c7c363 9adc62a 7c7c363 89dfc8f 7c7c363 c6e4140 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
########## Imports ############
from langchain_google_genai import ChatGoogleGenerativeAI
import os
from typing import TypedDict, List, Dict, Any, Optional
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
import string
from langchain_experimental.tools import PythonREPLTool
import ast, json
from langchain_community.tools import DuckDuckGoSearchRun
import os
from langchain_huggingface import HuggingFaceEndpoint , ChatHuggingFace
from langchain import LLMChain, PromptTemplate
def get_gpt_and_answer(question):
# إنشاء LLM باستخدام endpoint من Hugging Face
llm = HuggingFaceEndpoint(
repo_id="openai/gpt-oss-20b", # أو أي نموذج موجود في HF
task="text-generation",
huggingfacehub_api_token=os.environ["HUGGINGFACEHUB_API_TOKEN"],
provider="auto" # يسمح باختيار المزود تلقائياً
)
# إنشاء سلسلة (chain) بسيطة
template = """
Question: {question}
Answer:
"""
llm = ChatHuggingFace(llm=llm)
prompt = PromptTemplate.from_template(template)
chain = LLMChain(llm=llm, prompt=prompt)
# تجربة
# result = chain.invoke({"question": question})
model = ChatGoogleGenerativeAI(model="gemini-2.5-flash")
result = (model.invoke([HumanMessage(content=template)]).content)
return result #(result['text'])
########## State ############
class InfoState(TypedDict):
question: str
answer_type: Optional[str] # WebInfo - WIKI - MATH
answer_code : Optional[str]
main_parts: Optional[List[str]]
tool_answer : Optional[list[str]]
final_answer : Optional[str]
######### Nodes ############
def get_wiki_relate(state: InfoState) -> InfoState:
"""
Tool to Get the wikipedia info from keywords extracted from preprocessing at main_parts.
Uses: Wikipedia API
Returns: tool_answer (summary)
"""
print("Using Wikipedia...")
# Create the Wikipedia utility
wiki = WikipediaAPIWrapper(
lang="en", # Wikipedia language
top_k_results=1, # how many results to fetch
doc_content_chars_max=2000
)
# Make a tool from it
wiki_tool = WikipediaQueryRun(api_wrapper=wiki)
try:
wiki_answer = wiki_tool.run(" ".join(state["main_parts"]) + " full wikipedia article about this topic")
state['tool_answer'] = wiki_answer
return state
except Exception as e:
print("Rate limit Exception")
state['tool_answer'] = ""
return state
def execute_code(state: InfoState) -> InfoState :
"""Tool to calculate any math using python code or get current date time."""
print("Execut Code...")
python_tool = PythonREPLTool()
code = state["answer_code"]
state["tool_answer"]=python_tool.run(code)
return state
def get_code(state:InfoState) -> InfoState:
"""From prompt get the code to run."""
print("Getting Code (Gemini)...")
prompt = (
f"You are a strict code generator. "
f"Given the question: '{state['question']}', "
f"return ONLY valid Python code that computes the answer IF the question is about math, date, or time. "
f"Otherwise, return exactly: print('not valid')\n\n"
f"Rules:\n"
f"- Output ONLY the code or print('not valid')\n"
f"- No explanations, no markdown, no extra text\n"
f"- No quotes around the code\n"
f"- Use print() to show the result\n"
f"- Import modules only if needed (e.g. datetime, math)"
)
state["answer_code"] = get_gpt_and_answer(prompt).strip()
return state
def preprocess_text(state: dict) -> InfoState:
"""
Preprocess text to get the keywords to help get results directly from wikipedia.
Input: raw question
Output: main_parts (list of keywords)
"""
print("Preprocess text (Gemini)...")
# 1️⃣ Prepare the prompt
prompt = (
"We want to find the best-matching English Wikipedia pages for a factual question, "
"so we must extract only the essential topic names or entities that Wikipedia likely has pages for. "
"These should include the main subject (e.g., a person, event, place, or concept) and any directly relevant subtopic "
"(like 'Discography', 'Career', or 'History') if they help narrow the search.\n\n"
"Rules:\n"
"- Output 1 to 3 items maximum.\n"
"- Use English Wikipedia title format (capitalize each main word).\n"
"- Translate non-English names or terms to English.\n"
"- Exclude question words, pronouns, and filler terms.\n"
"- Fix spelling errors if necessary.\n"
"- Prefer specific Wikipedia topics over vague ones.\n\n"
"Do NOT include markdown formatting or code blocks like ```json```. Output plain JSON only.\n"
"Example:\n"
"Q: 'Who built the Eiffel Tower?'\n"
"A: [\"Eiffel Tower\", \"Gustave Eiffel\"]\n\n"
f"Question: '{state['question']}'\n\n"
"Output ONLY a valid JSON list as described — no explanations, markdown, or extra formatting."
)
response = get_gpt_and_answer(prompt).strip()
# 3️⃣ Try to safely parse
try:
# First, try JSON
state["main_parts"] = json.loads(response)
except json.JSONDecodeError:
try:
# If not JSON, try Python literal
state["main_parts"] = ast.literal_eval(response)
except Exception:
# If both fail, store fallback info
print("⚠️ Model returned invalid content:", response)
state["main_parts"] = []
return state
def get_answer(state: InfoState) -> InfoState :
"""
Final Node that returns the final answer organized.
Combines: tool_answer → final_answer
"""
print("Getting Answer (Gemini)...")
prompt = (
"You are a knowledgeable assistant that answers questions based on context and common factual knowledge.\n"
"Use the context first, but if it clearly lacks the needed details, you may rely on well-known public facts "
"(such as from Wikipedia) that logically complete the answer.\n\n"
f"Question: {state['question']}\n"
f"Context:\n{state.get('tool_answer')}\n\n"
"Instructions:\n"
"- Focus on producing one short factual answer.\n"
"- You Should think enough at first before giving final answer. \n"
"- Do not include tool names, prefixes, or metadata.\n"
"- If the context contains partial hints, you can infer the answer from general knowledge of the same topic.\n"
"- If the question asks about an attached file or audio, reply briefly that you cannot access attachments or audio files."
"- If the context lacks key details or references a file, start with: I'm not sure because the question depends on missing data or an attached file. Then, add what you reasonably know about the topic."
"- Final answer should be complete text not part of answer"
"Final Answer:"
)
state["final_answer"] = get_gpt_and_answer(prompt)
return state
def get_type(state: InfoState) -> InfoState:
"""Choose which tool to use based on question type (WIKI, SEARCH, CODE)."""
print("Getting Type (Gemini/OpenAI)...")
q = state["question"].lower()
if any(w in q for w in ["calculate", "sum", "add", "price", "how much", "date", "time"]):
state["answer_type"] = "MATH"
elif any(w in q for w in ["latest", "current", "today", "news"]):
state["answer_type"] = "WebInfo"
elif any(w in q for w in ["who", "where", "what", "when", "why", "how many", "which"]):
state["answer_type"] = "WIKI"
else:
try:
prompt = f"""
You are a strict classifier.
Your job is to classify the following question into ONE of four categories:
- WIKI → informative, factual, or science question
- WebInfo → up-to-date, news, or current event question
- MATH → math, numeric, date, or time calculation
- LLM → all others, including links, reasoning, and file-related tasks
Question: "{state['question']}"
Rules:
- Reply with exactly one of these words: WIKI, WebInfo, MATH, or LLM
- Output nothing else. No punctuation, no quotes, no explanation.
- If unsure, default to LLM.
"""
resp = get_gpt_and_answer(prompt)
state["answer_type"] = resp.split()[0].strip()
except:
state["answer_type"] = "LLM"
return state
def get_search_results(state: InfoState) -> InfoState:
"""Tool to search web for results using DuckDuckGo."""
print("Searching...")
search = DuckDuckGoSearchRun()
try:
state['tool_answer'] = search.run(state["question"]) #" " .join(state["main_parts"]))
return state
except Exception:
state['tool_answer'] = ""
return state
def route(state: InfoState):
print(state["answer_type"])
return state["answer_type"]
################# Graph ################
def get_graph():
graph = StateGraph(InfoState)
# Add nodes
#graph.add_node("get_wiki_relate", get_wiki_relate)
graph.add_node("preprocess_text", preprocess_text)
graph.add_node("get_answer", get_answer)
graph.add_node("get_type", get_type)
graph.add_node("get_search_results", get_search_results)
graph.add_node("execute_code", execute_code)
graph.add_node("get_code", get_code)
# Add edges
graph.add_edge(START, "preprocess_text")
graph.add_edge("preprocess_text", "get_type")
# Add conditional edges
graph.add_conditional_edges(
"get_type",
route,
{
"WebInfo": "get_search_results",
"WIKI": "get_search_results",#"get_wiki_relate",
"MATH": "get_code",
"LLM": "get_answer"
}
)
# Add final edges
graph.add_edge("get_search_results", "get_answer")
#graph.add_edge("get_wiki_relate", "get_answer")
graph.add_edge("get_code", "execute_code")
graph.add_edge("execute_code", "get_answer")
graph.add_edge("get_answer", END)
# Compile the graph
compiled_graph = graph.compile()
return compiled_graph
def ask(compiled_graph,question):
legitimate_result = compiled_graph.invoke({
"question": question,
})
return legitimate_result['final_answer'] #,legitimate_result |