mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
Merge pull request #8528 from Kilo-Org/caring-columnist
perf: targeted startup performance wins (parallel model fetches + event-driven webview hydration)
This commit is contained in:
@@ -1168,6 +1168,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
])
|
||||
this.sendNotificationSettings()
|
||||
this.sendTimelineSetting()
|
||||
this.postMessage({ type: "extensionDataReady" })
|
||||
|
||||
// Start polling worktree diff stats for the sidebar badge
|
||||
this.startStatsPolling()
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
* changes into a single write (which triggers disposeAll on the CLI).
|
||||
*/
|
||||
|
||||
import { createContext, useContext, createSignal, onCleanup, ParentComponent, Accessor } from "solid-js"
|
||||
import { createContext, useContext, createSignal, onCleanup } from "solid-js"
|
||||
import type { ParentComponent, Accessor } from "solid-js"
|
||||
import { useVSCode } from "./vscode"
|
||||
import type { Config, ExtensionMessage } from "../types/messages"
|
||||
import { deepMerge, stripNulls, resolveConfig } from "../utils/config-utils"
|
||||
@@ -71,25 +72,29 @@ export const ConfigProvider: ParentComponent = (props) => {
|
||||
|
||||
onCleanup(unsubscribe)
|
||||
|
||||
// Request config in case the initial push was missed.
|
||||
// Retry a few times because the extension's httpClient may
|
||||
// not be ready yet when the first request arrives.
|
||||
let retries = 0
|
||||
const maxRetries = 5
|
||||
const retryMs = 500
|
||||
|
||||
// Request config immediately; if the extension's httpClient is not yet ready,
|
||||
// extensionDataReady will fire once initialization completes and we retry once.
|
||||
vscode.postMessage({ type: "requestConfig" })
|
||||
|
||||
const retryTimer = setInterval(() => {
|
||||
retries++
|
||||
if (!loading() || retries >= maxRetries) {
|
||||
clearInterval(retryTimer)
|
||||
return
|
||||
const fallback = setTimeout(() => {
|
||||
if (loading()) {
|
||||
vscode.postMessage({ type: "requestConfig" })
|
||||
}
|
||||
vscode.postMessage({ type: "requestConfig" })
|
||||
}, retryMs)
|
||||
}, 3000)
|
||||
|
||||
onCleanup(() => clearInterval(retryTimer))
|
||||
const unsubReady = vscode.onMessage((message: ExtensionMessage) => {
|
||||
if (message.type !== "extensionDataReady") return
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
if (loading()) {
|
||||
vscode.postMessage({ type: "requestConfig" })
|
||||
}
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
})
|
||||
|
||||
function updateConfig(partial: Partial<Config>) {
|
||||
// Optimistically update local state with deep merge + null stripping
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* Selection is now per-session — see session.tsx.
|
||||
*/
|
||||
|
||||
import { createContext, useContext, createSignal, createMemo, onCleanup, ParentComponent, Accessor } from "solid-js"
|
||||
import { createContext, useContext, createSignal, createMemo, onCleanup } from "solid-js"
|
||||
import type { ParentComponent, Accessor } from "solid-js"
|
||||
import { useVSCode } from "./vscode"
|
||||
import type { Provider, ProviderModel, ModelSelection, ExtensionMessage, ProviderAuthState } from "../types/messages"
|
||||
import type { ProviderAuthMethod } from "@kilocode/sdk/v2/client"
|
||||
@@ -64,25 +65,29 @@ export const ProviderProvider: ParentComponent = (props) => {
|
||||
|
||||
onCleanup(unsubscribe)
|
||||
|
||||
// Request providers in case the initial push was missed.
|
||||
// Retry a few times because the extension's httpClient may
|
||||
// not be ready yet when the first request arrives.
|
||||
let retries = 0
|
||||
const maxRetries = 5
|
||||
const retryMs = 500
|
||||
|
||||
// Request providers immediately; if the extension's httpClient is not yet ready,
|
||||
// extensionDataReady will fire once initialization completes and we retry once.
|
||||
vscode.postMessage({ type: "requestProviders" })
|
||||
|
||||
const retryTimer = setInterval(() => {
|
||||
retries++
|
||||
if (Object.keys(providers()).length > 0 || retries >= maxRetries) {
|
||||
clearInterval(retryTimer)
|
||||
return
|
||||
const fallback = setTimeout(() => {
|
||||
if (Object.keys(providers()).length === 0) {
|
||||
vscode.postMessage({ type: "requestProviders" })
|
||||
}
|
||||
vscode.postMessage({ type: "requestProviders" })
|
||||
}, retryMs)
|
||||
}, 3000)
|
||||
|
||||
onCleanup(() => clearInterval(retryTimer))
|
||||
const unsubReady = vscode.onMessage((message: ExtensionMessage) => {
|
||||
if (message.type !== "extensionDataReady") return
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
if (Object.keys(providers()).length === 0) {
|
||||
vscode.postMessage({ type: "requestProviders" })
|
||||
}
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
})
|
||||
|
||||
const value: ProviderContextValue = {
|
||||
providers,
|
||||
|
||||
@@ -503,24 +503,10 @@ export const SessionProvider: ParentComponent = (props) => {
|
||||
})
|
||||
})
|
||||
|
||||
// Request agents in case the initial push was missed.
|
||||
// Retry a few times because the extension's httpClient may
|
||||
// not be ready yet when the first request arrives.
|
||||
let agentRetries = 0
|
||||
const agentMaxRetries = 5
|
||||
const agentRetryMs = 500
|
||||
|
||||
// Request agents immediately; if the extension's httpClient is not yet ready,
|
||||
// extensionDataReady will fire once initialization completes and we retry once.
|
||||
vscode.postMessage({ type: "requestAgents" })
|
||||
|
||||
const agentRetryTimer = setInterval(() => {
|
||||
agentRetries++
|
||||
if (agents().length > 0 || agentRetries >= agentMaxRetries) {
|
||||
clearInterval(agentRetryTimer)
|
||||
return
|
||||
}
|
||||
vscode.postMessage({ type: "requestAgents" })
|
||||
}, agentRetryMs)
|
||||
|
||||
// Skills loaded from the CLI backend
|
||||
const unsubSkills = vscode.onMessage((message: ExtensionMessage) => {
|
||||
if (message.type === "skillsLoaded") {
|
||||
@@ -545,24 +531,28 @@ export const SessionProvider: ParentComponent = (props) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Request MCP status on init with retry (same pattern as agents)
|
||||
let mcpRetries = 0
|
||||
// Request MCP status immediately; retry once on extensionDataReady if still missing.
|
||||
vscode.postMessage({ type: "requestMcpStatus" })
|
||||
const mcpRetryTimer = setInterval(() => {
|
||||
mcpRetries++
|
||||
if (Object.keys(mcpStatus()).length > 0 || mcpRetries >= 5) {
|
||||
clearInterval(mcpRetryTimer)
|
||||
return
|
||||
}
|
||||
vscode.postMessage({ type: "requestMcpStatus" })
|
||||
}, 500)
|
||||
|
||||
const fallback = setTimeout(() => {
|
||||
if (agents().length === 0) vscode.postMessage({ type: "requestAgents" })
|
||||
if (Object.keys(mcpStatus()).length === 0) vscode.postMessage({ type: "requestMcpStatus" })
|
||||
}, 3000)
|
||||
|
||||
const unsubReady = vscode.onMessage((message: ExtensionMessage) => {
|
||||
if (message.type !== "extensionDataReady") return
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
if (agents().length === 0) vscode.postMessage({ type: "requestAgents" })
|
||||
if (Object.keys(mcpStatus()).length === 0) vscode.postMessage({ type: "requestMcpStatus" })
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
unsubAgents()
|
||||
unsubSkills()
|
||||
unsubMcpStatus()
|
||||
clearInterval(agentRetryTimer)
|
||||
clearInterval(mcpRetryTimer)
|
||||
unsubReady()
|
||||
clearTimeout(fallback)
|
||||
})
|
||||
|
||||
// Variant (thinking effort) selection — keyed by "providerID/modelID"
|
||||
|
||||
@@ -1283,6 +1283,10 @@ export interface ClearPendingPromptsMessage {
|
||||
type: "clearPendingPrompts"
|
||||
}
|
||||
|
||||
export interface ExtensionDataReadyMessage {
|
||||
type: "extensionDataReady"
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Marketplace Messages
|
||||
// ============================================
|
||||
@@ -1475,6 +1479,7 @@ export type ExtensionMessage =
|
||||
| WorktreeStatsLoadedMessage
|
||||
| McpStatusLoadedMessage
|
||||
| ClearPendingPromptsMessage
|
||||
| ExtensionDataReadyMessage
|
||||
|
||||
// ============================================
|
||||
// Messages FROM webview TO extension
|
||||
|
||||
@@ -149,11 +149,10 @@ export namespace ModelsDev {
|
||||
|
||||
if (kiloAllowed && !providers["kilo"]) {
|
||||
const kiloOptions = config.provider?.kilo?.options
|
||||
// kilocode_change start - resolve org ID from auth (OAuth accountId) not just config
|
||||
// resolve org ID from auth (OAuth accountId) not just config
|
||||
const kiloAuth = await Auth.get("kilo")
|
||||
const kiloOrgId =
|
||||
kiloOptions?.kilocodeOrganizationId ?? (kiloAuth?.type === "oauth" ? kiloAuth.accountId : undefined)
|
||||
// kilocode_change end
|
||||
const normalizedBaseURL = normalizeKiloBaseURL(kiloOptions?.baseURL, kiloOrgId)
|
||||
const kiloFetchOptions = {
|
||||
...(normalizedBaseURL ? { baseURL: normalizedBaseURL } : {}),
|
||||
@@ -164,7 +163,19 @@ export namespace ModelsDev {
|
||||
: "https://api.kilo.ai/api/openrouter"
|
||||
const providerBaseURL = normalizedBaseURL ?? defaultBaseURL
|
||||
const ensureTrailingSlash = (value: string): string => (value.endsWith("/") ? value : `${value}/`)
|
||||
const kiloModels = await ModelCache.fetch("kilo", kiloFetchOptions).catch(() => ({}))
|
||||
const apertisConfig = config.provider?.apertis?.options
|
||||
const apertisBaseURL = apertisConfig?.baseURL ?? "https://api.apertis.ai/v1"
|
||||
const apertisFetchOptions = {
|
||||
...(apertisConfig?.baseURL ? { baseURL: apertisConfig.baseURL } : {}),
|
||||
}
|
||||
|
||||
const [kiloModels, apertisModels] = await Promise.all([
|
||||
ModelCache.fetch("kilo", kiloFetchOptions).catch(() => ({})),
|
||||
!providers["apertis"]
|
||||
? ModelCache.fetch("apertis", apertisFetchOptions).catch(() => ({}))
|
||||
: Promise.resolve(null),
|
||||
])
|
||||
|
||||
providers["kilo"] = {
|
||||
id: "kilo",
|
||||
name: "Kilo Gateway",
|
||||
@@ -176,12 +187,22 @@ export namespace ModelsDev {
|
||||
if (Object.keys(kiloModels).length === 0) {
|
||||
ModelCache.refresh("kilo", kiloFetchOptions).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
// Inject Apertis provider with dynamic model fetching
|
||||
if (!providers["apertis"]) {
|
||||
const apertisConfigObj = await Config.get()
|
||||
const apertisConfig = apertisConfigObj.provider?.apertis?.options
|
||||
if (!providers["apertis"] && apertisModels !== null) {
|
||||
providers["apertis"] = {
|
||||
id: "apertis",
|
||||
name: "Apertis",
|
||||
env: ["APERTIS_API_KEY"],
|
||||
api: apertisBaseURL,
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
models: apertisModels,
|
||||
}
|
||||
if (Object.keys(apertisModels).length === 0) {
|
||||
ModelCache.refresh("apertis", apertisFetchOptions).catch(() => {})
|
||||
}
|
||||
}
|
||||
} else if (!providers["apertis"]) {
|
||||
const apertisConfig = config.provider?.apertis?.options
|
||||
const apertisBaseURL = apertisConfig?.baseURL ?? "https://api.apertis.ai/v1"
|
||||
const apertisFetchOptions = {
|
||||
...(apertisConfig?.baseURL ? { baseURL: apertisConfig.baseURL } : {}),
|
||||
|
||||
Reference in New Issue
Block a user