mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 18:35:14 +00:00
Moving the copilot-sdk cookbook content in here
This commit is contained in:
19
cookbook/copilot-sdk/python/README.md
Normal file
19
cookbook/copilot-sdk/python/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# GitHub Copilot SDK Cookbook — Python
|
||||
|
||||
This folder hosts short, practical recipes for using the GitHub Copilot SDK with Python. Each recipe is concise, copy‑pasteable, and points to fuller examples and tests.
|
||||
|
||||
## Recipes
|
||||
|
||||
- [Error Handling](error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
|
||||
- [Multiple Sessions](multiple-sessions.md): Manage multiple independent conversations simultaneously.
|
||||
- [Managing Local Files](managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
|
||||
- [PR Visualization](pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
|
||||
- [Persisting Sessions](persisting-sessions.md): Save and resume sessions across restarts.
|
||||
|
||||
## Contributing
|
||||
|
||||
Add a new recipe by creating a markdown file in this folder and linking it above. Follow repository guidance in [CONTRIBUTING.md](../../CONTRIBUTING.md).
|
||||
|
||||
## Status
|
||||
|
||||
This README is a scaffold; recipe files are placeholders until populated.
|
||||
150
cookbook/copilot-sdk/python/error-handling.md
Normal file
150
cookbook/copilot-sdk/python/error-handling.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Error Handling Patterns
|
||||
|
||||
Handle errors gracefully in your Copilot SDK applications.
|
||||
|
||||
> **Runnable example:** [recipe/error_handling.py](recipe/error_handling.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> python error_handling.py
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You need to handle various error conditions like connection failures, timeouts, and invalid responses.
|
||||
|
||||
## Basic try-except
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
|
||||
try:
|
||||
client.start()
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
response = None
|
||||
def handle_message(event):
|
||||
nonlocal response
|
||||
if event["type"] == "assistant.message":
|
||||
response = event["data"]["content"]
|
||||
|
||||
session.on(handle_message)
|
||||
session.send(prompt="Hello!")
|
||||
session.wait_for_idle()
|
||||
|
||||
if response:
|
||||
print(response)
|
||||
|
||||
session.destroy()
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
finally:
|
||||
client.stop()
|
||||
```
|
||||
|
||||
## Handling specific error types
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
client.start()
|
||||
except FileNotFoundError:
|
||||
print("Copilot CLI not found. Please install it first.")
|
||||
except ConnectionError:
|
||||
print("Could not connect to Copilot CLI server.")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
```
|
||||
|
||||
## Timeout handling
|
||||
|
||||
```python
|
||||
import signal
|
||||
from contextlib import contextmanager
|
||||
|
||||
@contextmanager
|
||||
def timeout(seconds):
|
||||
def timeout_handler(signum, frame):
|
||||
raise TimeoutError("Request timed out")
|
||||
|
||||
old_handler = signal.signal(signal.SIGALRM, timeout_handler)
|
||||
signal.alarm(seconds)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
signal.alarm(0)
|
||||
signal.signal(signal.SIGALRM, old_handler)
|
||||
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
try:
|
||||
session.send(prompt="Complex question...")
|
||||
|
||||
# Wait with timeout (30 seconds)
|
||||
with timeout(30):
|
||||
session.wait_for_idle()
|
||||
|
||||
print("Response received")
|
||||
except TimeoutError:
|
||||
print("Request timed out")
|
||||
```
|
||||
|
||||
## Aborting a request
|
||||
|
||||
```python
|
||||
import threading
|
||||
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
# Start a request
|
||||
session.send(prompt="Write a very long story...")
|
||||
|
||||
# Abort it after some condition
|
||||
def abort_later():
|
||||
import time
|
||||
time.sleep(5)
|
||||
session.abort()
|
||||
print("Request aborted")
|
||||
|
||||
threading.Thread(target=abort_later).start()
|
||||
```
|
||||
|
||||
## Graceful shutdown
|
||||
|
||||
```python
|
||||
import signal
|
||||
import sys
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
print("\nShutting down...")
|
||||
errors = client.stop()
|
||||
if errors:
|
||||
print(f"Cleanup errors: {errors}")
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
```
|
||||
|
||||
## Context manager for automatic cleanup
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
|
||||
with CopilotClient() as client:
|
||||
client.start()
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
# ... do work ...
|
||||
|
||||
# client.stop() is automatically called when exiting context
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
1. **Always clean up**: Use try-finally or context managers to ensure `stop()` is called
|
||||
2. **Handle connection errors**: The CLI might not be installed or running
|
||||
3. **Set appropriate timeouts**: Long-running requests should have timeouts
|
||||
4. **Log errors**: Capture error details for debugging
|
||||
119
cookbook/copilot-sdk/python/managing-local-files.md
Normal file
119
cookbook/copilot-sdk/python/managing-local-files.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Grouping Files by Metadata
|
||||
|
||||
Use Copilot to intelligently organize files in a folder based on their metadata.
|
||||
|
||||
> **Runnable example:** [recipe/managing_local_files.py](recipe/managing_local_files.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> python managing_local_files.py
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You have a folder with many files and want to organize them into subfolders based on metadata like file type, creation date, size, or other attributes. Copilot can analyze the files and suggest or execute a grouping strategy.
|
||||
|
||||
## Example code
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
import os
|
||||
|
||||
# Create and start client
|
||||
client = CopilotClient()
|
||||
client.start()
|
||||
|
||||
# Create session
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
# 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']}")
|
||||
|
||||
session.on(handle_event)
|
||||
|
||||
# Ask Copilot to organize files
|
||||
target_folder = os.path.expanduser("~/Downloads")
|
||||
|
||||
session.send(prompt=f"""
|
||||
Analyze the files in "{target_folder}" and organize them into subfolders.
|
||||
|
||||
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
|
||||
|
||||
Please confirm before moving any files.
|
||||
""")
|
||||
|
||||
session.wait_for_idle()
|
||||
|
||||
client.stop()
|
||||
```
|
||||
|
||||
## Grouping strategies
|
||||
|
||||
### By file extension
|
||||
|
||||
```python
|
||||
# Groups files like:
|
||||
# images/ -> .jpg, .png, .gif
|
||||
# documents/ -> .pdf, .docx, .txt
|
||||
# videos/ -> .mp4, .avi, .mov
|
||||
```
|
||||
|
||||
### By creation date
|
||||
|
||||
```python
|
||||
# Groups files like:
|
||||
# 2024-01/ -> files created in January 2024
|
||||
# 2024-02/ -> files created in February 2024
|
||||
```
|
||||
|
||||
### By file size
|
||||
|
||||
```python
|
||||
# Groups files like:
|
||||
# tiny-under-1kb/
|
||||
# small-under-1mb/
|
||||
# medium-under-100mb/
|
||||
# large-over-100mb/
|
||||
```
|
||||
|
||||
## Dry-run mode
|
||||
|
||||
For safety, you can ask Copilot to only preview changes:
|
||||
|
||||
```python
|
||||
session.send(prompt=f"""
|
||||
Analyze files in "{target_folder}" and show me how you would organize them
|
||||
by file type. DO NOT move any files - just show me the plan.
|
||||
""")
|
||||
```
|
||||
|
||||
## Custom grouping with AI analysis
|
||||
|
||||
Let Copilot determine the best grouping based on file content:
|
||||
|
||||
```python
|
||||
session.send(prompt=f"""
|
||||
Look at the files in "{target_folder}" and suggest a logical organization.
|
||||
Consider:
|
||||
- File names and what they might contain
|
||||
- File types and their typical uses
|
||||
- Date patterns that might indicate projects or events
|
||||
|
||||
Propose folder names that are descriptive and useful.
|
||||
""")
|
||||
```
|
||||
|
||||
## Safety considerations
|
||||
|
||||
1. **Confirm before moving**: Ask Copilot to confirm before executing moves
|
||||
2. **Handle duplicates**: Consider what happens if a file with the same name exists
|
||||
3. **Preserve originals**: Consider copying instead of moving for important files
|
||||
78
cookbook/copilot-sdk/python/multiple-sessions.md
Normal file
78
cookbook/copilot-sdk/python/multiple-sessions.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Working with Multiple Sessions
|
||||
|
||||
Manage multiple independent conversations simultaneously.
|
||||
|
||||
> **Runnable example:** [recipe/multiple_sessions.py](recipe/multiple_sessions.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> python multiple_sessions.py
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You need to run multiple conversations in parallel, each with its own context and history.
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
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")
|
||||
|
||||
# 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")
|
||||
|
||||
# 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?")
|
||||
|
||||
# Clean up all sessions
|
||||
session1.destroy()
|
||||
session2.destroy()
|
||||
session3.destroy()
|
||||
client.stop()
|
||||
```
|
||||
|
||||
## Custom session IDs
|
||||
|
||||
Use custom IDs for easier tracking:
|
||||
|
||||
```python
|
||||
session = client.create_session(
|
||||
session_id="user-123-chat",
|
||||
model="gpt-5"
|
||||
)
|
||||
|
||||
print(session.session_id) # "user-123-chat"
|
||||
```
|
||||
|
||||
## Listing sessions
|
||||
|
||||
```python
|
||||
sessions = client.list_sessions()
|
||||
for session_info in sessions:
|
||||
print(f"Session: {session_info['sessionId']}")
|
||||
```
|
||||
|
||||
## Deleting sessions
|
||||
|
||||
```python
|
||||
# Delete a specific session
|
||||
client.delete_session("user-123-chat")
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
- **Multi-user applications**: One session per user
|
||||
- **Multi-task workflows**: Separate sessions for different tasks
|
||||
- **A/B testing**: Compare responses from different models
|
||||
83
cookbook/copilot-sdk/python/persisting-sessions.md
Normal file
83
cookbook/copilot-sdk/python/persisting-sessions.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Session Persistence and Resumption
|
||||
|
||||
Save and restore conversation sessions across application restarts.
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want users to be able to continue a conversation even after closing and reopening your application.
|
||||
|
||||
> **Runnable example:** [recipe/persisting_sessions.py](recipe/persisting_sessions.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> python persisting_sessions.py
|
||||
> ```
|
||||
|
||||
### Creating a session with a custom ID
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
client.start()
|
||||
|
||||
# Create session with a memorable ID
|
||||
session = client.create_session(
|
||||
session_id="user-123-conversation",
|
||||
model="gpt-5",
|
||||
)
|
||||
|
||||
session.send(prompt="Let's discuss TypeScript generics")
|
||||
|
||||
# Session ID is preserved
|
||||
print(session.session_id) # "user-123-conversation"
|
||||
|
||||
# Destroy session but keep data on disk
|
||||
session.destroy()
|
||||
client.stop()
|
||||
```
|
||||
|
||||
### Resuming a session
|
||||
|
||||
```python
|
||||
client = CopilotClient()
|
||||
client.start()
|
||||
|
||||
# Resume the previous session
|
||||
session = client.resume_session("user-123-conversation")
|
||||
|
||||
# Previous context is restored
|
||||
session.send(prompt="What were we discussing?")
|
||||
|
||||
session.destroy()
|
||||
client.stop()
|
||||
```
|
||||
|
||||
### Listing available sessions
|
||||
|
||||
```python
|
||||
sessions = client.list_sessions()
|
||||
for s in sessions:
|
||||
print("Session:", s["sessionId"])
|
||||
```
|
||||
|
||||
### Deleting a session permanently
|
||||
|
||||
```python
|
||||
# Remove session and all its data from disk
|
||||
client.delete_session("user-123-conversation")
|
||||
```
|
||||
|
||||
### Getting session history
|
||||
|
||||
```python
|
||||
messages = session.get_messages()
|
||||
for msg in messages:
|
||||
print(f"[{msg['type']}] {msg['data']}")
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
1. **Use meaningful session IDs**: Include user ID or context in the session ID
|
||||
2. **Handle missing sessions**: Check if a session exists before resuming
|
||||
3. **Clean up old sessions**: Periodically delete sessions that are no longer needed
|
||||
218
cookbook/copilot-sdk/python/pr-visualization.md
Normal file
218
cookbook/copilot-sdk/python/pr-visualization.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Generating PR Age Charts
|
||||
|
||||
Build an interactive CLI tool that visualizes pull request age distribution for a GitHub repository using Copilot's built-in capabilities.
|
||||
|
||||
> **Runnable example:** [recipe/pr_visualization.py](recipe/pr_visualization.py)
|
||||
>
|
||||
> ```bash
|
||||
> cd recipe && pip install -r requirements.txt
|
||||
> # Auto-detect from current git repo
|
||||
> python pr_visualization.py
|
||||
>
|
||||
> # Specify a repo explicitly
|
||||
> python pr_visualization.py --repo github/copilot-sdk
|
||||
> ```
|
||||
|
||||
## Example scenario
|
||||
|
||||
You want to understand how long PRs have been open in a repository. This tool detects the current Git repo or accepts a repo as input, then lets Copilot fetch PR data via the GitHub MCP Server and generate a chart image.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install copilot-sdk
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-detect from current git repo
|
||||
python pr_breakdown.py
|
||||
|
||||
# Specify a repo explicitly
|
||||
python pr_breakdown.py --repo github/copilot-sdk
|
||||
```
|
||||
|
||||
## Full example: pr_breakdown.py
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
from copilot import CopilotClient
|
||||
|
||||
# ============================================================================
|
||||
# Git & GitHub Detection
|
||||
# ============================================================================
|
||||
|
||||
def is_git_repo():
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "rev-parse", "--git-dir"],
|
||||
check=True,
|
||||
capture_output=True
|
||||
)
|
||||
return True
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return False
|
||||
|
||||
def get_github_remote():
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
remote_url = result.stdout.strip()
|
||||
|
||||
# Handle SSH: git@github.com:owner/repo.git
|
||||
import re
|
||||
ssh_match = re.search(r"git@github\.com:(.+/.+?)(?:\.git)?$", remote_url)
|
||||
if ssh_match:
|
||||
return ssh_match.group(1)
|
||||
|
||||
# Handle HTTPS: https://github.com/owner/repo.git
|
||||
https_match = re.search(r"https://github\.com/(.+/.+?)(?:\.git)?$", remote_url)
|
||||
if https_match:
|
||||
return https_match.group(1)
|
||||
|
||||
return None
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
def parse_args():
|
||||
args = sys.argv[1:]
|
||||
if "--repo" in args:
|
||||
idx = args.index("--repo")
|
||||
if idx + 1 < len(args):
|
||||
return {"repo": args[idx + 1]}
|
||||
return {}
|
||||
|
||||
def prompt_for_repo():
|
||||
return input("Enter GitHub repo (owner/repo): ").strip()
|
||||
|
||||
# ============================================================================
|
||||
# Main Application
|
||||
# ============================================================================
|
||||
|
||||
def main():
|
||||
print("🔍 PR Age Chart Generator\n")
|
||||
|
||||
# Determine the repository
|
||||
args = parse_args()
|
||||
repo = None
|
||||
|
||||
if "repo" in args:
|
||||
repo = args["repo"]
|
||||
print(f"📦 Using specified repo: {repo}")
|
||||
elif is_git_repo():
|
||||
detected = get_github_remote()
|
||||
if detected:
|
||||
repo = detected
|
||||
print(f"📦 Detected GitHub repo: {repo}")
|
||||
else:
|
||||
print("⚠️ Git repo found but no GitHub remote detected.")
|
||||
repo = prompt_for_repo()
|
||||
else:
|
||||
print("📁 Not in a git repository.")
|
||||
repo = prompt_for_repo()
|
||||
|
||||
if not repo or "/" not in repo:
|
||||
print("❌ Invalid repo format. Expected: owner/repo")
|
||||
sys.exit(1)
|
||||
|
||||
owner, repo_name = repo.split("/", 1)
|
||||
|
||||
# Create Copilot client - no custom tools needed!
|
||||
client = CopilotClient(log_level="error")
|
||||
client.start()
|
||||
|
||||
session = client.create_session(
|
||||
model="gpt-5",
|
||||
system_message={
|
||||
"content": f"""
|
||||
<context>
|
||||
You are analyzing pull requests for the GitHub repository: {owner}/{repo_name}
|
||||
The current working directory is: {os.getcwd()}
|
||||
</context>
|
||||
|
||||
<instructions>
|
||||
- Use the GitHub MCP Server tools to fetch PR data
|
||||
- Use your file and code execution tools to generate charts
|
||||
- Save any generated images to the current working directory
|
||||
- Be concise in your responses
|
||||
</instructions>
|
||||
"""
|
||||
}
|
||||
)
|
||||
|
||||
# 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()
|
||||
|
||||
client.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Repository detection**: Checks `--repo` flag → git remote → prompts user
|
||||
2. **No custom tools**: Relies entirely on Copilot CLI's built-in capabilities:
|
||||
- **GitHub MCP Server** - Fetches PR data from GitHub
|
||||
- **File tools** - Saves generated chart images
|
||||
- **Code execution** - Generates charts using Python/matplotlib or other methods
|
||||
3. **Interactive session**: After initial analysis, user can ask for adjustments
|
||||
|
||||
## Why this approach?
|
||||
|
||||
| Aspect | Custom Tools | Built-in Copilot |
|
||||
| --------------- | ----------------- | --------------------------------- |
|
||||
| Code complexity | High | **Minimal** |
|
||||
| Maintenance | You maintain | **Copilot maintains** |
|
||||
| Flexibility | Fixed logic | **AI decides best approach** |
|
||||
| Chart types | What you coded | **Any type Copilot can generate** |
|
||||
| Data grouping | Hardcoded buckets | **Intelligent grouping** |
|
||||
92
cookbook/copilot-sdk/python/recipe/README.md
Normal file
92
cookbook/copilot-sdk/python/recipe/README.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Runnable Recipe Examples
|
||||
|
||||
This folder contains standalone, executable Python examples for each cookbook recipe. Each file can be run directly as a Python script.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8 or later
|
||||
- Install dependencies (this installs the local SDK in editable mode):
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Running Examples
|
||||
|
||||
Each `.py` file is a complete, runnable program with executable permissions:
|
||||
|
||||
```bash
|
||||
python <filename>.py
|
||||
# or on Unix-like systems:
|
||||
./<filename>.py
|
||||
```
|
||||
|
||||
### Available Recipes
|
||||
|
||||
| Recipe | Command | Description |
|
||||
| -------------------- | -------------------------------- | ------------------------------------------ |
|
||||
| Error Handling | `python error_handling.py` | Demonstrates error handling patterns |
|
||||
| Multiple Sessions | `python multiple_sessions.py` | Manages multiple independent conversations |
|
||||
| Managing Local Files | `python managing_local_files.py` | Organizes files using AI grouping |
|
||||
| PR Visualization | `python pr_visualization.py` | Generates PR age charts |
|
||||
| Persisting Sessions | `python persisting_sessions.py` | Save and resume sessions across restarts |
|
||||
|
||||
### Examples with Arguments
|
||||
|
||||
**PR Visualization with specific repo:**
|
||||
|
||||
```bash
|
||||
python pr_visualization.py --repo github/copilot-sdk
|
||||
```
|
||||
|
||||
**Managing Local Files (edit the file to change target folder):**
|
||||
|
||||
```bash
|
||||
# Edit the target_folder variable in managing_local_files.py first
|
||||
python managing_local_files.py
|
||||
```
|
||||
|
||||
## Local SDK Development
|
||||
|
||||
The `requirements.txt` installs the local Copilot SDK using `-e ../..` (editable install). This means:
|
||||
|
||||
- Changes to the SDK source are immediately available
|
||||
- No need to publish or install from PyPI
|
||||
- Perfect for testing and development
|
||||
|
||||
If you modify the SDK source, Python will automatically use the updated code (no rebuild needed).
|
||||
|
||||
## Python Best Practices
|
||||
|
||||
These examples follow Python conventions:
|
||||
|
||||
- PEP 8 naming (snake_case for functions and variables)
|
||||
- Shebang line for direct execution
|
||||
- Proper exception handling
|
||||
- Type hints where appropriate
|
||||
- Standard library usage
|
||||
|
||||
## Virtual Environment (Recommended)
|
||||
|
||||
For isolated development:
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv venv
|
||||
|
||||
# Activate it
|
||||
# Windows:
|
||||
venv\Scripts\activate
|
||||
# Unix/macOS:
|
||||
source venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Learning Resources
|
||||
|
||||
- [Python Documentation](https://docs.python.org/3/)
|
||||
- [PEP 8 Style Guide](https://pep8.org/)
|
||||
- [GitHub Copilot SDK for Python](https://github.com/github/copilot-sdk/blob/main/python/README.md)
|
||||
- [Parent Cookbook](../README.md)
|
||||
28
cookbook/copilot-sdk/python/recipe/error_handling.py
Normal file
28
cookbook/copilot-sdk/python/recipe/error_handling.py
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
|
||||
try:
|
||||
client.start()
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
response = None
|
||||
def handle_message(event):
|
||||
nonlocal response
|
||||
if event["type"] == "assistant.message":
|
||||
response = event["data"]["content"]
|
||||
|
||||
session.on(handle_message)
|
||||
session.send(prompt="Hello!")
|
||||
session.wait_for_idle()
|
||||
|
||||
if response:
|
||||
print(response)
|
||||
|
||||
session.destroy()
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
finally:
|
||||
client.stop()
|
||||
42
cookbook/copilot-sdk/python/recipe/managing_local_files.py
Normal file
42
cookbook/copilot-sdk/python/recipe/managing_local_files.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from copilot import CopilotClient
|
||||
import os
|
||||
|
||||
# Create and start client
|
||||
client = CopilotClient()
|
||||
client.start()
|
||||
|
||||
# Create session
|
||||
session = client.create_session(model="gpt-5")
|
||||
|
||||
# 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']}")
|
||||
|
||||
session.on(handle_event)
|
||||
|
||||
# Ask Copilot to organize files
|
||||
# Change this to your target folder
|
||||
target_folder = os.path.expanduser("~/Downloads")
|
||||
|
||||
session.send(prompt=f"""
|
||||
Analyze the files in "{target_folder}" and organize them into subfolders.
|
||||
|
||||
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
|
||||
|
||||
Please confirm before moving any files.
|
||||
""")
|
||||
|
||||
session.wait_for_idle()
|
||||
|
||||
session.destroy()
|
||||
client.stop()
|
||||
35
cookbook/copilot-sdk/python/recipe/multiple_sessions.py
Normal file
35
cookbook/copilot-sdk/python/recipe/multiple_sessions.py
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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?")
|
||||
|
||||
print("Sent follow-up questions to each session")
|
||||
|
||||
# Clean up all sessions
|
||||
session1.destroy()
|
||||
session2.destroy()
|
||||
session3.destroy()
|
||||
client.stop()
|
||||
|
||||
print("All sessions destroyed successfully")
|
||||
36
cookbook/copilot-sdk/python/recipe/persisting_sessions.py
Normal file
36
cookbook/copilot-sdk/python/recipe/persisting_sessions.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from copilot import CopilotClient
|
||||
|
||||
client = CopilotClient()
|
||||
client.start()
|
||||
|
||||
# Create session with a memorable ID
|
||||
session = 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}")
|
||||
|
||||
# Destroy session but keep data on disk
|
||||
session.destroy()
|
||||
print("Session destroyed (state persisted)")
|
||||
|
||||
# Resume the previous session
|
||||
resumed = client.resume_session("user-123-conversation")
|
||||
print(f"Resumed: {resumed.session_id}")
|
||||
|
||||
resumed.send(prompt="What were we discussing?")
|
||||
|
||||
# List sessions
|
||||
sessions = client.list_sessions()
|
||||
print("Sessions:", [s["sessionId"] for s in sessions])
|
||||
|
||||
# Delete session permanently
|
||||
client.delete_session("user-123-conversation")
|
||||
print("Session deleted")
|
||||
|
||||
resumed.destroy()
|
||||
client.stop()
|
||||
161
cookbook/copilot-sdk/python/recipe/pr_visualization.py
Normal file
161
cookbook/copilot-sdk/python/recipe/pr_visualization.py
Normal file
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from copilot import CopilotClient
|
||||
|
||||
# ============================================================================
|
||||
# Git & GitHub Detection
|
||||
# ============================================================================
|
||||
|
||||
def is_git_repo():
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "rev-parse", "--git-dir"],
|
||||
check=True,
|
||||
capture_output=True
|
||||
)
|
||||
return True
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return False
|
||||
|
||||
def get_github_remote():
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
remote_url = result.stdout.strip()
|
||||
|
||||
# Handle SSH: git@github.com:owner/repo.git
|
||||
ssh_match = re.search(r"git@github\.com:(.+/.+?)(?:\.git)?$", remote_url)
|
||||
if ssh_match:
|
||||
return ssh_match.group(1)
|
||||
|
||||
# Handle HTTPS: https://github.com/owner/repo.git
|
||||
https_match = re.search(r"https://github\.com/(.+/.+?)(?:\.git)?$", remote_url)
|
||||
if https_match:
|
||||
return https_match.group(1)
|
||||
|
||||
return None
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
def parse_args():
|
||||
args = sys.argv[1:]
|
||||
if "--repo" in args:
|
||||
idx = args.index("--repo")
|
||||
if idx + 1 < len(args):
|
||||
return {"repo": args[idx + 1]}
|
||||
return {}
|
||||
|
||||
def prompt_for_repo():
|
||||
return input("Enter GitHub repo (owner/repo): ").strip()
|
||||
|
||||
# ============================================================================
|
||||
# Main Application
|
||||
# ============================================================================
|
||||
|
||||
def main():
|
||||
print("🔍 PR Age Chart Generator\n")
|
||||
|
||||
# Determine the repository
|
||||
args = parse_args()
|
||||
repo = None
|
||||
|
||||
if "repo" in args:
|
||||
repo = args["repo"]
|
||||
print(f"📦 Using specified repo: {repo}")
|
||||
elif is_git_repo():
|
||||
detected = get_github_remote()
|
||||
if detected:
|
||||
repo = detected
|
||||
print(f"📦 Detected GitHub repo: {repo}")
|
||||
else:
|
||||
print("⚠️ Git repo found but no GitHub remote detected.")
|
||||
repo = prompt_for_repo()
|
||||
else:
|
||||
print("📁 Not in a git repository.")
|
||||
repo = prompt_for_repo()
|
||||
|
||||
if not repo or "/" not in repo:
|
||||
print("❌ Invalid repo format. Expected: owner/repo")
|
||||
sys.exit(1)
|
||||
|
||||
owner, repo_name = repo.split("/", 1)
|
||||
|
||||
# Create Copilot client - no custom tools needed!
|
||||
client = CopilotClient(log_level="error")
|
||||
client.start()
|
||||
|
||||
session = client.create_session(
|
||||
model="gpt-5",
|
||||
system_message={
|
||||
"content": f"""
|
||||
<context>
|
||||
You are analyzing pull requests for the GitHub repository: {owner}/{repo_name}
|
||||
The current working directory is: {os.getcwd()}
|
||||
</context>
|
||||
|
||||
<instructions>
|
||||
- Use the GitHub MCP Server tools to fetch PR data
|
||||
- Use your file and code execution tools to generate charts
|
||||
- Save any generated images to the current working directory
|
||||
- Be concise in your responses
|
||||
</instructions>
|
||||
"""
|
||||
}
|
||||
)
|
||||
|
||||
# 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()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
2
cookbook/copilot-sdk/python/recipe/requirements.txt
Normal file
2
cookbook/copilot-sdk/python/recipe/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# Install the local Copilot SDK package in editable mode
|
||||
github-copilot-sdk
|
||||
Reference in New Issue
Block a user