嵌入 Copilot SDK¶
本導覽說明 FastAPI 應用程式如何嵌入 GitHub Copilot SDK,並將 Copilot 工作階段轉換為應用程式服務。您將了解應用程式如何啟動 SDK 用戶端、建立串流工作階段、接聽工作階段事件、將回呼銜接至非同步產生器,以及避免使用過時的模型目錄。
SDK 的使用位置¶
主要整合點是 app/services/copilot_chat.py。它使用下列方式匯入 SDK:
PyPI 套件為 github-copilot-sdk,版本固定為 1.0.9,設定於 pyproject.toml,但匯入根命名空間是 copilot:
需要 Python 3.11 或更新版本。
單一長效服務¶
app/main.py 會建立一個在 FastAPI 應用程式生命週期內持續存在的聊天服務:
此服務採延遲連線。 ensure_started() 由 asyncio.Lock保護,因此並行 HTTP 要求不會競相啟動兩個傳輸連線:
啟動與關閉程序皆明確執行:
SDK 也支援 async with CopilotClient(),適合短期執行的指令碼;實作範例會在用戶端僅存續一個命令時採用這種形式。
建立串流工作階段¶
CopilotChatService.chat_stream() 會為每個提示建立新的 SDK 工作階段:
session = await self._client.create_session(
model=model,
streaming=True,
system_message=(
{"mode": "append", "content": system_message} if system_message else None
),
)
create_session(...) 僅接受關鍵字引數。重要參數包括 model、 streaming、 system_message、 tools、 mcp_servers、 session_id,以及 on_permission_request。
system_message 是 TypedDict 聯集型別:
此示範使用附加模式,以便加入應用程式內容,而不取代 Copilot 的基礎行為。
⚠️ Python 自訂工具需要 on_permission_request,否則呼叫會遭拒絕。對於相同的簡易工具流程,.NET 範例不需要處理常式。Python 也不需要 GHCP001 實驗性 API 抑制設定,即可使用來自 copilot.rpc 的權限決策。
工作階段事件¶
核心架構重點:Python SDK 事件是 僅限推送的回呼。 session.on(handler) 會註冊處理常式並傳回可呼叫的取消訂閱函式;它不提供非同步迭代器。
與 C# SDK 不同,Python 僅公開 一個 SessionEvent 資料類別。您要依據 evt.type(一個 SessionEventType 列舉)進行分支,而不是為每種事件比對一個子類別。以下是實際的處理常式:
def on_event(evt: SessionEvent) -> None:
# Unlike .NET, every event arrives as one SessionEvent
# carrying a `type` enum and a `data` payload, so this
# dispatches on `evt.type` rather than on subclasses.
if evt.type is SessionEventType.ASSISTANT_MESSAGE_DELTA:
queue.put_nowait(evt.data.delta_content or "")
elif evt.type is SessionEventType.ASSISTANT_MESSAGE:
logger.info(
"Assistant response complete: %d chars",
len(evt.data.content or ""),
)
elif evt.type is SessionEventType.SESSION_IDLE:
if not done.done():
done.set_result(None)
elif evt.type is SessionEventType.SESSION_ERROR:
logger.error("Session error: %s", evt.data.message)
if not done.done():
done.set_exception(RuntimeError(evt.data.message))
ASSISTANT_MESSAGE_DELTA 會攜帶串流文字, ASSISTANT_MESSAGE 代表完整回答, SESSION_IDLE 會完成本輪等候,而 SESSION_ERROR 則會回報失敗。
將回呼銜接至非同步產生器¶
FastAPI 串流需要非同步迭代器,但 SDK 呼叫的是處理常式。此服務會將回呼銜接至 asyncio.Queue,再由 chat_stream() 取出內容:
item = await queue.get()
if item is _DONE:
break
if isinstance(item, BaseException):
raise item
yield item # type: ignore[misc]
_DONE 是哨兵物件,而不是內容值:
之所以需要佇列,是因為事件回呼無法對 HTTP 回應 yield。這直接對應到 C# 服務寫入 System.Threading.Channels 通道的做法。
重新連線行為¶
如果 SDK 傳輸連線中斷,服務會將用戶端標示為狀態異常,並透過佇列傳遞例外狀況:
except (ConnectionError, OSError) as ex:
logger.warning("Copilot connection lost: %s", ex)
self._is_started = False
queue.put_nowait(ex)
目前的要求會失敗,而下一個要求會再次呼叫 ensure_started()。由於 _is_started 為 false,因此會停止舊用戶端並建立新的傳輸連線。
列出目前可用的模型¶
CopilotChatService.list_models() 會向已連線的 Copilot CLI 查詢登入帳戶可使用哪些模型:
models = await self._client.list_models()
return [(m.id, m.name or m.id) for m in models or [] if m.id]
SDK 會傳回 ModelInfo 物件,其中包含 .id 與 .name。將模型 ID 寫死會造成問題,因為可用性會因帳戶和推出階段而異。
目前有一個已知的上游錯誤: list_models() 可能引發 ValueError: Missing required field 'multiplier' in ModelBilling (github/copilot-sdk#1302)。 app/routers/chat.py 會廣泛攔截例外狀況,並改用包含六個模型的靜態目錄:
except Exception:
# Includes the known SDK issue where ModelBilling is missing the
# required 'multiplier' field: github/copilot-sdk#1302.
logger.warning(
"Could not list models from Copilot CLI; using static catalog", exc_info=True
)
return list(AVAILABLE_MODELS.values())
如此一來,即使即時探索功能暫時失效,模型選擇器仍可使用。