Files
drug-discovery-prompts/upstream/mims-harvard-ToolUniverse/skills/devtu-create-tool/references/advanced-patterns.md

30 KiB
Raw Permalink Blame History

title, task, lineage_type, upstream_source, upstream_sha, imported_at, prompt_class, upstream_changes, author, validated
title task lineage_type upstream_source upstream_sha imported_at prompt_class upstream_changes author validated
Advanced Tool Implementation Patterns import https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/devtu-create-tool/references/advanced-patterns.md e2520a96 2026-06-26 prompt accepted upstream false

Advanced Tool Implementation Patterns

This reference covers advanced patterns and techniques for ToolUniverse tool development.

Offline / Computational Tools

Some tools require no network access at all — they perform purely local calculations using mathematical formulas, lookup tables, or parametric models. These tools are faster, more reliable, and easier to test than API-backed tools.

Minimal Offline Tool Skeleton

# my_calculator_tool.py
import math
from typing import Dict, Any, Optional
from tooluniverse.base_tool import BaseTool
from tooluniverse.tool_registry import register_tool

@register_tool("MyCalculatorTool")
class MyCalculatorTool(BaseTool):
    """
    Brief description.  Runs entirely offline — no network requests.
    """

    def __init__(self, tool_config: Dict[str, Any]):
        super().__init__(tool_config)
        fields = tool_config.get("fields", {})
        self.operation = fields.get("operation", "default_op")

    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        try:
            # operation can come from fields (set in JSON) or runtime arg
            op = arguments.get("operation") or self.operation

            def _get(key: str) -> Optional[float]:
                val = arguments.get(key)
                return float(val) if val is not None else None

            x = _get("x")
            if x is None:
                return {"status": "error", "message": "Missing required parameter: x"}

            result_value = x * 2          # your formula here

            return {
                "status": "success",
                "data": {
                    "operation": op,
                    "result": result_value,
                    "result_formatted": f"{result_value:.4g}",
                },
                "metadata": {
                    "note": "Runs offline — no network request.",
                    "formula": "result = x × 2",
                },
            }
        except ValueError as e:
            return {"status": "error", "message": str(e)}
        except Exception as e:
            return {"status": "error", "message": f"Unexpected error: {str(e)}"}

Key rules for offline tools:

  • Return {"status": "error", "message": "..."} — use "message" (not "error") to match the circuit plugin convention.
  • Include a "metadata" block that names the formula(s) used and notes "Runs offline".
  • Always float(val) when extracting numeric arguments from the dict (they may arrive as strings).
  • Use ValueError for domain validation; let the outer except Exception catch everything else.

SI Formatting Helper

Include a local _fmt_si() helper in every offline calculation tool for consistent output:

def _fmt_si(value: float, unit: str) -> str:
    """Format a numeric value with SI prefix and unit label."""
    abs_v = abs(value)
    if abs_v == 0:
        return f"0 {unit}"
    if abs_v >= 1e9:  return f"{value / 1e9:.4g} G{unit}"
    if abs_v >= 1e6:  return f"{value / 1e6:.4g} M{unit}"
    if abs_v >= 1e3:  return f"{value / 1e3:.4g} k{unit}"
    if abs_v >= 1:    return f"{value:.4g} {unit}"
    if abs_v >= 1e-3: return f"{value * 1e3:.4g} m{unit}"
    if abs_v >= 1e-6: return f"{value * 1e6:.4g} µ{unit}"
    if abs_v >= 1e-9: return f"{value * 1e9:.4g} n{unit}"
    return f"{value * 1e12:.4g} p{unit}"

Always include both the raw numeric field and a _formatted string sibling so LLMs can display human-readable values without reformatting:

"data": {
    "delay_ps": 34.0,
    "delay_formatted": "34 ps",        # human-readable sibling
    "frequency_Hz": 4.66e9,
    "frequency_formatted": "4.66 GHz", # human-readable sibling
}

