anky2002 commited on
Commit
5e4f7cd
·
verified ·
1 Parent(s): 23129e3

feat: OllamaProvider auto-detects extension bridge, falls back to direct connection

Browse files
Files changed (1) hide show
  1. src/contexts/ollama-context.tsx +76 -10
src/contexts/ollama-context.tsx CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { createContext, useContext, useState, useEffect, ReactNode } from "react"
4
 
5
- type OllamaMode = "local" | "cloud"
6
 
7
  interface OllamaSettings {
8
  mode: OllamaMode
@@ -10,6 +10,7 @@ interface OllamaSettings {
10
  cloudApiKey: string
11
  enabled: boolean
12
  availableModels: string[]
 
13
  }
14
 
15
  interface OllamaContextType {
@@ -29,6 +30,7 @@ const defaultSettings: OllamaSettings = {
29
  cloudApiKey: "",
30
  enabled: false,
31
  availableModels: [],
 
32
  }
33
 
34
  const OllamaContext = createContext<OllamaContextType | null>(null)
@@ -52,15 +54,42 @@ export function OllamaProvider({ children }: { children: ReactNode }) {
52
  }
53
  }, [])
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  // Save settings to localStorage when changed
56
  const updateSettings = (updates: Partial<OllamaSettings>) => {
57
  setSettings(prev => {
58
  const newSettings = { ...prev, ...updates }
59
  try {
60
- // Don't persist the API key in plain text - only save non-sensitive settings
61
- const toStore = { ...newSettings }
62
- // For security, we still store it but in a real app you might want to encrypt it
63
- localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore))
64
  } catch (error) {
65
  console.error("Failed to save Ollama settings:", error)
66
  }
@@ -70,7 +99,8 @@ export function OllamaProvider({ children }: { children: ReactNode }) {
70
 
71
  // Get the effective endpoint based on mode
72
  const getEffectiveEndpoint = (): string => {
73
- return settings.mode === "cloud" ? OLLAMA_CLOUD_URL : settings.apiUrl
 
74
  }
75
 
76
  // Get auth headers for API requests
@@ -82,11 +112,16 @@ export function OllamaProvider({ children }: { children: ReactNode }) {
82
  return headers
83
  }
84
 
85
- // Test connection to Ollama and fetch available models via API route (avoids CORS)
86
  const testConnection = async (): Promise<{ success: boolean; models?: string[]; error?: string }> => {
87
  setIsLoading(true)
88
  try {
89
- // Use our API route to proxy the request (avoids CORS issues with cloud)
 
 
 
 
 
90
  const params = new URLSearchParams({
91
  mode: settings.mode,
92
  url: settings.apiUrl,
@@ -109,9 +144,7 @@ export function OllamaProvider({ children }: { children: ReactNode }) {
109
  }
110
 
111
  const models = data.models?.map((m: { name: string }) => m.name) || []
112
-
113
  updateSettings({ availableModels: models, enabled: true })
114
-
115
  return { success: true, models }
116
  } catch (error) {
117
  const errorMessage = error instanceof Error ? error.message : "Connection failed"
@@ -121,6 +154,39 @@ export function OllamaProvider({ children }: { children: ReactNode }) {
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  return (
125
  <OllamaContext.Provider value={{
126
  settings,
 
2
 
3
  import { createContext, useContext, useState, useEffect, ReactNode } from "react"
4
 
5
+ type OllamaMode = "local" | "cloud" | "bridge"
6
 
7
  interface OllamaSettings {
8
  mode: OllamaMode
 
10
  cloudApiKey: string
11
  enabled: boolean
12
  availableModels: string[]
13
+ bridgeDetected: boolean
14
  }
15
 
16
  interface OllamaContextType {
 
30
  cloudApiKey: "",
31
  enabled: false,
32
  availableModels: [],
33
+ bridgeDetected: false,
34
  }
35
 
36
  const OllamaContext = createContext<OllamaContextType | null>(null)
 
54
  }
55
  }, [])
56
 
57
+ // Detect extension bridge availability
58
+ useEffect(() => {
59
+ const detectBridge = () => {
60
+ const hasBridge = document.documentElement.getAttribute('data-ollama-bridge') === 'true'
61
+ if (hasBridge) {
62
+ setSettings(prev => ({ ...prev, bridgeDetected: true }))
63
+ }
64
+ }
65
+
66
+ // Check immediately
67
+ detectBridge()
68
+
69
+ // Listen for bridge ready message
70
+ const handleMessage = (event: MessageEvent) => {
71
+ if (event.data?.type === 'OLLAMA_BRIDGE_READY') {
72
+ setSettings(prev => ({ ...prev, bridgeDetected: true }))
73
+ }
74
+ }
75
+
76
+ window.addEventListener('message', handleMessage)
77
+
78
+ // Also check after a short delay (extension might load after our script)
79
+ const timer = setTimeout(detectBridge, 2000)
80
+
81
+ return () => {
82
+ window.removeEventListener('message', handleMessage)
83
+ clearTimeout(timer)
84
+ }
85
+ }, [])
86
+
87
  // Save settings to localStorage when changed
88
  const updateSettings = (updates: Partial<OllamaSettings>) => {
89
  setSettings(prev => {
90
  const newSettings = { ...prev, ...updates }
91
  try {
92
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newSettings))
 
 
 
93
  } catch (error) {
94
  console.error("Failed to save Ollama settings:", error)
95
  }
 
99
 
100
  // Get the effective endpoint based on mode
101
  const getEffectiveEndpoint = (): string => {
102
+ if (settings.mode === "cloud") return OLLAMA_CLOUD_URL
103
+ return settings.apiUrl
104
  }
105
 
106
  // Get auth headers for API requests
 
112
  return headers
113
  }
114
 
115
+ // Test connection to Ollama
116
  const testConnection = async (): Promise<{ success: boolean; models?: string[]; error?: string }> => {
117
  setIsLoading(true)
118
  try {
119
+ // If bridge is available and mode is local, use bridge for zero-config
120
+ if (settings.bridgeDetected && settings.mode === "local") {
121
+ return await testViaBridge()
122
+ }
123
+
124
+ // Otherwise use the API route proxy
125
  const params = new URLSearchParams({
126
  mode: settings.mode,
127
  url: settings.apiUrl,
 
144
  }
145
 
146
  const models = data.models?.map((m: { name: string }) => m.name) || []
 
147
  updateSettings({ availableModels: models, enabled: true })
 
148
  return { success: true, models }
149
  } catch (error) {
150
  const errorMessage = error instanceof Error ? error.message : "Connection failed"
 
154
  }
155
  }
156
 
157
+ // Test connection via extension bridge (zero CORS config)
158
+ const testViaBridge = (): Promise<{ success: boolean; models?: string[]; error?: string }> => {
159
+ return new Promise((resolve) => {
160
+ const id = `test_${Date.now()}`
161
+ const timeout = setTimeout(() => {
162
+ window.removeEventListener('message', handler)
163
+ resolve({ success: false, error: 'Bridge timeout — is Ollama running?' })
164
+ }, 8000)
165
+
166
+ const handler = (event: MessageEvent) => {
167
+ if (event.data?.id !== id) return
168
+
169
+ clearTimeout(timeout)
170
+ window.removeEventListener('message', handler)
171
+
172
+ if (event.data.type === 'OLLAMA_ERROR') {
173
+ resolve({ success: false, error: event.data.error })
174
+ } else {
175
+ const models = event.data.data?.models?.map((m: { name: string }) => m.name) || []
176
+ updateSettings({ availableModels: models, enabled: true, mode: "bridge" })
177
+ resolve({ success: true, models })
178
+ }
179
+ }
180
+
181
+ window.addEventListener('message', handler)
182
+ window.postMessage({
183
+ type: 'OLLAMA_LIST_MODELS',
184
+ id,
185
+ ollamaUrl: settings.apiUrl,
186
+ }, '*')
187
+ })
188
+ }
189
+
190
  return (
191
  <OllamaContext.Provider value={{
192
  settings,