Skip to content

Commit e97181d

Browse files
refactor: address comments.
1 parent df65da8 commit e97181d

4 files changed

Lines changed: 56 additions & 5 deletions

File tree

playwright/app.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect, test } from '@playwright/test'
22
import type { Page } from '@playwright/test'
3+
import { defaultGitHubChatModel } from '../src/modules/github-api.js'
34

45
const webServerMode = process.env.PLAYWRIGHT_WEB_SERVER_MODE ?? 'dev'
56
const appEntryPath = webServerMode === 'preview' ? '/index.html' : '/src/index.html'
@@ -12,6 +13,8 @@ type ChatRequestMessage = {
1213
type ChatRequestBody = {
1314
metadata?: unknown
1415
messages?: ChatRequestMessage[]
16+
model?: string
17+
stream?: boolean
1518
}
1619

1720
const waitForAppReady = async (page: Page, path = appEntryPath) => {
@@ -256,6 +259,7 @@ test('AI chat prefers streaming responses when available', async ({ page }) => {
256259
)
257260

258261
expect(streamRequestBody?.metadata).toBeUndefined()
262+
expect(streamRequestBody?.model).toBe(defaultGitHubChatModel)
259263
const systemMessage = streamRequestBody?.messages?.find(
260264
(message: ChatRequestMessage) => message.role === 'system',
261265
)
@@ -335,9 +339,14 @@ test('AI chat falls back to non-streaming response when streaming fails', async
335339
}) => {
336340
let streamAttemptCount = 0
337341
let fallbackAttemptCount = 0
342+
const attemptedModels: string[] = []
338343

339344
await page.route('https://models.github.ai/inference/chat/completions', async route => {
340-
const body = route.request().postDataJSON() as { stream?: boolean } | null
345+
const body = route.request().postDataJSON() as ChatRequestBody | null
346+
if (typeof body?.model === 'string') {
347+
attemptedModels.push(body.model)
348+
}
349+
341350
if (body?.stream) {
342351
streamAttemptCount += 1
343352
await route.fulfill({
@@ -373,6 +382,10 @@ test('AI chat falls back to non-streaming response when streaming fails', async
373382
await connectByotWithSingleRepo(page)
374383
await ensureAiChatDrawerOpen(page)
375384

385+
const selectedModel = 'openai/gpt-5-mini'
386+
await page.locator('#ai-chat-model').selectOption(selectedModel)
387+
await expect(page.locator('#ai-chat-model')).toHaveValue(selectedModel)
388+
376389
await page.locator('#ai-chat-prompt').fill('Use fallback path.')
377390
await page.locator('#ai-chat-send').click()
378391

@@ -383,6 +396,8 @@ test('AI chat falls back to non-streaming response when streaming fails', async
383396
)
384397
expect(streamAttemptCount).toBeGreaterThan(0)
385398
expect(fallbackAttemptCount).toBeGreaterThan(0)
399+
expect(attemptedModels.length).toBeGreaterThan(0)
400+
expect(attemptedModels.every(model => model === selectedModel)).toBe(true)
386401
})
387402

388403
test('BYOT remembers selected repository across reloads', async ({ page }) => {

src/modules/github-chat-drawer.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ const formatModelAccessErrorMessage = selectedModel => {
4343
return `Selected model "${model}" is not available for this token. Choose a different model.`
4444
}
4545

46+
const isModelAccessStatusMessage = value => {
47+
if (typeof value !== 'string') {
48+
return false
49+
}
50+
51+
return (
52+
value.startsWith('Selected model "') && value.includes('not available for this token')
53+
)
54+
}
55+
4656
const toRepositoryLabel = repository => {
4757
if (!repository || typeof repository !== 'object') {
4858
return 'No repository selected'
@@ -204,7 +214,20 @@ export const createGitHubChatDrawer = ({
204214
modelIds: githubChatModelOptions,
205215
selectedModel: defaultGitHubChatModel,
206216
})
207-
setModelSelectDisabled(false)
217+
}
218+
219+
const syncModelSelectionForToken = token => {
220+
const hasToken = typeof token === 'string' && token.trim().length > 0
221+
222+
setModelSelectDisabled(!hasToken)
223+
224+
if (!hasToken && modelSelect instanceof HTMLSelectElement) {
225+
modelSelect.value = defaultGitHubChatModel
226+
}
227+
228+
if (hasToken && isModelAccessStatusMessage(statusNode?.textContent)) {
229+
setChatStatus('Idle', 'neutral')
230+
}
208231
}
209232

210233
const setOpen = nextOpen => {
@@ -426,7 +449,13 @@ export const createGitHubChatDrawer = ({
426449
}
427450

428451
if (modelSelect instanceof HTMLSelectElement) {
429-
modelSelect.disabled = isPending
452+
if (isPending) {
453+
modelSelect.disabled = true
454+
} else {
455+
const token = getToken?.()
456+
const hasToken = typeof token === 'string' && token.trim().length > 0
457+
modelSelect.disabled = !hasToken
458+
}
430459
}
431460
}
432461

@@ -596,6 +625,7 @@ export const createGitHubChatDrawer = ({
596625
toggleButton?.setAttribute('aria-expanded', 'false')
597626
drawer?.setAttribute('hidden', '')
598627
initializeModelOptions()
628+
syncModelSelectionForToken(getToken?.())
599629
syncRepositoryLabel()
600630
renderMessages()
601631
setChatStatus('Idle', 'neutral')
@@ -647,7 +677,9 @@ export const createGitHubChatDrawer = ({
647677
setSelectedRepository: () => {
648678
syncRepositoryLabel()
649679
},
650-
setToken: () => {},
680+
setToken: token => {
681+
syncModelSelectionForToken(token)
682+
},
651683
dispose: () => {
652684
stopPendingRequest()
653685
setPendingState(false)

src/styles/ai-controls.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,11 @@
358358
border: 1px solid var(--border-control);
359359
border-radius: 9px;
360360
background: var(--surface-select);
361+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='currentColor' d='M1.2 0 5 3.8 8.8 0 10 1.2 5 6 0 1.2z'/%3E%3C/svg%3E");
362+
background-repeat: no-repeat;
363+
background-position: right 10px center;
361364
color: var(--select-text);
362-
padding: 6px 10px;
365+
padding: 6px 30px 6px 10px;
363366
font-size: 0.8rem;
364367
font-family: inherit;
365368
color-scheme: var(--control-color-scheme);

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"moduleResolution": "NodeNext",
77
"strict": true,
88
"noEmit": true,
9+
"allowJs": true,
910
"types": ["node", "@playwright/test"]
1011
},
1112
"include": ["playwright/**/*.ts", "playwright.config.ts"]

0 commit comments

Comments
 (0)