Technology-Node Lookup Tables

Many chip-design quantities are tabulated per process node. Use a Dict[int, ...] keyed by node in nm with a _nearest_node() helper that falls back to the closest entry:

# Empirical FO4 delay values in ps, one per node
_FO4_TABLE: Dict[int, float] = {
    180: 250.0,
    130: 170.0,
    90:  115.0,
    65:   80.0,
    45:   55.0,
    32:   40.0,
    28:   34.0,
    20:   26.0,
    16:   20.0,
    10:   15.0,
    7:    11.0,
}

_SORTED_NODES = sorted(_FO4_TABLE.keys())

def _nearest_node(node_nm: float) -> int:
    """Return the nearest supported technology node for a given nm value."""
    return min(_SORTED_NODES, key=lambda n: abs(n - node_nm))

When the user's requested node is not in the table, include a "warning" field (not an error) in the data dict and continue with the nearest match:

nearest = _nearest_node(node_nm)
fo4_ps  = _FO4_TABLE[nearest]

warning = None
if nearest != int(node_nm):
    warning = (
        f"Node {node_nm} nm not in table; "
        f"using nearest supported node {nearest} nm."
    )

result = {
    "node_nm": nearest,
    "fo4_delay_ps": fo4_ps,
    # ...
}
if warning:
    result["warning"] = warning      # add only when needed

Also expose "supported_nodes_nm": _SORTED_NODES in the response so callers know which values are available without reading the source code.

Warning Fields vs Error Returns

Use these two patterns consistently:

Situation Pattern
Input is technically valid but uses a fallback (e.g. nearest node) data["warning"] = "..." — return status: success
Input violates a hard physical constraint (e.g. negative capacitance) raise ValueError("...") — caught → status: error, message: ...
A computed threshold is exceeded (e.g. ground bounce > 100 mV) data["warning"] = "..." + data["exceeds_threshold"] = True — still status: success

Example — threshold warning pattern:

THRESHOLD_V = 0.100  # 100 mV

v_noise = L * n * di_dt   # computation
exceeds = v_noise > THRESHOLD_V

result = {
    "ground_bounce_V":    v_noise,
    "ground_bounce_mV":   v_noise * 1e3,
    "exceeds_threshold":  exceeds,
}
if exceeds:
    result["warning"] = (
        f"Ground bounce {v_noise * 1e3:.1f} mV exceeds "
        f"{THRESHOLD_V * 1e3:.0f} mV threshold."
    )

Multi-Operation Offline Tool (fields.operation dispatch)

For tools that share the same physics domain but have multiple distinct computations, use the fields.operation dispatch pattern. The JSON config stores the default operation in "fields": { "operation": "op_name" }, and the runtime argument can override it:

# In JSON config (one entry per operation):
{
  "name": "Circuit_fo4_delay",
  "type": "FO4DelayTool",
  "fields": { "operation": "lookup" },    # default for this tool entry
  ...
}

# In __init__:
def __init__(self, tool_config):
    super().__init__(tool_config)
    self.operation = tool_config.get("fields", {}).get("operation", "lookup")

# In run():
op = arguments.get("operation") or self.operation   # runtime arg takes priority
if op == "lookup":
    ...
elif op == "estimate_path":
    ...
else:
    return {"status": "error", "message": f"Unknown operation '{op}'.  Valid: ..."}

This lets you expose a single Python class and multiple JSON entries (one per default operation) — or a single JSON entry that accepts the operation as a runtime argument.

Caching Strategies

Simple LRU Cache

from functools import lru_cache
import json

@register_tool("CachedAPITool")
class CachedAPITool(BaseTool):
    """Tool with response caching."""
    
    @lru_cache(maxsize=128)
    def _cached_request(self, url: str, params_json: str):
        """Cache API responses using LRU cache."""
        params = json.loads(params_json)
        response = requests.get(url, params=params)
        return response.json()
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        url = "https://api.example.com/data"
        
        # Convert params to JSON string for cache key
        params_json = json.dumps(arguments, sort_keys=True)
        
        try:
            data = self._cached_request(url, params_json)
            return {"status": "success", "data": data}
        except Exception as e:
            return {"status": "error", "error": str(e)}

