Có một kiểu lỗi rất khó chịu trong các bundle minify: mọi thứ vẫn chạy, nhưng một guard nội bộ lại lặng lẽ lọc mất dữ liệu bạn cần. Với Gemini trong GitHub.copilot-chat của VS Code Insiders, chuyện đó xảy ra ở đúng chỗ này: extension vẫn gọi Google Discovery API, nhưng kết quả lại bị giữ lại chỉ khi khớp với _knownModels nội bộ.
Điểm thú vị là: tên biến thay đổi mỗi lần update, nhưng cấu trúc logic thì khá ổn định. Vậy nên cách sửa bền nhất không phải là nhớ a, c, l, s hôm nay là gì, mà là tìm một trigger cấu trúc đủ ổn để nhận ra đúng vùng cần vá.
Vấn đề thật sự nằm ở đâu?
Gemini provider trong Copilot Chat có flow rất hợp lý ở bề mặt:
- gọi
models.list()từ Google AI - duyệt danh sách model trả về
- build metadata để đưa vào picker
Nhưng bước 3 lại bị chặn bởi một guard kiểu:
l&&this._knownModels&&this._knownModels[l]&&(s[l]=this._knownModels[l])
Ý nghĩa của nó khá thẳng:
- nếu model không nằm trong
_knownModels, nó bị bỏ qua - nếu Google trả về model mới, UI vẫn không thấy
- nếu biến minify đổi tên sau mỗi lần update, patch thủ công sẽ vỡ ngay
Cho nên bài toán không còn là “sửa một đoạn code”, mà là “định nghĩa một trigger đủ bền để nhận ra đúng đoạn code đó dù bundle đã đổi tên biến”.
Trigger mình dùng là gì?
Mình không bám vào tên biến. Mình bám vào ba lớp ổn định:
1) Anchor của provider
Tìm đúng provider Gemini bằng chuỗi ổn định:
static{this.providerName="Gemini"}
Cái này hiếm khi đổi, vì nó là dấu hiệu định danh của provider chứ không phải detail của logic.
2) Boundary của vùng cần vá
Ngay sau đó, giới hạn vùng tìm kiếm trước async provideLanguageModelChatResponse.
Điều này giúp tránh đụng sang provider khác trong bundle.
3) Mẫu hành vi cần thay thế
Trong vùng Gemini, mình tìm pattern của vòng lặp xử lý model discovery:
- có
for await (...) - đọc
name - check
_knownModels - rồi return bằng hàm build map cuối cùng
Tức là mình match hành vi, không match tên biến.
Đó là lý do update mới vẫn vá được: a, c, l, s, VEn, nIn có thể đổi, nhưng cấu trúc “iterate → filter known models → return map” vẫn còn.
Patch đúng nghĩa là gì?
Logic mình muốn giữ rất đơn giản:
- nếu model đã có metadata trong
_knownModels, dùng lại - nếu chưa có, tự tạo metadata từ response của Discovery API
- lưu lại vào
_knownModels - đưa toàn bộ model hợp lệ vào picker
Pseudo-flow sẽ giống như sau:
for await (let model of models) {
const name = model.name;
if (!name) continue;
let meta = this._knownModels?.[name];
if (!meta) {
meta = {
name: model.displayName ?? name,
toolCalling: true,
vision: true,
maxInputTokens: model.inputTokenLimit ?? 1048576,
maxOutputTokens: model.outputTokenLimit ?? 65536,
};
this._knownModels ??= {};
this._knownModels[name] = meta;
}
result[name] = meta;
}
Điểm cốt lõi ở đây là: không inject model thủ công. Nếu bạn inject tay một fallback kiểu gemini-3.5-flash, bạn sẽ tự tạo ra duplicate hoặc metadata lệch với backend.
Mục tiêu là để Discovery API tự quyết định model nào có mặt, còn patch chỉ gỡ lớp lọc cứng ở phía client.
Từ one-off patch sang auto-patch
Nếu chỉ patch một lần thì tìm đúng chỗ thủ công vẫn được. Nhưng khi VS Code Insiders update, bundle sẽ đổi, tên biến sẽ đổi, và copy-paste trực tiếp sẽ trở thành việc lặp lại khó chịu.
Vì vậy mình đóng gói nó thành một script tự vá.
Flow của script rất ngắn:
- đọc
extension.js - tìm anchor Gemini
- khoanh vùng đúng provider
- match loop theo cấu trúc
- sinh lại patch theo tên biến hiện tại của bundle
- backup trước khi ghi
- nếu đã patch rồi thì thoát an toàn
Nói cách khác, script không cố “nhớ” tên biến cũ. Nó chỉ cần biết:
- Gemini provider nằm ở đâu
- đoạn nào là model discovery loop
- đoạn nào là filter cũ cần thay
Đó mới là trigger bền.
Vì sao cách này bền hơn patch tay?
Patch tay
- nhanh lúc đầu
- dễ vỡ sau mỗi update
- phụ thuộc vào tên biến minify
- phải mở LLM hoặc tự đọc bundle mỗi lần
Auto-patch bằng trigger cấu trúc
- chạy lại được sau update
- ít phụ thuộc vào output minify
- có backup trước khi ghi
- có thể chạy dry-run để kiểm tra trước
- giảm đáng kể công việc lặp lại
Nếu nhìn theo góc độ vận hành, đây là một bài toán “đóng gói tri thức patch thành công cụ”, thay vì “ghi nhớ từng bundle version”.
Cách mình sẽ dùng sau mỗi lần Insiders update
node scripts/patch-vscode-insiders-gemini.mjs --dry-run
node scripts/patch-vscode-insiders-gemini.mjs
Sau đó:
- quit hẳn VS Code Insiders
- mở lại app
- mở Copilot Chat
- kiểm tra model picker của Gemini
Nếu model Discovery API trả về đủ quyền, picker sẽ hiện đầy đủ model thay vì bị khóa bởi whitelist nội bộ.
Bài học rút ra
Có hai kiểu trigger khi làm việc với bundle minify:
- trigger theo tên biến → mong manh
- trigger theo cấu trúc hành vi → bền hơn nhiều
Ở bài này, thứ quan trọng không phải là a hay l. Thứ quan trọng là nhận ra pattern: provider Gemini luôn đi qua một vòng discovery, và vòng đó luôn có một lớp lọc có thể bị thay thế.
Khi bạn tìm đúng invariant như vậy, việc update sau này chỉ còn là chạy lại script.
Kết luận
Nếu bạn đang patch một bundle thay đổi liên tục, đừng khóa mình vào tên biến minify. Hãy khóa vào:
- anchor định danh của provider
- boundary của vùng logic
- pattern hành vi của đoạn cần sửa
Với Gemini trong VS Code Insiders, chỉ cần đúng trigger đó là đủ để tự động vá lại và nhận toàn bộ model từ Google AI sau mỗi lần update.