browser-use-typescript
Version:
A TypeScript-based browser automation framework
148 lines • 5.57 kB
JavaScript
import * as fs from 'fs';
import * as path from 'path';
import { AIMessage, HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
/**
* Extract JSON from model output, handling both plain JSON and code-block-wrapped JSON.
*/
export function extractJsonFromModelOutput(content) {
try {
// If content is wrapped in code blocks, extract just the JSON part
if (content.includes('```')) {
// Find the JSON content between code blocks
content = content.split('```')[1];
// Remove language identifier if present (e.g., 'json\n')
if (content.includes('\n')) {
content = content.split('\n', 2)[1];
}
}
// Parse the cleaned content
return JSON.parse(content);
}
catch (e) {
console.warn(`Failed to parse model output: ${content} ${e}`);
throw new Error('Could not parse response.');
}
}
/**
* Convert input messages to a format that is compatible with the planner model
*/
export function convertInputMessages(inputMessages, modelName) {
if (!modelName) {
return inputMessages;
}
if (modelName === 'deepseek-reasoner' || modelName.includes('deepseek-r1')) {
const convertedInputMessages = convertMessagesForNonFunctionCallingModels(inputMessages);
const mergedInputMessages = mergeSuccessiveMessages(convertedInputMessages, HumanMessage);
return mergeSuccessiveMessages(mergedInputMessages, AIMessage);
}
return inputMessages;
}
/**
* Convert messages for non-function-calling models
*/
function convertMessagesForNonFunctionCallingModels(inputMessages) {
const outputMessages = [];
for (const message of inputMessages) {
if (message instanceof HumanMessage || message instanceof SystemMessage) {
outputMessages.push(message);
}
else if (message instanceof ToolMessage) {
outputMessages.push(new HumanMessage(message.content));
}
else if (message instanceof AIMessage) {
// Check if tool_calls is a valid JSON object
if (message.tool_calls) {
const toolCalls = JSON.stringify(message.tool_calls);
outputMessages.push(new AIMessage({
content: toolCalls
}));
}
else {
outputMessages.push(message);
}
}
else {
throw new Error(`Unknown message type: ${typeof message}`);
}
}
return outputMessages;
}
/**
* Some models like deepseek-reasoner don't allow multiple human messages in a row.
* This function merges them into one.
*/
function mergeSuccessiveMessages(messages, classToMerge) {
const mergedMessages = [];
let streak = 0;
for (const message of messages) {
if (message instanceof classToMerge) {
streak += 1;
if (streak > 1) {
// Handle content merging based on content type
const lastMessage = mergedMessages[mergedMessages.length - 1];
// Safe handling of message content
if (typeof message.content === 'string' && typeof lastMessage.content === 'string') {
lastMessage.content += message.content;
}
else if (Array.isArray(message.content) && Array.isArray(lastMessage.content)) {
// Safely handle array type content (more complex)
// For now, we'll just append the arrays
lastMessage.content = [...lastMessage.content, ...message.content];
}
// If content types don't match, we just keep both messages
else {
mergedMessages.push(message);
}
}
else {
mergedMessages.push(message);
}
}
else {
mergedMessages.push(message);
streak = 0;
}
}
return mergedMessages;
}
/**
* Save conversation history to file.
*/
export function saveConversation(inputMessages, response, target, encoding = 'utf-8') {
// Create folders if not exists
const dirname = path.dirname(target);
if (dirname) {
fs.mkdirSync(dirname, { recursive: true });
}
const fileContent = [];
// Add messages
for (const message of inputMessages) {
fileContent.push(` ${message.constructor.name} `);
// Handle different content types safely
if (typeof message.content === 'string') {
try {
const content = JSON.parse(message.content);
fileContent.push(JSON.stringify(content, null, 2));
}
catch (error) {
console.error(`Failed to parse message content into JSON: ${message.content} ${error}`);
fileContent.push(message.content.trim());
}
}
else if (Array.isArray(message.content)) {
// Handle array content by converting to string representation
const contentStr = JSON.stringify(message.content);
fileContent.push(contentStr);
}
fileContent.push('');
}
// Add response
fileContent.push(' RESPONSE');
fileContent.push(JSON.stringify(response, null, 2));
// Write to file with correct type for encoding
fs.writeFileSync(target, fileContent.join('\n'), { encoding });
}
/**
* Write model response to conversation file
*/
//# sourceMappingURL=utils.js.map