Time-Based Cache

from datetime import datetime, timedelta
from typing import Optional

@register_tool("TimeCachedTool")
class TimeCachedTool(BaseTool):
    """Tool with time-based caching."""
    
    def __init__(self):
        super().__init__()
        self._cache = {}
        self._cache_timeout = timedelta(minutes=15)
    
    def _get_cached(self, key: str) -> Optional[Dict]:
        """Get cached value if not expired."""
        if key in self._cache:
            data, timestamp = self._cache[key]
            if datetime.now() - timestamp < self._cache_timeout:
                return data
            else:
                del self._cache[key]
        return None
    
    def _set_cached(self, key: str, data: Dict):
        """Store value in cache with timestamp."""
        self._cache[key] = (data, datetime.now())
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        cache_key = json.dumps(arguments, sort_keys=True)
        
        # Check cache first
        cached = self._get_cached(cache_key)
        if cached:
            return {
                "status": "success",
                "data": cached,
                "cached": True
            }
        
        # Fetch fresh data
        try:
            response = requests.get(url, params=arguments)
            data = response.json()
            
            # Store in cache
            self._set_cached(cache_key, data)
            
            return {
                "status": "success",
                "data": data,
                "cached": False
            }
        except Exception as e:
            return {"status": "error", "error": str(e)}

Batch Processing

Batch Request Tool

from typing import List

@register_tool("BatchTool")
class BatchTool(BaseTool):
    """Process multiple requests in a single call."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        ids = arguments.get('ids', [])
        batch_size = arguments.get('batch_size', 10)
        
        if not ids:
            return {
                "status": "error",
                "error": "ids parameter is required"
            }
        
        results = []
        errors = []
        
        # Process in batches
        for i in range(0, len(ids), batch_size):
            batch = ids[i:i + batch_size]
            
            try:
                batch_results = self._fetch_batch(batch)
                results.extend(batch_results)
            except Exception as e:
                errors.append({
                    "batch": batch,
                    "error": str(e)
                })
        
        return {
            "status": "success" if not errors else "partial",
            "count": len(results),
            "results": results,
            "errors": errors if errors else None
        }
    
    def _fetch_batch(self, ids: List[str]) -> List[Dict]:
        """Fetch data for a batch of IDs."""
        response = requests.post(
            "https://api.example.com/batch",
            json={"ids": ids},
            timeout=60
        )
        response.raise_for_status()
        return response.json()

Parallel Requests

from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List

@register_tool("ParallelTool")
class ParallelTool(BaseTool):
    """Execute multiple requests in parallel."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        ids = arguments.get('ids', [])
        max_workers = min(arguments.get('max_workers', 5), 10)
        
        results = []
        errors = []
        
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            # Submit all requests
            future_to_id = {
                executor.submit(self._fetch_single, id_): id_
                for id_ in ids
            }
            
            # Collect results as they complete
            for future in as_completed(future_to_id):
                id_ = future_to_id[future]
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    errors.append({
                        "id": id_,
                        "error": str(e)
                    })
        
        return {
            "status": "success" if not errors else "partial",
            "count": len(results),
            "results": results,
            "errors": errors if errors else None
        }
    
    def _fetch_single(self, id_: str) -> Dict:
        """Fetch data for a single ID."""
        response = requests.get(
            f"https://api.example.com/items/{id_}",
            timeout=30
        )
        response.raise_for_status()
        return response.json()

Streaming and Pagination

Auto-Pagination Tool

