SyedHasanCronosPMC commited on
Commit
305500a
·
verified ·
1 Parent(s): 81a5656

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -144
app.py CHANGED
@@ -1,194 +1,165 @@
1
  import gradio as gr
2
  import os
3
  import matplotlib.pyplot as plt
4
- import pandas as pd
5
- from langgraph.graph import StateGraph
6
- from langchain_core.messages import HumanMessage, AIMessage
 
7
  import warnings
8
  warnings.filterwarnings("ignore")
9
 
10
- # Define a Command class for langgraph 0.0.41
11
- class Command:
12
- def __init__(self, update=None, next=None, goto=None):
13
- self.update = update or {}
14
- self.next = next
15
- self.goto = goto
16
-
17
- # Set API key (ensure you add this as a secret in HF Spaces)
18
  api_key = os.getenv("ANTHROPIC_API_KEY")
19
- if not api_key:
20
- raise ValueError("ANTHROPIC_API_KEY environment variable not set")
21
 
22
- # Mock LLM implementation that doesn't rely on any external HTTP clients
 
 
 
23
  class MockLLM:
24
  def __init__(self):
25
  self.model = "mock-model"
26
-
27
- def invoke(self, inputs):
28
- if isinstance(inputs, dict) and "messages" in inputs:
29
- # Process the messages
30
- content = ""
31
- for msg in inputs["messages"]:
32
- if hasattr(msg, "content"):
33
- content += msg.content + "\n"
34
- elif isinstance(msg, dict) and "content" in msg:
35
- content += msg["content"] + "\n"
36
-
37
- # For research queries
38
- if "research" in content.lower():
39
- return "Based on my research, here's what I found about your topic. FINAL ANSWER: This topic shows trends that would be interesting to visualize in a chart."
40
- # For chart generation
41
- else:
42
- return "I've analyzed the data and created a chart visualization. FINAL ANSWER: The chart shows an upward trend from 2020 to 2024."
43
-
44
- elif isinstance(inputs, str):
45
- if "research" in inputs.lower():
46
- return "Based on my research, here's what I found about your topic. FINAL ANSWER: This topic shows trends that would be interesting to visualize in a chart."
47
- else:
48
- return "I've analyzed the data and created a chart visualization. FINAL ANSWER: The chart shows an upward trend from 2020 to 2024."
49
-
50
- else:
51
- return "I've processed your request. FINAL ANSWER: Here's a summary of what I found."
52
-
53
- # Create our mock LLM
54
  llm = MockLLM()
55
 
56
- # System prompt constructor
57
- def make_system_prompt(suffix: str) -> str:
58
- return (
59
- "You are a helpful AI assistant, collaborating with other assistants. "
60
- "Use the provided tools to progress towards answering the question. "
61
- "If you are unable to fully answer, that's OK—another assistant with different tools "
62
- "will help where you left off. Execute what you can to make progress. "
63
- "If you or any of the other assistants have the final answer or deliverable, "
64
- "prefix your response with FINAL ANSWER so the team knows to stop.\n"
65
- f"{suffix}"
66
- )
67
-
68
- # Research phase
69
- def research_node(state):
70
- # Create a custom research agent using langgraph 0.0.41 compatible approach
71
- from langgraph.prebuilt import create_agent_executor
72
- agent = create_agent_executor(
73
- llm,
74
- tools=[],
75
- system_message=make_system_prompt("You can only do research.")
76
- )
77
-
78
- # Process the current state
79
- result = agent.invoke(state)
80
-
81
- # Check if we have a final answer
82
- last_message = result["messages"][-1]
83
- content = last_message.content if hasattr(last_message, "content") else last_message
84
-
85
- # Create an AIMessage with the researcher name
86
- if not isinstance(last_message, dict):
87
- result["messages"][-1] = AIMessage(content=content, name="researcher")
88
- else:
89
- result["messages"][-1]["name"] = "researcher"
90
-
91
- # In langgraph 0.0.41 style, return the updated state
92
- return {"messages": result["messages"], "has_final_answer": "FINAL ANSWER" in content}
93
-
94
- # Chart generation phase
95
- def chart_node(state):
96
- # Create a custom chart generator agent
97
- from langgraph.prebuilt import create_agent_executor
98
- agent = create_agent_executor(
99
- llm,
100
- tools=[],
101
- system_message=make_system_prompt("You can only generate charts.")
102
- )
103
-
104
- # Process the current state
105
- result = agent.invoke(state)
106
-
107
- # Add the chart_generator name
108
- last_message = result["messages"][-1]
109
- content = last_message.content if hasattr(last_message, "content") else last_message
110
-
111
- if not isinstance(last_message, dict):
112
- result["messages"][-1] = AIMessage(content=content, name="chart_generator")
113
- else:
114
- result["messages"][-1]["name"] = "chart_generator"
115
-
116
- # Return the updated state
117
- return {"messages": result["messages"], "has_final_answer": True}
118
-
119
- # Define the router function for the graph
120
- def router(state):
121
- # Check if we have a final answer
122
- return "chart_generator" if not state.get("has_final_answer", False) else "end"
123
-
124
- # Simplified end node that doesn't modify the state
125
- def end_node(state):
126
- return state
127
-
128
- # Build a proper LangGraph for v0.0.41
129
- workflow = StateGraph({"messages": [], "has_final_answer": False})
130
  workflow.add_node("researcher", research_node)
