From 0571313a4ac06255853fff83e2e6a3c8c5d7cbf9 Mon Sep 17 00:00:00 2001 From: pipper Date: Tue, 9 Sep 2025 18:08:52 +0200 Subject: [PATCH 1/3] initial endpoint --- .../src/app/plugins/remixAIPlugin.tsx | 6 ++- .../src/app/tabs/locales/en/settings.json | 5 ++- libs/remix-ai-core/src/index.ts | 4 +- .../src/inferencers/local/ollama.ts | 39 +++++++++++++++++++ .../settings/src/lib/remix-ui-settings.tsx | 10 +++++ .../settings/src/lib/settingsReducer.ts | 27 +++++++++++++ libs/remix-ui/settings/src/types/index.ts | 2 + 7 files changed, 89 insertions(+), 4 deletions(-) diff --git a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx index 6503110da8c..b333ca40818 100644 --- a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx +++ b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx @@ -1,6 +1,6 @@ import * as packageJson from '../../../../../package.json' import { Plugin } from '@remixproject/engine'; -import { IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, AssistantParams, CodeExplainAgent, SecurityAgent, CompletionParams, OllamaInferencer, isOllamaAvailable, getBestAvailableModel } from '@remix/remix-ai-core'; +import { IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, AssistantParams, CodeExplainAgent, SecurityAgent, CompletionParams, OllamaInferencer, isOllamaAvailable, getBestAvailableModel, resetOllamaHostOnSettingsChange } from '@remix/remix-ai-core'; import { CodeCompletionAgent, ContractAgent, workspaceAgent, IContextType } from '@remix/remix-ai-core'; import axios from 'axios'; import { endpointUrls } from "@remix-endpoints-helper" @@ -53,6 +53,10 @@ export class RemixAIPlugin extends Plugin { } onActivation(): void { + // Expose Ollama reset function globally for settings integration + if (typeof window !== 'undefined') { + (window as any).resetOllamaHostOnSettingsChange = resetOllamaHostOnSettingsChange; + } if (this.isOnDesktop) { this.useRemoteInferencer = true diff --git a/apps/remix-ide/src/app/tabs/locales/en/settings.json b/apps/remix-ide/src/app/tabs/locales/en/settings.json index 85223986235..0f930d99181 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/en/settings.json @@ -65,5 +65,8 @@ "settings.aiCopilotDescription": "AI Copilot assists with code suggestions and improvements.", "settings.aiPrivacyPolicy": "AI Privacy & Data Usage", "settings.viewPrivacyPolicy": "View Privacy Policy", - "settings.aiPrivacyPolicyDescription": "Understand how AI processes your data." + "settings.aiPrivacyPolicyDescription": "Understand how AI processes your data.", + "settings.ollamaConfig": "Ollama Configuration", + "settings.ollamaConfigDescription": "Configure Ollama endpoint for local AI model integration", + "settings.ollama-endpoint": "ENDPOINT URL" } diff --git a/libs/remix-ai-core/src/index.ts b/libs/remix-ai-core/src/index.ts index 1a8a9693bdd..60401239a26 100644 --- a/libs/remix-ai-core/src/index.ts +++ b/libs/remix-ai-core/src/index.ts @@ -7,7 +7,7 @@ import { DefaultModels, InsertionParams, CompletionParams, GenerationParams, Ass import { buildChatPrompt } from './prompts/promptBuilder' import { RemoteInferencer } from './inferencers/remote/remoteInference' import { OllamaInferencer } from './inferencers/local/ollamaInferencer' -import { isOllamaAvailable, getBestAvailableModel, listModels, discoverOllamaHost } from './inferencers/local/ollama' +import { isOllamaAvailable, getBestAvailableModel, listModels, discoverOllamaHost, resetOllamaHostOnSettingsChange } from './inferencers/local/ollama' import { FIMModelManager, FIMModelConfig, FIM_MODEL_CONFIGS } from './inferencers/local/fimModelConfig' import { ChatHistory } from './prompts/chat' import { downloadLatestReleaseExecutable } from './helpers/inferenceServerReleases' @@ -15,7 +15,7 @@ import { ChatCommandParser } from './helpers/chatCommandParser' export { IModel, IModelResponse, ChatCommandParser, ModelType, DefaultModels, ICompletions, IParams, IRemoteModel, buildChatPrompt, - RemoteInferencer, OllamaInferencer, isOllamaAvailable, getBestAvailableModel, listModels, discoverOllamaHost, + RemoteInferencer, OllamaInferencer, isOllamaAvailable, getBestAvailableModel, listModels, discoverOllamaHost, resetOllamaHostOnSettingsChange, FIMModelManager, FIMModelConfig, FIM_MODEL_CONFIGS, InsertionParams, CompletionParams, GenerationParams, AssistantParams, ChatEntry, AIRequestType, ChatHistory, downloadLatestReleaseExecutable diff --git a/libs/remix-ai-core/src/inferencers/local/ollama.ts b/libs/remix-ai-core/src/inferencers/local/ollama.ts index 605197ddef3..8075e855797 100644 --- a/libs/remix-ai-core/src/inferencers/local/ollama.ts +++ b/libs/remix-ai-core/src/inferencers/local/ollama.ts @@ -1,19 +1,52 @@ import axios from 'axios'; +import { Registry } from '@remix-project/remix-lib'; const _paq = (typeof window !== 'undefined' && (window as any)._paq) ? (window as any)._paq : [] // default Ollama ports to check (11434 is the legacy/standard port) const OLLAMA_PORTS = [11434, 11435, 11436]; const OLLAMA_BASE_HOST = 'http://localhost'; +const DEFAULT_OLLAMA_HOST = 'http://localhost:11434'; let discoveredOllamaHost: string | null = null; +function getConfiguredOllamaEndpoint(): string | null { + try { + const config = Registry.getInstance().get('config').api; + const configuredEndpoint = config.get('settings/ollama-endpoint'); + if (configuredEndpoint && configuredEndpoint !== DEFAULT_OLLAMA_HOST) { + _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_using_configured_endpoint', configuredEndpoint]); + return configuredEndpoint; + } + } catch (error) { + _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_config_access_failed', error.message || 'unknown']); + } + return null; +} + export async function discoverOllamaHost(): Promise { if (discoveredOllamaHost) { _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_host_cache_hit', discoveredOllamaHost]); return discoveredOllamaHost; } + // First, try to use the configured endpoint from settings + const configuredEndpoint = getConfiguredOllamaEndpoint(); + if (configuredEndpoint) { + try { + const res = await axios.get(`${configuredEndpoint}/api/tags`, { timeout: 2000 }); + if (res.status === 200) { + discoveredOllamaHost = configuredEndpoint; + _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_configured_endpoint_success', configuredEndpoint]); + return configuredEndpoint; + } + } catch (error) { + _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_configured_endpoint_failed', `${configuredEndpoint}:${error.message || 'unknown'}`]); + // Fall back to discovery if configured endpoint fails + } + } + + // Fall back to port discovery if no configured endpoint or it failed for (const port of OLLAMA_PORTS) { const host = `${OLLAMA_BASE_HOST}:${port}`; _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_port_check', `${port}`]); @@ -66,6 +99,12 @@ export function resetOllamaHost(): void { discoveredOllamaHost = null; } +export function resetOllamaHostOnSettingsChange(): void { + // This function should be called when Ollama settings are updated + resetOllamaHost(); + _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_reset_on_settings_change']); +} + export async function pullModel(modelName: string): Promise { // in case the user wants to pull a model from registry _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_pull_model_start', modelName]); diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 409c7a9dcc7..80206235bf2 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -139,6 +139,16 @@ const settingsSections: SettingsSection[] = [ action: 'link', link: 'https://remix-ide.readthedocs.io/en/latest/ai.html' } + }, + { + name: 'ollama-config', + label: 'settings.ollamaConfig', + description: 'settings.ollamaConfigDescription', + type: 'toggle', + toggleUIOptions: [{ + name: 'ollama-endpoint', + type: 'text' + }] }] } ]}, diff --git a/libs/remix-ui/settings/src/lib/settingsReducer.ts b/libs/remix-ui/settings/src/lib/settingsReducer.ts index c4b703f03e8..1825c47eace 100644 --- a/libs/remix-ui/settings/src/lib/settingsReducer.ts +++ b/libs/remix-ui/settings/src/lib/settingsReducer.ts @@ -17,12 +17,14 @@ const swarmPrivateBeeAddress = config.get('settings/swarm-private-bee-address') const swarmPostageStampId = config.get('settings/swarm-postage-stamp-id') || '' const sindriAccessToken = config.get('settings/sindri-access-token') || '' const etherscanAccessToken = config.get('settings/etherscan-access-token') || '' +const ollamaEndpoint = config.get('settings/ollama-endpoint') || 'http://localhost:11434' let githubConfig = config.get('settings/github-config') || false let ipfsConfig = config.get('settings/ipfs-config') || false let swarmConfig = config.get('settings/swarm-config') || false let sindriConfig = config.get('settings/sindri-config') || false let etherscanConfig = config.get('settings/etherscan-config') || false +let ollamaConfig = config.get('settings/ollama-config') || false let generateContractMetadata = config.get('settings/generate-contract-metadata') let autoCompletion = config.get('settings/auto-completion') let showGas = config.get('settings/show-gas') @@ -49,6 +51,10 @@ if (!etherscanConfig && etherscanAccessToken) { config.set('settings/etherscan-config', true) etherscanConfig = true } +if (!ollamaConfig && ollamaEndpoint !== 'http://localhost:11434') { + config.set('settings/ollama-config', true) + ollamaConfig = true +} if (typeof generateContractMetadata !== 'boolean') { config.set('settings/generate-contract-metadata', true) generateContractMetadata = true @@ -191,6 +197,14 @@ export const initialState: SettingsState = { value: '', isLoading: false }, + 'ollama-config': { + value: ollamaConfig, + isLoading: false + }, + 'ollama-endpoint': { + value: ollamaEndpoint, + isLoading: false + }, toaster: { value: '', isLoading: false @@ -201,6 +215,19 @@ export const settingReducer = (state: SettingsState, action: SettingsActions): S switch (action.type) { case 'SET_VALUE': config.set('settings/' + action.payload.name, action.payload.value) + + // Reset Ollama host cache when endpoint is changed + if (action.payload.name === 'ollama-endpoint') { + try { + // Check if the resetOllamaHostOnSettingsChange function is available globally + if (typeof window !== 'undefined' && (window as any).resetOllamaHostOnSettingsChange) { + (window as any).resetOllamaHostOnSettingsChange(); + } + } catch (error) { + // Ignore errors - Ollama functionality is optional + } + } + return { ...state, [action.payload.name]: { ...state[action.payload.name], value: action.payload.value, isLoading: false } } case 'SET_LOADING': return { ...state, [action.payload.name]: { ...state[action.payload.name], isLoading: true } } diff --git a/libs/remix-ui/settings/src/types/index.ts b/libs/remix-ui/settings/src/types/index.ts index 03e938aab22..c974e8dea61 100644 --- a/libs/remix-ui/settings/src/types/index.ts +++ b/libs/remix-ui/settings/src/types/index.ts @@ -112,6 +112,8 @@ export interface SettingsState { 'sindri-access-token': ConfigState, 'etherscan-access-token': ConfigState, 'ai-privacy-policy': ConfigState, + 'ollama-config': ConfigState, + 'ollama-endpoint': ConfigState, toaster: ConfigState } export interface SettingsActionPayloadTypes { From 63e67d0b4ffb8ca49ebf94519177f80fc838fd25 Mon Sep 17 00:00:00 2001 From: pipper Date: Wed, 10 Sep 2025 09:13:27 +0200 Subject: [PATCH 2/3] working on remote URL --- apps/remix-ide/src/app/plugins/remixAIPlugin.tsx | 1 - apps/remix-ide/src/app/tabs/locales/en/settings.json | 2 +- libs/remix-ai-core/src/inferencers/local/ollama.ts | 4 +++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx index b333ca40818..6490859916c 100644 --- a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx +++ b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx @@ -423,7 +423,6 @@ export class RemixAIPlugin extends Plugin { this.isInferencing = false }) - console.log(`Ollama model changed to: ${modelName}`) } catch (error) { console.error('Failed to set Ollama model:', error) } diff --git a/apps/remix-ide/src/app/tabs/locales/en/settings.json b/apps/remix-ide/src/app/tabs/locales/en/settings.json index 0f930d99181..52c898fbcf3 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/en/settings.json @@ -66,7 +66,7 @@ "settings.aiPrivacyPolicy": "AI Privacy & Data Usage", "settings.viewPrivacyPolicy": "View Privacy Policy", "settings.aiPrivacyPolicyDescription": "Understand how AI processes your data.", - "settings.ollamaConfig": "Ollama Configuration", + "settings.ollamaConfig": "Ollama URL Configuration", "settings.ollamaConfigDescription": "Configure Ollama endpoint for local AI model integration", "settings.ollama-endpoint": "ENDPOINT URL" } diff --git a/libs/remix-ai-core/src/inferencers/local/ollama.ts b/libs/remix-ai-core/src/inferencers/local/ollama.ts index 8075e855797..db9b62dc0f9 100644 --- a/libs/remix-ai-core/src/inferencers/local/ollama.ts +++ b/libs/remix-ai-core/src/inferencers/local/ollama.ts @@ -40,13 +40,15 @@ export async function discoverOllamaHost(): Promise { _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_configured_endpoint_success', configuredEndpoint]); return configuredEndpoint; } + return null; } catch (error) { _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_configured_endpoint_failed', `${configuredEndpoint}:${error.message || 'unknown'}`]); // Fall back to discovery if configured endpoint fails + return null; } } - // Fall back to port discovery if no configured endpoint or it failed + // Fall back to port discovery if no configured endpoint for (const port of OLLAMA_PORTS) { const host = `${OLLAMA_BASE_HOST}:${port}`; _paq.push(['trackEvent', 'ai', 'remixAI', 'ollama_port_check', `${port}`]); From eee9a127fb95d525b08d942cc0c9002ecd1964c5 Mon Sep 17 00:00:00 2001 From: pipper Date: Thu, 11 Sep 2025 09:35:48 +0200 Subject: [PATCH 3/3] rm global function --- .gitignore | 3 ++- apps/remix-ide/src/app/plugins/remixAIPlugin.tsx | 4 +--- libs/remix-ui/settings/src/lib/settingsReducer.ts | 7 ++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 6599f59e045..2cfd04832e6 100644 --- a/.gitignore +++ b/.gitignore @@ -77,4 +77,5 @@ apps/remixdesktop/circom-download apps/remixdesktop/log_input_signals.txt apps/remixdesktop/log_input_signals_new.txt logs -apps/remix-ide-e2e/src/extensions/chrome/metamask \ No newline at end of file +apps/remix-ide-e2e/src/extensions/chrome/metamask +apps/remix-ide-e2e/tmp/ \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx index 6490859916c..3323fe3b003 100644 --- a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx +++ b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx @@ -54,9 +54,7 @@ export class RemixAIPlugin extends Plugin { onActivation(): void { // Expose Ollama reset function globally for settings integration - if (typeof window !== 'undefined') { - (window as any).resetOllamaHostOnSettingsChange = resetOllamaHostOnSettingsChange; - } + resetOllamaHostOnSettingsChange(); if (this.isOnDesktop) { this.useRemoteInferencer = true diff --git a/libs/remix-ui/settings/src/lib/settingsReducer.ts b/libs/remix-ui/settings/src/lib/settingsReducer.ts index 1825c47eace..2c956f7d7b7 100644 --- a/libs/remix-ui/settings/src/lib/settingsReducer.ts +++ b/libs/remix-ui/settings/src/lib/settingsReducer.ts @@ -1,6 +1,6 @@ import { Registry } from '@remix-project/remix-lib' import { SettingsActions, SettingsState } from '../types' - +import { resetOllamaHostOnSettingsChange } from '@remix/remix-ai-core'; const config = Registry.getInstance().get('config').api const settingsConfig = Registry.getInstance().get('settingsConfig').api const defaultTheme = config.get('settings/theme') ? settingsConfig.themes.find((theme) => theme.name.toLowerCase() === config.get('settings/theme').toLowerCase()) : settingsConfig.themes[0] @@ -219,10 +219,7 @@ export const settingReducer = (state: SettingsState, action: SettingsActions): S // Reset Ollama host cache when endpoint is changed if (action.payload.name === 'ollama-endpoint') { try { - // Check if the resetOllamaHostOnSettingsChange function is available globally - if (typeof window !== 'undefined' && (window as any).resetOllamaHostOnSettingsChange) { - (window as any).resetOllamaHostOnSettingsChange(); - } + resetOllamaHostOnSettingsChange(); } catch (error) { // Ignore errors - Ollama functionality is optional }