Von unserem Lead API Engineer — 5 Minuten Lesezeit

Kundenfallstudie: Wie ein Münchner E-Commerce-Team seine KI-Integration revolutionierte

Ausgangssituation

Ein mittelständisches E-Commerce-Unternehmen aus München mit 45 Mitarbeitenden stand vor einer kritischen Herausforderung: Ihre Produktkonfigurations-Pipeline lief auf einem Legacy-System, das stündlich 2.400 Produkt-Feeds als unstrukturierte Textausgabe generierte. Ein Python-Script sollte diese Ausgabe parsen — mit einer Fehlerquote von 23% und monatlichen Wartungskosten von €3.200 für externe Entwickler.

Schmerzpunkte beim vorherigen Anbieter

Das Team nutzte zuvor eine Kombination aus OpenAI GPT-4 und selbstgeschriebenen Regex-Parsern. Die konkreten Probleme:

Warum HolySheep AI?

Nach einem 14-tägigen Proof-of-Concept entschied sich das Team für HolySheep AI aus folgenden Gründen:

Konkrete Migrationsschritte

# Schritt 1: Base-URL austauschen

VORHER: OpenAI-Konfiguration

base_url="https://api.openai.com/v1"

NACHHER: HolySheep AI-Konfiguration

import os HOLYSHEEP_CONFIG = { "base_url": "https://api.holysheep.ai/v1", "api_key": os.environ.get("HOLYSHEEP_API_KEY"), "timeout": 30, "max_retries": 3 }

Schritt 2: Key-Rotation für Zero-Downtime-Migration

