mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
Compare commits
5 Commits
fix/eval-e
...
feat/optim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6fd8d8140 | ||
|
|
4bd6071fc6 | ||
|
|
adfec7fde7 | ||
|
|
cb7d00bb3b | ||
|
|
bc56503957 |
@@ -16,16 +16,16 @@ import { ConversationIdParamSchema } from '../utils/validation'
|
||||
interface ChatRouteDeps {
|
||||
browser: Browser
|
||||
registry: ToolRegistry
|
||||
klavisClient: KlavisClient
|
||||
browserosId?: string
|
||||
rateLimiter?: RateLimiter
|
||||
aiSdkDevtoolsEnabled?: boolean
|
||||
}
|
||||
|
||||
export function createChatRoutes(deps: ChatRouteDeps) {
|
||||
const { browserosId, rateLimiter } = deps
|
||||
const { browserosId, rateLimiter, klavisClient } = deps
|
||||
|
||||
const sessionStore = new SessionStore()
|
||||
const klavisClient = new KlavisClient()
|
||||
const service = new ChatService({
|
||||
sessionStore,
|
||||
klavisClient,
|
||||
|
||||
@@ -17,6 +17,7 @@ const ServerNameSchema = z.object({
|
||||
|
||||
interface KlavisRouteDeps {
|
||||
browserosId: string
|
||||
klavisClient: KlavisClient
|
||||
}
|
||||
|
||||
const normalizeServerKey = (value: string): string =>
|
||||
@@ -43,8 +44,7 @@ const getAuthUrlForServer = (
|
||||
}
|
||||
|
||||
export function createKlavisRoutes(deps: KlavisRouteDeps) {
|
||||
const { browserosId } = deps
|
||||
const klavisClient = new KlavisClient()
|
||||
const { browserosId, klavisClient } = deps
|
||||
|
||||
// Chain route definitions for proper Hono RPC type inference
|
||||
return new Hono()
|
||||
@@ -124,6 +124,7 @@ export function createKlavisRoutes(deps: KlavisRouteDeps) {
|
||||
|
||||
logger.info('Adding server to strata', { serverName })
|
||||
|
||||
klavisClient.invalidateStrataCache(browserosId)
|
||||
const result = await klavisClient.createStrata(browserosId, [serverName])
|
||||
|
||||
return c.json({
|
||||
@@ -154,6 +155,7 @@ export function createKlavisRoutes(deps: KlavisRouteDeps) {
|
||||
|
||||
try {
|
||||
await klavisClient.submitApiKey(apiKeyUrl, apiKey)
|
||||
klavisClient.invalidateStrataCache(browserosId)
|
||||
|
||||
logger.info('Submitted API key for server', { serverName })
|
||||
|
||||
@@ -185,6 +187,7 @@ export function createKlavisRoutes(deps: KlavisRouteDeps) {
|
||||
logger.info('Removing server from strata', { serverName })
|
||||
|
||||
await klavisClient.removeServer(browserosId, serverName)
|
||||
klavisClient.invalidateStrataCache(browserosId)
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
|
||||
@@ -75,12 +75,15 @@ export async function createHttpServer(config: HttpServerConfig) {
|
||||
|
||||
const { onShutdown } = config
|
||||
|
||||
// Single KlavisClient shared across all routes for cache effectiveness
|
||||
const klavisClient = new KlavisClient()
|
||||
|
||||
// Connect Klavis proxy (non-blocking: browser tools still work if this fails)
|
||||
let klavisProxy: KlavisProxyHandle | null = null
|
||||
if (browserosId) {
|
||||
try {
|
||||
klavisProxy = await connectKlavisProxy({
|
||||
klavisClient: new KlavisClient(),
|
||||
klavisClient,
|
||||
browserosId,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -115,7 +118,7 @@ export async function createHttpServer(config: HttpServerConfig) {
|
||||
.route('/skills', createSkillsRoutes())
|
||||
.route('/test-provider', createProviderRoutes())
|
||||
.route('/refine-prompt', createRefinePromptRoutes())
|
||||
.route('/klavis', createKlavisRoutes({ browserosId: browserosId || '' }))
|
||||
.route('/klavis', createKlavisRoutes({ browserosId: browserosId || '', klavisClient }))
|
||||
.route(
|
||||
'/mcp',
|
||||
createMcpRoutes({
|
||||
@@ -132,6 +135,7 @@ export async function createHttpServer(config: HttpServerConfig) {
|
||||
createChatRoutes({
|
||||
browser,
|
||||
registry,
|
||||
klavisClient,
|
||||
browserosId,
|
||||
rateLimiter,
|
||||
aiSdkDevtoolsEnabled: config.aiSdkDevtoolsEnabled,
|
||||
|
||||
@@ -28,13 +28,25 @@ export interface UserIntegration {
|
||||
isAuthenticated: boolean
|
||||
}
|
||||
|
||||
interface CachedStrata {
|
||||
response: StrataCreateResponse
|
||||
expiresAt: number
|
||||
}
|
||||
|
||||
export class KlavisClient {
|
||||
private baseUrl: string
|
||||
private strataCache = new Map<string, CachedStrata>()
|
||||
private pendingRequests = new Map<string, Promise<StrataCreateResponse>>()
|
||||
private static STRATA_CACHE_TTL = 5 * 60 * 1000 // 5 minutes
|
||||
|
||||
constructor(baseUrl?: string) {
|
||||
this.baseUrl = baseUrl || EXTERNAL_URLS.KLAVIS_PROXY
|
||||
}
|
||||
|
||||
private buildStrataCacheKey(userId: string, servers: string[]): string {
|
||||
return JSON.stringify([userId, ...[...servers].sort()])
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
method: string,
|
||||
path: string,
|
||||
@@ -77,18 +89,44 @@ export class KlavisClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Strata instance with specified servers
|
||||
* Returns strataServerUrl for MCP connection and oauthUrls for authentication
|
||||
* Create Strata instance with specified servers.
|
||||
* Cached by (userId, sorted servers) with 5-min TTL.
|
||||
* Concurrent requests for the same key are deduplicated.
|
||||
*/
|
||||
async createStrata(
|
||||
userId: string,
|
||||
servers: string[],
|
||||
): Promise<StrataCreateResponse> {
|
||||
return this.request<StrataCreateResponse>(
|
||||
const cacheKey = this.buildStrataCacheKey(userId, servers)
|
||||
|
||||
const cached = this.strataCache.get(cacheKey)
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.response
|
||||
}
|
||||
|
||||
const pending = this.pendingRequests.get(cacheKey)
|
||||
if (pending) return pending
|
||||
|
||||
const promise = this.request<StrataCreateResponse>(
|
||||
'POST',
|
||||
'/mcp-server/strata/create',
|
||||
{ userId, servers },
|
||||
)
|
||||
.then((response) => {
|
||||
this.strataCache.set(cacheKey, {
|
||||
response,
|
||||
expiresAt: Date.now() + KlavisClient.STRATA_CACHE_TTL,
|
||||
})
|
||||
this.pendingRequests.delete(cacheKey)
|
||||
return response
|
||||
})
|
||||
.catch((error) => {
|
||||
this.pendingRequests.delete(cacheKey)
|
||||
throw error
|
||||
})
|
||||
|
||||
this.pendingRequests.set(cacheKey, promise)
|
||||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,5 +194,15 @@ export class KlavisClient {
|
||||
'DELETE',
|
||||
`/mcp-server/strata/${strata.strataId}/servers?servers=${encodeURIComponent(serverName)}`,
|
||||
)
|
||||
this.strataCache.delete(this.buildStrataCacheKey(userId, [serverName]))
|
||||
}
|
||||
|
||||
/** Clear all cached Strata entries for a user (call after auth changes). */
|
||||
invalidateStrataCache(userId: string): void {
|
||||
for (const key of this.strataCache.keys()) {
|
||||
if (key.startsWith(`["${userId}"`)) {
|
||||
this.strataCache.delete(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user