mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
feat openai-list-models
This commit is contained in:
@@ -519,6 +519,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
case "refreshOpenRouterModels":
|
case "refreshOpenRouterModels":
|
||||||
await this.refreshOpenRouterModels()
|
await this.refreshOpenRouterModels()
|
||||||
break
|
break
|
||||||
|
case "refreshOpenAiModels":
|
||||||
|
const { apiConfiguration } = await this.getState()
|
||||||
|
const openAiModels = await this.getOpenAiModels(apiConfiguration.openAiBaseUrl, apiConfiguration.openAiApiKey)
|
||||||
|
this.postMessageToWebview({ type: "openAiModels", openAiModels })
|
||||||
|
break
|
||||||
case "openImage":
|
case "openImage":
|
||||||
openImage(message.text!)
|
openImage(message.text!)
|
||||||
break
|
break
|
||||||
@@ -699,6 +704,32 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAi
|
||||||
|
|
||||||
|
async getOpenAiModels(baseUrl?: string, apiKey?: string) {
|
||||||
|
try {
|
||||||
|
if (!baseUrl) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!URL.canParse(baseUrl)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: Record<string, any> = {}
|
||||||
|
if (apiKey) {
|
||||||
|
config["headers"] = { Authorization: `Bearer ${apiKey}` }
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.get(`${baseUrl}/models`, config)
|
||||||
|
const modelsArray = response.data?.data?.map((model: any) => model.id) || []
|
||||||
|
const models = [...new Set<string>(modelsArray)]
|
||||||
|
return models
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenRouter
|
// OpenRouter
|
||||||
|
|
||||||
async handleOpenRouterCallback(code: string) {
|
async handleOpenRouterCallback(code: string) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export interface ExtensionMessage {
|
|||||||
| "invoke"
|
| "invoke"
|
||||||
| "partialMessage"
|
| "partialMessage"
|
||||||
| "openRouterModels"
|
| "openRouterModels"
|
||||||
|
| "openAiModels"
|
||||||
| "mcpServers"
|
| "mcpServers"
|
||||||
text?: string
|
text?: string
|
||||||
action?:
|
action?:
|
||||||
@@ -33,6 +34,7 @@ export interface ExtensionMessage {
|
|||||||
filePaths?: string[]
|
filePaths?: string[]
|
||||||
partialMessage?: ClineMessage
|
partialMessage?: ClineMessage
|
||||||
openRouterModels?: Record<string, ModelInfo>
|
openRouterModels?: Record<string, ModelInfo>
|
||||||
|
openAiModels?: string[]
|
||||||
mcpServers?: McpServer[]
|
mcpServers?: McpServer[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface WebviewMessage {
|
|||||||
| "openMention"
|
| "openMention"
|
||||||
| "cancelTask"
|
| "cancelTask"
|
||||||
| "refreshOpenRouterModels"
|
| "refreshOpenRouterModels"
|
||||||
|
| "refreshOpenAiModels"
|
||||||
| "alwaysAllowBrowser"
|
| "alwaysAllowBrowser"
|
||||||
| "alwaysAllowMcp"
|
| "alwaysAllowMcp"
|
||||||
| "playSound"
|
| "playSound"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import OpenRouterModelPicker, {
|
|||||||
ModelDescriptionMarkdown,
|
ModelDescriptionMarkdown,
|
||||||
OPENROUTER_MODEL_PICKER_Z_INDEX,
|
OPENROUTER_MODEL_PICKER_Z_INDEX,
|
||||||
} from "./OpenRouterModelPicker"
|
} from "./OpenRouterModelPicker"
|
||||||
|
import OpenAiModelPicker from "./OpenAiModelPicker"
|
||||||
|
|
||||||
interface ApiOptionsProps {
|
interface ApiOptionsProps {
|
||||||
showModelOptions: boolean
|
showModelOptions: boolean
|
||||||
@@ -438,13 +439,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
|
|||||||
placeholder="Enter API Key...">
|
placeholder="Enter API Key...">
|
||||||
<span style={{ fontWeight: 500 }}>API Key</span>
|
<span style={{ fontWeight: 500 }}>API Key</span>
|
||||||
</VSCodeTextField>
|
</VSCodeTextField>
|
||||||
<VSCodeTextField
|
<OpenAiModelPicker />
|
||||||
value={apiConfiguration?.openAiModelId || ""}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onInput={handleInputChange("openAiModelId")}
|
|
||||||
placeholder={"Enter Model ID..."}>
|
|
||||||
<span style={{ fontWeight: 500 }}>Model ID</span>
|
|
||||||
</VSCodeTextField>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<VSCodeCheckbox
|
<VSCodeCheckbox
|
||||||
checked={apiConfiguration?.includeStreamOptions ?? true}
|
checked={apiConfiguration?.includeStreamOptions ?? true}
|
||||||
|
|||||||
362
webview-ui/src/components/settings/OpenAiModelPicker.tsx
Normal file
362
webview-ui/src/components/settings/OpenAiModelPicker.tsx
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||||
|
import Fuse from "fuse.js"
|
||||||
|
import React, { KeyboardEvent, memo, useEffect, useMemo, useRef, useState } from "react"
|
||||||
|
import { useRemark } from "react-remark"
|
||||||
|
import { useMount } from "react-use"
|
||||||
|
import styled from "styled-components"
|
||||||
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
|
import { vscode } from "../../utils/vscode"
|
||||||
|
import { highlight } from "../history/HistoryView"
|
||||||
|
|
||||||
|
const OpenAiModelPicker: React.FC = () => {
|
||||||
|
const { apiConfiguration, setApiConfiguration, openAiModels } = useExtensionState()
|
||||||
|
const [searchTerm, setSearchTerm] = useState(apiConfiguration?.openAiModelId || "")
|
||||||
|
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) => {
|
||||||
|
// could be setting invalid model id/undefined info but validation will catch it
|
||||||
|
setApiConfiguration({
|
||||||
|
...apiConfiguration,
|
||||||
|
openAiModelId: newModelId,
|
||||||
|
})
|
||||||
|
setSearchTerm(newModelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
vscode.postMessage({ type: "refreshOpenAiModels" })
|
||||||
|
}, [apiConfiguration?.openAiBaseUrl, apiConfiguration?.openAiApiKey])
|
||||||
|
|
||||||
|
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 openAiModels.sort((a, b) => a.localeCompare(b))
|
||||||
|
}, [openAiModels])
|
||||||
|
|
||||||
|
const searchableItems = useMemo(() => {
|
||||||
|
return modelIds.map((id) => ({
|
||||||
|
id,
|
||||||
|
html: id,
|
||||||
|
}))
|
||||||
|
}, [modelIds])
|
||||||
|
|
||||||
|
const fuse = useMemo(() => {
|
||||||
|
return new Fuse(searchableItems, {
|
||||||
|
keys: ["html"], // highlight function will update this
|
||||||
|
threshold: 0.6,
|
||||||
|
shouldSort: true,
|
||||||
|
isCaseSensitive: false,
|
||||||
|
ignoreLocation: false,
|
||||||
|
includeMatches: true,
|
||||||
|
minMatchCharLength: 1,
|
||||||
|
})
|
||||||
|
}, [searchableItems])
|
||||||
|
|
||||||
|
const modelSearchResults = useMemo(() => {
|
||||||
|
let results: { id: string; html: string }[] = searchTerm
|
||||||
|
? highlight(fuse.search(searchTerm), "model-item-highlight")
|
||||||
|
: searchableItems
|
||||||
|
// results.sort((a, b) => a.id.localeCompare(b.id)) NOTE: sorting like this causes ids in objects to be reordered and mismatched
|
||||||
|
return results
|
||||||
|
}, [searchableItems, searchTerm, fuse])
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
.model-item-highlight {
|
||||||
|
background-color: var(--vscode-editor-findMatchHighlightBackground);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
<div>
|
||||||
|
<DropdownWrapper ref={dropdownRef}>
|
||||||
|
<VSCodeTextField
|
||||||
|
id="model-search"
|
||||||
|
placeholder="Search and select a model..."
|
||||||
|
value={searchTerm}
|
||||||
|
onInput={(e) => {
|
||||||
|
handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
|
||||||
|
setIsDropdownVisible(true)
|
||||||
|
}}
|
||||||
|
onFocus={() => setIsDropdownVisible(true)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
style={{ width: "100%", zIndex: OPENAI_MODEL_PICKER_Z_INDEX, position: "relative" }}>
|
||||||
|
{searchTerm && (
|
||||||
|
<div
|
||||||
|
className="input-icon-button codicon codicon-close"
|
||||||
|
aria-label="Clear search"
|
||||||
|
onClick={() => {
|
||||||
|
handleModelChange("")
|
||||||
|
setIsDropdownVisible(true)
|
||||||
|
}}
|
||||||
|
slot="end"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</VSCodeTextField>
|
||||||
|
{isDropdownVisible && (
|
||||||
|
<DropdownList ref={dropdownListRef}>
|
||||||
|
{modelSearchResults.map((item, index) => (
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OpenAiModelPicker
|
||||||
|
|
||||||
|
// Dropdown
|
||||||
|
|
||||||
|
const DropdownWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const OPENAI_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: ${OPENAI_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);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Markdown
|
||||||
|
|
||||||
|
const StyledMarkdown = styled.div`
|
||||||
|
font-family:
|
||||||
|
var(--vscode-font-family),
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
"Open Sans",
|
||||||
|
"Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
|
||||||
|
p,
|
||||||
|
li,
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
line-height: 1.25;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const ModelDescriptionMarkdown = memo(
|
||||||
|
({
|
||||||
|
markdown,
|
||||||
|
key,
|
||||||
|
isExpanded,
|
||||||
|
setIsExpanded,
|
||||||
|
}: {
|
||||||
|
markdown?: string
|
||||||
|
key: string
|
||||||
|
isExpanded: boolean
|
||||||
|
setIsExpanded: (isExpanded: boolean) => void
|
||||||
|
}) => {
|
||||||
|
const [reactContent, setMarkdown] = useRemark()
|
||||||
|
// const [isExpanded, setIsExpanded] = useState(false)
|
||||||
|
const [showSeeMore, setShowSeeMore] = useState(false)
|
||||||
|
const textContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const textRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMarkdown(markdown || "")
|
||||||
|
}, [markdown, setMarkdown])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (textRef.current && textContainerRef.current) {
|
||||||
|
const { scrollHeight } = textRef.current
|
||||||
|
const { clientHeight } = textContainerRef.current
|
||||||
|
const isOverflowing = scrollHeight > clientHeight
|
||||||
|
setShowSeeMore(isOverflowing)
|
||||||
|
// if (!isOverflowing) {
|
||||||
|
// setIsExpanded(false)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}, [reactContent, setIsExpanded])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledMarkdown key={key} style={{ display: "inline-block", marginBottom: 0 }}>
|
||||||
|
<div
|
||||||
|
ref={textContainerRef}
|
||||||
|
style={{
|
||||||
|
overflowY: isExpanded ? "auto" : "hidden",
|
||||||
|
position: "relative",
|
||||||
|
wordBreak: "break-word",
|
||||||
|
overflowWrap: "anywhere",
|
||||||
|
}}>
|
||||||
|
<div
|
||||||
|
ref={textRef}
|
||||||
|
style={{
|
||||||
|
display: "-webkit-box",
|
||||||
|
WebkitLineClamp: isExpanded ? "unset" : 3,
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
overflow: "hidden",
|
||||||
|
// whiteSpace: "pre-wrap",
|
||||||
|
// wordBreak: "break-word",
|
||||||
|
// overflowWrap: "anywhere",
|
||||||
|
}}>
|
||||||
|
{reactContent}
|
||||||
|
</div>
|
||||||
|
{!isExpanded && showSeeMore && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: "1.2em",
|
||||||
|
background:
|
||||||
|
"linear-gradient(to right, transparent, var(--vscode-sideBar-background))",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<VSCodeLink
|
||||||
|
style={{
|
||||||
|
// cursor: "pointer",
|
||||||
|
// color: "var(--vscode-textLink-foreground)",
|
||||||
|
fontSize: "inherit",
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingLeft: 3,
|
||||||
|
backgroundColor: "var(--vscode-sideBar-background)",
|
||||||
|
}}
|
||||||
|
onClick={() => setIsExpanded(true)}>
|
||||||
|
See more
|
||||||
|
</VSCodeLink>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</StyledMarkdown>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -17,6 +17,7 @@ export interface ExtensionStateContextType extends ExtensionState {
|
|||||||
showWelcome: boolean
|
showWelcome: boolean
|
||||||
theme: any
|
theme: any
|
||||||
openRouterModels: Record<string, ModelInfo>
|
openRouterModels: Record<string, ModelInfo>
|
||||||
|
openAiModels: string[],
|
||||||
mcpServers: McpServer[]
|
mcpServers: McpServer[]
|
||||||
filePaths: string[]
|
filePaths: string[]
|
||||||
setApiConfiguration: (config: ApiConfiguration) => void
|
setApiConfiguration: (config: ApiConfiguration) => void
|
||||||
@@ -59,6 +60,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
|
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
|
||||||
[openRouterDefaultModelId]: openRouterDefaultModelInfo,
|
[openRouterDefaultModelId]: openRouterDefaultModelInfo,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [openAiModels, setOpenAiModels] = useState<string[]>([])
|
||||||
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
|
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
|
||||||
|
|
||||||
const handleMessage = useCallback((event: MessageEvent) => {
|
const handleMessage = useCallback((event: MessageEvent) => {
|
||||||
@@ -116,6 +119,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case "openAiModels": {
|
||||||
|
const updatedModels = message.openAiModels ?? []
|
||||||
|
setOpenAiModels(updatedModels)
|
||||||
|
break
|
||||||
|
}
|
||||||
case "mcpServers": {
|
case "mcpServers": {
|
||||||
setMcpServers(message.mcpServers ?? [])
|
setMcpServers(message.mcpServers ?? [])
|
||||||
break
|
break
|
||||||
@@ -135,6 +143,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
showWelcome,
|
showWelcome,
|
||||||
theme,
|
theme,
|
||||||
openRouterModels,
|
openRouterModels,
|
||||||
|
openAiModels,
|
||||||
mcpServers,
|
mcpServers,
|
||||||
filePaths,
|
filePaths,
|
||||||
soundVolume: state.soundVolume,
|
soundVolume: state.soundVolume,
|
||||||
|
|||||||
Reference in New Issue
Block a user