mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 02:15:12 +00:00
Adding copilot-sdk stuff
This commit is contained in:
@@ -21,6 +21,7 @@ Discover our curated collections of prompts, instructions, and agents organized
|
||||
| Name | Description | Items | Tags |
|
||||
| ---- | ----------- | ----- | ---- |
|
||||
| [Awesome Copilot](collections/awesome-copilot.md) | Meta prompts that help you discover and generate curated GitHub Copilot chat modes, collections, instructions, prompts, and agents. | 6 items | github-copilot, discovery, meta, prompt-engineering, agents |
|
||||
| [Copilot SDK](collections/copilot-sdk.md) | Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. | 4 items | copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot |
|
||||
| [Partners](collections/partners.md) | Custom agents that have been created by GitHub partners | 20 items | devops, security, database, cloud, infrastructure, observability, feature-flags, cicd, migration, performance |
|
||||
|
||||
|
||||
|
||||
17
collections/copilot-sdk.collection.yml
Normal file
17
collections/copilot-sdk.collection.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
id: copilot-sdk
|
||||
name: Copilot SDK
|
||||
description: Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications.
|
||||
tags: [copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot]
|
||||
items:
|
||||
- path: instructions/copilot-sdk-csharp.instructions.md
|
||||
kind: instruction
|
||||
- path: instructions/copilot-sdk-go.instructions.md
|
||||
kind: instruction
|
||||
- path: instructions/copilot-sdk-nodejs.instructions.md
|
||||
kind: instruction
|
||||
- path: instructions/copilot-sdk-python.instructions.md
|
||||
kind: instruction
|
||||
display:
|
||||
ordering: manual
|
||||
show_badge: true
|
||||
featured: true
|
||||
17
collections/copilot-sdk.md
Normal file
17
collections/copilot-sdk.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copilot SDK
|
||||
|
||||
Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications.
|
||||
|
||||
**Tags:** copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot
|
||||
|
||||
## Items in this Collection
|
||||
|
||||
| Title | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| [GitHub Copilot SDK C# Instructions](../instructions/copilot-sdk-csharp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md) | Instruction | This file provides guidance on building C# applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Go Instructions](../instructions/copilot-sdk-go.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md) | Instruction | This file provides guidance on building Go applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Node.js Instructions](../instructions/copilot-sdk-nodejs.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md) | Instruction | This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Python Instructions](../instructions/copilot-sdk-python.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md) | Instruction | This file provides guidance on building Python applications using GitHub Copilot SDK. |
|
||||
|
||||
---
|
||||
*This collection includes 4 curated items for **Copilot SDK**.*
|
||||
@@ -17,6 +17,7 @@ Curated collections of related prompts, instructions, and agents organized aroun
|
||||
| Name | Description | Items | Tags |
|
||||
| ---- | ----------- | ----- | ---- |
|
||||
| [⭐ Awesome Copilot](../collections/awesome-copilot.md) | Meta prompts that help you discover and generate curated GitHub Copilot chat modes, collections, instructions, prompts, and agents. | 6 items | github-copilot, discovery, meta, prompt-engineering, agents |
|
||||
| [⭐ Copilot SDK](../collections/copilot-sdk.md) | Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. | 4 items | copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot |
|
||||
| [⭐ Partners](../collections/partners.md) | Custom agents that have been created by GitHub partners | 20 items | devops, security, database, cloud, infrastructure, observability, feature-flags, cicd, migration, performance |
|
||||
| [Azure & Cloud Development](../collections/azure-cloud-development.md) | Comprehensive Azure cloud development tools including Infrastructure as Code, serverless functions, architecture patterns, and cost optimization for building scalable cloud applications. | 18 items | azure, cloud, infrastructure, bicep, terraform, serverless, architecture, devops |
|
||||
| [C# .NET Development](../collections/csharp-dotnet-development.md) | Essential prompts, instructions, and chat modes for C# and .NET development including testing, documentation, and best practices. | 8 items | csharp, dotnet, aspnet, testing |
|
||||
|
||||
@@ -82,6 +82,10 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for
|
||||
| [Get Tooling for Power Apps Component Framework](../instructions/pcf-tooling.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpcf-tooling.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpcf-tooling.instructions.md) | Get Microsoft Power Platform CLI tooling for Power Apps Component Framework |
|
||||
| [Gilfoyle Code Review Instructions](../instructions/gilfoyle-code-review.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgilfoyle-code-review.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgilfoyle-code-review.instructions.md) | Gilfoyle-style code review instructions that channel the sardonic technical supremacy of Silicon Valley's most arrogant systems architect. |
|
||||
| [GitHub Actions CI/CD Best Practices](../instructions/github-actions-ci-cd-best-practices.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgithub-actions-ci-cd-best-practices.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgithub-actions-ci-cd-best-practices.instructions.md) | Comprehensive guide for building robust, secure, and efficient CI/CD pipelines using GitHub Actions. Covers workflow structure, jobs, steps, environment variables, secret management, caching, matrix strategies, testing, and deployment strategies. |
|
||||
| [GitHub Copilot SDK C# Instructions](../instructions/copilot-sdk-csharp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md) | This file provides guidance on building C# applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Go Instructions](../instructions/copilot-sdk-go.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md) | This file provides guidance on building Go applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Node.js Instructions](../instructions/copilot-sdk-nodejs.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md) | This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK. |
|
||||
| [GitHub Copilot SDK Python Instructions](../instructions/copilot-sdk-python.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md) | This file provides guidance on building Python applications using GitHub Copilot SDK. |
|
||||
| [Go Development Instructions](../instructions/go.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo.instructions.md) | Instructions for writing Go code following idiomatic Go practices and community standards |
|
||||
| [Go MCP Server Development Guidelines](../instructions/go-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo-mcp-server.instructions.md) | Best practices and patterns for building Model Context Protocol (MCP) servers in Go using the official github.com/modelcontextprotocol/go-sdk package. |
|
||||
| [Guidance for Localization](../instructions/localization.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Flocalization.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Flocalization.instructions.md) | Guidelines for localizing markdown documents |
|
||||
|
||||
550
instructions/copilot-sdk-csharp.instructions.md
Normal file
550
instructions/copilot-sdk-csharp.instructions.md
Normal file
@@ -0,0 +1,550 @@
|
||||
---
|
||||
applyTo: '**.cs, **.csproj'
|
||||
description: 'This file provides guidance on building C# applications using GitHub Copilot SDK.'
|
||||
name: 'GitHub Copilot SDK C# Instructions'
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
- The SDK is in technical preview and may have breaking changes
|
||||
- Requires .NET 10.0 or later
|
||||
- Requires GitHub Copilot CLI installed and in PATH
|
||||
- Uses async/await patterns throughout
|
||||
- Implements IAsyncDisposable for resource cleanup
|
||||
|
||||
## Installation
|
||||
|
||||
Always install via NuGet:
|
||||
```bash
|
||||
dotnet add package GitHub.Copilot.SDK
|
||||
```
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### Basic Client Setup
|
||||
|
||||
```csharp
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
```
|
||||
|
||||
### Client Configuration Options
|
||||
|
||||
When creating a CopilotClient, use `CopilotClientOptions`:
|
||||
|
||||
- `CliPath` - Path to CLI executable (default: "copilot" from PATH)
|
||||
- `CliArgs` - Extra arguments prepended before SDK-managed flags
|
||||
- `CliUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process
|
||||
- `Port` - Server port (default: 0 for random)
|
||||
- `UseStdio` - Use stdio transport instead of TCP (default: true)
|
||||
- `LogLevel` - Log level (default: "info")
|
||||
- `AutoStart` - Auto-start server (default: true)
|
||||
- `AutoRestart` - Auto-restart on crash (default: true)
|
||||
- `Cwd` - Working directory for the CLI process
|
||||
- `Environment` - Environment variables for the CLI process
|
||||
- `Logger` - ILogger instance for SDK logging
|
||||
|
||||
### Manual Server Control
|
||||
|
||||
For explicit control:
|
||||
```csharp
|
||||
var client = new CopilotClient(new CopilotClientOptions { AutoStart = false });
|
||||
await client.StartAsync();
|
||||
// Use client...
|
||||
await client.StopAsync();
|
||||
```
|
||||
|
||||
Use `ForceStopAsync()` when `StopAsync()` takes too long.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Creating Sessions
|
||||
|
||||
Use `SessionConfig` for configuration:
|
||||
|
||||
```csharp
|
||||
await using var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
Streaming = true,
|
||||
Tools = [...],
|
||||
SystemMessage = new SystemMessageConfig { ... },
|
||||
AvailableTools = ["tool1", "tool2"],
|
||||
ExcludedTools = ["tool3"],
|
||||
Provider = new ProviderConfig { ... }
|
||||
});
|
||||
```
|
||||
|
||||
### Session Config Options
|
||||
|
||||
- `SessionId` - Custom session ID
|
||||
- `Model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.)
|
||||
- `Tools` - Custom tools exposed to the CLI
|
||||
- `SystemMessage` - System message customization
|
||||
- `AvailableTools` - Allowlist of tool names
|
||||
- `ExcludedTools` - Blocklist of tool names
|
||||
- `Provider` - Custom API provider configuration (BYOK)
|
||||
- `Streaming` - Enable streaming response chunks (default: false)
|
||||
|
||||
### Resuming Sessions
|
||||
|
||||
```csharp
|
||||
var session = await client.ResumeSessionAsync(sessionId, new ResumeSessionConfig { ... });
|
||||
```
|
||||
|
||||
### Session Operations
|
||||
|
||||
- `session.SessionId` - Get session identifier
|
||||
- `session.SendAsync(new MessageOptions { Prompt = "...", Attachments = [...] })` - Send message
|
||||
- `session.AbortAsync()` - Abort current processing
|
||||
- `session.GetMessagesAsync()` - Get all events/messages
|
||||
- `await session.DisposeAsync()` - Clean up resources
|
||||
|
||||
## Event Handling
|
||||
|
||||
### Event Subscription Pattern
|
||||
|
||||
ALWAYS use TaskCompletionSource for waiting on session events:
|
||||
|
||||
```csharp
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
Console.WriteLine(msg.Data.Content);
|
||||
}
|
||||
else if (evt is SessionIdleEvent)
|
||||
{
|
||||
done.SetResult();
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = "..." });
|
||||
await done.Task;
|
||||
```
|
||||
|
||||
### Unsubscribing from Events
|
||||
|
||||
The `On()` method returns an IDisposable:
|
||||
|
||||
```csharp
|
||||
var subscription = session.On(evt => { /* handler */ });
|
||||
// Later...
|
||||
subscription.Dispose();
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
Use pattern matching or switch expressions for event handling:
|
||||
|
||||
```csharp
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case UserMessageEvent userMsg:
|
||||
// Handle user message
|
||||
break;
|
||||
case AssistantMessageEvent assistantMsg:
|
||||
Console.WriteLine(assistantMsg.Data.Content);
|
||||
break;
|
||||
case ToolExecutionStartEvent toolStart:
|
||||
// Tool execution started
|
||||
break;
|
||||
case ToolExecutionCompleteEvent toolComplete:
|
||||
// Tool execution completed
|
||||
break;
|
||||
case SessionStartEvent start:
|
||||
// Session started
|
||||
break;
|
||||
case SessionIdleEvent idle:
|
||||
// Session is idle (processing complete)
|
||||
break;
|
||||
case SessionErrorEvent error:
|
||||
Console.WriteLine($"Error: {error.Data.Message}");
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
### Enabling Streaming
|
||||
|
||||
Set `Streaming = true` in SessionConfig:
|
||||
|
||||
```csharp
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
Streaming = true
|
||||
});
|
||||
```
|
||||
|
||||
### Handling Streaming Events
|
||||
|
||||
Handle both delta events (incremental) and final events:
|
||||
|
||||
```csharp
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AssistantMessageDeltaEvent delta:
|
||||
// Incremental text chunk
|
||||
Console.Write(delta.Data.DeltaContent);
|
||||
break;
|
||||
case AssistantReasoningDeltaEvent reasoningDelta:
|
||||
// Incremental reasoning chunk (model-dependent)
|
||||
Console.Write(reasoningDelta.Data.DeltaContent);
|
||||
break;
|
||||
case AssistantMessageEvent msg:
|
||||
// Final complete message
|
||||
Console.WriteLine("\n--- Final ---");
|
||||
Console.WriteLine(msg.Data.Content);
|
||||
break;
|
||||
case AssistantReasoningEvent reasoning:
|
||||
// Final reasoning content
|
||||
Console.WriteLine("--- Reasoning ---");
|
||||
Console.WriteLine(reasoning.Data.Content);
|
||||
break;
|
||||
case SessionIdleEvent:
|
||||
done.SetResult();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = "Tell me a story" });
|
||||
await done.Task;
|
||||
```
|
||||
|
||||
Note: Final events (`AssistantMessageEvent`, `AssistantReasoningEvent`) are ALWAYS sent regardless of streaming setting.
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Defining Tools with AIFunctionFactory
|
||||
|
||||
Use `Microsoft.Extensions.AI.AIFunctionFactory.Create` for type-safe tools:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Extensions.AI;
|
||||
using System.ComponentModel;
|
||||
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
Tools = [
|
||||
AIFunctionFactory.Create(
|
||||
async ([Description("Issue ID")] string id) => {
|
||||
var issue = await FetchIssueAsync(id);
|
||||
return issue;
|
||||
},
|
||||
"lookup_issue",
|
||||
"Fetch issue details from tracker"),
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Tool Return Types
|
||||
|
||||
- Return any JSON-serializable value (automatically wrapped)
|
||||
- Or return `ToolResultAIContent` wrapping `ToolResultObject` for full control over metadata
|
||||
|
||||
### Tool Execution Flow
|
||||
|
||||
When Copilot invokes a tool, the client automatically:
|
||||
1. Runs your handler function
|
||||
2. Serializes the return value
|
||||
3. Responds to the CLI
|
||||
|
||||
## System Message Customization
|
||||
|
||||
### Append Mode (Default - Preserves Guardrails)
|
||||
|
||||
```csharp
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
SystemMessage = new SystemMessageConfig
|
||||
{
|
||||
Mode = SystemMessageMode.Append,
|
||||
Content = @"
|
||||
<workflow_rules>
|
||||
- Always check for security vulnerabilities
|
||||
- Suggest performance improvements when applicable
|
||||
</workflow_rules>
|
||||
"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Replace Mode (Full Control - Removes Guardrails)
|
||||
|
||||
```csharp
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5",
|
||||
SystemMessage = new SystemMessageConfig
|
||||
{
|
||||
Mode = SystemMessageMode.Replace,
|
||||
Content = "You are a helpful assistant."
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## File Attachments
|
||||
|
||||
Attach files to messages using `UserMessageDataAttachmentsItem`:
|
||||
|
||||
```csharp
|
||||
await session.SendAsync(new MessageOptions
|
||||
{
|
||||
Prompt = "Analyze this file",
|
||||
Attachments = new List<UserMessageDataAttachmentsItem>
|
||||
{
|
||||
new UserMessageDataAttachmentsItem
|
||||
{
|
||||
Type = UserMessageDataAttachmentsItemType.File,
|
||||
Path = "/path/to/file.cs",
|
||||
DisplayName = "My File"
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Message Delivery Modes
|
||||
|
||||
Use the `Mode` property in `MessageOptions`:
|
||||
|
||||
- `"enqueue"` - Queue message for processing
|
||||
- `"immediate"` - Process message immediately
|
||||
|
||||
```csharp
|
||||
await session.SendAsync(new MessageOptions
|
||||
{
|
||||
Prompt = "...",
|
||||
Mode = "enqueue"
|
||||
});
|
||||
```
|
||||
|
||||
## Multiple Sessions
|
||||
|
||||
Sessions are independent and can run concurrently:
|
||||
|
||||
```csharp
|
||||
var session1 = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
||||
var session2 = await client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" });
|
||||
|
||||
await session1.SendAsync(new MessageOptions { Prompt = "Hello from session 1" });
|
||||
await session2.SendAsync(new MessageOptions { Prompt = "Hello from session 2" });
|
||||
```
|
||||
|
||||
## Bring Your Own Key (BYOK)
|
||||
|
||||
Use custom API providers via `ProviderConfig`:
|
||||
|
||||
```csharp
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Provider = new ProviderConfig
|
||||
{
|
||||
Type = "openai",
|
||||
BaseUrl = "https://api.openai.com/v1",
|
||||
ApiKey = "your-api-key"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Session Lifecycle Management
|
||||
|
||||
### Listing Sessions
|
||||
|
||||
```csharp
|
||||
var sessions = await client.ListSessionsAsync();
|
||||
foreach (var metadata in sessions)
|
||||
{
|
||||
Console.WriteLine($"Session: {metadata.SessionId}");
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting Sessions
|
||||
|
||||
```csharp
|
||||
await client.DeleteSessionAsync(sessionId);
|
||||
```
|
||||
|
||||
### Checking Connection State
|
||||
|
||||
```csharp
|
||||
var state = client.State;
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Exception Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var session = await client.CreateSessionAsync();
|
||||
await session.SendAsync(new MessageOptions { Prompt = "Hello" });
|
||||
}
|
||||
catch (StreamJsonRpc.RemoteInvocationException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"JSON-RPC Error: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
### Session Error Events
|
||||
|
||||
Monitor `SessionErrorEvent` for runtime errors:
|
||||
|
||||
```csharp
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is SessionErrorEvent error)
|
||||
{
|
||||
Console.Error.WriteLine($"Session Error: {error.Data.Message}");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Connectivity Testing
|
||||
|
||||
Use PingAsync to verify server connectivity:
|
||||
|
||||
```csharp
|
||||
var response = await client.PingAsync("test message");
|
||||
```
|
||||
|
||||
## Resource Cleanup
|
||||
|
||||
### Automatic Cleanup with Using
|
||||
|
||||
ALWAYS use `await using` for automatic disposal:
|
||||
|
||||
```csharp
|
||||
await using var client = new CopilotClient();
|
||||
await using var session = await client.CreateSessionAsync();
|
||||
// Resources automatically cleaned up
|
||||
```
|
||||
|
||||
### Manual Cleanup
|
||||
|
||||
If not using `await using`:
|
||||
|
||||
```csharp
|
||||
var client = new CopilotClient();
|
||||
try
|
||||
{
|
||||
await client.StartAsync();
|
||||
// Use client...
|
||||
}
|
||||
finally
|
||||
{
|
||||
await client.StopAsync();
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use `await using`** for CopilotClient and CopilotSession
|
||||
2. **Use TaskCompletionSource** to wait for SessionIdleEvent
|
||||
3. **Handle SessionErrorEvent** for robust error handling
|
||||
4. **Use pattern matching** (switch expressions) for event handling
|
||||
5. **Enable streaming** for better UX in interactive scenarios
|
||||
6. **Use AIFunctionFactory** for type-safe tool definitions
|
||||
7. **Dispose event subscriptions** when no longer needed
|
||||
8. **Use SystemMessageMode.Append** to preserve safety guardrails
|
||||
9. **Provide descriptive tool names and descriptions** for better model understanding
|
||||
10. **Handle both delta and final events** when streaming is enabled
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Simple Query-Response
|
||||
|
||||
```csharp
|
||||
await using var client = new CopilotClient();
|
||||
await client.StartAsync();
|
||||
|
||||
await using var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5"
|
||||
});
|
||||
|
||||
var done = new TaskCompletionSource();
|
||||
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
Console.WriteLine(msg.Data.Content);
|
||||
}
|
||||
else if (evt is SessionIdleEvent)
|
||||
{
|
||||
done.SetResult();
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = "What is 2+2?" });
|
||||
await done.Task;
|
||||
```
|
||||
|
||||
### Multi-Turn Conversation
|
||||
|
||||
```csharp
|
||||
await using var session = await client.CreateSessionAsync();
|
||||
|
||||
async Task SendAndWait(string prompt)
|
||||
{
|
||||
var done = new TaskCompletionSource();
|
||||
var subscription = session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
Console.WriteLine(msg.Data.Content);
|
||||
}
|
||||
else if (evt is SessionIdleEvent)
|
||||
{
|
||||
done.SetResult();
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = prompt });
|
||||
await done.Task;
|
||||
subscription.Dispose();
|
||||
}
|
||||
|
||||
await SendAndWait("What is the capital of France?");
|
||||
await SendAndWait("What is its population?");
|
||||
```
|
||||
|
||||
### Tool with Complex Return Type
|
||||
|
||||
```csharp
|
||||
var session = await client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Tools = [
|
||||
AIFunctionFactory.Create(
|
||||
([Description("User ID")] string userId) => {
|
||||
return new {
|
||||
Id = userId,
|
||||
Name = "John Doe",
|
||||
Email = "john@example.com",
|
||||
Role = "Developer"
|
||||
};
|
||||
},
|
||||
"get_user",
|
||||
"Retrieve user information")
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
626
instructions/copilot-sdk-go.instructions.md
Normal file
626
instructions/copilot-sdk-go.instructions.md
Normal file
@@ -0,0 +1,626 @@
|
||||
---
|
||||
applyTo: "**.go, go.mod"
|
||||
description: "This file provides guidance on building Go applications using GitHub Copilot SDK."
|
||||
name: "GitHub Copilot SDK Go Instructions"
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
- The SDK is in technical preview and may have breaking changes
|
||||
- Requires Go 1.21 or later
|
||||
- Requires GitHub Copilot CLI installed and in PATH
|
||||
- Uses goroutines and channels for concurrent operations
|
||||
- No external dependencies beyond the standard library
|
||||
|
||||
## Installation
|
||||
|
||||
Always install via Go modules:
|
||||
|
||||
```bash
|
||||
go get github.com/github/copilot-sdk/go
|
||||
```
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### Basic Client Setup
|
||||
|
||||
```go
|
||||
import "github.com/github/copilot-sdk/go"
|
||||
|
||||
client := copilot.NewClient(nil)
|
||||
if err := client.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Stop()
|
||||
```
|
||||
|
||||
### Client Configuration Options
|
||||
|
||||
When creating a CopilotClient, use `ClientOptions`:
|
||||
|
||||
- `CLIPath` - Path to CLI executable (default: "copilot" from PATH)
|
||||
- `CLIUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process
|
||||
- `Port` - Server port (default: 0 for random)
|
||||
- `UseStdio` - Use stdio transport instead of TCP (default: true)
|
||||
- `LogLevel` - Log level (default: "info")
|
||||
- `AutoStart` - Auto-start server (default: true, use pointer: `boolPtr(true)`)
|
||||
- `AutoRestart` - Auto-restart on crash (default: true, use pointer: `boolPtr(true)`)
|
||||
- `Cwd` - Working directory for the CLI process
|
||||
- `Env` - Environment variables for the CLI process ([]string)
|
||||
|
||||
### Manual Server Control
|
||||
|
||||
For explicit control:
|
||||
|
||||
```go
|
||||
autoStart := false
|
||||
client := copilot.NewClient(&copilot.ClientOptions{AutoStart: &autoStart})
|
||||
if err := client.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Use client...
|
||||
client.Stop()
|
||||
```
|
||||
|
||||
Use `ForceStop()` when `Stop()` takes too long.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Creating Sessions
|
||||
|
||||
Use `SessionConfig` for configuration:
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Model: "gpt-5",
|
||||
Streaming: true,
|
||||
Tools: []copilot.Tool{...},
|
||||
SystemMessage: &copilot.SystemMessageConfig{ ... },
|
||||
AvailableTools: []string{"tool1", "tool2"},
|
||||
ExcludedTools: []string{"tool3"},
|
||||
Provider: &copilot.ProviderConfig{ ... },
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Session Config Options
|
||||
|
||||
- `SessionID` - Custom session ID
|
||||
- `Model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.)
|
||||
- `Tools` - Custom tools exposed to the CLI ([]Tool)
|
||||
- `SystemMessage` - System message customization (\*SystemMessageConfig)
|
||||
- `AvailableTools` - Allowlist of tool names ([]string)
|
||||
- `ExcludedTools` - Blocklist of tool names ([]string)
|
||||
- `Provider` - Custom API provider configuration (BYOK) (\*ProviderConfig)
|
||||
- `Streaming` - Enable streaming response chunks (bool)
|
||||
- `MCPServers` - MCP server configurations
|
||||
- `CustomAgents` - Custom agent configurations
|
||||
- `ConfigDir` - Config directory override
|
||||
- `SkillDirectories` - Skill directories ([]string)
|
||||
- `DisabledSkills` - Disabled skills ([]string)
|
||||
|
||||
### Resuming Sessions
|
||||
|
||||
```go
|
||||
session, err := client.ResumeSession("session-id")
|
||||
// Or with options:
|
||||
session, err := client.ResumeSessionWithOptions("session-id", &copilot.ResumeSessionConfig{ ... })
|
||||
```
|
||||
|
||||
### Session Operations
|
||||
|
||||
- `session.SessionID` - Get session identifier (string)
|
||||
- `session.Send(copilot.MessageOptions{Prompt: "...", Attachments: []copilot.Attachment{...}})` - Send message, returns (messageID string, error)
|
||||
- `session.SendAndWait(options, timeout)` - Send and wait for idle, returns (\*SessionEvent, error)
|
||||
- `session.Abort()` - Abort current processing, returns error
|
||||
- `session.GetMessages()` - Get all events/messages, returns ([]SessionEvent, error)
|
||||
- `session.Destroy()` - Clean up session, returns error
|
||||
|
||||
## Event Handling
|
||||
|
||||
### Event Subscription Pattern
|
||||
|
||||
ALWAYS use channels or done signals for waiting on session events:
|
||||
|
||||
```go
|
||||
done := make(chan struct{})
|
||||
|
||||
unsubscribe := session.On(func(evt copilot.SessionEvent) {
|
||||
switch evt.Type {
|
||||
case copilot.AssistantMessage:
|
||||
fmt.Println(*evt.Data.Content)
|
||||
case copilot.SessionIdle:
|
||||
close(done)
|
||||
}
|
||||
})
|
||||
defer unsubscribe()
|
||||
|
||||
session.Send(copilot.MessageOptions{Prompt: "..."})
|
||||
<-done
|
||||
```
|
||||
|
||||
### Unsubscribing from Events
|
||||
|
||||
The `On()` method returns a function that unsubscribes:
|
||||
|
||||
```go
|
||||
unsubscribe := session.On(func(evt copilot.SessionEvent) {
|
||||
// handler
|
||||
})
|
||||
// Later...
|
||||
unsubscribe()
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
Use type switches for event handling:
|
||||
|
||||
```go
|
||||
session.On(func(evt copilot.SessionEvent) {
|
||||
switch evt.Type {
|
||||
case copilot.UserMessage:
|
||||
// Handle user message
|
||||
case copilot.AssistantMessage:
|
||||
if evt.Data.Content != nil {
|
||||
fmt.Println(*evt.Data.Content)
|
||||
}
|
||||
case copilot.ToolExecutionStart:
|
||||
// Tool execution started
|
||||
case copilot.ToolExecutionComplete:
|
||||
// Tool execution completed
|
||||
case copilot.SessionStart:
|
||||
// Session started
|
||||
case copilot.SessionIdle:
|
||||
// Session is idle (processing complete)
|
||||
case copilot.SessionError:
|
||||
if evt.Data.Message != nil {
|
||||
fmt.Println("Error:", *evt.Data.Message)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
### Enabling Streaming
|
||||
|
||||
Set `Streaming: true` in SessionConfig:
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Model: "gpt-5",
|
||||
Streaming: true,
|
||||
})
|
||||
```
|
||||
|
||||
### Handling Streaming Events
|
||||
|
||||
Handle both delta events (incremental) and final events:
|
||||
|
||||
```go
|
||||
done := make(chan struct{})
|
||||
|
||||
session.On(func(evt copilot.SessionEvent) {
|
||||
switch evt.Type {
|
||||
case copilot.AssistantMessageDelta:
|
||||
// Incremental text chunk
|
||||
if evt.Data.DeltaContent != nil {
|
||||
fmt.Print(*evt.Data.DeltaContent)
|
||||
}
|
||||
case copilot.AssistantReasoningDelta:
|
||||
// Incremental reasoning chunk (model-dependent)
|
||||
if evt.Data.DeltaContent != nil {
|
||||
fmt.Print(*evt.Data.DeltaContent)
|
||||
}
|
||||
case copilot.AssistantMessage:
|
||||
// Final complete message
|
||||
fmt.Println("\n--- Final ---")
|
||||
if evt.Data.Content != nil {
|
||||
fmt.Println(*evt.Data.Content)
|
||||
}
|
||||
case copilot.AssistantReasoning:
|
||||
// Final reasoning content
|
||||
fmt.Println("--- Reasoning ---")
|
||||
if evt.Data.Content != nil {
|
||||
fmt.Println(*evt.Data.Content)
|
||||
}
|
||||
case copilot.SessionIdle:
|
||||
close(done)
|
||||
}
|
||||
})
|
||||
|
||||
session.Send(copilot.MessageOptions{Prompt: "Tell me a story"})
|
||||
<-done
|
||||
```
|
||||
|
||||
Note: Final events (`AssistantMessage`, `AssistantReasoning`) are ALWAYS sent regardless of streaming setting.
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Defining Tools
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Model: "gpt-5",
|
||||
Tools: []copilot.Tool{
|
||||
{
|
||||
Name: "lookup_issue",
|
||||
Description: "Fetch issue details from tracker",
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"id": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "Issue ID",
|
||||
},
|
||||
},
|
||||
"required": []string{"id"},
|
||||
},
|
||||
Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) {
|
||||
args := inv.Arguments.(map[string]interface{})
|
||||
issueID := args["id"].(string)
|
||||
|
||||
issue, err := fetchIssue(issueID)
|
||||
if err != nil {
|
||||
return copilot.ToolResult{}, err
|
||||
}
|
||||
|
||||
return copilot.ToolResult{
|
||||
TextResultForLLM: fmt.Sprintf("Issue: %v", issue),
|
||||
ResultType: "success",
|
||||
ToolTelemetry: map[string]interface{}{},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Tool Return Types
|
||||
|
||||
- Return `ToolResult` struct with fields:
|
||||
- `TextResultForLLM` (string) - Result text for the LLM
|
||||
- `ResultType` (string) - "success" or "failure"
|
||||
- `Error` (string, optional) - Internal error message (not shown to LLM)
|
||||
- `ToolTelemetry` (map[string]interface{}) - Telemetry data
|
||||
|
||||
### Tool Execution Flow
|
||||
|
||||
When Copilot invokes a tool, the client automatically:
|
||||
|
||||
1. Runs your handler function
|
||||
2. Returns the ToolResult
|
||||
3. Responds to the CLI
|
||||
|
||||
## System Message Customization
|
||||
|
||||
### Append Mode (Default - Preserves Guardrails)
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Model: "gpt-5",
|
||||
SystemMessage: &copilot.SystemMessageConfig{
|
||||
Mode: "append",
|
||||
Content: `
|
||||
<workflow_rules>
|
||||
- Always check for security vulnerabilities
|
||||
- Suggest performance improvements when applicable
|
||||
</workflow_rules>
|
||||
`,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Replace Mode (Full Control - Removes Guardrails)
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Model: "gpt-5",
|
||||
SystemMessage: &copilot.SystemMessageConfig{
|
||||
Mode: "replace",
|
||||
Content: "You are a helpful assistant.",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## File Attachments
|
||||
|
||||
Attach files to messages using `Attachment`:
|
||||
|
||||
```go
|
||||
messageID, err := session.Send(copilot.MessageOptions{
|
||||
Prompt: "Analyze this file",
|
||||
Attachments: []copilot.Attachment{
|
||||
{
|
||||
Type: "file",
|
||||
Path: "/path/to/file.go",
|
||||
DisplayName: "My File",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Message Delivery Modes
|
||||
|
||||
Use the `Mode` field in `MessageOptions`:
|
||||
|
||||
- `"enqueue"` - Queue message for processing
|
||||
- `"immediate"` - Process message immediately
|
||||
|
||||
```go
|
||||
session.Send(copilot.MessageOptions{
|
||||
Prompt: "...",
|
||||
Mode: "enqueue",
|
||||
})
|
||||
```
|
||||
|
||||
## Multiple Sessions
|
||||
|
||||
Sessions are independent and can run concurrently:
|
||||
|
||||
```go
|
||||
session1, _ := client.CreateSession(&copilot.SessionConfig{Model: "gpt-5"})
|
||||
session2, _ := client.CreateSession(&copilot.SessionConfig{Model: "claude-sonnet-4.5"})
|
||||
|
||||
session1.Send(copilot.MessageOptions{Prompt: "Hello from session 1"})
|
||||
session2.Send(copilot.MessageOptions{Prompt: "Hello from session 2"})
|
||||
```
|
||||
|
||||
## Bring Your Own Key (BYOK)
|
||||
|
||||
Use custom API providers via `ProviderConfig`:
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{
|
||||
Provider: &copilot.ProviderConfig{
|
||||
Type: "openai",
|
||||
BaseURL: "https://api.openai.com/v1",
|
||||
APIKey: "your-api-key",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Session Lifecycle Management
|
||||
|
||||
### Checking Connection State
|
||||
|
||||
```go
|
||||
state := client.GetState()
|
||||
// Returns: "disconnected", "connecting", "connected", or "error"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Exception Handling
|
||||
|
||||
```go
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
|
||||
_, err = session.Send(copilot.MessageOptions{Prompt: "Hello"})
|
||||
if err != nil {
|
||||
log.Printf("Failed to send: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Session Error Events
|
||||
|
||||
Monitor `SessionError` type for runtime errors:
|
||||
|
||||
```go
|
||||
session.On(func(evt copilot.SessionEvent) {
|
||||
if evt.Type == copilot.SessionError {
|
||||
if evt.Data.Message != nil {
|
||||
fmt.Fprintf(os.Stderr, "Session Error: %s\n", *evt.Data.Message)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Connectivity Testing
|
||||
|
||||
Use Ping to verify server connectivity:
|
||||
|
||||
```go
|
||||
resp, err := client.Ping("test message")
|
||||
if err != nil {
|
||||
log.Printf("Server unreachable: %v", err)
|
||||
} else {
|
||||
log.Printf("Server responded at %d", resp.Timestamp)
|
||||
}
|
||||
```
|
||||
|
||||
## Resource Cleanup
|
||||
|
||||
### Cleanup with Defer
|
||||
|
||||
ALWAYS use `defer` for cleanup:
|
||||
|
||||
```go
|
||||
client := copilot.NewClient(nil)
|
||||
if err := client.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Stop()
|
||||
|
||||
session, err := client.CreateSession(nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer session.Destroy()
|
||||
```
|
||||
|
||||
### Manual Cleanup
|
||||
|
||||
If not using defer:
|
||||
|
||||
```go
|
||||
client := copilot.NewClient(nil)
|
||||
err := client.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
session, err := client.CreateSession(nil)
|
||||
if err != nil {
|
||||
client.Stop()
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use session...
|
||||
|
||||
session.Destroy()
|
||||
errors := client.Stop()
|
||||
for _, err := range errors {
|
||||
log.Printf("Cleanup error: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use `defer`** for cleanup of clients and sessions
|
||||
2. **Use channels** to wait for SessionIdle event
|
||||
3. **Handle SessionError** events for robust error handling
|
||||
4. **Use type switches** for event handling
|
||||
5. **Enable streaming** for better UX in interactive scenarios
|
||||
6. **Provide descriptive tool names and descriptions** for better model understanding
|
||||
7. **Call unsubscribe functions** when no longer needed
|
||||
8. **Use SystemMessageConfig with Mode: "append"** to preserve safety guardrails
|
||||
9. **Handle both delta and final events** when streaming is enabled
|
||||
10. **Check nil pointers** in event data (Content, Message, etc. are pointers)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Simple Query-Response
|
||||
|
||||
```go
|
||||
client := copilot.NewClient(nil)
|
||||
if err := client.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Stop()
|
||||
|
||||
session, err := client.CreateSession(&copilot.SessionConfig{Model: "gpt-5"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer session.Destroy()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
session.On(func(evt copilot.SessionEvent) {
|
||||
if evt.Type == copilot.AssistantMessage && evt.Data.Content != nil {
|
||||
fmt.Println(*evt.Data.Content)
|
||||
} else if evt.Type == copilot.SessionIdle {
|
||||
close(done)
|
||||
}
|
||||
})
|
||||
|
||||
session.Send(copilot.MessageOptions{Prompt: "What is 2+2?"})
|
||||
<-done
|
||||
```
|
||||
|
||||
### Multi-Turn Conversation
|
||||
|
||||
```go
|
||||
session, _ := client.CreateSession(nil)
|
||||
defer session.Destroy()
|
||||
|
||||
sendAndWait := func(prompt string) error {
|
||||
done := make(chan struct{})
|
||||
var eventErr error
|
||||
|
||||
unsubscribe := session.On(func(evt copilot.SessionEvent) {
|
||||
switch evt.Type {
|
||||
case copilot.AssistantMessage:
|
||||
if evt.Data.Content != nil {
|
||||
fmt.Println(*evt.Data.Content)
|
||||
}
|
||||
case copilot.SessionIdle:
|
||||
close(done)
|
||||
case copilot.SessionError:
|
||||
if evt.Data.Message != nil {
|
||||
eventErr = fmt.Errorf(*evt.Data.Message)
|
||||
}
|
||||
}
|
||||
})
|
||||
defer unsubscribe()
|
||||
|
||||
if _, err := session.Send(copilot.MessageOptions{Prompt: prompt}); err != nil {
|
||||
return err
|
||||
}
|
||||
<-done
|
||||
return eventErr
|
||||
}
|
||||
|
||||
sendAndWait("What is the capital of France?")
|
||||
sendAndWait("What is its population?")
|
||||
```
|
||||
|
||||
### SendAndWait Helper
|
||||
|
||||
```go
|
||||
// Use built-in SendAndWait for simpler synchronous interaction
|
||||
response, err := session.SendAndWait(copilot.MessageOptions{
|
||||
Prompt: "What is 2+2?",
|
||||
}, 0) // 0 uses default 60s timeout
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
}
|
||||
if response != nil && response.Data.Content != nil {
|
||||
fmt.Println(*response.Data.Content)
|
||||
}
|
||||
```
|
||||
|
||||
### Tool with Struct Return Type
|
||||
|
||||
```go
|
||||
type UserInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
session, _ := client.CreateSession(&copilot.SessionConfig{
|
||||
Tools: []copilot.Tool{
|
||||
{
|
||||
Name: "get_user",
|
||||
Description: "Retrieve user information",
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"user_id": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "User ID",
|
||||
},
|
||||
},
|
||||
"required": []string{"user_id"},
|
||||
},
|
||||
Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) {
|
||||
args := inv.Arguments.(map[string]interface{})
|
||||
userID := args["user_id"].(string)
|
||||
|
||||
user := UserInfo{
|
||||
ID: userID,
|
||||
Name: "John Doe",
|
||||
Email: "john@example.com",
|
||||
Role: "Developer",
|
||||
}
|
||||
|
||||
jsonBytes, _ := json.Marshal(user)
|
||||
return copilot.ToolResult{
|
||||
TextResultForLLM: string(jsonBytes),
|
||||
ResultType: "success",
|
||||
ToolTelemetry: map[string]interface{}{},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
717
instructions/copilot-sdk-nodejs.instructions.md
Normal file
717
instructions/copilot-sdk-nodejs.instructions.md
Normal file
@@ -0,0 +1,717 @@
|
||||
---
|
||||
applyTo: "**.ts, **.js, package.json"
|
||||
description: "This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK."
|
||||
name: "GitHub Copilot SDK Node.js Instructions"
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
- The SDK is in technical preview and may have breaking changes
|
||||
- Requires Node.js 18.0 or later
|
||||
- Requires GitHub Copilot CLI installed and in PATH
|
||||
- Built with TypeScript for type safety
|
||||
- Uses async/await patterns throughout
|
||||
- Provides full TypeScript type definitions
|
||||
|
||||
## Installation
|
||||
|
||||
Always install via npm/pnpm/yarn:
|
||||
|
||||
```bash
|
||||
npm install @github/copilot-sdk
|
||||
# or
|
||||
pnpm add @github/copilot-sdk
|
||||
# or
|
||||
yarn add @github/copilot-sdk
|
||||
```
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### Basic Client Setup
|
||||
|
||||
```typescript
|
||||
import { CopilotClient } from "@github/copilot-sdk";
|
||||
|
||||
const client = new CopilotClient();
|
||||
await client.start();
|
||||
// Use client...
|
||||
await client.stop();
|
||||
```
|
||||
|
||||
### Client Configuration Options
|
||||
|
||||
When creating a CopilotClient, use `CopilotClientOptions`:
|
||||
|
||||
- `cliPath` - Path to CLI executable (default: "copilot" from PATH)
|
||||
- `cliArgs` - Extra arguments prepended before SDK-managed flags (string[])
|
||||
- `cliUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process
|
||||
- `port` - Server port (default: 0 for random)
|
||||
- `useStdio` - Use stdio transport instead of TCP (default: true)
|
||||
- `logLevel` - Log level (default: "debug")
|
||||
- `autoStart` - Auto-start server (default: true)
|
||||
- `autoRestart` - Auto-restart on crash (default: true)
|
||||
- `cwd` - Working directory for the CLI process (default: process.cwd())
|
||||
- `env` - Environment variables for the CLI process (default: process.env)
|
||||
|
||||
### Manual Server Control
|
||||
|
||||
For explicit control:
|
||||
|
||||
```typescript
|
||||
const client = new CopilotClient({ autoStart: false });
|
||||
await client.start();
|
||||
// Use client...
|
||||
await client.stop();
|
||||
```
|
||||
|
||||
Use `forceStop()` when `stop()` takes too long.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Creating Sessions
|
||||
|
||||
Use `SessionConfig` for configuration:
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
model: "gpt-5",
|
||||
streaming: true,
|
||||
tools: [...],
|
||||
systemMessage: { ... },
|
||||
availableTools: ["tool1", "tool2"],
|
||||
excludedTools: ["tool3"],
|
||||
provider: { ... }
|
||||
});
|
||||
```
|
||||
|
||||
### Session Config Options
|
||||
|
||||
- `sessionId` - Custom session ID (string)
|
||||
- `model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.)
|
||||
- `tools` - Custom tools exposed to the CLI (Tool[])
|
||||
- `systemMessage` - System message customization (SystemMessageConfig)
|
||||
- `availableTools` - Allowlist of tool names (string[])
|
||||
- `excludedTools` - Blocklist of tool names (string[])
|
||||
- `provider` - Custom API provider configuration (BYOK) (ProviderConfig)
|
||||
- `streaming` - Enable streaming response chunks (boolean)
|
||||
- `mcpServers` - MCP server configurations (MCPServerConfig[])
|
||||
- `customAgents` - Custom agent configurations (CustomAgentConfig[])
|
||||
- `configDir` - Config directory override (string)
|
||||
- `skillDirectories` - Skill directories (string[])
|
||||
- `disabledSkills` - Disabled skills (string[])
|
||||
- `onPermissionRequest` - Permission request handler (PermissionHandler)
|
||||
|
||||
### Resuming Sessions
|
||||
|
||||
```typescript
|
||||
const session = await client.resumeSession("session-id", {
|
||||
tools: [myNewTool],
|
||||
});
|
||||
```
|
||||
|
||||
### Session Operations
|
||||
|
||||
- `session.sessionId` - Get session identifier (string)
|
||||
- `await session.send({ prompt: "...", attachments: [...] })` - Send message, returns Promise<string>
|
||||
- `await session.sendAndWait({ prompt: "..." }, timeout)` - Send and wait for idle, returns Promise<AssistantMessageEvent | null>
|
||||
- `await session.abort()` - Abort current processing
|
||||
- `await session.getMessages()` - Get all events/messages, returns Promise<SessionEvent[]>
|
||||
- `await session.destroy()` - Clean up session
|
||||
|
||||
## Event Handling
|
||||
|
||||
### Event Subscription Pattern
|
||||
|
||||
ALWAYS use async/await or Promises for waiting on session events:
|
||||
|
||||
```typescript
|
||||
await new Promise<void>((resolve) => {
|
||||
session.on((event) => {
|
||||
if (event.type === "assistant.message") {
|
||||
console.log(event.data.content);
|
||||
} else if (event.type === "session.idle") {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
session.send({ prompt: "..." });
|
||||
});
|
||||
```
|
||||
|
||||
### Unsubscribing from Events
|
||||
|
||||
The `on()` method returns a function that unsubscribes:
|
||||
|
||||
```typescript
|
||||
const unsubscribe = session.on((event) => {
|
||||
// handler
|
||||
});
|
||||
// Later...
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
Use discriminated unions with type guards for event handling:
|
||||
|
||||
```typescript
|
||||
session.on((event) => {
|
||||
switch (event.type) {
|
||||
case "user.message":
|
||||
// Handle user message
|
||||
break;
|
||||
case "assistant.message":
|
||||
console.log(event.data.content);
|
||||
break;
|
||||
case "tool.executionStart":
|
||||
// Tool execution started
|
||||
break;
|
||||
case "tool.executionComplete":
|
||||
// Tool execution completed
|
||||
break;
|
||||
case "session.start":
|
||||
// Session started
|
||||
break;
|
||||
case "session.idle":
|
||||
// Session is idle (processing complete)
|
||||
break;
|
||||
case "session.error":
|
||||
console.error(`Error: ${event.data.message}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
### Enabling Streaming
|
||||
|
||||
Set `streaming: true` in SessionConfig:
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
model: "gpt-5",
|
||||
streaming: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Handling Streaming Events
|
||||
|
||||
Handle both delta events (incremental) and final events:
|
||||
|
||||
```typescript
|
||||
await new Promise<void>((resolve) => {
|
||||
session.on((event) => {
|
||||
switch (event.type) {
|
||||
case "assistant.message.delta":
|
||||
// Incremental text chunk
|
||||
process.stdout.write(event.data.deltaContent);
|
||||
break;
|
||||
case "assistant.reasoning.delta":
|
||||
// Incremental reasoning chunk (model-dependent)
|
||||
process.stdout.write(event.data.deltaContent);
|
||||
break;
|
||||
case "assistant.message":
|
||||
// Final complete message
|
||||
console.log("\n--- Final ---");
|
||||
console.log(event.data.content);
|
||||
break;
|
||||
case "assistant.reasoning":
|
||||
// Final reasoning content
|
||||
console.log("--- Reasoning ---");
|
||||
console.log(event.data.content);
|
||||
break;
|
||||
case "session.idle":
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
session.send({ prompt: "Tell me a story" });
|
||||
});
|
||||
```
|
||||
|
||||
Note: Final events (`assistant.message`, `assistant.reasoning`) are ALWAYS sent regardless of streaming setting.
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Defining Tools with defineTool
|
||||
|
||||
Use `defineTool` for type-safe tool definitions:
|
||||
|
||||
```typescript
|
||||
import { defineTool } from "@github/copilot-sdk";
|
||||
|
||||
const session = await client.createSession({
|
||||
model: "gpt-5",
|
||||
tools: [
|
||||
defineTool({
|
||||
name: "lookup_issue",
|
||||
description: "Fetch issue details from tracker",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Issue ID" },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
handler: async (args) => {
|
||||
const issue = await fetchIssue(args.id);
|
||||
return issue;
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Using Zod for Parameters
|
||||
|
||||
The SDK supports Zod schemas for parameters:
|
||||
|
||||
```typescript
|
||||
import { z } from "zod";
|
||||
|
||||
const session = await client.createSession({
|
||||
tools: [
|
||||
defineTool({
|
||||
name: "get_weather",
|
||||
description: "Get weather for a location",
|
||||
parameters: z.object({
|
||||
location: z.string().describe("City name"),
|
||||
units: z.enum(["celsius", "fahrenheit"]).optional(),
|
||||
}),
|
||||
handler: async (args) => {
|
||||
return { temperature: 72, units: args.units || "fahrenheit" };
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Tool Return Types
|
||||
|
||||
- Return any JSON-serializable value (automatically wrapped)
|
||||
- Or return `ToolResultObject` for full control over metadata:
|
||||
|
||||
```typescript
|
||||
{
|
||||
textResultForLlm: string; // Result shown to LLM
|
||||
resultType: "success" | "failure";
|
||||
error?: string; // Internal error (not shown to LLM)
|
||||
toolTelemetry?: Record<string, unknown>;
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Execution Flow
|
||||
|
||||
When Copilot invokes a tool, the client automatically:
|
||||
|
||||
1. Runs your handler function
|
||||
2. Serializes the return value
|
||||
3. Responds to the CLI
|
||||
|
||||
## System Message Customization
|
||||
|
||||
### Append Mode (Default - Preserves Guardrails)
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
model: "gpt-5",
|
||||
systemMessage: {
|
||||
mode: "append",
|
||||
content: `
|
||||
<workflow_rules>
|
||||
- Always check for security vulnerabilities
|
||||
- Suggest performance improvements when applicable
|
||||
</workflow_rules>
|
||||
`,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Replace Mode (Full Control - Removes Guardrails)
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
model: "gpt-5",
|
||||
systemMessage: {
|
||||
mode: "replace",
|
||||
content: "You are a helpful assistant.",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## File Attachments
|
||||
|
||||
Attach files to messages:
|
||||
|
||||
```typescript
|
||||
await session.send({
|
||||
prompt: "Analyze this file",
|
||||
attachments: [
|
||||
{
|
||||
type: "file",
|
||||
path: "/path/to/file.ts",
|
||||
displayName: "My File",
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Message Delivery Modes
|
||||
|
||||
Use the `mode` property in message options:
|
||||
|
||||
- `"enqueue"` - Queue message for processing
|
||||
- `"immediate"` - Process message immediately
|
||||
|
||||
```typescript
|
||||
await session.send({
|
||||
prompt: "...",
|
||||
mode: "enqueue",
|
||||
});
|
||||
```
|
||||
|
||||
## Multiple Sessions
|
||||
|
||||
Sessions are independent and can run concurrently:
|
||||
|
||||
```typescript
|
||||
const session1 = await client.createSession({ model: "gpt-5" });
|
||||
const session2 = await client.createSession({ model: "claude-sonnet-4.5" });
|
||||
|
||||
await Promise.all([
|
||||
session1.send({ prompt: "Hello from session 1" }),
|
||||
session2.send({ prompt: "Hello from session 2" }),
|
||||
]);
|
||||
```
|
||||
|
||||
## Bring Your Own Key (BYOK)
|
||||
|
||||
Use custom API providers via `provider`:
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession({
|
||||
provider: {
|
||||
type: "openai",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: "your-api-key",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Session Lifecycle Management
|
||||
|
||||
### Listing Sessions
|
||||
|
||||
```typescript
|
||||
const sessions = await client.listSessions();
|
||||
for (const metadata of sessions) {
|
||||
console.log(`${metadata.sessionId}: ${metadata.summary}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting Sessions
|
||||
|
||||
```typescript
|
||||
await client.deleteSession(sessionId);
|
||||
```
|
||||
|
||||
### Getting Last Session ID
|
||||
|
||||
```typescript
|
||||
const lastId = await client.getLastSessionId();
|
||||
if (lastId) {
|
||||
const session = await client.resumeSession(lastId);
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Connection State
|
||||
|
||||
```typescript
|
||||
const state = client.getState();
|
||||
// Returns: "disconnected" | "connecting" | "connected" | "error"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Exception Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const session = await client.createSession();
|
||||
await session.send({ prompt: "Hello" });
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Session Error Events
|
||||
|
||||
Monitor `session.error` event type for runtime errors:
|
||||
|
||||
```typescript
|
||||
session.on((event) => {
|
||||
if (event.type === "session.error") {
|
||||
console.error(`Session Error: ${event.data.message}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Connectivity Testing
|
||||
|
||||
Use ping to verify server connectivity:
|
||||
|
||||
```typescript
|
||||
const response = await client.ping("health check");
|
||||
console.log(`Server responded at ${new Date(response.timestamp)}`);
|
||||
```
|
||||
|
||||
## Resource Cleanup
|
||||
|
||||
### Automatic Cleanup with Try-Finally
|
||||
|
||||
ALWAYS use try-finally or cleanup in a finally block:
|
||||
|
||||
```typescript
|
||||
const client = new CopilotClient();
|
||||
try {
|
||||
await client.start();
|
||||
const session = await client.createSession();
|
||||
try {
|
||||
// Use session...
|
||||
} finally {
|
||||
await session.destroy();
|
||||
}
|
||||
} finally {
|
||||
await client.stop();
|
||||
}
|
||||
```
|
||||
|
||||
### Cleanup Function Pattern
|
||||
|
||||
```typescript
|
||||
async function withClient<T>(
|
||||
fn: (client: CopilotClient) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const client = new CopilotClient();
|
||||
try {
|
||||
await client.start();
|
||||
return await fn(client);
|
||||
} finally {
|
||||
await client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
async function withSession<T>(
|
||||
client: CopilotClient,
|
||||
fn: (session: CopilotSession) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const session = await client.createSession();
|
||||
try {
|
||||
return await fn(session);
|
||||
} finally {
|
||||
await session.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
await withClient(async (client) => {
|
||||
await withSession(client, async (session) => {
|
||||
await session.send({ prompt: "Hello!" });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use try-finally** for resource cleanup
|
||||
2. **Use Promises** to wait for session.idle event
|
||||
3. **Handle session.error** events for robust error handling
|
||||
4. **Use type guards or switch statements** for event handling
|
||||
5. **Enable streaming** for better UX in interactive scenarios
|
||||
6. **Use defineTool** for type-safe tool definitions
|
||||
7. **Use Zod schemas** for runtime parameter validation
|
||||
8. **Dispose event subscriptions** when no longer needed
|
||||
9. **Use systemMessage with mode: "append"** to preserve safety guardrails
|
||||
10. **Handle both delta and final events** when streaming is enabled
|
||||
11. **Leverage TypeScript types** for compile-time safety
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Simple Query-Response
|
||||
|
||||
```typescript
|
||||
import { CopilotClient } from "@github/copilot-sdk";
|
||||
|
||||
const client = new CopilotClient();
|
||||
try {
|
||||
await client.start();
|
||||
|
||||
const session = await client.createSession({ model: "gpt-5" });
|
||||
try {
|
||||
await new Promise<void>((resolve) => {
|
||||
session.on((event) => {
|
||||
if (event.type === "assistant.message") {
|
||||
console.log(event.data.content);
|
||||
} else if (event.type === "session.idle") {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
session.send({ prompt: "What is 2+2?" });
|
||||
});
|
||||
} finally {
|
||||
await session.destroy();
|
||||
}
|
||||
} finally {
|
||||
await client.stop();
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Turn Conversation
|
||||
|
||||
```typescript
|
||||
const session = await client.createSession();
|
||||
|
||||
async function sendAndWait(prompt: string): Promise<void> {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const unsubscribe = session.on((event) => {
|
||||
if (event.type === "assistant.message") {
|
||||
console.log(event.data.content);
|
||||
} else if (event.type === "session.idle") {
|
||||
unsubscribe();
|
||||
resolve();
|
||||
} else if (event.type === "session.error") {
|
||||
unsubscribe();
|
||||
reject(new Error(event.data.message));
|
||||
}
|
||||
});
|
||||
|
||||
session.send({ prompt });
|
||||
});
|
||||
}
|
||||
|
||||
await sendAndWait("What is the capital of France?");
|
||||
await sendAndWait("What is its population?");
|
||||
```
|
||||
|
||||
### SendAndWait Helper
|
||||
|
||||
```typescript
|
||||
// Use built-in sendAndWait for simpler synchronous interaction
|
||||
const response = await session.sendAndWait({ prompt: "What is 2+2?" }, 60000);
|
||||
|
||||
if (response) {
|
||||
console.log(response.data.content);
|
||||
}
|
||||
```
|
||||
|
||||
### Tool with Type-Safe Parameters
|
||||
|
||||
```typescript
|
||||
import { z } from "zod";
|
||||
import { defineTool } from "@github/copilot-sdk";
|
||||
|
||||
interface UserInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
const session = await client.createSession({
|
||||
tools: [
|
||||
defineTool({
|
||||
name: "get_user",
|
||||
description: "Retrieve user information",
|
||||
parameters: z.object({
|
||||
userId: z.string().describe("User ID"),
|
||||
}),
|
||||
handler: async (args): Promise<UserInfo> => {
|
||||
return {
|
||||
id: args.userId,
|
||||
name: "John Doe",
|
||||
email: "john@example.com",
|
||||
role: "Developer",
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Streaming with Progress
|
||||
|
||||
```typescript
|
||||
let currentMessage = "";
|
||||
|
||||
const unsubscribe = session.on((event) => {
|
||||
if (event.type === "assistant.message.delta") {
|
||||
currentMessage += event.data.deltaContent;
|
||||
process.stdout.write(event.data.deltaContent);
|
||||
} else if (event.type === "assistant.message") {
|
||||
console.log("\n\n=== Complete ===");
|
||||
console.log(`Total length: ${event.data.content.length} chars`);
|
||||
} else if (event.type === "session.idle") {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
await session.send({ prompt: "Write a long story" });
|
||||
```
|
||||
|
||||
### Error Recovery
|
||||
|
||||
```typescript
|
||||
session.on((event) => {
|
||||
if (event.type === "session.error") {
|
||||
console.error("Session error:", event.data.message);
|
||||
// Optionally retry or handle error
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await session.send({ prompt: "risky operation" });
|
||||
} catch (error) {
|
||||
// Handle send errors
|
||||
console.error("Failed to send:", error);
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript-Specific Features
|
||||
|
||||
### Type Inference
|
||||
|
||||
```typescript
|
||||
import type { SessionEvent, AssistantMessageEvent } from "@github/copilot-sdk";
|
||||
|
||||
session.on((event: SessionEvent) => {
|
||||
if (event.type === "assistant.message") {
|
||||
// TypeScript knows event is AssistantMessageEvent here
|
||||
const content: string = event.data.content;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Generic Helper
|
||||
|
||||
```typescript
|
||||
async function waitForEvent<T extends SessionEvent["type"]>(
|
||||
session: CopilotSession,
|
||||
eventType: T,
|
||||
): Promise<Extract<SessionEvent, { type: T }>> {
|
||||
return new Promise((resolve) => {
|
||||
const unsubscribe = session.on((event) => {
|
||||
if (event.type === eventType) {
|
||||
unsubscribe();
|
||||
resolve(event as Extract<SessionEvent, { type: T }>);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Usage
|
||||
const message = await waitForEvent(session, "assistant.message");
|
||||
console.log(message.data.content);
|
||||
```
|
||||
806
instructions/copilot-sdk-python.instructions.md
Normal file
806
instructions/copilot-sdk-python.instructions.md
Normal file
@@ -0,0 +1,806 @@
|
||||
---
|
||||
applyTo: "**.py, pyproject.toml, setup.py"
|
||||
description: "This file provides guidance on building Python applications using GitHub Copilot SDK."
|
||||
name: "GitHub Copilot SDK Python Instructions"
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
- The SDK is in technical preview and may have breaking changes
|
||||
- Requires Python 3.9 or later
|
||||
- Requires GitHub Copilot CLI installed and in PATH
|
||||
- Uses async/await patterns throughout (asyncio)
|
||||
- Supports both async context managers and manual lifecycle management
|
||||
- Type hints provided for better IDE support
|
||||
|
||||
## Installation
|
||||
|
||||
Always install via pip:
|
||||
|
||||
```bash
|
||||
pip install copilot-sdk
|
||||
# or with poetry
|
||||
poetry add copilot-sdk
|
||||
# or with uv
|
||||
uv add copilot-sdk
|
||||
```
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### Basic Client Setup
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
|
||||
async with CopilotClient() as client:
|
||||
# Use client...
|
||||
pass
|
||||
```
|
||||
|
||||
### Client Configuration Options
|
||||
|
||||
When creating a CopilotClient, use a dict with these keys:
|
||||
|
||||
- `cli_path` - Path to CLI executable (default: "copilot" from PATH or COPILOT_CLI_PATH env var)
|
||||
- `cli_url` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process
|
||||
- `port` - Server port (default: 0 for random)
|
||||
- `use_stdio` - Use stdio transport instead of TCP (default: True)
|
||||
- `log_level` - Log level (default: "info")
|
||||
- `auto_start` - Auto-start server (default: True)
|
||||
- `auto_restart` - Auto-restart on crash (default: True)
|
||||
- `cwd` - Working directory for the CLI process (default: os.getcwd())
|
||||
- `env` - Environment variables for the CLI process (dict)
|
||||
|
||||
### Manual Server Control
|
||||
|
||||
For explicit control:
|
||||
|
||||
```python
|
||||
client = CopilotClient({"auto_start": False})
|
||||
await client.start()
|
||||
# Use client...
|
||||
await client.stop()
|
||||
```
|
||||
|
||||
Use `force_stop()` when `stop()` takes too long.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Creating Sessions
|
||||
|
||||
Use a dict for SessionConfig:
|
||||
|
||||
```python
|
||||
session = await client.create_session({
|
||||
"model": "gpt-5",
|
||||
"streaming": True,
|
||||
"tools": [...],
|
||||
"system_message": { ... },
|
||||
"available_tools": ["tool1", "tool2"],
|
||||
"excluded_tools": ["tool3"],
|
||||
"provider": { ... }
|
||||
})
|
||||
```
|
||||
|
||||
### Session Config Options
|
||||
|
||||
- `session_id` - Custom session ID (str)
|
||||
- `model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.)
|
||||
- `tools` - Custom tools exposed to the CLI (list[Tool])
|
||||
- `system_message` - System message customization (dict)
|
||||
- `available_tools` - Allowlist of tool names (list[str])
|
||||
- `excluded_tools` - Blocklist of tool names (list[str])
|
||||
- `provider` - Custom API provider configuration (BYOK) (ProviderConfig)
|
||||
- `streaming` - Enable streaming response chunks (bool)
|
||||
- `mcp_servers` - MCP server configurations (list)
|
||||
- `custom_agents` - Custom agent configurations (list)
|
||||
- `config_dir` - Config directory override (str)
|
||||
- `skill_directories` - Skill directories (list[str])
|
||||
- `disabled_skills` - Disabled skills (list[str])
|
||||
- `on_permission_request` - Permission request handler (callable)
|
||||
|
||||
### Resuming Sessions
|
||||
|
||||
```python
|
||||
session = await client.resume_session("session-id", {
|
||||
"tools": [my_new_tool]
|
||||
})
|
||||
```
|
||||
|
||||
### Session Operations
|
||||
|
||||
- `session.session_id` - Get session identifier (str)
|
||||
- `await session.send({"prompt": "...", "attachments": [...]})` - Send message, returns str (message ID)
|
||||
- `await session.send_and_wait({"prompt": "..."}, timeout=60.0)` - Send and wait for idle, returns SessionEvent | None
|
||||
- `await session.abort()` - Abort current processing
|
||||
- `await session.get_messages()` - Get all events/messages, returns list[SessionEvent]
|
||||
- `await session.destroy()` - Clean up session
|
||||
|
||||
## Event Handling
|
||||
|
||||
### Event Subscription Pattern
|
||||
|
||||
ALWAYS use asyncio events or futures for waiting on session events:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
done = asyncio.Event()
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message":
|
||||
print(event.data.content)
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
|
||||
session.on(handler)
|
||||
await session.send({"prompt": "..."})
|
||||
await done.wait()
|
||||
```
|
||||
|
||||
### Unsubscribing from Events
|
||||
|
||||
The `on()` method returns a function that unsubscribes:
|
||||
|
||||
```python
|
||||
unsubscribe = session.on(lambda event: print(event.type))
|
||||
# Later...
|
||||
unsubscribe()
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
Use attribute access for event type checking:
|
||||
|
||||
```python
|
||||
def handler(event):
|
||||
if event.type == "user.message":
|
||||
# Handle user message
|
||||
pass
|
||||
elif event.type == "assistant.message":
|
||||
print(event.data.content)
|
||||
elif event.type == "tool.executionStart":
|
||||
# Tool execution started
|
||||
pass
|
||||
elif event.type == "tool.executionComplete":
|
||||
# Tool execution completed
|
||||
pass
|
||||
elif event.type == "session.start":
|
||||
# Session started
|
||||
pass
|
||||
elif event.type == "session.idle":
|
||||
# Session is idle (processing complete)
|
||||
pass
|
||||
elif event.type == "session.error":
|
||||
print(f"Error: {event.data.message}")
|
||||
|
||||
session.on(handler)
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
### Enabling Streaming
|
||||
|
||||
Set `streaming: True` in SessionConfig:
|
||||
|
||||
```python
|
||||
session = await client.create_session({
|
||||
"model": "gpt-5",
|
||||
"streaming": True
|
||||
})
|
||||
```
|
||||
|
||||
### Handling Streaming Events
|
||||
|
||||
Handle both delta events (incremental) and final events:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
done = asyncio.Event()
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message.delta":
|
||||
# Incremental text chunk
|
||||
print(event.data.delta_content, end="", flush=True)
|
||||
elif event.type == "assistant.reasoning.delta":
|
||||
# Incremental reasoning chunk (model-dependent)
|
||||
print(event.data.delta_content, end="", flush=True)
|
||||
elif event.type == "assistant.message":
|
||||
# Final complete message
|
||||
print("\n--- Final ---")
|
||||
print(event.data.content)
|
||||
elif event.type == "assistant.reasoning":
|
||||
# Final reasoning content
|
||||
print("--- Reasoning ---")
|
||||
print(event.data.content)
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
|
||||
session.on(handler)
|
||||
await session.send({"prompt": "Tell me a story"})
|
||||
await done.wait()
|
||||
```
|
||||
|
||||
Note: Final events (`assistant.message`, `assistant.reasoning`) are ALWAYS sent regardless of streaming setting.
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Defining Tools with define_tool
|
||||
|
||||
Use `define_tool` for tool definitions:
|
||||
|
||||
```python
|
||||
from copilot import define_tool
|
||||
|
||||
async def fetch_issue(issue_id: str):
|
||||
# Fetch issue from tracker
|
||||
return {"id": issue_id, "status": "open"}
|
||||
|
||||
session = await client.create_session({
|
||||
"model": "gpt-5",
|
||||
"tools": [
|
||||
define_tool(
|
||||
name="lookup_issue",
|
||||
description="Fetch issue details from tracker",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {"type": "string", "description": "Issue ID"}
|
||||
},
|
||||
"required": ["id"]
|
||||
},
|
||||
handler=lambda args, inv: fetch_issue(args["id"])
|
||||
)
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Using Pydantic for Parameters
|
||||
|
||||
The SDK works well with Pydantic models:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class WeatherArgs(BaseModel):
|
||||
location: str = Field(description="City name")
|
||||
units: str = Field(default="fahrenheit", description="Temperature units")
|
||||
|
||||
async def get_weather(args: WeatherArgs, inv):
|
||||
return {"temperature": 72, "units": args.units}
|
||||
|
||||
session = await client.create_session({
|
||||
"tools": [
|
||||
define_tool(
|
||||
name="get_weather",
|
||||
description="Get weather for a location",
|
||||
parameters=WeatherArgs.model_json_schema(),
|
||||
handler=lambda args, inv: get_weather(WeatherArgs(**args), inv)
|
||||
)
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Tool Return Types
|
||||
|
||||
- Return any JSON-serializable value (automatically wrapped)
|
||||
- Or return a ToolResult dict for full control:
|
||||
|
||||
```python
|
||||
{
|
||||
"text_result_for_llm": str, # Result shown to LLM
|
||||
"result_type": "success" | "failure",
|
||||
"error": str, # Optional: Internal error (not shown to LLM)
|
||||
"tool_telemetry": dict # Optional: Telemetry data
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Handler Signature
|
||||
|
||||
Tool handlers receive two arguments:
|
||||
|
||||
- `args` (dict) - The tool arguments passed by the LLM
|
||||
- `invocation` (ToolInvocation) - Metadata about the invocation
|
||||
- `invocation.session_id` - Session ID
|
||||
- `invocation.tool_call_id` - Tool call ID
|
||||
- `invocation.tool_name` - Tool name
|
||||
- `invocation.arguments` - Same as args parameter
|
||||
|
||||
### Tool Execution Flow
|
||||
|
||||
When Copilot invokes a tool, the client automatically:
|
||||
|
||||
1. Runs your handler function
|
||||
2. Serializes the return value
|
||||
3. Responds to the CLI
|
||||
|
||||
## System Message Customization
|
||||
|
||||
### Append Mode (Default - Preserves Guardrails)
|
||||
|
||||
```python
|
||||
session = await client.create_session({
|
||||
"model": "gpt-5",
|
||||
"system_message": {
|
||||
"mode": "append",
|
||||
"content": """
|
||||
<workflow_rules>
|
||||
- Always check for security vulnerabilities
|
||||
- Suggest performance improvements when applicable
|
||||
</workflow_rules>
|
||||
"""
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Replace Mode (Full Control - Removes Guardrails)
|
||||
|
||||
```python
|
||||
session = await client.create_session({
|
||||
"model": "gpt-5",
|
||||
"system_message": {
|
||||
"mode": "replace",
|
||||
"content": "You are a helpful assistant."
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## File Attachments
|
||||
|
||||
Attach files to messages:
|
||||
|
||||
```python
|
||||
await session.send({
|
||||
"prompt": "Analyze this file",
|
||||
"attachments": [
|
||||
{
|
||||
"type": "file",
|
||||
"path": "/path/to/file.py",
|
||||
"display_name": "My File"
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## Message Delivery Modes
|
||||
|
||||
Use the `mode` key in message options:
|
||||
|
||||
- `"enqueue"` - Queue message for processing
|
||||
- `"immediate"` - Process message immediately
|
||||
|
||||
```python
|
||||
await session.send({
|
||||
"prompt": "...",
|
||||
"mode": "enqueue"
|
||||
})
|
||||
```
|
||||
|
||||
## Multiple Sessions
|
||||
|
||||
Sessions are independent and can run concurrently:
|
||||
|
||||
```python
|
||||
session1 = await client.create_session({"model": "gpt-5"})
|
||||
session2 = await client.create_session({"model": "claude-sonnet-4.5"})
|
||||
|
||||
await asyncio.gather(
|
||||
session1.send({"prompt": "Hello from session 1"}),
|
||||
session2.send({"prompt": "Hello from session 2"})
|
||||
)
|
||||
```
|
||||
|
||||
## Bring Your Own Key (BYOK)
|
||||
|
||||
Use custom API providers via `provider`:
|
||||
|
||||
```python
|
||||
session = await client.create_session({
|
||||
"provider": {
|
||||
"type": "openai",
|
||||
"base_url": "https://api.openai.com/v1",
|
||||
"api_key": "your-api-key"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Session Lifecycle Management
|
||||
|
||||
### Listing Sessions
|
||||
|
||||
```python
|
||||
sessions = await client.list_sessions()
|
||||
for metadata in sessions:
|
||||
print(f"{metadata.session_id}: {metadata.summary}")
|
||||
```
|
||||
|
||||
### Deleting Sessions
|
||||
|
||||
```python
|
||||
await client.delete_session(session_id)
|
||||
```
|
||||
|
||||
### Getting Last Session ID
|
||||
|
||||
```python
|
||||
last_id = await client.get_last_session_id()
|
||||
if last_id:
|
||||
session = await client.resume_session(last_id)
|
||||
```
|
||||
|
||||
### Checking Connection State
|
||||
|
||||
```python
|
||||
state = client.get_state()
|
||||
# Returns: "disconnected" | "connecting" | "connected" | "error"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Exception Handling
|
||||
|
||||
```python
|
||||
try:
|
||||
session = await client.create_session()
|
||||
await session.send({"prompt": "Hello"})
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
```
|
||||
|
||||
### Session Error Events
|
||||
|
||||
Monitor `session.error` event type for runtime errors:
|
||||
|
||||
```python
|
||||
def handler(event):
|
||||
if event.type == "session.error":
|
||||
print(f"Session Error: {event.data.message}")
|
||||
|
||||
session.on(handler)
|
||||
```
|
||||
|
||||
## Connectivity Testing
|
||||
|
||||
Use ping to verify server connectivity:
|
||||
|
||||
```python
|
||||
response = await client.ping("health check")
|
||||
print(f"Server responded at {response['timestamp']}")
|
||||
```
|
||||
|
||||
## Resource Cleanup
|
||||
|
||||
### Automatic Cleanup with Context Managers
|
||||
|
||||
ALWAYS use async context managers for automatic cleanup:
|
||||
|
||||
```python
|
||||
async with CopilotClient() as client:
|
||||
async with await client.create_session() as session:
|
||||
# Use session...
|
||||
await session.send({"prompt": "Hello"})
|
||||
# Session automatically destroyed
|
||||
# Client automatically stopped
|
||||
```
|
||||
|
||||
### Manual Cleanup with Try-Finally
|
||||
|
||||
```python
|
||||
client = CopilotClient()
|
||||
try:
|
||||
await client.start()
|
||||
session = await client.create_session()
|
||||
try:
|
||||
# Use session...
|
||||
pass
|
||||
finally:
|
||||
await session.destroy()
|
||||
finally:
|
||||
await client.stop()
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use async context managers** (`async with`) for automatic cleanup
|
||||
2. **Use asyncio.Event or asyncio.Future** to wait for session.idle event
|
||||
3. **Handle session.error** events for robust error handling
|
||||
4. **Use if/elif chains** for event type checking
|
||||
5. **Enable streaming** for better UX in interactive scenarios
|
||||
6. **Use define_tool** for tool definitions
|
||||
7. **Use Pydantic models** for type-safe parameter validation
|
||||
8. **Dispose event subscriptions** when no longer needed
|
||||
9. **Use system_message with mode: "append"** to preserve safety guardrails
|
||||
10. **Handle both delta and final events** when streaming is enabled
|
||||
11. **Use type hints** for better IDE support and code clarity
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Simple Query-Response
|
||||
|
||||
```python
|
||||
from copilot import CopilotClient
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
async with CopilotClient() as client:
|
||||
async with await client.create_session({"model": "gpt-5"}) as session:
|
||||
done = asyncio.Event()
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message":
|
||||
print(event.data.content)
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
|
||||
session.on(handler)
|
||||
await session.send({"prompt": "What is 2+2?"})
|
||||
await done.wait()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Multi-Turn Conversation
|
||||
|
||||
```python
|
||||
async def send_and_wait(session, prompt: str):
|
||||
done = asyncio.Event()
|
||||
result = []
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message":
|
||||
result.append(event.data.content)
|
||||
print(event.data.content)
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
elif event.type == "session.error":
|
||||
result.append(None)
|
||||
done.set()
|
||||
|
||||
unsubscribe = session.on(handler)
|
||||
await session.send({"prompt": prompt})
|
||||
await done.wait()
|
||||
unsubscribe()
|
||||
|
||||
return result[0] if result else None
|
||||
|
||||
async with await client.create_session() as session:
|
||||
await send_and_wait(session, "What is the capital of France?")
|
||||
await send_and_wait(session, "What is its population?")
|
||||
```
|
||||
|
||||
### SendAndWait Helper
|
||||
|
||||
```python
|
||||
# Use built-in send_and_wait for simpler synchronous interaction
|
||||
async with await client.create_session() as session:
|
||||
response = await session.send_and_wait(
|
||||
{"prompt": "What is 2+2?"},
|
||||
timeout=60.0
|
||||
)
|
||||
|
||||
if response and response.type == "assistant.message":
|
||||
print(response.data.content)
|
||||
```
|
||||
|
||||
### Tool with Dataclass Return Type
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass, asdict
|
||||
from copilot import define_tool
|
||||
|
||||
@dataclass
|
||||
class UserInfo:
|
||||
id: str
|
||||
name: str
|
||||
email: str
|
||||
role: str
|
||||
|
||||
async def get_user(args, inv) -> dict:
|
||||
user = UserInfo(
|
||||
id=args["user_id"],
|
||||
name="John Doe",
|
||||
email="john@example.com",
|
||||
role="Developer"
|
||||
)
|
||||
return asdict(user)
|
||||
|
||||
session = await client.create_session({
|
||||
"tools": [
|
||||
define_tool(
|
||||
name="get_user",
|
||||
description="Retrieve user information",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": {"type": "string", "description": "User ID"}
|
||||
},
|
||||
"required": ["user_id"]
|
||||
},
|
||||
handler=get_user
|
||||
)
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Streaming with Progress
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
current_message = []
|
||||
done = asyncio.Event()
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message.delta":
|
||||
current_message.append(event.data.delta_content)
|
||||
print(event.data.delta_content, end="", flush=True)
|
||||
elif event.type == "assistant.message":
|
||||
print(f"\n\n=== Complete ===")
|
||||
print(f"Total length: {len(event.data.content)} chars")
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
|
||||
unsubscribe = session.on(handler)
|
||||
await session.send({"prompt": "Write a long story"})
|
||||
await done.wait()
|
||||
unsubscribe()
|
||||
```
|
||||
|
||||
### Error Recovery
|
||||
|
||||
```python
|
||||
def handler(event):
|
||||
if event.type == "session.error":
|
||||
print(f"Session error: {event.data.message}")
|
||||
# Optionally retry or handle error
|
||||
|
||||
session.on(handler)
|
||||
|
||||
try:
|
||||
await session.send({"prompt": "risky operation"})
|
||||
except Exception as e:
|
||||
# Handle send errors
|
||||
print(f"Failed to send: {e}")
|
||||
```
|
||||
|
||||
### Using TypedDict for Type Safety
|
||||
|
||||
```python
|
||||
from typing import TypedDict, List
|
||||
|
||||
class MessageOptions(TypedDict, total=False):
|
||||
prompt: str
|
||||
attachments: List[dict]
|
||||
mode: str
|
||||
|
||||
class SessionConfig(TypedDict, total=False):
|
||||
model: str
|
||||
streaming: bool
|
||||
tools: List
|
||||
|
||||
# Usage with type hints
|
||||
options: MessageOptions = {
|
||||
"prompt": "Hello",
|
||||
"mode": "enqueue"
|
||||
}
|
||||
await session.send(options)
|
||||
|
||||
config: SessionConfig = {
|
||||
"model": "gpt-5",
|
||||
"streaming": True
|
||||
}
|
||||
session = await client.create_session(config)
|
||||
```
|
||||
|
||||
### Async Generator for Streaming
|
||||
|
||||
```python
|
||||
from typing import AsyncGenerator
|
||||
|
||||
async def stream_response(session, prompt: str) -> AsyncGenerator[str, None]:
|
||||
"""Stream response chunks as an async generator."""
|
||||
queue = asyncio.Queue()
|
||||
done = asyncio.Event()
|
||||
|
||||
def handler(event):
|
||||
if event.type == "assistant.message.delta":
|
||||
queue.put_nowait(event.data.delta_content)
|
||||
elif event.type == "session.idle":
|
||||
done.set()
|
||||
|
||||
unsubscribe = session.on(handler)
|
||||
await session.send({"prompt": prompt})
|
||||
|
||||
while not done.is_set():
|
||||
try:
|
||||
chunk = await asyncio.wait_for(queue.get(), timeout=0.1)
|
||||
yield chunk
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
|
||||
# Drain remaining items
|
||||
while not queue.empty():
|
||||
yield queue.get_nowait()
|
||||
|
||||
unsubscribe()
|
||||
|
||||
# Usage
|
||||
async for chunk in stream_response(session, "Tell me a story"):
|
||||
print(chunk, end="", flush=True)
|
||||
```
|
||||
|
||||
### Decorator Pattern for Tools
|
||||
|
||||
```python
|
||||
from typing import Callable, Any
|
||||
from copilot import define_tool
|
||||
|
||||
def copilot_tool(
|
||||
name: str,
|
||||
description: str,
|
||||
parameters: dict
|
||||
) -> Callable:
|
||||
"""Decorator to convert a function into a Copilot tool."""
|
||||
def decorator(func: Callable) -> Any:
|
||||
return define_tool(
|
||||
name=name,
|
||||
description=description,
|
||||
parameters=parameters,
|
||||
handler=lambda args, inv: func(**args)
|
||||
)
|
||||
return decorator
|
||||
|
||||
@copilot_tool(
|
||||
name="calculate",
|
||||
description="Perform a calculation",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {"type": "string", "description": "Math expression"}
|
||||
},
|
||||
"required": ["expression"]
|
||||
}
|
||||
)
|
||||
def calculate(expression: str) -> float:
|
||||
return eval(expression)
|
||||
|
||||
session = await client.create_session({"tools": [calculate]})
|
||||
```
|
||||
|
||||
## Python-Specific Features
|
||||
|
||||
### Async Context Manager Protocol
|
||||
|
||||
The SDK implements `__aenter__` and `__aexit__`:
|
||||
|
||||
```python
|
||||
class CopilotClient:
|
||||
async def __aenter__(self):
|
||||
await self.start()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
await self.stop()
|
||||
return False
|
||||
|
||||
class CopilotSession:
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
await self.destroy()
|
||||
return False
|
||||
```
|
||||
|
||||
### Dataclass Support
|
||||
|
||||
Event data is available as attributes:
|
||||
|
||||
```python
|
||||
def handler(event):
|
||||
# Access event attributes directly
|
||||
print(event.type)
|
||||
print(event.data.content) # For assistant.message
|
||||
print(event.data.delta_content) # For assistant.message.delta
|
||||
```
|
||||
Reference in New Issue
Block a user