multimind-sdk
Version:
This SDK gives JavaScript/TypeScript developers full access to advanced AI features like agent orchestration, RAG, and fine-tuning — without needing to manage backend code.
718 lines (712 loc) • 27.7 kB
JavaScript
import { py } from './bridge/multimind-bridge.js';
export class ContextTransferManager {
constructor(config) {
this.supportedModels = {
"chatgpt": "ChatGPT",
"deepseek": "DeepSeek",
"claude": "Claude",
"gemini": "Gemini",
"mistral": "Mistral",
"llama": "Llama",
"cohere": "Cohere",
"anthropic_claude": "Anthropic Claude",
"openai_gpt4": "OpenAI GPT-4",
"gpt4": "GPT-4",
"gpt-4": "GPT-4",
"gpt3": "GPT-3",
"gpt-3": "GPT-3",
"claude-3": "Claude-3",
"claude-2": "Claude-2",
"claude-1": "Claude-1"
};
this.config = {
maxContextLength: 32000,
defaultSummaryLength: 1000,
includeMetadata: true,
preserveFormatting: true,
smartTruncation: true,
contextCompression: false,
...config
};
}
async extractContext(messages, lastN = 5, smartExtraction = true) {
if (!messages.length)
return [];
if (smartExtraction) {
return this.smartExtractContext(messages, lastN);
}
else {
const extractedCount = Math.min(lastN, messages.length);
const extractedMessages = messages.slice(-extractedCount);
console.log(`Extracted ${extractedMessages.length} messages from conversation`);
return extractedMessages;
}
}
smartExtractContext(messages, lastN) {
if (messages.length <= lastN)
return messages;
const recentMessages = messages.slice(-lastN);
const importantContext = [];
for (const msg of messages.slice(0, -lastN)) {
if (msg.role === 'system' || this.isImportantContext(msg.content)) {
importantContext.push(msg);
}
}
if (importantContext.length) {
const contextToInclude = importantContext.slice(-2);
const combined = [
...contextToInclude,
...recentMessages.slice(-(lastN - contextToInclude.length))
];
console.log(`Smart extraction: ${contextToInclude.length} important + ${combined.length - contextToInclude.length} recent messages`);
return combined;
}
return recentMessages;
}
isImportantContext(content) {
const importantKeywords = [
"system", "setup", "configuration", "requirements", "constraints",
"important", "note", "warning", "error", "critical", "essential"
];
const contentLower = content.toLowerCase();
return importantKeywords.some(keyword => contentLower.includes(keyword));
}
async summarizeContext(messages, summaryType = "concise") {
if (!messages.length)
return "No conversation context available.";
switch (summaryType) {
case "structured":
return this.createStructuredSummary(messages);
case "detailed":
return this.createDetailedSummary(messages);
default:
return this.createConciseSummary(messages);
}
}
createConciseSummary(messages) {
const summaryParts = [];
for (const message of messages) {
let content = message.content;
if (content.length > 500) {
content = content.substring(0, 500) + "...";
}
switch (message.role) {
case 'user':
summaryParts.push(`User: ${content}`);
break;
case 'assistant':
summaryParts.push(`Assistant: ${content}`);
break;
case 'system':
summaryParts.push(`System: ${content}`);
break;
}
}
const summary = summaryParts.join('\n');
console.log(`Generated concise summary with ${summaryParts.length} parts`);
return summary;
}
createDetailedSummary(messages) {
const summaryParts = [];
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
switch (message.role) {
case 'user':
summaryParts.push(`User (Message ${i + 1}): ${message.content}`);
break;
case 'assistant':
summaryParts.push(`Assistant (Response ${i + 1}): ${message.content}`);
break;
case 'system':
summaryParts.push(`System Configuration: ${message.content}`);
break;
}
}
const summary = summaryParts.join('\n\n');
console.log(`Generated detailed summary with ${summaryParts.length} parts`);
return summary;
}
createStructuredSummary(messages) {
const userMessages = [];
const assistantMessages = [];
const systemMessages = [];
for (const message of messages) {
switch (message.role) {
case 'user':
userMessages.push(message.content);
break;
case 'assistant':
assistantMessages.push(message.content);
break;
case 'system':
systemMessages.push(message.content);
break;
}
}
const summaryParts = [];
if (systemMessages.length) {
summaryParts.push("System Context:");
summaryParts.push(...systemMessages.map(msg => `- ${msg}`));
summaryParts.push("");
}
summaryParts.push("Conversation Flow:");
for (let i = 0; i < Math.max(userMessages.length, assistantMessages.length); i++) {
if (i < userMessages.length) {
summaryParts.push(`User: ${userMessages[i]}`);
}
if (i < assistantMessages.length) {
summaryParts.push(`Assistant: ${assistantMessages[i]}`);
}
summaryParts.push("");
}
const summary = summaryParts.join('\n').trim();
console.log(`Generated structured summary with ${userMessages.length} user and ${assistantMessages.length} assistant messages`);
return summary;
}
async loadConversationFromFile(filePath, formatType = "auto") {
try {
if (formatType === "auto") {
formatType = this.detectFileFormat(filePath);
}
switch (formatType) {
case "json":
return this.loadJsonConversation(filePath);
case "txt":
return this.loadTextConversation(filePath);
case "markdown":
return this.loadMarkdownConversation(filePath);
default:
throw new Error(`Unsupported format: ${formatType}`);
}
}
catch (error) {
console.error(`Error loading conversation from ${filePath}:`, error);
throw error;
}
}
detectFileFormat(filePath) {
const extension = filePath.split('.').pop()?.toLowerCase();
if (extension === "json")
return "json";
if (extension === "md" || extension === "markdown")
return "markdown";
return "txt";
}
async loadJsonConversation(filePath) {
const fs = await import('fs/promises');
const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
let messages;
if (Array.isArray(data)) {
messages = data;
}
else if (data.messages) {
messages = data.messages;
}
else if (data.conversation) {
messages = data.conversation;
}
else {
throw new Error("Invalid JSON structure. Expected list of messages or dict with 'messages' key.");
}
console.log(`Loaded ${messages.length} messages from JSON file ${filePath}`);
return messages;
}
async loadTextConversation(filePath) {
const fs = await import('fs/promises');
const content = await fs.readFile(filePath, 'utf-8');
const lines = content.trim().split('\n');
const messages = [];
let currentRole = null;
let currentContent = [];
for (const line of lines) {
if (line.startsWith('User:') || line.startsWith('Assistant:') || line.startsWith('System:')) {
if (currentRole && currentContent.length) {
messages.push({
role: currentRole,
content: currentContent.join('\n').trim()
});
}
if (line.startsWith('User:')) {
currentRole = "user";
}
else if (line.startsWith('Assistant:')) {
currentRole = "assistant";
}
else if (line.startsWith('System:')) {
currentRole = "system";
}
currentContent = [line.split(':', 1)[1]?.trim() || ''];
}
else {
currentContent.push(line);
}
}
if (currentRole && currentContent.length) {
messages.push({
role: currentRole,
content: currentContent.join('\n').trim()
});
}
console.log(`Loaded ${messages.length} messages from text file ${filePath}`);
return messages;
}
async loadMarkdownConversation(filePath) {
const fs = await import('fs/promises');
const content = await fs.readFile(filePath, 'utf-8');
const lines = content.split('\n');
const messages = [];
let currentRole = null;
let currentContent = [];
for (const line of lines) {
if (line.startsWith('### User:') || line.startsWith('### Assistant:') || line.startsWith('### System:')) {
if (currentRole && currentContent.length) {
messages.push({
role: currentRole,
content: currentContent.join('\n').trim()
});
}
if (line.startsWith('### User:')) {
currentRole = "user";
}
else if (line.startsWith('### Assistant:')) {
currentRole = "assistant";
}
else if (line.startsWith('### System:')) {
currentRole = "system";
}
currentContent = [line.split(':', 1)[1]?.trim() || ''];
}
else {
currentContent.push(line);
}
}
if (currentRole && currentContent.length) {
messages.push({
role: currentRole,
content: currentContent.join('\n').trim()
});
}
console.log(`Loaded ${messages.length} messages from markdown file ${filePath}`);
return messages;
}
async saveFormattedPrompt(content, outputFile, formatType = "txt") {
try {
switch (formatType) {
case "json":
await this.saveJsonPrompt(content, outputFile);
break;
case "markdown":
await this.saveMarkdownPrompt(content, outputFile);
break;
default:
await this.saveTextPrompt(content, outputFile);
}
console.log(`Formatted prompt saved to ${outputFile} in ${formatType} format`);
}
catch (error) {
console.error(`Error saving formatted prompt to ${outputFile}:`, error);
throw error;
}
}
async saveTextPrompt(content, outputFile) {
const fs = await import('fs/promises');
await fs.writeFile(outputFile, content, 'utf-8');
}
async saveJsonPrompt(content, outputFile) {
const fs = await import('fs/promises');
const promptData = {
prompt: content,
metadata: {
created_at: new Date().toISOString(),
format: "json",
length: content.length
}
};
await fs.writeFile(outputFile, JSON.stringify(promptData, null, 2), 'utf-8');
}
async saveMarkdownPrompt(content, outputFile) {
const fs = await import('fs/promises');
const markdownContent = `# Formatted Prompt
## Content
${content}
---
*Generated by MultiMind Context Transfer*
`;
await fs.writeFile(outputFile, markdownContent, 'utf-8');
}
async transferContext(fromModel, toModel, inputFile, outputFile, lastN = 5, includeSummary = true, summaryType = "concise", smartExtraction = true, outputFormat = "txt", options = {}) {
const messages = await this.loadConversationFromFile(inputFile);
const extractedMessages = await this.extractContext(messages, lastN, smartExtraction);
let summary;
if (includeSummary) {
summary = await this.summarizeContext(extractedMessages, summaryType);
}
else {
summary = await this.summarizeContext(extractedMessages.slice(-1), "concise");
}
const formattedPrompt = await this.formatForTargetModel(toModel, summary, fromModel, options);
await this.saveFormattedPrompt(formattedPrompt, outputFile, outputFormat);
return formattedPrompt;
}
async formatForTargetModel(targetModel, summary, sourceModel, options = {}) {
const targetModelLower = targetModel.toLowerCase().replace(/[ -]/g, "_");
try {
const adapter = await this.getAdapter(targetModelLower);
return adapter.formatContext(summary, sourceModel, options);
}
catch (error) {
return this.formatGeneric(summary, sourceModel, targetModel, options);
}
}
async getAdapter(modelName) {
try {
return await py `AdapterFactory.get_adapter(${modelName})`;
}
catch (error) {
throw new Error(`Model '${modelName}' not supported`);
}
}
formatGeneric(summary, sourceModel, targetModel, options = {}) {
const includeMetadata = options.include_metadata ?? this.config.includeMetadata;
let prompt = `You are ${targetModel}, an AI assistant.
A user was previously working with ${sourceModel} on the following conversation:
${summary}
Please continue helping the user from where they left off. Maintain the context and provide helpful responses.`;
if (includeMetadata) {
prompt += `\n\n---\nContext transferred from ${sourceModel} to ${targetModel} using MultiMind SDK`;
}
return prompt;
}
getSupportedModels() {
return Object.keys(this.supportedModels);
}
async getModelInfo(modelName) {
try {
const capabilities = await py `AdapterFactory.get_model_capabilities(${modelName})`;
return capabilities;
}
catch (error) {
return {
name: modelName,
supportedFormats: ["text"],
maxContextLength: 8000,
supportsCode: true,
supportsImages: false,
supportsTools: false
};
}
}
async listAllModels() {
try {
return await py `AdapterFactory.list_all_capabilities()`;
}
catch (error) {
console.error('Error getting model capabilities:', error);
return {};
}
}
}
export class ContextTransferAPI {
constructor() {
this.manager = new ContextTransferManager();
this.supportedFormats = ["json", "txt", "markdown"];
this.supportedModels = this.manager.getSupportedModels();
}
async transferContextAPI(sourceModel, targetModel, conversationData, options = {}) {
try {
const defaultOptions = {
lastN: 5,
includeSummary: true,
summaryType: "concise",
smartExtraction: true,
outputFormat: "txt",
includeMetadata: true,
includeCodeContext: false,
includeReasoning: false,
includeSafety: false,
includeCreativity: false,
includeExamples: false,
includeStepByStep: false,
includeMultimodal: false,
includeWebSearch: false
};
const finalOptions = { ...defaultOptions, ...options };
const messages = this.processConversationData(conversationData);
const extractedMessages = await this.manager.extractContext(messages, finalOptions.lastN, finalOptions.smartExtraction);
let summary;
if (finalOptions.includeSummary) {
summary = await this.manager.summarizeContext(extractedMessages, finalOptions.summaryType);
}
else {
summary = await this.manager.summarizeContext(extractedMessages.slice(-1), "concise");
}
const formattingOptions = Object.fromEntries(Object.entries(finalOptions).filter(([key]) => key.startsWith('include_') && key !== 'includeMetadata'));
const formattedPrompt = await this.manager.formatForTargetModel(targetModel, summary, sourceModel, formattingOptions);
const response = {
success: true,
formattedPrompt,
metadata: {
sourceModel,
targetModel,
summaryType: finalOptions.summaryType,
smartExtraction: finalOptions.smartExtraction,
messagesProcessed: messages.length,
messagesExtracted: extractedMessages.length,
promptLength: formattedPrompt.length,
createdAt: new Date().toISOString(),
outputFormat: finalOptions.outputFormat
}
};
if (finalOptions.includeMetadata) {
response.metadata.modelCapabilities = {
source: await this.manager.getModelInfo(sourceModel),
target: await this.manager.getModelInfo(targetModel)
};
}
console.log(`Context transfer completed: ${sourceModel} -> ${targetModel}`);
return response;
}
catch (error) {
console.error('Context transfer failed:', error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
};
}
}
processConversationData(data) {
if (typeof data === 'string') {
// This would need to be handled asynchronously in a real implementation
throw new Error('File path processing not implemented in this demo');
}
else if (Array.isArray(data)) {
return data;
}
else if (typeof data === 'object') {
if (data.messages) {
return data.messages;
}
else if (data.conversation) {
return data.conversation;
}
else {
return [data];
}
}
else {
throw new Error(`Unsupported data type: ${typeof data}`);
}
}
async getSupportedModels() {
try {
const capabilities = await this.manager.listAllModels();
return {
success: true,
models: capabilities,
totalModels: Object.keys(capabilities).length,
supportedFormats: this.supportedFormats,
metadata: {
generatedAt: new Date().toISOString(),
apiVersion: "2.0"
}
};
}
catch (error) {
console.error('Failed to get supported models:', error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
};
}
}
async getModelCapabilities(modelName) {
try {
const capabilities = await this.manager.getModelInfo(modelName);
return {
success: true,
model: modelName,
capabilities,
metadata: {
generatedAt: new Date().toISOString()
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
};
}
}
async validateConversationFormat(data) {
try {
const messages = this.processConversationData(data);
const analysis = {
totalMessages: messages.length,
userMessages: 0,
assistantMessages: 0,
systemMessages: 0,
unknownMessages: 0,
averageMessageLength: 0,
hasSystemContext: false
};
let totalLength = 0;
for (const msg of messages) {
const role = msg.role;
const content = msg.content;
switch (role) {
case 'user':
analysis.userMessages++;
break;
case 'assistant':
analysis.assistantMessages++;
break;
case 'system':
analysis.systemMessages++;
analysis.hasSystemContext = true;
break;
default:
analysis.unknownMessages++;
}
totalLength += content.length;
}
if (analysis.totalMessages > 0) {
analysis.averageMessageLength = totalLength / analysis.totalMessages;
}
return {
success: true,
valid: true,
analysis,
recommendations: this.generateRecommendations(analysis)
};
}
catch (error) {
return {
success: false,
valid: false,
error: error instanceof Error ? error.message : String(error),
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
};
}
}
generateRecommendations(analysis) {
const recommendations = [];
if (analysis.totalMessages === 0) {
recommendations.push("No messages found in conversation data");
}
if (analysis.userMessages === 0) {
recommendations.push("No user messages found - ensure conversation has user input");
}
if (analysis.assistantMessages === 0) {
recommendations.push("No assistant messages found - ensure conversation has AI responses");
}
if (analysis.unknownMessages > 0) {
recommendations.push(`Found ${analysis.unknownMessages} messages with unknown roles`);
}
if (analysis.averageMessageLength > 1000) {
recommendations.push("Long messages detected - consider using smart extraction");
}
if (analysis.totalMessages > 20) {
recommendations.push("Large conversation detected - consider using smart extraction and detailed summary");
}
if (!analysis.hasSystemContext) {
recommendations.push("No system context found - consider adding system messages for better context");
}
return recommendations;
}
async batchTransfer(transfers) {
const results = [];
let successful = 0;
let failed = 0;
for (let i = 0; i < transfers.length; i++) {
const transferConfig = transfers[i];
try {
const result = await this.transferContextAPI(transferConfig.sourceModel, transferConfig.targetModel, transferConfig.conversationData, transferConfig.options);
// Only add transferIndex if metadata exists
if (result.metadata) {
result.metadata.transferIndex = i;
}
results.push(result);
if (result.success) {
successful++;
}
else {
failed++;
}
}
catch (error) {
results.push({
success: false,
error: error instanceof Error ? error.message : String(error),
errorType: error instanceof Error ? error.constructor.name : 'Unknown',
// Do not set metadata for error case
});
failed++;
}
}
return {
success: failed === 0,
totalTransfers: transfers.length,
successfulTransfers: successful,
failedTransfers: failed,
results,
metadata: {
completedAt: new Date().toISOString()
}
};
}
createChromeExtensionConfig() {
return {
apiVersion: "2.0",
supportedModels: this.supportedModels,
supportedFormats: this.supportedFormats,
defaultOptions: {
lastN: 5,
includeSummary: true,
summaryType: "concise",
smartExtraction: true,
outputFormat: "txt"
},
chromeExtension: {
manifestVersion: 3,
permissions: ["activeTab", "storage"],
contentScripts: ["content.js"],
backgroundScripts: ["background.js"],
popup: "popup.html"
},
endpoints: {
transfer: "/api/transfer",
models: "/api/models",
validate: "/api/validate",
batch: "/api/batch"
},
metadata: {
generatedAt: new Date().toISOString(),
sdkVersion: "2.0.0"
}
};
}
}
// Convenience functions
export async function quickTransfer(sourceModel, targetModel, conversationData, options = {}) {
const api = new ContextTransferAPI();
const result = await api.transferContextAPI(sourceModel, targetModel, conversationData, options);
if (result.success && result.formattedPrompt) {
return result.formattedPrompt;
}
else {
throw new Error(`Transfer failed: ${result.error || 'Unknown error'}`);
}
}
export async function getAllModels() {
const api = new ContextTransferAPI();
return api.getSupportedModels();
}
export async function validateConversation(data) {
const api = new ContextTransferAPI();
return api.validateConversationFormat(data);
}
//# sourceMappingURL=contextTransfer.js.map