11import {
22 defaultGitHubChatModel ,
3+ githubChatModelOptions ,
34 requestGitHubChatCompletion ,
45 streamGitHubChatCompletion ,
56} from './github-api.js'
@@ -12,17 +13,34 @@ const toChatText = value => {
1213 return value . trim ( )
1314}
1415
15- const toModelLabelText = value => {
16+ const toModelId = value => {
1617 if ( typeof value !== 'string' ) {
17- return ''
18+ return defaultGitHubChatModel
1819 }
1920
2021 const model = value . trim ( )
21- if ( ! model ) {
22- return ''
22+ return model || defaultGitHubChatModel
23+ }
24+
25+ const isModelAccessError = error => {
26+ const message = error instanceof Error ? error . message . toLowerCase ( ) : ''
27+ if ( ! message ) {
28+ return false
2329 }
2430
25- return `(${ model } )`
31+ return (
32+ ( message . includes ( 'model' ) && message . includes ( 'access' ) ) ||
33+ ( message . includes ( 'model' ) && message . includes ( 'permission' ) ) ||
34+ ( message . includes ( 'model' ) && message . includes ( 'not available' ) ) ||
35+ ( message . includes ( 'model' ) && message . includes ( 'not found' ) ) ||
36+ ( message . includes ( 'model' ) && message . includes ( 'not enabled' ) ) ||
37+ ( message . includes ( 'forbidden' ) && message . includes ( 'model' ) )
38+ )
39+ }
40+
41+ const formatModelAccessErrorMessage = selectedModel => {
42+ const model = toModelId ( selectedModel )
43+ return `Selected model "${ model } " is not available for this token. Choose a different model.`
2644}
2745
2846const toRepositoryLabel = repository => {
@@ -59,6 +77,7 @@ export const createGitHubChatDrawer = ({
5977 drawer,
6078 closeButton,
6179 promptInput,
80+ modelSelect,
6281 sendButton,
6382 clearButton,
6483 statusNode,
@@ -82,6 +101,7 @@ export const createGitHubChatDrawer = ({
82101 setOpen : ( ) => { } ,
83102 isOpen : ( ) => false ,
84103 setSelectedRepository : ( ) => { } ,
104+ setToken : ( ) => { } ,
85105 dispose : ( ) => { } ,
86106 }
87107 }
@@ -140,6 +160,53 @@ export const createGitHubChatDrawer = ({
140160 pendingAbortController = null
141161 }
142162
163+ const setModelSelectDisabled = isDisabled => {
164+ if ( ! ( modelSelect instanceof HTMLSelectElement ) ) {
165+ return
166+ }
167+
168+ modelSelect . disabled = isDisabled
169+ }
170+
171+ const replaceModelOptions = ( { modelIds, selectedModel } ) => {
172+ if ( ! ( modelSelect instanceof HTMLSelectElement ) ) {
173+ return
174+ }
175+
176+ const nextSelectedModel = toModelId ( selectedModel )
177+ const nextModelIds = [ ...new Set ( [ defaultGitHubChatModel , ...modelIds ] ) ]
178+
179+ modelSelect . replaceChildren ( )
180+
181+ for ( const modelId of nextModelIds ) {
182+ const option = document . createElement ( 'option' )
183+ option . value = modelId
184+ option . textContent = modelId
185+ option . selected = modelId === nextSelectedModel
186+ modelSelect . append ( option )
187+ }
188+
189+ if ( ! nextModelIds . includes ( nextSelectedModel ) ) {
190+ modelSelect . value = defaultGitHubChatModel
191+ }
192+ }
193+
194+ const getSelectedModel = ( ) => {
195+ if ( ! ( modelSelect instanceof HTMLSelectElement ) ) {
196+ return defaultGitHubChatModel
197+ }
198+
199+ return toModelId ( modelSelect . value )
200+ }
201+
202+ const initializeModelOptions = ( ) => {
203+ replaceModelOptions ( {
204+ modelIds : githubChatModelOptions ,
205+ selectedModel : defaultGitHubChatModel ,
206+ } )
207+ setModelSelectDisabled ( false )
208+ }
209+
143210 const setOpen = nextOpen => {
144211 open = nextOpen === true
145212
@@ -238,16 +305,6 @@ export const createGitHubChatDrawer = ({
238305 label . className = 'ai-chat-message__label'
239306 label . textContent = message . role === 'assistant' ? 'Assistant' : 'You'
240307
241- if ( message . role === 'assistant' ) {
242- const modelLabelText = toModelLabelText ( message . model )
243- if ( modelLabelText ) {
244- const modelLabel = document . createElement ( 'span' )
245- modelLabel . className = 'ai-chat-message__label-model'
246- modelLabel . textContent = modelLabelText
247- label . append ( modelLabel )
248- }
249- }
250-
251308 item . append ( label )
252309
253310 const body = document . createElement ( 'p' )
@@ -367,6 +424,10 @@ export const createGitHubChatDrawer = ({
367424 if ( promptInput instanceof HTMLTextAreaElement ) {
368425 promptInput . disabled = isPending
369426 }
427+
428+ if ( modelSelect instanceof HTMLSelectElement ) {
429+ modelSelect . disabled = isPending
430+ }
370431 }
371432
372433 const runChatRequest = async ( ) => {
@@ -389,13 +450,15 @@ export const createGitHubChatDrawer = ({
389450 return
390451 }
391452
453+ const selectedModel = getSelectedModel ( )
454+
392455 stopPendingRequest ( )
393456 const requestAbortController = new AbortController ( )
394457 const requestSignal = requestAbortController . signal
395458 pendingAbortController = requestAbortController
396459
397460 appendMessage ( { role : 'user' , content : prompt } )
398- appendMessage ( { role : 'assistant' , content : '' , model : defaultGitHubChatModel } )
461+ appendMessage ( { role : 'assistant' , content : '' , model : selectedModel } )
399462 if ( promptInput instanceof HTMLTextAreaElement ) {
400463 promptInput . value = ''
401464 }
@@ -418,6 +481,7 @@ export const createGitHubChatDrawer = ({
418481 const streamResult = await streamGitHubChatCompletion ( {
419482 token,
420483 messages : outboundMessages ,
484+ model : selectedModel ,
421485 signal : requestSignal ,
422486 onToken : tokenChunk => {
423487 streamedContent += tokenChunk
@@ -447,6 +511,24 @@ export const createGitHubChatDrawer = ({
447511 return
448512 }
449513
514+ if ( isModelAccessError ( streamError ) ) {
515+ const modelAccessMessage = formatModelAccessErrorMessage ( selectedModel )
516+
517+ updateLastAssistantMessage ( modelAccessMessage )
518+ const lastMessage = messages [ messages . length - 1 ]
519+ if ( lastMessage ) {
520+ lastMessage . level = 'error'
521+ }
522+ renderMessages ( )
523+ setChatStatus ( modelAccessMessage , 'error' )
524+
525+ if ( pendingAbortController === requestAbortController ) {
526+ pendingAbortController = null
527+ setPendingState ( false )
528+ }
529+ return
530+ }
531+
450532 setChatStatus (
451533 'Streaming unavailable. Retrying with fallback response...' ,
452534 'pending' ,
@@ -465,6 +547,7 @@ export const createGitHubChatDrawer = ({
465547 const fallbackResult = await requestGitHubChatCompletion ( {
466548 token,
467549 messages : outboundMessages ,
550+ model : selectedModel ,
468551 signal : requestSignal ,
469552 } )
470553
@@ -487,8 +570,11 @@ export const createGitHubChatDrawer = ({
487570 return
488571 }
489572
490- const fallbackMessage =
491- fallbackError instanceof Error ? fallbackError . message : 'Chat request failed.'
573+ const fallbackMessage = isModelAccessError ( fallbackError )
574+ ? formatModelAccessErrorMessage ( selectedModel )
575+ : fallbackError instanceof Error
576+ ? fallbackError . message
577+ : 'Chat request failed.'
492578
493579 setRateMetadata ( fallbackError ?. rateLimit )
494580
@@ -509,6 +595,7 @@ export const createGitHubChatDrawer = ({
509595
510596 toggleButton ?. setAttribute ( 'aria-expanded' , 'false' )
511597 drawer ?. setAttribute ( 'hidden' , '' )
598+ initializeModelOptions ( )
512599 syncRepositoryLabel ( )
513600 renderMessages ( )
514601 setChatStatus ( 'Idle' , 'neutral' )
@@ -560,6 +647,7 @@ export const createGitHubChatDrawer = ({
560647 setSelectedRepository : ( ) => {
561648 syncRepositoryLabel ( )
562649 } ,
650+ setToken : ( ) => { } ,
563651 dispose : ( ) => {
564652 stopPendingRequest ( )
565653 setPendingState ( false )
0 commit comments