KheemDH commited on
Commit
178b9ef
Β·
1 Parent(s): 788c7c4

Updated Script

Browse files
Files changed (1) hide show
  1. app.py +69 -81
app.py CHANGED
@@ -5,50 +5,46 @@ from typing import Dict, TypedDict
5
 
6
  import gradio as gr
7
 
8
- from langgraph.graph import StateGraph, END
9
  from langchain_core.prompts import ChatPromptTemplate
10
  from langchain_openai import ChatOpenAI
11
  from browser_use import Agent
12
 
13
- # Load environment variables (including OPENAI_API_KEY) from .env if present.
 
 
14
  load_dotenv()
15
 
16
- # -------------------------------------------------------
17
- # Helpers to get ChatOpenAI models using the environment key
18
- # -------------------------------------------------------
19
  def get_llm():
20
- """
21
- Returns a ChatOpenAI instance using the OPENAI_API_KEY from environment.
22
- """
23
  return ChatOpenAI(
24
  temperature=0,
25
  openai_api_key=os.getenv("OPENAI_API_KEY")
26
  )
27
 
28
  def get_llm_browser():
29
- """
30
- Returns a ChatOpenAI instance (GPT-4 or your custom model)
31
- using the OPENAI_API_KEY from environment.
32
- """
33
  return ChatOpenAI(
34
- model="gpt-4o", # Adjust the model name as needed
35
  temperature=0,
36
  openai_api_key=os.getenv("OPENAI_API_KEY")
37
  )
38
 
39
- # -------------------------------------------------------
40
- # TypedDict for State
41
- # -------------------------------------------------------
42
  class State(TypedDict):
43
  query: str
44
  category: str
45
  sentiment: str
46
  response: str
47
 
48
- # -------------------------------------------------------
49
- # Node functions for our workflow
50
- # (using get_llm() or get_llm_browser() on-demand)
51
- # -------------------------------------------------------
52
  def categorize(state: State) -> State:
