mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-21 04:45:12 +00:00
* 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>
217 lines
6.8 KiB
TypeScript
217 lines
6.8 KiB
TypeScript
import { z } from "zod";
|
|
import { tool as createLangChainTool } from "@langchain/core/tools";
|
|
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
import { ExecutionContext } from "@/lib/runtime/ExecutionContext";
|
|
import { ToolConfig } from "./ToolConfig";
|
|
import { LangChainProviderFactory, LLMOverrides } from "@/lib/llm";
|
|
import { Logging } from "@/lib/utils/Logging";
|
|
import BrowserContext from "@/lib/browser/BrowserContext";
|
|
import { profileStart, profileEnd } from "@/lib/utils/Profiler";
|
|
|
|
/**
|
|
* Abstract base class for all Nxtscape tools
|
|
*/
|
|
export abstract class NxtscapeTool<TInput = any, TOutput = any> {
|
|
protected executionContext: ExecutionContext;
|
|
protected browserContext: BrowserContext;
|
|
protected config: ToolConfig<TInput, TOutput>;
|
|
private llmInstance?: BaseChatModel; // Cached LLM instance
|
|
private llmPromise?: Promise<BaseChatModel>; // Promise to prevent concurrent initialization
|
|
|
|
constructor(
|
|
config: ToolConfig<TInput, TOutput>,
|
|
executionContext: ExecutionContext,
|
|
) {
|
|
this.config = config;
|
|
this.executionContext = executionContext;
|
|
this.browserContext = executionContext.browserContext;
|
|
}
|
|
|
|
/**
|
|
* Get an LLM instance for this tool based on user's browser settings
|
|
*
|
|
* The base instance (without overrides) is cached after first creation to avoid multiple
|
|
* initializations and API calls to browser preferences.
|
|
*
|
|
* @param overrides - Optional overrides for model/temperature
|
|
* Note: When overrides are provided, a new instance is created without caching
|
|
* @returns Promise resolving to the LLM instance
|
|
*
|
|
* @example
|
|
* // Get default LLM (cached)
|
|
* const llm = await this.getLLM();
|
|
*
|
|
* @example
|
|
* // Get LLM with custom temperature (not cached)
|
|
* const customLLM = await this.getLLM({ temperature: 0.7 });
|
|
*/
|
|
protected async getLLM(overrides?: LLMOverrides): Promise<BaseChatModel> {
|
|
// If we have custom overrides, create a new instance without caching
|
|
if (overrides && (overrides.model || overrides.temperature !== undefined)) {
|
|
Logging.log(
|
|
this.config.name,
|
|
"Creating LLM with custom overrides (not cached)",
|
|
);
|
|
return LangChainProviderFactory.createLLM(overrides);
|
|
}
|
|
|
|
// Return cached instance if available
|
|
if (this.llmInstance) {
|
|
return this.llmInstance;
|
|
}
|
|
|
|
// If already initializing, wait for the existing promise
|
|
if (this.llmPromise) {
|
|
return this.llmPromise;
|
|
}
|
|
|
|
// Initialize LLM with proper error handling and concurrency protection
|
|
this.llmPromise = LangChainProviderFactory.createLLM()
|
|
.then((llm) => {
|
|
this.llmInstance = llm;
|
|
Logging.log(this.config.name, "LLM initialized and cached for tool");
|
|
return llm;
|
|
})
|
|
.catch((error) => {
|
|
// Clear the promise on error so it can be retried
|
|
this.llmPromise = undefined;
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : String(error);
|
|
Logging.log(
|
|
this.config.name,
|
|
`Failed to initialize LLM: ${errorMessage}`,
|
|
"error",
|
|
);
|
|
throw error;
|
|
});
|
|
|
|
return this.llmPromise;
|
|
}
|
|
|
|
/**
|
|
* Execute the tool with input validation and output formatting
|
|
*/
|
|
protected abstract execute(input: TInput): Promise<TOutput>;
|
|
|
|
/**
|
|
* Format tool output for UI display
|
|
* Each tool MUST implement this method to format its own output
|
|
* @param output - The validated tool output
|
|
* @returns Formatted string ready for UI display
|
|
*/
|
|
abstract FormatResultForUI(output: TOutput): string;
|
|
|
|
/**
|
|
* Get the tool configuration
|
|
*/
|
|
getConfig(): ToolConfig<TInput, TOutput> {
|
|
return this.config;
|
|
}
|
|
|
|
/**
|
|
* Get the LangChain-compatible tool with enhanced display result
|
|
*/
|
|
getLangChainTool() {
|
|
return createLangChainTool(
|
|
async (input: any) => {
|
|
const profileLabel = `Tool.${this.config.name}`;
|
|
profileStart(profileLabel);
|
|
|
|
try {
|
|
// Validate input
|
|
const validatedInput = this.config.inputSchema.parse(input);
|
|
|
|
// Execute tool
|
|
const result = await this.execute(validatedInput);
|
|
|
|
// Validate output
|
|
const validatedOutput = this.config.outputSchema.parse(result);
|
|
|
|
// Create enhanced result with display formatting
|
|
const enhancedResult = {
|
|
...validatedOutput,
|
|
_displayResult: this.FormatResultForUI(validatedOutput), // Add display-ready result
|
|
_toolName: this.config.name, // Add tool name for identification
|
|
};
|
|
|
|
// Return as JSON string for LangChain compatibility
|
|
profileEnd(profileLabel);
|
|
return JSON.stringify(enhancedResult);
|
|
} catch (error) {
|
|
profileEnd(profileLabel);
|
|
|
|
// Handle validation errors
|
|
if (error instanceof z.ZodError) {
|
|
const errorMessage = `Validation error: ${error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`;
|
|
const errorResult = {
|
|
success: false,
|
|
error: errorMessage,
|
|
_displayResult: `❌ ${errorMessage}`,
|
|
_toolName: this.config.name,
|
|
};
|
|
return JSON.stringify(errorResult);
|
|
}
|
|
|
|
// Handle other errors
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : String(error);
|
|
const errorResult = {
|
|
success: false,
|
|
error: errorMessage,
|
|
_displayResult: `❌ ${errorMessage}`,
|
|
_toolName: this.config.name,
|
|
};
|
|
return JSON.stringify(errorResult);
|
|
}
|
|
},
|
|
{
|
|
name: this.config.name,
|
|
description: this.config.description,
|
|
schema: this.config.inputSchema,
|
|
},
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get streaming display configuration
|
|
*/
|
|
getUIConfig() {
|
|
return (
|
|
this.config.streamingConfig || {
|
|
displayName: this.config.name,
|
|
icon: "🔧",
|
|
progressMessage: `Running ${this.config.name}...`,
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generate contextual display message based on tool arguments
|
|
* Each tool MUST implement this method to provide specific messages based on their input
|
|
* @param args - Tool arguments of type TInput
|
|
* @returns Contextual display message
|
|
*/
|
|
abstract getProgressMessage(args: TInput): string;
|
|
|
|
/**
|
|
* Get display information for streaming
|
|
* @param args - Tool arguments of type TInput
|
|
* @returns Complete display information
|
|
*/
|
|
getToolMetadata(args: TInput): {
|
|
displayName: string;
|
|
icon: string;
|
|
description: string;
|
|
} {
|
|
const streamingConfig = this.getUIConfig();
|
|
|
|
return {
|
|
displayName: streamingConfig.displayName || this.config.name,
|
|
icon: streamingConfig.icon || "🔧",
|
|
description: this.getProgressMessage(args),
|
|
};
|
|
}
|
|
|
|
}
|
|
|