diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 62f58c0..6069f9d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - chat types: - opened - synchronize diff --git a/docs/ai-chat-context-and-payload-strategy.md b/docs/ai-chat-context-and-payload-strategy.md index 72ca275..4ca4a5c 100644 --- a/docs/ai-chat-context-and-payload-strategy.md +++ b/docs/ai-chat-context-and-payload-strategy.md @@ -16,7 +16,7 @@ Each request includes a system prompt with policy guidance, then augments that p Primary implementation: -- src/modules/github/chat/payload.js +- src/modules/chat/payload.js ### 2. Repository context @@ -29,7 +29,7 @@ Each request includes repository targeting context as a dedicated system message Primary implementation: -- src/modules/github/chat/drawer.js +- src/modules/chat/drawer.js ### 3. Editor context (Send tab content) @@ -44,8 +44,8 @@ This context is designed to support dynamic proposal targeting by tab id/path an Primary implementation: -- src/modules/github/chat/active-tab-context.js -- src/modules/github/chat/drawer.js +- src/modules/chat/active-tab-context.js +- src/modules/chat/drawer.js ### 4. Tooling model @@ -62,9 +62,9 @@ Contract: Primary implementation: -- src/modules/github/chat/proposals.js -- src/modules/github/chat/tab-target-resolver.js -- src/modules/github/chat/drawer.js +- src/modules/chat/proposals.js +- src/modules/chat/tab-target-resolver.js +- src/modules/chat/drawer.js ### 5. Apply and undo behavior @@ -74,8 +74,8 @@ Primary implementation: Primary implementation: -- src/modules/github/chat/drawer.js -- src/modules/github/chat/tab-scoped-undo-state.js +- src/modules/chat/drawer.js +- src/modules/chat/tab-scoped-undo-state.js ### 6. Payload size controls and summary strategy @@ -88,7 +88,7 @@ The payload builder includes bounded-conversation controls: Primary implementation: -- src/modules/github/chat/payload.js +- src/modules/chat/payload.js ### 7. Fallback and transport behavior @@ -98,8 +98,8 @@ Primary implementation: Primary implementation: -- src/modules/github/chat/drawer.js -- src/modules/github/api/chat.js +- src/modules/chat/drawer.js +- src/modules/chat/api/completions.js ## Why this approach diff --git a/playwright/github-byot-ai.spec.ts b/playwright/github-byot-ai.spec.ts index 4dd64ee..ec587bd 100644 --- a/playwright/github-byot-ai.spec.ts +++ b/playwright/github-byot-ai.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test' -import { defaultGitHubChatModel } from '../src/modules/github/api/chat.js' +import { defaultChatModel } from '../src/modules/chat/api/completions.js' import type { ChatRequestBody, ChatRequestMessage } from './helpers/app-test-helpers.js' import { appEntryPath, @@ -867,7 +867,7 @@ test('AI chat prefers streaming responses when available', async ({ page }) => { await expect(page.getByText('Streaming response ready')).toBeVisible() expect(streamRequestBody?.metadata).toBeUndefined() - expect(streamRequestBody?.model).toBe(defaultGitHubChatModel) + expect(streamRequestBody?.model).toBe(defaultChatModel) expect(streamRequestBody?.tool_choice).toBe('auto') expect( streamRequestBody?.tools?.some( diff --git a/src/app.js b/src/app.js index 9ba30b4..b120a23 100644 --- a/src/app.js +++ b/src/app.js @@ -51,10 +51,10 @@ import { persistClosedPrContextRecords } from './modules/app-core/pr-context-rec import { createPrContextStateChangeHandler } from './modules/app-core/pr-context-state-change-handler.js' import { createWorkspaceContextStatusController } from './modules/app-core/workspace-context-status-controller.js' import { createWorkspaceRecordAppliedHandler } from './modules/app-core/workspace-record-applied-handler.js' -import { createGitHubChatWorkspaceActions } from './modules/app-core/github-chat-workspace-actions.js' +import { createChatWorkspaceActions } from './modules/chat/workspace-actions.js' import { createShareCurrentLocalWorkspace } from './modules/app-core/workspace-share-action.js' import { createDiagnosticsUiController } from './modules/diagnostics/diagnostics-ui.js' -import { createGitHubChatDrawer } from './modules/github/chat/drawer.js' +import { initializeChatWorkflows } from './modules/app-core/chat-workflows.js' import { createGitHubByotControls } from './modules/github/byot-controls.js' import { formatActivePrReference, @@ -1172,7 +1172,7 @@ const onPrContextStateChange = createPrContextStateChangeHandler({ editedIndicatorVisibilityController, }) -const githubChatWorkspaceActions = createGitHubChatWorkspaceActions({ +const githubChatWorkspaceActions = createChatWorkspaceActions({ getActiveWorkspaceTab, isStyleWorkspaceTab, getCssSource: () => getCssSource(), @@ -1187,7 +1187,6 @@ const githubChatWorkspaceActions = createGitHubChatWorkspaceActions({ const githubWorkflows = createGitHubWorkflowsSetup({ factories: { createGitHubPrEditorSyncController, - createGitHubChatDrawer, createGitHubPrDrawer, createWorkspacesDrawer, }, @@ -1210,17 +1209,6 @@ const githubWorkflows = createGitHubWorkflowsSetup({ byotControls.clearSelectedRepositoryPreference(), }, ui: { - aiChatToggle, - aiChatDrawer, - aiChatClose, - aiChatPrompt, - aiChatModel, - aiChatIncludeEditors, - aiChatSend, - aiChatClear, - aiChatStatus, - aiChatRepository, - aiChatMessages, githubPrToggle, githubPrDrawer, githubPrClose, @@ -1351,7 +1339,6 @@ const githubWorkflows = createGitHubWorkflowsSetup({ /* Save failures are already surfaced through saver onError. */ }) }, - getPersistedActivePrContext, getTokenForVisibility: () => githubAiContextState.token, getActivePrEditorSyncKey: () => githubAiContextState.activePrEditorSyncKey, syncFromActiveContext: ({ tabTargets }) => { @@ -1373,7 +1360,6 @@ const githubWorkflows = createGitHubWorkflowsSetup({ setStatus, showAppToast, shareCurrentLocalWorkspace, - ...githubChatWorkspaceActions, scheduleRender: () => { if ( autoRenderToggle?.checked && @@ -1385,7 +1371,35 @@ const githubWorkflows = createGitHubWorkflowsSetup({ }, }) -chatDrawerController = githubWorkflows.chatDrawerController +const chatWorkflows = initializeChatWorkflows({ + aiChatToggle, + aiChatDrawer, + aiChatClose, + aiChatPrompt, + aiChatModel, + aiChatIncludeEditors, + aiChatSend, + aiChatClear, + aiChatStatus, + aiChatRepository, + aiChatMessages, + getToken: getCurrentGitHubToken, + getSelectedRepository: getCurrentSelectedRepository, + ...githubChatWorkspaceActions, + getRenderMode: () => renderMode.value, + getStyleMode: () => styleMode.value, + getPersistedActivePrContext, + scheduleRender: () => { + if ( + autoRenderToggle?.checked && + typeof renderRuntime?.scheduleRender === 'function' + ) { + renderRuntime.scheduleRender() + } + }, +}) + +chatDrawerController = chatWorkflows.chatDrawerController prDrawerController = githubWorkflows.prDrawerController workspacesDrawerController = githubWorkflows.workspacesDrawerController diff --git a/src/modules/app-core/chat-workflows.js b/src/modules/app-core/chat-workflows.js new file mode 100644 index 0000000..29c92ae --- /dev/null +++ b/src/modules/app-core/chat-workflows.js @@ -0,0 +1,54 @@ +import { createChatDrawer } from '../chat/drawer.js' + +const initializeChatWorkflows = ({ + aiChatToggle, + aiChatDrawer, + aiChatClose, + aiChatPrompt, + aiChatModel, + aiChatIncludeEditors, + aiChatSend, + aiChatClear, + aiChatStatus, + aiChatRepository, + aiChatMessages, + getToken, + getSelectedRepository, + getActiveWorkspaceTabContext, + getWorkspaceTabContexts, + applyWorkspaceTabContent, + scheduleRender, + getRenderMode, + getStyleMode, + getPersistedActivePrContext, +}) => { + const chatDrawerController = createChatDrawer({ + toggleButton: aiChatToggle, + drawer: aiChatDrawer, + closeButton: aiChatClose, + promptInput: aiChatPrompt, + modelSelect: aiChatModel, + includeEditorsContextToggle: aiChatIncludeEditors, + sendButton: aiChatSend, + clearButton: aiChatClear, + statusNode: aiChatStatus, + repositoryNode: aiChatRepository, + messagesNode: aiChatMessages, + getToken, + getSelectedRepository, + getActiveWorkspaceTabContext, + getWorkspaceTabContexts, + applyWorkspaceTabContent, + scheduleRender, + getRenderMode, + getStyleMode, + getDrawerSide: () => { + return 'right' + }, + getPersistedActivePrContext, + }) + + return { chatDrawerController } +} + +export { initializeChatWorkflows } diff --git a/src/modules/app-core/github-workflows-setup.js b/src/modules/app-core/github-workflows-setup.js index 2edadb2..b4c39a7 100644 --- a/src/modules/app-core/github-workflows-setup.js +++ b/src/modules/app-core/github-workflows-setup.js @@ -35,7 +35,6 @@ const createGitHubWorkflowsSetup = ({ getEditorSyncTargets: workspace.getEditorSyncTargets, getRenderMode: runtime.getRenderMode, getStyleMode: runtime.getStyleMode, - getPersistedActivePrContext: runtime.getPersistedActivePrContext, setCurrentSelectedRepository: byot.setCurrentSelectedRepository, clearCurrentSelectedRepository: byot.clearCurrentSelectedRepository, reconcileWorkspaceTabsWithPushUpdates: @@ -56,9 +55,6 @@ const createGitHubWorkflowsSetup = ({ setStatus: actions.setStatus, showAppToast: actions.showAppToast, shareCurrentLocalWorkspace: actions.shareCurrentLocalWorkspace, - getActiveWorkspaceTabContext: actions.getActiveWorkspaceTabContext, - getWorkspaceTabContexts: actions.getWorkspaceTabContexts, - applyWorkspaceTabContent: actions.applyWorkspaceTabContent, scheduleRender: actions.scheduleRender, applyWorkspaceFontCssUrl: workspace.applyWorkspaceFontCssUrl, }) diff --git a/src/modules/app-core/github-workflows.js b/src/modules/app-core/github-workflows.js index 482f158..dd21b5d 100644 --- a/src/modules/app-core/github-workflows.js +++ b/src/modules/app-core/github-workflows.js @@ -2,7 +2,6 @@ import { toWorkspaceRecordKey } from '../workspace/workspace-tab-helpers.js' const initializeGitHubWorkflows = ({ createGitHubPrEditorSyncController, - createGitHubChatDrawer, createGitHubPrDrawer, createWorkspacesDrawer, ensureJsxTransformSource, @@ -13,17 +12,6 @@ const initializeGitHubWorkflows = ({ byotControls, getCurrentGitHubToken, getCurrentSelectedRepository, - aiChatToggle, - aiChatDrawer, - aiChatClose, - aiChatPrompt, - aiChatModel, - aiChatIncludeEditors, - aiChatSend, - aiChatClear, - aiChatStatus, - aiChatRepository, - aiChatMessages, githubPrToggle, githubPrDrawer, githubPrClose, @@ -71,7 +59,6 @@ const initializeGitHubWorkflows = ({ getStyleMode, setCurrentSelectedRepository, clearCurrentSelectedRepository, - getPersistedActivePrContext, reconcileWorkspaceTabsWithPushUpdates, getActivePrContextSyncKey, prContextUi, @@ -89,9 +76,6 @@ const initializeGitHubWorkflows = ({ setStatus, showAppToast, shareCurrentLocalWorkspace, - getActiveWorkspaceTabContext, - getWorkspaceTabContexts, - applyWorkspaceTabContent, scheduleRender, }) => { const getCurrentWritableRepositories = () => @@ -192,32 +176,6 @@ const initializeGitHubWorkflows = ({ shouldApplySyncResult: shouldApplyActivePrEditorSync, }) - const chatDrawerController = createGitHubChatDrawer({ - toggleButton: aiChatToggle, - drawer: aiChatDrawer, - closeButton: aiChatClose, - promptInput: aiChatPrompt, - modelSelect: aiChatModel, - includeEditorsContextToggle: aiChatIncludeEditors, - sendButton: aiChatSend, - clearButton: aiChatClear, - statusNode: aiChatStatus, - repositoryNode: aiChatRepository, - messagesNode: aiChatMessages, - getToken: getCurrentGitHubToken, - getSelectedRepository: getCurrentSelectedRepository, - getActiveWorkspaceTabContext, - getWorkspaceTabContexts, - applyWorkspaceTabContent, - scheduleRender, - getRenderMode, - getStyleMode, - getDrawerSide: () => { - return 'right' - }, - getPersistedActivePrContext, - }) - const prDrawerController = createGitHubPrDrawer({ toggleButton: githubPrToggle, drawer: githubPrDrawer, @@ -792,7 +750,6 @@ const initializeGitHubWorkflows = ({ }) return { - chatDrawerController, prDrawerController, workspacesDrawerController, } diff --git a/src/modules/github/chat/active-tab-context.js b/src/modules/chat/active-tab-context.js similarity index 100% rename from src/modules/github/chat/active-tab-context.js rename to src/modules/chat/active-tab-context.js diff --git a/src/modules/github/api/chat.js b/src/modules/chat/api/completions.js similarity index 93% rename from src/modules/github/api/chat.js rename to src/modules/chat/api/completions.js index c0037f5..4d1eb21 100644 --- a/src/modules/github/api/chat.js +++ b/src/modules/chat/api/completions.js @@ -1,14 +1,10 @@ -import { - defaultGitHubChatModel, - githubChatModelOptions, - githubModelsApiUrl, -} from './constants.js' +import { chatCompletionsUrl, chatModelOptions, defaultChatModel } from './constants.js' import { buildChatRequestHeaders, parseErrorResponse, parseRateMetadata, toApiError, -} from './core.js' +} from './request.js' const normalizeChatMessage = message => { if (!message || typeof message !== 'object') { @@ -278,17 +274,17 @@ const parseSseDataLine = line => { } } -const streamGitHubChatCompletion = async ({ +const streamChatCompletion = async ({ token, messages, signal, onToken, - model = defaultGitHubChatModel, + model = defaultChatModel, tools, toolChoice, }) => { if (typeof token !== 'string' || token.trim().length === 0) { - throw new Error('A GitHub token is required to start a chat request.') + throw new Error('An API key is required to start a chat request.') } const normalizedMessages = normalizeChatMessages(messages) @@ -296,7 +292,7 @@ const streamGitHubChatCompletion = async ({ throw new Error('At least one message is required to start a chat request.') } - const response = await fetch(githubModelsApiUrl, { + const response = await fetch(chatCompletionsUrl, { method: 'POST', headers: buildChatRequestHeaders({ token, stream: true }), body: JSON.stringify( @@ -396,16 +392,16 @@ const streamGitHubChatCompletion = async ({ } } -const requestGitHubChatCompletion = async ({ +const requestChatCompletion = async ({ token, messages, signal, - model = defaultGitHubChatModel, + model = defaultChatModel, tools, toolChoice, }) => { if (typeof token !== 'string' || token.trim().length === 0) { - throw new Error('A GitHub token is required to start a chat request.') + throw new Error('An API key is required to start a chat request.') } const normalizedMessages = normalizeChatMessages(messages) @@ -413,7 +409,7 @@ const requestGitHubChatCompletion = async ({ throw new Error('At least one message is required to start a chat request.') } - const response = await fetch(githubModelsApiUrl, { + const response = await fetch(chatCompletionsUrl, { method: 'POST', headers: buildChatRequestHeaders({ token, stream: false }), body: JSON.stringify( @@ -438,7 +434,7 @@ const requestGitHubChatCompletion = async ({ const toolCalls = extractChatCompletionToolCalls(body) if (!content && toolCalls.length === 0) { - throw new Error('GitHub chat response did not include assistant content.') + throw new Error('Chat response did not include assistant content.') } return { @@ -449,9 +445,4 @@ const requestGitHubChatCompletion = async ({ } } -export { - defaultGitHubChatModel, - githubChatModelOptions, - requestGitHubChatCompletion, - streamGitHubChatCompletion, -} +export { chatModelOptions, defaultChatModel, requestChatCompletion, streamChatCompletion } diff --git a/src/modules/chat/api/constants.js b/src/modules/chat/api/constants.js new file mode 100644 index 0000000..f12d9eb --- /dev/null +++ b/src/modules/chat/api/constants.js @@ -0,0 +1,23 @@ +export const chatCompletionsUrl = 'https://models.github.ai/inference/chat/completions' + +export const defaultChatModel = 'openai/gpt-4.1-mini' + +/* Local model options avoid browser CORS failures when calling catalog endpoints directly. */ +export const chatModelOptions = [ + 'openai/gpt-4.1-mini', + 'openai/gpt-4.1', + 'openai/gpt-4.1-nano', + 'openai/gpt-4o', + 'openai/gpt-4o-mini', + 'openai/gpt-5', + 'openai/gpt-5-chat', + 'openai/gpt-5-mini', + 'openai/gpt-5-nano', + 'cohere/cohere-command-r-plus-08-2024', + 'deepseek/deepseek-v3-0324', + 'meta/llama-4-maverick-17b-128e-instruct-fp8', + 'meta/llama-4-scout-17b-16e-instruct', + 'mistral-ai/ministral-3b', + 'mistral-ai/mistral-medium-2505', + 'mistral-ai/mistral-small-2503', +] diff --git a/src/modules/chat/api/request.js b/src/modules/chat/api/request.js new file mode 100644 index 0000000..28c4102 --- /dev/null +++ b/src/modules/chat/api/request.js @@ -0,0 +1,104 @@ +export const buildChatRequestHeaders = ({ token, stream }) => ({ + Accept: stream ? 'text/event-stream' : 'application/json', + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + 'X-GitHub-Api-Version': '2022-11-28', +}) + +const toFiniteNumber = value => { + if (value === null || value === undefined) { + return null + } + + if (typeof value === 'string' && value.trim().length === 0) { + return null + } + + const numberValue = Number(value) + return Number.isFinite(numberValue) ? numberValue : null +} + +const parseRateMetadataFromHeaders = headers => { + if (!headers || typeof headers.get !== 'function') { + return { + remaining: null, + resetEpochSeconds: null, + } + } + + const remaining = + toFiniteNumber(headers.get('x-ratelimit-remaining')) ?? + toFiniteNumber(headers.get('ratelimit-remaining')) + + const resetEpochSeconds = + toFiniteNumber(headers.get('x-ratelimit-reset')) ?? + toFiniteNumber(headers.get('ratelimit-reset')) + + return { + remaining, + resetEpochSeconds, + } +} + +const parseRateMetadataFromBody = body => { + if (!body || typeof body !== 'object') { + return { + remaining: null, + resetEpochSeconds: null, + } + } + + const rateLimit = body.rate_limit ?? body.rateLimit ?? null + + const remaining = + toFiniteNumber(rateLimit?.remaining) ?? toFiniteNumber(body.remaining) ?? null + + const resetEpochSeconds = + toFiniteNumber(rateLimit?.reset) ?? + toFiniteNumber(rateLimit?.reset_epoch_seconds) ?? + toFiniteNumber(rateLimit?.resetEpochSeconds) ?? + toFiniteNumber(body.reset) ?? + null + + return { + remaining, + resetEpochSeconds, + } +} + +const mergeRateMetadata = (primary, fallback) => ({ + remaining: primary.remaining ?? fallback.remaining ?? null, + resetEpochSeconds: primary.resetEpochSeconds ?? fallback.resetEpochSeconds ?? null, +}) + +export const parseRateMetadata = ({ headers, body }) => { + const fromHeaders = parseRateMetadataFromHeaders(headers) + const fromBody = parseRateMetadataFromBody(body) + return mergeRateMetadata(fromHeaders, fromBody) +} + +export const toApiError = ({ message, rateLimit }) => { + const error = new Error(message) + error.rateLimit = rateLimit + return error +} + +export const parseErrorResponse = async response => { + let body = null + + try { + body = await response.json() + } catch { + /* noop */ + } + + const message = + body && typeof body.message === 'string' && body.message.trim() + ? body.message + : `Chat API request failed with status ${response.status}` + + return { + message, + rateLimit: parseRateMetadata({ headers: response.headers, body }), + } +} diff --git a/src/modules/github/chat/drawer.js b/src/modules/chat/drawer.js similarity index 97% rename from src/modules/github/chat/drawer.js rename to src/modules/chat/drawer.js index 8727f67..52e3f3d 100644 --- a/src/modules/github/chat/drawer.js +++ b/src/modules/chat/drawer.js @@ -1,9 +1,9 @@ import { - defaultGitHubChatModel, - githubChatModelOptions, - requestGitHubChatCompletion, - streamGitHubChatCompletion, -} from '../api/chat.js' + chatModelOptions, + defaultChatModel, + requestChatCompletion, + streamChatCompletion, +} from './api/completions.js' import { formatModelAccessErrorMessage, isModelAccessError, @@ -48,7 +48,7 @@ const createMessageLabelIconTemplate = role => { return svg } -export const createGitHubChatDrawer = ({ +export const createChatDrawer = ({ toggleButton, drawer, closeButton, @@ -183,7 +183,7 @@ export const createGitHubChatDrawer = ({ } const nextSelectedModel = toModelId(selectedModel) - const nextModelIds = [...new Set([defaultGitHubChatModel, ...modelIds])] + const nextModelIds = [...new Set([defaultChatModel, ...modelIds])] modelSelect.replaceChildren() @@ -196,13 +196,13 @@ export const createGitHubChatDrawer = ({ } if (!nextModelIds.includes(nextSelectedModel)) { - modelSelect.value = defaultGitHubChatModel + modelSelect.value = defaultChatModel } } const getSelectedModel = () => { if (!(modelSelect instanceof HTMLSelectElement)) { - return defaultGitHubChatModel + return defaultChatModel } return toModelId(modelSelect.value) @@ -210,8 +210,8 @@ export const createGitHubChatDrawer = ({ const initializeModelOptions = () => { replaceModelOptions({ - modelIds: githubChatModelOptions, - selectedModel: defaultGitHubChatModel, + modelIds: chatModelOptions, + selectedModel: defaultChatModel, }) } @@ -221,7 +221,7 @@ export const createGitHubChatDrawer = ({ setModelSelectDisabled(!hasToken) if (!hasToken && modelSelect instanceof HTMLSelectElement) { - modelSelect.value = defaultGitHubChatModel + modelSelect.value = defaultChatModel } if (hasToken && isModelAccessStatusMessage(statusNode?.textContent)) { @@ -773,7 +773,7 @@ export const createGitHubChatDrawer = ({ let streamSucceeded = false try { - const streamResult = await streamGitHubChatCompletion({ + const streamResult = await streamChatCompletion({ token, messages: outboundMessages, model: selectedModel, @@ -838,7 +838,7 @@ export const createGitHubChatDrawer = ({ } try { - const fallbackResult = await requestGitHubChatCompletion({ + const fallbackResult = await requestChatCompletion({ token, messages: outboundMessages, model: selectedModel, diff --git a/src/modules/github/chat/payload.js b/src/modules/chat/payload.js similarity index 100% rename from src/modules/github/chat/payload.js rename to src/modules/chat/payload.js diff --git a/src/modules/github/chat/proposals.js b/src/modules/chat/proposals.js similarity index 100% rename from src/modules/github/chat/proposals.js rename to src/modules/chat/proposals.js diff --git a/src/modules/github/chat/tab-scoped-undo-state.js b/src/modules/chat/tab-scoped-undo-state.js similarity index 100% rename from src/modules/github/chat/tab-scoped-undo-state.js rename to src/modules/chat/tab-scoped-undo-state.js diff --git a/src/modules/github/chat/tab-target-resolver.js b/src/modules/chat/tab-target-resolver.js similarity index 100% rename from src/modules/github/chat/tab-target-resolver.js rename to src/modules/chat/tab-target-resolver.js diff --git a/src/modules/github/chat/utils.js b/src/modules/chat/utils.js similarity index 87% rename from src/modules/github/chat/utils.js rename to src/modules/chat/utils.js index dd39ebe..9ee64a3 100644 --- a/src/modules/github/chat/utils.js +++ b/src/modules/chat/utils.js @@ -1,4 +1,4 @@ -import { defaultGitHubChatModel } from '../api/chat.js' +import { defaultChatModel } from './api/completions.js' export const toChatText = value => { if (typeof value !== 'string') { @@ -10,11 +10,11 @@ export const toChatText = value => { export const toModelId = value => { if (typeof value !== 'string') { - return defaultGitHubChatModel + return defaultChatModel } const model = value.trim() - return model || defaultGitHubChatModel + return model || defaultChatModel } export const isModelAccessError = error => { @@ -35,7 +35,7 @@ export const isModelAccessError = error => { export const formatModelAccessErrorMessage = selectedModel => { const model = toModelId(selectedModel) - return `Selected model "${model}" is not available for this token. Choose a different model.` + return `Selected model "${model}" is not available for this key. Choose a different model.` } export const isModelAccessStatusMessage = value => { @@ -44,7 +44,7 @@ export const isModelAccessStatusMessage = value => { } return ( - value.startsWith('Selected model "') && value.includes('not available for this token') + value.startsWith('Selected model "') && value.includes('not available for this key') ) } diff --git a/src/modules/app-core/github-chat-workspace-actions.js b/src/modules/chat/workspace-actions.js similarity index 95% rename from src/modules/app-core/github-chat-workspace-actions.js rename to src/modules/chat/workspace-actions.js index 3a5613d..a7b7267 100644 --- a/src/modules/app-core/github-chat-workspace-actions.js +++ b/src/modules/chat/workspace-actions.js @@ -1,4 +1,4 @@ -const createGitHubChatWorkspaceActions = ({ +const createChatWorkspaceActions = ({ getActiveWorkspaceTab, isStyleWorkspaceTab, getCssSource, @@ -82,4 +82,4 @@ const createGitHubChatWorkspaceActions = ({ } } -export { createGitHubChatWorkspaceActions } +export { createChatWorkspaceActions } diff --git a/src/modules/github/api/constants.js b/src/modules/github/api/constants.js index c2d2684..fbe912d 100644 --- a/src/modules/github/api/constants.js +++ b/src/modules/github/api/constants.js @@ -1,24 +1 @@ export const githubApiBaseUrl = 'https://api.github.com' -export const githubModelsApiUrl = 'https://models.github.ai/inference/chat/completions' - -export const defaultGitHubChatModel = 'openai/gpt-4.1-mini' - -/* Local model options avoid browser CORS failures when calling catalog endpoints directly. */ -export const githubChatModelOptions = [ - 'openai/gpt-4.1-mini', - 'openai/gpt-4.1', - 'openai/gpt-4.1-nano', - 'openai/gpt-4o', - 'openai/gpt-4o-mini', - 'openai/gpt-5', - 'openai/gpt-5-chat', - 'openai/gpt-5-mini', - 'openai/gpt-5-nano', - 'cohere/cohere-command-r-plus-08-2024', - 'deepseek/deepseek-v3-0324', - 'meta/llama-4-maverick-17b-128e-instruct-fp8', - 'meta/llama-4-scout-17b-16e-instruct', - 'mistral-ai/ministral-3b', - 'mistral-ai/mistral-medium-2505', - 'mistral-ai/mistral-small-2503', -] diff --git a/src/modules/github/api/core.js b/src/modules/github/api/core.js index 27c3ddd..089bedc 100644 --- a/src/modules/github/api/core.js +++ b/src/modules/github/api/core.js @@ -39,13 +39,6 @@ const buildRequestHeaders = token => ({ 'X-GitHub-Api-Version': '2022-11-28', }) -const buildChatRequestHeaders = ({ token, stream }) => ({ - Accept: stream ? 'text/event-stream' : 'application/json', - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - 'X-GitHub-Api-Version': '2022-11-28', -}) - const toFiniteNumber = value => { if (value === null || value === undefined) { return null @@ -204,7 +197,6 @@ const buildRepoApiUrl = ({ owner, repo, path }) => `${githubApiBaseUrl}/repos/${owner}/${repo}${path}` export { - buildChatRequestHeaders, buildRepoApiUrl, buildRequestHeaders, encodePathForApi,