diff --git a/examples/python-sdk/README.md b/examples/python-sdk/README.md new file mode 100644 index 0000000..3c0b316 --- /dev/null +++ b/examples/python-sdk/README.md @@ -0,0 +1,279 @@ +# Augment SDK Examples + +This directory contains examples demonstrating how to use the Augment Python SDK. + +## Basic Examples + +### `basic_usage.py` +Demonstrates fundamental SDK features: +- Creating an agent +- Simple text responses +- Typed responses (int, bool, list, dict) +- Dataclass results +- Enum results +- Accessing model explanations +- Session management + +**Run it:** +```bash +python examples/basic_usage.py +``` + +### `session_usage.py` +Shows how to use sessions for conversation continuity: +- Default behavior (independent calls) +- Session behavior (calls remember each other) +- Continuing sessions automatically +- Working with multiple sessions + +**Run it:** +```bash +python examples/session_usage.py +``` + +### `list_prs.py` / `list_prs_2.py` +Examples of working with GitHub PRs using the SDK. + +**Run them:** +```bash +python examples/list_prs.py +python examples/list_prs_2.py +``` + +## ACP Client Examples + +### `acp_example_usage.py` +Demonstrates the AuggieACPClient for persistent sessions with the Augment CLI: +- Starting and stopping the agent +- Sending messages with automatic context sharing +- Using event listeners +- Context managers + +**Run it:** +```bash +python examples/acp_example_usage.py +``` + +### Claude Code Client Tests + +For ClaudeCodeACPClient examples and testing, see the **real E2E tests**: + +**Location:** `tests/test_claude_code_client_e2e.py` + +**Prerequisites:** +```bash +npm install -g @zed-industries/claude-code-acp +export ANTHROPIC_API_KEY=... +``` + +**Run tests:** +```bash +# Quick tests (~30 seconds) +pytest tests/test_claude_code_client_e2e.py + +# All tests including slow ones (~5-10 minutes) +pytest tests/test_claude_code_client_e2e.py -m "" +``` + +**Documentation:** +- [Claude Code Client Guide](../docs/CLAUDE_CODE_CLIENT.md) +- [Testing Guide](../tests/README_CLAUDE_CODE_TESTS.md) + +## Prompt-to-SDK Conversion + +### `example_complex_prompt.txt` +A sample complex prompt that demonstrates multiple stages, conditions, and iterations. Use this to test the `/prompt-to-sdk` command. + +**Example prompt structure:** +``` +Analyze all TypeScript files in clients/beachhead/src/cli. + +For each file: +1. Check if it has proper error handling +2. Check if it has JSDoc comments +3. Identify potential bugs + +After analyzing all files: +1. Create a summary report +2. If >5 files missing error handling, create a plan +3. If >10 files missing JSDoc, generate templates + +Finally: +- Count total issues +- Prioritize top 3 critical issues +- Create fix suggestions for top 3 +``` + +**Convert it to SDK code:** +```bash +# In TUI mode +/prompt-to-sdk examples/example_complex_prompt.txt + +# From command line +auggie command prompt-to-sdk examples/example_complex_prompt.txt +``` + +### `demo_prompt_to_sdk.sh` +Interactive demo script that shows the prompt-to-sdk conversion process. + +**Run it:** +```bash +./examples/demo_prompt_to_sdk.sh +``` + +## Workflow Patterns + +The examples demonstrate these common patterns: + +### 1. Sequential Workflow +```python +with agent.session() as session: + session("Step 1") + session("Step 2 that builds on step 1") + session("Step 3 that uses both") +``` + +### 2. Conditional Logic +```python +exists = agent("Does file exist?", bool) +if exists: + agent("Process existing file") +else: + agent("Create new file") +``` + +### 3. Iteration with Structured Data +```python +@dataclass +class FileInfo: + path: str + size: int + +files = agent("Analyze files", list[FileInfo]) +for file in files: + if file.size > 1000: + agent(f"Optimize {file.path}") +``` + +### 4. Function Calling +```python +def run_tests(test_file: str) -> dict: + """ + Run tests from a test file. + + Args: + test_file: Path to the test file + """ + # Your test logic + return {"passed": 10, "failed": 2, "file": test_file} + +# Agent can call the function as needed +result = agent.run( + "Run tests in test_auth.py and analyze the results", + return_type=dict, + functions=[run_tests] +) +``` + +### 5. Error Handling +```python +from auggie_sdk.exceptions import AugmentCLIError, AugmentParseError + +try: + result = agent("Complex task", int) +except AugmentParseError: + print("Could not parse result") +except AugmentCLIError: + print("Agent execution failed") +``` + +## Creating Your Own Examples + +### Simple Script Template +```python +#!/usr/bin/env python3 +""" +Description of what this script does. +""" + +from auggie_sdk import Auggie + +def main(): + # Create agent + agent = Auggie(workspace_root=".", model="claude-3-5-sonnet-latest") + + # Your workflow here + result = agent("Your instruction") + print(f"Result: {result}") + +if __name__ == "__main__": + main() +``` + +### Complex Workflow Template +```python +#!/usr/bin/env python3 +""" +Multi-stage workflow with structured data. +""" + +from auggie_sdk import Auggie +from dataclasses import dataclass +from typing import List, Optional + +@dataclass +class YourDataClass: + field1: str + field2: int + +def main(): + agent = Auggie(workspace_root=".", model="claude-3-5-sonnet-latest") + + # Stage 1: Get structured data + items = agent("Get items", list[YourDataClass]) + print(f"Found {len(items)} items") + + # Stage 2: Process with session + with agent.session() as session: + for item in items: + session(f"Process {item.field1}") + + # Stage 3: Final report + agent("Create summary report") + print("Workflow complete!") + +if __name__ == "__main__": + main() +``` + +## Testing Examples + +All examples can be run directly: + +```bash +# Run a specific example +python examples/basic_usage.py + +# Run with custom workspace +python examples/basic_usage.py --workspace /path/to/workspace + +# Run with different model +python examples/basic_usage.py --model gpt-4o +``` + +## Documentation + +- **SDK README**: `../README.md` +- **Prompt-to-SDK Guide**: `../PROMPT_TO_SDK_GUIDE.md` +- **Slash Command Summary**: `../SLASH_COMMAND_SUMMARY.md` + +## Getting Help + +If you encounter issues: + +1. Check that the SDK is installed: `pip install -e .` +2. Verify auggie CLI is available: `auggie --version` +3. Check the workspace path is correct +4. Review error messages for specific issues + +For more help, see the main SDK documentation. diff --git a/examples/python-sdk/README_PROMPT_TO_CODE.md b/examples/python-sdk/README_PROMPT_TO_CODE.md new file mode 100644 index 0000000..4faee2b --- /dev/null +++ b/examples/python-sdk/README_PROMPT_TO_CODE.md @@ -0,0 +1,219 @@ +# Prompt to Code Converter Examples + +This directory contains examples for using the `prompt_to_code.py` tool. + +## Quick Start + +1. **Create a prompt file** describing your workflow: + +```bash +cat > my_workflow.txt << 'EOF' +Analyze all Python files in the src/ directory, identify any security +vulnerabilities or code quality issues, create a detailed report in +markdown format, and if there are any critical security issues found, +generate fix suggestions for each one with specific code changes needed. +EOF +``` + +2. **Convert the prompt to an SDK program**: + +```bash +python ../prompt_to_code.py my_workflow.txt +``` + +3. **Review and run the generated program**: + +```bash +# Review the generated code +cat generated_sdk_program.py + +# Run it +python generated_sdk_program.py +``` + +## Example Prompts + +### Example 1: Security Analysis (included) + +See `example_prompt.txt` for a simple security analysis workflow. + +```bash +python ../prompt_to_code.py example_prompt.txt --output security_analyzer.py +``` + +### Example 2: Test Generation Workflow + +Create a file `test_generation.txt`: + +``` +First, scan all TypeScript files in the components/ directory and identify +which ones are missing unit tests. For each file without tests, create a +comprehensive test file with at least 80% coverage. Then run all the new +tests and if any fail, fix the issues. Finally, generate a summary report +of test coverage improvements. +``` + +Convert it: + +```bash +python ../prompt_to_code.py test_generation.txt --output test_generator.py +``` + +### Example 3: Documentation Generator + +Create a file `doc_generator.txt`: + +``` +Analyze all public functions and classes in the src/ directory. For each +one that's missing a docstring or has an incomplete docstring, generate +comprehensive documentation including description, parameters, return +values, and usage examples. Then validate that all docstrings follow +Google style guide format. +``` + +Convert it: + +```bash +python ../prompt_to_code.py doc_generator.txt --output doc_generator.py +``` + +### Example 4: Code Refactoring Pipeline + +Create a file `refactor_pipeline.txt`: + +``` +Find all functions in the codebase that are longer than 50 lines. For +each one, analyze if it can be broken down into smaller functions. If +yes, refactor it into multiple well-named functions with clear +responsibilities. After each refactoring, run the existing tests to +ensure nothing broke. Keep a log of all refactorings performed. +``` + +Convert it: + +```bash +python ../prompt_to_code.py refactor_pipeline.txt --output refactor_pipeline.py +``` + +## Tips for Writing Good Prompts + +### ✅ Good Prompts + +Good prompts clearly describe: +- **Sequential stages**: What happens first, second, third? +- **Data flow**: What information passes between steps? +- **Conditions**: When should different actions be taken? +- **Iterations**: What collections need processing? +- **Outputs**: What should be created? + +Example: +``` +First, list all Python files in src/. For each file, check if it has +type hints. If not, add type hints. Then run mypy to validate. If +there are errors, fix them. Finally, create a report of all changes. +``` + +This shows: +- Clear stages (list → check → add → validate → fix → report) +- Iteration (for each file) +- Conditions (if not, if errors) +- Output (report) + +### ❌ Less Effective Prompts + +Avoid vague prompts like: +``` +Make the code better +``` + +This doesn't provide enough structure for conversion. + +## Advanced Usage + +### Custom Model + +Use a specific model for conversion: + +```bash +python ../prompt_to_code.py my_prompt.txt --model claude-3-5-sonnet-latest +``` + +### Custom Workspace + +Specify a workspace directory: + +```bash +python ../prompt_to_code.py my_prompt.txt --workspace /path/to/project +``` + +### Custom Output Location + +Save to a specific file: + +```bash +python ../prompt_to_code.py my_prompt.txt --output ~/my_scripts/workflow.py +``` + +## What Gets Generated + +The tool generates a complete Python program with: + +1. **Proper shebang and docstring** +2. **All necessary imports** (Agent, dataclasses, typing, etc.) +3. **Dataclass definitions** for structured data +4. **Agent initialization** with appropriate settings +5. **Workflow implementation** using SDK patterns: + - Sessions for context continuity + - Typed results for decision-making + - Loops for iteration + - Error handling +6. **Main function** that can be run directly +7. **Helpful comments** explaining each stage + +## Modifying Generated Programs + +After generation, you can: + +1. **Add error handling**: +```python +try: + result = agent("Some operation", int) +except AugmentCLIError as e: + print(f"Error: {e}") +``` + +2. **Add logging**: +```python +import logging +logging.basicConfig(level=logging.INFO) +logging.info(f"Processing {len(files)} files") +``` + +3. **Add function calling**: +```python +def run_tests(file: str) -> dict: + """Run tests for a file.""" + # Your implementation + return {"passed": 10, "failed": 0} + +result = agent.run( + "Run tests and analyze results", + return_type=dict, + functions=[run_tests] +) +``` + +4. **Add event listeners**: +```python +from auggie_sdk import LoggingAgentListener + +listener = LoggingAgentListener(verbose=True) +agent = Auggie(listener=listener) +``` + +## See Also + +- [Main Documentation](../docs/PROMPT_TO_CODE.md) +- [Agent Documentation](../docs/AGENT_EVENT_LISTENER.md) +- [Session Continuity](../docs/SESSION_CONTINUITY.md) +- [Function Calling](../docs/FUNCTION_CALLING.md) diff --git a/examples/python-sdk/acp_demo.py b/examples/python-sdk/acp_demo.py new file mode 100644 index 0000000..889a5ac --- /dev/null +++ b/examples/python-sdk/acp_demo.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +""" +Simple demo of the AuggieACPClient. + +This demonstrates the four key features: +1. Starting/stopping the agent +2. Sending messages and getting responses +3. Listening to events +4. Clearing context +""" + +from auggie_sdk.acp import AuggieACPClient, AgentEventListener +from typing import Optional, Any + + +class DemoListener(AgentEventListener): + """Simple listener that prints events in a user-friendly way.""" + + def on_agent_message(self, text: str) -> None: + print(text, end="", flush=True) + + def on_tool_call_start( + self, + tool_call_id: str, + title: str, + kind: Optional[str] = None, + status: Optional[str] = None, + ) -> None: + print(f"\n 🔧 Using tool: {title}", flush=True) + + def on_tool_call_update( + self, + tool_call_id: str, + status: Optional[str] = None, + content: Optional[Any] = None, + ) -> None: + if status == "completed": + print(" ✓ Tool completed", flush=True) + + def on_agent_thought(self, text: str) -> None: + pass + + +def main(): + print("=" * 80) + print("AuggieACPClient Demo") + print("=" * 80) + + # Create client with event listener + listener = DemoListener() + client = AuggieACPClient(listener=listener) + + # Feature 1: Start the agent + print("\n1️⃣ Starting agent...") + client.start() + print(f" ✓ Agent started (session: {client.session_id})\n") + + # Feature 2: Send messages and get responses + print("2️⃣ Sending message: 'What is 2 + 2?'\n") + print(" Agent response: ", end="") + client.send_message("What is 2 + 2? Answer in one sentence.") + print("\n ✓ Got response\n") + + # Feature 3: Events are automatically captured by the listener + print("3️⃣ Sending message that triggers tool calls: 'Read the README.md'\n") + print(" Agent response: ", end="") + client.send_message( + "Read the file experimental/guy/auggie_sdk/README.md and tell me what it's about in one sentence.", + timeout=30.0, + ) + print("\n ✓ Got response (with tool call events shown above)\n") + + # Feature 4: Clear context + print("4️⃣ Clearing context...") + old_session = client.session_id + client.clear_context() + new_session = client.session_id + print(" ✓ Context cleared") + print(f" Old session: {old_session}") + print(f" New session: {new_session}\n") + + # Verify context was cleared + print(" Verifying context was cleared...") + print(" Agent response: ", end="") + client.send_message("What was the last file I asked you to read?") + print("\n ✓ Agent doesn't remember (context was cleared)\n") + + # Stop the agent + print("5️⃣ Stopping agent...") + client.stop() + print(" ✓ Agent stopped\n") + + print("=" * 80) + print("Demo completed successfully! ✨") + print("=" * 80) + print("\nKey takeaways:") + print(" • Simple API: start(), send_message(), clear_context(), stop()") + print(" • Event listener: See what the agent is doing in real-time") + print(" • Context management: Clear context to start fresh conversations") + print(" • No asyncio needed: Everything is synchronous and easy to use") + print() + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/acp_example_usage.py b/examples/python-sdk/acp_example_usage.py new file mode 100644 index 0000000..b64b1f0 --- /dev/null +++ b/examples/python-sdk/acp_example_usage.py @@ -0,0 +1,264 @@ +""" +Example usage of the AuggieACPClient. + +This demonstrates all the key features: +1. Starting/stopping the agent +2. Sending messages and getting responses +3. Listening to events via a custom listener +4. Clearing context +""" + +from auggie_sdk.acp import AuggieACPClient, AgentEventListener, ModelName +from typing import Optional, Any + + +class MyEventListener(AgentEventListener): + """Example event listener that prints all agent events.""" + + def on_agent_message(self, text: str) -> None: + """Called when the agent sends a message chunk.""" + print(f"[AGENT MESSAGE] {text}", end="", flush=True) + + def on_tool_call_start( + self, + tool_call_id: str, + title: str, + kind: Optional[str] = None, + status: Optional[str] = None, + ) -> None: + """Called when the agent starts a tool call.""" + print(f"\n[TOOL CALL START] {title}") + print(f" ID: {tool_call_id}") + print(f" Kind: {kind}") + print(f" Status: {status}") + + def on_tool_call_update( + self, + tool_call_id: str, + status: Optional[str] = None, + content: Optional[Any] = None, + ) -> None: + """Called when a tool call is updated.""" + print(f"[TOOL CALL UPDATE] {tool_call_id}") + print(f" Status: {status}") + if content: + print(f" Content: {str(content)[:100]}...") # Truncate long content + + def on_agent_thought(self, text: str) -> None: + """Called when the agent shares its internal reasoning.""" + print(f"[AGENT THOUGHT] {text}", end="", flush=True) + + +def example_basic_usage(): + """Example 1: Basic usage without event listener.""" + print("=" * 80) + print("EXAMPLE 1: Basic Usage") + print("=" * 80) + + # Create client without listener + # You can optionally specify model and workspace_root: + # client = AuggieACPClient( + # model=ModelName.SONNET_4_5, # Use enum for type safety + # workspace_root="/path/to/workspace" + # ) + client = AuggieACPClient() + + # Start the agent + print("Starting agent...") + client.start() + print(f"Agent started! Session ID: {client.session_id}\n") + + # Send a simple message + message = "What is 2 + 2? Answer in one sentence." + print(f"Sending: {message}") + response = client.send_message(message) + print(f"Response: {response}\n") + + # Stop the agent + print("Stopping agent...") + client.stop() + print("Agent stopped.\n") + + +def example_with_listener(): + """Example 2: Using an event listener to see what the agent is doing.""" + print("=" * 80) + print("EXAMPLE 2: With Event Listener") + print("=" * 80) + + # Create client with listener + listener = MyEventListener() + client = AuggieACPClient(listener=listener) + + print("Starting agent...") + client.start() + print(f"Agent started! Session ID: {client.session_id}\n") + + # Send a message that will trigger tool calls + message = "Please read the file experimental/guy/auggie_sdk/README.md and summarize it in one sentence." + print(f"Sending: {message}\n") + response = client.send_message(message, timeout=30.0) + print(f"\n\nFinal Response: {response}\n") + + client.stop() + print("Agent stopped.\n") + + +def example_context_manager(): + """Example 3: Using context manager for automatic cleanup.""" + print("=" * 80) + print("EXAMPLE 3: Context Manager") + print("=" * 80) + + listener = MyEventListener() + + # Use context manager - automatically starts and stops + with AuggieACPClient(listener=listener) as client: + print(f"Agent started! Session ID: {client.session_id}\n") + + message = "What is 10 * 5?" + print(f"Sending: {message}\n") + response = client.send_message(message) + print(f"\n\nFinal Response: {response}\n") + + print("Agent automatically stopped.\n") + + +def example_clear_context(): + """Example 4: Clearing context between conversations.""" + print("=" * 80) + print("EXAMPLE 4: Clearing Context") + print("=" * 80) + + client = AuggieACPClient() + client.start() + + # First conversation + print("First conversation:") + print(f"Session ID: {client.session_id}") + response1 = client.send_message("Remember this number: 42") + print(f"Response: {response1}\n") + + # Clear context (restarts agent with new session) + print("Clearing context...") + old_session_id = client.session_id + client.clear_context() + print( + f"Context cleared! Old session: {old_session_id}, New session: {client.session_id}\n" + ) + + # Second conversation - agent won't remember the number + print("Second conversation (after clearing context):") + response2 = client.send_message("What number did I ask you to remember?") + print(f"Response: {response2}\n") + + client.stop() + print("Agent stopped.\n") + + +def example_multiple_messages(): + """Example 5: Multiple messages in the same session.""" + print("=" * 80) + print("EXAMPLE 5: Multiple Messages in Same Session") + print("=" * 80) + + client = AuggieACPClient() + client.start() + print(f"Session ID: {client.session_id}\n") + + # Send multiple messages in the same session + messages = [ + "What is 5 + 3?", + "What is that number multiplied by 2?", + "What is the final result?", + ] + + for i, message in enumerate(messages, 1): + print(f"Message {i}: {message}") + response = client.send_message(message) + print(f"Response {i}: {response}\n") + + client.stop() + print("Agent stopped.\n") + + +def example_model_and_workspace(): + """Example 6: Using model and workspace configuration.""" + print("=" * 80) + print("EXAMPLE 6: Model and Workspace Configuration") + print("=" * 80) + + import os + + # Create client with specific model and workspace + # You can use the ModelName enum for type safety: + client = AuggieACPClient( + model=ModelName.SONNET_4_5, # Use enum for type safety + workspace_root=os.getcwd(), # Specify workspace root + ) + + # Or use a string directly: + # client = AuggieACPClient( + # model="sonnet4.5", # String also works + # workspace_root=os.getcwd(), + # ) + + print(f"Starting agent with model: {client.model}") + print(f"Workspace root: {client.workspace_root}\n") + + client.start() + print(f"Agent started! Session ID: {client.session_id}\n") + + # Send a message + message = "What is 5 * 7? Answer with just the number." + print(f"Sending: {message}") + response = client.send_message(message) + print(f"Response: {response}\n") + + # Stop the agent + client.stop() + print("Agent stopped.\n") + + +def main(): + """Run all examples.""" + print("\n" + "=" * 80) + print("AUGGIE ACP CLIENT - EXAMPLES") + print("=" * 80 + "\n") + + # Run examples + example_basic_usage() + input("Press Enter to continue to next example...") + + example_with_listener() + input("Press Enter to continue to next example...") + + example_context_manager() + input("Press Enter to continue to next example...") + + example_clear_context() + input("Press Enter to continue to next example...") + + example_multiple_messages() + input("Press Enter to continue to next example...") + + example_model_and_workspace() + + print("\n" + "=" * 80) + print("ALL EXAMPLES COMPLETED") + print("=" * 80 + "\n") + + +if __name__ == "__main__": + # Run just one example for quick testing + # Uncomment the one you want to run: + + # example_basic_usage() + # example_with_listener() + # example_context_manager() + # example_clear_context() + # example_multiple_messages() + example_model_and_workspace() + + # Or run all examples: + # main() diff --git a/examples/python-sdk/basic_usage.py b/examples/python-sdk/basic_usage.py new file mode 100755 index 0000000..b743be2 --- /dev/null +++ b/examples/python-sdk/basic_usage.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +Basic usage examples for the Augment Python SDK. +""" + +import sys +from pathlib import Path +from dataclasses import dataclass +from enum import Enum + +# Add the parent directory to the path so we can import auggie_sdk +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from auggie_sdk import Auggie + + +@dataclass +class Task: + title: str + priority: str + estimated_hours: int + + +class Priority(Enum): + LOW = "low" + MEDIUM = "medium" + HIGH = "high" + + +def main(): + """Demonstrate basic agent usage.""" + + # Create an agent with custom model + # Supported models: "haiku4.5", "sonnet4.5", "sonnet4", "gpt5" + agent = Auggie(model="sonnet4.5") + + print("🤖 Basic Agent Usage Examples") + print("=" * 40) + + # Example 1: Automatic type inference (no return_type specified) + print("\n1. Automatic type inference:") + result, inferred_type = agent.run("What is 15 + 27?") + print(f"Result: {result} (inferred type: {inferred_type.__name__})") + + # Example 2: Explicit typed response - integer + print("\n2. Explicit typed response (integer):") + result = agent.run("What is 15 + 27?", int) + print(f"Result: {result} (type: {type(result).__name__})") + + # Example 3: @ operator syntax + print("\n3. @ operator syntax:") + result = agent(int) @ "What is 8 * 7?" + print(f"Result: {result} (type: {type(result).__name__})") + + # Example 4: List of strings + print("\n4. List of strings:") + colors = agent(list[str]) @ "Give me 5 colors" + print(f"Colors: {colors}") + + # Example 5: Dataclass result + print("\n5. Dataclass result:") + task = agent(Task) @ "Create a task: 'Write unit tests', medium priority, 4 hours" + print( + f"Task: {task.title}, Priority: {task.priority}, Hours: {task.estimated_hours}" + ) + + # Example 6: List of dataclasses + print("\n6. List of dataclasses:") + tasks = agent(list[Task]) @ "Create 3 example programming tasks" + print("Tasks:") + for i, task in enumerate(tasks, 1): + print( + f" {i}. {task.title} ({task.priority} priority, {task.estimated_hours}h)" + ) + + # Example 7: Enum result + print("\n7. Enum result:") + priority = ( + agent(Priority) @ "What priority should 'fix critical security bug' have?" + ) + print(f"Priority: {priority} (type: {type(priority).__name__})") + + # Example 8: Access model's explanation + print("\n8. Model explanation:") + result = agent(bool) @ "Is Python a compiled language?" + print(f"Answer: {result}") + print(f"Explanation: {agent.last_model_answer}") + + # Example 9: Session context manager + print("\n9. Session context manager:") + print(" Default behavior (no memory):") + agent.run("Create a function called 'greet_user'") + agent.run("Now add error handling to that function") # Won't remember greet_user + print(" ✅ Independent calls completed") + + print(" Session behavior (with memory - CLI creates session ID):") + with agent.session() as session: + session.run( + "Create a function called 'calculate_discount' that takes price and percentage" + ) + session.run( + "Now add input validation to that function" + ) # Remembers calculate_discount + session.run( + "Add comprehensive docstring and type hints" + ) # Remembers the function + + # Use @ operator in session + functions = ( + session(list[str]) + @ "List all the functions we've worked on in this session" + ) + print(f" Functions in session: {functions}") + print(f" Session ID: {session.last_session_id}") + + print(" Continuing the same session automatically:") + with agent.session() as session: # Automatically resumes last session + session.run("Add unit tests for the discount function") + print(" ✅ Continued session and added tests") + + print("\n✅ All examples completed successfully!") + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/cli_analyzer.py b/examples/python-sdk/cli_analyzer.py new file mode 100644 index 0000000..8cf528f --- /dev/null +++ b/examples/python-sdk/cli_analyzer.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +""" +CLI Code Analyzer using Augment SDK + +This program analyzes TypeScript files in the CLI directory for: +- Error handling completeness +- JSDoc documentation coverage +- Potential bugs and code smells + +It generates reports and conditional recommendations based on the findings. +""" + +from auggie_sdk import Auggie +from dataclasses import dataclass +from typing import List, Optional +from pathlib import Path +import sys + + +@dataclass +class FileAnalysis: + """Analysis results for a single TypeScript file.""" + file_path: str + has_error_handling: bool + has_jsdoc: bool + bugs_found: List[str] + code_smells: List[str] + importance_score: int # 1-10, higher = more important + + +@dataclass +class Issue: + """Represents a code issue found during analysis.""" + file_path: str + severity: str # "critical", "high", "medium", "low" + category: str # "error_handling", "documentation", "bug", "code_smell" + description: str + line_number: Optional[int] = None + + +def main(): + """Main execution function.""" + + # Initialize the agent + print("🚀 Initializing Augment Agent...") + agent = Auggie( + workspace_root="/home/augment/augment2", + model="claude-3-5-sonnet-latest" + ) + + # ============================================================================ + # STAGE 1: Discover TypeScript files in the CLI directory + # ============================================================================ + print("\n📁 Stage 1: Discovering TypeScript files...") + + try: + ts_files = agent( + "List all TypeScript files (.ts) in the clients/beachhead/src/cli directory. " + "Return just the file paths as a list of strings, relative to the workspace root.", + list[str] + ) + print(f" Found {len(ts_files)} TypeScript files to analyze") + except Exception as e: + print(f"❌ Error discovering files: {e}") + return 1 + + if not ts_files: + print("⚠️ No TypeScript files found in the specified directory") + return 0 + + # ============================================================================ + # STAGE 2: Analyze each file individually + # ============================================================================ + print(f"\n🔍 Stage 2: Analyzing {len(ts_files)} files...") + + all_analyses: List[FileAnalysis] = [] + all_issues: List[Issue] = [] + + # Use a session to maintain context across file analyses + with agent.session() as session: + for idx, file_path in enumerate(ts_files, 1): + print(f" [{idx}/{len(ts_files)}] Analyzing {file_path}...") + + try: + # Analyze this specific file + analysis = session( + f"Analyze the file {file_path}. Check for:\n" + f"1. Whether it has proper error handling (try-catch blocks, error returns)\n" + f"2. Whether exported functions have JSDoc comments\n" + f"3. Any potential bugs or code smells\n" + f"Also rate the importance of this file from 1-10 based on its role.\n" + f"Return a FileAnalysis object.", + FileAnalysis + ) + all_analyses.append(analysis) + + # Convert analysis to issues + if not analysis.has_error_handling: + all_issues.append(Issue( + file_path=file_path, + severity="high", + category="error_handling", + description="Missing proper error handling" + )) + + if not analysis.has_jsdoc: + all_issues.append(Issue( + file_path=file_path, + severity="medium", + category="documentation", + description="Missing JSDoc comments for exported functions" + )) + + for bug in analysis.bugs_found: + all_issues.append(Issue( + file_path=file_path, + severity="critical", + category="bug", + description=bug + )) + + for smell in analysis.code_smells: + all_issues.append(Issue( + file_path=file_path, + severity="low", + category="code_smell", + description=smell + )) + + except Exception as e: + print(f" ⚠️ Error analyzing {file_path}: {e}") + continue + + print(f" ✅ Analysis complete: {len(all_issues)} issues found") + + # ============================================================================ + # STAGE 3: Create summary report + # ============================================================================ + print("\n📊 Stage 3: Creating summary report...") + + files_missing_error_handling = sum(1 for a in all_analyses if not a.has_error_handling) + files_missing_jsdoc = sum(1 for a in all_analyses if not a.has_jsdoc) + + print(f" - Files missing error handling: {files_missing_error_handling}") + print(f" - Files missing JSDoc: {files_missing_jsdoc}") + + try: + agent( + f"Create a comprehensive analysis report in cli_analysis_report.md with:\n" + f"- Summary statistics: {len(ts_files)} files analyzed, {len(all_issues)} issues found\n" + f"- {files_missing_error_handling} files missing error handling\n" + f"- {files_missing_jsdoc} files missing JSDoc comments\n" + f"- Breakdown by severity and category\n" + f"- List of all files analyzed with their status\n" + f"Use the analysis data from our previous conversation in this session." + ) + print(" ✅ Report created: cli_analysis_report.md") + except Exception as e: + print(f" ❌ Error creating report: {e}") + + # ============================================================================ + # STAGE 4: Conditional actions based on thresholds + # ============================================================================ + print("\n⚙️ Stage 4: Checking thresholds for conditional actions...") + + # Condition 1: More than 5 files missing error handling + if files_missing_error_handling > 5: + print(f" 🔧 {files_missing_error_handling} files missing error handling (>5 threshold)") + print(" Creating detailed error handling plan...") + + try: + agent( + f"Create a detailed plan for adding error handling to the {files_missing_error_handling} " + f"files that are missing it. Save this plan in error_handling_plan.md. " + f"Include:\n" + f"- Prioritization strategy\n" + f"- Common error patterns to handle\n" + f"- Code examples for typical CLI error scenarios\n" + f"- Step-by-step implementation guide\n" + f"Reference the files we analyzed earlier in this session." + ) + print(" ✅ Error handling plan created: error_handling_plan.md") + except Exception as e: + print(f" ❌ Error creating plan: {e}") + else: + print(f" ℹ️ Only {files_missing_error_handling} files missing error handling (≤5 threshold)") + + # Condition 2: More than 10 files missing JSDoc + if files_missing_jsdoc > 10: + print(f" 📝 {files_missing_jsdoc} files missing JSDoc (>10 threshold)") + print(" Generating JSDoc templates for top 5 most important files...") + + # Find the 5 most important files missing JSDoc + files_needing_jsdoc = [a for a in all_analyses if not a.has_jsdoc] + top_5_files = sorted(files_needing_jsdoc, key=lambda x: x.importance_score, reverse=True)[:5] + + print(f" Top 5 files: {[f.file_path for f in top_5_files]}") + + try: + with agent.session() as session: + for file_analysis in top_5_files: + session( + f"Generate JSDoc comment templates for all exported functions in " + f"{file_analysis.file_path}. Add these as comments in the file, " + f"following TypeScript JSDoc best practices." + ) + print(" ✅ JSDoc templates generated for top 5 files") + except Exception as e: + print(f" ❌ Error generating JSDoc templates: {e}") + else: + print(f" ℹ️ Only {files_missing_jsdoc} files missing JSDoc (≤10 threshold)") + + # ============================================================================ + # STAGE 5: Prioritize and fix top 3 critical issues + # ============================================================================ + print("\n🎯 Stage 5: Prioritizing and fixing top issues...") + + print(f" Total issues found: {len(all_issues)}") + + # Prioritize issues (critical > high > medium > low) + severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3} + sorted_issues = sorted(all_issues, key=lambda x: severity_order.get(x.severity, 4)) + + top_3_issues = sorted_issues[:3] + + if not top_3_issues: + print(" ✅ No critical issues found!") + return 0 + + print(f" Top 3 critical issues:") + for idx, issue in enumerate(top_3_issues, 1): + print(f" {idx}. [{issue.severity.upper()}] {issue.file_path}: {issue.description}") + + # Generate specific fixes for each top issue + print("\n Generating fix suggestions with code examples...") + + with agent.session() as session: + for idx, issue in enumerate(top_3_issues, 1): + print(f" [{idx}/3] Creating fix for: {issue.description} in {issue.file_path}") + + try: + session( + f"Create a specific fix suggestion for this issue:\n" + f"File: {issue.file_path}\n" + f"Issue: {issue.description}\n" + f"Category: {issue.category}\n" + f"Severity: {issue.severity}\n\n" + f"Provide:\n" + f"1. Explanation of the problem\n" + f"2. Concrete code example showing the fix\n" + f"3. Why this fix is important\n\n" + f"Add this to a file called fix_suggestion_{idx}.md" + ) + except Exception as e: + print(f" ⚠️ Error creating fix suggestion: {e}") + + print(" ✅ Fix suggestions created: fix_suggestion_1.md, fix_suggestion_2.md, fix_suggestion_3.md") + + # ============================================================================ + # FINAL SUMMARY + # ============================================================================ + print("\n" + "="*80) + print("📋 ANALYSIS COMPLETE") + print("="*80) + print(f"Files analyzed: {len(ts_files)}") + print(f"Total issues found: {len(all_issues)}") + print(f"Files missing error handling: {files_missing_error_handling}") + print(f"Files missing JSDoc: {files_missing_jsdoc}") + print(f"\nGenerated files:") + print(f" - cli_analysis_report.md (summary report)") + if files_missing_error_handling > 5: + print(f" - error_handling_plan.md (error handling plan)") + if files_missing_jsdoc > 10: + print(f" - JSDoc templates added to top 5 files") + if top_3_issues: + print(f" - fix_suggestion_1.md, fix_suggestion_2.md, fix_suggestion_3.md (fix suggestions)") + print("="*80) + + return 0 + + +if __name__ == "__main__": + try: + sys.exit(main()) + except KeyboardInterrupt: + print("\n\n⚠️ Analysis interrupted by user") + sys.exit(1) + except Exception as e: + print(f"\n\n❌ Unexpected error: {e}") + sys.exit(1) + diff --git a/examples/python-sdk/demo_session_continuity.py b/examples/python-sdk/demo_session_continuity.py new file mode 100644 index 0000000..69c1f64 --- /dev/null +++ b/examples/python-sdk/demo_session_continuity.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +Demo: Session Continuity in ACP Client + +This demonstrates how the ACP client automatically maintains session context +across multiple messages, unlike the Agent library which requires explicit +session management. +""" + +from auggie_sdk.acp import AuggieACPClient, AgentEventListener + + +class SimpleListener(AgentEventListener): + """Simple listener that prints agent responses.""" + + def on_agent_message_chunk(self, text: str): + print(text, end="", flush=True) + + +def demo_session_continuity(): + """Demonstrate automatic session continuity.""" + print("=" * 80) + print("DEMO: Automatic Session Continuity in ACP Client") + print("=" * 80) + print() + + # Create client with listener for real-time output + client = AuggieACPClient( + model="claude-3-5-sonnet-latest", listener=SimpleListener() + ) + + print("Starting ACP client (creates a persistent session)...") + client.start() + print(f"✓ Session started: {client.session_id}\n") + + # Message 1: Ask agent to remember something + print("=" * 80) + print("MESSAGE 1: Remember a number") + print("=" * 80) + print() + client.send_message("Remember the number 42. Just acknowledge you'll remember it.") + print("\n") + + input("Press Enter to send next message...") + print() + + # Message 2: Ask agent to recall it + print("=" * 80) + print("MESSAGE 2: Recall the number (tests session continuity)") + print("=" * 80) + print() + client.send_message("What number did I ask you to remember?") + print("\n") + + input("Press Enter to send next message...") + print() + + # Message 3: Do some math with it + print("=" * 80) + print("MESSAGE 3: Use the number in a calculation") + print("=" * 80) + print() + client.send_message("What is that number multiplied by 2?") + print("\n") + + input("Press Enter to send next message...") + print() + + # Message 4: Create a function + print("=" * 80) + print("MESSAGE 4: Create a function") + print("=" * 80) + print() + client.send_message( + "Create a Python function called 'greet' that takes a name and returns 'Hello, {name}!'" + ) + print("\n") + + input("Press Enter to send next message...") + print() + + # Message 5: Reference the function we just created + print("=" * 80) + print("MESSAGE 5: Reference the function (tests code context)") + print("=" * 80) + print() + client.send_message("Now call that greet function with the name 'Alice'") + print("\n") + + # Stop the client + print("=" * 80) + print("Stopping client...") + client.stop() + print("✓ Client stopped") + print("=" * 80) + print() + + print("✅ DEMO COMPLETE!") + print() + print("Key Takeaway:") + print(" - All 5 messages went to the SAME session") + print(" - The agent remembered the number (42)") + print(" - The agent remembered the function (greet)") + print(" - NO explicit session management needed!") + print(" - This is the power of the long-running ACP architecture") + print() + + +def demo_comparison(): + """Show the difference between Agent and ACP client.""" + print("=" * 80) + print("COMPARISON: Agent vs ACP Client Session Management") + print("=" * 80) + print() + + print("Agent Library (subprocess-based):") + print("-" * 80) + print(""" +# Each call spawns a new process - NO context by default +agent = Auggie() +agent.run("Remember the number 42") +agent.run("What number did I ask you to remember?") # ❌ Doesn't remember! + +# Need explicit session for context +with agent.session() as session: + session.run("Remember the number 42") + session.run("What number did I ask you to remember?") # ✅ Remembers! +""") + + print() + print("ACP Client (long-running):") + print("-" * 80) + print(""" +# Single process, single session - context is AUTOMATIC +client = AuggieACPClient() +client.start() +client.send_message("Remember the number 42") +client.send_message("What number did I ask you to remember?") # ✅ Remembers! +client.stop() +""") + + print() + print("Summary:") + print(" - Agent: Explicit session management required for context") + print(" - ACP Client: Automatic session continuity, no management needed") + print() + + +if __name__ == "__main__": + print("\n") + + # Show comparison first + demo_comparison() + + input("Press Enter to run the live demo...") + print("\n") + + # Run the live demo + demo_session_continuity() diff --git a/examples/python-sdk/event_listener_demo.py b/examples/python-sdk/event_listener_demo.py new file mode 100644 index 0000000..51cff35 --- /dev/null +++ b/examples/python-sdk/event_listener_demo.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +""" +Example: AgentEventListener Demo + +This example demonstrates all the events in the AgentEventListener interface +and shows you exactly when each event is triggered and what data it contains. +""" + +import time +from typing import Any, Optional + +from auggie_sdk import Auggie +from auggie_sdk.acp import AgentEventListener + + +class DetailedEventLogger(AgentEventListener): + """ + A listener that logs all events with detailed information. + + This helps you understand what's happening during agent execution. + """ + + def __init__(self): + self.message_chunks = [] + self.tool_calls = {} + self.thoughts = [] + self.start_time = time.time() + + def _timestamp(self) -> str: + """Get elapsed time since start.""" + elapsed = time.time() - self.start_time + return f"[{elapsed:6.2f}s]" + + def on_agent_message_chunk(self, text: str) -> None: + """ + Called when the agent sends a message chunk (streaming). + + This is the agent's final response to you, streamed in real-time. + You'll receive many small chunks that together form the complete message. + """ + self.message_chunks.append(text) + + # Show the chunk (with visible quotes to see whitespace) + print(f"{self._timestamp()} 💬 AGENT MESSAGE CHUNK: {repr(text)}") + + def on_agent_message(self, message: str) -> None: + """ + Called when the agent finishes sending the complete message. + + This is called once with the full assembled message. + """ + print(f"{self._timestamp()} ✅ COMPLETE MESSAGE: {repr(message)}") + + def on_tool_call( + self, + tool_call_id: str, + title: str, + kind: Optional[str] = None, + status: Optional[str] = None, + ) -> None: + """ + Called when the agent makes a tool call. + + Tools are things like: + - "view" - reading a file + - "str-replace-editor" - editing a file + - "launch-process" - running a command + - "save-file" - creating a new file + """ + self.tool_calls[tool_call_id] = { + "title": title, + "kind": kind, + "status": status, + "start_time": time.time(), + "responses": [] + } + + print(f"\n{self._timestamp()} 🔧 TOOL CALL:") + print(f" ID: {tool_call_id}") + print(f" Title: {title}") + print(f" Kind: {kind}") + print(f" Status: {status}") + + def on_tool_response( + self, + tool_call_id: str, + status: Optional[str] = None, + content: Optional[Any] = None, + ) -> None: + """ + Called when a tool responds with results. + + This happens when: + - The tool completes and returns results + - The tool fails with an error + """ + if tool_call_id in self.tool_calls: + self.tool_calls[tool_call_id]["responses"].append({ + "status": status, + "content": content, + "time": time.time() + }) + + print(f"{self._timestamp()} 📥 TOOL RESPONSE:") + print(f" ID: {tool_call_id}") + print(f" Status: {status}") + if content: + # Show first 100 chars of content + content_str = str(content)[:100] + print(f" Content: {content_str}...") + + def on_agent_thought(self, text: str) -> None: + """ + Called when the agent shares its internal reasoning. + + This is like the agent "thinking out loud" about what it's going to do. + Not all agents/models provide thoughts. + """ + self.thoughts.append(text) + print(f"{self._timestamp()} 💭 AGENT THOUGHT: {text}") + + def print_summary(self): + """Print a summary of all events.""" + print("\n" + "=" * 80) + print("EVENT SUMMARY") + print("=" * 80) + + print(f"\n📊 Statistics:") + print(f" - Message chunks received: {len(self.message_chunks)}") + print(f" - Tool calls made: {len(self.tool_calls)}") + print(f" - Thoughts shared: {len(self.thoughts)}") + + print(f"\n💬 Complete Agent Message:") + full_message = "".join(self.message_chunks) + print(f" {full_message}") + + if self.tool_calls: + print(f"\n🔧 Tools Used:") + for tool_id, info in self.tool_calls.items(): + print(f" - {info['title']} ({info['kind']})") + print(f" Responses: {len(info['responses'])}") + + if self.thoughts: + print(f"\n💭 Agent Thoughts:") + for thought in self.thoughts: + print(f" - {thought}") + + +def demo_simple_query(): + """Demo 1: Simple query that doesn't use tools.""" + print("\n" + "=" * 80) + print("DEMO 1: Simple Math Query (No Tools)") + print("=" * 80) + print("This query is simple enough that the agent can answer directly") + print("without using any tools. You'll see message chunks but no tool calls.") + print() + + listener = DetailedEventLogger() + agent = Auggie(listener=listener) + + print("Sending: 'What is 2 + 2?'\n") + response = agent.run("What is 2 + 2?") + + listener.print_summary() + + +def demo_file_operation(): + """Demo 2: Query that requires reading a file.""" + print("\n" + "=" * 80) + print("DEMO 2: File Reading Query (Uses Tools)") + print("=" * 80) + print("This query requires the agent to read a file, so you'll see") + print("tool calls in addition to the message chunks.") + print() + + listener = DetailedEventLogger() + agent = Auggie(listener=listener) + + print("Sending: 'How many lines are in the file README.md?'\n") + response = agent.run("How many lines are in the file README.md?") + + listener.print_summary() + + +def demo_with_session(): + """Demo 3: Multiple queries in a session.""" + print("\n" + "=" * 80) + print("DEMO 3: Session with Multiple Queries") + print("=" * 80) + print("Using a session to maintain context across multiple queries.") + print() + + listener = DetailedEventLogger() + agent = Auggie(listener=listener) + + with agent.session() as session: + print("Query 1: 'What is 5 + 3?'\n") + response1 = session.run("What is 5 + 3?") + + print("\n" + "-" * 80) + print("Query 2: 'What was the last number I asked about?'\n") + response2 = session.run("What was the last number I asked about?") + + listener.print_summary() + + +def main(): + """Run all demos.""" + print(""" +╔════════════════════════════════════════════════════════════════════════════╗ +║ AgentEventListener Demo ║ +║ ║ +║ This demo shows you exactly what events are triggered when you use ║ +║ the Agent, and what data each event contains. ║ +╚════════════════════════════════════════════════════════════════════════════╝ + """) + + # Run demos + demo_simple_query() + + print("\n\n" + "🎯 " * 20) + input("Press Enter to continue to Demo 2...") + demo_file_operation() + + print("\n\n" + "🎯 " * 20) + input("Press Enter to continue to Demo 3...") + demo_with_session() + + print("\n\n" + "=" * 80) + print("✅ All demos complete!") + print("=" * 80) + print("\nKey Takeaways:") + print(" 1. on_agent_message_chunk() is called many times with small chunks") + print(" 2. on_agent_message() is called once with the complete message") + print(" 3. on_tool_call() is called when the agent uses a tool") + print(" 4. on_tool_response() provides the tool's results") + print(" 5. on_agent_thought() shows the agent's reasoning (if available)") + print("\nSee docs/AGENT_EVENT_LISTENER.md for more details!") + + +if __name__ == "__main__": + main() + diff --git a/examples/python-sdk/example_prompt.txt b/examples/python-sdk/example_prompt.txt new file mode 100644 index 0000000..a2999c7 --- /dev/null +++ b/examples/python-sdk/example_prompt.txt @@ -0,0 +1 @@ +Analyze all Python files in the src/ directory, identify any security vulnerabilities or code quality issues, create a detailed report in markdown format, and if there are any critical security issues found, generate fix suggestions for each one with specific code changes needed. diff --git a/examples/python-sdk/function_calling_example.py b/examples/python-sdk/function_calling_example.py new file mode 100644 index 0000000..ccd9db1 --- /dev/null +++ b/examples/python-sdk/function_calling_example.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +Example demonstrating function calling with the Augment Agent SDK. + +This example shows how to provide Python functions that the agent can call +during execution to accomplish tasks. +""" + +from auggie_sdk import Auggie + + +def get_weather(location: str, unit: str = "celsius") -> dict: + """ + Get the current weather for a location. + + Args: + location: City name or coordinates + unit: Temperature unit - either 'celsius' or 'fahrenheit' + + Returns: + Dictionary with weather information + """ + # In a real implementation, this would call a weather API + # For demo purposes, return mock data + return { + "location": location, + "temperature": 22 if unit == "celsius" else 72, + "unit": unit, + "condition": "sunny", + "humidity": 65, + } + + +def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float: + """ + Calculate distance between two coordinates in kilometers. + + Args: + lat1: Latitude of first point + lon1: Longitude of first point + lat2: Latitude of second point + lon2: Longitude of second point + + Returns: + Distance in kilometers + """ + # Simplified calculation for demo + import math + + # Haversine formula + R = 6371 # Earth's radius in km + + lat1_rad = math.radians(lat1) + lat2_rad = math.radians(lat2) + delta_lat = math.radians(lat2 - lat1) + delta_lon = math.radians(lon2 - lon1) + + a = ( + math.sin(delta_lat / 2) ** 2 + + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon / 2) ** 2 + ) + c = 2 * math.asin(math.sqrt(a)) + + return R * c + + +def run_tests(test_file: str) -> dict: + """ + Run tests from a test file. + + Args: + test_file: Path to the test file + + Returns: + Dictionary with test results + """ + # Mock test results for demo + return { + "file": test_file, + "total": 15, + "passed": 13, + "failed": 2, + "skipped": 0, + "duration": 2.5, + } + + +def main(): + """Demonstrate function calling with various examples.""" + + agent = Auggie() + + print("🔧 Function Calling Examples") + print("=" * 50) + + # Example 1: Single function call + print("\n1. Weather Query:") + result = agent.run( + "What's the weather in San Francisco? Use the get_weather function.", + return_type=dict, + functions=[get_weather], + ) + print(f" Result: {result}") + + # Example 2: Function with calculations + print("\n2. Distance Calculation:") + result = agent.run( + "Calculate the distance between New York (40.7128, -74.0060) and " + "Los Angeles (34.0522, -118.2437) using the calculate_distance function.", + return_type=float, + functions=[calculate_distance], + ) + print(f" Distance: {result:.2f} km") + + # Example 3: Multiple functions + print("\n3. Multiple Functions:") + result = agent.run( + "First, get the weather in London. Then calculate the distance from " + "London (51.5074, -0.1278) to Paris (48.8566, 2.3522).", + return_type=str, + functions=[get_weather, calculate_distance], + ) + print(f" Result: {result}") + + # Example 4: Test runner + print("\n4. Running Tests:") + result = agent.run( + "Run the tests in 'test_auth.py' and tell me if they passed.", + return_type=dict, + functions=[run_tests], + ) + print(f" Test Results: {result}") + + # Example 5: Complex workflow + print("\n5. Complex Workflow:") + + def fetch_data(source: str) -> list: + """Fetch data from a source.""" + return [{"id": 1, "value": 100}, {"id": 2, "value": 200}] + + def process_data(data: list) -> dict: + """Process the fetched data.""" + total = sum(item["value"] for item in data) + return {"count": len(data), "total": total, "average": total / len(data)} + + result = agent.run( + "Fetch data from 'api' and then process it to get statistics.", + return_type=dict, + functions=[fetch_data, process_data], + ) + print(f" Statistics: {result}") + + print("\n✅ All examples completed!") + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/list_models.py b/examples/python-sdk/list_models.py new file mode 100644 index 0000000..83f5498 --- /dev/null +++ b/examples/python-sdk/list_models.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Example: List available AI models + +This example demonstrates how to use the Auggie.get_available_models() method +to retrieve and display the list of available AI models for your account. +""" + +from auggie_sdk import Auggie + +def main(): + """List all available models.""" + print("Fetching available models...") + print() + + try: + models = Auggie.get_available_models() + + print(f"Found {len(models)} available models:") + print("=" * 80) + print() + + for model in models: + print(f"📦 {model.name}") + print(f" ID: {model.id}") + if model.description: + print(f" {model.description}") + print() + + print("=" * 80) + print() + print("To use a specific model, pass it to the Agent constructor:") + print(f" agent = Auggie(model='{models[0].id}')") + + except Exception as e: + print(f"Error: {e}") + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main()) + diff --git a/examples/python-sdk/list_prs.py b/examples/python-sdk/list_prs.py new file mode 100644 index 0000000..137f79e --- /dev/null +++ b/examples/python-sdk/list_prs.py @@ -0,0 +1,38 @@ +import sys +from dataclasses import dataclass +from enum import Enum +from pathlib import Path + +# Add the parent directory to the path so we can import auggie_sdk +sys.path.insert(0, str(Path(__file__).parent.parent)) + + +from auggie_sdk import Auggie + + +@dataclass +class PR: + title: str + number: int + url: str + author: str + + +def main(): + a = Agent(workspace_root=Path.cwd()) + + if not a(bool) @ "are you connected to github?": + raise Exception("Not connected to github") + + prs = a(list[PR]) @ "List the last 3 PRs in the current repo" + + summaries = [a(str) @ f"summarize PR {pr}" for pr in prs] + + for pr, summary in zip(prs, summaries): + print("Title:", pr.title) + print("Summary:") + print(summary) + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/mock_client_example.py b/examples/python-sdk/mock_client_example.py new file mode 100644 index 0000000..367a152 --- /dev/null +++ b/examples/python-sdk/mock_client_example.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +""" +Example: Using a mock ACP client for testing + +This example demonstrates how to create an Agent with a provided ACPClient +for testing purposes. This is useful when you want to test your code that +uses the Agent without making real calls to the auggie CLI. +""" + +from auggie_sdk import Auggie +from auggie_sdk.acp import ACPClient + + +class MockACPClient(ACPClient): + """ + A simple mock ACP client for testing. + + This client simulates responses without actually calling the auggie CLI. + """ + + def __init__(self): + self._running = False + self._responses = { + "What is 2 + 2?": "4", + "What is the capital of France?": "Paris", + "List three colors": "Red, Blue, Green", + } + + def start(self) -> None: + """Start the mock client.""" + if self._running: + raise RuntimeError("Client is already running") + self._running = True + print("Mock client started") + + def stop(self) -> None: + """Stop the mock client.""" + self._running = False + print("Mock client stopped") + + def send_message(self, message: str, timeout: float = 30.0) -> str: + """ + Send a message and get a mock response. + + Args: + message: The message to send + timeout: Ignored in mock + + Returns: + A mock response based on the message + """ + if not self._running: + raise RuntimeError("Client is not running") + + print(f"Mock client received: {message}") + + # Return a predefined response if available + for question, answer in self._responses.items(): + if question.lower() in message.lower(): + return answer + + # Default response + return "I don't have a mock response for that question." + + def clear_context(self) -> None: + """Clear context (no-op in mock).""" + print("Mock client context cleared") + + @property + def is_running(self) -> bool: + """Check if the mock client is running.""" + return self._running + + +def main(): + """Demonstrate using a mock ACP client.""" + print("=" * 80) + print("Example: Using a Mock ACP Client for Testing") + print("=" * 80) + print() + + # Create a mock client + mock_client = MockACPClient() + + # Create an agent with the mock client + agent = Auggie(acp_client=mock_client) + + print("1. Testing with run() - client is started/stopped automatically") + print("-" * 80) + response1 = agent.run("What is 2 + 2?") + print(f"Response: {response1}") + print() + + print("2. Testing with another run() call") + print("-" * 80) + response2 = agent.run("What is the capital of France?") + print(f"Response: {response2}") + print() + + print("3. Testing with session - client stays running") + print("-" * 80) + with agent.session() as session: + response3 = session.run("List three colors") + print(f"Response: {response3}") + + response4 = session.run("What is 2 + 2?") + print(f"Response: {response4}") + print() + + print("=" * 80) + print("Benefits of using a provided ACP client:") + print(" - Test your code without calling the real auggie CLI") + print(" - Control responses for predictable testing") + print(" - Faster test execution") + print(" - No need for authentication or network access") + print("=" * 80) + + +if __name__ == "__main__": + main() + diff --git a/examples/python-sdk/session_usage.py b/examples/python-sdk/session_usage.py new file mode 100644 index 0000000..7a9a76b --- /dev/null +++ b/examples/python-sdk/session_usage.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +""" +Example demonstrating session context manager usage. +""" + +import sys +from pathlib import Path +from dataclasses import dataclass + +# Add the parent directory to the path so we can import auggie_sdk +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from auggie_sdk import Auggie + + +@dataclass +class Function: + name: str + description: str + parameters: list + return_type: str + + +def main(): + """Demonstrate session vs non-session usage.""" + + agent = Auggie() + + print("🔄 DEFAULT BEHAVIOR - Each call is independent:") + print("=" * 50) + + # Default behavior - no memory between calls + response1 = agent.run("Create a function called 'calculate_area' that takes width and height") + print("✅ Created function") + + # This call won't remember the previous function + response2 = agent.run("Now create a test for that function") + print("✅ Attempted to create test (but agent may not remember the function)") + + print("\n🔗 SESSION BEHAVIOR - Calls remember each other:") + print("=" * 50) + + # Session behavior - memory between calls (CLI creates session ID) + with agent.session() as session: + # First call in session + session.run("Create a function called 'calculate_volume' that takes length, width, and height") + print("✅ Created volume function in session") + + # Second call remembers the first + session.run("Now create a comprehensive test suite for that function") + print("✅ Created tests (agent remembers the volume function)") + + # Third call with typed result - also remembers previous context + functions = session(list[Function]) @ "List all the functions we've created in this conversation" + print(f"✅ Retrieved {len(functions)} functions from session memory:") + for func in functions: + print(f" - {func.name}: {func.description}") + + print(f" Session ID from CLI: {session.last_session_id}") + + print(f" Agent now remembers last session: {agent.last_session_id}") + + print("\n🎯 MIXED USAGE - Sessions for related work, independent calls for unrelated:") + print("=" * 50) + + # Independent call + agent.run("What's the weather like?") + print("✅ Independent weather query") + + # Continue the previous session automatically (uses agent.last_session_id) + print(f"\n Continuing last session automatically...") + with agent.session() as session: # Automatically resumes last session + session.run("Add error handling to the volume function") + print("✅ Continued session and added error handling") + + # New session for different task (CLI creates new session ID) + with agent.session() as session: + session.run("Create a function to reverse a string") + session.run("Create a function to check if a string is a palindrome") + + # This will only know about string functions, not the math functions from before + string_functions = session(list[Function]) @ "List the string utility functions we created" + print(f"✅ String session has {len(string_functions)} functions:") + for func in string_functions: + print(f" - {func.name}: {func.description}") + + print(f" New session ID: {session.last_session_id}") + + print("\n✨ Session context manager provides clean separation of concerns!") + print(" - CLI automatically creates session IDs") + print(" - agent.session() automatically resumes last session if available") + print(" - agent.session(id) explicitly specifies a session ID") + print(" - Agent remembers last_session_id for automatic continuation") + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/success_criteria_example.py b/examples/python-sdk/success_criteria_example.py new file mode 100644 index 0000000..259e845 --- /dev/null +++ b/examples/python-sdk/success_criteria_example.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +""" +Example demonstrating success_criteria parameter. + +This shows how to use success criteria to ensure the agent iteratively +works on a task and verifies criteria until all are met. + +The agent will: +1. Work on the task +2. Verify success criteria +3. If not all met, fix issues and repeat +4. Continue until all criteria are met or max rounds reached +5. If criteria not met after max rounds, raises AugmentVerificationError +""" + +from auggie_sdk import Agent, AugmentVerificationError + + +def main(): + """Demonstrate success criteria usage.""" + + agent = Auggie() + + print("🎯 Success Criteria Example") + print("=" * 60) + + # Example 1: Create a file with specific requirements + print("\n1. Creating a file with success criteria:") + print(" Task: Create a Python function") + print(" Criteria:") + print(" - Function must have type hints") + print(" - Function must have a docstring") + print(" - Function must have error handling") + + agent.run( + "Create a Python function called 'divide_numbers' that divides two numbers", + success_criteria=[ + "The function has type hints for all parameters and return value", + "The function has a comprehensive docstring", + "The function handles division by zero with proper error handling", + ], + ) + print(" ✅ Function created and verified!") + + # Example 2: Code refactoring with quality checks + print("\n2. Refactoring with quality criteria:") + print(" Task: Refactor a function") + print(" Criteria:") + print(" - Code follows PEP 8 style guidelines") + print(" - No code duplication") + print(" - All edge cases are handled") + + agent.run( + "Refactor the divide_numbers function to be more robust", + success_criteria=[ + "Code follows PEP 8 style guidelines", + "No code duplication exists", + "All edge cases (zero, negative numbers, type errors) are handled", + ], + ) + print(" ✅ Refactoring completed and verified!") + + # Example 3: Documentation with completeness checks + print("\n3. Documentation with completeness criteria:") + + agent.run( + "Add comprehensive documentation to the divide_numbers function", + success_criteria=[ + "Docstring includes parameter descriptions", + "Docstring includes return value description", + "Docstring includes examples of usage", + "Docstring includes information about exceptions that can be raised", + ], + ) + print(" ✅ Documentation added and verified!") + + # Example 4: Demonstrating iterative verification with max_verification_rounds + print("\n4. Iterative verification with custom max rounds:") + print(" Task: Create a complex function") + print(" Max verification rounds: 5") + + agent.run( + "Create a Python function called 'process_data' that validates and transforms input data", + success_criteria=[ + "Function has complete type hints", + "Function has comprehensive docstring with examples", + "Function handles all edge cases (None, empty, invalid types)", + "Function includes input validation with clear error messages", + "Code follows PEP 8 and has no duplication", + ], + max_verification_rounds=5, # Allow up to 5 rounds of verification + ) + print(" ✅ Complex function created with iterative verification!") + + # Example 5: Exception handling when criteria not met + print("\n5. Exception handling for unmet criteria:") + print(" Task: Intentionally difficult task with strict criteria") + + try: + agent.run( + "Create a simple hello world function", + success_criteria=[ + "Function has type hints", + "Function has docstring", + # This criterion is intentionally impossible to demonstrate exception + "Function must be written in a language that doesn't exist", + ], + max_verification_rounds=2, # Low max to trigger exception faster + ) + except AugmentVerificationError as e: + print(f" ⚠️ Verification failed after {e.rounds_attempted} rounds") + print(f" Unmet criteria: {e.unmet_criteria}") + print(f" Issues: {e.issues[:1]}...") # Show first issue + print(" ✅ Exception handling works correctly!") + + print("\n" + "=" * 60) + print("✨ All examples completed with success criteria verification!") + print("\nKey benefits of success_criteria:") + print(" • Ensures quality standards are met") + print(" • Agent iteratively verifies and fixes issues") + print(" • Reduces need for manual review") + print(" • Catches common mistakes automatically") + print(" • Provides structured feedback on what needs fixing") + print(" • Raises exception if criteria cannot be met") + print("\nHow it works:") + print(" 1. Agent works on the task") + print(" 2. Agent verifies all success criteria") + print(" 3. If not all met, agent receives specific feedback") + print(" 4. Agent fixes the identified issues") + print(" 5. Repeat until all criteria met or max rounds reached") + print(" 6. If max rounds exceeded, raises AugmentVerificationError") + + +if __name__ == "__main__": + main()