GitHub Copilotのプログラムからの呼び出し
問題の説明
GitHub CopilotをIDEプラグインとして利用している多くの開発者が、プログラムから自動的にCopilotの機能を呼び出したいというニーズを持っています。具体的な課題として:
- Copilotが生成する上位のコードサジェストを自動取得したい
- 特定のコンテキストで提案されたコードをファイルに保存したい
- コード生成結果をバッチ処理や自動テストに統合したい
Copilotは内部でOpenAIモデルを使用しているにもかかわらず、公式のAPIが提供されていないため、多くの開発者が自動化手段を探しています。
解決策
推奨方法: 公式Language Serverの利用 (2024年更新)
GitHubが公開した公式の@github/copilot-language-server
パッケージを使用する方法:
# npmで公式Language Serverをインストール
npm install @github/copilot-language-server
基本的な動作フロー:
具体的な実装手順
Node.jsを使用した最小限の実装例:
const { spawn } = require('child_process');
// 公式Language Serverを起動
const server = spawn('node', [
'./node_modules/@github/copilot-language-server/dist/language-server.js',
'--stdio'
]);
// LSPメッセージ送信関数
const sendMessage = (data) => {
const dataString = JSON.stringify({ ...data, jsonrpc: "2.0" });
const contentLength = Buffer.byteLength(dataString, "utf8");
const rpcString = `Content-Length: ${contentLength}\r\n\r\n${dataString}`;
server.stdin.write(rpcString);
};
// リクエスト管理用Map
const resolveMap = new Map();
let requestId = 0;
// リクエスト送信関数
const sendRequest = (method, params) => {
sendMessage({ id: ++requestId, method, params });
return new Promise((resolve) => {
resolveMap.set(requestId, resolve);
});
};
// サーバーからのレスポンス処理
server.stdout.on('data', (data) => {
const responses = data.toString().split(/Content-Length: \d+\r\n\r\n/);
responses.forEach(raw => {
if (raw.trim()) {
try {
const response = JSON.parse(raw);
if (response.id && resolveMap.has(response.id)) {
resolveMap.get(response.id)(response.result);
}
} catch(e) { /* エラー処理 */ }
}
});
});
// メイン処理
const main = async () => {
// サーバーの初期化
await sendRequest("initialize", {
capabilities: { workspace: { workspaceFolders: true } }
});
// ファイルオープン通知
sendMessage({
method: "textDocument/didOpen",
params: {
textDocument: {
uri: "file:///project/main.py",
languageId: "python",
text: "def calculate_sum(a, b):\n return a +"
}
}
});
// 補完リクエスト
const suggestions = await sendRequest("getCompletions", {
doc: {
uri: "file:///project/main.py",
position: { line: 1, character: 14 }
}
});
console.log("上位サジェスト:", suggestions.items.slice(0, 3));
};
main();
認証の準備
コード実行前にCopilotにログインしている必要があります
- 初回実行時には認証コードが表示される
- 指示に従ってGitHubで認証を完了
実行結果の例
{
"items": [
{
"text": "b",
"position": { "line": 1, "character": 14 },
"uuid": "1a2b3c4d"
},
{
"text": "b * 2",
"position": { "line": 1, "character": 14 },
"uuid": "5e6f7g8h"
},
{
"text": "b if a > 0 else 0",
"position": { "line": 1, "character": 14 },
"uuid": "9i0j1k2l"
}
]
}
非公式APIを使う代替方法
copilot-apiプロジェクトの利用方法:
# リポジトリのクローン
git clone https://github.com/B00TK1D/copilot-api.git
cd copilot-api
# 依存関係のインストール
npm install
# サーバー起動
node server.js
API呼び出し例:
import requests
response = requests.post(
"http://localhost:3000/completions",
json={
"code": "def calculate_sum(a, b):\n return a +",
"position": {"line": 1, "column": 14}
}
)
print(response.json()["suggestions"])
注意点
非公式APIはGitHubの利用規約に抵触する可能性があるため、自己責任で使用してください
詳細解説
Language Server Protocol (LSP) の仕組み
GitHub CopilotはLanguage Server Protocolを通じて通信します。主要なメソッド:
メソッド名 | 役割 |
---|---|
initialize | 接続初期化 |
textDocument/didOpen | ファイルオープン通知 |
textDocument/didChange | コンテンツ変更通知 |
getCompletions | コード補完リクエスト (カスタム) |
getCompletionsCycling | 代替候補取得 (カスタム) |
位置指定の重要性
position
パラメータでは正確なカーソル位置を指定:
line
: 0から始まる行番号character
: 行内の文字位置(スペースもカウント)
# サンプルコード
def example():
print("Hello, World!") # この行はline:1、最後の"の位置はcharacter:22
認証フロー
自動ログインを実装する場合は追加処理が必要:
// 認証開始リクエスト
const authData = await sendRequest("signInInitiate", {
user: "github_username"
});
// ユーザーが認証コードをGitHubに入力
console.log("認証コード:", authData.userCode);
// 認証確認
const tokens = await sendRequest("signInConfirm", {
deviceCode: authData.deviceCode,
userCode: authData.userCode
});
console.log("アクセストークン:", tokens.token);
考慮事項
- レート制限: Copilotには不明確なレート制限が存在
- 機密情報: クライアント実装で取得したデータは慎重に扱う
- バージョン互換性: Language Serverの更新でAPIが変更される可能性
- 著作権: 生成されたコードのライセンスと著作権に注意
- 公式APIの将来: GitHubが公式APIを公開する可能性を監視
重要警告
この手法はGitHub Copilotの利用規約を厳密に解釈するとグレーゾーンです
最適な実装例(要約)
const runCopilot = async (filePath, code, position) => {
const server = spawn('node', [copilotServerPath]);
// ... LSP処理関数の定義 ...
await sendRequest("initialize", { capabilities: {} });
sendMessage({
method: "textDocument/didOpen",
params: { textDocument: {
uri: `file://${filePath}`,
text: code,
languageId: detectLanguage(filePath)
}}
});
return sendRequest("getCompletions", {
doc: { uri: `file://${filePath}`, position }
});
};
// 使用方法:
const results = await runCopilot(
"/project/main.py",
"def example():\n return '",
{ line: 1, character: 14 }
);
console.log("上位3つのサジェスト:", results.items.slice(0, 3));
まとめ
GitHub Copilotのサジェストをプログラムから取得するには:
- 公式Language Serverを使用した方法が最も信頼性が高い
- Node.jsでLSPクライアントを実装し、
getCompletions
リクエストを送信 - 認証状態を事前に確立しておく
- 公式APIが公開されるまで代替手段として活用可能
- 生成されたコードの品質管理は依然として重要
実際のプロダクション環境で使用する場合は、法的観点からの検討と継続的なメンテナンス計画を推奨します。