diff --git a/packages/web-ui/example/src/main.ts b/packages/web-ui/example/src/main.ts index c2cf293e..2a87c9c5 100644 --- a/packages/web-ui/example/src/main.ts +++ b/packages/web-ui/example/src/main.ts @@ -8,6 +8,7 @@ import { ChatPanel, CustomProvidersStore, createJavaScriptReplTool, + createStreamFn, IndexedDBStorageBackend, // PersistentStorageDialog, // TODO: Fix - currently broken ProviderKeysStore, @@ -160,6 +161,10 @@ const createAgent = async (initialState?: Partial) => { agentUnsubscribe(); } + // Read proxy settings for streamFn + const proxyEnabled = await storage.settings.get("proxy.enabled"); + const proxyUrl = proxyEnabled ? (await storage.settings.get("proxy.url")) || undefined : undefined; + agent = new Agent({ initialState: initialState || { systemPrompt: `You are a helpful AI assistant with access to various tools. @@ -181,6 +186,8 @@ Feel free to use these tools when needed to provide accurate and helpful respons const key = await storage.providerKeys.get(provider); return key ?? undefined; }, + // Use streamFn with CORS proxy support + streamFn: createStreamFn(proxyUrl), }); agentUnsubscribe = agent.subscribe((event: any) => { diff --git a/packages/web-ui/src/index.ts b/packages/web-ui/src/index.ts index 6c9fd4d5..cff990b0 100644 --- a/packages/web-ui/src/index.ts +++ b/packages/web-ui/src/index.ts @@ -112,3 +112,4 @@ export { loadAttachment } from "./utils/attachment-utils.js"; export { clearAuthToken, getAuthToken } from "./utils/auth-token.js"; export { formatCost, formatModelCost, formatTokenCount, formatUsage } from "./utils/format.js"; export { i18n, setLanguage, translations } from "./utils/i18n.js"; +export { applyProxyIfNeeded, createStreamFn, isCorsError, shouldUseProxyForProvider } from "./utils/proxy-utils.js"; diff --git a/packages/web-ui/src/utils/proxy-utils.ts b/packages/web-ui/src/utils/proxy-utils.ts index 65c68e57..0792809a 100644 --- a/packages/web-ui/src/utils/proxy-utils.ts +++ b/packages/web-ui/src/utils/proxy-utils.ts @@ -1,4 +1,5 @@ -import type { Api, Model } from "@mariozechner/pi-ai"; +import type { Api, Context, Model, SimpleStreamOptions } from "@mariozechner/pi-ai"; +import { streamSimple } from "@mariozechner/pi-ai"; /** * Centralized proxy decision logic. @@ -110,3 +111,21 @@ export function isCorsError(error: unknown): boolean { return false; } + +/** + * Create a streamFn that applies CORS proxy when needed. + * + * @param proxyUrl - CORS proxy URL, or undefined to disable + * @returns A streamFn compatible with Agent's streamFn option + */ +export function createStreamFn(proxyUrl?: string) { + return (model: Model, context: Context, options?: SimpleStreamOptions) => { + const apiKey = options?.apiKey; + if (!apiKey || !proxyUrl) { + return streamSimple(model, context, options); + } + + const proxiedModel = applyProxyIfNeeded(model, apiKey, proxyUrl); + return streamSimple(proxiedModel, context, options); + }; +}