techy-ai commited on
Commit
47bae79
Β·
1 Parent(s): 47e9704

basic agent

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ venv/
2
+ .env
3
+ __pycache__/
agent.py ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from typing import List, Dict, Any, Optional
4
+ import tempfile
5
+ import re
6
+ import json
7
+ import requests
8
+ from urllib.parse import urlparse
9
+ import pytesseract
10
+ from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter
11
+ import cmath
12
+ import pandas as pd
13
+ import uuid
14
+ import numpy as np
15
+ from code_interpreter import CodeInterpreter
16
+ import logging
17
+
18
+ interpreter_instance = CodeInterpreter()
19
+
20
+ from image_processing import *
21
+
22
+ """Langraph"""
23
+ from langgraph.graph import START, StateGraph, MessagesState
24
+ from langchain_community.tools.tavily_search import TavilySearchResults
25
+ from langchain_community.document_loaders import WikipediaLoader
26
+ from langchain_community.document_loaders import ArxivLoader
27
+ from langgraph.prebuilt import ToolNode, tools_condition
28
+ from langchain_google_genai import ChatGoogleGenerativeAI
29
+ from langchain_groq import ChatGroq
30
+ from langchain_huggingface import (
31
+ ChatHuggingFace,
32
+ HuggingFaceEndpoint,
33
+ HuggingFaceEmbeddings,
34
+ )
35
+ from langchain_community.vectorstores import SupabaseVectorStore
36
+ from langchain_core.messages import SystemMessage, HumanMessage
37
+ from langchain_core.tools import tool
38
+ from langchain.tools.retriever import create_retriever_tool
39
+ from supabase.client import Client, create_client
40
+
41
+ load_dotenv()
42
+
43
+ logging.basicConfig(level=logging.INFO)
44
+ logger = logging.getLogger("agent")
45
+
46
+ def tool_response(success: bool, data=None, error=None):
47
+ """Standardized response format for tools."""
48
+ return {
49
+ "status": "success" if success else "error",
50
+ "data": data,
51
+ "error": error
52
+ }
53
+
54
+ from typing import Any
55
+
56
+ @tool
57
+ def multiply(a: Any, b: Any):
58
+ """Multiply two numbers and return the product."""
59
+ logger.info("multiply called with a=%s, b=%s", a, b)
60
+ try:
61
+ a = float(a)
62
+ b = float(b)
63
+ result = a * b
64
+ return tool_response(True, result)
65
+ except Exception as e:
66
+ logger.error("multiply failed: %s", str(e))
67
+ return tool_response(False, error=f"Invalid input: {e}")
68
+
69
+ @tool
70
+ def add(a: Any, b: Any):
71
+ """Add two numbers and return the sum."""
72
+ logger.info("add called with a=%s, b=%s", a, b)
73
+ try:
74
+ a = float(a)
75
+ b = float(b)
76
+ return tool_response(True, a + b)
77
+ except Exception as e:
78
+ logger.error("add failed: %s", str(e))
79
+ return tool_response(False, error=f"Invalid input: {e}")
80
+
81
+ @tool
82
+ def subtract(a: Any, b: Any):
83
+ """Subtract b from a and return the result."""
84
+ logger.info("subtract called with a=%s, b=%s", a, b)
85
+ try:
86
+ a = float(a)
87
+ b = float(b)
88
+ return tool_response(True, a - b)
89
+ except Exception as e:
90
+ logger.error("subtract failed: %s", str(e))
91
+ return tool_response(False, error=f"Invalid input: {e}")
92
+
93
+ @tool
94
+ def divide(a: Any, b: Any):
95
+ """Divide a by b and return the quotient."""
96
+ logger.info("divide called with a=%s, b=%s", a, b)
97
+ try:
98
+ a = float(a)
99
+ b = float(b)
100
+ if b == 0:
101
+ return tool_response(False, error="Division by zero")
102
+ return tool_response(True, a / b)
103
+ except Exception as e:
104
+ logger.error("divide failed: %s", str(e))
105
+ return tool_response(False, error=f"Invalid input: {e}")
106
+
107
+ @tool
108
+ def modulus(a: Any, b: Any):
109
+ """Return the remainder of a divided by b."""
110
+ logger.info("modulus called with a=%s, b=%s", a, b)
111
+ try:
112
+ a = float(a)
113
+ b = float(b)
114
+ return tool_response(True, a % b)
115
+ except Exception as e:
116
+ logger.error("modulus failed: %s", str(e))
117
+ return tool_response(False, error=f"Invalid input: {e}")
118
+
119
+ @tool
120
+ def power(a: Any, b: Any):
121
+ """Raise a to the power of b."""
122
+ logger.info("power called with a=%s, b=%s", a, b)
123
+ try:
124
+ a = float(a)
125
+ b = float(b)
126
+ return tool_response(True, a ** b)
127
+ except Exception as e:
128
+ logger.error("power failed: %s", str(e))
129
+ return tool_response(False, error=f"Invalid input: {e}")
130
+
131
+ @tool
132
+ def square_root(a: Any):
133
+ """Return the square root of a number."""
134
+ logger.info("square_root called with a=%s", a)
135
+ try:
136
+ a = float(a)
137
+ if a < 0:
138
+ # use complex math if negative
139
+ return tool_response(True, str(cmath.sqrt(a)))
140
+ return tool_response(True, a ** 0.5)
141
+ except Exception as e:
142
+ logger.error("square_root failed: %s", str(e))
143
+ return tool_response(False, error=f"Invalid input: {e}")
144
+
145
+ # =========================
146
+ # πŸ“‚ File Tools
147
+ # =========================
148
+
149
+ @tool
150
+ def save_and_read_file(filename: str, content: str):
151
+ """Save content to a file and return the content back."""
152
+ logger.info("save_and_read_file called with filename=%s", filename)
153
+ try:
154
+ with open(filename, "w", encoding="utf-8") as f:
155
+ f.write(content)
156
+ with open(filename, "r", encoding="utf-8") as f:
157
+ result = f.read()
158
+ return tool_response(True, result)
159
+ except Exception as e:
160
+ logger.error("save_and_read_file failed: %s", str(e))
161
+ return tool_response(False, error=f"File error: {e}")
162
+
163
+
164
+ @tool
165
+ def download_file_from_url(url: str):
166
+ """Download a file from a URL and return its local path."""
167
+ logger.info("download_file_from_url called with url=%s", url)
168
+ try:
169
+ if url.startswith("file://"):
170
+ raise ValueError("Local file:// URLs not allowed")
171
+ response = requests.get(url, timeout=10)
172
+ response.raise_for_status()
173
+ filename = os.path.basename(urlparse(url).path) or f"download_{uuid.uuid4()}"
174
+ with open(filename, "wb") as f:
175
+ f.write(response.content)
176
+ return tool_response(True, filename)
177
+ except Exception as e:
178
+ logger.error("download_file_from_url failed: %s", str(e))
179
+ return tool_response(False, error=f"Download error: {e}")
180
+
181
+
182
+ # =========================
183
+ # πŸ–ΌοΈ Image Tools
184
+ # =========================
185
+
186
+ @tool
187
+ def extract_text_from_image(image_path: str):
188
+ """Extract text from an image using OCR."""
189
+ logger.info("extract_text_from_image called with image_path=%s", image_path)
190
+ try:
191
+ text = pytesseract.image_to_string(Image.open(image_path))
192
+ return tool_response(True, text.strip())
193
+ except Exception as e:
194
+ logger.error("extract_text_from_image failed: %s", str(e))
195
+ return tool_response(False, error=f"OCR error: {e}")
196
+
197
+
198
+ @tool
199
+ def analyze_image(image_path: str):
200
+ """Return basic analysis (size, mode) of an image."""
201
+ logger.info("analyze_image called with image_path=%s", image_path)
202
+ try:
203
+ with Image.open(image_path) as img:
204
+ data = {"format": img.format, "mode": img.mode, "size": img.size}
205
+ return tool_response(True, data)
206
+ except Exception as e:
207
+ logger.error("analyze_image failed: %s", str(e))
208
+ return tool_response(False, error=f"Image analysis error: {e}")
209
+
210
+
211
+ @tool
212
+ def transform_image(image_path: str, operation: str):
213
+ """Apply a simple transform (grayscale, blur, sharpen)."""
214
+ logger.info("transform_image called with image_path=%s operation=%s", image_path, operation)
215
+ try:
216
+ img = Image.open(image_path)
217
+ if operation == "grayscale":
218
+ img = img.convert("L")
219
+ elif operation == "blur":
220
+ img = img.filter(ImageFilter.BLUR)
221
+ elif operation == "sharpen":
222
+ img = img.filter(ImageFilter.SHARPEN)
223
+ else:
224
+ raise ValueError(f"Unsupported operation: {operation}")
225
+ output_path = f"transformed_{uuid.uuid4()}.png"
226
+ img.save(output_path)
227
+ return tool_response(True, output_path)
228
+ except Exception as e:
229
+ logger.error("transform_image failed: %s", str(e))
230
+ return tool_response(False, error=f"Transform error: {e}")
231
+
232
+
233
+ @tool
234
+ def draw_on_image(image_path: str, text: str):
235
+ """Draw text on an image."""
236
+ logger.info("draw_on_image called with image_path=%s text=%s", image_path, text)
237
+ try:
238
+ img = Image.open(image_path)
239
+ draw = ImageDraw.Draw(img)
240
+ draw.text((10, 10), text, fill="black")
241
+ output_path = f"drawn_{uuid.uuid4()}.png"
242
+ img.save(output_path)
243
+ return tool_response(True, output_path)
244
+ except Exception as e:
245
+ logger.error("draw_on_image failed: %s", str(e))
246
+ return tool_response(False, error=f"Draw error: {e}")
247
+
248
+
249
+ @tool
250
+ def generate_simple_image(text: str):
251
+ """Generate a simple image with text."""
252
+ logger.info("generate_simple_image called with text=%s", text)
253
+ try:
254
+ img = Image.new("RGB", (200, 100), color="white")
255
+ draw = ImageDraw.Draw(img)
256
+ draw.text((10, 40), text, fill="black")
257
+ output_path = f"generated_{uuid.uuid4()}.png"
258
+ img.save(output_path)
259
+ return tool_response(True, output_path)
260
+ except Exception as e:
261
+ logger.error("generate_simple_image failed: %s", str(e))
262
+ return tool_response(False, error=f"Image generation error: {e}")
263
+
264
+
265
+ @tool
266
+ def combine_images(image1_path: str, image2_path: str):
267
+ """Combine two images side by side."""
268
+ logger.info("combine_images called with %s and %s", image1_path, image2_path)
269
+ try:
270
+ img1 = Image.open(image1_path)
271
+ img2 = Image.open(image2_path)
272
+ combined = Image.new("RGB", (img1.width + img2.width, max(img1.height, img2.height)))
273
+ combined.paste(img1, (0, 0))
274
+ combined.paste(img2, (img1.width, 0))
275
+ output_path = f"combined_{uuid.uuid4()}.png"
276
+ combined.save(output_path)
277
+ return tool_response(True, output_path)
278
+ except Exception as e:
279
+ logger.error("combine_images failed: %s", str(e))
280
+ return tool_response(False, error=f"Combine error: {e}")
281
+
282
+
283
+ # =========================
284
+ # πŸ“Š Data Tools
285
+ # =========================
286
+
287
+ @tool
288
+ def analyze_csv_file(file_path: str):
289
+ """Analyze a CSV file and return basic info."""
290
+ logger.info("analyze_csv_file called with file_path=%s", file_path)
291
+ try:
292
+ df = pd.read_csv(file_path)
293
+ summary = {"shape": df.shape, "columns": df.columns.tolist(), "head": df.head(3).to_dict()}
294
+ return tool_response(True, summary)
295
+ except Exception as e:
296
+ logger.error("analyze_csv_file failed: %s", str(e))
297
+ return tool_response(False, error=f"CSV analysis error: {e}")
298
+
299
+
300
+ @tool
301
+ def analyze_excel_file(file_path: str):
302
+ """Analyze an Excel file and return basic info."""
303
+ logger.info("analyze_excel_file called with file_path=%s", file_path)
304
+ try:
305
+ df = pd.read_excel(file_path)
306
+ summary = {"shape": df.shape, "columns": df.columns.tolist(), "head": df.head(3).to_dict()}
307
+ return tool_response(True, summary)
308
+ except Exception as e:
309
+ logger.error("analyze_excel_file failed: %s", str(e))
310
+ return tool_response(False, error=f"Excel analysis error: {e}")
311
+
312
+
313
+ # =========================
314
+ # πŸ’» Code Tool
315
+ # =========================
316
+
317
+ @tool
318
+ def execute_code_multilang(code: str, language: str = "python"):
319
+ """Execute code in multiple languages using CodeInterpreter."""
320
+ logger.info("execute_code_multilang called with language=%s", language)
321
+ try:
322
+ result = interpreter_instance.execute_code(code, language)
323
+ return tool_response(True, result)
324
+ except Exception as e:
325
+ logger.error("execute_code_multilang failed: %s", str(e))
326
+ return tool_response(False, error=f"Code execution error: {e}")
327
+
328
+ # =========================
329
+ # 🌍 Search Tools
330
+ # =========================
331
+
332
+ @tool
333
+ def web_search(query: str, max_results: int = 3):
334
+ """Perform a web search using TavilySearchResults."""
335
+ logger.info("web_search called with query=%s", query)
336
+ try:
337
+ tavily = TavilySearchResults(max_results=max_results)
338
+ results = tavily.invoke(query)
339
+ return tool_response(True, results)
340
+ except Exception as e:
341
+ logger.error("web_search failed: %s", str(e))
342
+ return tool_response(False, error=f"Web search error: {e}")
343
+
344
+
345
+ @tool
346
+ def wiki_search(query: str):
347
+ """Search Wikipedia and return documents."""
348
+ logger.info("wiki_search called with query=%s", query)
349
+ try:
350
+ loader = WikipediaLoader(query=query, load_max_docs=3)
351
+ docs = loader.load()
352
+ results = [doc.page_content for doc in docs]
353
+ return tool_response(True, results)
354
+ except Exception as e:
355
+ logger.error("wiki_search failed: %s", str(e))
356
+ return tool_response(False, error=f"Wikipedia error: {e}")
357
+
358
+
359
+ @tool
360
+ def arxiv_search(query: str):
361
+ """Search Arxiv and return documents."""
362
+ logger.info("arxiv_search called with query=%s", query)
363
+ try:
364
+ loader = ArxivLoader(query=query, load_max_docs=3)
365
+ docs = loader.load()
366
+ results = [doc.page_content for doc in docs]
367
+ return tool_response(True, results)
368
+ except Exception as e:
369
+ logger.error("arxiv_search failed: %s", str(e))
370
+ return tool_response(False, error=f"Arxiv error: {e}")
371
+
372
+ if __name__ == "__main__":
373
+ logger.info("=== Running Tool Tests ===")
374
+
375
+
376
+ # =========================
377
+ # 🌍 Tested for tools
378
+ # =========================
379
+ # 🌍 Search Tools
380
+ # print("\n--- web_search ---")
381
+ # print(web_search.invoke({"query": "latest AI research", "max_results": 2}))
382
+
383
+ # print("\n--- wiki_search ---")
384
+ # print(wiki_search.invoke({"query": "LangChain"}))
385
+
386
+ # print("\n--- arxiv_search ---")
387
+ # print(arxiv_search.invoke({"query": "transformers"}))
388
+
389
+ # πŸ’» Code Execution
390
+ # print("\n--- execute_code_multilang ---")
391
+ # print(execute_code_multilang.invoke({"code": "print(2+3)", "language": "python"}))
392
+
393
+
394
+ # load the system prompt from the file
395
+ with open("system_prompt.txt", "r", encoding="utf-8") as f:
396
+ system_prompt = f.read()
397
+ print(system_prompt)
398
+
399
+ # System message
400
+ sys_msg = SystemMessage(content=system_prompt)
401
+
402
+ # build a retriever
403
+ embeddings = HuggingFaceEmbeddings(
404
+ model_name="sentence-transformers/all-mpnet-base-v2"
405
+ ) # dim=768
406
+ from dotenv import load_dotenv
407
+ load_dotenv()
408
+ supabase_url = os.environ.get("SUPABASE_URL")
409
+ supabase_key = os.environ.get("SUPABASE_KEY")
410
+ supabase: Client = create_client(
411
+ supabase_url, supabase_key
412
+ )
413
+ vector_store = SupabaseVectorStore(
414
+ client=supabase,
415
+ embedding=embeddings,
416
+ table_name="documents2",
417
+ query_name="match_documents_2",
418
+ )
419
+ create_retriever_tool = create_retriever_tool(
420
+ retriever=vector_store.as_retriever(),
421
+ name="Question Search",
422
+ description="A tool to retrieve similar questions from a vector store.",
423
+ )
424
+
425
+
426
+ tools = [
427
+ web_search,
428
+ wiki_search,
429
+ arxiv_search,
430
+ multiply,
431
+ add,
432
+ subtract,
433
+ divide,
434
+ modulus,
435
+ power,
436
+ square_root,
437
+ save_and_read_file,
438
+ download_file_from_url,
439
+ extract_text_from_image,
440
+ analyze_csv_file,
441
+ analyze_excel_file,
442
+ execute_code_multilang,
443
+ analyze_image,
444
+ transform_image,
445
+ draw_on_image,
446
+ generate_simple_image,
447
+ combine_images,
448
+ ]
449
+
450
+
451
+ # Build graph function
452
+ def build_graph(provider: str = "groq"):
453
+ """Build the graph"""
454
+ # Load environment variables from .env file
455
+ if provider == "groq":
456
+ # Groq https://console.groq.com/docs/models
457
+ llm = ChatGroq(model="qwen/qwen3-32b", temperature=0)
458
+ elif provider == "huggingface":
459
+ # TODO: Add huggingface endpoint
460
+ llm = ChatHuggingFace(
461
+ llm=HuggingFaceEndpoint(
462
+ repo_id="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
463
+ task="text-generation", # for chat‐style use β€œtext-generation”
464
+ max_new_tokens=1024,
465
+ do_sample=False,
466
+ repetition_penalty=1.03,
467
+ temperature=0,
468
+ ),
469
+ verbose=True,
470
+ )
471
+ else:
472
+ raise ValueError("Invalid provider. Choose 'groq' or 'huggingface'.")
473
+ # Bind tools to LLM
474
+ llm_with_tools = llm.bind_tools(tools)
475
+
476
+ # Node
477
+ def assistant(state: MessagesState):
478
+ """Assistant node"""
479
+ return {"messages": [llm_with_tools.invoke(state["messages"])]}
480
+
481
+ def retriever(state: MessagesState):
482
+ """Retriever node"""
483
+ similar_question = vector_store.similarity_search(state["messages"][0].content)
484
+
485
+ if similar_question: # Check if the list is not empty
486
+ example_msg = HumanMessage(
487
+ content=f"Here I provide a similar question and answer for reference: \n\n{similar_question[0].page_content}",
488
+ )
489
+ return {"messages": [sys_msg] + state["messages"] + [example_msg]}
490
+ else:
491
+ # Handle the case when no similar questions are found
492
+ return {"messages": [sys_msg] + state["messages"]}
493
+
494
+ builder = StateGraph(MessagesState)
495
+ builder.add_node("retriever", retriever)
496
+ builder.add_node("assistant", assistant)
497
+ builder.add_node("tools", ToolNode(tools))
498
+ builder.add_edge(START, "retriever")
499
+ builder.add_edge("retriever", "assistant")
500
+ builder.add_conditional_edges(
501
+ "assistant",
502
+ tools_condition,
503
+ )
504
+ builder.add_edge("tools", "assistant")
505
+
506
+ # Compile graph
507
+ return builder.compile()
508
+
509
+
510
+ # test
511
+ if __name__ == "__main__":
512
+ question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?"
513
+ graph = build_graph(provider="groq")
514
+ messages = [HumanMessage(content=question)]
515
+ messages = graph.invoke({"messages": messages})
516
+ for m in messages["messages"]:
517
+ m.pretty_print()
518
+
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import inspect
5
+ import pandas as pd
6
+
7
+ # (Keep Constants as is)
8
+ # --- Constants ---
9
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+
11
+ # --- Basic Agent Definition ---
12
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
+ class BasicAgent:
14
+ def __init__(self):
15
+ print("BasicAgent initialized.")
16
+ def __call__(self, question: str) -> str:
17
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
18
+ fixed_answer = "This is a default answer."
19
+ print(f"Agent returning fixed answer: {fixed_answer}")
20
+ return fixed_answer
21
+
22
+ def run_and_submit_all( profile: gr.OAuthProfile | None):
23
+ """
24
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
25
+ and displays the results.
26
+ """
27
+ # --- Determine HF Space Runtime URL and Repo URL ---
28
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
+
30
+ if profile:
31
+ username= f"{profile.username}"
32
+ print(f"User logged in: {username}")
33
+ else:
34
+ print("User not logged in.")
35
+ return "Please Login to Hugging Face with the button.", None
36
+
37
+ api_url = DEFAULT_API_URL
38
+ questions_url = f"{api_url}/questions"
39
+ submit_url = f"{api_url}/submit"
40
+
41
+ # 1. Instantiate Agent ( modify this part to create your agent)
42
+ try:
43
+ agent = BasicAgent()
44
+ except Exception as e:
45
+ print(f"Error instantiating agent: {e}")
46
+ return f"Error initializing agent: {e}", None
47
+ # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
48
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
49
+ print(agent_code)
50
+
51
+ # 2. Fetch Questions
52
+ print(f"Fetching questions from: {questions_url}")
53
+ try:
54
+ response = requests.get(questions_url, timeout=15)
55
+ response.raise_for_status()
56
+ questions_data = response.json()
57
+ if not questions_data:
58
+ print("Fetched questions list is empty.")
59
+ return "Fetched questions list is empty or invalid format.", None
60
+ print(f"Fetched {len(questions_data)} questions.")
61
+ except requests.exceptions.RequestException as e:
62
+ print(f"Error fetching questions: {e}")
63
+ return f"Error fetching questions: {e}", None
64
+ except requests.exceptions.JSONDecodeError as e:
65
+ print(f"Error decoding JSON response from questions endpoint: {e}")
66
+ print(f"Response text: {response.text[:500]}")
67
+ return f"Error decoding server response for questions: {e}", None
68
+ except Exception as e:
69
+ print(f"An unexpected error occurred fetching questions: {e}")
70
+ return f"An unexpected error occurred fetching questions: {e}", None
71
+
72
+ # 3. Run your Agent
73
+ results_log = []
74
+ answers_payload = []
75
+ print(f"Running agent on {len(questions_data)} questions...")
76
+ for item in questions_data:
77
+ task_id = item.get("task_id")
78
+ question_text = item.get("question")
79
+ if not task_id or question_text is None:
80
+ print(f"Skipping item with missing task_id or question: {item}")
81
+ continue
82
+ try:
83
+ submitted_answer = agent(question_text)
84
+ answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
86
+ except Exception as e:
87
+ print(f"Error running agent on task {task_id}: {e}")
88
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
89
+
90
+ if not answers_payload:
91
+ print("Agent did not produce any answers to submit.")
92
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
+
94
+ # 4. Prepare Submission
95
+ submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
+ print(status_update)
98
+
99
+ # 5. Submit
100
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
101
+ try:
102
+ response = requests.post(submit_url, json=submission_data, timeout=60)
103
+ response.raise_for_status()
104
+ result_data = response.json()
105
+ final_status = (
106
+ f"Submission Successful!\n"
107
+ f"User: {result_data.get('username')}\n"
108
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
109
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
110
+ f"Message: {result_data.get('message', 'No message received.')}"
111
+ )
112
+ print("Submission successful.")
113
+ results_df = pd.DataFrame(results_log)
114
+ return final_status, results_df
115
+ except requests.exceptions.HTTPError as e:
116
+ error_detail = f"Server responded with status {e.response.status_code}."
117
+ try:
118
+ error_json = e.response.json()
119
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
120
+ except requests.exceptions.JSONDecodeError:
121
+ error_detail += f" Response: {e.response.text[:500]}"
122
+ status_message = f"Submission Failed: {error_detail}"
123
+ print(status_message)
124
+ results_df = pd.DataFrame(results_log)
125
+ return status_message, results_df
126
+ except requests.exceptions.Timeout:
127
+ status_message = "Submission Failed: The request timed out."
128
+ print(status_message)
129
+ results_df = pd.DataFrame(results_log)
130
+ return status_message, results_df
131
+ except requests.exceptions.RequestException as e:
132
+ status_message = f"Submission Failed: Network error - {e}"
133
+ print(status_message)
134
+ results_df = pd.DataFrame(results_log)
135
+ return status_message, results_df
136
+ except Exception as e:
137
+ status_message = f"An unexpected error occurred during submission: {e}"
138
+ print(status_message)
139
+ results_df = pd.DataFrame(results_log)
140
+ return status_message, results_df
141
+
142
+
143
+ # --- Build Gradio Interface using Blocks ---
144
+ with gr.Blocks() as demo:
145
+ gr.Markdown("# Basic Agent Evaluation Runner")
146
+ gr.Markdown(
147
+ """
148
+ **Instructions:**
149
+
150
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
153
+
154
+ ---
155
+ **Disclaimers:**
156
+ Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
157
+ This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
158
+ """
159
+ )
160
+
161
+ gr.LoginButton()
162
+
163
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
164
+
165
+ status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
+ # Removed max_rows=10 from DataFrame constructor
167
+ results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
+
169
+ run_button.click(
170
+ fn=run_and_submit_all,
171
+ outputs=[status_output, results_table]
172
+ )
173
+
174
+ if __name__ == "__main__":
175
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
176
+ # Check for SPACE_HOST and SPACE_ID at startup for information
177
+ space_host_startup = os.getenv("SPACE_HOST")
178
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
+
180
+ if space_host_startup:
181
+ print(f"βœ… SPACE_HOST found: {space_host_startup}")
182
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
+ else:
184
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
+
186
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
187
+ print(f"βœ… SPACE_ID found: {space_id_startup}")
188
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
+ else:
191
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
+
193
+ print("-"*(60 + len(" App Starting ")) + "\n")
194
+
195
+ print("Launching Gradio Interface for Basic Agent Evaluation...")
196
+ demo.launch(debug=True, share=False)
code_interpreter.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import sys
4
+ import uuid
5
+ import base64
6
+ import traceback
7
+ import contextlib
8
+ import tempfile
9
+ import subprocess
10
+ import sqlite3
11
+ from typing import Dict, List, Any, Optional, Union
12
+ import numpy as np
13
+ import pandas as pd
14
+ import matplotlib.pyplot as plt
15
+ from PIL import Image
16
+
17
+ class CodeInterpreter:
18
+ def __init__(self, allowed_modules=None, max_execution_time=30, working_directory=None):
19
+ """Initialize the code interpreter with safety measures."""
20
+ self.allowed_modules = allowed_modules or [
21
+ "numpy", "pandas", "matplotlib", "scipy", "sklearn",
22
+ "math", "random", "statistics", "datetime", "collections",
23
+ "itertools", "functools", "operator", "re", "json",
24
+ "sympy", "networkx", "nltk", "PIL", "pytesseract",
25
+ "cmath", "uuid", "tempfile", "requests", "urllib"
26
+ ]
27
+ self.max_execution_time = max_execution_time
28
+ self.working_directory = working_directory or os.path.join(os.getcwd())
29
+ if not os.path.exists(self.working_directory):
30
+ os.makedirs(self.working_directory)
31
+
32
+ self.globals = {
33
+ "__builtins__": __builtins__,
34
+ "np": np,
35
+ "pd": pd,
36
+ "plt": plt,
37
+ "Image": Image,
38
+ }
39
+ self.temp_sqlite_db = os.path.join(tempfile.gettempdir(), "code_exec.db")
40
+
41
+ def execute_code(self, code: str, language: str = "python") -> Dict[str, Any]:
42
+ """Execute the provided code in the selected programming language."""
43
+ language = language.lower()
44
+ execution_id = str(uuid.uuid4())
45
+
46
+ result = {
47
+ "execution_id": execution_id,
48
+ "status": "error",
49
+ "stdout": "",
50
+ "stderr": "",
51
+ "result": None,
52
+ "plots": [],
53
+ "dataframes": []
54
+ }
55
+
56
+ try:
57
+ if language == "python":
58
+ return self._execute_python(code, execution_id)
59
+ elif language == "bash":
60
+ return self._execute_bash(code, execution_id)
61
+ elif language == "sql":
62
+ return self._execute_sql(code, execution_id)
63
+ elif language == "c":
64
+ return self._execute_c(code, execution_id)
65
+ elif language == "java":
66
+ return self._execute_java(code, execution_id)
67
+ else:
68
+ result["stderr"] = f"Unsupported language: {language}"
69
+ except Exception as e:
70
+ result["stderr"] = str(e)
71
+
72
+ return result
73
+
74
+ def _execute_python(self, code: str, execution_id: str) -> dict:
75
+ output_buffer = io.StringIO()
76
+ error_buffer = io.StringIO()
77
+ result = {
78
+ "execution_id": execution_id,
79
+ "status": "error",
80
+ "stdout": "",
81
+ "stderr": "",
82
+ "result": None,
83
+ "plots": [],
84
+ "dataframes": []
85
+ }
86
+
87
+ try:
88
+ exec_dir = os.path.join(self.working_directory, execution_id)
89
+ os.makedirs(exec_dir, exist_ok=True)
90
+ plt.switch_backend('Agg')
91
+
92
+ with contextlib.redirect_stdout(output_buffer), contextlib.redirect_stderr(error_buffer):
93
+ exec_result = exec(code, self.globals)
94
+
95
+ if plt.get_fignums():
96
+ for i, fig_num in enumerate(plt.get_fignums()):
97
+ fig = plt.figure(fig_num)
98
+ img_path = os.path.join(exec_dir, f"plot_{i}.png")
99
+ fig.savefig(img_path)
100
+ with open(img_path, "rb") as img_file:
101
+ img_data = base64.b64encode(img_file.read()).decode('utf-8')
102
+ result["plots"].append({
103
+ "figure_number": fig_num,
104
+ "data": img_data
105
+ })
106
+
107
+ for var_name, var_value in self.globals.items():
108
+ if isinstance(var_value, pd.DataFrame) and len(var_value) > 0:
109
+ result["dataframes"].append({
110
+ "name": var_name,
111
+ "head": var_value.head().to_dict(),
112
+ "shape": var_value.shape,
113
+ "dtypes": str(var_value.dtypes)
114
+ })
115
+
116
+ result["status"] = "success"
117
+ result["stdout"] = output_buffer.getvalue()
118
+ result["result"] = exec_result
119
+
120
+ except Exception as e:
121
+ result["status"] = "error"
122
+ result["stderr"] = f"{error_buffer.getvalue()}\n{traceback.format_exc()}"
123
+
124
+ return result
125
+
126
+ def _execute_bash(self, code: str, execution_id: str) -> dict:
127
+ try:
128
+ completed = subprocess.run(
129
+ code, shell=True, capture_output=True, text=True, timeout=self.max_execution_time
130
+ )
131
+ return {
132
+ "execution_id": execution_id,
133
+ "status": "success" if completed.returncode == 0 else "error",
134
+ "stdout": completed.stdout,
135
+ "stderr": completed.stderr,
136
+ "result": None,
137
+ "plots": [],
138
+ "dataframes": []
139
+ }
140
+ except subprocess.TimeoutExpired:
141
+ return {
142
+ "execution_id": execution_id,
143
+ "status": "error",
144
+ "stdout": "",
145
+ "stderr": "Execution timed out.",
146
+ "result": None,
147
+ "plots": [],
148
+ "dataframes": []
149
+ }
150
+
151
+ def _execute_sql(self, code: str, execution_id: str) -> dict:
152
+ result = {
153
+ "execution_id": execution_id,
154
+ "status": "error",
155
+ "stdout": "",
156
+ "stderr": "",
157
+ "result": None,
158
+ "plots": [],
159
+ "dataframes": []
160
+ }
161
+ try:
162
+ conn = sqlite3.connect(self.temp_sqlite_db)
163
+ cur = conn.cursor()
164
+ cur.execute(code)
165
+ if code.strip().lower().startswith("select"):
166
+ columns = [description[0] for description in cur.description]
167
+ rows = cur.fetchall()
168
+ df = pd.DataFrame(rows, columns=columns)
169
+ result["dataframes"].append({
170
+ "name": "query_result",
171
+ "head": df.head().to_dict(),
172
+ "shape": df.shape,
173
+ "dtypes": str(df.dtypes)
174
+ })
175
+ else:
176
+ conn.commit()
177
+
178
+ result["status"] = "success"
179
+ result["stdout"] = "Query executed successfully."
180
+
181
+ except Exception as e:
182
+ result["stderr"] = str(e)
183
+ finally:
184
+ conn.close()
185
+
186
+ return result
187
+
188
+ def _execute_c(self, code: str, execution_id: str) -> dict:
189
+ temp_dir = tempfile.mkdtemp()
190
+ source_path = os.path.join(temp_dir, "program.c")
191
+ binary_path = os.path.join(temp_dir, "program")
192
+
193
+ try:
194
+ with open(source_path, "w") as f:
195
+ f.write(code)
196
+
197
+ compile_proc = subprocess.run(
198
+ ["gcc", source_path, "-o", binary_path],
199
+ capture_output=True, text=True, timeout=self.max_execution_time
200
+ )
201
+ if compile_proc.returncode != 0:
202
+ return {
203
+ "execution_id": execution_id,
204
+ "status": "error",
205
+ "stdout": compile_proc.stdout,
206
+ "stderr": compile_proc.stderr,
207
+ "result": None,
208
+ "plots": [],
209
+ "dataframes": []
210
+ }
211
+
212
+ run_proc = subprocess.run(
213
+ [binary_path],
214
+ capture_output=True, text=True, timeout=self.max_execution_time
215
+ )
216
+ return {
217
+ "execution_id": execution_id,
218
+ "status": "success" if run_proc.returncode == 0 else "error",
219
+ "stdout": run_proc.stdout,
220
+ "stderr": run_proc.stderr,
221
+ "result": None,
222
+ "plots": [],
223
+ "dataframes": []
224
+ }
225
+ except Exception as e:
226
+ return {
227
+ "execution_id": execution_id,
228
+ "status": "error",
229
+ "stdout": "",
230
+ "stderr": str(e),
231
+ "result": None,
232
+ "plots": [],
233
+ "dataframes": []
234
+ }
235
+
236
+ def _execute_java(self, code: str, execution_id: str) -> dict:
237
+ temp_dir = tempfile.mkdtemp()
238
+ source_path = os.path.join(temp_dir, "Main.java")
239
+
240
+ try:
241
+ with open(source_path, "w") as f:
242
+ f.write(code)
243
+
244
+ compile_proc = subprocess.run(
245
+ ["javac", source_path],
246
+ capture_output=True, text=True, timeout=self.max_execution_time
247
+ )
248
+ if compile_proc.returncode != 0:
249
+ return {
250
+ "execution_id": execution_id,
251
+ "status": "error",
252
+ "stdout": compile_proc.stdout,
253
+ "stderr": compile_proc.stderr,
254
+ "result": None,
255
+ "plots": [],
256
+ "dataframes": []
257
+ }
258
+
259
+ run_proc = subprocess.run(
260
+ ["java", "-cp", temp_dir, "Main"],
261
+ capture_output=True, text=True, timeout=self.max_execution_time
262
+ )
263
+ return {
264
+ "execution_id": execution_id,
265
+ "status": "success" if run_proc.returncode == 0 else "error",
266
+ "stdout": run_proc.stdout,
267
+ "stderr": run_proc.stderr,
268
+ "result": None,
269
+ "plots": [],
270
+ "dataframes": []
271
+ }
272
+ except Exception as e:
273
+ return {
274
+ "execution_id": execution_id,
275
+ "status": "error",
276
+ "stdout": "",
277
+ "stderr": str(e),
278
+ "result": None,
279
+ "plots": [],
280
+ "dataframes": []
281
+ }
huggingface.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub import login
2
+ import os
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+ # Make sure you have set your token as an environment variable
7
+ # e.g., in your terminal: export HUGGINGFACEHUB_API_TOKEN="your_token_here"
8
+ token = os.getenv("HUGGINGFACEHUB_API_TOKEN")
9
+
10
+ if not token:
11
+ raise ValueError("Please set the environment variable HUGGINGFACEHUB_API_TOKEN")
12
+
13
+ # Login programmatically
14
+ login(token=token)
15
+ print("βœ… Logged in to Hugging Face Hub successfully!")
image_processing.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import base64
4
+ import uuid
5
+ from PIL import Image
6
+
7
+ # Helper functions for image processing
8
+ def encode_image(image_path: str) -> str:
9
+ """Convert an image file to base64 string."""
10
+ with open(image_path, "rb") as image_file:
11
+ return base64.b64encode(image_file.read()).decode("utf-8")
12
+
13
+
14
+ def decode_image(base64_string: str) -> Image.Image:
15
+ """Convert a base64 string to a PIL Image."""
16
+ image_data = base64.b64decode(base64_string)
17
+ return Image.open(io.BytesIO(image_data))
18
+
19
+
20
+ def save_image(image: Image.Image, directory: str = "image_outputs") -> str:
21
+ """Save a PIL Image to disk and return the path."""
22
+ os.makedirs(directory, exist_ok=True)
23
+ image_id = str(uuid.uuid4())
24
+ image_path = os.path.join(directory, f"{image_id}.png")
25
+ image.save(image_path)
26
+ return image_path
mcp/__init__.py ADDED
File without changes
mcp/tavily_client.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_mcp_adapters.client import MultiServerMCPClient
2
+ import os
3
+
4
+ client = MultiServerMCPClient({
5
+ "tavily_mcp": {
6
+ "command": "npx",
7
+ "args": ["-y", "tavily-mcp@latest"],
8
+ "env": {"TAVILY_API_KEY": os.environ["TAVILY_API_KEY"]}
9
+ }
10
+ # Or use remote URL instead of local command...
11
+ })
metadata.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.1.0
2
+ accelerate==1.2.1
3
+ addict==2.4.0
4
+ aiobotocore==2.23.0
5
+ aiofiles==24.1.0
6
+ aiohappyeyeballs==2.6.1
7
+ aiohttp==3.12.13
8
+ aiohttp-cors==0.8.1
9
+ aioitertools==0.12.0
10
+ aiosignal==1.3.1
11
+ altair==5.4.1
12
+ altgraph==0.17.4
13
+ annotated-types==0.7.0
14
+ anyascii==0.3.3
15
+ anyio==3.7.1
16
+ appdirs==1.4.4
17
+ apriori==1.0.0
18
+ argon2-cffi==21.3.0
19
+ argon2-cffi-bindings==21.2.0
20
+ arrow==1.2.3
21
+ astor==0.8.1
22
+ asttokens==2.2.1
23
+ astunparse==1.6.3
24
+ async-lru==2.0.3
25
+ async-timeout==4.0.2
26
+ asyncpg==0.30.0
27
+ attrs==24.2.0
28
+ audioread==3.0.1
29
+ Babel==2.12.1
30
+ backcall==0.2.0
31
+ backoff==2.2.1
32
+ backrefs==5.9
33
+ bangla==0.0.5
34
+ basicsr==1.4.2
35
+ bcrypt==4.3.0
36
+ beautifulsoup4==4.12.2
37
+ blake3==1.0.5
38
+ bleach==6.0.0
39
+ blinker==1.9.0
40
+ blis==1.2.1
41
+ bnnumerizer==0.0.2
42
+ bnunicodenormalizer==0.1.7
43
+ botocore==1.38.27
44
+ build==1.2.2.post1
45
+ cachetools==5.5.0
46
+ camelot-py==0.11.0
47
+ catalogue==2.0.10
48
+ cbor2==5.7.0
49
+ certifi==2024.7.4
50
+ cffi==1.15.1
51
+ cfgv==3.4.0
52
+ chardet==5.2.0
53
+ charset-normalizer==3.2.0
54
+ chromadb==1.0.15
55
+ click==8.1.8
56
+ cloudpathlib==0.21.1
57
+ cloudpickle==2.2.1
58
+ cmake==4.0.0
59
+ colorama==0.4.6
60
+ coloredlogs==15.0.1
61
+ colorful==0.5.7
62
+ colorlog==6.9.0
63
+ comm==0.1.3
64
+ compressed-tensors==0.10.2
65
+ comtypes==1.4.11
66
+ confection==0.1.5
67
+ contourpy==1.3.1
68
+ coqpit==0.0.17
69
+ cramjam==2.11.0
70
+ crewai==0.140.0
71
+ cryptography==43.0.3
72
+ cssselect==1.3.0
73
+ cssutils==2.11.1
74
+ cycler==0.12.1
75
+ cymem==2.0.11
76
+ Cython==3.1.2
77
+ dataclasses-json==0.6.7
78
+ dateparser==1.1.8
79
+ debugpy==1.6.7
80
+ decorator==5.1.1
81
+ defusedxml==0.7.1
82
+ Deprecated==1.2.18
83
+ depyf==0.19.0
84
+ dill==0.4.0
85
+ diskcache==5.6.3
86
+ distlib==0.3.9
87
+ distro==1.9.0
88
+ dlib==19.24.8
89
+ dnspython==2.7.0
90
+ docopt==0.6.2
91
+ docstring_parser==0.16
92
+ dotenv==0.9.9
93
+ duckduckgo_search==8.0.4
94
+ durationpy==0.10
95
+ ecdsa==0.19.1
96
+ efficientnet-pytorch==0.7.1
97
+ einops==0.8.1
98
+ elevenlabs==2.6.0
99
+ email-validator==2.3.0
100
+ encodec==0.1.1
101
+ environs==14.1.0
102
+ et_xmlfile==2.0.0
103
+ executing==1.2.0
104
+ exllamav2==0.3.2
105
+ extra-streamlit-components==0.1.80
106
+ facenet-pytorch==2.6.0
107
+ facexlib==0.3.0
108
+ fastapi==0.115.6
109
+ fastapi-cli==0.0.10
110
+ fastapi-cloud-cli==0.1.5
111
+ fastapi_cors==0.0.6
112
+ fastjsonschema==2.18.0
113
+ fastparquet==2024.11.0
114
+ ffmpeg-python==0.2.0
115
+ filelock==3.16.1
116
+ filterpy==1.4.5
117
+ Flask==3.1.0
118
+ flask-cors==5.0.1
119
+ Flask-PyMongo==3.0.1
120
+ flatbuffers==24.3.25
121
+ fonttools==4.55.0
122
+ fqdn==1.5.1
123
+ frozenlist==1.4.0
124
+ fsspec==2025.5.1
125
+ ftfy==6.3.1
126
+ future==1.0.0
127
+ g2pkk==0.1.2
128
+ gast==0.6.0
129
+ geocoder==1.38.1
130
+ gfpgan==1.3.8
131
+ gguf==0.17.1
132
+ ghostscript==0.7
133
+ ghp-import==2.1.0
134
+ git-filter-repo==2.47.0
135
+ gitdb==4.0.11
136
+ GitPython==3.1.43
137
+ google-ai-generativelanguage==0.6.15
138
+ google-api-core==2.25.1
139
+ google-api-python-client==2.175.0
140
+ google-auth==2.40.3
141
+ google-auth-httplib2==0.2.0
142
+ google-auth-oauthlib==1.2.2
143
+ google-generativeai==0.8.5
144
+ google-pasta==0.2.0
145
+ googleapis-common-protos==1.70.0
146
+ GPUtil==1.4.0
147
+ graphviz==0.21
148
+ greenlet==3.1.1
149
+ groq==0.29.0
150
+ grpcio==1.74.0
151
+ grpcio-status==1.71.2
152
+ gruut==2.2.3
153
+ gruut-ipa==0.13.0
154
+ gruut-lang-de==2.0.1
155
+ gruut-lang-en==2.0.1
156
+ gruut-lang-es==2.0.1
157
+ gruut-lang-fr==2.0.2
158
+ gTTS==2.5.4
159
+ gunicorn==23.0.0
160
+ h11==0.14.0
161
+ h5py==3.14.0
162
+ hangul-romanize==0.1.0
163
+ httpcore==1.0.7
164
+ httplib2==0.22.0
165
+ httptools==0.6.4
166
+ httpx==0.28.1
167
+ httpx-sse==0.4.1
168
+ huggingface-hub==0.34.4
169
+ humanfriendly==10.0
170
+ ibm-cloud-sdk-core==3.24.1
171
+ ibm-platform-services==0.66.1
172
+ identify==2.6.12
173
+ idna==3.4
174
+ imageio==2.37.0
175
+ imagesize==1.4.1
176
+ importlib_metadata==8.4.0
177
+ importlib_resources==6.5.2
178
+ inflect==7.5.0
179
+ iniconfig==2.1.0
180
+ instructor==1.9.1
181
+ interegular==0.3.3
182
+ ipycytoscape==1.3.3
183
+ ipykernel==6.24.0
184
+ ipython==8.14.0
185
+ ipython-genutils==0.2.0
186
+ ipywidgets==8.1.7
187
+ isoduration==20.11.0
188
+ itsdangerous==2.2.0
189
+ jamo==0.4.1
190
+ jedi==0.18.2
191
+ jieba==0.42.1
192
+ Jinja2==3.1.6
193
+ jiter==0.8.2
194
+ jmespath==1.0.1
195
+ joblib==1.4.2
196
+ Js2Py==0.74
197
+ json5==0.12.0
198
+ json_repair==0.25.2
199
+ jsonlines==1.2.0
200
+ jsonpatch==1.33
201
+ jsonpickle==3.0.3
202
+ jsonpointer==2.4
203
+ jsonref==1.1.0
204
+ jsonschema==4.24.0
205
+ jsonschema-specifications==2023.7.1
206
+ jupyter==1.0.0
207
+ jupyter-console==6.6.3
208
+ jupyter-events==0.6.3
209
+ jupyter-lsp==2.2.0
210
+ jupyter_client==8.3.0
211
+ jupyter_core==5.3.1
212
+ jupyter_server==2.7.0
213
+ jupyter_server_terminals==0.4.4
214
+ jupyterlab==4.0.3
215
+ jupyterlab-pygments==0.2.2
216
+ jupyterlab_server==2.23.0
217
+ jupyterlab_widgets==3.0.15
218
+ kaggle==1.7.4.2
219
+ keras==3.9.2
220
+ kiwisolver==1.4.7
221
+ kubernetes==33.1.0
222
+ lab==8.3
223
+ langchain==0.3.26
224
+ langchain-community==0.3.27
225
+ langchain-core==0.3.68
226
+ langchain-openai==0.3.27
227
+ langchain-text-splitters==0.3.8
228
+ langcodes==3.5.0
229
+ langsmith==0.4.4
230
+ language_data==1.3.0
231
+ lark==1.2.2
232
+ lazy_loader==0.4
233
+ libclang==18.1.1
234
+ librosa==0.11.0
235
+ litellm==1.72.6
236
+ llvmlite==0.44.0
237
+ lm-format-enforcer==0.10.12
238
+ lmdb==1.6.2
239
+ load-dotenv==0.1.0
240
+ lxml==5.3.0
241
+ lz4==4.4.4
242
+ marisa-trie==1.2.1
243
+ Markdown==3.6
244
+ markdown-it-py==3.0.0
245
+ MarkupSafe==2.1.3
246
+ marshmallow==3.25.1
247
+ matplotlib==3.9.2
248
+ matplotlib-inline==0.1.6
249
+ mdurl==0.1.2
250
+ mergedeep==1.3.4
251
+ mistral_common==1.8.4
252
+ mistune==3.0.1
253
+ mkdocs==1.6.1
254
+ mkdocs-get-deps==0.2.0
255
+ mkdocs-material==9.6.15
256
+ mkdocs-material-extensions==1.3.1
257
+ ml_dtypes==0.5.1
258
+ mmh3==5.1.0
259
+ mock==5.2.0
260
+ more-itertools==10.7.0
261
+ MouseInfo==0.1.3
262
+ mpmath==1.3.0
263
+ msgpack==1.1.1
264
+ msgspec==0.19.0
265
+ mtcnn==1.0.0
266
+ multidict==6.0.4
267
+ munch==4.0.0
268
+ murmurhash==1.0.13
269
+ mypy_extensions==1.1.0
270
+ mysql-connector-python==9.2.0
271
+ namex==0.0.8
272
+ narwhals==1.47.0
273
+ nbclient==0.8.0
274
+ nbconvert==7.7.2
275
+ nbformat==5.9.1
276
+ neo4j==5.28.1
277
+ nest-asyncio==1.5.6
278
+ networkx==3.2.1
279
+ ninja==1.13.0
280
+ nltk==3.9.1
281
+ nodeenv==1.9.1
282
+ notebook==7.0.3
283
+ notebook_shim==0.2.3
284
+ num2words==0.5.14
285
+ numba==0.61.2
286
+ numpy==2.2.6
287
+ oauthlib==3.3.1
288
+ onnxruntime==1.22.0
289
+ openai==1.104.2
290
+ openai-harmony==0.0.4
291
+ opencensus==0.11.4
292
+ opencensus-context==0.1.3
293
+ opencv-contrib-python==4.10.0.84
294
+ opencv-python==4.11.0.86
295
+ opencv-python-headless==4.12.0.88
296
+ openpyxl==3.1.5
297
+ opentelemetry-api==1.33.0
298
+ opentelemetry-exporter-otlp-proto-common==1.33.0
299
+ opentelemetry-exporter-otlp-proto-grpc==1.33.0
300
+ opentelemetry-exporter-otlp-proto-http==1.34.1
301
+ opentelemetry-exporter-prometheus==0.54b0
302
+ opentelemetry-instrumentation==0.54b0
303
+ opentelemetry-instrumentation-requests==0.54b0
304
+ opentelemetry-proto==1.33.0
305
+ opentelemetry-sdk==1.33.0
306
+ opentelemetry-semantic-conventions==0.54b0
307
+ opentelemetry-util-http==0.54b0
308
+ opt-einsum==3.3.0
309
+ optree==0.14.1
310
+ orjson==3.10.18
311
+ outcome==1.3.0.post0
312
+ outlines_core==0.2.10
313
+ overrides==7.3.1
314
+ packaging==24.2
315
+ paddleocr==3.1.0
316
+ paddlex==3.1.1
317
+ paginate==0.5.7
318
+ pandas==1.5.3
319
+ pandocfilters==1.5.0
320
+ parso==0.8.3
321
+ partial-json-parser==0.2.1.1.post6
322
+ passlib==1.7.4
323
+ pathspec==0.12.1
324
+ pbr==6.1.1
325
+ pdfminer.six==20250506
326
+ pdfplumber==0.11.7
327
+ pefile==2023.2.7
328
+ pgvector==0.4.1
329
+ pickleshare==0.7.5
330
+ pillow==11.2.1
331
+ pipwin==0.5.2
332
+ platformdirs==3.9.1
333
+ playsound==1.3.0
334
+ plotly==6.2.0
335
+ pluggy==1.5.0
336
+ pooch==1.8.2
337
+ posthog==5.4.0
338
+ pre_commit==4.2.0
339
+ premailer==3.10.0
340
+ preprocessing==0.1.13
341
+ preshed==3.0.10
342
+ pretrainedmodels==0.7.4
343
+ prettytable==3.16.0
344
+ primp==0.15.0
345
+ prometheus-fastapi-instrumentator==7.1.0
346
+ prometheus_client==0.22.1
347
+ prompt-toolkit==3.0.39
348
+ propcache==0.3.2
349
+ proto-plus==1.26.1
350
+ protobuf==5.29.5
351
+ psutil==5.9.5
352
+ psycopg2-binary==2.9.10
353
+ pure-eval==0.2.2
354
+ py-cpuinfo==9.0.0
355
+ py-spy==0.4.0
356
+ pyarrow==18.0.0
357
+ pyasn1==0.6.1
358
+ pyasn1_modules==0.4.2
359
+ PyAudio==0.2.14
360
+ PyAutoGUI==0.9.54
361
+ pybase64==1.4.1
362
+ pyclipper==1.3.0.post6
363
+ pycountry==24.6.1
364
+ pycparser==2.21
365
+ pydantic==2.11.7
366
+ pydantic-extra-types==2.10.5
367
+ pydantic-settings==2.10.1
368
+ pydantic_core==2.33.2
369
+ pydeck==0.9.1
370
+ pydot==4.0.1
371
+ pydub==0.25.1
372
+ pygame==2.6.1
373
+ PyGetWindow==0.0.9
374
+ Pygments==2.19.2
375
+ pyinstaller==6.11.1
376
+ pyinstaller-hooks-contrib==2024.10
377
+ pyjsparser==2.7.1
378
+ PyJWT==2.10.1
379
+ pylatexenc==2.10
380
+ PyMatting==1.1.14
381
+ pymdown-extensions==10.16
382
+ pymongo==4.12.0
383
+ PyMsgBox==1.0.9
384
+ pynndescent==0.5.13
385
+ pyparsing==3.2.0
386
+ pypdf==5.1.0
387
+ PyPDF2==3.0.1
388
+ pypdfium2==4.30.1
389
+ pyperclip==1.9.0
390
+ PyPika==0.48.9
391
+ pypinyin==0.54.0
392
+ pypiwin32==223
393
+ PyPrind==2.11.3
394
+ pyproject_hooks==1.2.0
395
+ pyreadline3==3.5.4
396
+ PyRect==0.2.0
397
+ pysbd==0.3.4
398
+ PyScreeze==1.0.1
399
+ pySmartDL==1.3.4
400
+ PySocks==1.7.1
401
+ pyspnego==0.11.2
402
+ pytesseract==0.3.13
403
+ pytest==8.3.5
404
+ pytest-asyncio==1.1.0
405
+ pytest-mock==3.14.1
406
+ python-crfsuite==0.9.11
407
+ python-dateutil==2.8.2
408
+ python-dotenv==1.0.0
409
+ python-jose==3.5.0
410
+ python-json-logger==2.0.7
411
+ python-multipart==0.0.20
412
+ python-slugify==8.0.4
413
+ pyttsx3==2.98
414
+ pytweening==1.2.0
415
+ pytz==2023.3.post1
416
+ pyvis==0.3.2
417
+ pywhatkit==5.4
418
+ pywin32==306
419
+ pywin32-ctypes==0.2.3
420
+ pywinpty==2.0.11
421
+ PyYAML==6.0.2
422
+ pyyaml_env_tag==1.1
423
+ pyzmq==25.1.0
424
+ qc-grader @ git+https://github.com/qiskit-community/Quantum-Challenge-Grader.git@1d7a6915623b0cfeac4c114391c279e9d98eb7f9
425
+ qiskit==2.1.1
426
+ qiskit-aer==0.17.1
427
+ qiskit-ibm-runtime==0.40.1
428
+ qiskit-serverless==0.25.1
429
+ qtconsole==5.4.4
430
+ QtPy==2.4.0
431
+ ratelim==0.1.6
432
+ ray==2.47.1
433
+ # Editable install with no version control (realesrgan==0.3.0)
434
+ -e c:\python311\lib\site-packages
435
+ referencing==0.30.0
436
+ regex==2024.11.6
437
+ relaxml==0.1.3
438
+ rembg==2.0.66
439
+ requests==2.32.3
440
+ requests-oauthlib==2.0.0
441
+ requests-toolbelt==1.0.0
442
+ requests_ntlm==1.3.0
443
+ rfc3339-validator==0.1.4
444
+ rfc3986-validator==0.1.1
445
+ rich==13.9.4
446
+ rich-toolkit==0.15.0
447
+ rignore==0.6.4
448
+ rpds-py==0.9.2
449
+ rsa==4.9.1
450
+ ruamel.yaml==0.18.14
451
+ ruamel.yaml.clib==0.2.12
452
+ rustworkx==0.16.0
453
+ s3fs==2025.5.1
454
+ safetensors==0.5.2
455
+ scikit-image==0.25.2
456
+ scikit-learn==1.6.0
457
+ scipy==1.14.1
458
+ seaborn==0.13.2
459
+ selenium==4.27.1
460
+ Send2Trash==1.8.2
461
+ sentencepiece==0.2.1
462
+ sentry-sdk==2.35.2
463
+ serpapi==0.1.5
464
+ setproctitle==1.3.6
465
+ shapely==2.1.1
466
+ shellingham==1.5.4
467
+ simplejson==3.19.3
468
+ six==1.16.0
469
+ smart_open==7.3.0.post1
470
+ smmap==5.0.1
471
+ smolagents==1.18.0
472
+ sniffio==1.3.0
473
+ sortedcontainers==2.4.0
474
+ soundfile==0.13.1
475
+ soupsieve==2.4.1
476
+ soxr==0.5.0.post1
477
+ spacy==3.8.7
478
+ spacy-legacy==3.0.12
479
+ spacy-loggers==1.0.5
480
+ spectate==1.0.1
481
+ SpeechRecognition==3.14.3
482
+ sphinx-rtd-theme==0.2.4
483
+ SQLAlchemy==2.0.40
484
+ srsly==2.5.1
485
+ sspilib==0.3.1
486
+ stack-data==0.6.2
487
+ starlette==0.41.3
488
+ stevedore==5.4.1
489
+ streamlit==1.40.1
490
+ streamlit_mic_recorder==0.0.8
491
+ streamlit_TTS==0.0.7
492
+ stripe==12.3.0
493
+ SudachiDict-core==20250515
494
+ SudachiPy==0.6.10
495
+ symengine==0.13.0
496
+ sympy==1.14.0
497
+ tabulate==0.9.0
498
+ tb-nightly==2.20.0a20250621
499
+ tenacity==9.0.0
500
+ tensorboard==2.19.0
501
+ tensorboard-data-server==0.7.2
502
+ tensorflow==2.19.0
503
+ tensorflow-intel==2.16.2
504
+ tensorflow-io-gcs-filesystem==0.31.0
505
+ termcolor==2.4.0
506
+ terminado==0.17.1
507
+ tesseract==0.1.3
508
+ text-unidecode==1.3
509
+ thinc==8.3.4
510
+ threadpoolctl==3.5.0
511
+ three==0.8.0
512
+ tifffile==2025.6.11
513
+ tiktoken==0.9.0
514
+ tinycss2==1.2.1
515
+ tk==0.1.0
516
+ tokenizers==0.22.0
517
+ toml==0.10.2
518
+ tomli==2.2.1
519
+ tomli_w==1.2.0
520
+ torch==2.7.1
521
+ torchvision==0.22.1
522
+ tornado==6.3.2
523
+ tqdm==4.67.1
524
+ trainer==0.0.36
525
+ traitlets==5.9.0
526
+ transformers==4.56.0
527
+ trio==0.27.0
528
+ trio-websocket==0.11.1
529
+ TTS==0.22.0
530
+ txt2tags==3.9
531
+ typeguard==4.4.4
532
+ typer==0.16.0
533
+ typing-inspect==0.9.0
534
+ typing-inspection==0.4.1
535
+ typing_extensions==4.14.1
536
+ tzdata==2023.3
537
+ tzlocal==5.3.1
538
+ ujson==5.10.0
539
+ umap-learn==0.5.9.post2
540
+ Unidecode==1.4.0
541
+ uri-template==1.3.0
542
+ uritemplate==4.2.0
543
+ urllib3==2.5.0
544
+ utils==1.0.2
545
+ uv==0.7.19
546
+ uvicorn==0.34.0
547
+ virtualenv==20.31.2
548
+ vllm==0.10.1.1
549
+ wasabi==1.1.3
550
+ watchdog==6.0.0
551
+ watchfiles==1.1.0
552
+ wcwidth==0.2.6
553
+ weasel==0.4.1
554
+ webcolors==24.11.1
555
+ webencodings==0.5.1
556
+ websocket-client==1.8.0
557
+ websockets==15.0.1
558
+ Werkzeug==3.1.3
559
+ whisper-openai==1.0.0
560
+ widgetsnbextension==4.0.14
561
+ wikipedia==1.4.0
562
+ wrapt==1.16.0
563
+ wsproto==1.2.0
564
+ yapf==0.43.0
565
+ yarl==1.20.1
566
+ zipp==3.19.1
567
+ zstandard==0.23.0
568
+ gradio[oauth]
system_prompt.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ You are a helpful assistant tasked with answering questions using a set of tools.
2
+ Now, I will ask you a question. Report your thoughts, and finish your answer with the following template:
3
+ FINAL ANSWER: [YOUR FINAL ANSWER].
4
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, Apply the rules above for each element (number or string), ensure there is exactly one space after each comma.
5
+ Your answer should only start with "FINAL ANSWER: ", then follows with the answer.
test_agent.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ import os
3
+ from unittest.mock import patch, MagicMock
4
+ from agent import (
5
+ build_graph,
6
+ multiply,
7
+ add,
8
+ subtract,
9
+ divide,
10
+ modulus,
11
+ power,
12
+ square_root,
13
+ save_and_read_file,
14
+ download_file_from_url,
15
+ extract_text_from_image,
16
+ analyze_image,
17
+ transform_image,
18
+ draw_on_image,
19
+ generate_simple_image,
20
+ combine_images,
21
+ analyze_csv_file,
22
+ analyze_excel_file,
23
+ execute_code_multilang,
24
+ web_search,
25
+ wiki_search,
26
+ arxiv_search,
27
+ vector_store,
28
+ )
29
+
30
+ class TestAgent(unittest.TestCase):
31
+ def test_multiply(self):
32
+ response = multiply.invoke({"a": 6, "b": 7})
33
+ self.assertTrue(response["status"])
34
+ self.assertEqual(response["data"], 42)
35
+
36
+ def test_add(self):
37
+ response = add.invoke({"a": 5, "b": 3})
38
+ self.assertTrue(response["status"])
39
+ self.assertEqual(response["data"], 8)
40
+
41
+ def test_llm(self):
42
+ graph = build_graph()
43
+ response = graph.invoke({"messages": ["what is 1 + 1"]})
44
+ self.assertIsNotNone(response)
45
+ print(response)
46
+
47
+
48
+ if __name__ == '__main__':
49
+ unittest.main()
test_llm.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Complete LLM Testing Script
4
+ Supports Groq and local HuggingFace LLMs with proper LangChain integration.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ from dotenv import load_dotenv
10
+
11
+ # LangChain & LangGraph imports
12
+ try:
13
+ from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
14
+ from langchain_groq import ChatGroq
15
+ from langgraph.graph import START, StateGraph, MessagesState
16
+ from langgraph.prebuilt import ToolNode, tools_condition
17
+ print("βœ… LangChain imports successful")
18
+ except ImportError as e:
19
+ print(f"❌ Import error: {e}")
20
+ print("πŸ’‘ Install missing packages: pip install langchain-groq langgraph")
21
+ sys.exit(1)
22
+
23
+ load_dotenv()
24
+
25
+
26
+ class LocalHuggingFaceLLM:
27
+ """Custom wrapper for local HuggingFace models"""
28
+ def __init__(self, model, tokenizer, device):
29
+ self.model = model
30
+ self.tokenizer = tokenizer
31
+ self.device = device
32
+ self.model.eval()
33
+
34
+ def invoke(self, messages):
35
+ """Generate response from local model, return AIMessage"""
36
+ from langchain_core.messages import AIMessage
37
+ import torch
38
+
39
+ # Convert messages to text
40
+ if isinstance(messages, list):
41
+ text = ""
42
+ for msg in messages:
43
+ if hasattr(msg, 'content'):
44
+ if hasattr(msg, 'type'):
45
+ if msg.type == "system":
46
+ text += f"System: {msg.content}\n"
47
+ elif msg.type == "human":
48
+ text += f"Human: {msg.content}\n"
49
+ else:
50
+ text += f"{msg.content}\n"
51
+ else:
52
+ text += f"Human: {msg.content}\n"
53
+ else:
54
+ text += str(msg) + "\n"
55
+ text += "Assistant:"
56
+ else:
57
+ text = str(messages)
58
+
59
+ try:
60
+ inputs = self.tokenizer.encode(text, return_tensors="pt", max_length=512, truncation=True)
61
+ if self.device == "cuda" and torch.cuda.is_available():
62
+ inputs = inputs.to(self.device)
63
+ self.model = self.model.to(self.device)
64
+
65
+ outputs = self.model.generate(
66
+ inputs,
67
+ max_new_tokens=100,
68
+ do_sample=True,
69
+ temperature=0.7,
70
+ pad_token_id=self.tokenizer.eos_token_id,
71
+ attention_mask=torch.ones_like(inputs),
72
+ no_repeat_ngram_size=2,
73
+ early_stopping=True
74
+ )
75
+
76
+ response_text = self.tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True).strip()
77
+ return AIMessage(content=response_text if response_text else "I understand.")
78
+
79
+ except Exception as e:
80
+ return AIMessage(content=f"Error generating response: {str(e)}")
81
+
82
+
83
+ def create_local_huggingface_llm():
84
+ """Initialize local HuggingFace model"""
85
+ try:
86
+ from transformers import AutoTokenizer, AutoModelForCausalLM
87
+ import torch
88
+
89
+ model_name = "microsoft/DialoGPT-small"
90
+ device = "cuda" if torch.cuda.is_available() else "cpu"
91
+
92
+ tokenizer = AutoTokenizer.from_pretrained(model_name, padding_side="left")
93
+ if tokenizer.pad_token is None:
94
+ tokenizer.pad_token = tokenizer.eos_token
95
+
96
+ model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16 if device == "cuda" else torch.float32)
97
+ return LocalHuggingFaceLLM(model, tokenizer, device)
98
+
99
+ except Exception as e:
100
+ print(f"❌ Failed to load local HuggingFace model: {e}")
101
+ return None
102
+
103
+
104
+ def create_minimal_graph(provider: str = "groq"):
105
+ """Create a minimal graph for testing"""
106
+ try:
107
+ if provider == "groq":
108
+ if not os.getenv("GROQ_API_KEY"):
109
+ raise ValueError("GROQ_API_KEY not found")
110
+ llm = ChatGroq(model="qwen/qwen3-32b", temperature=0)
111
+
112
+ def assistant(state: MessagesState):
113
+ return {"messages": [llm.invoke(state["messages"])]}
114
+
115
+ builder = StateGraph(MessagesState)
116
+ builder.add_node("assistant", assistant)
117
+ builder.add_edge(START, "assistant")
118
+ return builder.compile()
119
+
120
+ elif provider == "huggingface_local":
121
+ llm = create_local_huggingface_llm()
122
+ if llm is None:
123
+ raise ValueError("Failed to create local HuggingFace model")
124
+
125
+ def assistant(state: MessagesState):
126
+ # Return AIMessage directly
127
+ return {"messages": [llm.invoke(state["messages"])]}
128
+
129
+ builder = StateGraph(MessagesState)
130
+ builder.add_node("assistant", assistant)
131
+ builder.add_edge(START, "assistant")
132
+ return builder.compile()
133
+
134
+ else:
135
+ raise ValueError(f"Unknown provider: {provider}")
136
+
137
+ except Exception as e:
138
+ print(f"❌ Failed to create minimal graph: {e}")
139
+ return None
140
+
141
+
142
+ def test_basic_llm_response(provider: str = "groq"):
143
+ """Test basic LLM response"""
144
+ print(f"\nπŸ§ͺ Testing Basic LLM Response ({provider})")
145
+ try:
146
+ if provider == "groq":
147
+ if not os.getenv("GROQ_API_KEY"):
148
+ return {"status": "error", "error": "GROQ_API_KEY not found"}
149
+ llm = ChatGroq(model="qwen/qwen3-32b", temperature=0)
150
+ elif provider == "huggingface_local":
151
+ llm = create_local_huggingface_llm()
152
+ if llm is None:
153
+ return {"status": "error", "error": "Failed to create local HuggingFace model"}
154
+ else:
155
+ return {"status": "error", "error": f"Unknown provider: {provider}"}
156
+
157
+ test_message = "Hello! Please respond with 'LLM is working correctly'"
158
+ response = llm.invoke([HumanMessage(content=test_message)])
159
+ print(f"πŸ“₯ Response: {response.content[:200]}")
160
+ return {"status": "success", "provider": provider, "response": response.content}
161
+
162
+ except Exception as e:
163
+ return {"status": "error", "error": str(e)}
164
+
165
+
166
+ def test_llm_with_system_prompt(provider: str = "groq"):
167
+ """Test LLM with system prompt"""
168
+ print(f"\nπŸ§ͺ Testing LLM with System Prompt ({provider})")
169
+ try:
170
+ if provider == "groq":
171
+ llm = ChatGroq(model="qwen/qwen3-32b", temperature=0)
172
+ elif provider == "huggingface_local":
173
+ llm = create_local_huggingface_llm()
174
+ if llm is None:
175
+ return {"status": "error", "error": "Failed to create local HuggingFace model"}
176
+ else:
177
+ return {"status": "error", "error": f"Unknown provider: {provider}"}
178
+
179
+ system_msg = SystemMessage(content="You are a helpful assistant. Answer briefly and clearly.")
180
+ user_msg = HumanMessage(content="What is 2+2? Just give me the number.")
181
+
182
+ response = llm.invoke([system_msg, user_msg])
183
+ print(f"πŸ“₯ Response: {response.content}")
184
+ return {"status": "success", "provider": provider, "response": response.content}
185
+
186
+ except Exception as e:
187
+ return {"status": "error", "error": str(e)}
188
+
189
+
190
+ def test_graph_workflow(provider: str = "groq"):
191
+ """Test graph workflow"""
192
+ print(f"\nπŸ§ͺ Testing Graph Workflow ({provider})")
193
+ try:
194
+ graph = create_minimal_graph(provider)
195
+ if graph is None:
196
+ return {"status": "error", "error": "Failed to create graph"}
197
+
198
+ test_query = "What is 5 + 3? Just give me the answer."
199
+ result = graph.invoke({"messages": [HumanMessage(content=test_query)]})
200
+
201
+ if result and "messages" in result:
202
+ last_message = result["messages"][-1]
203
+ print(f"πŸ“₯ Final response: {last_message.content}")
204
+ return {"status": "success", "response": last_message.content, "message_count": len(result["messages"])}
205
+ else:
206
+ return {"status": "error", "error": "No valid response from graph"}
207
+
208
+ except Exception as e:
209
+ return {"status": "error", "error": str(e)}
210
+
211
+
212
+ def run_all_tests():
213
+ """Run all LLM tests"""
214
+ results = {}
215
+ # Groq tests
216
+ results["groq_basic"] = test_basic_llm_response("groq")
217
+ results["groq_system_prompt"] = test_llm_with_system_prompt("groq")
218
+ results["groq_graph"] = test_graph_workflow("groq")
219
+ # HuggingFace local tests
220
+ results["huggingface_local_basic"] = test_basic_llm_response("huggingface_local")
221
+ results["huggingface_local_system_prompt"] = test_llm_with_system_prompt("huggingface_local")
222
+ results["huggingface_local_graph"] = test_graph_workflow("huggingface_local")
223
+ return results
224
+
225
+
226
+ if __name__ == "__main__":
227
+ test_results = run_all_tests()
228
+ print("\nπŸ“Š Test Results:")
229
+ for k, v in test_results.items():
230
+ print(f"{k}: {v}")
test_local_hf.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Simple test for local HuggingFace models"""
3
+
4
+ import torch
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM
6
+
7
+ def test_local_model():
8
+ print("πŸ§ͺ Testing Local HuggingFace Model...")
9
+
10
+ # Check device
11
+ device = "cuda" if torch.cuda.is_available() else "cpu"
12
+ print(f"πŸ–₯️ Using device: {device}")
13
+
14
+ # Load model
15
+ model_name = "microsoft/DialoGPT-small"
16
+ print(f"πŸ“¦ Loading {model_name}...")
17
+
18
+ try:
19
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
20
+ model = AutoModelForCausalLM.from_pretrained(model_name)
21
+
22
+ if tokenizer.pad_token is None:
23
+ tokenizer.pad_token = tokenizer.eos_token
24
+
25
+ print("βœ… Model loaded successfully!")
26
+
27
+ # Test generation
28
+ text = "Hello, how are you?"
29
+ inputs = tokenizer.encode(text, return_tensors="pt")
30
+
31
+ with torch.no_grad():
32
+ outputs = model.generate(
33
+ inputs,
34
+ max_new_tokens=50,
35
+ do_sample=True,
36
+ temperature=0.7,
37
+ pad_token_id=tokenizer.eos_token_id
38
+ )
39
+
40
+ response = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
41
+ print(f"πŸ€– Model response: {response}")
42
+ print("βœ… Local HuggingFace model is working!")
43
+
44
+ return True
45
+
46
+ except Exception as e:
47
+ print(f"❌ Error: {e}")
48
+ return False
49
+
50
+ if __name__ == "__main__":
51
+ success = test_local_model()
52
+ if success:
53
+ print("\nπŸŽ‰ You can now run the main test with local HuggingFace models!")
54
+ else:
55
+ print("\n❌ Setup incomplete. Check the error messages above.")
validation_json/metadata.jsonl ADDED
The diff for this file is too large to render. See raw diff