diff --git a/cookbook/python/recipe/error_handling.py b/cookbook/python/recipe/error_handling.py index 57073037..4b285a73 100644 --- a/cookbook/python/recipe/error_handling.py +++ b/cookbook/python/recipe/error_handling.py @@ -1,28 +1,39 @@ #!/usr/bin/env python3 +import asyncio from copilot import CopilotClient +from copilot.generated.session_events import SessionEventType -client = CopilotClient() +async def main(): + client = CopilotClient() -try: - client.start() - session = client.create_session(model="gpt-5") + try: + await client.start() + # Ensure model is passed as part of a dict + session = await client.create_session({"model": "gpt-5"}) - response = None - def handle_message(event): - nonlocal response - if event["type"] == "assistant.message": - response = event["data"]["content"] + # Using a list to allow modification inside inner function (closure workaround) + # or nonlocal would work if defined inside main + response_data = {"content": None} - session.on(handle_message) - session.send(prompt="Hello!") - session.wait_for_idle() + def handle_message(event): + if event.type == SessionEventType.ASSISTANT_MESSAGE: + response_data["content"] = event.data.content - if response: - print(response) + session.on(handle_message) - session.destroy() -except Exception as e: - print(f"Error: {e}") -finally: - client.stop() + # Use send_and_wait with timeout + await session.send_and_wait({"prompt": "Hello!"}, timeout=30) + + if response_data["content"]: + print(f"Copilot: {response_data['content']}") + + await session.destroy() + + except Exception as e: + print(f"Error: {e}") + finally: + await client.stop() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/cookbook/python/recipe/managing_local_files.py b/cookbook/python/recipe/managing_local_files.py index 0fd43e50..4359b641 100644 --- a/cookbook/python/recipe/managing_local_files.py +++ b/cookbook/python/recipe/managing_local_files.py @@ -1,42 +1,63 @@ #!/usr/bin/env python3 -from copilot import CopilotClient +import asyncio import os +from copilot import CopilotClient +from copilot.generated.session_events import SessionEventType + +async def main(): + # Create and start client + client = CopilotClient() + await client.start() + + try: + # Create session + session = await client.create_session({"model": "gpt-5"}) + + # Event handler + def handle_event(event): + if event.type == SessionEventType.ASSISTANT_MESSAGE: + print(f"\nCopilot: {event.data.content}") + elif event.type == SessionEventType.TOOL_EXECUTION_START: + print(f" → Running: {event.data.tool_name}") + elif event.type == SessionEventType.TOOL_EXECUTION_COMPLETE: + # Check if tool_call_id exists in data + call_id = getattr(event.data, "tool_call_id", "unknown") + print(f" āœ“ Completed: {call_id}") + + session.on(handle_event) -# Create and start client -client = CopilotClient() -client.start() + # Ask Copilot to organize files + # Change this to your target folder + target_folder = os.path.expanduser("~/Downloads") -# Create session -session = client.create_session(model="gpt-5") + print(f"šŸ“‚ Organizing files in: {target_folder}\n") -# Event handler -def handle_event(event): - if event["type"] == "assistant.message": - print(f"\nCopilot: {event['data']['content']}") - elif event["type"] == "tool.execution_start": - print(f" → Running: {event['data']['toolName']}") - elif event["type"] == "tool.execution_complete": - print(f" āœ“ Completed: {event['data']['toolCallId']}") + await session.send_and_wait({ + "prompt": f""" + Analyze the files in "{target_folder}" and organize them into subfolders. -session.on(handle_event) + 1. First, list all files and their metadata + 2. Preview grouping by file extension + 3. Create appropriate subfolders (e.g., "images", "documents", "videos") + 4. Move each file to its appropriate subfolder -# Ask Copilot to organize files -# Change this to your target folder -target_folder = os.path.expanduser("~/Downloads") + Please confirm before moving any files. + """ + }, timeout=300) -session.send(prompt=f""" -Analyze the files in "{target_folder}" and organize them into subfolders. + # Allow user to respond if Copilot asks for confirmation + while True: + user_input = await asyncio.get_event_loop().run_in_executor(None, input, "\nYou: ") + if user_input.lower() in ["exit", "quit", "no", "n"]: + break -1. First, list all files and their metadata -2. Preview grouping by file extension -3. Create appropriate subfolders (e.g., "images", "documents", "videos") -4. Move each file to its appropriate subfolder + await session.send_and_wait({"prompt": user_input}, timeout=300) -Please confirm before moving any files. -""") + await session.destroy() -session.wait_for_idle() + finally: + await client.stop() -session.destroy() -client.stop() +if __name__ == "__main__": + asyncio.run(main()) diff --git a/cookbook/python/recipe/multiple_sessions.py b/cookbook/python/recipe/multiple_sessions.py index 92921d2d..8c3819d7 100644 --- a/cookbook/python/recipe/multiple_sessions.py +++ b/cookbook/python/recipe/multiple_sessions.py @@ -1,35 +1,46 @@ #!/usr/bin/env python3 +import asyncio from copilot import CopilotClient -client = CopilotClient() -client.start() +async def main(): + client = CopilotClient() + await client.start() -# Create multiple independent sessions -session1 = client.create_session(model="gpt-5") -session2 = client.create_session(model="gpt-5") -session3 = client.create_session(model="claude-sonnet-4.5") + try: + # Create multiple independent sessions with config dicts + # Use gpt-5 for the first two sessions, per the SDK's SessionConfig valid models. + session1 = await client.create_session({"model": "gpt-5"}) + session2 = await client.create_session({"model": "gpt-5"}) + # Use a valid Claude Sonnet model identifier as per SessionConfig type definition. + session3 = await client.create_session({"model": "claude-sonnet-4.5"}) -print("Created 3 independent sessions") + print("Created 3 independent sessions") -# Each session maintains its own conversation history -session1.send(prompt="You are helping with a Python project") -session2.send(prompt="You are helping with a TypeScript project") -session3.send(prompt="You are helping with a Go project") + # Each session maintains its own conversation history + # We can run these in parallel or sequence. Sequence is easier to follow in logs. + await session1.send_and_wait({"prompt": "You are helping with a Python project"}) + await session2.send_and_wait({"prompt": "You are helping with a TypeScript project"}) + await session3.send_and_wait({"prompt": "You are helping with a Go project"}) -print("Sent initial context to all sessions") + print("Sent initial context to all sessions") -# Follow-up messages stay in their respective contexts -session1.send(prompt="How do I create a virtual environment?") -session2.send(prompt="How do I set up tsconfig?") -session3.send(prompt="How do I initialize a module?") + # Follow-up messages stay in their respective contexts + await session1.send_and_wait({"prompt": "How do I create a virtual environment?"}) + await session2.send_and_wait({"prompt": "How do I set up tsconfig?"}) + await session3.send_and_wait({"prompt": "How do I initialize a module?"}) -print("Sent follow-up questions to each session") + print("Sent follow-up questions to each session") -# Clean up all sessions -session1.destroy() -session2.destroy() -session3.destroy() -client.stop() + # Clean up all sessions + await session1.destroy() + await session2.destroy() + await session3.destroy() -print("All sessions destroyed successfully") + print("All sessions destroyed successfully") + + finally: + await client.stop() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/cookbook/python/recipe/persisting_sessions.py b/cookbook/python/recipe/persisting_sessions.py index 071ff1a8..b04cdfcb 100644 --- a/cookbook/python/recipe/persisting_sessions.py +++ b/cookbook/python/recipe/persisting_sessions.py @@ -1,36 +1,50 @@ #!/usr/bin/env python3 +import asyncio from copilot import CopilotClient -client = CopilotClient() -client.start() +async def main(): + client = CopilotClient() + await client.start() -# Create session with a memorable ID -session = client.create_session( - session_id="user-123-conversation", - model="gpt-5", -) + try: + # Create session with a memorable ID + # Note: the SDK might generate the ID, or we pass it in config if supported + # Looking at client.py, create_session takes config which can have session_id + session = await client.create_session({ + "session_id": "user-123-conversation", + "model": "gpt-5", + }) -session.send(prompt="Let's discuss TypeScript generics") -print(f"Session created: {session.session_id}") + await session.send_and_wait({"prompt": "Let's discuss TypeScript generics"}) + print(f"Session created: {session.session_id}") -# Destroy session but keep data on disk -session.destroy() -print("Session destroyed (state persisted)") + # Destroy session but keep data on disk? + # Actually session.destroy() releases resources in the client but also sends 'session.destroy' to the server. + # If the server persists sessions, we can resume. + await session.destroy() + print("Session destroyed (client side resources released)") -# Resume the previous session -resumed = client.resume_session("user-123-conversation") -print(f"Resumed: {resumed.session_id}") + # Resume the previous session + resumed = await client.resume_session("user-123-conversation") + print(f"Resumed: {resumed.session_id}") -resumed.send(prompt="What were we discussing?") + await resumed.send_and_wait({"prompt": "What were we discussing?"}) -# List sessions -sessions = client.list_sessions() -print("Sessions:", [s["sessionId"] for s in sessions]) + # Listen for the response to verify + # (In a real app we'd set up handlers before sending) -# Delete session permanently -client.delete_session("user-123-conversation") -print("Session deleted") + # list_sessions and delete_session are not currently available in the Python SDK Client + # sessions = await client.list_sessions() + # print("Sessions:", [s["sessionId"] for s in sessions]) -resumed.destroy() -client.stop() + # client.delete_session("user-123-conversation") + # print("Session deleted") + + await resumed.destroy() + + finally: + await client.stop() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/cookbook/python/recipe/pr_visualization.py b/cookbook/python/recipe/pr_visualization.py index 72226c3d..dd4aa85b 100644 --- a/cookbook/python/recipe/pr_visualization.py +++ b/cookbook/python/recipe/pr_visualization.py @@ -4,6 +4,7 @@ import sys import os import re +import asyncio from copilot import CopilotClient # ============================================================================ @@ -60,7 +61,7 @@ def prompt_for_repo(): # Main Application # ============================================================================ -def main(): +async def run_analysis(): print("šŸ” PR Age Chart Generator\n") # Determine the repository @@ -88,14 +89,16 @@ def main(): owner, repo_name = repo.split("/", 1) - # Create Copilot client - no custom tools needed! - client = CopilotClient(log_level="error") - client.start() + # Create Copilot client - pass options as a dict + client = CopilotClient({"log_level": "error"}) + + try: + await client.start() - session = client.create_session( - model="gpt-5", - system_message={ - "content": f""" + session = await client.create_session({ + "model": "gpt-5", + "system_message": { + "content": f""" You are analyzing pull requests for the GitHub repository: {owner}/{repo_name} The current working directory is: {os.getcwd()} @@ -108,54 +111,59 @@ def main(): - Be concise in your responses """ - } - ) - - # Set up event handling - def handle_event(event): - if event["type"] == "assistant.message": - print(f"\nšŸ¤– {event['data']['content']}\n") - elif event["type"] == "tool.execution_start": - print(f" āš™ļø {event['data']['toolName']}") - - session.on(handle_event) - - # Initial prompt - let Copilot figure out the details - print("\nšŸ“Š Starting analysis...\n") - - session.send(prompt=f""" - Fetch the open pull requests for {owner}/{repo_name} from the last week. - Calculate the age of each PR in days. - Then generate a bar chart image showing the distribution of PR ages - (group them into sensible buckets like <1 day, 1-3 days, etc.). - Save the chart as "pr-age-chart.png" in the current directory. - Finally, summarize the PR health - average age, oldest PR, and how many might be considered stale. - """) - - session.wait_for_idle() - - # Interactive loop - print("\nšŸ’” Ask follow-up questions or type \"exit\" to quit.\n") - print("Examples:") - print(" - \"Expand to the last month\"") - print(" - \"Show me the 5 oldest PRs\"") - print(" - \"Generate a pie chart instead\"") - print(" - \"Group by author instead of age\"") - print() - - while True: - user_input = input("You: ").strip() - - if user_input.lower() in ["exit", "quit"]: - print("šŸ‘‹ Goodbye!") - break - - if user_input: - session.send(prompt=user_input) - session.wait_for_idle() - - session.destroy() - client.stop() + } + }) + + # Set up event handling + def handle_event(event): + if event.type == "assistant.message": + print(f"\nšŸ¤– {event.data.content}\n") + elif event.type == "tool.execution_start": + print(f" āš™ļø {event.data.tool_name}") + + session.on(handle_event) + + # Initial prompt + print("\nšŸ“Š Starting analysis...\n") + + await session.send_and_wait({ + "prompt": f""" + Fetch the open pull requests for {owner}/{repo_name} from the last week. + Calculate the age of each PR in days. + Then generate a bar chart image showing the distribution of PR ages + (group them into sensible buckets like <1 day, 1-3 days, etc.). + Save the chart as "pr-age-chart.png" in the current directory. + Finally, summarize the PR health - average age, oldest PR, and how many might be considered stale. + """ + }, timeout=300) + + # Interactive loop + print("\nšŸ’” Ask follow-up questions or type \"exit\" to quit.\n") + print("Examples:") + print(" - \"Expand to the last month\"") + print(" - \"Show me the 5 oldest PRs\"") + print(" - \"Generate a pie chart instead\"") + print(" - \"Group by author instead of age\"") + print() + + while True: + # Use run_in_executor so that the blocking input() call doesn't block the event loop. + user_input = await asyncio.get_event_loop().run_in_executor(None, input, "You: ") + user_input = user_input.strip() + + if user_input.lower() in ["exit", "quit"]: + print("šŸ‘‹ Goodbye!") + break + + if user_input: + await session.send_and_wait({"prompt": user_input}) + + finally: + # Cleanup + await client.stop() + +def main(): + asyncio.run(run_analysis()) if __name__ == "__main__": main()