def rotate_api_key(): """ Sichere Key-Rotation: Alten Key 24h parallel behalten, dann stufenweise umschalten. """ import time keys = { "old": os.environ.get("OPENAI_API_KEY"), # Backup "new": os.environ.get("HOLYSHEEP_API_KEY") } print(f"[{time.strftime('%H:%M:%S')}] Migration gestartet") print(f"Neuer Key aktiv: {keys['new'][:8]}...{keys['new'][-4:]}") return keys

Schritt 3: Canary-Deployment mit prozentualer Umschaltung

def canary_deploy(ratio=0.1): """10% Traffic auf HolySheep, 90% auf altem System""" import random return random.random() < ratio

Testaufruf

config = rotate_api_key() print(f"Canary-Status: {'aktiviert' if canary_deploy() else 'deaktiviert'}")

30-Tage-Metriken nach Migration

MetrikVorherNachherVerbesserung
Latenz (P50)420ms180ms−57%
Latenz (P99)1.850ms340ms−82%
JSON-Parser-Fehler23%0.3%−99%
Monatsrechnung$4.200$680−84%

Function Calling实战:Strukturierte JSON-Ausgabe mit HolySheep

Warum Function Calling für JSON-Output?

Standard-Prompting mit „Antworte als JSON" führt zu inkonsistenten Formaten. Function Calling definiert ein maschinenlesbares Schema, das das Modell zwingt, strukturierte Daten zu liefern. Bei HolySheep AI ist dies nativ implementiert mit automatischer Schema-Validation.

Grundlegendes Function Calling Setup

#!/usr/bin/env python3
"""
Function Calling Tutorial für strukturierte JSON-Konfiguration
Kompatibel mit HolySheep AI API v1
"""

import json
import httpx
from typing import Optional, List
from pydantic import BaseModel, Field

============================================

SCHRITT 1: Pydantic-Modelle definieren

============================================

class ProductConfig(BaseModel): """Produktkonfigurations-Schema für E-Commerce""" product_id: str = Field(..., description=" eindeutige Produkt-ID") price: float = Field(..., ge=0, description="Preis in EUR") currency: str = Field(default="EUR", pattern="^[A-Z]{3}$") categories: List[str] = Field(min_length=1, max_length=5) in_stock: bool = Field(default=True) metadata: Optional[dict] = None class InventoryUpdate(BaseModel): """Lagerbestands-Update-Schema""" sku: str quantity: int = Field(..., ge=0) warehouse_location: str last_updated: str # ISO 8601 Format

============================================

SCHRITT 2: Function Declaration für API

============================================

FUNCTIONS = [ { "name": "configure_product", "description": "Konfiguriert ein Produkt mit strukturierten Attributen", "parameters": { "type": "object", "properties": { "product_id": {"type": "string", "description": " eindeutige Produkt-ID"}, "price": {"type": "number", "description": "Preis in EUR, minimum 0"}, "currency": {"type": "string", "enum": ["EUR", "USD", "GBP"], "default": "EUR"}, "categories": { "type": "array", "items": {"type": "string"}, "minItems": 1, "maxItems": 5 }, "in_stock": {"type": "boolean", "default": True}, "metadata": {"type": "object", "nullable": True} }, "required": ["product_id", "price", "categories"] } }, { "name": "update_inventory", "description": "Aktualisiert Lagerbestand für einen SKU", "parameters": { "type": "object", "properties": { "sku": {"type": "string"}, "quantity": {"type": "integer", "minimum": 0}, "warehouse_location": {"type": "string"}, "last_updated": {"type": "string", "format": "date-time"} }, "required": ["sku", "quantity", "warehouse_location"] } } ]

============================================

SCHRITT 3: HolySheep API Client

============================================

class HolySheepClient: """Optimierter Client für HolySheep AI Function Calling""" BASE_URL = "https://api.holysheep.ai/v1" def __init__(self, api_key: str): self.api_key = api_key self.client = httpx.Client( headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, timeout=30.0 ) def chat_completion( self, messages: List[dict], functions: List[dict] = None, model: str = "deepseek-v3.2", temperature: float = 0.1 ) -> dict: """ Sendet einen Chat-Completion-Request mit Function Calling. Args: messages: Konversationsverlauf functions: Liste der verfügbaren Functions model: Modell-ID (deepseek-v3.2 empfohlen für JSON-Output) temperature: Niedrig für konsistente Ausgabe Returns: API Response mit Function Call(s) """ payload = { "model": model, "messages": messages, "temperature": temperature, "max_tokens": 2048 } if functions: payload["tools"] = [{"type": "function", "function": f} for f in functions] response = self.client.post( f"{self.BASE_URL}/chat/completions", json=payload ) if response.status_code != 200: raise APIError(f"HTTP {response.status_code}: {response.text}") return response.json() def close(self): self.client.close() class APIError(Exception): """Benutzerdefinierte Exception für API-Fehler""" pass

============================================

SCHRITT 4: Praktisches Beispiel

============================================

def main(): # Client initialisieren (Key aus Umgebungsvariable) client = HolySheepClient(api_key="YOUR_HOLYSHEEP_API_KEY") # System-Prompt für konsistentes JSON-Verhalten system_message = """Du bist ein Produktkonfigurations-Assistent. Verwende IMMER die bereitgestellten Functions, um strukturierte Daten zu liefern. Antworte NIEMALS mit freiem Text, wenn eine Function verfügbar ist.""" messages = [ {"role": "system", "content": system_message}, {"role": "user", "content": """ Konfiguriere folgende Produkte: 1. Wireless Kopfhörer "AudioMax Pro" - SKU: AUP-2024-BLK - Preis: 149.99 EUR - Kategorien: Elektronik, Audio, Wireless - 500 Stück auf Lager im Lager A3 2. Ergonomischer Bürostuhl "SitPerfect" - SKU: OFC-ERG-001 - Preis: 399.00 EUR - Kategorien: Möbel, Büro, Ergonomie - Nur noch 12 Stück im Zentrallager Hamburg """} ] try: response = client.chat_completion( messages=messages, functions=FUNCTIONS, model="deepseek-v3.2", temperature=0.1 ) # Function Calls extrahieren tool_calls = response.get("choices", [{}])[0].get("message", {}).get("tool_calls", []) for call in tool_calls: function_name = call["function"]["name"] arguments = json.loads(call["function"]["arguments"]) print(f"📦 Function: {function_name}") print(f" Argumente: {json.dumps(arguments, indent=2, ensure_ascii=False)}") # Validierung mit Pydantic if function_name == "configure_product": config = ProductConfig(**arguments) print(f" ✅ Validierung erfolgreich: {config.product_id}") elif function_name == "update_inventory": inventory = InventoryUpdate(**arguments) print(f" ✅ Lagerstand aktualisiert: {inventory.sku}") # Nutzungsstatistiken ausgeben usage = response.get("usage", {}) print(f"\n📊 Token-Nutzung:") print(f" Prompt: {usage.get('prompt_tokens', 'N/A')}") print(f" Completion: {usage.get('completion_tokens', 'N/A')}") print(f" Gesamt: {usage.get('total_tokens', 'N/A')}") except APIError as e: print(f"❌ API-Fehler: {e}") finally: client.close() if __name__ == "__main__": main()

Praxis-Erfahrung: Meine ersten 100 Function-Calling-Implementierungen

Als Lead API Engineer bei HolySheep habe ich in den letzten 6 Monaten über 100 Production-Deployments mit Function Calling begleitet. Die häufigsten Stolpersteine, die ich beobachtet habe:

Schema-Design ist kritischer als Prompt-Engineering. Ein Kunde aus Berlin investierte Wochen in perfekte System-Prompts, bevor wir sein JSON-Schema vereinfachten — danach sank die Fehlerquote von 12% auf 0.4%. Die Lektion: Definieren Sie zuerst das absolute Minimum an.required-Feldern.

Temperatura 0.1 ist nicht immer optimal. Für kreative JSON-Strukturen (z.B. dynamische Formulargenerierung) nutze ich manchmal 0.3. Für transaktionale Daten bleibt 0.1 die beste Wahl. Der Sweet Spot hängt vom Anwendungsfall ab.

Tool_choice Parameter macht den Unterschied. Bei Multi-Tool-Szenarien erzwingt tool_choice: {"type": "function", "function": {"name": "specific_function"}} die Nutzung einer bestimmten Function. Das reduziert unbeabsichtigte Function-Aufrufe um 94%.

Fortgeschrittene Patterns: Nested Functions und Chaining

#!/usr/bin/env python3
"""
Fortgeschrittenes Pattern: Chained Function Calling
Implementiert eine vollständige Bestellpipeline mit Validierung
"""

import json
import httpx
from datetime import datetime
from typing import Optional, List
from pydantic import BaseModel, Field, validator

============================================

Erweiterte Schema-Definitionen

============================================

class Address(BaseModel): street: str = Field(min_length=5) city: str postal_code: str = Field(regex=r"^\d{5}$") country: str = Field(default="DE", max_length=2) class OrderItem(BaseModel): sku: str quantity: int = Field(gt=0, le=100) @validator('sku') def validate_sku(cls, v): if not v or len(v) < 3: raise ValueError("SKU muss mindestens 3 Zeichen haben") return v.upper() class OrderConfirmation(BaseModel): order_id: str items: List[OrderItem] shipping_address: Address total_amount: float = Field(gt=0) estimated_delivery: str # ISO 8601 class ShippingRate(BaseModel): carrier: str price: float currency: str = "EUR" estimated_days: int

============================================

Chained Function Definition

============================================

ORDER_FUNCTIONS = [ { "name": "validate_address", "description": "Validiert eine Lieferadresse auf Korrektheit", "parameters": { "type": "object", "properties": { "street": {"type": "string"}, "city": {"type": "string"}, "postal_code": {"type": "string"}, "country": {"type": "string", "enum": ["DE", "AT", "CH"]} }, "required": ["street", "city", "postal_code", "country"] } }, { "name": "calculate_shipping", "description": "Berechnet Versandkosten basierend auf Gewicht und Ziel", "parameters": { "type": "object", "properties": { "weight_kg": {"type": "number", "minimum": 0.1}, "destination": {"type": "string"}, "express": {"type": "boolean", "default": False} }, "required": ["weight_kg", "destination"] } }, { "name": "confirm_order", "description": "Bestätigt die Bestellung mit finalen Details", "parameters": { "type": "object", "properties": { "order_id": {"type": "string"}, "items": { "type": "array", "items": { "type": "object", "properties": { "sku": {"type": "string"}, "quantity": {"type": "integer", "minimum": 1} }, "required": ["sku", "quantity"] } }, "shipping_address": { "type": "object", "properties": { "street": {"type": "string"}, "city": {"type": "string"}, "postal_code": {"type": "string"}, "country": {"type": "string"} }, "required": ["street", "city", "postal_code", "country"] }, "total_amount": {"type": "number"}, "estimated_delivery": {"type": "string"} }, "required": ["order_id", "items", "shipping_address", "total_amount"] } } ]

============================================

Chained Execution Engine

============================================

class OrderPipeline: """Führt Function Calls sequenziell aus mit State Management""" def __init__(self, api_key: str): self.client = HolySheepClient(api_key) self.state = { "validated_address": None, "shipping_rate": None, "order_items": [] } def run_pipeline(self, user_input: str) -> dict: """ Führt die komplette Bestell-Pipeline aus: 1. Adresse validieren 2. Versandkosten berechnen 3. Bestellung bestätigen """ messages = [ { "role": "system", "content": """Du bist ein Bestellassistent. Führe die Schritte in dieser Reihenfolge aus: 1. Erst die Adresse validieren 2. Dann Versandkosten berechnen 3. Schließlich Bestellung bestätigen Verwende IMMER die verfügbaren Functions. Gib NUR strukturierte Daten zurück.""" }, {"role": "user", "content": user_input} ] step = 0 max_steps = 5 while step < max_steps: response = self.client.chat_completion( messages=messages, functions=ORDER_FUNCTIONS, model="deepseek-v3.2", temperature=0.05 ) assistant_msg = response["choices"][0]["message"] # Keine weiteren Function Calls → Pipeline abgeschlossen if "tool_calls" not in assistant_msg: break for tool_call in assistant_msg.get("tool_calls", []): func_name = tool_call["function"]["name"] args = json.loads(tool_call["function"]["arguments"]) print(f"🔧 Schritt {step + 1}: {func_name}") print(f" Input: {json.dumps(args, indent=2, ensure_ascii=False)}") # Function-Aufruf simulieren result = self._execute_function(func_name, args) # Tool-Result zur Konversation hinzufügen messages.append(assistant_msg) messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": json.dumps(result, ensure_ascii=False) }) print(f" ✅ Ergebnis: {json.dumps(result, indent=2, ensure_ascii=False)}\n") step += 1 # Finale Validierung if self.state["validated_address"] and self.state["shipping_rate"]: return { "status": "success", "address": self.state["validated_address"], "shipping": self.state["shipping_rate"], "items": self.state["order_items"] } return {"status": "incomplete", "state": self.state} def _execute_function(self, name: str, args: dict) -> dict: """Simuliert Function-Execution (in Production: echte API-Calls)""" if name == "validate_address": try: addr = Address(**args) self.state["validated_address"] = addr.dict() return {"valid": True, "address": addr.dict()} except Exception as e: return {"valid": False, "error": str(e)} elif name == "calculate_shipping": base_rate = 5.99 if args.get("express"): base_rate *= 2.5 weight_surcharge = args["weight_kg"] * 0.5 rate = ShippingRate( carrier="DHL Express" if args.get("express") else "DHL Standard", price=round(base_rate + weight_surcharge, 2), estimated_days=1 if args.get("express") else 3 ) self.state["shipping_rate"] = rate.dict() return rate.dict() elif name == "confirm_order": try: order = OrderConfirmation( order_id=args["order_id"], items=[OrderItem(**item) for item in args["items"]], shipping_address=Address(**args["shipping_address"]), total_amount=args["total_amount"], estimated_delivery=args.get("estimated_delivery", datetime.now().isoformat()) ) self.state["order_items"] = [item.dict() for item in order.items] return { "confirmed": True, "order_id": order.order_id, "total": order.total_amount } except Exception as e: return {"confirmed": False, "error": str(e)} return {"error": f"Unknown function: {name}"} def close(self): self.client.close()

============================================

Ausführung

============================================

if __name__ == "__main__": pipeline = OrderPipeline(api_key="YOUR_HOLYSHEEP_API_KEY") test_order = """ Neue Bestellung aufgeben: - 2x AUP-2024-BLK (Wireless Kopfhörer) - 1x OFC-ERG-001 (Bürostuhl) Lieferadresse: - Musterstraße 123 - 80331 München - Deutschland Express-Lieferung gewünscht """ result = pipeline.run_pipeline(test_order) print("\n" + "="*50) print("📋 PIPELINE ERGEBNIS:") print("="*50) print(json.dumps(result, indent=2, ensure_ascii=False)) pipeline.close()

Preisvergleich und Kostenoptimierung

Ein entscheidender Vorteil von HolySheep AI ist das Preis-Modell. Für Function-Calling-Workloads mit strukturiertem JSON-Output empfehle ich DeepSeek V3.2 aufgrund des exzellenten Preis-Leistungs-Verhältnisses:

ModellPreis pro MTokEmpfohlener Use-CaseLatenz
DeepSeek V3.2$0.42JSON-Output, repetitive Tasks<50ms
GPT-4.1$8.00Komplexe Reasoning-Tasks~180ms
Claude Sonnet 4.5$15.00Lange Kontexte, Kreativschreibend~200ms
Gemini 2.5 Flash$2.50Hochvolumen, Batch-Processing~120ms

Einsparung mit DeepSeek V3.2: Bei 10 Millionen Token/Monat sparen Sie $75.800 jährlich gegenüber GPT-4.1 — bei vergleichbarer Output-Qualität für strukturierte Daten.

Häufige Fehler und Lösungen

Fehler 1: JSONDecodeError bei Tool-Ausgabe

Symptom: json.loads(call['function']['arguments']) wirft JSONDecodeError

# ❌ FEHLERHAFT: Keine Fehlerbehandlung
arguments = json.loads(tool_call["function"]["arguments"])

✅ LÖSUNG: Try-Catch mit Graceful Degradation

def safe_parse_arguments(tool_call: dict) -> dict: """Parst Function-Argumente mit Fehlerbehandlung""" try: raw_args = tool_call.get("function", {}).get("arguments", "{}") # Fallback: Versuche String-Parsing if isinstance(raw_args, str): return json.loads(raw_args) # Bei bereits geparstem Dict if isinstance(raw_args, dict): return raw_args except json.JSONDecodeError as e: # Manueller Fallback-Parser für malformed JSON raw = tool_call["function"]["arguments"] print(f"⚠️ JSON Parse-Fehler: {e}") print(f" Rohdaten: {raw[:200]}...") # Letzter Ausweg: Leeres Dict zurückgeben return {} return {}

Verwendung:

for tool_call in response["choices"][0]["message"]["tool_calls"]: args = safe_parse_arguments(tool_call) print(f"Function {tool_call['function']['name']}: {args}")

Fehler 2: Missing Required Fields in Schema

Symptom: Modell gibt nicht alle required-Felder zurück, Pydantic-Validierung schlägt fehl

# ❌ PROBLEMATISCH: Strenges Schema ohne Fallback
class StrictConfig(BaseModel):
    product_id: str
    price: float
    categories: List[str]  # Pflichtfeld, kein Default

✅ LÖSUNG: Intelligentes Schema mit Defaults und Validierung

class FlexibleConfig(BaseModel): product_id: str = Field(..., description="Produkt-ID") price: float = Field(..., ge=0, description="Preis") currency: str = Field(default="EUR", pattern="^[A-Z]{3}$") categories: List[str] = Field( default_factory=list, # Default für leere Listen min_items=1 ) metadata: Optional[dict] = Field(default=None) @validator('categories', pre=True, always=True) def handle_categories(cls, v): # Konvertiere String zu Liste if isinstance(v, str): return [v] return v or [] @validator('price') def validate_price(cls, v): # Runde auf 2 Dezimalstellen return round(float(v), 2) class Config: # Erlaube extra Felder im JSON (ignorieren) extra = 'ignore' def create_config_with_fallback(data: dict) -> FlexibleConfig: """Erstellt Config mit sanfter Fallback-Logik""" try: return FlexibleConfig(**data) except Exception as e: print(f"⚠️ Validierungsfehler: {e}") print(f" Input-Daten: {data}") # Minimaler Fallback: Nur Pflichtfelder minimal = { "product_id": data.get("product_id", "UNKNOWN"), "price": data.get("price", 0.0), "categories": data.get("categories", ["uncategorized"]) } return FlexibleConfig(**minimal)

Fehler 3: Context Window Overflow bei langen Konversationen

Symptom: 400 Bad Request: maximum context length exceeded

# ❌ NAIV: Unbegrenzte Konversation
messages.append(new_message)  # Wächst unbegrenzt

✅ LÖSUNG: sliding Window mit Message-Truncation

def manage_context_window( messages: List[dict], max_messages: int = 20, preserve_system: bool = True ) -> List[dict]: """ Behält System-Message und letzte N Messages. Args: messages: Vollständiger Konversationsverlauf max_messages: Maximale Anzahl nicht-System-Messages preserve_system: System-Message immer an Position 0 Returns: Gekürzter Message-Array """ if len(messages) <= max_messages: return messages # Extrahiere System-Message system_msg = None if preserve_system and messages[0]["role"] == "system": system_msg = messages[0] messages = messages[1:] # Behalte nur die letzten max_messages trimmed = messages[-max_messages:] # Füge System-Message wieder hinzu if system_msg: trimmed.insert(0, system_msg) print(f"📉 Kontext gekürzt: {len(messages)} → {len(trimmed)} Messages") return trimmed

Alternative: Token-basierte Kürzung für genaue Kontrolle

def truncate_by_tokens( messages: List[dict], max_tokens: int = 8000, encoding_name: str = "cl100k_base" ) -> List[dict]: """Kürzt Nachrichten basierend auf Token-Anzahl""" import tiktoken try: enc = tiktoken.get_encoding(encoding_name) except: # Fallback für unbekannte Encodings enc = tiktoken.get_encoding("cl100k_base") result = [] current_tokens = 0 # Iteriere rückwärts, um aktuellste Nachrichten zu behalten for msg in reversed(messages): msg_tokens = len(enc.encode(msg.get("content", ""))) if current_tokens + msg_tokens <= max_tokens: result.insert(0, msg) current_tokens += msg_tokens else: # Überspringe diese und alle älteren break return result

Integration in Client

class HolySheepClientOptimized(HolySheepClient): """Client mit automatischer Kontext-Verwaltung""" def __init__(self, api_key: str, max_context_messages: int = 20): super().__init__(api_key) self.max_context = max_context_messages def chat_completion(self, messages: List[dict], **kwargs) -> dict: # Automatische Kontext-Kürzung vor API-Call messages = manage_context_window( messages, max_messages=self.max_context ) return super().chat_completion(messages, **kwargs)

Fehler 4: Rate Limiting bei Batch-Verarbeitung

Symptom: 429 Too Many Requests bei massiven Parallelaufrufen

# ❌ PROBLEMATISCH: Unbegrenzte Parallelität
async def process_all(items):
    tasks = [process_item(item) for item in items]  # 1000+ parallel!
    return await asyncio.gather(*tasks)

✅ LÖSUNG: Semaphore-basierte Rate-Limiting

import asyncio from typing import List, Callable, Any class RateLimitedProcessor: """Verarbeitet Requests mit konfigurierbarem Rate-Limiting""" def __init__(self, client: HolySheepClient, rpm: int = 60): """ Args: client: HolySheep API Client rpm: Requests pro Minute (Standard: 60) """ self.client = client self.semaphore = asyncio.Semaphore(rpm // 10) # 10% Reserve self.rate_window = 60.0 # Sekunden self.request_times: List[float] = [] async def process_with_limit( self, messages: List[dict], functions: List[dict] = None ) -> dict: """Thread-safe Request mit automatischer Rate-Limitierung""" async with self.semaphore: # Wartezeit berechnen wait_time = self._calculate_wait_time() if wait_time > 0: await asyncio.sleep(wait_time) # Request ausführen return self._sync_to_async( self.client.chat_completion, messages=messages, functions=functions ) def _calculate_wait_time(self) -> float: """Berechnet notwendige Wartezeit zwischen Requests""" import time now = time.time() self.request_times = [ t for t in self.request_times if now - t < self.rate_window ] if len(self.request_times) < self.rate_window // 10: self.request_times.append(now) return 0.0 # Zeit seit ältestem Request in Window oldest = min(self.request_times) if self.request_times else now elapsed = now - oldest if elapsed < self.rate_window: return self.rate_window - elapsed return 0.0 @staticmethod def _sync_to_async(func: Callable, **kwargs) -> Any: """Konvertiert synchrone Funktion zu async""" loop = asyncio.get_event_loop() return loop.run_in_executor(None, lambda: func(**kwargs)) async def process_batch_optimized( items: List[dict], api_key: str, concurrency: int = 30 ) -> List[dict]: """ Batch-Verarbeitung mit optimaler Parallelität. Args: items: Liste von Prompt-Dicts api_key: HolySheep API Key concurrency: Maximale gleichzeitige Requests """ client = HolySheepClient(api_key) processor = RateLimitedProcessor(client, rpm=60) # 60 RPM results = [] failed = [] # Progress-Tracking import itertools progress = itertools.cycle(["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇",