Spaces:
Runtime error
Runtime error
| import os | |
| from dotenv import load_dotenv | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| import operator | |
| from langgraph.graph import StateGraph, START, END | |
| from typing import Annotated, Literal | |
| from typing_extensions import TypedDict | |
| from langgraph.types import Send | |
| from pydantic import BaseModel | |
| from langchain.tools import DuckDuckGoSearchRun | |
| from langchain_community.utilities import WikipediaAPIWrapper | |
| from langchain_community.tools import WikipediaQueryRun | |
| from langchain_experimental.utilities import PythonREPL | |
| class SmallerQuestions(BaseModel): | |
| questions: list[str] | |
| class Nature(BaseModel): | |
| nature: Literal["research", "computation", "interpretation"] | |
| class OverallState(TypedDict): | |
| initial_question: str = "" | |
| smaller_questions: list[str] = [] | |
| current_question: str = "" | |
| context: str = "" | |
| key_words: str = "" | |
| wiki_results: str = "" | |
| web_results: str = "" | |
| nature: Literal["research", "computation", "interpretation"] = "interpretation" | |
| middle_answers: Annotated[list, operator.add] = [] | |
| answer: str = "" | |
| #Nodes | |
| def BreakQuestion(state: OverallState): | |
| prompt = """Task: | |
| Break the following question down into a maximum of 7 smaller questions. | |
| Your response must be a list in which each item corresponds to one smaller question. | |
| Beware that the answer to each one of the questions you list should lead to the next question without holdbacks. | |
| Question: | |
| """ + state["initial_question"] | |
| response = llm.with_structured_output(SmallerQuestions).invoke(prompt) | |
| return {"smaller_questions": response.questions, | |
| "current_question": response.questions[0]} | |
| def DecideNature(state: OverallState): | |
| prompt = """Task: | |
| The following question is answerable via one of the following three approaches: "computation", "research" or "interpretation". | |
| Computation is a question that requires a Python interpreter; research is a question that requires a web search in sites like Wikipedia; interpretation is a question that needs only reasoning to be answered. | |
| Decide which of those is the best approach and return only the string corresponding to the one you choose, and nothing else. | |
| Question: | |
| """ + state["current_question"] | |
| response = llm.with_structured_output(Nature).invoke(prompt) | |
| return {"nature": response.nature} | |
| def KeyWords(state: OverallState): | |
| prompt = """Task: | |
| Gather the keywords from the following question to perform a search. Your response must return only keywords and nothing else. | |
| Question: | |
| """ + state["current_question"] | |
| response = llm.invoke(prompt) | |
| return {"key_words": response.content} | |
| def WikiSearch(state: OverallState): | |
| wikipedia_api = WikipediaAPIWrapper(top_k_results=1,doc_content_chars_max=1000) | |
| wikipedia = WikipediaQueryRun(api_wrapper=wikipedia_api) | |
| result = wikipedia.run({"query": state["key_words"]}) | |
| return {"wiki_results": result} | |
| def WebSearch(state: OverallState): | |
| search_tool = DuckDuckGoSearchRun() | |
| result = search_tool.invoke(state["key_words"]) | |
| return {"web_results": result} | |
| def ContextAnswer(state: OverallState): | |
| context = "Wiki results: " + state.get("wiki_results", "No wiki results found") + "\n\n Web results: " + state.get("web_results", "No web results found") | |
| return {"context": context} | |
| def Computation(state: OverallState): | |
| python_repl = PythonREPL() | |
| prompt = """Task: | |
| The following question requires a computation to be answered. Create a code that solves the problem. | |
| Your response must be only the code that solves the problem and nothing else. | |
| Question: | |
| """ + state["current_question"] | |
| response = llm.invoke(prompt) | |
| code_result = python_repl.run(response.content) | |
| structured_answer = "The result of the following problem is " + code_result + "Problem: " + state["current_question"] | |
| return {"middle_answers": [structured_answer]} | |
| def Reasoning(state: OverallState): | |
| prompt = """Task: | |
| Answer the following question using your reasoning. Your response must contain only the answer to the question and nothing else. | |
| Question: | |
| """ + state["current_question"] | |
| response = llm.invoke(prompt) | |
| return {"middle_answers": [response.content]} | |
| def AdvanceToNextQuestion(state: OverallState): | |
| current_index = state["smaller_questions"].index(state["current_question"]) | |
| if current_index < len(state["smaller_questions"]) - 1: | |
| return {"current_question": state["smaller_questions"][current_index + 1], | |
| "context": ""} | |
| return {} | |
| def FinalAnswer(state: OverallState): | |
| middle_answers = "\n\n".join(state["middle_answers"]) | |
| prompt = """Task: | |
| You will receive a question and some information from which you must be able to solve the question completely. | |
| Do your best to keep your response as close to the given information as possible. Your response should contain only the final answer to the question and nothing else. | |
| Question: | |
| """ + state["initial_question"] + "Context: " + middle_answers | |
| response = llm.invoke(prompt) | |
| return {"answer": response.content} | |
| #Edges | |
| def Nature(state: OverallState): | |
| if state["nature"] == "computation": | |
| return Send("computation", state) | |
| elif state["nature"] == "research": | |
| return Send("key_words", state) | |
| else: | |
| return Send("interpretation", state) | |
| def NextQuestion(state: OverallState): | |
| if state["current_question"] == state["smaller_questions"][-1]: | |
| return "final_answer" | |
| else: | |
| return "define_nature" | |
| load_dotenv() | |
| llm = ChatGoogleGenerativeAI( | |
| model="gemini-1.5-pro", # or "gemini-1.5-flash" for faster/cheaper | |
| temperature=0, | |
| max_tokens=1024, | |
| timeout=30, | |
| max_retries=2, | |
| google_api_key=os.getenv("GOOGLE_API_KEY") | |
| ) | |
| builder = StateGraph(OverallState) | |
| builder.add_node("break_question", BreakQuestion) | |
| builder.add_node("advance", AdvanceToNextQuestion) | |
| builder.add_node("define_nature", DecideNature) | |
| builder.add_node("key_words", KeyWords) | |
| builder.add_node("wiki_search", WikiSearch) | |
| builder.add_node("web_search", WebSearch) | |
| builder.add_node("answer", ContextAnswer) | |
| builder.add_node("computation", Computation) | |
| builder.add_node("interpretation", Reasoning) | |
| builder.add_node("final_answer", FinalAnswer) | |
| builder.add_edge(START, "break_question") | |
| builder.add_conditional_edges("define_nature", Nature) | |
| builder.add_edge("key_words", "wiki_search") | |
| builder.add_edge("key_words", "web_search") | |
| builder.add_edge("web_search", "answer") | |
| builder.add_edge("wiki_search", "answer") | |
| builder.add_edge("answer", "advance") | |
| builder.add_edge("computation", "advance") | |
| builder.add_edge("interpretation", "advance") | |
| builder.add_conditional_edges("advance", NextQuestion) | |
| builder.add_edge("final_answer", END) | |
| graph = builder.compile() | |