# Practical Examples This guide shows real-world use cases for AbstractCore with complete, copy-paste examples. All examples work across any provider - just change the provider name. ## Table of Contents - [Basic Usage](#basic-usage) - [Glyph Visual-Text Compression](#glyph-visual-text-compression) - [Tool Calling Examples](#tool-calling-examples) - [Tool Call Syntax Rewriting Examples](#tool-call-syntax-rewriting-examples) - [Structured Output Examples](#structured-output-examples) - [Streaming Examples](#streaming-examples) - [Session Management](#session-management) - [Interaction Tracing (Observability)](#interaction-tracing-observability) - [Production Patterns](#production-patterns) - [Integration Examples](#integration-examples) ## Basic Usage ### Simple Q&A ```python from abstractcore import create_llm # Works with any provider llm = create_llm("openai", model="gpt-4o-mini") # or "anthropic", "ollama"... response = llm.generate("What is the difference between Python and JavaScript?") print(response.content) ``` ### Multiple Providers Comparison ```python from abstractcore import create_llm providers = [ ("openai", "gpt-4o-mini"), ("anthropic", "claude-haiku-4-5"), ("ollama", "qwen3:4b-instruct") ] question = "Explain Python list comprehensions with examples" for provider_name, model in providers: try: llm = create_llm(provider_name, model=model) response = llm.generate(question) print(f"\n--- {provider_name.upper()} ---") print(response.content[:200] + "...") except Exception as e: print(f"{provider_name} failed: {e}") ``` ### Provider Fallback ```python from abstractcore import create_llm def generate_with_fallback(prompt, **kwargs): """Try multiple providers until one works.""" providers = [ ("openai", "gpt-4o-mini"), ("anthropic", "claude-haiku-4-5"), ("ollama", "qwen3:4b-instruct") ] for provider_name, model in providers: try: llm = create_llm(provider_name, model=model) return llm.generate(prompt, **kwargs) except Exception as e: print(f"{provider_name} failed: {e}") continue raise Exception("All providers failed") # Usage response = generate_with_fallback("What is machine learning?") print(response.content) ``` ## Glyph Visual-Text Compression Glyph compression renders long text into images for vision-capable models to reduce effective token usage (often 3–4x on long text; depends on content/model). Works with the light install (`pip install abstractcore`), including PDF/Office text extraction. ### Automatic Compression with Ollama ```python from abstractcore import create_llm # Use a vision-capable model - Glyph works automatically llm = create_llm("ollama", model="llama3.2-vision:11b") # Large documents are automatically compressed when beneficial response = llm.generate( "What are the key findings and methodology in this research paper?", media=["research_paper.pdf"] # Automatically compressed if size > threshold ) print(f"Analysis: {response.content}") print(f"Processing time: {response.gen_time}ms") # Check if compression was used if response.metadata and response.metadata.get('compression_used'): stats = response.metadata.get('compression_stats', {}) print(f"✅ Glyph compression used!") print(f"Compression ratio: {stats.get('compression_ratio', 'N/A')}x") print(f"Original tokens: {stats.get('original_tokens', 'N/A')}") print(f"Compressed tokens: {stats.get('compressed_tokens', 'N/A')}") ``` ### Explicit Compression Control ```python from abstractcore import create_llm # Force compression for testing llm = create_llm("ollama", model="qwen2.5vl:7b") # Always compress response = llm.generate( "Summarize the main conclusions of this document", media=["long_document.pdf"], glyph_compression="always" # Force compression ) # Never compress (for comparison) response_no_compression = llm.generate( "Summarize the main conclusions of this document", media=["long_document.pdf"], glyph_compression="never" # Disable compression ) print(f"With compression: {response.gen_time}ms") print(f"Without compression: {response_no_compression.gen_time}ms") ``` ### Custom Configuration ```python from abstractcore import create_llm from abstractcore.compression import GlyphConfig # Configure compression behavior glyph_config = GlyphConfig( enabled=True, global_default="auto", # "auto", "always", "never" quality_threshold=0.95, # Minimum quality score (0-1) target_compression_ratio=3.0, # Target compression ratio provider_optimization=True, # Enable provider-specific optimization cache_enabled=True, # Enable compression caching provider_profiles={ "ollama": { "dpi": 150, # Higher DPI for better quality "font_size": 9, # Smaller font for more content "quality_threshold": 0.95 } } ) llm = create_llm("ollama", model="granite3.2-vision:latest", glyph_config=glyph_config) response = llm.generate( "Analyze the figures and tables in this academic paper", media=["academic_paper.pdf"] ) ``` ### Performance Benchmarking ```python import time from abstractcore import create_llm def benchmark_glyph_compression(document_path, model_name="llama3.2-vision:11b"): """Compare processing with and without Glyph compression""" llm = create_llm("ollama", model=model_name) # Test without compression start = time.time() response_no_glyph = llm.generate( "Provide a detailed analysis of this document", media=[document_path], glyph_compression="never" ) time_no_glyph = time.time() - start # Test with compression start = time.time() response_glyph = llm.generate( "Provide a detailed analysis of this document", media=[document_path], glyph_compression="always" ) time_glyph = time.time() - start # Compare results print(f"📊 Glyph Compression Benchmark") print(f"Document: {document_path}") print(f"Model: {model_name}") print(f"") print(f"Without Glyph: {time_no_glyph:.2f}s") print(f"With Glyph: {time_glyph:.2f}s") print(f"Speedup: {time_no_glyph/time_glyph:.2f}x") print(f"") print(f"Response quality comparison:") print(f"No Glyph length: {len(response_no_glyph.content)} chars") print(f"Glyph length: {len(response_glyph.content)} chars") return response_glyph, response_no_glyph # Run benchmark glyph_response, normal_response = benchmark_glyph_compression("large_document.pdf") ``` ### Multi-Provider Testing ```python from abstractcore import create_llm # Test Glyph across different providers and models models_to_test = [ ("ollama", "llama3.2-vision:11b"), ("ollama", "qwen2.5vl:7b"), ("ollama", "granite3.2-vision:latest"), # Add LMStudio if running # ("lmstudio", "your-vision-model"), ] document = "research_paper.pdf" question = "What are the key innovations presented in this paper?" for provider, model in models_to_test: try: print(f"\n🧪 Testing {provider} - {model}") llm = create_llm(provider, model=model) response = llm.generate( question, media=[document], glyph_compression="auto" ) print(f"✅ Success - {response.gen_time}ms") print(f"Response: {response.content[:100]}...") # Check compression usage if response.metadata and response.metadata.get('compression_used'): print(f"🎨 Glyph compression was used") else: print(f"📝 Standard processing was used") except Exception as e: print(f"❌ Failed: {e}") ``` **Key Benefits Demonstrated:** - **Automatic optimization**: Glyph decides when compression is beneficial - **Transparent integration**: Works with existing media handling code - **Quality preservation**: No loss of analytical accuracy - **Provider flexibility**: Works across Ollama, LMStudio, and other vision providers [Learn more about Glyph configuration and advanced features](glyphs.md) ## Tool Calling Examples ### Weather Tool ```python from abstractcore import create_llm import requests def get_weather(city: str, units: str = "metric") -> str: """Get current weather for a city.""" # In production, use a real weather API # This is a simulated implementation temperatures = { "paris": "22°C, sunny", "london": "15°C, cloudy", "tokyo": "28°C, humid", "new york": "18°C, windy" } return temperatures.get(city.lower(), f"Weather data not available for {city}") # Tool definition weather_tool = { "name": "get_weather", "description": "Get current weather information for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "Name of the city" }, "units": { "type": "string", "enum": ["metric", "imperial"], "description": "Temperature units" } }, "required": ["city"] } } # Works with any provider that supports tools llm = create_llm("openai", model="gpt-4o-mini") response = llm.generate( "What's the weather like in Paris and London?", tools=[weather_tool] ) print(response.content) print(response.tool_calls) # Structured tool call requests (host/runtime executes them) ``` ### Calculator Tool ```python from abstractcore import create_llm import math def calculate(expression: str) -> str: """Safely evaluate mathematical expressions.""" try: # In production, use a proper expression parser # This is simplified for demo purposes allowed_chars = set('0123456789+-*/.() ') if not all(c in allowed_chars for c in expression): return "Error: Invalid characters in expression" result = eval(expression) return f"{expression} = {result}" except Exception as e: return f"Error calculating {expression}: {str(e)}" def sqrt(number: float) -> str: """Calculate square root.""" try: result = math.sqrt(number) return f"√{number} = {result}" except Exception as e: return f"Error: {str(e)}" # Tool definitions tools = [ { "name": "calculate", "description": "Perform basic mathematical calculations", "parameters": { "type": "object", "properties": { "expression": {"type": "string", "description": "Mathematical expression"} }, "required": ["expression"] } }, { "name": "sqrt", "description": "Calculate square root of a number", "parameters": { "type": "object", "properties": { "number": {"type": "number", "description": "Number to calculate square root of"} }, "required": ["number"] } } ] llm = create_llm("openai", model="gpt-4o-mini") response = llm.generate( "What is 25 * 4 + 12, and what's the square root of 144?", tools=tools ) print(response.content) print(response.tool_calls) # Structured tool call requests (host/runtime executes them) ``` ### File Operations Tool ```python from abstractcore import create_llm from pathlib import Path import os def list_files(directory: str = ".") -> str: """List files in a directory.""" try: path = Path(directory) if not path.exists(): return f"Directory {directory} does not exist" files = [] for item in path.iterdir(): if item.is_file(): files.append(f"FILE: {item.name}") elif item.is_dir(): files.append(f"DIR: {item.name}/") return f"Contents of {directory}:\n" + "\n".join(sorted(files)) except Exception as e: return f"Error listing files: {str(e)}" def read_file(filename: str) -> str: """Read contents of a text file.""" try: path = Path(filename) if not path.exists(): return f"File {filename} does not exist" content = path.read_text(encoding='utf-8') return f"Contents of {filename}:\n{content}" except Exception as e: return f"Error reading file: {str(e)}" # Tool definitions file_tools = [ { "name": "list_files", "description": "List files and directories in a given path", "parameters": { "type": "object", "properties": { "directory": {"type": "string", "description": "Directory path to list"} } } }, { "name": "read_file", "description": "Read the contents of a text file", "parameters": { "type": "object", "properties": { "filename": {"type": "string", "description": "Path to the file to read"} }, "required": ["filename"] } } ] llm = create_llm("anthropic", model="claude-haiku-4-5") response = llm.generate( "List the files in the current directory and read the README.md file if it exists", tools=file_tools ) print(response.content) print(response.tool_calls) # Structured tool call requests (host/runtime executes them) ``` ## Tool Call Syntax Rewriting Examples > **Real-time tool call format conversion for agentic CLI compatibility** Tool call syntax rewriting enables AbstractCore to work seamlessly with any agentic CLI by converting tool calls to the expected format in real-time. This happens automatically during generation, including streaming. > **Related**: [Tool Call Syntax Rewriting Guide](tool-syntax-rewriting.md) ### Codex CLI Integration (Qwen3 Tags) ```python from abstractcore import create_llm # Define tools (standard JSON format) weather_tool = { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } # Codex CLI expects qwen3-style tool-call tags in assistant content. # By default, AbstractCore strips tool-call markup from `response.content`; # pass `tool_call_tags` to preserve/emit the tags for downstream parsers. llm = create_llm("ollama", model="qwen3:4b-instruct") response = llm.generate("What's the weather in Tokyo?", tools=[weather_tool], tool_call_tags="qwen3") print(response.content) print(response.tool_calls) # Content includes: <|tool_call|>{"name": "get_weather", "arguments": {"city": "Tokyo"}} ``` ### Crush CLI Integration ```python # Crush CLI expects LLaMA3 format - just specify the format llm = create_llm("ollama", model="qwen3:4b-instruct") response = llm.generate("Get weather for London", tools=[weather_tool], tool_call_tags="llama3") print(response.content) # Output includes: {"name": "get_weather", "arguments": {"city": "London"}} ``` ### Custom CLI Format ```python # Your custom CLI expects: [TOOL]...JSON...[/TOOL] llm = create_llm("ollama", model="qwen3:4b-instruct") response = llm.generate("Check weather in Paris", tools=[weather_tool], tool_call_tags="[TOOL],[/TOOL]") print(response.content) # Output includes: [TOOL]{"name": "get_weather", "arguments": {"city": "Paris"}}[/TOOL] ``` ### Real-Time Streaming with Tag Rewriting ```python # Streaming works seamlessly with any format calculator_tool = { "name": "calculate", "description": "Perform mathematical calculations", "parameters": { "type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"] } } llm = create_llm("ollama", model="qwen3-coder:30b") print("AI: ", end="", flush=True) for chunk in llm.generate( "Calculate 15 * 23 and explain the result", tools=[calculator_tool], stream=True, tool_call_tags="llama3", ): print(chunk.content, end="", flush=True) # Tool calls are surfaced in real-time (execution is host/runtime-owned) if chunk.tool_calls: for tool_call in chunk.tool_calls: print(f"\n[TOOL CALL] {tool_call}") print("\n") # Shows: {"name": "calculate", "arguments": {"expression": "15 * 23"}} # Tool execution is owned by the host/runtime. ``` ### Multiple Tools with Different Formats ```python # Define multiple tools tools = [ { "name": "get_weather", "description": "Get weather information", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } }, { "name": "calculate", "description": "Perform calculations", "parameters": { "type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"] } }, { "name": "list_files", "description": "List files in a directory", "parameters": { "type": "object", "properties": {"directory": {"type": "string"}}, "required": ["directory"] } } ] # Test with XML format for Gemini CLI llm = create_llm("ollama", model="qwen3:4b-instruct") response = llm.generate( "What's 2+2, weather in NYC, and files in current directory?", tools=tools, tool_call_tags="xml", ) print(response.content) print(response.tool_calls) # All tool calls converted to: {"name": "...", "arguments": {...}} ``` ### Session-Based Format Configuration ```python from abstractcore import BasicSession # Apply a consistent tool-call tag format across a session by reusing a variable tool_call_tags = "llama3" llm = create_llm("ollama", model="qwen3:4b-instruct") session = BasicSession(provider=llm) session.generate("Calculate 10 * 5", tools=[calculator_tool], tool_call_tags=tool_call_tags) session.generate("What's the weather like?", tools=[weather_tool], tool_call_tags=tool_call_tags) session.generate("List files in documents", tools=[{ "name": "list_files", "description": "List directory contents", "parameters": { "type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"] } }], tool_call_tags=tool_call_tags) # All responses contain: ...JSON... ``` ### Production Monitoring with Events ```python from abstractcore.events import EventType, on_global # Monitor tool usage across different formats def log_tool_calls(event): # Tool execution events are emitted when tools are executed (e.g., via ToolRegistry # or when using `execute_tools=True` (deprecated)). print(f"[TOOL EVENT] {event.type}: {event.data}") on_global(EventType.TOOL_COMPLETED, log_tool_calls) # Test with different formats for format_name in ["qwen3", "llama3", "xml"]: llm = create_llm("ollama", model="qwen3:4b-instruct") response = llm.generate("Calculate 5 * 5", tools=[calculator_tool], tool_call_tags=format_name) print(f"{format_name} format result: {response.content[:100]}...") ``` **Key Benefits**: - Per-call configuration: pass `tool_call_tags=...` when you need tool-call markup preserved/rewritten in `response.content` - Real-time processing: No post-processing delays - Streaming compatible: Works with streaming mode - Format flexibility: Predefined formats plus custom tags > **Related**: [Tool Call Syntax Rewriting Guide](tool-syntax-rewriting.md) | [Unified Streaming Architecture](architecture.md#unified-streaming-architecture) ## Structured Output Examples > **Complete Guide**: [Structured Output Documentation](structured-output.md) - Native vs prompted strategies, provider support, schema design best practices ### User Profile Extraction ```python from abstractcore import create_llm from pydantic import BaseModel, field_validator from typing import Optional class UserProfile(BaseModel): name: str age: int email: str occupation: Optional[str] = None interests: list[str] = [] @field_validator('age') @classmethod def validate_age(cls, v): if v < 0 or v > 150: raise ValueError('Age must be between 0 and 150') return v @field_validator('email') @classmethod def validate_email(cls, v): if '@' not in v: raise ValueError('Invalid email format') return v llm = create_llm("openai", model="gpt-4o-mini") # Text with user information user_text = """ Hi, I'm Sarah Johnson, I'm 28 years old and work as a software engineer. My email is sarah.johnson@techcorp.com. I love hiking, photography, and cooking. """ # Extract structured data with automatic validation user = llm.generate( f"Extract user profile from: {user_text}", response_model=UserProfile ) print(f"Name: {user.name}") print(f"Age: {user.age}") print(f"Email: {user.email}") print(f"Occupation: {user.occupation}") print(f"Interests: {', '.join(user.interests)}") ``` ### Product Catalog Extraction ```python from abstractcore import create_llm from pydantic import BaseModel, field_validator from typing import List from enum import Enum class ProductCategory(str, Enum): ELECTRONICS = "electronics" CLOTHING = "clothing" BOOKS = "books" HOME = "home" SPORTS = "sports" class Product(BaseModel): name: str price: float category: ProductCategory description: str in_stock: bool = True @field_validator('price') @classmethod def validate_price(cls, v): if v <= 0: raise ValueError('Price must be positive') return v class ProductCatalog(BaseModel): products: List[Product] total_count: int @field_validator('total_count') @classmethod def validate_count(cls, v, info): products = info.data.get('products', []) if v != len(products): raise ValueError(f'Total count {v} does not match products length {len(products)}') return v llm = create_llm("anthropic", model="claude-haiku-4-5") catalog_text = """ Our store has these items: 1. Gaming Laptop - $1299.99 - High-performance laptop for gaming and work 2. Wireless Headphones - $199.99 - Noise-cancelling bluetooth headphones 3. Python Programming Book - $49.99 - Complete guide to Python programming 4. Coffee Maker - $89.99 - Automatic drip coffee maker, currently out of stock """ catalog = llm.generate( f"Extract product catalog from: {catalog_text}", response_model=ProductCatalog ) print(f"Total products: {catalog.total_count}") for product in catalog.products: status = "In Stock" if product.in_stock else "Out of Stock" print(f"- {product.name}: ${product.price} ({product.category}) - {status}") ``` ### Code Review Analysis ```python from abstractcore import create_llm from pydantic import BaseModel from typing import List from enum import Enum class Severity(str, Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" class CodeIssue(BaseModel): line_number: int severity: Severity issue_type: str description: str suggestion: str class CodeReview(BaseModel): language: str overall_quality: str issues: List[CodeIssue] recommendations: List[str] llm = create_llm("ollama", model="qwen3:4b-instruct") code_to_review = ''' def calculate_average(numbers): total = 0 for num in numbers: total += num return total / len(numbers) def process_data(data): if data == None: return [] result = [] for item in data: result.append(item * 2) return result ''' review = llm.generate( f"Review this Python code for issues:\n{code_to_review}", response_model=CodeReview ) print(f"Language: {review.language}") print(f"Overall Quality: {review.overall_quality}") print(f"\nIssues Found ({len(review.issues)}):") for issue in review.issues: print(f" Line {issue.line_number}: [{issue.severity.upper()}] {issue.issue_type}") print(f" Problem: {issue.description}") print(f" Fix: {issue.suggestion}\n") print("Recommendations:") for rec in review.recommendations: print(f" - {rec}") ``` ## Streaming Examples ### Basic Streaming (Unified 2025) ```python # Streaming uses a unified processor across providers (exact chunking depends on the backend) from abstractcore import create_llm llm = create_llm("anthropic", model="claude-haiku-4-5") print("AI Story Generator: ", end="", flush=True) for chunk in llm.generate( "Write a short story about a programmer who discovers their code is alive", stream=True ): print(chunk.content or "", end="", flush=True) print("\n") ``` ### Advanced Streaming with Progress and Performance Tracking ```python from abstractcore import create_llm import time def streaming_with_insights(prompt): # Supports any provider: OpenAI, Anthropic, Ollama, MLX llm = create_llm("openai", model="gpt-4o-mini") print("Generating response...") start_time = time.time() chunks = [] print("Response: ", end="", flush=True) for chunk in llm.generate(prompt, stream=True): chunks.append(chunk) print(chunk.content or "", end="", flush=True) # Optional real-time performance insights if len(chunks) % 10 == 0: current_time = time.time() - start_time chars_generated = sum(len(c.content or "") for c in chunks) print(f"\n[PROGRESS] {len(chunks)} chunks, {chars_generated} chars, {current_time:.1f}s") # Final performance summary total_time = time.time() - start_time total_chars = sum(len(chunk.content or "") for chunk in chunks) print(f"\n\n[STATS] Streaming Performance:") print(f"- Total Chunks: {len(chunks)}") print(f"- Total Characters: {total_chars}") print(f"- Duration: {total_time:.2f}s") print(f"- Speed: {total_chars/total_time:.0f} chars/sec") # Usage with various prompts streaming_with_insights("Explain quantum computing in simple terms") ``` ### Real-Time Streaming with Tools (Unified Implementation) ```python from abstractcore import create_llm from datetime import datetime def get_current_time() -> str: """Get the current time.""" return datetime.now().strftime("%H:%M:%S") def get_weather(city: str) -> str: """Get current weather for a city.""" weather_data = { "New York": "Sunny, 22°C", "London": "Cloudy, 15°C", "Tokyo": "Partly cloudy, 25°C" } return weather_data.get(city, f"Weather data unavailable for {city}") time_tool = { "name": "get_current_time", "description": "Get the current time", "parameters": {"type": "object", "properties": {}} } weather_tool = { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "Name of the city"} } } } # Works similarly across providers (exact chunking depends on the backend) llm = create_llm("ollama", model="qwen3:4b-instruct") print("AI Assistant: ", end="", flush=True) for chunk in llm.generate( "What time is it right now? And can you tell me the weather in New York?", tools=[time_tool, weather_tool], stream=True ): # Real-time chunk processing and tool call detection print(chunk.content or "", end="", flush=True) # Tool calls are surfaced as structured dicts; execute them in your host/runtime. if chunk.tool_calls: print(f"\n[TOOL] Tool calls: {chunk.tool_calls}") print("\n") # Newline after streaming # Notes: # - Real-time tool call detection # - Streams chunks as they arrive (minimal buffering) # - Works with OpenAI, Anthropic, Ollama, MLX (provider-dependent details) ``` ### Performance-Optimized Streaming ```python from abstractcore import create_llm import time def compare_providers(prompt): """Compare streaming performance across providers.""" providers = [ ("openai", "gpt-4o-mini"), ("anthropic", "claude-haiku-4-5"), ("ollama", "qwen3:4b-instruct") ] for provider, model in providers: try: llm = create_llm(provider, model=model) print(f"\n[TEST] {provider.upper()} - {model}") start_time = time.time() chunks = [] for chunk in llm.generate(prompt, stream=True): chunks.append(chunk) print(chunk.content or "", end="", flush=True) total_time = time.time() - start_time total_chars = sum(len(chunk.content or "") for chunk in chunks) print(f"\n\n[PERF] {provider.upper()} Performance:") print(f"- Chunks: {len(chunks)}") print(f"- Characters: {total_chars}") print(f"- Duration: {total_time:.2f}s") print(f"- Speed: {total_chars/total_time:.0f} chars/sec") except Exception as e: print(f"[ERROR] {provider} failed: {e}") # Compare streaming performance compare_providers("Write a creative short story about artificial intelligence") ``` **Streaming Features**: - Time-to-first-token depends on provider/model/network - Unified strategy across all providers - Real-time tool call detection - Streams chunks as they arrive (minimal buffering) - Supports: OpenAI, Anthropic, Ollama, MLX, LMStudio, HuggingFace - Robust error handling for malformed responses ## Session Management ### Basic Conversation ```python from abstractcore import create_llm, BasicSession llm = create_llm("openai", model="gpt-4o-mini") session = BasicSession( provider=llm, system_prompt="You are a helpful coding tutor. Always provide examples." ) # Multi-turn conversation print("=== Conversation Start ===") response1 = session.generate("Hi, I'm learning Python. What are decorators?") print("User: Hi, I'm learning Python. What are decorators?") print(f"AI: {response1.content}\n") response2 = session.generate("Can you show me a practical example?") print("User: Can you show me a practical example?") print(f"AI: {response2.content}\n") response3 = session.generate("What was my first question?") print("User: What was my first question?") print(f"AI: {response3.content}\n") print(f"Total messages in conversation: {len(session.messages)}") ``` ### Session Persistence ```python from abstractcore import create_llm, BasicSession from pathlib import Path # Create and use session llm = create_llm("anthropic", model="claude-haiku-4-5") session = BasicSession( provider=llm, system_prompt="You are a travel advisor. Help plan trips." ) # Have a conversation session.generate("I want to plan a trip to Japan") session.generate("I'm interested in both modern cities and traditional culture") session.generate("My budget is around $3000 for 10 days") # Save session session_file = Path("travel_planning_session.json") session.save(session_file) print(f"Session saved to {session_file}") # Later: Load session and continue new_session = BasicSession.load(session_file, provider=llm) response = new_session.generate("What were we discussing?") print(f"AI remembers: {response.content}") # Clean up session_file.unlink() # Delete the file ``` ### Context Management ```python from abstractcore import create_llm, BasicSession def create_coding_assistant(): """Create a specialized coding assistant session.""" llm = create_llm("ollama", model="qwen3:4b-instruct") system_prompt = """ You are an expert Python coding assistant. For each request: 1. Provide working code examples 2. Explain the code clearly 3. Mention potential issues or improvements 4. Keep responses concise but complete """ return BasicSession(provider=llm, system_prompt=system_prompt) # Usage assistant = create_coding_assistant() # The assistant will remember the context throughout the conversation assistant.generate("I need a function to validate email addresses") assistant.generate("Now add logging to that function") assistant.generate("How would I test this function?") print(f"Conversation history: {len(assistant.messages)} messages") # Clear history but keep system prompt assistant.clear_history() print(f"After clearing: {len(assistant.messages)} messages") # Just system prompt remains ``` ## Interaction Tracing (Observability) ### Basic Tracing Enable tracing to capture complete LLM interaction history for debugging and transparency: ```python from abstractcore import create_llm # Enable tracing on provider llm = create_llm( 'openai', model='gpt-4o-mini', enable_tracing=True, max_traces=100 # Keep last 100 interactions (ring buffer) ) # Generate with custom metadata response = llm.generate( "Explain quantum computing", temperature=0.7, trace_metadata={ 'user_id': 'user_123', 'session_type': 'educational', 'topic': 'quantum_physics' } ) # Access trace by ID trace_id = response.metadata['trace_id'] trace = llm.get_traces(trace_id=trace_id) print(f"Trace ID: {trace['trace_id']}") print(f"Timestamp: {trace['timestamp']}") print(f"Prompt: {trace['prompt']}") print(f"Response: {trace['response']['content'][:100]}...") print(f"Tokens: {trace['response']['usage']['total_tokens']}") print(f"Time: {trace['response']['generation_time_ms']:.2f}ms") print(f"Custom metadata: {trace['metadata']}") ``` ### Session-Level Tracing Automatically track all interactions in a session with correlation: ```python from abstractcore import create_llm from abstractcore.core.session import BasicSession llm = create_llm('openai', model='gpt-4o-mini', enable_tracing=True) session = BasicSession(provider=llm, enable_tracing=True) # All interactions automatically traced session.generate("What is Python?") session.generate("Give me an example") session.generate("Explain list comprehensions") # Get all session traces traces = session.get_interaction_history() print(f"\nSession ID: {session.id}") print(f"Total interactions: {len(traces)}") for i, trace in enumerate(traces, 1): print(f"\nInteraction {i}:") print(f" Prompt: {trace['prompt']}") print(f" Tokens: {trace['response']['usage']['total_tokens']}") print(f" Time: {trace['response']['generation_time_ms']:.0f}ms") print(f" Session ID: {trace['metadata']['session_id']}") ``` ### Multi-Step Workflow with Retries Track code generation workflows with retry attempts: ```python from abstractcore import create_llm from abstractcore.core.session import BasicSession llm = create_llm('openai', model='gpt-4o-mini', enable_tracing=True) session = BasicSession(provider=llm, enable_tracing=True) # Step 1: Generate code response = session.generate( "Write a Python function to calculate fibonacci numbers", system_prompt="You are a Python code generator. Only output code.", step_type='code_generation', attempt_number=1, temperature=0 ) code = response.content success = False # Step 2-4: Execute with retry logic for attempt in range(1, 4): try: exec(code) # Simulate execution success = True break except Exception as e: # Retry with error context response = session.generate( f"Previous code failed: {e}. Fix it.", step_type='code_generation', attempt_number=attempt + 1, temperature=0 ) code = response.content # Get workflow summary traces = session.get_interaction_history() print(f"\nWorkflow Summary:") print(f"Total attempts: {len(traces)}") print(f"Final status: {'Success' if success else 'Failed'}") for trace in traces: step = trace['metadata']['step_type'] attempt = trace['metadata']['attempt_number'] tokens = trace['response']['usage']['total_tokens'] print(f" {step} (Attempt {attempt}): {tokens} tokens") ``` ### Export Traces Export traces to different formats for analysis: ```python from abstractcore import create_llm from abstractcore.utils import export_traces, summarize_traces llm = create_llm('openai', model='gpt-4o-mini', enable_tracing=True) # Generate some interactions for i in range(5): llm.generate(f"Question {i+1}", temperature=0) traces = llm.get_traces() # Export to JSONL (one JSON per line) export_traces(traces, format='jsonl', file_path='traces.jsonl') # Export to pretty JSON export_traces(traces, format='json', file_path='traces.json') # Export to Markdown report export_traces(traces, format='markdown', file_path='trace_report.md') # Get summary statistics summary = summarize_traces(traces) print(f"\nSummary:") print(f" Total interactions: {summary['total_interactions']}") print(f" Total tokens: {summary['total_tokens']}") print(f" Average tokens: {summary['avg_tokens_per_interaction']:.0f}") print(f" Total time: {summary['total_time_ms']:.2f}ms") print(f" Average time: {summary['avg_time_ms']:.2f}ms") print(f" Providers: {summary['providers']}") print(f" Models: {summary['models']}") ``` ### Retrieve Specific Traces Different ways to retrieve traces: ```python from abstractcore import create_llm llm = create_llm('openai', model='gpt-4o-mini', enable_tracing=True) # Generate some interactions for i in range(10): llm.generate(f"Test {i}", temperature=0) # Get all traces all_traces = llm.get_traces() print(f"Total traces: {len(all_traces)}") # Get last 5 traces recent = llm.get_traces(last_n=5) print(f"Last 5 prompts: {[t['prompt'] for t in recent]}") # Get specific trace by ID response = llm.generate("Specific query", temperature=0) trace_id = response.metadata['trace_id'] trace = llm.get_traces(trace_id=trace_id) print(f"Specific trace: {trace['prompt']}") ``` [Learn more about Interaction Tracing](interaction-tracing.md) ## Production Patterns ### Retry and Error Handling ```python from abstractcore import create_llm from abstractcore.core.retry import RetryConfig from abstractcore.exceptions import ProviderAPIError, RateLimitError import logging # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def create_production_llm(): """Create LLM with production-grade retry configuration.""" retry_config = RetryConfig( max_attempts=3, initial_delay=1.0, max_delay=30.0, use_jitter=True, failure_threshold=5 ) return create_llm( "openai", model="gpt-4o-mini", retry_config=retry_config, timeout=30 ) def safe_generate(prompt, **kwargs): """Generate with comprehensive error handling.""" llm = create_production_llm() try: logger.info(f"Generating response for prompt: {prompt[:50]}...") response = llm.generate(prompt, **kwargs) logger.info(f"Response generated successfully: {len(response.content)} chars") return response except RateLimitError as e: logger.warning(f"Rate limited: {e}") raise except ProviderAPIError as e: logger.error(f"API error: {e}") raise except Exception as e: logger.error(f"Unexpected error: {e}") raise # Usage try: response = safe_generate("What is machine learning?") print(response.content) except Exception as e: print(f"Generation failed: {e}") ``` ### Cost Monitoring ```python from abstractcore import create_llm from abstractcore.events import EventType, on_global from datetime import datetime import json class CostMonitor: def __init__(self, budget_limit=10.0): self.total_cost = 0.0 self.budget_limit = budget_limit self.requests = [] # Register event handlers on_global(EventType.GENERATION_COMPLETED, self.track_cost) def track_cost(self, event): """Track costs from generation events.""" cost = event.data.get("cost_usd") if cost: # NOTE: `cost_usd` is a best-effort estimate based on token usage. cost_f = float(cost) self.total_cost += cost_f self.requests.append({ 'timestamp': event.timestamp.isoformat(), 'provider': event.data.get('provider'), 'model': event.data.get('model'), 'cost_usd': cost_f, 'tokens_input': event.data.get('tokens_input'), 'tokens_output': event.data.get('tokens_output') }) print(f"[COST] ${cost_f:.4f} | Total: ${self.total_cost:.4f}") if self.total_cost > self.budget_limit: print(f"[WARN] BUDGET EXCEEDED: ${self.total_cost:.4f} > ${self.budget_limit}") def get_report(self): """Get cost report.""" return { 'total_cost': self.total_cost, 'budget_limit': self.budget_limit, 'total_requests': len(self.requests), 'average_cost': self.total_cost / len(self.requests) if self.requests else 0, 'requests': self.requests } # Usage monitor = CostMonitor(budget_limit=1.0) # $1 budget llm = create_llm("openai", model="gpt-4o-mini") # Make some requests for i in range(3): response = llm.generate(f"Tell me a fact about number {i+1}") print(f"Fact {i+1}: {response.content[:100]}...\n") # Get report report = monitor.get_report() print(f"\n[REPORT] Final Cost Summary:") print(f"Total cost: ${report['total_cost']:.4f}") print(f"Requests: {report['total_requests']}") print(f"Average per request: ${report['average_cost']:.4f}") ``` ### Load Balancing ```python from abstractcore import create_llm import random import time from typing import List, Tuple class LoadBalancer: def __init__(self, providers: List[Tuple[str, str]]): """Initialize with list of (provider, model) tuples.""" self.providers = [] self.weights = [] for provider_name, model in providers: try: llm = create_llm(provider_name, model=model) self.providers.append((llm, provider_name, model)) self.weights.append(1.0) # Equal weight initially print(f"[OK] {provider_name} ({model}) ready") except Exception as e: print(f"[FAIL] {provider_name} ({model}) failed: {e}") def generate(self, prompt, **kwargs): """Generate using weighted random selection.""" if not self.providers: raise Exception("No providers available") # Weighted random selection provider_data = random.choices( self.providers, weights=self.weights, k=1 )[0] llm, provider_name, model = provider_data try: start_time = time.time() response = llm.generate(prompt, **kwargs) duration = time.time() - start_time print(f"[OK] {provider_name} responded in {duration:.2f}s") return response except Exception as e: print(f"[FAIL] {provider_name} failed: {e}") # Remove failed provider temporarily idx = self.providers.index(provider_data) self.weights[idx] *= 0.1 # Reduce weight dramatically raise # Usage balancer = LoadBalancer([ ("openai", "gpt-4o-mini"), ("anthropic", "claude-haiku-4-5"), ("ollama", "qwen3:4b-instruct") ]) # Make requests - they'll be distributed across available providers for i in range(5): try: response = balancer.generate(f"Tell me about topic number {i+1}") print(f"Response {i+1}: {response.content[:50]}...\n") except Exception as e: print(f"Request {i+1} failed: {e}\n") ``` ## Integration Examples ### FastAPI Integration ```python from fastapi import FastAPI, HTTPException from pydantic import BaseModel from abstractcore import create_llm, BasicSession from typing import Optional import uuid app = FastAPI(title="AbstractCore API") # Global LLM instance llm = create_llm("openai", model="gpt-4o-mini") # Store sessions in memory (use Redis in production) sessions = {} class ChatRequest(BaseModel): message: str session_id: Optional[str] = None system_prompt: Optional[str] = None class ChatResponse(BaseModel): response: str session_id: str @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest): try: # Get or create session if request.session_id and request.session_id in sessions: session = sessions[request.session_id] else: session_id = request.session_id or str(uuid.uuid4()) session = BasicSession( provider=llm, system_prompt=request.system_prompt or "You are a helpful assistant." ) sessions[session_id] = session # Generate response response = session.generate(request.message) return ChatResponse( response=response.content, session_id=session_id ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.delete("/sessions/{session_id}") async def clear_session(session_id: str): if session_id in sessions: del sessions[session_id] return {"message": "Session cleared"} raise HTTPException(status_code=404, detail="Session not found") # Run with: uvicorn main:app --reload if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) ``` ### Gradio Web Interface ```python import gradio as gr from abstractcore import create_llm, BasicSession from typing import List, Tuple class ChatInterface: def __init__(self): self.llm = create_llm("anthropic", model="claude-haiku-4-5") self.session = BasicSession( provider=self.llm, system_prompt="You are a helpful AI assistant." ) def chat(self, message: str, history: List[Tuple[str, str]]) -> Tuple[str, List[Tuple[str, str]]]: """Handle chat interaction.""" try: response = self.session.generate(message) history.append((message, response.content)) return "", history except Exception as e: history.append((message, f"Error: {str(e)}")) return "", history def clear(self) -> Tuple[str, List]: """Clear conversation history.""" self.session.clear_history() return "", [] # Create interface chat_interface = ChatInterface() with gr.Blocks(title="AbstractCore Chat") as demo: gr.Markdown("# AbstractCore Chat Interface") chatbot = gr.Chatbot(label="Conversation", height=400) msg = gr.Textbox( label="Message", placeholder="Type your message here...", lines=2 ) with gr.Row(): submit = gr.Button("Send", variant="primary") clear = gr.Button("Clear", variant="secondary") # Event handlers msg.submit( chat_interface.chat, inputs=[msg, chatbot], outputs=[msg, chatbot] ) submit.click( chat_interface.chat, inputs=[msg, chatbot], outputs=[msg, chatbot] ) clear.click( chat_interface.clear, outputs=[msg, chatbot] ) if __name__ == "__main__": demo.launch(share=True) ``` ### Jupyter Notebook Integration ```python # Cell 1: Setup from abstractcore import create_llm from IPython.display import display, Markdown, HTML import json # Create LLM instance llm = create_llm("openai", model="gpt-4o-mini") def display_response(response, title="AI Response"): """Pretty display for Jupyter notebooks.""" html = f"""