@register_tool("AutoPaginationTool")
class AutoPaginationTool(BaseTool):
    """Automatically fetch all pages."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get('query')
        max_pages = arguments.get('max_pages', 10)
        
        all_results = []
        page = 1
        
        while page <= max_pages:
            try:
                response = requests.get(
                    "https://api.example.com/search",
                    params={
                        'query': query,
                        'page': page,
                        'page_size': 100
                    },
                    timeout=30
                )
                response.raise_for_status()
                data = response.json()
                
                results = data.get('results', [])
                all_results.extend(results)
                
                # Stop if no more results
                if not data.get('next'):
                    break
                
                page += 1
                
            except Exception as e:
                return {
                    "status": "error",
                    "error": f"Failed on page {page}: {str(e)}",
                    "partial_results": all_results
                }
        
        return {
            "status": "success",
            "count": len(all_results),
            "pages": page,
            "results": all_results
        }

Cursor-Based Pagination

@register_tool("CursorPaginationTool")
class CursorPaginationTool(BaseTool):
    """Handle cursor-based pagination."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        cursor = arguments.get('cursor')
        page_size = arguments.get('page_size', 20)
        
        params = {'page_size': page_size}
        if cursor:
            params['cursor'] = cursor
        
        try:
            response = requests.get(
                "https://api.example.com/items",
                params=params,
                timeout=30
            )
            response.raise_for_status()
            data = response.json()
            
            return {
                "status": "success",
                "count": len(data.get('results', [])),
                "next_cursor": data.get('next_cursor'),
                "has_more": data.get('has_more', False),
                "results": data.get('results', [])
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

Authentication Patterns

API Key Authentication

import os

@register_tool("APIKeyTool")
class APIKeyTool(BaseTool):
    """Tool with API key authentication."""
    
    def __init__(self):
        super().__init__()
        self.api_key = os.environ.get('API_KEY')
        if not self.api_key:
            raise ValueError("API_KEY environment variable not set")
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        try:
            response = requests.get(
                "https://api.example.com/data",
                headers={"X-API-Key": self.api_key},
                params=arguments,
                timeout=30
            )
            response.raise_for_status()
            
            return {
                "status": "success",
                "data": response.json()
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

OAuth Token Authentication

@register_tool("OAuthTool")
class OAuthTool(BaseTool):
    """Tool with OAuth token authentication."""
    
    def __init__(self):
        super().__init__()
        self.token = self._get_token()
    
    def _get_token(self) -> str:
        """Get or refresh OAuth token."""
        client_id = os.environ.get('CLIENT_ID')
        client_secret = os.environ.get('CLIENT_SECRET')
        
        response = requests.post(
            "https://api.example.com/oauth/token",
            data={
                'grant_type': 'client_credentials',
                'client_id': client_id,
                'client_secret': client_secret
            }
        )
        response.raise_for_status()
        return response.json()['access_token']
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        try:
            response = requests.get(
                "https://api.example.com/data",
                headers={"Authorization": f"Bearer {self.token}"},
                params=arguments,
                timeout=30
            )
            response.raise_for_status()
            
            return {
                "status": "success",
                "data": response.json()
            }
        except requests.HTTPError as e:
            if e.response.status_code == 401:
                # Token expired, refresh and retry
                self.token = self._get_token()
                return self.run(arguments)
            return {
                "status": "error",
                "error": str(e)
            }

Data Transformation

Field Selection and Projection

@register_tool("ProjectionTool")
class ProjectionTool(BaseTool):
    """Tool with field selection support."""
    
    ALLOWED_FIELDS = {
        'id', 'name', 'description', 'created_date',
        'updated_date', 'status', 'author', 'tags'
    }
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        id_ = arguments.get('id')
        fields = arguments.get('fields', ['id', 'name'])
        
        # Validate fields
        invalid = set(fields) - self.ALLOWED_FIELDS
        if invalid:
            return {
                "status": "error",
                "error": f"Invalid fields: {invalid}",
                "allowed_fields": list(self.ALLOWED_FIELDS)
            }
        
        try:
            # Fetch full data
            response = requests.get(
                f"https://api.example.com/items/{id_}",
                timeout=30
            )
            response.raise_for_status()
            full_data = response.json()
            
            # Project only requested fields
            projected = {
                k: v for k, v in full_data.items()
                if k in fields
            }
            
            return {
                "status": "success",
                "data": projected
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

Data Filtering

@register_tool("FilterTool")
class FilterTool(BaseTool):
    """Tool with client-side filtering."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get('query')
        filters = arguments.get('filters', {})
        
        try:
            # Fetch data
            response = requests.get(
                "https://api.example.com/search",
                params={'query': query},
                timeout=30
            )
            response.raise_for_status()
            data = response.json()
            
            # Apply filters
            results = data.get('results', [])
            filtered = self._apply_filters(results, filters)
            
            return {
                "status": "success",
                "count": len(filtered),
                "total": len(results),
                "results": filtered
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }
    
    def _apply_filters(self, results: List[Dict], filters: Dict) -> List[Dict]:
        """Apply filters to results."""
        filtered = results
        
        # Status filter
        if 'status' in filters:
            status = filters['status']
            filtered = [r for r in filtered if r.get('status') == status]
        
        # Date range filter
        if 'date_from' in filters:
            date_from = filters['date_from']
            filtered = [
                r for r in filtered
                if r.get('date', '') >= date_from
            ]
        
        if 'date_to' in filters:
            date_to = filters['date_to']
            filtered = [
                r for r in filtered
                if r.get('date', '') <= date_to
            ]
        
        # Tag filter
        if 'tags' in filters:
            required_tags = set(filters['tags'])
            filtered = [
                r for r in filtered
                if required_tags.issubset(set(r.get('tags', [])))
            ]
        
        return filtered

GraphQL Integration

GraphQL Query Tool

@register_tool("GraphQLTool")
class GraphQLTool(BaseTool):
    """Execute GraphQL queries."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get('query')
        variables = arguments.get('variables', {})
        
        if not query:
            return {
                "status": "error",
                "error": "query parameter is required"
            }
        
        try:
            response = requests.post(
                "https://api.example.com/graphql",
                json={
                    'query': query,
                    'variables': variables
                },
                timeout=30
            )
            response.raise_for_status()
            result = response.json()
            
            # Check for GraphQL errors
            if 'errors' in result:
                return {
                    "status": "error",
                    "error": "GraphQL query failed",
                    "details": result['errors']
                }
            
            return {
                "status": "success",
                "data": result.get('data')
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

GraphQL with Predefined Queries

@register_tool("PredefinedGraphQLTool")
class PredefinedGraphQLTool(BaseTool):
    """GraphQL tool with predefined queries."""
    
    QUERIES = {
        'get_drug': """
            query GetDrug($id: ID!) {
                drug(id: $id) {
                    id
                    name
                    description
                    manufacturer
                }
            }
        """,
        'search_drugs': """
            query SearchDrugs($query: String!, $limit: Int) {
                searchDrugs(query: $query, limit: $limit) {
                    id
                    name
                    manufacturer
                }
            }
        """
    }
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        operation = arguments.get('operation')
        variables = arguments.get('variables', {})
        
        if operation not in self.QUERIES:
            return {
                "status": "error",
                "error": f"Unknown operation: {operation}",
                "available_operations": list(self.QUERIES.keys())
            }
        
        query = self.QUERIES[operation]
        
        try:
            response = requests.post(
                "https://api.example.com/graphql",
                json={
                    'query': query,
                    'variables': variables
                },
                timeout=30
            )
            response.raise_for_status()
            result = response.json()
            
            if 'errors' in result:
                return {
                    "status": "error",
                    "error": "GraphQL query failed",
                    "details": result['errors']
                }
            
            return {
                "status": "success",
                "data": result.get('data')
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

Rate Limiting

Simple Rate Limiter

from time import time, sleep
from collections import deque

@register_tool("RateLimitedTool")
class RateLimitedTool(BaseTool):
    """Tool with rate limiting."""
    
    def __init__(self):
        super().__init__()
        self.requests = deque()
        self.max_requests = 10  # 10 requests
        self.time_window = 60   # per 60 seconds
    
    def _wait_if_needed(self):
        """Wait if rate limit would be exceeded."""
        now = time()
        
        # Remove requests outside time window
        while self.requests and self.requests[0] < now - self.time_window:
            self.requests.popleft()
        
        # Wait if at limit
        if len(self.requests) >= self.max_requests:
            sleep_time = self.requests[0] + self.time_window - now
            if sleep_time > 0:
                sleep(sleep_time)
            self.requests.popleft()
        
        # Record this request
        self.requests.append(now)
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        self._wait_if_needed()
        
        try:
            response = requests.get(
                "https://api.example.com/data",
                params=arguments,
                timeout=30
            )
            response.raise_for_status()
            
            return {
                "status": "success",
                "data": response.json()
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

Webhook and Async Operations

Async Job Polling Tool

@register_tool("AsyncJobTool")
class AsyncJobTool(BaseTool):
    """Tool that polls for async job completion."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get('query')
        timeout = arguments.get('timeout', 300)  # 5 minutes default
        poll_interval = arguments.get('poll_interval', 5)  # 5 seconds
        
        try:
            # Start async job
            response = requests.post(
                "https://api.example.com/jobs",
                json={'query': query},
                timeout=30
            )
            response.raise_for_status()
            job_data = response.json()
            job_id = job_data['job_id']
            
            # Poll for completion
            start_time = time()
            while time() - start_time < timeout:
                status_response = requests.get(
                    f"https://api.example.com/jobs/{job_id}",
                    timeout=30
                )
                status_response.raise_for_status()
                status = status_response.json()
                
                if status['state'] == 'completed':
                    return {
                        "status": "success",
                        "job_id": job_id,
                        "data": status['result']
                    }
                elif status['state'] == 'failed':
                    return {
                        "status": "error",
                        "error": "Job failed",
                        "detail": status.get('error')
                    }
                
                sleep(poll_interval)
            
            return {
                "status": "timeout",
                "error": f"Job did not complete within {timeout} seconds",
                "job_id": job_id
            }
            
        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

Multi-Source Aggregation

Aggregation Tool

@register_tool("AggregationTool")
class AggregationTool(BaseTool):
    """Aggregate data from multiple sources."""
    
    def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
        query = arguments.get('query')
        sources = arguments.get('sources', ['source1', 'source2', 'source3'])
        
        results = []
        errors = []
        
        for source in sources:
            try:
                data = self._fetch_from_source(source, query)
                results.extend(data)
            except Exception as e:
                errors.append({
                    "source": source,
                    "error": str(e)
                })
        
        # Deduplicate by ID
        seen_ids = set()
        unique_results = []
        for result in results:
            id_ = result.get('id')
            if id_ not in seen_ids:
                seen_ids.add(id_)
                unique_results.append(result)
        
        return {
            "status": "success" if not errors else "partial",
            "count": len(unique_results),
            "sources_queried": len(sources),
            "sources_failed": len(errors),
            "results": unique_results,
            "errors": errors if errors else None
        }
    
    def _fetch_from_source(self, source: str, query: str) -> List[Dict]:
        """Fetch data from a specific source."""
        url_map = {
            'source1': 'https://api1.example.com/search',
            'source2': 'https://api2.example.com/query',
            'source3': 'https://api3.example.com/find'
        }
        
        url = url_map.get(source)
        if not url:
            raise ValueError(f"Unknown source: {source}")
        
        response = requests.get(url, params={'q': query}, timeout=30)
        response.raise_for_status()
        
        return response.json().get('results', [])