Spaces:
Running
Running
File size: 13,451 Bytes
25bef75 f060f46 bbe6fe0 2f7f9b6 bbe6fe0 f060f46 25bef75 f060f46 25bef75 c62316d f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 2f7f9b6 bbe6fe0 f060f46 874f037 f060f46 874f037 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 21f5be1 25bef75 f060f46 21f5be1 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 25bef75 f060f46 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 2f7f9b6 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 c3e36e2 bbe6fe0 25bef75 f060f46 bbe6fe0 f060f46 bbe6fe0 f060f46 bbe6fe0 f060f46 25bef75 c62316d bbe6fe0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
import argparse
import os
import threading
import sys
import logging
from io import StringIO
from contextlib import redirect_stdout, redirect_stderr
from dotenv import load_dotenv
from huggingface_hub import login
from scripts.text_inspector_tool import TextInspectorTool
from scripts.text_web_browser import (
ArchiveSearchTool,
FinderTool,
FindNextTool,
PageDownTool,
PageUpTool,
SimpleTextBrowser,
VisitTool,
)
from scripts.visual_qa import visualizer
from smolagents import (
CodeAgent,
ToolCallingAgent,
LiteLLMModel,
DuckDuckGoSearchTool,
Tool,
)
AUTHORIZED_IMPORTS = [
"shell_gpt", "sgpt", "openai", "requests", "zipfile", "os", "pandas", "numpy", "sympy", "json", "bs4",
"pubchempy", "xml", "yahoo_finance", "Bio", "sklearn", "scipy", "pydub",
"yaml", "string", "secrets", "io", "PIL", "chess", "PyPDF2", "pptx", "torch", "datetime", "fractions", "csv",
]
append_answer_lock = threading.Lock()
class StreamingHandler(logging.Handler):
"""Custom logging handler that captures agent logs"""
def __init__(self):
super().__init__()
self.callbacks = []
self.buffer = []
def add_callback(self, callback):
self.callbacks.append(callback)
def emit(self, record):
msg = self.format(record)
self.buffer.append(msg + '\n')
for callback in self.callbacks:
callback(msg + '\n')
class StreamingCapture:
"""Captures stdout/stderr and yields content in real-time"""
def __init__(self):
self.content = []
self.callbacks = []
def add_callback(self, callback):
self.callbacks.append(callback)
def write(self, text):
if text.strip():
self.content.append(text)
for callback in self.callbacks:
callback(text)
return len(text)
def flush(self):
pass
def create_agent(
model_id="gpt-4o-mini",
hf_token=None,
openai_api_key=None,
serpapi_key=None,
api_endpoint=None,
custom_api_endpoint=None,
custom_api_key=None,
search_provider="serper",
search_api_key=None,
custom_search_url=None
):
print("[DEBUG] Creating agent with model_id:", model_id)
if hf_token:
print("[DEBUG] Logging into HuggingFace")
login(hf_token)
model_params = {
"model_id": model_id,
"custom_role_conversions": {"tool-call": "assistant", "tool-response": "user"},
"max_completion_tokens": 8192,
}
if model_id == "gpt-4o-mini":
model_params["reasoning_effort"] = "high"
if custom_api_endpoint and custom_api_key:
print("[DEBUG] Using custom API endpoint:", custom_api_endpoint)
model_params["base_url"] = custom_api_endpoint
model_params["api_key"] = custom_api_key
model = LiteLLMModel(**model_params)
print("[DEBUG] Model initialized")
text_limit = 100000
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"
browser_config = {
"viewport_size": 1024 * 5,
"downloads_folder": "downloads_folder",
"request_kwargs": {
"headers": {"User-Agent": user_agent},
"timeout": 300,
},
"serpapi_key": serpapi_key,
}
os.makedirs(f"./{browser_config['downloads_folder']}", exist_ok=True)
browser = SimpleTextBrowser(**browser_config)
print("[DEBUG] Browser initialized")
# Correct tool selection
if search_provider == "searxng":
print("[DEBUG] Using SearxNG-compatible DuckDuckGoSearchTool with base_url override")
search_tool = DuckDuckGoSearchTool()
if custom_search_url:
search_tool.base_url = custom_search_url # Override default DuckDuckGo URL (only if supported)
else:
print("[DEBUG] Using default DuckDuckGoSearchTool for Serper/standard search")
search_tool = DuckDuckGoSearchTool()
WEB_TOOLS = [
search_tool,
VisitTool(browser),
PageUpTool(browser),
PageDownTool(browser),
FinderTool(browser),
FindNextTool(browser),
ArchiveSearchTool(browser),
TextInspectorTool(model, text_limit),
]
text_webbrowser_agent = ToolCallingAgent(
model=model,
tools=WEB_TOOLS,
max_steps=20,
verbosity_level=3,
planning_interval=4,
name="search_agent",
description="A team member that will search the internet to answer your question.",
provide_run_summary=True,
)
text_webbrowser_agent.prompt_templates["managed_agent"]["task"] += """You can navigate to .txt online files.
If a non-html page is in another format, especially .pdf or a Youtube video, use tool 'inspect_file_as_text' to inspect it.
Additionally, if after some searching you find out that you need more information to answer the question, you can use `final_answer` with your request for clarification as argument to request for more information."""
manager_agent = CodeAgent(
model=model,
tools=[visualizer, TextInspectorTool(model, text_limit)],
max_steps=16,
verbosity_level=3,
additional_authorized_imports=AUTHORIZED_IMPORTS,
planning_interval=4,
managed_agents=[text_webbrowser_agent],
)
print("[DEBUG] Agent fully initialized")
return manager_agent
def run_agent_with_streaming(agent, question, stream_callback=None):
"""Run agent and stream output in real-time"""
# Set up logging capture
log_handler = StreamingHandler()
if stream_callback:
log_handler.add_callback(stream_callback)
# Add handler to root logger and smolagents loggers
root_logger = logging.getLogger()
smolagents_logger = logging.getLogger('smolagents')
# Store original handlers
original_handlers = root_logger.handlers[:]
original_level = root_logger.level
try:
# Configure logging to capture everything
root_logger.setLevel(logging.DEBUG)
root_logger.addHandler(log_handler)
smolagents_logger.setLevel(logging.DEBUG)
smolagents_logger.addHandler(log_handler)
# Also capture stdout/stderr
stdout_capture = StreamingCapture()
stderr_capture = StreamingCapture()
if stream_callback:
stdout_capture.add_callback(stream_callback)
stderr_capture.add_callback(stream_callback)
with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
if stream_callback:
stream_callback(f"[STARTING] Running agent with question: {question}\n")
answer = agent.run(question)
if stream_callback:
stream_callback(f"[COMPLETED] Final answer: {answer}\n")
return answer
except Exception as e:
error_msg = f"[ERROR] Exception occurred: {str(e)}\n"
if stream_callback:
stream_callback(error_msg)
raise
finally:
# Restore original logging configuration
root_logger.handlers = original_handlers
root_logger.setLevel(original_level)
smolagents_logger.removeHandler(log_handler)
def create_gradio_interface():
"""Create Gradio interface with streaming support"""
import gradio as gr
import time
import threading
def process_question(question, model_id, hf_token, serpapi_key, custom_api_endpoint,
custom_api_key, search_provider, search_api_key, custom_search_url):
# Create agent
agent = create_agent(
model_id=model_id,
hf_token=hf_token,
openai_api_key=None,
serpapi_key=serpapi_key,
api_endpoint=None,
custom_api_endpoint=custom_api_endpoint,
custom_api_key=custom_api_key,
search_provider=search_provider,
search_api_key=search_api_key,
custom_search_url=custom_search_url,
)
# Shared state for streaming
output_buffer = []
is_complete = False
def stream_callback(text):
output_buffer.append(text)
def run_agent_async():
nonlocal is_complete
try:
answer = run_agent_with_streaming(agent, question, stream_callback)
output_buffer.append(f"\n\n**FINAL ANSWER:** {answer}")
except Exception as e:
output_buffer.append(f"\n\n**ERROR:** {str(e)}")
finally:
is_complete = True
# Start agent in background thread
agent_thread = threading.Thread(target=run_agent_async)
agent_thread.start()
# Generator that yields updates
last_length = 0
while not is_complete or agent_thread.is_alive():
current_output = "".join(output_buffer)
if len(current_output) > last_length:
yield current_output
last_length = len(current_output)
time.sleep(0.1) # Small delay to prevent excessive updates
# Final yield to ensure everything is captured
final_output = "".join(output_buffer)
if len(final_output) > last_length:
yield final_output
# Create Gradio interface
with gr.Blocks(title="Streaming Agent Chat") as demo:
gr.Markdown("# Streaming Agent Chat Interface")
with gr.Row():
with gr.Column():
question_input = gr.Textbox(label="Question", placeholder="Enter your question here...")
model_id_input = gr.Textbox(label="Model ID", value="gpt-4o-mini")
hf_token_input = gr.Textbox(label="HuggingFace Token", type="password")
serpapi_key_input = gr.Textbox(label="SerpAPI Key", type="password")
custom_api_endpoint_input = gr.Textbox(label="Custom API Endpoint")
custom_api_key_input = gr.Textbox(label="Custom API Key", type="password")
search_provider_input = gr.Dropdown(
choices=["serper", "searxng"],
value="serper",
label="Search Provider"
)
search_api_key_input = gr.Textbox(label="Search API Key", type="password")
custom_search_url_input = gr.Textbox(label="Custom Search URL")
submit_btn = gr.Button("Submit", variant="primary")
with gr.Column():
output = gr.Textbox(
label="Agent Output (Streaming)",
lines=30,
max_lines=50,
interactive=False
)
submit_btn.click(
fn=process_question,
inputs=[
question_input, model_id_input, hf_token_input, serpapi_key_input,
custom_api_endpoint_input, custom_api_key_input, search_provider_input,
search_api_key_input, custom_search_url_input
],
outputs=output,
show_progress=True
)
return demo
def main():
print("[DEBUG] Loading environment variables")
load_dotenv(override=True)
parser = argparse.ArgumentParser()
parser.add_argument("--gradio", action="store_true", help="Launch Gradio interface")
parser.add_argument("question", type=str, nargs='?', help="Question to ask (CLI mode)")
parser.add_argument("--model-id", type=str, default="gpt-4o-mini")
parser.add_argument("--hf-token", type=str, default=os.getenv("HF_TOKEN"))
parser.add_argument("--serpapi-key", type=str, default=os.getenv("SERPAPI_API_KEY"))
parser.add_argument("--custom-api-endpoint", type=str, default=None)
parser.add_argument("--custom-api-key", type=str, default=None)
parser.add_argument("--search-provider", type=str, default="serper")
parser.add_argument("--search-api-key", type=str, default=None)
parser.add_argument("--custom-search-url", type=str, default=None)
args = parser.parse_args()
print("[DEBUG] CLI arguments parsed:", args)
if args.gradio:
# Launch Gradio interface
demo = create_gradio_interface()
demo.launch(share=True)
else:
# CLI mode
if not args.question:
print("Error: Question required for CLI mode")
return
agent = create_agent(
model_id=args.model_id,
hf_token=args.hf_token,
openai_api_key=None, # Fix: was openai_api_token
serpapi_key=args.serpapi_key,
api_endpoint=None, # Fix: was api_endpoint
custom_api_endpoint=args.custom_api_endpoint,
custom_api_key=args.custom_api_key,
search_provider=args.search_provider,
search_api_key=args.search_api_key,
custom_search_url=args.custom_search_url,
)
print("[DEBUG] Running agent...")
def print_stream(text):
print(text, end='', flush=True)
answer = run_agent_with_streaming(agent, args.question, print_stream)
print(f"\n\nGot this answer: {answer}")
if __name__ == "__main__":
main() |