131
  workflow.add_node("chart_generator", chart_node)
132
- workflow.add_node("end", end_node)
133
  workflow.set_entry_point("researcher")
134
- workflow.add_conditional_edges("researcher", router)
135
- workflow.add_edge("chart_generator", "end")
 
 
 
 
 
 
 
136
  graph = workflow.compile()
137
 
138
- # LangGraph runner
139
- def run_langgraph(user_input):
 
 
 
140
  try:
141
- # Create a human message
142
- human_message = HumanMessage(content=user_input)
143
-
144
- # Run the workflow with proper initialization
145
- events = graph.stream({"messages": [human_message], "has_final_answer": False})
146
- outputs = list(events)
147
-
148
- # Get the final message
149
- final_state = outputs[-1]
150
  final_messages = final_state.get("messages", [])
151
-
152
  if not final_messages:
153
  return "Error: No messages in the final state.", None
154
-
155
  final_message = final_messages[-1]
156
- final_content = final_message.content if hasattr(final_message, "content") else final_message
157
-
 
 
158
  if isinstance(final_content, str) and "FINAL ANSWER" in final_content:
159
- # Simulated chart (you can later parse dynamic values if needed)
160
  years = [2020, 2021, 2022, 2023, 2024]
161
  gdp = [21.4, 22.0, 23.1, 24.8, 26.2]
162
  plt.figure()
163
- plt.plot(years, gdp, marker='o')
164
  plt.title("USA GDP Over Last 5 Years")
165
  plt.xlabel("Year")
166
  plt.ylabel("GDP in Trillions USD")
167
  plt.grid(True)
168
  plt.tight_layout()
169
  plt.savefig("gdp_chart.png")
170
- return "Chart generated based on FINAL ANSWER.", "gdp_chart.png"
171
- else:
172
- if isinstance(final_content, str):
173
- return final_content, None
174
- else:
175
- return str(final_content), None
176
  except Exception as e:
177
- print(f"Error in run_langgraph: {e}")
178
  import traceback
179
  traceback.print_exc()
180
  return f"Error: {str(e)}", None
181
 
 
 
182
  # Gradio UI
 
183
  def process_input(user_input):
184
  return run_langgraph(user_input)
185
 
 
186
  interface = gr.Interface(
187
  fn=process_input,
188
  inputs=gr.Textbox(label="Enter your research task"),
189
  outputs=[gr.Textbox(label="Output"), gr.Image(type="filepath", label="Chart")],
190
  title="LangGraph Research Automation",
191
- description="Enter a research prompt and view chart output when applicable."
192
  )
193
 
194
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  import os
3
  import matplotlib.pyplot as plt
4
+ from typing import TypedDict, Annotated, List
5
+ from operator import add
6
+ from langgraph.graph import StateGraph, END
7
+ from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
8
  import warnings
9
  warnings.filterwarnings("ignore")
10
 
11
+ # API key is optional because we use a MockLLM below.
12
+ # If/when you swap in a real Anthropic client, you can reinstate the check.
 
 
 
 
 
 
13
  api_key = os.getenv("ANTHROPIC_API_KEY")
 
 
14
 
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Mock LLM — does not require network access or an API key.
18
+ # ---------------------------------------------------------------------------
19
  class MockLLM:
20
  def __init__(self):
21
  self.model = "mock-model"
22
+
23
+ def invoke(self, messages):
24
+ last_content = ""
25
+ if isinstance(messages, list) and messages:
26
+ last = messages[-1]
27
+ last_content = last.content if hasattr(last, "content") else str(last)
28
+ elif isinstance(messages, str):
29
+ last_content = messages
30
+
31
+ # Second-pass (chart) request — emit the FINAL ANSWER sentinel.
32
+ if "chart" in last_content.lower() or "visuali" in last_content.lower():
33
+ return AIMessage(
34
+ content=(
35
+ "I've analyzed the data and created a chart visualization. "
36
+ "FINAL ANSWER: The chart shows an upward trend from 2020 to 2024."
37
+ )
38
+ )
39
+ # First-pass (research) request — stay open-ended.
40
+ return AIMessage(
41
+ content=(
42
+ "Based on my research, here's what I found about your topic. "
43
+ "This topic shows trends that would be interesting to visualize in a chart."
44
+ )
45
+ )
46
+
47
+
 
 
48
  llm = MockLLM()
49
 
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Graph state — TypedDict is required by StateGraph (not a dict instance).
53
+ # `add` is used as the reducer so that node returns of {"messages": [msg]}
54
+ # append rather than overwrite.
55
+ # ---------------------------------------------------------------------------
56
+ class GraphState(TypedDict):
57
+ messages: Annotated[List[BaseMessage], add]
58
+ has_final_answer: bool
59
+
60
+
61
+ # ---------------------------------------------------------------------------
62
+ # Nodes
63
+ # ---------------------------------------------------------------------------
64
+ def research_node(state: GraphState):
65
+ response = llm.invoke(state["messages"])
66
+ response.name = "researcher"
67
+ return {
68
+ "messages": [response],
69
+ "has_final_answer": "FINAL ANSWER" in response.content,
70
+ }
71
+
72
+
73
+ def chart_node(state: GraphState):
74
+ # Nudge the LLM toward the chart-generation branch.
75
+ prompt = state["messages"] + [HumanMessage(content="Now create the chart visualization.")]
76
+ response = llm.invoke(prompt)
77
+ response.name = "chart_generator"
78
+ return {
79
+ "messages": [response],
80
+ "has_final_answer": True,
81
+ }
82
+
83
+
84
+ # Router returns the key used in the conditional-edge mapping below.
85
+ def router(state: GraphState) -> str:
86
+ return "end" if state.get("has_final_answer", False) else "chart_generator"
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Build the graph — use END (not a custom "end" node) to terminate.
91
+ # ---------------------------------------------------------------------------
92
+ workflow = StateGraph(GraphState)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  workflow.add_node("researcher", research_node)
94
  workflow.add_node("chart_generator", chart_node)
 
95
  workflow.set_entry_point("researcher")
96
+ workflow.add_conditional_edges(
97
+ "researcher",
98
+ router,
99
+ {
100
+ "chart_generator": "chart_generator",
101
+ "end": END,
102
+ },
103
+ )
104
+ workflow.add_edge("chart_generator", END)
105
  graph = workflow.compile()
106
 
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Runner
110
+ # ---------------------------------------------------------------------------
111
+ def run_langgraph(user_input: str):
112
  try:
113
+ initial_state: GraphState = {
114
+ "messages": [HumanMessage(content=user_input)],
115
+ "has_final_answer": False,
116
+ }
117
+ final_state = graph.invoke(initial_state)
 
 
 
 
118
  final_messages = final_state.get("messages", [])
119
+
120
  if not final_messages:
121
  return "Error: No messages in the final state.", None
122
+
123
  final_message = final_messages[-1]
124
+ final_content = (
125
+ final_message.content if hasattr(final_message, "content") else str(final_message)
126
+ )
127
+
128
  if isinstance(final_content, str) and "FINAL ANSWER" in final_content:
 
129
  years = [2020, 2021, 2022, 2023, 2024]
130
  gdp = [21.4, 22.0, 23.1, 24.8, 26.2]
131
  plt.figure()
132
+ plt.plot(years, gdp, marker="o")
133
  plt.title("USA GDP Over Last 5 Years")
134
  plt.xlabel("Year")
135
  plt.ylabel("GDP in Trillions USD")
136
  plt.grid(True)
137
  plt.tight_layout()
138
  plt.savefig("gdp_chart.png")
139
+ plt.close()
140
+ return f"{final_content}\n\nChart generated based on FINAL ANSWER.", "gdp_chart.png"
141
+
142
+ return str(final_content), None
143
+
 
144
  except Exception as e:
 
145
  import traceback
146
  traceback.print_exc()
147
  return f"Error: {str(e)}", None
148
 
149
+
150
+ # ---------------------------------------------------------------------------
151
  # Gradio UI
152
+ # ---------------------------------------------------------------------------
153
  def process_input(user_input):
154
  return run_langgraph(user_input)
155
 
156
+
157
  interface = gr.Interface(
158
  fn=process_input,
159
  inputs=gr.Textbox(label="Enter your research task"),
160
  outputs=[gr.Textbox(label="Output"), gr.Image(type="filepath", label="Chart")],
161
  title="LangGraph Research Automation",
162
+ description="Enter a research prompt and view chart output when applicable.",
163
  )
164
 
165
  if __name__ == "__main__":