{title}

{response.content}

""" display(HTML(html)) print("AbstractCore setup complete!") # Cell 2: Basic Usage response = llm.generate("Explain quantum computing in simple terms") display_response(response, "Quantum Computing Explanation") # Cell 3: Structured Output from pydantic import BaseModel from typing import List class LearningPlan(BaseModel): topic: str difficulty: str estimated_hours: int prerequisites: List[str] learning_steps: List[str] plan = llm.generate( "Create a learning plan for someone who wants to learn machine learning", response_model=LearningPlan ) # Display as nice table display(HTML(f"""
Topic:{plan.topic}
Difficulty:{plan.difficulty}
Estimated Hours:{plan.estimated_hours}
Prerequisites:{', '.join(plan.prerequisites)}
""")) display(Markdown("### Learning Steps:")) for i, step in enumerate(plan.learning_steps, 1): display(Markdown(f"{i}. {step}")) ``` ### Discord Bot Integration ```python import discord from discord.ext import commands from abstractcore import create_llm, BasicSession import asyncio # Bot setup intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) # LLM setup llm = create_llm("anthropic", model="claude-haiku-4-5") sessions = {} # Store user sessions @bot.event async def on_ready(): print(f'{bot.user} has connected to Discord!') @bot.command(name='ask') async def ask(ctx, *, question): """Ask the AI a question.""" user_id = ctx.author.id # Get or create session for user if user_id not in sessions: sessions[user_id] = BasicSession( provider=llm, system_prompt="You are a helpful Discord bot assistant. Keep responses concise." ) try: # Show typing indicator async with ctx.typing(): response = sessions[user_id].generate(question) # Discord has a 2000 character limit content = response.content if len(content) > 2000: content = content[:1997] + "..." await ctx.reply(content) except Exception as e: await ctx.reply(f"Sorry, I encountered an error: {str(e)}") @bot.command(name='clear') async def clear_session(ctx): """Clear your conversation history.""" user_id = ctx.author.id if user_id in sessions: sessions[user_id].clear_history() await ctx.reply("Your conversation history has been cleared!") else: await ctx.reply("You don't have an active session to clear.") @bot.command(name='stats') async def stats(ctx): """Show session statistics.""" user_id = ctx.author.id if user_id in sessions: session = sessions[user_id] message_count = len(session.messages) await ctx.reply(f"Your session has {message_count} messages.") else: await ctx.reply("You don't have an active session.") # Run bot (add your Discord bot token) # bot.run('YOUR_DISCORD_BOT_TOKEN') ``` ## Next Steps These examples show AbstractCore's versatility across different use cases. To continue learning: 1. **Start with basics** - Try the simple Q&A examples 2. **Add tools** - Experiment with the tool calling examples 3. **Structure output** - Use Pydantic models for type-safe responses 4. **Go production** - Implement error handling and monitoring 5. **Build apps** - Use the integration examples as starting points For more information: - [Getting Started](getting-started.md) - Basic setup and usage - [Capabilities](capabilities.md) - What AbstractCore can do - [Prerequisites](prerequisites.md) - Provider setup and configuration - [API Reference](api-reference.md) - Complete API documentation --- **Remember**: All these examples work with any provider - just change the `create_llm()` call to switch between OpenAI, Anthropic, Ollama, MLX, and others!