mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
feat: add Azure AI models support in context and API handling
This commit is contained in:
@@ -754,6 +754,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
this.postMessageToWebview({ type: "openAiModels", openAiModels })
|
this.postMessageToWebview({ type: "openAiModels", openAiModels })
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case "refreshAzureAiModels":
|
||||||
|
if (message?.values?.endpoint && message?.values?.key) {
|
||||||
|
const azureAiModels = await this.getAzureAiModels(
|
||||||
|
message?.values?.endpoint,
|
||||||
|
message?.values?.key,
|
||||||
|
)
|
||||||
|
this.postMessageToWebview({ type: "azureAiModels", azureAiModels })
|
||||||
|
}
|
||||||
|
break
|
||||||
case "openImage":
|
case "openImage":
|
||||||
openImage(message.text!)
|
openImage(message.text!)
|
||||||
break
|
break
|
||||||
@@ -1645,6 +1654,28 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Azure AI
|
||||||
|
|
||||||
|
async getAzureAiModels(endpoint?: string, key?: string) {
|
||||||
|
try {
|
||||||
|
if (!endpoint || !key) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!URL.canParse(endpoint)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.get(`${endpoint}/deployments`, {
|
||||||
|
headers: { "api-key": key },
|
||||||
|
})
|
||||||
|
const modelsArray = response.data?.value?.map((deployment: any) => deployment.name) || []
|
||||||
|
const models = [...new Set<string>(modelsArray)]
|
||||||
|
return models
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
// OpenRouter
|
// OpenRouter
|
||||||
|
|
||||||
async handleOpenRouterCallback(code: string) {
|
async handleOpenRouterCallback(code: string) {
|
||||||
|
|||||||
@@ -7,220 +7,223 @@ import { CustomSupportPrompts } from "./support-prompt"
|
|||||||
import { ExperimentId } from "./experiments"
|
import { ExperimentId } from "./experiments"
|
||||||
|
|
||||||
export interface LanguageModelChatSelector {
|
export interface LanguageModelChatSelector {
|
||||||
vendor?: string
|
vendor?: string
|
||||||
family?: string
|
family?: string
|
||||||
version?: string
|
version?: string
|
||||||
id?: string
|
id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionMessage {
|
export interface ExtensionMessage {
|
||||||
type:
|
type:
|
||||||
| "action"
|
| "action"
|
||||||
| "state"
|
| "state"
|
||||||
| "selectedImages"
|
| "selectedImages"
|
||||||
| "ollamaModels"
|
| "ollamaModels"
|
||||||
| "lmStudioModels"
|
| "lmStudioModels"
|
||||||
| "theme"
|
| "theme"
|
||||||
| "workspaceUpdated"
|
| "workspaceUpdated"
|
||||||
| "invoke"
|
| "invoke"
|
||||||
| "partialMessage"
|
| "partialMessage"
|
||||||
| "glamaModels"
|
| "glamaModels"
|
||||||
| "openRouterModels"
|
| "openRouterModels"
|
||||||
| "openAiModels"
|
| "openAiModels"
|
||||||
| "mcpServers"
|
| "refreshAzureAiModels"
|
||||||
| "enhancedPrompt"
|
| "azureAiModels"
|
||||||
| "commitSearchResults"
|
| "mcpServers"
|
||||||
| "listApiConfig"
|
| "enhancedPrompt"
|
||||||
| "vsCodeLmModels"
|
| "commitSearchResults"
|
||||||
| "vsCodeLmApiAvailable"
|
| "listApiConfig"
|
||||||
| "requestVsCodeLmModels"
|
| "vsCodeLmModels"
|
||||||
| "updatePrompt"
|
| "vsCodeLmApiAvailable"
|
||||||
| "systemPrompt"
|
| "requestVsCodeLmModels"
|
||||||
| "autoApprovalEnabled"
|
| "updatePrompt"
|
||||||
| "updateCustomMode"
|
| "systemPrompt"
|
||||||
| "deleteCustomMode"
|
| "autoApprovalEnabled"
|
||||||
text?: string
|
| "updateCustomMode"
|
||||||
action?:
|
| "deleteCustomMode"
|
||||||
| "chatButtonClicked"
|
text?: string
|
||||||
| "mcpButtonClicked"
|
action?:
|
||||||
| "settingsButtonClicked"
|
| "chatButtonClicked"
|
||||||
| "historyButtonClicked"
|
| "mcpButtonClicked"
|
||||||
| "promptsButtonClicked"
|
| "settingsButtonClicked"
|
||||||
| "didBecomeVisible"
|
| "historyButtonClicked"
|
||||||
invoke?: "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" | "setChatBoxMessage"
|
| "promptsButtonClicked"
|
||||||
state?: ExtensionState
|
| "didBecomeVisible"
|
||||||
images?: string[]
|
invoke?: "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" | "setChatBoxMessage"
|
||||||
ollamaModels?: string[]
|
state?: ExtensionState
|
||||||
lmStudioModels?: string[]
|
images?: string[]
|
||||||
vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
|
ollamaModels?: string[]
|
||||||
filePaths?: string[]
|
lmStudioModels?: string[]
|
||||||
openedTabs?: Array<{
|
vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
|
||||||
label: string
|
filePaths?: string[]
|
||||||
isActive: boolean
|
openedTabs?: Array<{
|
||||||
path?: string
|
label: string
|
||||||
}>
|
isActive: boolean
|
||||||
partialMessage?: ClineMessage
|
path?: string
|
||||||
glamaModels?: Record<string, ModelInfo>
|
}>
|
||||||
openRouterModels?: Record<string, ModelInfo>
|
partialMessage?: ClineMessage
|
||||||
openAiModels?: string[]
|
glamaModels?: Record<string, ModelInfo>
|
||||||
mcpServers?: McpServer[]
|
openRouterModels?: Record<string, ModelInfo>
|
||||||
commits?: GitCommit[]
|
openAiModels?: string[]
|
||||||
listApiConfig?: ApiConfigMeta[]
|
azureAiModels?: string[]
|
||||||
mode?: Mode
|
mcpServers?: McpServer[]
|
||||||
customMode?: ModeConfig
|
commits?: GitCommit[]
|
||||||
slug?: string
|
listApiConfig?: ApiConfigMeta[]
|
||||||
|
mode?: Mode
|
||||||
|
customMode?: ModeConfig
|
||||||
|
slug?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiConfigMeta {
|
export interface ApiConfigMeta {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
apiProvider?: ApiProvider
|
apiProvider?: ApiProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionState {
|
export interface ExtensionState {
|
||||||
version: string
|
version: string
|
||||||
clineMessages: ClineMessage[]
|
clineMessages: ClineMessage[]
|
||||||
taskHistory: HistoryItem[]
|
taskHistory: HistoryItem[]
|
||||||
shouldShowAnnouncement: boolean
|
shouldShowAnnouncement: boolean
|
||||||
apiConfiguration?: ApiConfiguration
|
apiConfiguration?: ApiConfiguration
|
||||||
currentApiConfigName?: string
|
currentApiConfigName?: string
|
||||||
listApiConfigMeta?: ApiConfigMeta[]
|
listApiConfigMeta?: ApiConfigMeta[]
|
||||||
customInstructions?: string
|
customInstructions?: string
|
||||||
customModePrompts?: CustomModePrompts
|
customModePrompts?: CustomModePrompts
|
||||||
customSupportPrompts?: CustomSupportPrompts
|
customSupportPrompts?: CustomSupportPrompts
|
||||||
alwaysAllowReadOnly?: boolean
|
alwaysAllowReadOnly?: boolean
|
||||||
alwaysAllowWrite?: boolean
|
alwaysAllowWrite?: boolean
|
||||||
alwaysAllowExecute?: boolean
|
alwaysAllowExecute?: boolean
|
||||||
alwaysAllowBrowser?: boolean
|
alwaysAllowBrowser?: boolean
|
||||||
alwaysAllowMcp?: boolean
|
alwaysAllowMcp?: boolean
|
||||||
alwaysApproveResubmit?: boolean
|
alwaysApproveResubmit?: boolean
|
||||||
alwaysAllowModeSwitch?: boolean
|
alwaysAllowModeSwitch?: boolean
|
||||||
requestDelaySeconds: number
|
requestDelaySeconds: number
|
||||||
rateLimitSeconds: number
|
rateLimitSeconds: number
|
||||||
uriScheme?: string
|
uriScheme?: string
|
||||||
allowedCommands?: string[]
|
allowedCommands?: string[]
|
||||||
soundEnabled?: boolean
|
soundEnabled?: boolean
|
||||||
soundVolume?: number
|
soundVolume?: number
|
||||||
diffEnabled?: boolean
|
diffEnabled?: boolean
|
||||||
browserViewportSize?: string
|
browserViewportSize?: string
|
||||||
screenshotQuality?: number
|
screenshotQuality?: number
|
||||||
fuzzyMatchThreshold?: number
|
fuzzyMatchThreshold?: number
|
||||||
preferredLanguage: string
|
preferredLanguage: string
|
||||||
writeDelayMs: number
|
writeDelayMs: number
|
||||||
terminalOutputLineLimit?: number
|
terminalOutputLineLimit?: number
|
||||||
mcpEnabled: boolean
|
mcpEnabled: boolean
|
||||||
enableMcpServerCreation: boolean
|
enableMcpServerCreation: boolean
|
||||||
mode: Mode
|
mode: Mode
|
||||||
modeApiConfigs?: Record<Mode, string>
|
modeApiConfigs?: Record<Mode, string>
|
||||||
enhancementApiConfigId?: string
|
enhancementApiConfigId?: string
|
||||||
experiments: Record<ExperimentId, boolean>
|
experiments: Record<ExperimentId, boolean>
|
||||||
autoApprovalEnabled?: boolean
|
autoApprovalEnabled?: boolean
|
||||||
customModes: ModeConfig[]
|
customModes: ModeConfig[]
|
||||||
toolRequirements?: Record<string, boolean>
|
toolRequirements?: Record<string, boolean>
|
||||||
azureAiDeployments?: Record<string, AzureDeploymentConfig>
|
azureAiDeployments?: Record<string, AzureDeploymentConfig>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClineMessage {
|
export interface ClineMessage {
|
||||||
ts: number
|
ts: number
|
||||||
type: "ask" | "say"
|
type: "ask" | "say"
|
||||||
ask?: ClineAsk
|
ask?: ClineAsk
|
||||||
say?: ClineSay
|
say?: ClineSay
|
||||||
text?: string
|
text?: string
|
||||||
images?: string[]
|
images?: string[]
|
||||||
partial?: boolean
|
partial?: boolean
|
||||||
reasoning?: string
|
reasoning?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ClineAsk =
|
export type ClineAsk =
|
||||||
| "followup"
|
| "followup"
|
||||||
| "command"
|
| "command"
|
||||||
| "command_output"
|
| "command_output"
|
||||||
| "completion_result"
|
| "completion_result"
|
||||||
| "tool"
|
| "tool"
|
||||||
| "api_req_failed"
|
| "api_req_failed"
|
||||||
| "resume_task"
|
| "resume_task"
|
||||||
| "resume_completed_task"
|
| "resume_completed_task"
|
||||||
| "mistake_limit_reached"
|
| "mistake_limit_reached"
|
||||||
| "browser_action_launch"
|
| "browser_action_launch"
|
||||||
| "use_mcp_server"
|
| "use_mcp_server"
|
||||||
|
|
||||||
export type ClineSay =
|
export type ClineSay =
|
||||||
| "task"
|
| "task"
|
||||||
| "error"
|
| "error"
|
||||||
| "api_req_started"
|
| "api_req_started"
|
||||||
| "api_req_finished"
|
| "api_req_finished"
|
||||||
| "text"
|
| "text"
|
||||||
| "reasoning"
|
| "reasoning"
|
||||||
| "completion_result"
|
| "completion_result"
|
||||||
| "user_feedback"
|
| "user_feedback"
|
||||||
| "user_feedback_diff"
|
| "user_feedback_diff"
|
||||||
| "api_req_retried"
|
| "api_req_retried"
|
||||||
| "api_req_retry_delayed"
|
| "api_req_retry_delayed"
|
||||||
| "command_output"
|
| "command_output"
|
||||||
| "tool"
|
| "tool"
|
||||||
| "shell_integration_warning"
|
| "shell_integration_warning"
|
||||||
| "browser_action"
|
| "browser_action"
|
||||||
| "browser_action_result"
|
| "browser_action_result"
|
||||||
| "command"
|
| "command"
|
||||||
| "mcp_server_request_started"
|
| "mcp_server_request_started"
|
||||||
| "mcp_server_response"
|
| "mcp_server_response"
|
||||||
| "new_task_started"
|
| "new_task_started"
|
||||||
| "new_task"
|
| "new_task"
|
||||||
|
|
||||||
export interface ClineSayTool {
|
export interface ClineSayTool {
|
||||||
tool:
|
tool:
|
||||||
| "editedExistingFile"
|
| "editedExistingFile"
|
||||||
| "appliedDiff"
|
| "appliedDiff"
|
||||||
| "newFileCreated"
|
| "newFileCreated"
|
||||||
| "readFile"
|
| "readFile"
|
||||||
| "listFilesTopLevel"
|
| "listFilesTopLevel"
|
||||||
| "listFilesRecursive"
|
| "listFilesRecursive"
|
||||||
| "listCodeDefinitionNames"
|
| "listCodeDefinitionNames"
|
||||||
| "searchFiles"
|
| "searchFiles"
|
||||||
| "switchMode"
|
| "switchMode"
|
||||||
| "newTask"
|
| "newTask"
|
||||||
path?: string
|
path?: string
|
||||||
diff?: string
|
diff?: string
|
||||||
content?: string
|
content?: string
|
||||||
regex?: string
|
regex?: string
|
||||||
filePattern?: string
|
filePattern?: string
|
||||||
mode?: string
|
mode?: string
|
||||||
reason?: string
|
reason?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const browserActions = ["launch", "click", "type", "scroll_down", "scroll_up", "close"] as const
|
export const browserActions = ["launch", "click", "type", "scroll_down", "scroll_up", "close"] as const
|
||||||
export type BrowserAction = (typeof browserActions)[number]
|
export type BrowserAction = (typeof browserActions)[number]
|
||||||
|
|
||||||
export interface ClineSayBrowserAction {
|
export interface ClineSayBrowserAction {
|
||||||
action: BrowserAction
|
action: BrowserAction
|
||||||
coordinate?: string
|
coordinate?: string
|
||||||
text?: string
|
text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BrowserActionResult = {
|
export type BrowserActionResult = {
|
||||||
screenshot?: string
|
screenshot?: string
|
||||||
logs?: string
|
logs?: string
|
||||||
currentUrl?: string
|
currentUrl?: string
|
||||||
currentMousePosition?: string
|
currentMousePosition?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClineAskUseMcpServer {
|
export interface ClineAskUseMcpServer {
|
||||||
serverName: string
|
serverName: string
|
||||||
type: "use_mcp_tool" | "access_mcp_resource"
|
type: "use_mcp_tool" | "access_mcp_resource"
|
||||||
toolName?: string
|
toolName?: string
|
||||||
arguments?: string
|
arguments?: string
|
||||||
uri?: string
|
uri?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClineApiReqInfo {
|
export interface ClineApiReqInfo {
|
||||||
request?: string
|
request?: string
|
||||||
tokensIn?: number
|
tokensIn?: number
|
||||||
tokensOut?: number
|
tokensOut?: number
|
||||||
cacheWrites?: number
|
cacheWrites?: number
|
||||||
cacheReads?: number
|
cacheReads?: number
|
||||||
cost?: number
|
cost?: number
|
||||||
cancelReason?: ClineApiReqCancelReason
|
cancelReason?: ClineApiReqCancelReason
|
||||||
streamingFailedMessage?: string
|
streamingFailedMessage?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled"
|
export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export function checkExistKey(config: ApiConfiguration | undefined) {
|
|||||||
config.deepSeekApiKey,
|
config.deepSeekApiKey,
|
||||||
config.mistralApiKey,
|
config.mistralApiKey,
|
||||||
config.vsCodeLmModelSelector,
|
config.vsCodeLmModelSelector,
|
||||||
|
config.azureAiKey,
|
||||||
].some((key) => key !== undefined)
|
].some((key) => key !== undefined)
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +1,305 @@
|
|||||||
import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||||
import { memo } from "react"
|
import { Fzf } from "fzf"
|
||||||
|
import React, { KeyboardEvent, memo, useEffect, useMemo, useRef, useState } from "react"
|
||||||
|
import debounce from "debounce"
|
||||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
|
import { vscode } from "../../utils/vscode"
|
||||||
|
import { highlightFzfMatch } from "../../utils/highlight"
|
||||||
import { Pane } from "vscrui"
|
import { Pane } from "vscrui"
|
||||||
import { azureAiModelInfoSaneDefaults } from "../../../../src/shared/api"
|
import { azureAiModelInfoSaneDefaults } from "../../../../src/shared/api"
|
||||||
|
import styled from "styled-components"
|
||||||
|
|
||||||
const AzureAiModelPicker: React.FC = () => {
|
const AzureAiModelPicker: React.FC = () => {
|
||||||
const { apiConfiguration, handleInputChange } = useExtensionState()
|
const { apiConfiguration, setApiConfiguration, azureAiModels, onUpdateApiConfig } = useExtensionState()
|
||||||
|
const [searchTerm, setSearchTerm] = useState(apiConfiguration?.apiModelId || "")
|
||||||
|
const [isDropdownVisible, setIsDropdownVisible] = useState(false)
|
||||||
|
const [selectedIndex, setSelectedIndex] = useState(-1)
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||||
|
const itemRefs = useRef<(HTMLDivElement | null)[]>([])
|
||||||
|
const dropdownListRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const handleModelChange = (newModelId: string) => {
|
||||||
|
const apiConfig = {
|
||||||
|
...apiConfiguration,
|
||||||
|
apiModelId: newModelId,
|
||||||
|
}
|
||||||
|
setApiConfiguration(apiConfig)
|
||||||
|
onUpdateApiConfig(apiConfig)
|
||||||
|
setSearchTerm(newModelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (apiConfiguration?.apiModelId && apiConfiguration?.apiModelId !== searchTerm) {
|
||||||
|
setSearchTerm(apiConfiguration?.apiModelId)
|
||||||
|
}
|
||||||
|
}, [apiConfiguration, searchTerm])
|
||||||
|
|
||||||
|
const debouncedRefreshModels = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((endpoint: string, key: string) => {
|
||||||
|
vscode.postMessage({
|
||||||
|
type: "refreshAzureAiModels",
|
||||||
|
values: {
|
||||||
|
endpoint,
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, 50),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!apiConfiguration?.azureAiEndpoint || !apiConfiguration?.azureAiKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedRefreshModels(apiConfiguration.azureAiEndpoint, apiConfiguration.azureAiKey)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
debouncedRefreshModels.clear()
|
||||||
|
}
|
||||||
|
}, [apiConfiguration?.azureAiEndpoint, apiConfiguration?.azureAiKey, debouncedRefreshModels])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||||
|
setIsDropdownVisible(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const modelIds = useMemo(() => {
|
||||||
|
return azureAiModels.sort((a, b) => a.localeCompare(b))
|
||||||
|
}, [azureAiModels])
|
||||||
|
|
||||||
|
const searchableItems = useMemo(() => {
|
||||||
|
return modelIds.map((id) => ({
|
||||||
|
id,
|
||||||
|
html: id,
|
||||||
|
}))
|
||||||
|
}, [modelIds])
|
||||||
|
|
||||||
|
const fzf = useMemo(() => {
|
||||||
|
return new Fzf(searchableItems, {
|
||||||
|
selector: (item) => item.html,
|
||||||
|
})
|
||||||
|
}, [searchableItems])
|
||||||
|
|
||||||
|
const modelSearchResults = useMemo(() => {
|
||||||
|
if (!searchTerm) return searchableItems
|
||||||
|
|
||||||
|
const searchResults = fzf.find(searchTerm)
|
||||||
|
return searchResults.map((result) => ({
|
||||||
|
...result.item,
|
||||||
|
html: highlightFzfMatch(result.item.html, Array.from(result.positions), "model-item-highlight"),
|
||||||
|
}))
|
||||||
|
}, [searchableItems, searchTerm, fzf])
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (!isDropdownVisible) return
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowDown":
|
||||||
|
event.preventDefault()
|
||||||
|
setSelectedIndex((prev) => (prev < modelSearchResults.length - 1 ? prev + 1 : prev))
|
||||||
|
break
|
||||||
|
case "ArrowUp":
|
||||||
|
event.preventDefault()
|
||||||
|
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : prev))
|
||||||
|
break
|
||||||
|
case "Enter":
|
||||||
|
event.preventDefault()
|
||||||
|
if (selectedIndex >= 0 && selectedIndex < modelSearchResults.length) {
|
||||||
|
handleModelChange(modelSearchResults[selectedIndex].id)
|
||||||
|
setIsDropdownVisible(false)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "Escape":
|
||||||
|
setIsDropdownVisible(false)
|
||||||
|
setSelectedIndex(-1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedIndex(-1)
|
||||||
|
if (dropdownListRef.current) {
|
||||||
|
dropdownListRef.current.scrollTop = 0
|
||||||
|
}
|
||||||
|
}, [searchTerm])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedIndex >= 0 && itemRefs.current[selectedIndex]) {
|
||||||
|
itemRefs.current[selectedIndex]?.scrollIntoView({
|
||||||
|
block: "nearest",
|
||||||
|
behavior: "smooth",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [selectedIndex])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", rowGap: "5px" }}>
|
<>
|
||||||
<VSCodeTextField
|
<style>
|
||||||
value={apiConfiguration?.azureAiEndpoint || ""}
|
{`
|
||||||
style={{ width: "100%" }}
|
.model-item-highlight {
|
||||||
type="url"
|
background-color: var(--vscode-editor-findMatchHighlightBackground);
|
||||||
onChange={handleInputChange("azureAiEndpoint")}
|
color: inherit;
|
||||||
placeholder="https://your-endpoint.region.inference.ai.azure.com">
|
}
|
||||||
<span style={{ fontWeight: 500 }}>Base URL</span>
|
`}
|
||||||
</VSCodeTextField>
|
</style>
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", rowGap: "5px" }}>
|
||||||
|
<VSCodeTextField
|
||||||
|
value={apiConfiguration?.azureAiEndpoint || ""}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
type="url"
|
||||||
|
onChange={(e) => {
|
||||||
|
const apiConfig = {
|
||||||
|
...apiConfiguration,
|
||||||
|
azureAiEndpoint: (e.target as HTMLInputElement).value,
|
||||||
|
}
|
||||||
|
setApiConfiguration(apiConfig)
|
||||||
|
onUpdateApiConfig(apiConfig)
|
||||||
|
}}
|
||||||
|
placeholder="https://your-endpoint.region.inference.ai.azure.com">
|
||||||
|
<span style={{ fontWeight: 500 }}>Base URL</span>
|
||||||
|
</VSCodeTextField>
|
||||||
|
|
||||||
<VSCodeTextField
|
<VSCodeTextField
|
||||||
value={apiConfiguration?.azureAiKey || ""}
|
value={apiConfiguration?.azureAiKey || ""}
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
type="password"
|
type="password"
|
||||||
onChange={handleInputChange("azureAiKey")}
|
onChange={(e) => {
|
||||||
placeholder="Enter API Key...">
|
const apiConfig = {
|
||||||
<span style={{ fontWeight: 500 }}>API Key</span>
|
...apiConfiguration,
|
||||||
</VSCodeTextField>
|
azureAiKey: (e.target as HTMLInputElement).value,
|
||||||
|
}
|
||||||
|
setApiConfiguration(apiConfig)
|
||||||
|
onUpdateApiConfig(apiConfig)
|
||||||
|
}}
|
||||||
|
placeholder="Enter API Key...">
|
||||||
|
<span style={{ fontWeight: 500 }}>API Key</span>
|
||||||
|
</VSCodeTextField>
|
||||||
|
|
||||||
<VSCodeTextField
|
<DropdownWrapper ref={dropdownRef}>
|
||||||
value={apiConfiguration?.apiModelId || ""}
|
<VSCodeTextField
|
||||||
style={{ width: "100%" }}
|
id="model-search"
|
||||||
type="text"
|
placeholder="Search and select a model..."
|
||||||
onChange={handleInputChange("apiModelId")}
|
value={searchTerm}
|
||||||
placeholder="Enter model deployment name...">
|
onInput={(e) => {
|
||||||
<span style={{ fontWeight: 500 }}>Model Deployment Name</span>
|
handleModelChange((e.target as HTMLInputElement)?.value)
|
||||||
</VSCodeTextField>
|
setIsDropdownVisible(true)
|
||||||
|
}}
|
||||||
<Pane
|
onFocus={() => setIsDropdownVisible(true)}
|
||||||
title="Model Configuration"
|
onKeyDown={handleKeyDown}
|
||||||
open={false}
|
style={{ width: "100%", zIndex: AZURE_MODEL_PICKER_Z_INDEX, position: "relative" }}>
|
||||||
actions={[
|
<span style={{ fontWeight: 500 }}>Model Deployment Name</span>
|
||||||
{
|
{searchTerm && (
|
||||||
iconName: "refresh",
|
<div
|
||||||
onClick: () =>
|
className="input-icon-button codicon codicon-close"
|
||||||
handleInputChange("azureAiModelConfig")({
|
aria-label="Clear search"
|
||||||
target: { value: azureAiModelInfoSaneDefaults },
|
onClick={() => {
|
||||||
}),
|
handleModelChange("")
|
||||||
},
|
setIsDropdownVisible(true)
|
||||||
]}>
|
}}
|
||||||
<div
|
slot="end"
|
||||||
style={{
|
style={{
|
||||||
padding: 15,
|
display: "flex",
|
||||||
backgroundColor: "var(--vscode-editor-background)",
|
justifyContent: "center",
|
||||||
}}>
|
alignItems: "center",
|
||||||
<p
|
height: "100%",
|
||||||
style={{
|
}}
|
||||||
fontSize: "12px",
|
/>
|
||||||
color: "var(--vscode-descriptionForeground)",
|
)}
|
||||||
margin: "0 0 15px 0",
|
</VSCodeTextField>
|
||||||
lineHeight: "1.4",
|
{isDropdownVisible && (
|
||||||
}}>
|
<DropdownList ref={dropdownListRef}>
|
||||||
Configure capabilities for your deployed model.
|
{modelSearchResults.map((item, index) => (
|
||||||
</p>
|
<DropdownItem
|
||||||
|
key={item.id}
|
||||||
|
ref={(el) => (itemRefs.current[index] = el)}
|
||||||
|
isSelected={index === selectedIndex}
|
||||||
|
onMouseEnter={() => setSelectedIndex(index)}
|
||||||
|
onClick={() => {
|
||||||
|
handleModelChange(item.id)
|
||||||
|
setIsDropdownVisible(false)
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: item.html,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</DropdownList>
|
||||||
|
)}
|
||||||
|
</DropdownWrapper>
|
||||||
|
|
||||||
|
<Pane
|
||||||
|
title="Model Configuration"
|
||||||
|
open={false}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
iconName: "refresh",
|
||||||
|
onClick: () => {
|
||||||
|
const apiConfig = {
|
||||||
|
...apiConfiguration,
|
||||||
|
azureAiModelConfig: azureAiModelInfoSaneDefaults,
|
||||||
|
}
|
||||||
|
setApiConfiguration(apiConfig)
|
||||||
|
onUpdateApiConfig(apiConfig)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
|
padding: 15,
|
||||||
padding: "12px",
|
backgroundColor: "var(--vscode-editor-background)",
|
||||||
borderRadius: "4px",
|
|
||||||
marginTop: "8px",
|
|
||||||
}}>
|
}}>
|
||||||
<span
|
<p
|
||||||
style={{
|
style={{
|
||||||
fontSize: "11px",
|
fontSize: "12px",
|
||||||
fontWeight: 500,
|
color: "var(--vscode-descriptionForeground)",
|
||||||
color: "var(--vscode-editor-foreground)",
|
margin: "0 0 15px 0",
|
||||||
display: "block",
|
lineHeight: "1.4",
|
||||||
marginBottom: "10px",
|
|
||||||
}}>
|
}}>
|
||||||
Model Features
|
Configure capabilities for your deployed model.
|
||||||
</span>
|
</p>
|
||||||
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
|
<div
|
||||||
<VSCodeTextField
|
style={{
|
||||||
value={
|
backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
|
||||||
apiConfiguration?.azureAiModelConfig?.contextWindow?.toString() ||
|
padding: "12px",
|
||||||
azureAiModelInfoSaneDefaults.contextWindow?.toString() ||
|
borderRadius: "4px",
|
||||||
""
|
marginTop: "8px",
|
||||||
}
|
}}>
|
||||||
type="text"
|
<span
|
||||||
style={{ width: "100%" }}
|
style={{
|
||||||
onChange={(e: any) => {
|
fontSize: "11px",
|
||||||
const parsed = parseInt(e.target.value)
|
fontWeight: 500,
|
||||||
handleInputChange("azureAiModelConfig")({
|
color: "var(--vscode-editor-foreground)",
|
||||||
target: {
|
display: "block",
|
||||||
value: {
|
marginBottom: "10px",
|
||||||
|
}}>
|
||||||
|
Model Features
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
|
||||||
|
<VSCodeTextField
|
||||||
|
value={
|
||||||
|
apiConfiguration?.azureAiModelConfig?.contextWindow?.toString() ||
|
||||||
|
azureAiModelInfoSaneDefaults.contextWindow?.toString() ||
|
||||||
|
""
|
||||||
|
}
|
||||||
|
type="text"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
onChange={(e: any) => {
|
||||||
|
const parsed = parseInt(e.target.value)
|
||||||
|
const apiConfig = {
|
||||||
|
...apiConfiguration,
|
||||||
|
azureAiModelConfig: {
|
||||||
...(apiConfiguration?.azureAiModelConfig ||
|
...(apiConfiguration?.azureAiModelConfig ||
|
||||||
azureAiModelInfoSaneDefaults),
|
azureAiModelInfoSaneDefaults),
|
||||||
contextWindow:
|
contextWindow:
|
||||||
@@ -104,43 +309,81 @@ const AzureAiModelPicker: React.FC = () => {
|
|||||||
? azureAiModelInfoSaneDefaults.contextWindow
|
? azureAiModelInfoSaneDefaults.contextWindow
|
||||||
: parsed,
|
: parsed,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
})
|
setApiConfiguration(apiConfig)
|
||||||
}}
|
onUpdateApiConfig(apiConfig)
|
||||||
placeholder="e.g. 128000">
|
}}
|
||||||
<span style={{ fontWeight: 500 }}>Context Window Size</span>
|
placeholder="e.g. 128000">
|
||||||
</VSCodeTextField>
|
<span style={{ fontWeight: 500 }}>Context Window Size</span>
|
||||||
<p
|
</VSCodeTextField>
|
||||||
style={{
|
<p
|
||||||
fontSize: "11px",
|
style={{
|
||||||
color: "var(--vscode-descriptionForeground)",
|
fontSize: "11px",
|
||||||
marginTop: "4px",
|
color: "var(--vscode-descriptionForeground)",
|
||||||
}}>
|
marginTop: "4px",
|
||||||
Total tokens the model can process in a single request.
|
}}>
|
||||||
</p>
|
Total tokens the model can process in a single request.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Pane>
|
||||||
</Pane>
|
|
||||||
|
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
marginTop: "5px",
|
marginTop: "5px",
|
||||||
color: "var(--vscode-descriptionForeground)",
|
color: "var(--vscode-descriptionForeground)",
|
||||||
}}>
|
}}>
|
||||||
Configure your Azure AI Model Inference endpoint and model deployment. API keys are stored locally.
|
Configure your Azure AI Model Inference endpoint and model deployment. API keys are stored locally.
|
||||||
{!apiConfiguration?.azureAiKey && (
|
{!apiConfiguration?.azureAiKey && (
|
||||||
<VSCodeLink
|
<VSCodeLink
|
||||||
href="https://learn.microsoft.com/azure/ai-foundry/model-inference/reference/reference-model-inference-chat-completions"
|
href="https://learn.microsoft.com/azure/ai-foundry/model-inference/reference/reference-model-inference-chat-completions"
|
||||||
style={{ display: "inline", fontSize: "inherit" }}>
|
style={{ display: "inline", fontSize: "inherit" }}>
|
||||||
{" "}
|
{" "}
|
||||||
Learn more about Azure AI Model Inference.
|
Learn more about Azure AI Model Inference.
|
||||||
</VSCodeLink>
|
</VSCodeLink>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(AzureAiModelPicker)
|
export default memo(AzureAiModelPicker)
|
||||||
|
|
||||||
|
// Dropdown
|
||||||
|
|
||||||
|
const DropdownWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const AZURE_MODEL_PICKER_Z_INDEX = 1_000
|
||||||
|
|
||||||
|
const DropdownList = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% - 3px);
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% - 2px);
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: var(--vscode-dropdown-background);
|
||||||
|
border: 1px solid var(--vscode-list-activeSelectionBackground);
|
||||||
|
z-index: ${AZURE_MODEL_PICKER_Z_INDEX - 1};
|
||||||
|
border-bottom-left-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const DropdownItem = styled.div<{ isSelected: boolean }>`
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: normal;
|
||||||
|
|
||||||
|
background-color: ${({ isSelected }) => (isSelected ? "var(--vscode-list-activeSelectionBackground)" : "inherit")};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--vscode-list-activeSelectionBackground);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface ExtensionStateContextType extends ExtensionState {
|
|||||||
glamaModels: Record<string, ModelInfo>
|
glamaModels: Record<string, ModelInfo>
|
||||||
openRouterModels: Record<string, ModelInfo>
|
openRouterModels: Record<string, ModelInfo>
|
||||||
openAiModels: string[]
|
openAiModels: string[]
|
||||||
|
azureAiModels: string[]
|
||||||
mcpServers: McpServer[]
|
mcpServers: McpServer[]
|
||||||
filePaths: string[]
|
filePaths: string[]
|
||||||
openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
|
openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
|
||||||
@@ -123,6 +124,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
})
|
})
|
||||||
|
|
||||||
const [openAiModels, setOpenAiModels] = useState<string[]>([])
|
const [openAiModels, setOpenAiModels] = useState<string[]>([])
|
||||||
|
const [azureAiModels, setAzureAiModels] = useState<string[]>([])
|
||||||
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
|
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
|
||||||
|
|
||||||
const setListApiConfigMeta = useCallback(
|
const setListApiConfigMeta = useCallback(
|
||||||
@@ -247,6 +249,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
glamaModels,
|
glamaModels,
|
||||||
openRouterModels,
|
openRouterModels,
|
||||||
openAiModels,
|
openAiModels,
|
||||||
|
azureAiModels,
|
||||||
mcpServers,
|
mcpServers,
|
||||||
filePaths,
|
filePaths,
|
||||||
openedTabs,
|
openedTabs,
|
||||||
|
|||||||
Reference in New Issue
Block a user