Files
BrowserOS/reference-code/old-lib/llm/settings/LLMSettingsReader.ts
Felarof 8245dfe0ff Rewrite Agent Loop (#7)
* clean-up bunch of files for re-write

* more clean-up and adding basic agent

* Minor fix moved types into respective files.

* Deleted bunch of old files

backup

Update gitignore

Deleted a bunch of files

Remove message manager

Deleted old docs

Update rules

rename Profiler to profiler

* Temporarily adding old code

* Adding two small things back

* backup

* Implemented LangChainProvider and updated cursor rules

backup

LangChainProvider

curosr rules

* Implement tests for LangChainProvider -- unit test and integration test

integration test passes

integration test backup

* Tool Design

Tools Desing

tools design

* NavigationTool ready

NavigationTool ready

NavigationTool ready

NaivgationTool ready

backup

* MessageManager

MessageManager

backup

* Fixed integration test

* Agent design new

Updated agent design and added bunch of /NTN commands

agent new design

* Delete old agent design

* MessageManagerReadOnly class

* PlannerTool ready

PlannerTool almost ready

* ToolManager and DoneTool

* Integration of BrowserAgent

* BrowserAgent implementation v0.1

* BrowserAgent small fix v0.2

* Tool calling design

too call design

tool design claude

* Update agent tool design with // NTN

* add zod-to-json npm install

* BrowserAGent v0.3

* BrowserAgent v0.4

* BrowserAgent v0.5

* fixes

* Build error fixes in my NEWLY added code

build errors fix

* Build error fixes in old code (integration work)

backup

* Comment StreamEventProcessor for now, it is not used

* Small build error fix

* Small rename

* Added integration test to check structuredLLM and changed to 4o-mini

change default to nxtscape

integration test

* Small docstring

* Simplified BrowserAgent code and added integration test

Simplified BrowserAgent code

BrowserAGent integrationt est

* Update CLAUDE.md with project memory and instructions on how to write code

Update CLAUDE.md with project memory and instructions on how to write code

Project Memory

* Just a mova.. Moved ToolManager outside. Build works.

* TabOperations tool

TabOperations Tool and fixing some test

tab operations

* Update CLAUDE.md

* Added ClassificationTool

classifiction tool

classification prommpt

* Refactored and simplified PlannerTool unit test and integration test

* Updated Plnnaer tool

* Update CLAUDE.md

* BrowserAgent modified to do classification

BrowserAgent with classification

* minor fix to ToolManager

* Instead of ToolCall and ToolResult -- just updating message manager once

* minor fix to BrowserAgent integration test

* Changed done to "done_tool"

* Updated CLAUDE.md to reflect understanding of claude

* Uncommented stream event processor

* Renamed EventBus to StreamEventBus

* Commented StreamEventProcessor

* Event Processor

* Integrated EventProcessor with BrowserAgent

Added EventProcessor to BrowserAgetn

* Renamed StreamEventBus to EventBus

* Made EventBus required parameter in ExecutionContext

* PlanGenerator rewrite

PlanGenerator rewrite

backup

* For simple task, explicitly tell it to call done tool

* Max attempts for simple task

* backup

* Revert "backup"

This reverts commit 7d79a3d4d5774bfef79ec9827878b74edad3593f.

* Consolidating where EventBus and EventProcessor are created and initialized

backup

* Update CLAUDE.md

Update CLAUDE.md

* Improving agent loop code

Cleaned up processTooCall

classification task

* Create test-writer subAgent

test-agent-prompt

test agent prompt

test-agent-prompt

Update test-writer.md

* BrowserAgent test

Browseragent test

BrowserAgent test

* BrowserAgent refactor

backup

backup

* Minor fixes

* Minor fix

* minor change -- NEW AGENT LOOP IS WORKING WELL

* Update cursor rules

* Small change

* Improved BrowserAgent integration test

Improved BrowserAgent integration test

* Small change

* Update CLAUDE.md

* Different tools

* FindElementTool is ready

Find element update

backup

find element backup

* Updated to test strings to say "tests..."

* ScrollTool is ready

* RefreshStateTool is updated as well

* MessageManager updated

* SearchTool is ready

backup

* Interaction Element is also ready

* Add debugMessage emitter

* ValidatorTool ready and tests are passing

Validation Tool

validator tool

backup

backup

* GroupTabs tool ready

* Registered all the tools

* Planning changed to 5 steps

* BrowserAgent integration test fix

* Minor string changes

* backup

* Removed too many confusing events in EventProcessor -- there is only event.info right now

* Abort control implemented

backup

Abort

* Formatter for toolResult

Formatter for toolResult

backup

* Always render using Markdown

* Minor fix

---------

Co-authored-by: Nikhil Sonti <nikhilsv92@gmail.com>
2025-07-29 08:14:45 -07:00

234 lines
8.5 KiB
TypeScript

import { Logging } from '@/lib/utils/Logging'
import { isMockLLMSettings } from '@/config'
import {
LLMSettings,
LLMSettingsSchema,
PREFERENCE_KEYS,
ProviderType
} from './types'
// Type definitions for chrome.settingsPrivate API
declare global {
namespace chrome {
namespace settingsPrivate {
interface PrefObject {
key?: string
type?: string
value?: any
controlledBy?: string
enforcement?: string
recommendedValue?: any
userControlDisabled?: boolean
}
function getPref(name: string, callback: (pref: PrefObject) => void): void
function setPref(name: string, value: any, pageId: string, callback?: (success: boolean) => void): void
function getAllPrefs(callback: (prefs: PrefObject[]) => void): void
const onPrefsChanged: {
addListener(callback: (prefs: PrefObject[]) => void): void
removeListener(callback: (prefs: PrefObject[]) => void): void
}
}
}
}
/**
* Mock preference values for development mode
*
* IMPORTANT: These are mock values for development/testing only!
* They will only be used when:
* 1. DEV_MODE is true in src/config.ts
* 2. chrome.settingsPrivate API is not available
*
* To test different providers, change 'nxtscape.default_provider' to:
* - 'nxtscape' (default) - Uses Nxtscape proxy, no real API key needed
* - 'openai' - Would require real API key in production
* - 'anthropic' - Would require real API key in production
* - 'gemini' - Would require real Google AI API key in production
* - 'ollama' - Requires local Ollama server running
*/
const MOCK_PREFERENCES: Record<string, string | undefined> = {
'nxtscape.default_provider': 'gemini', // Change this to test different providers
'nxtscape.nxtscape_model': 'claude-3-5-sonnet',
'nxtscape.openai_api_key': 'TBD',
'nxtscape.openai_model': 'gpt-4o',
'nxtscape.openai_base_url': undefined,
'nxtscape.anthropic_api_key': 'TBD',
'nxtscape.anthropic_model': 'claude-3-5-sonnet-latest',
'nxtscape.anthropic_base_url': undefined,
'nxtscape.gemini_api_key': 'TBD',
'nxtscape.gemini_model': 'gemini-2.0-flash',
'nxtscape.gemini_base_url': undefined,
'nxtscape.ollama_base_url': 'http://localhost:11434',
'nxtscape.ollama_model': 'qwen3:4b',
'nxtscape.ollama_api_key': undefined // Optional
}
/**
* Reads LLM settings from browser preferences
*/
export class LLMSettingsReader {
/**
* Override mock preferences for testing (DEV MODE ONLY)
* @param overrides - Partial preferences to override
*/
static setMockPreferences(overrides: Partial<typeof MOCK_PREFERENCES>): void {
if (!isMockLLMSettings()) {
Logging.log('LLMSettingsReader', 'setMockPreferences is only available in development mode', 'warning')
return
}
Object.assign(MOCK_PREFERENCES, overrides)
Logging.log('LLMSettingsReader', `Mock preferences updated: ${JSON.stringify(overrides)}`)
}
/**
* Read all LLM settings from Chrome storage
* @returns Promise resolving to LLM settings
*/
static async read(): Promise<LLMSettings> {
try {
Logging.log('LLMSettingsReader', 'Reading LLM settings from Chrome preferences')
// Get all preference values from Chrome storage
const preferences = await this.getPreferences()
// Construct settings object
const settings: LLMSettings = {
defaultProvider: this.getProviderType(preferences[PREFERENCE_KEYS.DEFAULT_PROVIDER]),
nxtscape: {
model: preferences[PREFERENCE_KEYS.NXTSCAPE_MODEL]
},
openai: {
apiKey: preferences[PREFERENCE_KEYS.OPENAI_API_KEY],
model: preferences[PREFERENCE_KEYS.OPENAI_MODEL],
baseUrl: preferences[PREFERENCE_KEYS.OPENAI_BASE_URL]
},
anthropic: {
apiKey: preferences[PREFERENCE_KEYS.ANTHROPIC_API_KEY],
model: preferences[PREFERENCE_KEYS.ANTHROPIC_MODEL],
baseUrl: preferences[PREFERENCE_KEYS.ANTHROPIC_BASE_URL]
},
gemini: {
apiKey: preferences[PREFERENCE_KEYS.GEMINI_API_KEY],
model: preferences[PREFERENCE_KEYS.GEMINI_MODEL],
baseUrl: preferences[PREFERENCE_KEYS.GEMINI_BASE_URL]
},
ollama: {
apiKey: preferences[PREFERENCE_KEYS.OLLAMA_API_KEY],
baseUrl: preferences[PREFERENCE_KEYS.OLLAMA_BASE_URL],
model: preferences[PREFERENCE_KEYS.OLLAMA_MODEL]
}
}
// Validate with Zod schema
const validated = LLMSettingsSchema.parse(settings)
Logging.log('LLMSettingsReader', `Settings loaded successfully. Provider: ${validated.defaultProvider}`)
return validated
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
Logging.log('LLMSettingsReader', `Failed to read settings: ${errorMessage}`, 'error')
// Return default settings on error
return {
defaultProvider: 'nxtscape',
nxtscape: {},
openai: {},
anthropic: {},
gemini: {},
ollama: {}
}
}
}
/**
* Get preferences from Chrome settings
* @returns Promise resolving to preference values
*/
private static async getPreferences(): Promise<Record<string, string | undefined>> {
// Check if chrome.settingsPrivate is available
if (typeof chrome === 'undefined' || !chrome.settingsPrivate || !chrome.settingsPrivate.getPref) {
// In development mode, return mock values for testing
if (isMockLLMSettings()) {
Logging.log('LLMSettingsReader', 'Chrome settingsPrivate API not available, using mock values for development', 'warning')
Logging.log('LLMSettingsReader', `Mock provider: ${MOCK_PREFERENCES['nxtscape.default_provider']}`)
return MOCK_PREFERENCES
}
Logging.log('LLMSettingsReader', 'Chrome settingsPrivate API not available, using defaults', 'warning')
return {}
}
// Get all preference values in parallel
const prefPromises = Object.entries(PREFERENCE_KEYS).map(([key, prefName]) =>
new Promise<[string, string | undefined]>((resolve) => {
chrome.settingsPrivate.getPref(prefName, (pref: chrome.settingsPrivate.PrefObject) => {
if (chrome.runtime.lastError) {
Logging.log('LLMSettingsReader',
`Failed to read preference ${prefName}: ${chrome.runtime.lastError.message}`, 'warning')
resolve([prefName, undefined])
} else {
// Extract the value from the preference object
const value = pref?.value as string | undefined
resolve([prefName, value])
}
})
})
)
// Wait for all preferences to be fetched
const prefEntries = await Promise.all(prefPromises)
// Convert array of entries back to object
return Object.fromEntries(prefEntries)
}
/**
* Convert string to provider type with validation
* @param value - Raw provider value
* @returns Valid provider type or default
*/
private static getProviderType(value: string | undefined): ProviderType {
const validProviders: ProviderType[] = ['nxtscape', 'openai', 'anthropic', 'gemini', 'ollama']
if (value && validProviders.includes(value as ProviderType)) {
return value as ProviderType
}
return 'nxtscape' // Default provider
}
/**
* Read a single preference value
* @param key - Preference key to read
* @returns Promise resolving to the preference value
*/
static async readPreference(key: string): Promise<string | undefined> {
// Check if chrome.settingsPrivate is available
if (typeof chrome === 'undefined' || !chrome.settingsPrivate || !chrome.settingsPrivate.getPref) {
// In development mode, return mock value for testing
if (isMockLLMSettings()) {
Logging.log('LLMSettingsReader', `Chrome settingsPrivate API not available, using mock value for ${key}`, 'warning')
return MOCK_PREFERENCES[key]
}
Logging.log('LLMSettingsReader', 'Chrome settingsPrivate API not available', 'warning')
return undefined
}
return new Promise((resolve) => {
chrome.settingsPrivate.getPref(key, (pref: chrome.settingsPrivate.PrefObject) => {
if (chrome.runtime.lastError) {
Logging.log('LLMSettingsReader',
`Failed to read preference ${key}: ${chrome.runtime.lastError.message}`, 'warning')
resolve(undefined)
} else {
// Extract the value from the preference object
const value = pref?.value as string | undefined
resolve(value)
}
})
})
}
}