Spaces:
Build error
Build error
Download examples/multi_tool_agent_test.py from minhtudragon/headroom_3: direct link, hf CLI and curl.
- Browser
- Download file 12.4 kB
-
https://huggingface.co/spaces/minhtudragon/headroom_3/resolve/f59690a7a70c08d451b8540def7e9ff8d75bc36e/examples/multi_tool_agent_test.py
- Command line
-
hf download hf://spaces/minhtudragon/headroom_3@f59690a7a70c08d451b8540def7e9ff8d75bc36e/examples/multi_tool_agent_test.py
-
curl -L -o multi_tool_agent_test.py https://huggingface.co/spaces/minhtudragon/headroom_3/resolve/f59690a7a70c08d451b8540def7e9ff8d75bc36e/examples/multi_tool_agent_test.py
12.4 kB
| #!/usr/bin/env python3 | |
| """ | |
| Multi-Tool Agent Test: Diverse Data Types with Claude API | |
| This test creates an agent with multiple tools returning different data types: | |
| - GitHub: Issues, PRs, repo metadata | |
| - ArXiv: Paper abstracts and citations | |
| - Code Search: Source code snippets | |
| - Database: JSON records | |
| We run it WITHOUT Headroom and WITH Headroom to compare token usage. | |
| Uses Claude API for real function calling. | |
| """ | |
| import json | |
| import os | |
| import time | |
| from dataclasses import dataclass | |
| from agno.agent import Agent | |
| from agno.models.anthropic import Claude | |
| from agno.tools import tool | |
| # Check for API key | |
| if not os.environ.get("ANTHROPIC_API_KEY"): | |
| raise ValueError("ANTHROPIC_API_KEY environment variable required") | |
| # ============================================================================= | |
| # MOCK TOOL DATA - Realistic responses from various sources | |
| # ============================================================================= | |
| GITHUB_ISSUES = [ | |
| { | |
| "number": i, | |
| "title": f"Issue #{i}: {'Memory leak in worker pool' if i == 42 else 'Feature request: ' + ['dark mode', 'API pagination', 'webhook support', 'rate limiting'][i % 4]}", | |
| "state": "open" if i % 3 != 0 else "closed", | |
| "author": f"user{i % 20}", | |
| "labels": ["bug", "priority:high"] if i == 42 else ["enhancement"], | |
| "created_at": f"2024-12-{(i % 28) + 1:02d}T10:00:00Z", | |
| "updated_at": f"2024-12-{(i % 28) + 1:02d}T15:00:00Z", | |
| "comments": i % 10, | |
| "body": "Worker threads are not being released after task completion, causing memory to grow unboundedly. Stack trace attached." | |
| if i == 42 | |
| else f"Please add support for {['dark mode', 'API pagination', 'webhook support', 'rate limiting'][i % 4]}. This would greatly improve the user experience.", | |
| "assignees": ["maintainer1"] if i == 42 else [], | |
| "milestone": "v2.0" if i < 20 else None, | |
| "reactions": {"thumbs_up": 47 if i == 42 else i % 5, "thumbs_down": 0}, | |
| } | |
| for i in range(50) | |
| ] | |
| ARXIV_PAPERS = [ | |
| { | |
| "id": f"2401.{i:05d}", | |
| "title": f"{'Attention Is All You Need: Revisited' if i == 15 else ['Deep Learning for Code Generation', 'Efficient Transformers', 'Neural Architecture Search', 'Language Model Scaling'][i % 4]}", | |
| "authors": [f"Author{j}" for j in range(3 + i % 3)], | |
| "abstract": "We revisit the transformer architecture and propose key optimizations that reduce memory usage by 40% while maintaining accuracy. Our method introduces sparse attention patterns..." | |
| if i == 15 | |
| else f"This paper presents a novel approach to {['code generation', 'transformer efficiency', 'neural architecture', 'model scaling'][i % 4]}. We demonstrate state-of-the-art results on benchmark datasets.", | |
| "categories": ["cs.LG", "cs.CL"] if i == 15 else ["cs.LG"], | |
| "published": f"2024-01-{(i % 28) + 1:02d}", | |
| "citations": 1247 if i == 15 else i * 3, | |
| "pdf_url": f"https://arxiv.org/pdf/2401.{i:05d}.pdf", | |
| "comment": "Accepted at NeurIPS 2024" if i == 15 else None, | |
| } | |
| for i in range(30) | |
| ] | |
| CODE_SEARCH_RESULTS = [ | |
| { | |
| "file": f"src/{'worker.py' if i == 23 else ['utils.py', 'api.py', 'models.py', 'handlers.py'][i % 4]}", | |
| "line": 100 + i * 10, | |
| "content": '''def cleanup_worker(self): | |
| """Release worker resources - MEMORY LEAK FIX""" | |
| self.thread_pool.shutdown(wait=True) | |
| self.connections.clear() | |
| gc.collect() # Force garbage collection''' | |
| if i == 23 | |
| else f'''def process_{["data", "request", "model", "event"][i % 4]}(self, input): | |
| """Process incoming {["data", "request", "model", "event"][i % 4]}""" | |
| result = self.transform(input) | |
| return self.validate(result)''', | |
| "language": "python", | |
| "repository": "main-app", | |
| "relevance_score": 0.98 if i == 23 else 0.7 - (i * 0.01), | |
| "context_before": [" # Worker management", " "], | |
| "context_after": ["", " def start_worker(self):"], | |
| } | |
| for i in range(40) | |
| ] | |
| DATABASE_RECORDS = [ | |
| { | |
| "id": f"rec_{i:06d}", | |
| "type": "error" if i == 17 else "info", | |
| "timestamp": f"2024-12-15T{(i % 24):02d}:{(i % 60):02d}:00Z", | |
| "service": "worker-pool" if i == 17 else ["api", "auth", "db", "cache"][i % 4], | |
| "message": "OutOfMemoryError: heap space exhausted in WorkerPool.execute()" | |
| if i == 17 | |
| else f"Operation completed: {['request processed', 'user authenticated', 'query executed', 'cache updated'][i % 4]}", | |
| "metadata": { | |
| "heap_used": "7.8GB" if i == 17 else f"{1 + i % 3}GB", | |
| "heap_max": "8GB", | |
| "thread_count": 847 if i == 17 else 50 + i % 50, | |
| }, | |
| "stack_trace": "java.lang.OutOfMemoryError: Java heap space\n\tat WorkerPool.execute(WorkerPool.java:234)\n\tat TaskRunner.run(TaskRunner.java:89)" | |
| if i == 17 | |
| else None, | |
| } | |
| for i in range(60) | |
| ] | |
| # ============================================================================= | |
| # TOOL DEFINITIONS | |
| # ============================================================================= | |
| def search_github_issues(query: str, repo: str = "main-app") -> str: | |
| """Search GitHub issues in a repository. | |
| Args: | |
| query: Search query for issues | |
| repo: Repository name | |
| Returns: | |
| JSON array of matching issues | |
| """ | |
| return json.dumps(GITHUB_ISSUES, indent=2) | |
| def search_arxiv_papers(query: str, max_results: int = 30) -> str: | |
| """Search ArXiv for academic papers. | |
| Args: | |
| query: Search query for papers | |
| max_results: Maximum number of results | |
| Returns: | |
| JSON array of matching papers | |
| """ | |
| return json.dumps(ARXIV_PAPERS, indent=2) | |
| def search_code(query: str, language: str = "python") -> str: | |
| """Search codebase for matching code. | |
| Args: | |
| query: Code search query | |
| language: Programming language filter | |
| Returns: | |
| JSON array of code search results | |
| """ | |
| return json.dumps(CODE_SEARCH_RESULTS, indent=2) | |
| def query_database(query: str, table: str = "logs") -> str: | |
| """Query the database for records. | |
| Args: | |
| query: SQL-like query | |
| table: Table to query | |
| Returns: | |
| JSON array of database records | |
| """ | |
| return json.dumps(DATABASE_RECORDS, indent=2) | |
| # ============================================================================= | |
| # TEST RUNNER | |
| # ============================================================================= | |
| class TestResult: | |
| label: str | |
| input_tokens: int | |
| output_tokens: int | |
| response: str | |
| duration_ms: float | |
| tool_calls: int | |
| def count_tokens_approx(text: str) -> int: | |
| """Approximate token count (Ollama doesn't always report tokens).""" | |
| return len(text) // 4 | |
| def run_agent_test(use_headroom: bool) -> TestResult: | |
| """Run the multi-tool agent test.""" | |
| label = "WITH Headroom" if use_headroom else "WITHOUT Headroom (Baseline)" | |
| if use_headroom: | |
| from headroom.integrations.agno import HeadroomAgnoModel | |
| base_model = Claude(id="claude-sonnet-4-20250514") | |
| model = HeadroomAgnoModel(wrapped_model=base_model) | |
| else: | |
| model = Claude(id="claude-sonnet-4-20250514") | |
| agent = Agent( | |
| model=model, | |
| tools=[search_github_issues, search_arxiv_papers, search_code, query_database], | |
| markdown=True, | |
| ) | |
| # The question that requires searching multiple sources | |
| question = """I'm investigating a memory leak in our application. Please: | |
| 1. Search GitHub issues for memory-related bugs | |
| 2. Search our codebase for memory leak fixes | |
| 3. Check the database logs for OutOfMemory errors | |
| 4. Find any relevant research papers about memory management in worker pools | |
| Summarize what you find and recommend a fix.""" | |
| print(f"\n{'=' * 70}") | |
| print(f"Running: {label}") | |
| print(f"{'=' * 70}") | |
| print(f"Question: {question[:100]}...") | |
| start_time = time.time() | |
| try: | |
| response = agent.run(question) | |
| response_text = response.content if hasattr(response, "content") else str(response) | |
| except Exception as e: | |
| response_text = f"Error: {e}" | |
| duration_ms = (time.time() - start_time) * 1000 | |
| # Get token counts | |
| if use_headroom and hasattr(model, "total_tokens_saved"): | |
| summary = model.get_savings_summary() | |
| input_tokens = summary.get("total_tokens_after", 0) # Actual tokens sent to API | |
| tokens_before = summary.get("total_tokens_before", 0) | |
| tokens_saved = model.total_tokens_saved | |
| savings_pct = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0 | |
| print("\nπ Headroom Optimization Stats:") | |
| print(f" API requests made: {summary.get('total_requests', 0)}") | |
| print(f" Tokens BEFORE optimization: {tokens_before:,}") | |
| print(f" Tokens AFTER optimization: {input_tokens:,}") | |
| print(f" Tokens SAVED: {tokens_saved:,} ({savings_pct:.1f}%)") | |
| else: | |
| # Estimate from data size | |
| total_data = ( | |
| json.dumps(GITHUB_ISSUES) | |
| + json.dumps(ARXIV_PAPERS) | |
| + json.dumps(CODE_SEARCH_RESULTS) | |
| + json.dumps(DATABASE_RECORDS) | |
| ) | |
| input_tokens = count_tokens_approx(total_data + question) | |
| print(f"\nResponse preview: {response_text[:500]}...") | |
| print(f"Duration: {duration_ms:.0f}ms") | |
| return TestResult( | |
| label=label, | |
| input_tokens=input_tokens, | |
| output_tokens=count_tokens_approx(response_text), | |
| response=response_text, | |
| duration_ms=duration_ms, | |
| tool_calls=4, # We expect 4 tool calls | |
| ) | |
| def main(): | |
| print("\n" + "=" * 70) | |
| print("MULTI-TOOL AGENT TEST") | |
| print("Testing diverse data types: GitHub, ArXiv, Code, Database") | |
| print("Model: Claude Sonnet (claude-sonnet-4-20250514)") | |
| print("=" * 70) | |
| # Show data sizes | |
| print("\nTool output sizes:") | |
| print( | |
| f" GitHub Issues: {len(json.dumps(GITHUB_ISSUES)):,} chars ({len(GITHUB_ISSUES)} items)" | |
| ) | |
| print(f" ArXiv Papers: {len(json.dumps(ARXIV_PAPERS)):,} chars ({len(ARXIV_PAPERS)} items)") | |
| print( | |
| f" Code Search: {len(json.dumps(CODE_SEARCH_RESULTS)):,} chars ({len(CODE_SEARCH_RESULTS)} items)" | |
| ) | |
| print( | |
| f" Database Logs: {len(json.dumps(DATABASE_RECORDS)):,} chars ({len(DATABASE_RECORDS)} items)" | |
| ) | |
| total_chars = sum( | |
| len(json.dumps(d)) | |
| for d in [GITHUB_ISSUES, ARXIV_PAPERS, CODE_SEARCH_RESULTS, DATABASE_RECORDS] | |
| ) | |
| print(f" TOTAL: {total_chars:,} chars (~{total_chars // 4:,} tokens)") | |
| # Run baseline (no Headroom) | |
| print("\n" + "-" * 70) | |
| baseline = run_agent_test(use_headroom=False) | |
| # Run with Headroom | |
| print("\n" + "-" * 70) | |
| optimized = run_agent_test(use_headroom=True) | |
| # Final comparison | |
| print("\n" + "=" * 70) | |
| print("FINAL COMPARISON") | |
| print("=" * 70) | |
| print(f""" | |
| Baseline Headroom | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Tokens Sent to API: {baseline.input_tokens:>6,} {optimized.input_tokens:>6,} | |
| Duration: {baseline.duration_ms:>6,.0f}ms {optimized.duration_ms:>6,.0f}ms | |
| Tool Calls: {baseline.tool_calls:>6} {optimized.tool_calls:>6} | |
| """) | |
| if baseline.input_tokens > optimized.input_tokens: | |
| saved = baseline.input_tokens - optimized.input_tokens | |
| percent = (saved / baseline.input_tokens) * 100 | |
| print(f" β¨ Tokens Saved: {saved:,} ({percent:.1f}% reduction)") | |
| print(f" π° Estimated Cost Savings: {percent:.0f}% on input tokens") | |
| print("\n" + "=" * 70) | |
| print("BASELINE RESPONSE (excerpt):") | |
| print("=" * 70) | |
| print(baseline.response[:1500] if len(baseline.response) > 1500 else baseline.response) | |
| print("\n" + "=" * 70) | |
| print("HEADROOM RESPONSE (excerpt):") | |
| print("=" * 70) | |
| print(optimized.response[:1500] if len(optimized.response) > 1500 else optimized.response) | |
| if __name__ == "__main__": | |
| main() | |