53
  prompt = ChatPromptTemplate.from_template(
54
  "Categorize the following customer query into one of these categories: "
@@ -62,7 +58,8 @@ def categorize(state: State) -> State:
62
  def analyze_sentiment(state: State) -> State:
63
  prompt = ChatPromptTemplate.from_template(
64
  "Analyze the sentiment of the following customer query. "
65
- "Respond with either 'Positive', 'Neutral', or 'Negative'. Query: {query}"
 
66
  )
67
  chain = prompt | get_llm()
68
  sentiment = chain.invoke({"query": state["query"]}).content.strip()
@@ -88,17 +85,13 @@ def handle_billing(state: State) -> State:
88
  return state
89
 
90
  async def run_browser_agent(task: str) -> str:
91
- """
92
- Helper to run the browser-use Agent asynchronously.
93
- """
94
  agent = Agent(task=task, llm=get_llm_browser())
95
  result = await agent.run()
96
  return result
97
 
98
  def handle_general(state: State) -> State:
99
- """
100
- For general queries, we use the browser agent to consult online resources.
101
- """
102
  task = (
103
  "You are a customer support agent that consults online sources. "
104
  f"Provide a detailed, informed response to this customer query: {state['query']}"
@@ -109,12 +102,10 @@ def handle_general(state: State) -> State:
109
  if isinstance(result, str):
110
  final_text = result.strip()
111
  elif hasattr(result, "all_results"):
112
- # Iterate over the list of ActionResults to extract the final done answer
113
  for action in result.all_results:
114
- # Check if the action is marked as done and has extracted content
115
  if action.get("is_done") and action.get("extracted_content"):
116
- final_text = action.get("extracted_content").strip()
117
- # Fallback in case no done action is found
118
  if not final_text:
119
  final_text = str(result).strip()
120
  else:
@@ -129,7 +120,7 @@ def escalate(state: State) -> State:
129
 
130
  def route_query(state: State) -> str:
131
  """
132
- Determine which node to route to based on sentiment and category.
133
  """
134
  if state["sentiment"].lower() == "negative":
135
  return "escalate"
@@ -140,78 +131,78 @@ def route_query(state: State) -> str:
140
  else:
141
  return "handle_general"
142
 
143
- # -------------------------------------------------------
144
- # Create the workflow graph
145
- # -------------------------------------------------------
146
- workflow = StateGraph(State)
147
- workflow.add_node("categorize", categorize)
148
- workflow.add_node("analyze_sentiment", analyze_sentiment)
149
- workflow.add_node("handle_technical", handle_technical)
150
- workflow.add_node("handle_billing", handle_billing)
151
- workflow.add_node("handle_general", handle_general)
152
- workflow.add_node("escalate", escalate)
153
-
154
- workflow.add_edge("categorize", "analyze_sentiment")
155
- workflow.add_conditional_edges(
156
- "analyze_sentiment",
157
- route_query,
158
- {
159
- "handle_technical": "handle_technical",
160
- "handle_billing": "handle_billing",
161
- "handle_general": "handle_general",
162
- "escalate": "escalate"
163
- }
164
- )
165
- workflow.add_edge("handle_technical", END)
166
- workflow.add_edge("handle_billing", END)
167
- workflow.add_edge("handle_general", END)
168
- workflow.add_edge("escalate", END)
169
- workflow.set_entry_point("categorize")
170
-
171
- # Compile the workflow into a "CompiledStateGraph" object.
172
- app = workflow.compile()
173
-
174
-
175
- # -------------------------------------------------------
176
- # Gradio callback function
177
- # -------------------------------------------------------
178
  async def run_customer_support(query: str, api_key: str = "") -> str:
179
  """
180
  Main function called by Gradio upon submit.
181
  - If user provided an API key, set it in the environment.
182
- - Then run the compiled workflow on the user's query.
183
- - Return the final response from the workflow.
184
  """
 
185
  if not api_key and not os.getenv("OPENAI_API_KEY"):
186
  return "Error: Please provide an OpenAI API key."
187
 
 
188
  if api_key:
189
  os.environ["OPENAI_API_KEY"] = api_key
190
 
191
  try:
192
  # Initialize the state
193
- state = {
194
  "query": query,
195
  "category": "",
196
  "sentiment": "",
197
  "response": ""
198
  }
199
- # Instead of app(state) or app.run(state), call .execute(...)
200
- final_state = app.execute(state)
201
  return final_state["response"]
202
  except Exception as e:
203
  return f"Error: {str(e)}"
204
 
205
- # -------------------------------------------------------
206
- # Build the Gradio UI
207
- # -------------------------------------------------------
208
  with gr.Blocks(title="Customer Support Agent with Browser Use") as demo:
209
  gr.Markdown("# Customer Support Agent with Browser Use")
210
  gr.Markdown(
211
  "This agent categorizes customer queries and uses a browser-based agent "
212
  "to provide informed answers (when the query is general)."
213
  )
214
-
215
  with gr.Row():
216
  with gr.Column():
217
  api_key_input = gr.Textbox(
@@ -231,16 +222,13 @@ with gr.Blocks(title="Customer Support Agent with Browser Use") as demo:
231
  lines=10,
232
  interactive=False
233
  )
234
-
235
- # Note: The order of inputs here matches the function signature.
236
  submit_btn.click(
237
  fn=run_customer_support,
238
  inputs=[query_input, api_key_input],
239
  outputs=output_box
240
  )
241
 
242
- # -------------------------------------------------------
243
- # Launch in local or Hugging Face Spaces
244
- # -------------------------------------------------------
245
  if __name__ == "__main__":
246
  demo.launch()
 
5
 
6
  import gradio as gr
7
 
 
8
  from langchain_core.prompts import ChatPromptTemplate
9
  from langchain_openai import ChatOpenAI
10
  from browser_use import Agent
11
 
12
+ # ──────────────────────────────────────────────────────────────────────────
13
+ # 1) Load environment
14
+ # ──────────────────────────────────────────────────────────────────────────
15
  load_dotenv()
16
 
17
+ # ──────────────────────────────────────────────────────────────────────────
18
+ # 2) Helper to get ChatOpenAI from environment
19
+ # ──────────────────────────────────────────────────────────────────────────
20
  def get_llm():
21
+ """Returns a ChatOpenAI instance using the OPENAI_API_KEY from environment."""
 
 
22
  return ChatOpenAI(
23
  temperature=0,
24
  openai_api_key=os.getenv("OPENAI_API_KEY")
25
  )
26
 
27
  def get_llm_browser():
28
+ """Returns a ChatOpenAI instance for the browser agent (e.g., GPT-4) from environment."""
 
 
 
29
  return ChatOpenAI(
30
+ model="gpt-4o", # Adjust if needed
31
  temperature=0,
32
  openai_api_key=os.getenv("OPENAI_API_KEY")
33
  )
34
 
35
+ # ──────────────────────────────────────────────────────────────────────────
36
+ # 3) TypedDict for internal state
37
+ # ──────────────────────────────────────────────────────────────────────────
38
  class State(TypedDict):
39
  query: str
40
  category: str
41
  sentiment: str
42
  response: str
43
 
44
+ # ──────────────────────────────────────────────────────────────────────────
45
+ # 4) Individual node-like functions
46
+ # ──────────────────────────────────────────────────────────────────────────
47
+
48
  def categorize(state: State) -> State:
49
  prompt = ChatPromptTemplate.from_template(
50
  "Categorize the following customer query into one of these categories: "
 
58
  def analyze_sentiment(state: State) -> State:
59
  prompt = ChatPromptTemplate.from_template(
60
  "Analyze the sentiment of the following customer query. "
61
+ "Respond with either 'Positive', 'Neutral', or 'Negative'. "
62
+ "Query: {query}"
63
  )
64
  chain = prompt | get_llm()
65
  sentiment = chain.invoke({"query": state["query"]}).content.strip()
 
85
  return state
86
 
87
  async def run_browser_agent(task: str) -> str:
88
+ """Helper to run the browser-use Agent asynchronously."""
 
 
89
  agent = Agent(task=task, llm=get_llm_browser())
90
  result = await agent.run()
91
  return result
92
 
93
  def handle_general(state: State) -> State:
94
+ """For general queries, we use the browser agent to consult online resources."""
 
 
95
  task = (
96
  "You are a customer support agent that consults online sources. "
97
  f"Provide a detailed, informed response to this customer query: {state['query']}"
 
102
  if isinstance(result, str):
103
  final_text = result.strip()
104
  elif hasattr(result, "all_results"):
105
+ # Check if any ActionResults are "done" with extracted content
106
  for action in result.all_results:
 
107
  if action.get("is_done") and action.get("extracted_content"):
108
+ final_text = action["extracted_content"].strip()
 
109
  if not final_text:
110
  final_text = str(result).strip()
111
  else:
 
120
 
121
  def route_query(state: State) -> str:
122
  """
123
+ Determine which function to use based on sentiment and category.
124
  """
125
  if state["sentiment"].lower() == "negative":
126
  return "escalate"
 
131
  else:
132
  return "handle_general"
133
 
134
+ # ──────────────────────────────────────────────────────────────────────────
135
+ # 5) A simple "workflow" function (manual, no langgraph)
136
+ # ──────────────────────────────────────────────────────────────────────────
137
+
138
+ def run_workflow(state: State) -> State:
139
+ """
140
+ Manually steps through:
141
+ 1) categorize
142
+ 2) sentiment
143
+ 3) route
144
+ 4) handle_x or escalate
145
+ """
146
+ # Step 1
147
+ state = categorize(state)
148
+ # Step 2
149
+ state = analyze_sentiment(state)
150
+ # Step 3 - route
151
+ next_step = route_query(state)
152
+
153
+ if next_step == "handle_technical":
154
+ state = handle_technical(state)
155
+ elif next_step == "handle_billing":
156
+ state = handle_billing(state)
157
+ elif next_step == "handle_general":
158
+ state = handle_general(state)
159
+ else:
160
+ state = escalate(state)
161
+
162
+ return state
163
+
164
+ # ──────────────────────────────────────────────────────────────────────────
165
+ # 6) Gradio callback
166
+ # ──────────────────────────────────────────────────────────────────────────
167
+
 
168
  async def run_customer_support(query: str, api_key: str = "") -> str:
169
  """
170
  Main function called by Gradio upon submit.
171
  - If user provided an API key, set it in the environment.
172
+ - Then run the manual "workflow" on the user's query.
173
+ - Return the final response from the final state.
174
  """
175
+ # Check key
176
  if not api_key and not os.getenv("OPENAI_API_KEY"):
177
  return "Error: Please provide an OpenAI API key."
178
 
179
+ # Set user-provided key
180
  if api_key:
181
  os.environ["OPENAI_API_KEY"] = api_key
182
 
183
  try:
184
  # Initialize the state
185
+ state: State = {
186
  "query": query,
187
  "category": "",
188
  "sentiment": "",
189
  "response": ""
190
  }
191
+ final_state = run_workflow(state) # Manually run the chain of steps
 
192
  return final_state["response"]
193
  except Exception as e:
194
  return f"Error: {str(e)}"
195
 
196
+ # ──────────────────────────────────────────────────────────────────────────
197
+ # 7) Build the Gradio UI
198
+ # ──────────────────────────────────────────────────────────────────────────
199
  with gr.Blocks(title="Customer Support Agent with Browser Use") as demo:
200
  gr.Markdown("# Customer Support Agent with Browser Use")
201
  gr.Markdown(
202
  "This agent categorizes customer queries and uses a browser-based agent "
203
  "to provide informed answers (when the query is general)."
204
  )
205
+
206
  with gr.Row():
207
  with gr.Column():
208
  api_key_input = gr.Textbox(
 
222
  lines=10,
223
  interactive=False
224
  )
225
+
226
+ # The order of inputs in submit_btn.click must match run_customer_support signature
227
  submit_btn.click(
228
  fn=run_customer_support,
229
  inputs=[query_input, api_key_input],
230
  outputs=output_box
231
  )
232
 
 
 
 
233
  if __name__ == "__main__":
234
  demo.launch()