Files
2026-07-16 15:15:55 +02:00

139 lines
3.9 KiB
TypeScript

import { createAgent } from "langchain";
import {
getProviderCredentials,
getTenantConfig,
} from "@/actions/lighthouse-v1/lighthouse";
import { isAllowedTool } from "@/lib/lighthouse-v1/allowed-tools";
import { TOOLS_UNAVAILABLE_MESSAGE } from "@/lib/lighthouse-v1/constants";
import type { ProviderType } from "@/lib/lighthouse-v1/llm-factory";
import { createLLM } from "@/lib/lighthouse-v1/llm-factory";
import {
getMCPTools,
initializeMCPClient,
isMCPAvailable,
} from "@/lib/lighthouse-v1/mcp-client";
import { getAllSkillMetadata } from "@/lib/lighthouse-v1/skills/index";
import {
generateSkillCatalog,
generateUserDataSection,
LIGHTHOUSE_SYSTEM_PROMPT_TEMPLATE,
} from "@/lib/lighthouse-v1/system-prompt";
import { loadSkill } from "@/lib/lighthouse-v1/tools/load-skill";
import { describeTool, executeTool } from "@/lib/lighthouse-v1/tools/meta-tool";
import { getModelParams } from "@/lib/lighthouse-v1/utils";
export interface RuntimeConfig {
model?: string;
provider?: string;
businessContext?: string;
currentData?: string;
}
/**
* Truncate description to specified length
*/
function truncateDescription(desc: string | undefined, maxLen: number): string {
if (!desc) return "No description available";
const cleaned = desc.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
if (cleaned.length <= maxLen) return cleaned;
return cleaned.substring(0, maxLen) + "...";
}
/**
* Generate dynamic tool listing from MCP tools.
* Only includes tools that are explicitly whitelisted.
*/
function generateToolListing(): string {
if (!isMCPAvailable()) {
return TOOLS_UNAVAILABLE_MESSAGE;
}
const mcpTools = getMCPTools();
if (mcpTools.length === 0) {
return TOOLS_UNAVAILABLE_MESSAGE;
}
// Only include whitelisted tools
const safeTools = mcpTools.filter((tool) => isAllowedTool(tool.name));
let listing = "\n## Available Prowler Tools\n\n";
listing += `${safeTools.length} tools loaded from Prowler MCP\n\n`;
for (const tool of safeTools) {
const desc = truncateDescription(tool.description, 150);
listing += `- **${tool.name}**: ${desc}\n`;
}
listing +=
"\nUse describe_tool with exact tool name to see full schema and parameters.\n";
return listing;
}
export async function initLighthouseWorkflow(runtimeConfig?: RuntimeConfig) {
await initializeMCPClient();
const toolListing = generateToolListing();
let systemPrompt = LIGHTHOUSE_SYSTEM_PROMPT_TEMPLATE.replace(
"{{TOOL_LISTING}}",
toolListing,
);
// Generate and inject skill catalog
const skillCatalog = generateSkillCatalog(getAllSkillMetadata());
systemPrompt = systemPrompt.replace("{{SKILL_CATALOG}}", skillCatalog);
// Add user-provided data section if available
const userDataSection = generateUserDataSection(
runtimeConfig?.businessContext,
runtimeConfig?.currentData,
);
if (userDataSection) {
systemPrompt += userDataSection;
}
const tenantConfigResult = await getTenantConfig();
const tenantConfig = tenantConfigResult?.data?.attributes;
const defaultProvider = tenantConfig?.default_provider || "openai";
const defaultModels = tenantConfig?.default_models || {};
const defaultModel = defaultModels[defaultProvider] || "gpt-5.2";
const providerType = (runtimeConfig?.provider ||
defaultProvider) as ProviderType;
const modelId = runtimeConfig?.model || defaultModel;
// Get credentials
const providerConfig = await getProviderCredentials(providerType);
const { credentials, base_url: baseUrl } = providerConfig;
// Get model params
const modelParams = getModelParams({ model: modelId });
// Initialize LLM
const llm = createLLM({
provider: providerType,
model: modelId,
credentials,
baseUrl,
streaming: true,
tags: ["lighthouse-agent"],
modelParams,
});
const agent = createAgent({
model: llm,
tools: [describeTool, executeTool, loadSkill],
systemPrompt,
});
return agent;
}