From 836a4fb99ace12f7cb04671021e83a3c6b3897b5 Mon Sep 17 00:00:00 2001 From: John Aziz Date: Mon, 26 Jan 2026 23:41:20 +0200 Subject: [PATCH 1/4] chore: update pr visualization example to latest version of sdk --- cookbook/python/recipe/pr_visualization.py | 86 ++++++++++++---------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/cookbook/python/recipe/pr_visualization.py b/cookbook/python/recipe/pr_visualization.py index 72226c3d..6c0862e8 100644 --- a/cookbook/python/recipe/pr_visualization.py +++ b/cookbook/python/recipe/pr_visualization.py @@ -1,33 +1,31 @@ #!/usr/bin/env python3 -import subprocess -import sys import os import re -from copilot import CopilotClient +import subprocess +import sys + +from copilot import CopilotClient, SessionConfig +from copilot.generated.session_events import SessionEvent, SessionEventType +from copilot.types import CopilotClientOptions, MessageOptions, SystemMessageAppendConfig # ============================================================================ # Git & GitHub Detection # ============================================================================ + def is_git_repo(): try: - subprocess.run( - ["git", "rev-parse", "--git-dir"], - check=True, - capture_output=True - ) + 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 + ["git", "remote", "get-url", "origin"], check=True, capture_output=True, text=True ) remote_url = result.stdout.strip() @@ -45,6 +43,7 @@ def get_github_remote(): except (subprocess.CalledProcessError, FileNotFoundError): return None + def parse_args(): args = sys.argv[1:] if "--repo" in args: @@ -53,14 +52,17 @@ def parse_args(): return {"repo": args[idx + 1]} return {} + def prompt_for_repo(): return input("Enter GitHub repo (owner/repo): ").strip() + # ============================================================================ # Main Application # ============================================================================ -def main(): + +async def main(): print("šŸ” PR Age Chart Generator\n") # Determine the repository @@ -88,14 +90,17 @@ def main(): owner, repo_name = repo.split("/", 1) + options = CopilotClientOptions( + log_level="info", + ) # Create Copilot client - no custom tools needed! - client = CopilotClient(log_level="error") - client.start() + client = CopilotClient(options=options) + await client.start() - session = client.create_session( + session_config = SessionConfig( model="gpt-5", - system_message={ - "content": f""" + system_message=SystemMessageAppendConfig( + content=f""" You are analyzing pull requests for the GitHub repository: {owner}/{repo_name} The current working directory is: {os.getcwd()} @@ -108,39 +113,43 @@ def main(): - Be concise in your responses """ - } + ), ) + session = await client.create_session(config=session_config) + # 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']}") + def handle_event(event: SessionEvent): + if event.type == SessionEventType.ASSISTANT_MESSAGE: + print(f"\nšŸ¤– {event.data.content}\n") + elif event.type == SessionEventType.TOOL_EXECUTION_START: + print(f" āš™ļø {event.data.tool_name}") session.on(handle_event) # Initial prompt - let Copilot figure out the details print("\nšŸ“Š Starting analysis...\n") - session.send(prompt=f""" + message_options = MessageOptions( + 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() + await session.send_and_wait(options=message_options, timeout=300.0) # Interactive loop - print("\nšŸ’” Ask follow-up questions or type \"exit\" to quit.\n") + 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(' - "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: @@ -151,11 +160,14 @@ def handle_event(event): break if user_input: - session.send(prompt=user_input) - session.wait_for_idle() + message_options = MessageOptions(prompt=user_input) + await session.send_and_wait(options=message_options, timeout=300.0) + + await session.destroy() + await client.stop() - session.destroy() - client.stop() if __name__ == "__main__": - main() + import asyncio + + asyncio.run(main()) From 844493d4d6482355f92b1852f093e258ba5a3dad Mon Sep 17 00:00:00 2001 From: John Aziz Date: Mon, 26 Jan 2026 23:41:30 +0200 Subject: [PATCH 2/4] docs: update pr visualization documentation to match python example --- cookbook/python/pr-visualization.md | 89 ++++++++++++++++------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/cookbook/python/pr-visualization.md b/cookbook/python/pr-visualization.md index af2ce20c..2fde96ff 100644 --- a/cookbook/python/pr-visualization.md +++ b/cookbook/python/pr-visualization.md @@ -27,21 +27,25 @@ pip install copilot-sdk ```bash # Auto-detect from current git repo -python pr_breakdown.py +python pr_visualization.py # Specify a repo explicitly -python pr_breakdown.py --repo github/copilot-sdk +python pr_visualization.py --repo github/copilot-sdk ``` -## Full example: pr_breakdown.py +## Full example: pr_visualization.py ```python #!/usr/bin/env python3 +import os +import re import subprocess import sys -import os -from copilot import CopilotClient + +from copilot import CopilotClient, SessionConfig +from copilot.generated.session_events import SessionEvent, SessionEventType +from copilot.types import CopilotClientOptions, MessageOptions, SystemMessageAppendConfig # ============================================================================ # Git & GitHub Detection @@ -49,27 +53,20 @@ from copilot import CopilotClient def is_git_repo(): try: - subprocess.run( - ["git", "rev-parse", "--git-dir"], - check=True, - capture_output=True - ) + 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 + ["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) @@ -91,14 +88,17 @@ def parse_args(): return {"repo": args[idx + 1]} return {} + def prompt_for_repo(): return input("Enter GitHub repo (owner/repo): ").strip() + # ============================================================================ # Main Application # ============================================================================ -def main(): + +async def main(): print("šŸ” PR Age Chart Generator\n") # Determine the repository @@ -126,14 +126,17 @@ def main(): owner, repo_name = repo.split("/", 1) + options = CopilotClientOptions( + log_level="info", + ) # Create Copilot client - no custom tools needed! - client = CopilotClient(log_level="error") - client.start() + client = CopilotClient(options=options) + await client.start() - session = client.create_session( + session_config = SessionConfig( model="gpt-5", - system_message={ - "content": f""" + system_message=SystemMessageAppendConfig( + content=f""" You are analyzing pull requests for the GitHub repository: {owner}/{repo_name} The current working directory is: {os.getcwd()} @@ -146,39 +149,43 @@ The current working directory is: {os.getcwd()} - Be concise in your responses """ - } + ), ) + session = await client.create_session(config=session_config) + # 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']}") + def handle_event(event: SessionEvent): + if event.type == SessionEventType.ASSISTANT_MESSAGE: + print(f"\nšŸ¤– {event.data.content}\n") + elif event.type == SessionEventType.TOOL_EXECUTION_START: + print(f" āš™ļø {event.data.tool_name}") session.on(handle_event) # Initial prompt - let Copilot figure out the details print("\nšŸ“Š Starting analysis...\n") - session.send(prompt=f""" + message_options = MessageOptions( + 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() + await session.send_and_wait(options=message_options, timeout=300.0) # Interactive loop - print("\nšŸ’” Ask follow-up questions or type \"exit\" to quit.\n") + 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(' - "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: @@ -189,13 +196,17 @@ The current working directory is: {os.getcwd()} break if user_input: - session.send(prompt=user_input) - session.wait_for_idle() + message_options = MessageOptions(prompt=user_input) + await session.send_and_wait(options=message_options, timeout=300.0) + + await session.destroy() + await client.stop() - client.stop() if __name__ == "__main__": - main() + import asyncio + + asyncio.run(main()) ``` ## How it works From 727b022ec8316e5a9427078b86acbb2c5d2625fc Mon Sep 17 00:00:00 2001 From: John Aziz Date: Mon, 26 Jan 2026 23:59:49 +0200 Subject: [PATCH 3/4] fix: apply pr review comments --- cookbook/python/pr-visualization.md | 30 +++++++++++----------- cookbook/python/recipe/pr_visualization.py | 28 ++++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/cookbook/python/pr-visualization.md b/cookbook/python/pr-visualization.md index 2fde96ff..84fa2788 100644 --- a/cookbook/python/pr-visualization.md +++ b/cookbook/python/pr-visualization.md @@ -45,7 +45,7 @@ import sys from copilot import CopilotClient, SessionConfig from copilot.generated.session_events import SessionEvent, SessionEventType -from copilot.types import CopilotClientOptions, MessageOptions, SystemMessageAppendConfig +from copilot.types import CopilotClientOptions, MessageOptions # ============================================================================ # Git & GitHub Detection @@ -126,17 +126,17 @@ async def main(): owner, repo_name = repo.split("/", 1) - options = CopilotClientOptions( - log_level="info", - ) + options: CopilotClientOptions = { + "log_level": "info", + } # Create Copilot client - no custom tools needed! client = CopilotClient(options=options) await client.start() - session_config = SessionConfig( - model="gpt-5", - system_message=SystemMessageAppendConfig( - content=f""" + session_config: SessionConfig = { + "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()} @@ -149,8 +149,8 @@ The current working directory is: {os.getcwd()} - Be concise in your responses """ - ), - ) + }, + } session = await client.create_session(config=session_config) @@ -166,16 +166,16 @@ The current working directory is: {os.getcwd()} # Initial prompt - let Copilot figure out the details print("\nšŸ“Š Starting analysis...\n") - message_options = MessageOptions( - prompt=f""" + message_options: MessageOptions = { + "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. - """, - ) + """ + } await session.send_and_wait(options=message_options, timeout=300.0) @@ -196,7 +196,7 @@ The current working directory is: {os.getcwd()} break if user_input: - message_options = MessageOptions(prompt=user_input) + message_options: MessageOptions = {"prompt": user_input} await session.send_and_wait(options=message_options, timeout=300.0) await session.destroy() diff --git a/cookbook/python/recipe/pr_visualization.py b/cookbook/python/recipe/pr_visualization.py index 6c0862e8..095ef610 100644 --- a/cookbook/python/recipe/pr_visualization.py +++ b/cookbook/python/recipe/pr_visualization.py @@ -7,7 +7,7 @@ from copilot import CopilotClient, SessionConfig from copilot.generated.session_events import SessionEvent, SessionEventType -from copilot.types import CopilotClientOptions, MessageOptions, SystemMessageAppendConfig +from copilot.types import CopilotClientOptions, MessageOptions # ============================================================================ # Git & GitHub Detection @@ -90,17 +90,17 @@ async def main(): owner, repo_name = repo.split("/", 1) - options = CopilotClientOptions( - log_level="info", - ) + options: CopilotClientOptions = { + "log_level": "info", + } # Create Copilot client - no custom tools needed! client = CopilotClient(options=options) await client.start() - session_config = SessionConfig( - model="gpt-5", - system_message=SystemMessageAppendConfig( - content=f""" + session_config: SessionConfig = { + "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()} @@ -113,8 +113,8 @@ async def main(): - Be concise in your responses """ - ), - ) + }, + } session = await client.create_session(config=session_config) @@ -130,8 +130,8 @@ def handle_event(event: SessionEvent): # Initial prompt - let Copilot figure out the details print("\nšŸ“Š Starting analysis...\n") - message_options = MessageOptions( - prompt=f""" + message_options: MessageOptions = { + "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 @@ -139,7 +139,7 @@ def handle_event(event: SessionEvent): 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. """ - ) + } await session.send_and_wait(options=message_options, timeout=300.0) @@ -160,7 +160,7 @@ def handle_event(event: SessionEvent): break if user_input: - message_options = MessageOptions(prompt=user_input) + message_options: MessageOptions = {"prompt": user_input} await session.send_and_wait(options=message_options, timeout=300.0) await session.destroy() From fc22d036e050a59d6b5164e82d8530c96f72cbf1 Mon Sep 17 00:00:00 2001 From: John Aziz Date: Tue, 27 Jan 2026 00:02:28 +0200 Subject: [PATCH 4/4] revert log level change --- cookbook/python/pr-visualization.md | 2 +- cookbook/python/recipe/pr_visualization.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/python/pr-visualization.md b/cookbook/python/pr-visualization.md index 84fa2788..2114e16b 100644 --- a/cookbook/python/pr-visualization.md +++ b/cookbook/python/pr-visualization.md @@ -127,7 +127,7 @@ async def main(): owner, repo_name = repo.split("/", 1) options: CopilotClientOptions = { - "log_level": "info", + "log_level": "error", } # Create Copilot client - no custom tools needed! client = CopilotClient(options=options) diff --git a/cookbook/python/recipe/pr_visualization.py b/cookbook/python/recipe/pr_visualization.py index 095ef610..2cbb615a 100644 --- a/cookbook/python/recipe/pr_visualization.py +++ b/cookbook/python/recipe/pr_visualization.py @@ -91,7 +91,7 @@ async def main(): owner, repo_name = repo.split("/", 1) options: CopilotClientOptions = { - "log_level": "info", + "log_level": "error", } # Create Copilot client - no custom tools needed! client = CopilotClient(options=options)