ChienChung commited on
Commit
b1c16fe
·
verified ·
1 Parent(s): 538ccb9

Upload 9 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ chroma_db/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
Dockerfile.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Set environment
4
+ ENV PYTHONUNBUFFERED=1
5
+ WORKDIR /app
6
+
7
+ # System dependencies
8
+ RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Install Python dependencies
11
+ COPY requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy all source code
15
+ COPY . .
16
+
17
+ # Expose default Gradio port
18
+ EXPOSE 7860
19
+
20
+ # Run app
21
+ CMD ["python", "app.py"]
app-7.py ADDED
@@ -0,0 +1,1150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ import os
3
+ import shutil
4
+ import json
5
+ import torch
6
+ import re
7
+ import requests
8
+ import transformers
9
+ import chardet
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer
11
+ from transformers.models.llama.configuration_llama import LlamaConfig
12
+ from huggingface_hub import hf_hub_download
13
+ import gradio as gr
14
+
15
+ # Solve permission issues
16
+ os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib"
17
+ os.environ["HOME"] = "/tmp"
18
+ os.environ["XDG_CACHE_HOME"] = "/tmp/.cache"
19
+ os.environ["HF_HOME"] = "/tmp/huggingface"
20
+ os.environ["TRANSFORMERS_CACHE"] = "/tmp/huggingface/transformers"
21
+ os.environ["HF_DATASETS_CACHE"] = "/tmp/huggingface/datasets"
22
+ os.environ["HF_METRICS_CACHE"] = "/tmp/huggingface/metrics"
23
+ os.environ["GRADIO_FLAGGING_DIR"] = "/tmp/flagged"
24
+ os.environ["SENTENCE_TRANSFORMERS_HOME"] = "/tmp/sentence_transformers"
25
+ os.environ["HF_HUB_CACHE"] = "/tmp/huggingface/hf_cache"
26
+ os.environ["HF_HUB_DOWNLOAD_TIMEOUT"] = "60"
27
+
28
+ # Load Required Modules
29
+ from langchain.embeddings import HuggingFaceEmbeddings
30
+ from langchain.vectorstores import Chroma, FAISS
31
+ from langchain.chains import RetrievalQA
32
+ from langchain.prompts import PromptTemplate
33
+ from langchain.llms import HuggingFacePipeline
34
+ from langchain.chat_models import ChatOpenAI
35
+ from langchain.chains import ConversationalRetrievalChain
36
+ from langchain.memory import ConversationBufferMemory
37
+ from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredWordDocumentLoader
38
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
39
+ from langchain.chains.summarize import load_summarize_chain
40
+ from tempfile import mkdtemp
41
+ from langchain.schema import AIMessage
42
+ from datetime import datetime, timedelta
43
+ from zoneinfo import ZoneInfo
44
+ from dateutil import parser as date_parser
45
+ import numexpr as ne
46
+ import pandas as pd
47
+
48
+ # Multi-Agent Imports
49
+ from serpapi import GoogleSearch
50
+ # CrewAI Section: completely use CrewAI's Agent, Task, Crew and @tool decorator
51
+ from crewai import Crew, Agent, Task, Process
52
+ from crewai.tools import tool
53
+ from geopy.geocoders import Nominatim
54
+ from timezonefinder import TimezoneFinder
55
+ from langchain_experimental.agents import create_pandas_dataframe_agent
56
+
57
+
58
+ session_retriever = None
59
+ session_qa_chain = None
60
+ csv_dataframe = None # CSV tool will use this
61
+
62
+ # Safe Result Formatter
63
+ def safe_format_result(result) -> str:
64
+ try:
65
+ if hasattr(result, "agent_name") and hasattr(result, "output"):
66
+ return f"[Agent: {result.agent_name}]\n{result.output}"
67
+ elif isinstance(result, str):
68
+ return result
69
+ elif isinstance(result, dict):
70
+ return json.dumps(result, indent=2)
71
+ elif isinstance(result, list):
72
+ return "\n".join(str(r) for r in result)
73
+ else:
74
+ return str(result)
75
+ except Exception as e:
76
+ return f"Error formatting result: {e}"
77
+
78
+ # Model and Device Setup
79
+ if torch.backends.mps.is_available():
80
+ device = "mps"
81
+ elif torch.cuda.is_available():
82
+ device = "cuda"
83
+ else:
84
+ device = "cpu"
85
+ print(f"Using device => {device}")
86
+
87
+ hf_token = os.environ.get("HF_TOKEN")
88
+ openai_api_key = os.environ.get("OPENAI_API_KEY")
89
+ model_id = "ChienChung/my-llama-1b"
90
+
91
+ config_path = hf_hub_download(
92
+ repo_id=model_id,
93
+ filename="config.json",
94
+ use_auth_token=hf_token,
95
+ cache_dir="/tmp/huggingface"
96
+ )
97
+ with open(config_path, "r", encoding="utf-8") as f:
98
+ config_dict = json.load(f)
99
+ if "rope_scaling" in config_dict:
100
+ config_dict["rope_scaling"] = {"type": "dynamic", "factor": config_dict["rope_scaling"].get("factor", 32.0)}
101
+ model_config = LlamaConfig.from_dict(config_dict)
102
+ model_config.trust_remote_code = True
103
+
104
+ print("Loading Llama model...")
105
+ model = AutoModelForCausalLM.from_pretrained(
106
+ model_id,
107
+ config=model_config,
108
+ trust_remote_code=True,
109
+ use_auth_token=hf_token,
110
+ cache_dir="/tmp/huggingface"
111
+ )
112
+ model.to(device)
113
+ print("Model loaded!")
114
+
115
+ print("Loading tokenizer...")
116
+ tokenizer = AutoTokenizer.from_pretrained(
117
+ model_id,
118
+ trust_remote_code=True,
119
+ use_auth_token=hf_token,
120
+ cache_dir="/tmp/huggingface"
121
+ )
122
+ if tokenizer.pad_token is None:
123
+ tokenizer.pad_token = tokenizer.eos_token
124
+ print("Tokenizer loaded!")
125
+
126
+ query_pipeline = transformers.pipeline(
127
+ "text-generation",
128
+ model=model,
129
+ tokenizer=tokenizer,
130
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32,
131
+ device_map="auto" if device != "cpu" else None,
132
+ do_sample=False,
133
+ temperature=0.0,
134
+ max_new_tokens=200,
135
+ return_full_text=False
136
+ )
137
+
138
+ # Chroma DB and Document Retrieval Setup
139
+ print("Loading Chroma DB for Biden Speech...")
140
+ if not os.path.exists("/tmp/chroma_db"):
141
+ shutil.copytree("./chroma_db", "/tmp/chroma_db")
142
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
143
+ vectordb = Chroma(persist_directory="/tmp/chroma_db", embedding_function=embeddings)
144
+ retriever = vectordb.as_retriever()
145
+
146
+ custom_prompt = PromptTemplate(
147
+ input_variables=["context", "question"],
148
+ template="""You are a helpful AI assistant. Use only the text from the context below to answer the user's question.
149
+ If the answer is not in the context, say "No relevant info found."
150
+ If the question is not in the context, say "No relevant info found."
151
+
152
+ Return only the final answer in one to three sentences.
153
+ Do not restate the question or context.
154
+ Do not include these instructions in your final output.
155
+
156
+ Context:
157
+ {context}
158
+
159
+ Question: {question}
160
+
161
+ Answer:
162
+ """
163
+ )
164
+
165
+ llm_local = HuggingFacePipeline(pipeline=query_pipeline)
166
+ llm_gpt4 = ChatOpenAI(model_name="gpt-4o-mini", temperature=0.2, openai_api_key=openai_api_key)
167
+ crew_llm = ChatOpenAI(
168
+ model_name="gpt-4o-mini",
169
+ temperature=0.2,
170
+ openai_api_key=openai_api_key
171
+ )
172
+
173
+ memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
174
+ qa_gpt = ConversationalRetrievalChain.from_llm(
175
+ llm=llm_gpt4,
176
+ retriever=retriever,
177
+ memory=memory,
178
+ combine_docs_chain_kwargs={"prompt": custom_prompt}
179
+ )
180
+
181
+ # Helper Function: Extract file path from uploaded file
182
+ def get_file_path(file):
183
+ if isinstance(file, str):
184
+ return file
185
+ elif isinstance(file, dict):
186
+ # Prefer using the "data" key, then "name"
187
+ return file.get("data", file.get("name", None))
188
+ elif hasattr(file, "save"):
189
+ temp_dir = mkdtemp()
190
+ file_path = os.path.join(temp_dir, file.name)
191
+ file.save(file_path)
192
+ return file_path
193
+ else:
194
+ return None
195
+
196
+ # Original functionalities (Tabs 1-4) functions
197
+ def rag_llama_qa(query):
198
+ output = RetrievalQA.from_chain_type(
199
+ llm=llm_local,
200
+ chain_type="stuff",
201
+ retriever=retriever,
202
+ return_source_documents=False,
203
+ chain_type_kwargs={"prompt": custom_prompt}
204
+ ).run(query)
205
+ lower_text = output.lower()
206
+ idx = lower_text.find("answer:")
207
+ return output[idx + len("answer:"):].strip() if idx != -1 else output
208
+
209
+ def rag_gpt4_qa(query):
210
+ return qa_gpt.run(query)
211
+
212
+ def upload_and_chat(file, query):
213
+ file_path = get_file_path(file)
214
+ if file_path is None:
215
+ return "Unable to obtain the uploaded file path."
216
+ if file_path.lower().endswith(".pdf"):
217
+ loader = PyPDFLoader(file_path)
218
+ elif file_path.lower().endswith(".docx"):
219
+ loader = UnstructuredWordDocumentLoader(file_path)
220
+ else:
221
+ loader = TextLoader(file_path)
222
+ docs = loader.load()
223
+ chunks = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(docs)
224
+ db = FAISS.from_documents(chunks, embeddings)
225
+ temp_retriever = db.as_retriever()
226
+ qa_temp = RetrievalQA.from_chain_type(
227
+ llm=llm_gpt4,
228
+ chain_type="stuff",
229
+ retriever=temp_retriever,
230
+ return_source_documents=False,
231
+ chain_type_kwargs={"prompt": custom_prompt}
232
+ )
233
+ return qa_temp.run(query)
234
+
235
+ initial_prompt = PromptTemplate(
236
+ input_variables=["text"],
237
+ template="""Write a concise and structured summary of the following content. Focus on capturing the main ideas and key details:
238
+
239
+ {text}
240
+
241
+ --- Summary ---
242
+ """
243
+ )
244
+ refine_prompt = PromptTemplate(
245
+ input_variables=["existing_answer", "text"],
246
+ template="""You already have an existing summary:
247
+ {existing_answer}
248
+
249
+ Refine the summary based on the new content below. Add or update information only if it's relevant. Keep it concise:
250
+
251
+ {text}
252
+
253
+ --- Refined Summary ---
254
+ """
255
+ )
256
+
257
+ def document_summarize(file):
258
+ file_path = get_file_path(file)
259
+ if file_path is None:
260
+ return "Unable to obtain the uploaded file."
261
+ if file_path.lower().endswith(".pdf"):
262
+ loader = PyPDFLoader(file_path)
263
+ elif file_path.lower().endswith(".docx"):
264
+ loader = UnstructuredWordDocumentLoader(file_path)
265
+ else:
266
+ loader = TextLoader(file_path)
267
+ docs = loader.load()
268
+ summarize_chain = load_summarize_chain(llm_gpt4, chain_type="refine", question_prompt=initial_prompt, refine_prompt=refine_prompt)
269
+ summary = summarize_chain.invoke(docs)
270
+ return summary['output_text']
271
+
272
+ def csv_agent(file, query):
273
+ file_path = get_file_path(file)
274
+ if file_path is None:
275
+ return "Unable to obtain the uploaded CSV file."
276
+ try:
277
+ with open(file_path, 'rb') as f:
278
+ result = chardet.detect(f.read())
279
+ encoding = result['encoding']
280
+ df = pd.read_csv(file_path, encoding=encoding)
281
+ except Exception as e:
282
+ return f"Error reading CSV: {e}"
283
+ safe_dict = {"df": df}
284
+ try:
285
+ result = ne.evaluate(query, local_dict=safe_dict)
286
+ return str(result)
287
+ except Exception as e:
288
+ return f"Query error: {e}"
289
+
290
+ def search_web(query):
291
+ if isinstance(query, dict):
292
+ query = query.get("query", "")
293
+ api_key = os.environ.get("SERPAPI_API_KEY")
294
+ if not api_key:
295
+ return "SERPAPI_API_KEY not set. Please set the environment variable."
296
+ params = {"engine": "google", "q": query, "api_key": api_key, "num": 5}
297
+ search = GoogleSearch(params)
298
+ results = search.get_dict()
299
+ if "organic_results" in results:
300
+ raw_output = ""
301
+ for result in results["organic_results"]:
302
+ title = result.get("title", "No Title")
303
+ link = result.get("link", "No Link")
304
+ snippet = result.get("snippet", "No Snippet")
305
+ raw_output += f"Title: {title}\nLink: {link}\nSnippet: {snippet}\n\n"
306
+ prompt = "Summarize the following search results in a concise, human-friendly way:\n" + raw_output
307
+ summarized = _general_chat(prompt)
308
+ return summarized if summarized else raw_output.strip()
309
+ else:
310
+ return "No results found."
311
+
312
+ def uploaded_qa(file, query):
313
+ file_path = get_file_path(file)
314
+ if file_path is None:
315
+ return "Unable to obtain the uploaded file path."
316
+ if file_path.lower().endswith(".pdf"):
317
+ loader = PyPDFLoader(file_path)
318
+ elif file_path.lower().endswith(".docx"):
319
+ loader = UnstructuredWordDocumentLoader(file_path)
320
+ else:
321
+ loader = TextLoader(file_path)
322
+ docs = loader.load()
323
+ chunks = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(docs)
324
+ db = FAISS.from_documents(chunks, embeddings)
325
+ temp_retriever = db.as_retriever()
326
+ qa_temp = RetrievalQA.from_chain_type(
327
+ llm=llm_gpt4,
328
+ chain_type="stuff",
329
+ retriever=temp_retriever,
330
+ return_source_documents=False,
331
+ chain_type_kwargs={"prompt": custom_prompt}
332
+ )
333
+ return qa_temp.run(query)
334
+
335
+ # CrewAI Multi-Agent System (Tab 5)
336
+ # Completely abandon langchain.agents.Tool and use CrewAI's @tool decorator to define tools
337
+ from pydantic import BaseModel
338
+ class SimpleQuery(BaseModel):
339
+ query: str
340
+
341
+ def _general_chat(query: str) -> str:
342
+ try:
343
+ response = llm_gpt4.invoke(query)
344
+ if isinstance(response, AIMessage):
345
+ response = response.content # Extract the actual string
346
+ if any(kw in response.lower() for kw in ["i'm not sure", "i don't know", "no information", "can't find"]):
347
+ return _search_web_tool(query)
348
+ return response
349
+ except Exception as e:
350
+ return f"General chat error: {e}"
351
+ @tool("general_chat")
352
+ def general_chat_tool(query: str) -> str:
353
+ """General assistant: Answer general questions without relying on documents."""
354
+ try:
355
+ response = llm_gpt4.invoke(query)
356
+ if isinstance(response, AIMessage):
357
+ response = response.content # Extract the actual string
358
+ if any(kw in response.lower() for kw in ["i'm not sure", "i don't know", "no information", "can't find"]):
359
+ return search_web(query)
360
+ return response
361
+ except Exception as e:
362
+ return f"General chat error: {e}"
363
+
364
+ def location_to_timezone(location: str) -> str:
365
+ try:
366
+ geo = Nominatim(user_agent="time_agent_demo")
367
+ loc = geo.geocode(location)
368
+ if not loc:
369
+ return "Europe/London"
370
+ tf = TimezoneFinder()
371
+ return tf.timezone_at(lng=loc.longitude, lat=loc.latitude) or "Europe/London"
372
+ except Exception:
373
+ return "Europe/London"
374
+
375
+ def get_time_tool(query: str) -> str:
376
+
377
+ # use GPT to find location keyword
378
+ try:
379
+ location_prompt = f"""
380
+ You are a location extractor. Given a user's query about time or date, return the location mentioned in it. If not found, return "London".
381
+
382
+ Examples:
383
+ - "What's the time in Tokyo now?" → Tokyo
384
+ - "今天台北幾點?" → Taipei
385
+ - "現在在紐約幾點?" → New York
386
+ - "今天幾號?" → London
387
+ - "What date is today?" → London
388
+
389
+ Now process this query: "{query}"
390
+ """
391
+ location_response = llm_gpt4.invoke(location_prompt)
392
+ if isinstance(location_response, AIMessage):
393
+ location = location_response.content.strip()
394
+ else:
395
+ location = str(location_response).strip()
396
+ except Exception as e:
397
+ location = "London"
398
+
399
+ location_key = location.lower()
400
+ tz_str = location_to_timezone(location)
401
+ now = datetime.now(ZoneInfo(tz_str))
402
+
403
+ # return time or date
404
+ q_lower = query.lower()
405
+ if any(k in q_lower for k in ["date", "幾號", "today", "day"]):
406
+ return now.strftime(f"The date in {location.title()} is %B %d, %Y (%A).")
407
+ elif any(k in q_lower for k in ["time", "幾點", "現在"]):
408
+ return now.strftime(f"The time in {location.title()} is %I:%M %p.")
409
+ else:
410
+ return now.strftime(f"The local time in {location.title()} is %I:%M %p on %B %d, %Y.")
411
+
412
+ @tool("time_tl")
413
+ def time_tool(query: str) -> str:
414
+ """Time Agent: Answer time or date queries worldwide using LLM + GeoLocator + TimezoneFinder."""
415
+ # use GPT to find location keyword
416
+ try:
417
+ location_prompt = f"""
418
+ You are a location extractor. Given a user's query about time or date, return the location mentioned in it. If not found, return "London".
419
+
420
+ Examples:
421
+ - "What's the time in Tokyo now?" → Tokyo
422
+ - "今天台北幾點?" → Taipei
423
+ - "現在在紐約幾點?" → New York
424
+ - "今天幾號?" → London
425
+ - "What date is today?" → London
426
+
427
+ Now process this query: "{query}"
428
+ """
429
+ location_response = llm_gpt4.invoke(location_prompt)
430
+ if isinstance(location_response, AIMessage):
431
+ location = location_response.content.strip()
432
+ else:
433
+ location = str(location_response).strip()
434
+ except Exception as e:
435
+ location = "London"
436
+
437
+ location_key = location.lower()
438
+ tz_str = zone_map.get(location_key, "Europe/London")
439
+ now = datetime.now(ZoneInfo(tz_str))
440
+
441
+ # return time or date
442
+ q_lower = query.lower()
443
+ if any(k in q_lower for k in ["date", "幾號", "today", "day"]):
444
+ return now.strftime(f"The date in {location.title()} is %B %d, %Y (%A).")
445
+ elif any(k in q_lower for k in ["time", "幾點", "現在"]):
446
+ return now.strftime(f"The time in {location.title()} is %I:%M %p.")
447
+ else:
448
+ return now.strftime(f"The local time in {location.title()} is %I:%M %p on %B %d, %Y.")
449
+
450
+ weather_api_key = os.environ.get("WEATHER_API_KEY")
451
+
452
+
453
+ def get_time_tool2(query: str) -> datetime:
454
+ try:
455
+ # Step 1: 抽出地點
456
+ location_prompt = f"""
457
+ You are a location extractor. Given a user's query about time or date, return the location mentioned in it.
458
+ If not found, return "London".
459
+
460
+ Query: "{query}"
461
+ """
462
+ location_response = llm_gpt4.invoke(location_prompt)
463
+ location = location_response.content.strip() if isinstance(location_response, AIMessage) else str(location_response).strip()
464
+
465
+ # Step 2: 當地目前時間(加入 DEBUG)
466
+ print(f"[DEBUG] Extracted Location: {location}")
467
+ tz_str = location_to_timezone(location)
468
+ print(f"[DEBUG] Timezone: {tz_str}")
469
+ now = datetime.now(ZoneInfo(tz_str))
470
+ print(f"[DEBUG] Local Time at {location}: {now}")
471
+
472
+ # Step 3: 動態 few-shot prompt(每次更新 based on now)
473
+ examples = [
474
+ ("five hours later", now + timedelta(hours=5)),
475
+ ("later", now + timedelta(hours=2)),
476
+ ("soon", now + timedelta(minutes=30)),
477
+ ("shortly", now + timedelta(minutes=15)),
478
+ ("after a while", now + timedelta(hours=1)),
479
+ ("tomorrow at 3pm", now.replace(hour=15, minute=0, second=0) + timedelta(days=1)),
480
+ ("the day after tomorrow at 10am", now.replace(hour=10, minute=0, second=0) + timedelta(days=2)),
481
+ ("last Monday 9am", (now - timedelta(days=(now.weekday() + 7))).replace(hour=9, minute=0, second=0)),
482
+ ("next Monday", (now + timedelta(days=(7 - now.weekday()))).replace(hour=12, minute=0, second=0)),
483
+ ("last Friday", (now - timedelta(days=(now.weekday() - 4 + 7) % 7)).replace(hour=12, minute=0, second=0)),
484
+ ("next Friday", (now + timedelta(days=(4 - now.weekday() + 7) % 7)).replace(hour=12, minute=0, second=0)),
485
+ ("in 10 hours", now + timedelta(hours=10)),
486
+ ("this weekend", (now + timedelta(days=(5 - now.weekday()) % 7)).replace(hour=10, minute=0, second=0)),
487
+ ("next weekend", (now + timedelta(days=((5 - now.weekday()) % 7) + 7)).replace(hour=10, minute=0, second=0)),
488
+ ("下週一下午三點", (now + timedelta(days=(7 - now.weekday() + 0) % 7)).replace(hour=15, minute=0, second=0)),
489
+ ("昨天下午五點", (now - timedelta(days=1)).replace(hour=17, minute=0, second=0)),
490
+ ("昨天早上八點", (now - timedelta(days=1)).replace(hour=8, minute=0, second=0)),
491
+ ("later this evening", now.replace(hour=20, minute=0, second=0)),
492
+ ("現在", now),
493
+ ("last month", (now - timedelta(days=30)).replace(hour=12, minute=0, second=0)),
494
+ ("early tomorrow morning", now.replace(hour=6, minute=0, second=0) + timedelta(days=1)),
495
+ ("in 2 hours", now + timedelta(hours=2)),
496
+ ("in one hour", now + timedelta(hours=1)),
497
+ ("in 30 minutes", now + timedelta(minutes=30)),
498
+ ("in a few minutes", now + timedelta(minutes=10)),
499
+ ]
500
+
501
+ # 加入 local time 說明在 Examples 區段
502
+ examples_header = f"""Assume the current local time in {location} is exactly:
503
+ **{now.strftime('%Y-%m-%d %H:%M:%S')}** (timezone: {tz_str})
504
+
505
+ Use this exact time to reason all examples below.
506
+ """
507
+ examples_str = "\n".join([f'User Query: "{q}" → {dt.strftime("%Y-%m-%d %H:%M:%S")}' for q, dt in examples])
508
+
509
+ # Step 4: 构建完整 prompt
510
+ # Step 4: 构建完整 prompt
511
+ time_query_prompt = f"""
512
+ You are a timezone-aware time reasoner. Based on the user's query, calculate the **exact target time** they are referring to.
513
+ Remember: all relative expressions like "later", "in 2 hours", "tomorrow" must be strictly calculated based on the current local time above.
514
+ {examples_header}
515
+
516
+ Please return the result in this **exact format**: `YYYY-MM-DD HH:MM:SS` (24-hour clock, no timezone info).
517
+ Only return the time string — no explanation, no extra words.
518
+
519
+ ### Examples:
520
+ {examples_str}
521
+
522
+ ### Now process:
523
+ User Query: "{query}"
524
+
525
+ """
526
+
527
+ time_response = llm_gpt4.invoke(time_query_prompt)
528
+ time_str = time_response.content.strip() if isinstance(time_response, AIMessage) else str(time_response).strip()
529
+
530
+ # Step 5: 嘗試解析時間
531
+ try:
532
+ target_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
533
+ target_time = target_time.replace(tzinfo=ZoneInfo(tz_str))
534
+ return target_time
535
+ except Exception:
536
+ return f"Failed to parse time string from LLM: '{time_str}'"
537
+
538
+ except Exception as e:
539
+ return f"Error in retrieving location or time information: {e}"
540
+
541
+
542
+ def weather_agent_tool(query: str) -> str:
543
+ """Weather Agent: Return current, hourly, or historical weather info using WeatherAPI."""
544
+ try:
545
+ weather_api_key = os.environ.get("WEATHER_API_KEY")
546
+ if not weather_api_key:
547
+ return "Weather API key not found. Please set WEATHER_API_KEY env variable."
548
+
549
+ # Step 1: Extract location
550
+ location_prompt = f"""
551
+ You are a location extractor. Given a user's query about weather, extract the location mentioned in it.
552
+ If not found, return "London".
553
+
554
+ Examples:
555
+ - "Is it gonna rain in Tokyo?" → Tokyo
556
+ - "Will it be hot in New York later?" → New York
557
+ - "明天下午高雄會不會下雨?" → Kaohsiung
558
+ - "How’s the weather?" → London
559
+
560
+ Query: "{query}"
561
+ """
562
+ location_resp = llm_gpt4.invoke(location_prompt)
563
+ location = location_resp.content.strip() if isinstance(location_resp, AIMessage) else str(location_resp).strip()
564
+
565
+ # Step 2: Get timezone and time
566
+ target_dt = get_time_tool2(query)
567
+
568
+ # if isinstance(target_dt, str):
569
+ # target_dt = datetime.strptime(target_dt, "%Y-%m-%d %H:%M:%S")
570
+ if not isinstance(target_dt, datetime):
571
+ return f"Failed to parse the target time from your query. Got: {target_dt}"
572
+
573
+ tz_str = location_to_timezone(location)
574
+ target_dt = target_dt.replace(tzinfo=ZoneInfo(tz_str))
575
+ now = datetime.now(ZoneInfo(tz_str)) # 用同一時區的 now 去比較!
576
+
577
+ # Step 3: Check limits and decide API
578
+ if target_dt < now - timedelta(days=7):
579
+ return "Only supports up to 7 days of historical data."
580
+ elif target_dt > now + timedelta(days=2):
581
+ return "Only supports up to 3 days of forecast."
582
+
583
+ if target_dt < now:
584
+ url = f"http://api.weatherapi.com/v1/history.json?key={weather_api_key}&q={location}&dt={target_dt.strftime('%Y-%m-%d')}"
585
+ else:
586
+ url = f"http://api.weatherapi.com/v1/forecast.json?key={weather_api_key}&q={location}&days=3&aqi=no&alerts=no"
587
+
588
+ data = requests.get(url).json()
589
+ forecast_hours = []
590
+ if "forecast" in data:
591
+ for day in data["forecast"]["forecastday"]:
592
+ for hour in day["hour"]:
593
+ forecast_hours.append(hour)
594
+ elif "forecastday" in data:
595
+ forecast_hours = data["forecastday"][0]["hour"]
596
+ else:
597
+ return "No forecast data available."
598
+
599
+ # Step 4: Find closest hour
600
+ min_diff = float("inf")
601
+ closest_hour = None
602
+ for hour_data in forecast_hours:
603
+ hour_dt = date_parser.parse(hour_data["time"]).replace(tzinfo=ZoneInfo(tz_str))
604
+ diff = abs((hour_dt - target_dt).total_seconds())
605
+ if diff < min_diff:
606
+ min_diff = diff
607
+ closest_hour = hour_data
608
+
609
+ if not closest_hour:
610
+ return f"No hourly data found for {target_dt.strftime('%Y-%m-%d %H:%M')}."
611
+
612
+ # Step 5: Generate summary
613
+ condition = closest_hour["condition"]["text"]
614
+ temp = closest_hour["temp_c"]
615
+ feels = closest_hour["feelslike_c"]
616
+ humidity = closest_hour["humidity"]
617
+ chance_rain = closest_hour.get("chance_of_rain", 0)
618
+ hour_str = closest_hour["time"].split(" ")[1]
619
+
620
+ summary_prompt = f"""
621
+ Summarise this weather forecast naturally:
622
+ Location: {location}
623
+ Time: {target_dt.strftime('%Y-%m-%d')} at {hour_str}
624
+ Condition: {condition}
625
+ Temp: {temp}°C (Feels like {feels}°C)
626
+ Humidity: {humidity}%
627
+ Chance of rain: {chance_rain}%
628
+
629
+ Make it short, friendly, and human-style.
630
+ """
631
+ response = llm_gpt4.invoke(summary_prompt)
632
+ return response.content.strip() if isinstance(response, AIMessage) else str(response)
633
+
634
+ except Exception as e:
635
+ return f"Weather Agent Error: {e}"
636
+
637
+
638
+ @tool("weather")
639
+ def weather_tool(query: str) -> str:
640
+ """Weather Agent: Return current, hourly, or historical weather info using WeatherAPI."""
641
+ try:
642
+ weather_api_key = os.environ.get("WEATHER_API_KEY")
643
+ if not weather_api_key:
644
+ return "Weather API key not found. Please set WEATHER_API_KEY env variable."
645
+
646
+ # Step 1: Extract location
647
+ location_prompt = f"""
648
+ You are a location extractor. Given a user's query about weather, extract the location mentioned in it.
649
+ If not found, return "London".
650
+
651
+ Examples:
652
+ - "Is it gonna rain in Tokyo?" → Tokyo
653
+ - "Will it be hot in New York later?" → New York
654
+ - "明天下午高雄會不會下雨?" → Kaohsiung
655
+ - "How’s the weather?" → London
656
+
657
+ Query: "{query}"
658
+ """
659
+ location_resp = llm_gpt4.invoke(location_prompt)
660
+ location = location_resp.content.strip() if isinstance(location_resp, AIMessage) else str(location_resp).strip()
661
+
662
+ # Step 2: Get timezone and time
663
+ target_dt = get_time_tool2(query)
664
+
665
+ # if isinstance(target_dt, str):
666
+ # target_dt = datetime.strptime(target_dt, "%Y-%m-%d %H:%M:%S")
667
+ if not isinstance(target_dt, datetime):
668
+ return f"Failed to parse the target time from your query. Got: {target_dt}"
669
+
670
+ tz_str = location_to_timezone(location)
671
+ target_dt = target_dt.replace(tzinfo=ZoneInfo(tz_str))
672
+ now = datetime.now(ZoneInfo(tz_str)) # 用同一時區的 now 去比較!
673
+
674
+ # Step 3: Check limits and decide API
675
+ if target_dt < now - timedelta(days=7):
676
+ return "Only supports up to 7 days of historical data."
677
+ elif target_dt > now + timedelta(days=2):
678
+ return "Only supports up to 3 days of forecast."
679
+
680
+ if target_dt < now:
681
+ url = f"http://api.weatherapi.com/v1/history.json?key={weather_api_key}&q={location}&dt={target_dt.strftime('%Y-%m-%d')}"
682
+ else:
683
+ url = f"http://api.weatherapi.com/v1/forecast.json?key={weather_api_key}&q={location}&days=3&aqi=no&alerts=no"
684
+
685
+ data = requests.get(url).json()
686
+ forecast_hours = []
687
+ if "forecast" in data:
688
+ for day in data["forecast"]["forecastday"]:
689
+ for hour in day["hour"]:
690
+ forecast_hours.append(hour)
691
+ elif "forecastday" in data:
692
+ forecast_hours = data["forecastday"][0]["hour"]
693
+ else:
694
+ return "No forecast data available."
695
+
696
+ # Step 4: Find closest hour
697
+ min_diff = float("inf")
698
+ closest_hour = None
699
+ for hour_data in forecast_hours:
700
+ hour_dt = date_parser.parse(hour_data["time"]).replace(tzinfo=ZoneInfo(tz_str))
701
+ diff = abs((hour_dt - target_dt).total_seconds())
702
+ if diff < min_diff:
703
+ min_diff = diff
704
+ closest_hour = hour_data
705
+
706
+ if not closest_hour:
707
+ return f"No hourly data found for {target_dt.strftime('%Y-%m-%d %H:%M')}."
708
+
709
+ # Step 5: Generate summary
710
+ condition = closest_hour["condition"]["text"]
711
+ temp = closest_hour["temp_c"]
712
+ feels = closest_hour["feelslike_c"]
713
+ humidity = closest_hour["humidity"]
714
+ chance_rain = closest_hour.get("chance_of_rain", 0)
715
+ hour_str = closest_hour["time"].split(" ")[1]
716
+
717
+ summary_prompt = f"""
718
+ Summarise this weather forecast naturally:
719
+ Location: {location}
720
+ Time: {target_dt.strftime('%Y-%m-%d')} at {hour_str}
721
+ Condition: {condition}
722
+ Temp: {temp}°C (Feels like {feels}°C)
723
+ Humidity: {humidity}%
724
+ Chance of rain: {chance_rain}%
725
+
726
+ Make it short, friendly, and human-style.
727
+ """
728
+ response = llm_gpt4.invoke(summary_prompt)
729
+ return response.content.strip() if isinstance(response, AIMessage) else str(response)
730
+
731
+ except Exception as e:
732
+ return f"Weather Agent Error: {e}"
733
+
734
+ @tool("summarise")
735
+ def summarise_tool(query: str) -> str:
736
+ """Summarise: Use document summarisation functionality."""
737
+ global session_retriever, session_qa_chain
738
+ if session_retriever is None:
739
+ return "No document uploaded."
740
+ try:
741
+ docs = session_retriever.get_relevant_documents(query if query.strip() else "summary")
742
+ if not docs:
743
+ return "No relevant content found in the document."
744
+ summarize_chain = load_summarize_chain(llm_gpt4, chain_type="refine", question_prompt=initial_prompt, refine_prompt=refine_prompt)
745
+ summary = summarize_chain.invoke(docs)
746
+ return summary['output_text']
747
+ except Exception as e:
748
+ return f"Summarisation error: {e}"
749
+
750
+ def _calc_tool(query: str) -> str:
751
+ import math
752
+ import re
753
+ try:
754
+ # Handle pure arithmetic expressions (only numbers and symbols)
755
+ if re.fullmatch(r"[0-9\.\+\-\*/%\^\(\)\s]+", query.strip()):
756
+ cleaned = query.strip().replace("^", "**")
757
+ result = ne.evaluate(cleaned)
758
+ return f"The result is: {result}"
759
+
760
+ # For expressions containing sin/cos/log etc., automatically apply math + radians
761
+ expr = query.lower()
762
+ expr = re.sub(r'sin\(([^)]+)\)', r'sin(math.radians(\1))', expr)
763
+ expr = re.sub(r'cos\(([^)]+)\)', r'cos(math.radians(\1))', expr)
764
+ expr = re.sub(r'tan\(([^)]+)\)', r'tan(math.radians(\1))', expr)
765
+ expr = expr.replace("^", "**")
766
+
767
+ result = eval(expr, {"__builtins__": None}, {
768
+ "math": math, "sin": math.sin, "cos": math.cos, "tan": math.tan,
769
+ "log": math.log10, "sqrt": math.sqrt, "exp": math.exp,
770
+ "pi": math.pi, "e": math.e
771
+ })
772
+ return f"The result is: {result}"
773
+
774
+ except Exception:
775
+ try:
776
+ # Fallback: ask GPT to calculate and explain briefly in plain English (avoid messy symbols)
777
+ response = llm_gpt4.invoke(f"Please calculate this and explain briefly in plain English: {query}. Avoid math symbols like $ or \\n or \\(.")
778
+ result = response.content if isinstance(response, AIMessage) else response
779
+ result = re.sub(r"\\\[.*?\\\]", "", result) # Remove \[...\]
780
+ result = re.sub(r"\\\(.*?\\\)", "", result) # Remove \(...\)
781
+ return result.strip()
782
+ except Exception as e:
783
+ return f"Natural language fallback error: {e}"
784
+
785
+ @tool("python_calc")
786
+ def python_calc_tool(query: str) -> str:
787
+ """Python Calculation: Perform basic arithmetic or logical operations."""
788
+ try:
789
+ result = ne.evaluate(query)
790
+ return str(result)
791
+ except Exception as e:
792
+ return f"Calculation error: {e}"
793
+ def _search_web_tool(query: str) -> str:
794
+ return search_web(query)
795
+ @tool("search_tool")
796
+ def search_tool_func(query: str) -> str:
797
+ """Search: Perform web searches using external search engines."""
798
+ return search_web(query)
799
+
800
+ @tool("uploaded_qa")
801
+ def uploaded_qa_tool_func(query: str) -> str:
802
+ """Document QA: Answer questions based on the uploaded document content."""
803
+ global session_qa_chain
804
+ if session_qa_chain is not None:
805
+ try:
806
+ return session_qa_chain.run(query)
807
+ except Exception as e:
808
+ return f"Document QA error: {e}"
809
+ else:
810
+ return "No document uploaded."
811
+
812
+ @tool("csv_agent")
813
+ def csv_tool_func(query: str) -> str:
814
+ """CSV Agent: Use natural language to analyse uploaded CSV files."""
815
+ global csv_dataframe
816
+ if csv_dataframe is None:
817
+ return "No CSV file uploaded."
818
+ try:
819
+ agent = create_pandas_dataframe_agent(llm=llm_gpt4, df=csv_dataframe, verbose=True)
820
+ return agent.run(f"Here is the table:\n{csv_dataframe.head().to_string(index=False)}\n\n{query}")
821
+ except Exception as e:
822
+ return f"CSV Agent error: {e}"
823
+
824
+ # Establish CrewAI agents (for Tab 5 only)
825
+ general_agent = Agent(
826
+ role="General Assistant",
827
+ goal="Respond to any general query that is not related to documents or CSV files.",
828
+ backstory="You're an intelligent assistant who answers questions about anything general, such as math, dates, or general knowledge.",
829
+ tools=[general_chat_tool],
830
+ verbose=True
831
+ )
832
+ summarizer_agent = Agent(
833
+ role="Document Summarizer",
834
+ goal="Summarise the content of the uploaded document.",
835
+ backstory="You are a professional summarisation expert who can identify key points in long documents.",
836
+ tools=[summarise_tool],
837
+ verbose=True
838
+ )
839
+ document_qa_agent = Agent(
840
+ role="Document QA Specialist",
841
+ goal="Answer questions based on the uploaded document.",
842
+ backstory="You are an expert in document understanding and can accurately extract answers.",
843
+ tools=[uploaded_qa_tool_func],
844
+ verbose=True
845
+ )
846
+
847
+ search_agent = Agent(
848
+ role="Search Expert",
849
+ goal="Search the web and provide relevant information.",
850
+ backstory="You are an expert at finding relevant information from the internet.",
851
+ tools=[search_tool_func],
852
+ verbose=True
853
+ )
854
+ time_agent = Agent(
855
+ role="Time Assistant",
856
+ goal="Answer current time or date related questions across different time zones.",
857
+ backstory="You're a time-aware agent who can tell time or date in any major city.",
858
+ tools=[time_tool],
859
+ verbose=True
860
+ )
861
+
862
+ weather_agent = Agent(
863
+ role="Weather Expert",
864
+ goal="Answer global weather queries.",
865
+ backstory="You are a weather analyst who provides accurate and real-time weather information for any location.",
866
+ tools=[weather_tool],
867
+ verbose=True
868
+ )
869
+
870
+ math_agent = Agent(
871
+ role="Math Assistant",
872
+ goal="Perform accurate arithmetic or logical calculations.",
873
+ backstory="You are a calculator expert skilled at quick computations.",
874
+ tools=[python_calc_tool],
875
+ verbose=True
876
+ )
877
+ csv_agent = Agent(
878
+ role="CSV Analyst",
879
+ goal="Analyse tabular data and answer questions about the uploaded CSV file.",
880
+ backstory="You are skilled in interpreting tabular datasets and can extract numerical or logical insights.",
881
+ tools=[csv_tool_func],
882
+ verbose=True
883
+ )
884
+ router_agent = Agent(
885
+ role="Query Router",
886
+ goal="Determine the most suitable agent or tool to handle the user query.",
887
+ backstory="You are an intelligent query dispatcher that analyses the user's intent and chooses the best AI agent to answer.",
888
+ tools=[python_calc_tool, search_tool_func, csv_tool_func, uploaded_qa_tool_func, summarise_tool, general_chat_tool, time_tool, weather_tool],
889
+ verbose=True
890
+ )
891
+ router_task = Task(
892
+ description="""
893
+ Based on the user's query, decide which agent or tool is best suited to handle it:
894
+ - If the query is related to the content of an uploaded file (e.g., 'what is this document about?'), send it to the **Document QA Agent**.
895
+ - If the query contains words like 'summarize', 'summary', or 'main points', use the **Summarizer Agent**.
896
+ - If the query **includes any numbers or symbols** (like +, -, *, /, %, ^), or **mentions math terms** (like 'calculate', 'how much', 'percent', 'square root', 'log', 'cos', 'sin', etc.), or starts with 'what is', 'what’s', 'how much is', assume it is a **math question** and send it to the **Math Agent**.
897
+ - If the user uploaded a CSV file and asks about table content, data trends, or uses words like 'data', 'table', 'csv', 'column', or 'row', send it to the **CSV Agent**.
898
+ - If the user asks about current events, trending topics, or online information (e.g., 'What is LangChain?', 'latest news'), send it to the **Search Agent**.
899
+ - If the query is about current date, time, or day of week (e.g., 'what is today's date?', 'what time is it?', 'what day is it?', '現在幾點', '今天幾號', '禮拜幾'), send it to the **Time Agent**.
900
+ - If the query is about weather, rain, temperature, or forecasts (e.g., "What's the weather in Paris?", "Will it rain tomorrow in London?"), send it to the **Weather Agent**.
901
+ - If the question is general and not related to documents, calculations, CSVs, or the internet (e.g., 'Who are you?', 'Tell me a fun fact'), send it to the **General Agent**.
902
+ - If none of these apply, use your best judgment to choose the most relevant agent.
903
+ """,
904
+ expected_output="The final answer from the selected agent or tool.",
905
+ agent=router_agent,
906
+ input_variables=["query"]
907
+ )
908
+
909
+ crew = Crew(
910
+ agents=[general_agent, summarizer_agent, document_qa_agent, search_agent, math_agent, time_agent, csv_agent, weather_agent],
911
+ tasks=[router_task],
912
+ process=Process.sequential,
913
+ verbose=True,
914
+ llm=crew_llm
915
+ )
916
+
917
+ def multi_agent_chat_advanced(query: str, file=None) -> str:
918
+ global session_retriever, session_qa_chain, csv_dataframe
919
+
920
+ # Smart routing without needing uploaded files
921
+ lower_query = query.lower()
922
+
923
+ math_keywords = ["how much", "calculate", "what is", "what’s", "%", "sin", "cos", "log", "sqrt", "^", "*", "/", "+", "-", "="]
924
+ if any(k in lower_query for k in math_keywords):
925
+ return _calc_tool(query)
926
+
927
+ date_keywords = ["what date", "today", "what time", "what day", "current time", "date", "現在幾點", "今天幾號", "禮拜幾"]
928
+ if any(k in lower_query for k in date_keywords):
929
+ return get_time_tool(query)
930
+ weather_keywords = ["weather", "rain", "snow", "cold", "hot", "cloudy", "sunny", "temperature", "forecast", "天氣", "會不會下雨", "冷嗎", "熱嗎", "氣溫"]
931
+ if any(k in lower_query for k in weather_keywords):
932
+ return weather_agent_tool(query)
933
+ search_keywords = ["latest", "news", "startup", "startups", "company", "companies", "top", "trending", "in 2025", "in 2024", "tell me"]
934
+ if any(k in lower_query for k in search_keywords):
935
+ return search_web(query)
936
+
937
+ general_keywords = ["who are you", "what is your name", "what can you do", "fun fact"]
938
+ if any(k in lower_query for k in general_keywords):
939
+ return _general_chat(query)
940
+
941
+ # Check if file exists and determine its format
942
+ file_path = get_file_path(file) if file is not None else None
943
+
944
+ # Determine if the query should be processed as document-related
945
+ non_doc_keywords = ["calculate", "sum", "date", "time", "how many", "how much", "weather", "temperature"]
946
+ use_file_chain = not any(kw in query.lower() for kw in non_doc_keywords)
947
+
948
+ # Step 3: If a file is uploaded
949
+ if file_path:
950
+ file_lower = file_path.lower()
951
+
952
+ # Process CSV
953
+ if file_lower.endswith(".csv"):
954
+ try:
955
+ with open(file_path, 'rb') as f:
956
+ result = chardet.detect(f.read())
957
+ encoding = result['encoding']
958
+ df = pd.read_csv(file_path, encoding=encoding)
959
+ csv_dataframe = df # Ensure global assignment
960
+
961
+ # If query mentions file, add context
962
+ if "file" in query.lower() or "upload" in query.lower():
963
+ query = f"The user uploaded the following CSV file:\n\n{query}"
964
+
965
+ result = crew.kickoff(inputs={"query": query})
966
+ return safe_format_result(result)
967
+ except Exception as e:
968
+ return f"CSV Parsing Error: {e}"
969
+
970
+ # 3-2: Process PDF / DOCX / TXT
971
+ elif file_lower.endswith((".pdf", ".txt", ".docx")):
972
+ try:
973
+ loader = (
974
+ PyPDFLoader(file_path) if file_lower.endswith(".pdf")
975
+ else UnstructuredWordDocumentLoader(file_path) if file_lower.endswith(".docx")
976
+ else TextLoader(file_path)
977
+ )
978
+ docs = loader.load()
979
+ chunks = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(docs)
980
+ db = FAISS.from_documents(chunks, embeddings)
981
+ session_retriever = db.as_retriever()
982
+ session_qa_chain = ConversationalRetrievalChain.from_llm(
983
+ llm=llm_gpt4,
984
+ retriever=session_retriever,
985
+ memory=ConversationBufferMemory(memory_key="chat_history", return_messages=True),
986
+ )
987
+
988
+ # If the query is summary-related, use Summarize Chain
989
+ if any(kw in query.lower() for kw in ["summarize", "summary", "summarise", "summarisation", "summarization", "摘要", "總結"]):
990
+ return document_summarize(file_path)
991
+
992
+ # If using QA Chain is appropriate
993
+ if use_file_chain:
994
+ try:
995
+ return session_qa_chain.run(query)
996
+ except Exception as e:
997
+ return f"Document QA Error: {e}"
998
+
999
+ # Otherwise, proceed with Multi-Agent reasoning
1000
+ if "file" in query.lower() or "upload" in query.lower():
1001
+ query = f"The user uploaded the following document:\n\n{query}"
1002
+
1003
+ result = crew.kickoff(inputs={"query": query})
1004
+ return safe_format_result(result)
1005
+
1006
+ except Exception as e:
1007
+ return f"Document Processing Error: {e}"
1008
+
1009
+ else:
1010
+ return "Unsupported file format."
1011
+
1012
+ # Step 4: If no file is uploaded, directly use CrewAI reasoning
1013
+ try:
1014
+ result = crew.kickoff(inputs={"query": query})
1015
+ return safe_format_result(result)
1016
+ except Exception as e:
1017
+ return f"Multi-Agent Error: {e}"
1018
+
1019
+ # Gradio Interface Settings
1020
+ demo_description = """
1021
+ **Context**:
1022
+ This demo uses a **Retrieval-Augmented Generation (RAG)** system based on
1023
+ Biden’s 2023 State of the Union Address.
1024
+ All responses are grounded in this document.
1025
+ If no relevant information is found in the document, the system will say "No relevant info found."
1026
+
1027
+ **Sample Questions**:
1028
+ 1. What were the main topics regarding infrastructure in this speech?
1029
+ 2. How does the speech address the competition with China?
1030
+ 3. What does Biden say about job growth in the past two years?
1031
+ 4. Does the speech mention anything about Social Security or Medicare?
1032
+ 5. What does the speech propose regarding Big Tech or online privacy?
1033
+
1034
+ *Note: The LLaMA module generates responses based solely on the current query without follow-up memory or chat history management.*
1035
+
1036
+ Feel free to ask any question related to Biden’s 2023 State of the Union Address.
1037
+ """
1038
+ demo_description2 = """
1039
+ **Context**:
1040
+ This demo uses a **Retrieval-Augmented Generation (RAG)** system based on
1041
+ Biden’s 2023 State of the Union Address.
1042
+ All responses are grounded in this document.
1043
+ If no relevant information is found in the document, the system will say "No relevant info found."
1044
+
1045
+ **Sample Questions**:
1046
+ 1. What were the main topics regarding infrastructure in this speech?
1047
+ 2. How does the speech address the competition with China?
1048
+ 3. What does Biden say about job growth in the past two years?
1049
+ 4. Does the speech mention anything about Social Security or Medicare?
1050
+ 5. What does the speech propose regarding Big Tech or online privacy?
1051
+
1052
+ *Note: The GPT module supports follow-up questions with conversation history management, enabling more interactive and context-aware discussions.*
1053
+
1054
+ Feel free to ask any question related to Biden’s 2023 State of the Union Address.
1055
+ """
1056
+ demo_description3 = """
1057
+ **Context**:
1058
+ Upload a PDF, TXT, or DOCX file and ask a question about its content.
1059
+ This demo uses **GPT-4o-Mini** to answer questions based on the content of your uploaded document.
1060
+
1061
+ Feel free to ask any question related to your document.
1062
+ """
1063
+ demo_description4 = """
1064
+ **Context**:
1065
+ This demo uses a **refinement-based document summarisation chain**.
1066
+ Upload a PDF, TXT, or DOCX file to get a concise, structured summary of its contents.
1067
+ """
1068
+ demo_description5 = """
1069
+ **Context**:
1070
+ This demo presents a GPT-style Multi-Agent AI Assistant, built with **LangChain, CrewAI**, and **RAG (Retrieval-Augmented Generation)**. The system automatically understands your intent and routes the query to the best expert agent, enabling dynamic **multi-agent orchestration**.
1071
+
1072
+ **Supported features**:
1073
+ - 📄 **Document Summarisation** (PDF, DOCX, TXT)
1074
+ - ❓ **FAQ-style Q&A based on uploaded documents** (RAG-based)
1075
+ - 🌐 **Live Web Search** (Online RAG + GPT post-processing summary)
1076
+ - 📅 **Real-time Worldwide Date & Time** (LLM + GeoLocator + TimezoneFinder, supports any city globally)
1077
+ - 🌦️ **Global Weather** (LLM Time Reasoning + Timezone + Few-Shot, supports fuzzy queries, 3-day forecast, 7-day history, hourly precision)
1078
+ - ➗ **Math and Logic Calculations** (from scientific equations to financial or tax-related use cases)
1079
+ - 💬 **General Chatting / Reasoning**
1080
+
1081
+ **Sample Questions**:
1082
+ 1. Summarise the document. *(→ Summarisation Agent)*
1083
+ 2. What are the key ideas mentioned in this file? *(→ RAG QA Agent)*
1084
+ 3. What is LangChain used for? | What are the latest trends in AI startups in 2025? | Tell me the most recent breakthrough in quantum computing. *(→ Online Rag Agent)*
1085
+ 4. What's the current time in London? | What’s today’s date in New York? | What time is it in Taipei right now? *(→ Time Agent)*
1086
+ 5. Will it rain in New York in 2 hours? | Is it going to be hot tomorrow in Nottingham? | What was the weather like in Paris two days ago? | Is it gonna rain later? *(→ Weather Agent)*
1087
+ 6. If I earn $15 per hour and work 8 hours a day for 5 days, how much will I earn? | What is 5 * 22.5 / sin(45) | 3^3 + 4^2 | Calculate 25 * log(1000) | What is the square root of 144 *(→ Math Agent)*
1088
+ 7. Who are you? | What can you do? | What is the meaning of life? *(→ General Chat Agent)*
1089
+
1090
+ Feel free to upload a document and ask related questions, or just type a question directly—no file upload required. *Note: CSV file analysis and auto visualisation is coming soon.*
1091
+ """
1092
+
1093
+ demo = gr.TabbedInterface(
1094
+ interface_list=[
1095
+ gr.Interface(
1096
+ fn=multi_agent_chat_advanced,
1097
+ inputs=[
1098
+ gr.Textbox(label="Enter your query"),
1099
+ gr.File(label="Upload file (CSV, PDF, TXT, DOCX)", file_count="single")
1100
+ ],
1101
+ outputs="text",
1102
+ title="Multi-Agent AI Assistant",
1103
+ allow_flagging="never",
1104
+ description=demo_description5
1105
+ ),
1106
+ gr.Interface(
1107
+ fn=document_summarize,
1108
+ inputs=[gr.File(label="Upload PDF, TXT, or DOCX")],
1109
+ outputs="text",
1110
+ title="Document Summarisation",
1111
+ allow_flagging="never",
1112
+ description=demo_description4
1113
+ ),
1114
+ gr.Interface(
1115
+ fn=upload_and_chat,
1116
+ inputs=[gr.File(label="Upload PDF, TXT, or DOCX"), gr.Textbox(label="Ask a question")],
1117
+ outputs="text",
1118
+ title="Your Docs Q&A (Upload + GPT-4 RAG)",
1119
+ allow_flagging="never",
1120
+ description=demo_description3
1121
+ ),
1122
+ gr.Interface(
1123
+ fn=rag_gpt4_qa,
1124
+ inputs="text",
1125
+ outputs="text",
1126
+ title="Biden Q&A (GPT-4 RAG)",
1127
+ allow_flagging="never",
1128
+ description=demo_description2
1129
+ ),
1130
+ gr.Interface(
1131
+ fn=rag_llama_qa,
1132
+ inputs="text",
1133
+ outputs="text",
1134
+ title="Biden Q&A (LLaMA RAG)",
1135
+ allow_flagging="never",
1136
+ description=demo_description
1137
+ ),
1138
+ ],
1139
+ tab_names=[
1140
+ "Multi-Agent AI Assistant",
1141
+ "Document Summarisation",
1142
+ "Your Docs Q&A (Upload + GPT-4 RAG)",
1143
+ "Biden Q&A (GPT-4 RAG)",
1144
+ "Biden Q&A (LLaMA RAG)"
1145
+ ],
1146
+ title="Smart RAG + Multi-Agent Assistant (with Web + Document AI)"
1147
+ )
1148
+
1149
+ if __name__ == "__main__":
1150
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
biden-sotu-2023-planned-official.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ Mr. Speaker. Madam Vice President. Our First Lady and Second Gentleman. Members of Congress and the Cabinet. Leaders of our military. Mr. Chief Justice, Associate Justices, and retired Justices of the Supreme Court. And you, my fellow Americans. I start tonight by congratulating the members of the 118th Congress and the new Speaker of the House, Kevin McCarthy. Mr. Speaker, I look forward to working together. I also want to congratulate the new leader of the House Democrats and the first Black House Minority Leader in history, Hakeem Jeffries. Congratulations to the longest serving Senate Leader in history, Mitch McConnell. And congratulations to Chuck Schumer for another term as Senate Majority Leader, this time with an even bigger majority. And I want to give special recognition to someone who I think will be considered the greatest Speaker in the history of this country, Nancy Pelosi. The story of America is a story of progress and resilience. Of always moving forward. Of never giving up. A story that is unique among all nations. We are the only country that has emerged from every crisis stronger than when we entered it. That is what we are doing again. Two years ago, our economy was reeling. As I stand here tonight, we have created a record 12 million new jobs, more jobs created in two years than any president has ever created in four years. Two years ago, COVID had shut down our businesses, closed our schools, and robbed us of so much. Today, COVID no longer controls our lives. And two years ago, our democracy faced its greatest threat since the Civil War. Today, though bruised, our democracy remains unbowed and unbroken. As we gather here tonight, we are writing the next chapter in the great American story, a story of progress and resilience. When world leaders ask me to define America, I define our country in one word: Possibilities. You know, we’re often told that Democrats and Republicans can’t work together. But over these past two years, we proved the cynics and the naysayers wrong. Yes, we disagreed plenty. And yes, there were times when Democrats had to go it alone. But time and again, Democrats and Republicans came together. Came together to defend a stronger and safer Europe. Came together to pass a once-in-a-generation infrastructure law, building bridges to connect our nation and people. Came together to pass one of the most significant laws ever, helping veterans exposed to toxic burn pits. In fact, I signed over 300 bipartisan laws since becoming President. From reauthorizing the Violence Against Women Act, to the Electoral Count Reform Act, to the Respect for Marriage Act that protects the right to marry the person you love. To my Republican friends, if we could work together in the last Congress, there is no reason we can’t work together in this new Congress. The people sent us a clear message. Fighting for the sake of fighting, power for the sake of power, conflict for the sake of conflict, gets us nowhere. And that’s always been my vision for our country. To restore the soul of the nation. To rebuild the backbone of America, the middle class. To unite the country. We’ve been sent here to finish the job. For decades, the middle class was hollowed out. Too many good-paying manufacturing jobs moved overseas. Factories at home closed down. Once-thriving cities and towns became shadows of what they used to be. And along the way, something else was lost. Pride. That sense of self-worth. I ran for President to fundamentally change things, to make sure the economy works for everyone so we can all feel pride in what we do. To build an economy from the bottom up and the middle out, not from the top down. Because when the middle class does well, the poor have a ladder up and the wealthy still do very well. We all do well. As my Dad used to say, a job is about a lot more than a paycheck. It’s about your dignity. It’s about respect. It’s about being able to look your kid in the eye and say, “Honey –it’s going to be OK,” and mean it. So, let’s look at the results. Unemployment rate at 3.4%, a 50-year low. Near record low unemployment for Black and Hispanic workers. We’ve already created 800,000 good-paying manufacturing jobs, the fastest growth in 40 years. Where is it written that America can’t lead the world in manufacturing again? For too many decades, we imported products and exported jobs. Now, thanks to all we’ve done, we’re exporting American products and creating American jobs. Inflation has been a global problem because of the pandemic that disrupted supply chains and Putin’s war that disrupted energy and food supplies. But we’re better positioned than any country on Earth. We have more to do, but here at home, inflation is coming down. Here at home, gas prices are down $1.50 a gallon since their peak. Food inflation is coming down. Inflation has fallen every month for the last six months while take home pay has gone up. Additionally, over the last two years, a record 10 million Americans applied to start a new small business. Every time somebody starts a small business, it’s an act of hope. And the Vice President will continue her work to ensure more small businesses can access capital and the historic laws we enacted. Standing here last year, I shared with you a story of American genius and possibility. Semiconductors, the small computer chips the size of your fingertip that power everything from cellphones to automobiles, and so much more. These chips were invented right here in America. America used to make nearly 40% of the world’s chips. But in the last few decades, we lost our edge and we’re down to producing only 10%. We all saw what happened during the pandemic when chip factories overseas shut down. Today’s automobiles need up to 3,000 chips each, but American automakers couldn’t make enough cars because there weren’t enough chips. Car prices went up. So did everything from refrigerators to cellphones. We can never let that happen again. That’s why we came together to pass the bipartisan CHIPS and Science Act. We’re making sure the supply chain for America begins in America. We’ve already created 800,000 manufacturing jobs even without this law. With this new law, we will create hundreds of thousands of new jobs across the country. That’s going to come from companies that have announced more than $300 billion in investments in American manufacturing in the last two years. Outside of Columbus, Ohio, Intel is building semiconductor factories on a thousand acres – a literal field of dreams. That’ll create 10,000 jobs. 7,000 construction jobs. 3,000 jobs once the factories are finished. Jobs paying $130,000 a year, and many don’t require a college degree. Jobs where people don’t have to leave home in search of opportunity. And it’s just getting started. Think about the new homes, new small businesses, and so much more that will come to life. Talk to mayors and Governors, Democrats and Republicans, and they’ll tell you what this means to their communities. We’re seeing these fields of dreams transform the heartland. But to maintain the strongest economy in the world, we also need the best infrastructure in the world. We used to be #1 in the world in infrastructure, then we fell to #13th. Now we’re coming back because we came together to pass the Bipartisan Infrastructure Law, the largest investment in infrastructure since President Eisenhower’s Interstate Highway System. Already, we’ve funded over 20,000 projects, including at major airports from Boston to Atlanta to Portland. These projects will put hundreds of thousands of people to work rebuilding our highways, bridges, railroads, tunnels, ports and airports, clean water, and high-speed internet across America. Urban. Suburban. Rural. Tribal. And we’re just getting started. I sincerely thank my Republican friends who voted for the law. And to my Republican friends who voted against it but still ask to fund projects in their districts, don’t worry. I promised to be the president for all Americans. We’ll fund your projects. And I’ll see you at the ground-breaking. This law will help further unite all of America. Major projects like the Brent Spence bridge between Kentucky and Ohio over the Ohio River. Built 60 years ago. Badly in need of repairs. One of the nation’s most congested freight routes carrying $2 billion worth of freight every day. Folks have been talking about fixing it for decades, but we’re finally going to get it done. I went there last month with Democrats and Republicans from both states to deliver $1.6 billion for this project. While I was there, I met an ironworker named Sara, who is here tonight. For 30 years, she’s been a proud member of Ironworkers Local 44, known as the “cowboys of the sky” who built the Cincinnati skyline. Sara said she can’t wait to be ten stories above the Ohio River building that new bridge. That’s pride. That’s what we’re also building – Pride. We’re also replacing poisonous lead pipes that go into 10 million homes and 400,000 schools and childcare centers, so every child in America can drink clean water. We’re making sure that every community has access to affordable, high-speed internet. No parent should have to drive to a McDonald’s parking lot so their kid can do their homework online. And when we do these projects, we’re going to Buy American. Buy American has been the law of the land since 1933. But for too long, past administrations have found ways to get around it. Not anymore. Tonight, I’m also announcing new standards to require all construction materials used in federal infrastructure projects to be made in America. American-made lumber, glass, drywall, fiber optic cables. And on my watch, American roads, American bridges, and American highways will be made with American products. My economic plan is about investing in places and people that have been forgotten. Amid the economic upheaval of the past four decades, too many people have been left behind or treated like they’re invisible. Maybe that’s you, watching at home. You remember the jobs that went away. And you wonder whether a path even exists anymore for you and your children to get ahead without moving away. I get it. That’s why we’re building an economy where no one is left behind. Jobs are coming back, pride is coming back, because of the choices we made in the last two years. This is a blue-collar blueprint to rebuild America and make a real difference in your lives. For example, too many of you lay in bed at night staring at the ceiling, wondering what will happen if your spouse gets cancer, your child gets sick, or if something happens to you. Will you have the money to pay your medical bills? Will you have to sell the house? I get it. With the Inflation Reduction Act that I signed into law, we’re taking on powerful interests to bring your health care costs down so you can sleep better at night. You know, we pay more for prescription drugs than any major country on Earth. For example, one in ten Americans has diabetes. Every day, millions need insulin to control their diabetes so they can stay alive. Insulin has been around for 100 years. It costs drug companies just $10 a vial to make. But, Big Pharma has been unfairly charging people hundreds of dollars – and making record profits. Not anymore. We capped the cost of insulin at $35 a month for seniors on Medicare. But there are millions of other Americans who are not on Medicare, including 200,000 young people with Type I diabetes who need insulin to save their lives. Let’s finish the job this time. Let’s cap the cost of insulin at $35 a month for every American who needs it. This law also caps out-of-pocket drug costs for seniors on Medicare at a maximum $2,000 per year when there are in fact many drugs, like expensive cancer drugs, that can cost up to $10,000, $12,000, and $14,000 a year. If drug prices rise faster than inflation, drug companies will have to pay Medicare back the difference. And we’re finally giving Medicare the power to negotiate drug prices. Bringing down prescription drug costs doesn’t just save seniors money. It will cut the federal deficit, saving tax payers hundreds of billions of dollars on the prescription drugs the government buys for Medicare. Why wouldn’t we want to do that? Now, some members here are threatening to repeal the Inflation Reduction Act. Make no mistake, if you try to do anything to raise the cost of prescription drugs, I will veto it. I’m pleased to say that more Americans have health insurance now than ever in history. A record 16 million people are enrolled under the Affordable Care Act. Thanks to the law I signed last year, millions are saving $800 a year on their premiums. But the way that law was written, that benefit expires after 2025. Let’s finish the job, make those savings permanent, and expand coverage to those left off Medicaid. Look, the Inflation Reduction Act is also the most significant investment ever to tackle the climate crisis. Lowering utility bills, creating American jobs, and leading the world to a clean energy future. I’ve visited the devastating aftermaths of record floods and droughts, storms and wildfires. In addition to emergency recovery from Puerto Rico to Florida to Idaho, we are rebuilding for the long term. New electric grids able to weather the next major storm. Roads and water systems to withstand the next big flood. Clean energy to cut pollution and create jobs in communities too often left behind. We’re building 500,000 electric vehicle charging stations installed across the country by tens of thousands of IBEW workers. And helping families save more than $1,000 a year with tax credits for the purchase of electric vehicles and energy-efficient appliances. Historic conservation efforts to be responsible stewards of our lands. Let’s face reality. The climate crisis doesn’t care if your state is red or blue. It is an existential threat. We have an obligation to our children and grandchildren to confront it. I’m proud of how America is at last stepping up to the challenge. But there’s so much more to do. We will finish the job. And we pay for these investments in our future by finally making the wealthiest and the biggest corporations begin to pay their fair share. I’m a capitalist. But just pay your fair share. And I think a lot of you at home agree with me that our present tax system is simply unfair. The idea that in 2020, 55 of the biggest companies in America made $40 billion in profits and paid zero in federal income taxes? That’s simply not fair. But now, because of the law I signed, billion-dollar companies have to pay a minimum of 15%. Just 15%. That’s less than a nurse pays. Let me be clear. Under my plan, nobody earning less than $400,000 a year will pay an additional penny in taxes. Nobody. Not one penny. But there’s more to do. Let’s finish the job. Reward work, not just wealth. Pass my proposal for a billionaire minimum tax. Because no billionaire should pay a lower tax rate than a school teacher or a firefighter. You may have noticed that Big Oil just reported record profits. Last year, they made $200 billion in the midst of a global energy crisis. It’s outrageous. They invested too little of that profit to increase domestic production and keep gas prices down. Instead, they used those record profits to buy back their own stock, rewarding their CEOs and shareholders. Corporations ought to do the right thing. That’s why I propose that we quadruple the tax on corporate stock buybacks to encourage long term investments instead. They will still make a considerable profit. Let’s finish the job and close the loopholes that allow the very wealthy to avoid paying their taxes. Instead of cutting the number of audits of wealthy tax payers, I signed a law that will reduce the deficit by $114 billion by cracking down on wealthy tax cheats. That’s being fiscally responsible. In the last two years, my administration cut the deficit by more than $1.7 trillion – the largest deficit reduction in American history. Under the previous administration, America’s deficit went up four years in a row. Because of those record deficits, no president added more to the national debt in any four years than my predecessor. Nearly 25% of the entire national debt, a debt that took 200 years to accumulate, was added by that administration alone. How did Congress respond to all that debt? They lifted the debt ceiling three times without preconditions or crisis. They paid America’s bills to prevent economic disaster for our country. Tonight, I’m asking this Congress to follow suit. Let us commit here tonight that the full faith and credit of the United States of America will never, ever be questioned. Some of my Republican friends want to take the economy hostage unless I agree to their economic plans. All of you at home should know what their plans are. Instead of making the wealthy pay their fair share, some Republicans want Medicare and Social Security to sunset every five years. That means if Congress doesn’t vote to keep them, those programs will go away. Other Republicans say if we don’t cut Social Security and Medicare, they’ll let America default on its debt for the first time in our history. I won’t let that happen. Social Security and Medicare are a lifeline for millions of seniors. Americans have been paying into them with every single paycheck since they started working. So tonight, let’s all agree to stand up for seniors. Stand up and show them we will not cut Social Security. We will not cut Medicare. Those benefits belong to the American people. They earned them. If anyone tries to cut Social Security, I will stop them. And if anyone tries to cut Medicare, I will stop them. I will not allow them to be taken away. Not today. Not tomorrow. Not ever. Next month when I offer my fiscal plan, I ask my Republican friends to offer their plan. We can sit down together and discuss both plans together. My plan will lower the deficit by $2 trillion. I won’t cut a single Social Security or Medicare benefit. In fact, I will extend the Medicare Trust Fund by at least two decades. I will not raise taxes on anyone making under $400,000 a year. And I will pay for the ideas I’ve talked about tonight by making the wealthy and big corporations begin to pay their fair share. Look, here’s the deal. Big corporations aren’t just taking advantage of the tax code. They’re taking advantage of you, the American consumer. Here’s my message to all of you out there: I have your back. We’re already preventing insurance companies from sending surprise medical bills, stopping 1 million surprise bills a month. We’re protecting seniors’ lives and life savings by cracking down on nursing homes that commit fraud, endanger patient safety, or prescribe drugs they don’t need. Millions of Americans can now save thousands of dollars because they can finally get hearing aids over-the-counter without a prescription. Capitalism without competition is not capitalism. It is exploitation. Last year I cracked down on foreign shipping companies that were making you pay higher prices for everyday goods coming into our country. I signed a bipartisan bill that cut shipping costs by 90%, helping American farmers, businesses, and consumers. Let’s finish the job. Pass bipartisan legislation to strengthen antitrust enforcement and prevent big online platforms from giving their own products an unfair advantage. My administration is also taking on “junk” fees, those hidden surcharges too many businesses use to make you pay more. For example, we’re making airlines show you the full ticket price upfront and refund your money if your flight is cancelled or delayed. We’ve reduced exorbitant bank overdraft fees, saving consumers more than $1 billion a year. We’re cutting credit card late fees by 75%, from $30 to $8. Junk fees may not matter to the very wealthy, but they matter to most folks in homes like the one I grew up in. They add up to hundreds of dollars a month. They make it harder for you to pay the bills or afford that family trip. I know how unfair it feels when a company overcharges you and gets away with it. Not anymore. We’ve written a bill to stop all that. It’s called the Junk Fee Prevention Act. We’ll ban surprise “resort fees” that hotels tack on to your bill. These fees can cost you up to $90 a night at hotels that aren’t even resorts. We’ll make cable internet and cellphone companies stop charging you up to $200 or more when you decide to switch to another provider. We’ll cap service fees on tickets to concerts and sporting events and make companies disclose all fees upfront. And we’ll prohibit airlines from charging up to $50 roundtrip for families just to sit together. Baggage fees are bad enough – they can’t just treat your child like a piece of luggage. Americans are tired of being played for suckers. Pass the Junk Fee Prevention Act so companies stop ripping us off. For too long, workers have been getting stiffed. Not anymore. We’re beginning to restore the dignity of work. For example, 30 million workers had to sign non-compete agreements when they took a job. So a cashier at a burger place can’t cross the street to take the same job at another burger place to make a couple bucks more. Not anymore. We’re banning those agreements so companies have to compete for workers and pay them what they’re worth. I’m so sick and tired of companies breaking the law by preventing workers from organizing. Pass the PRO Act because workers have a right to form a union. And let’s guarantee all workers a living wage. Let’s also make sure working parents can afford to raise a family with sick days, paid family and medical leave, and affordable child care that will enable millions more people to go to work. Let’s also restore the full Child Tax Credit, which gave tens of millions of parents some breathing room and cut child poverty in half, to the lowest level in history. And by the way, when we do all of these things, we increase productivity. We increase economic growth. Let’s also finish the job and get more families access to affordable and quality housing. Let’s get seniors who want to stay in their homes the care they need to do so. And give a little more breathing room to millions of family caregivers looking after their loved ones. Pass my plan so we get seniors and people with disabilities the home care services they need and support the workers who are doing God’s work. These plans are fully paid for and we can afford to do them. Restoring the dignity of work also means making education an affordable ticket to the middle class. When we made 12 years of public education universal in the last century, it made us the best-educated, best-prepared nation in the world. But the world has caught up. Jill, who teaches full-time, has an expression: “Any nation that out-educates us will out-compete us.” Folks, you all know 12 years is not enough to win the economic competition for the 21st Century. If you want America to have the best-educated workforce, let’s finish the job by providing access to pre-school for 3- and 4-year-olds. Studies show that children who go to pre-school are nearly 50% more likely to finish high school and go on to earn a 2- or 4-year degree, no matter their background. Let’s give public school teachers a raise. And we’re making progress by reducing student debt and increasing Pell Grants for working- and middle-class families. Let’s finish the job, connect students to career opportunities starting in high school and provide two years of community college, some of the best career training in America, in addition to being a pathway to a four-year degree. Let’s offer every American the path to a good career whether they go to college or not. And folks, in the midst of the COVID crisis when schools were closed, let’s also recognize how far we’ve come in the fight against the pandemic itself. While the virus is not gone, thanks to the resilience of the American people, we have broken COVID’s grip on us. COVID deaths are down nearly 90%. We’ve saved millions of lives and opened our country back up. And soon we’ll end the public health emergency. But we will remember the toll and pain that will never go away for so many. More than 1 million Americans have lost their lives to COVID. Families grieving. Children orphaned. Empty chairs at the dining room table. We remember them, and we remain vigilant. We still need to monitor dozens of variants and support new vaccines and treatments. So Congress needs to fund these efforts and keep America safe. And as we emerge from this crisis stronger, I’m also doubling down on prosecuting criminals who stole relief money meant to keep workers and small businesses afloat during the pandemic. Before I came to office many inspector generals who protect taxpayer dollars were sidelined. Fraud was rampant. Last year, I told you the watchdogs are back. Since then, we’ve recovered billions of taxpayer dollars. Now, let’s triple our anti-fraud strike forces going after these criminals, double the statute of limitations on these crimes, and crack down on identity fraud by criminal syndicates stealing billions of dollars from the American people. For every dollar we put into fighting fraud, taxpayers get back at least ten times as much. COVID left other scars, like the spike in violent crime in 2020, the first year of the pandemic. We have an obligation to make sure all our people are safe. Public safety depends on public trust. But too often that trust is violated. Joining us tonight are the parents of Tyre Nichols, who had to bury him just last week. There are no words to describe the heartbreak and grief of losing a child. But imagine what it’s like to lose a child at the hands of the law. Imagine having to worry whether your son or daughter will come home from walking down the street or playing in the park or just driving their car. I’ve never had to have the talk with my children – Beau, Hunter, and Ashley – that so many Black and Brown families have had with their children. If a police officer pulls you over, turn on your interior lights. Don’t reach for your license. Keep your hands on the steering wheel. Imagine having to worry like that every day in America. Here’s what Tyre’s mom shared with me when I asked her how she finds the courage to carry on and speak out. With faith in God, she said her son “was a beautiful soul and something good will come from this.” Imagine how much courage and character that takes. It’s up to us. It’s up to all of us. We all want the same thing. Neighborhoods free of violence. Law enforcement who earn the community’s trust. Our children to come home safely. Equal protection under the law; that’s the covenant we have with each other in America. And we know police officers put their lives on the line every day, and we ask them to do too much. To be counselors, social workers, psychologists; responding to drug overdoses, mental health crises, and more. We ask too much of them. I know most cops are good, decent people. They risk their lives every time they put on that shield. But what happened to Tyre in Memphis happens too often. We have to do better. Give law enforcement the training they need, hold them to higher standards, and help them succeed in keeping everyone safe. We also need more first responders and other professionals to address growing mental health and substance abuse challenges. More resources to reduce violent crime and gun crime; more community intervention programs; more investments in housing, education, and job training. All this can help prevent violence in the first place. And when police officers or departments violate the public’s trust, we must hold them accountable. With the support of families of victims, civil rights groups, and law enforcement, I signed an executive order for all federal officers banning chokeholds, restricting no-knock warrants, and other key elements of the George Floyd Act. Let’s commit ourselves to make the words of Tyre’s mother come true, something good must come from this. All of us in this chamber, we need to rise to this moment. We can’t turn away. Let’s do what we know in our hearts we need to do. Let’s come together and finish the job on police reform. Do something. That was the same plea of parents who lost their children in Uvalde: Do something on gun violence. Thank God we did, passing the most sweeping gun safety law in three decades. That includes things that the majority of responsible gun owners support, like enhanced background checks for 18 to 21-year-olds and red flag laws keeping guns out of the hands of people who are a danger to themselves and others. But we know our work is not done. Joining us tonight is Brandon Tsay, a 26-year-old hero. Brandon put off his college dreams to stay by his mom’s side as she was dying from cancer. He now works at a dance studio started by his grandparents. Two weeks ago, during Lunar New Year celebrations, he heard the studio’s front door close and saw a man pointing a gun at him. He thought he was going to die, but then he thought about the people inside. In that instant, he found the courage to act and wrestled the semi-automatic pistol away from a gunman who had already killed 11 people at another dance studio. He saved lives. It’s time we do the same as well. Ban assault weapons once and for all. We did it before. I led the fight to ban them in 1994. In the 10 years the ban was law, mass shootings went down. After Republicans let it expire, mass shootings tripled. Let’s finish the job and ban assault weapons again. And let’s also come together on immigration and make it a bipartisan issue like it was before. We now have a record number of personnel working to secure the border, arresting 8,000 human smugglers and seizing over 23,000 pounds of fentanyl in just the last several months. Since we launched our new border plan last month, unlawful migration from Cuba, Haiti, Nicaragua, and Venezuela has come down 97%. But America’s border problems won’t be fixed until Congress acts. If you won’t pass my comprehensive immigration reform, at least pass my plan to provide the equipment and officers to secure the border. And a pathway to citizenship for Dreamers, those on temporary status, farm workers, and essential workers. Here in the people’s House, it’s our duty to protect all the people’s rights and freedoms. Congress must restore the right the Supreme Court took away last year and codify Roe v. Wade to protect every woman’s constitutional right to choose. The Vice President and I are doing everything we can to protect access to reproductive health care and safeguard patient privacy. But already, more than a dozen states are enforcing extreme abortion bans. Make no mistake; if Congress passes a national abortion ban, I will veto it. Let’s also pass the bipartisan Equality Act to ensure LGBTQ Americans, especially transgender young people, can live with safety and dignity. Our strength is not just the example of our power, but the power of our example. Let’s remember the world is watching. I spoke from this chamber one year ago, just days after Vladimir Putin unleashed his brutal war against Ukraine. A murderous assault, evoking images of the death and destruction Europe suffered in World War II. Putin’s invasion has been a test for the ages. A test for America. A test for the world. Would we stand for the most basic of principles? Would we stand for sovereignty? Would we stand for the right of people to live free from tyranny? Would we stand for the defense of democracy? For such a defense matters to us because it keeps the peace and prevents open season for would-be aggressors to threaten our security and prosperity. One year later, we know the answer. Yes, we would. And yes, we did. Together, we did what America always does at our best. We led. We united NATO and built a global coalition. We stood against Putin’s aggression. We stood with the Ukrainian people. Tonight, we are once again joined by Ukraine’s Ambassador to the United States. She represents not just her nation, but the courage of her people. Ambassador, America is united in our support for your country. We will stand with you as long as it takes. Our nation is working for more freedom, more dignity, and more peace,not just in Europe, but everywhere. Before I came to office, the story was about how the People’s Republic of China was increasing its power and America was falling in the world. Not anymore. I’ve made clear with President Xi that we seek competition, not conflict. I will make no apologies that we are investing to make America strong. Investing in American innovation, in industries that will define the future, and that China’s government is intent on dominating. Investing in our alliances and working with our allies to protect our advanced technologies so they’re not used against us. Modernizing our military to safeguard stability and deter aggression. Today, we’re in the strongest position in decades to compete with China or anyone else in the world. I am committed to work with China where it can advance American interests and benefit the world. But make no mistake: as we made clear last week, if China’s threatens our sovereignty, we will act to protect our country. And we did. And let’s be clear: winning the competition with China should unite all of us. We face serious challenges across the world. But in the past two years, democracies have become stronger, not weaker. Autocracies have grown weaker, not stronger. America is rallying the world again to meet those challenges, from climate and global health, to food insecurity, to terrorism and territorial aggression. Allies are stepping up, spending more and doing more. And bridges are forming between partners in the Pacific and those in the Atlantic. And those who bet against America are learning just how wrong they are. It’s never a good bet to bet against America. When I came to office, most everyone assumed bipartisanship was impossible. But I never believed it. That’s why a year ago, I offered a Unity Agenda for the nation. We’ve made real progress. Together, we passed a law making it easier for doctors to prescribe effective treatments for opioid addiction. Passed a gun safety law making historic investments in mental health. Launched ARPA-H to drive breakthroughs in the fight against cancer,Alzheimer’s, diabetes, and so much more. We passed the Heath Robinson PACT Act, named for the late Iraq war veteran whose story about exposure to toxic burn pits I shared here last year. But there is so much more to do. And we can do it together. Joining us tonight is a father named Doug from Newton, New Hampshire. He wrote Jill and me a letter about his daughter Courtney. Contagious laugh. Her sister’s best friend. He shared a story all too familiar to millions of Americans. Courtney discovered pills in high school. It spiraled into addiction and eventually her death from a fentanyl overdose. She was 20 years old. Describing the last eight years without her, Doug said, “There is no worse pain.” Yet their family has turned pain into purpose, working to end stigma and change laws. He told us he wants to “start the journey towards America’s recovery.” Doug, we’re with you. Fentanyl is killing more than 70,000 Americans a year. Let’s launch a major surge to stop fentanyl production, sale, and trafficking, with more drug detection machines to inspect cargo and stop pills and powder at the border. Working with couriers like Fed Ex to inspect more packages for drugs. Strong penalties to crack down on fentanyl trafficking. Second, let’s do more on mental health, especially for our children. When millions of young people are struggling with bullying, violence, trauma, we owe them greater access to mental health care at school. We must finally hold social media companies accountable for the experiment they are running on our children for profit. And it’s time to pass bipartisan legislation to stop Big Tech from collecting personal data on kids and teenagers online, ban targeted advertising to children, and impose stricter limits on the personal data these companies collect on all of us. Third, let’s do more to keep our nation’s one truly sacred obligation: to equip those we send into harm’s way and care for them and their families when they come home. Job training and job placement for veterans and their spouses as they return to civilian life. Helping veterans afford their rent because no one should be homeless in this country, especially not those who served it. And we cannot go on losing 17 veterans a day to the silent scourge of suicide. The VA is doing everything it can, including expanding mental health screenings and a proven program that recruits veterans to help other veterans understand what they’re going through and get the help they need. And fourth, last year Jill and I re-ignited the Cancer Moonshot that President Obama asked me to lead in our Administration. Our goal is to cut the cancer death rate by at least 50% over the next 25 years. Turn more cancers from death sentences into treatable diseases. And provide more support for patients and families. It’s personal for so many of us. Joining us are Maurice and Kandice, an Irishman and a daughter of immigrants from Panama. They met and fell in love in New York City and got married in the same chapel as Jill and I did. Kindred spirits. He wrote us a letter about their little daughter Ava. She was just a year old when she was diagnosed with a rare kidney cancer. 26 blood transfusions. 11 rounds of radiation. 8 rounds of chemo. 1 kidney removed. A 5% survival rate. He wrote how in the darkest moments he thought, “if she goes, I can’t stay.” Jill and I understand, like so many of you. They read how Jill described our family’s cancer journey and how we tried to steal moments of joy where you can. For them, that glimmer of joy was a half-smile from their baby girl. It meant everything. They never gave up hope. Ava never gave up hope. She turns four next month. They just found out that Ava beat the odds and is on her way to being cancer free, and she’s watching from the White House tonight. For the lives we can save and for the lives we have lost, let this be a truly American moment that rallies the country and the world together and proves that we can do big things. Twenty years ago, under the leadership of President Bush and countless advocates and champions, we undertook a bipartisan effort through PEPFAR to transform the global fight against HIV/AIDS. It’s been a huge success. I believe we can do the same with cancer. Let’s end cancer as we know it and cure some cancers once and for all. There’s one reason why we’re able to do all of these things: our democracy itself. It’s the most fundamental thing of all. With democracy, everything is possible. Without it, nothing is. For the last few years our democracy has been threatened, attacked, and put at risk. Put to the test here, in this very room, on January 6th. And then, just a few months ago, unhinged by the Big Lie, an assailant unleashed political violence in the home of the then-Speaker of this House of Representatives. Using the very same language that insurrectionists who stalked these halls chanted on January 6th. Here tonight in this chamber is the man who bears the scars of that brutal attack, but is as tough and strong and as resilient as they get. My friend, Paul Pelosi. But such a heinous act never should have happened. We must all speak out. There is no place for political violence in America. In America, we must protect the right to vote, not suppress that fundamental right. We honor the results of our elections, not subvert the will of the people. We must uphold the rule of the law and restore trust in our institutions of democracy. And we must give hate and extremism in any form no safe harbor. Democracy must not be a partisan issue. It must be an American issue. Every generation of Americans has faced a moment where they have been called on to protect our democracy, to defend it, to stand up for it. And this is our moment. My fellow Americans, we meet tonight at an inflection point. One of those moments that only a few generations ever face, where the decisions we make now will decide the course of this nation and of the world for decades to come. We are not bystanders to history. We are not powerless before the forces that confront us. It is within our power, of We the People. We are facing the test of our time and the time for choosing is at hand. We must be the nation we have always been at our best. Optimistic. Hopeful. Forward-looking. A nation that embraces, light over darkness, hope over fear, unity over division. Stability over chaos. We must see each other not as enemies, but as fellow Americans. We are a good people, the only nation in the world built on an idea. That all of us, every one of us, is created equal in the image of God. A nation that stands as a beacon to the world. A nation in a new age of possibilities. So I have come here to fulfil my constitutional duty to report on the state of the union. And here is my report. Because the soul of this nation is strong, because the backbone of this nation is strong, because the people of this nation are strong, the State of the Union is strong. As I stand here tonight, I have never been more optimistic about the future of America. We just have to remember who we are. We are the United States of America and there is nothing, nothingbeyond our capacity if we do it together. May God bless you all. May God protect our troops.
chroma_db/81a4760c-68ee-43ed-a69d-0242a02b1c19/data_level0.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a13e72541800c513c73dccea69f79e39cf4baef4fa23f7e117c0d6b0f5f99670
3
+ size 3212000
chroma_db/81a4760c-68ee-43ed-a69d-0242a02b1c19/header.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0ec6df10978b056a10062ed99efeef2702fa4a1301fad702b53dd2517103c746
3
+ size 100
chroma_db/81a4760c-68ee-43ed-a69d-0242a02b1c19/length.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fc19b1997119425765295aeab72d76faa6927d4f83985d328c26f20468d6cc76
3
+ size 4000
chroma_db/81a4760c-68ee-43ed-a69d-0242a02b1c19/link_lists.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
3
+ size 0
chroma_db/chroma.sqlite3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2fa9bc2ad76e0104286a0155d01334e7c44fafe35d6518ba15bd02781c900558
3
+ size 15343616
requirements-3.txt ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ transformers==4.49.0
2
+ sentencepiece
3
+ sentence-transformers
4
+ torch
5
+ langchain==0.2.1
6
+ chromadb
7
+ gradio
8
+ huggingface-hub
9
+ faiss-cpu
10
+ openai
11
+ langchain_community
12
+ pypdf
13
+ google-search-results
14
+ pandasai
15
+ crewai
16
+ litellm>=1.35.6
17
+ langchain-experimental
18
+ chardet
19
+ numexpr
20
+ geopy
21
+ timezonefinder
22
+ openmeteo-requests
23
+ requests
24
+ python-dateutil