116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
/**
|
|
* AI Analyze Step — send data to AI for analysis/summary.
|
|
* Config: { purpose: "summarize_alert", prompt: "...", system_prompt: "...", prompt_template_id?: 1 }
|
|
*/
|
|
|
|
import { registerStepExecutor } from '../pipeline-engine';
|
|
import { postgresClient } from '../postgres-client';
|
|
import { PipelineStep, PipelineContext, StepExecutorResult } from '../../types/pipeline';
|
|
|
|
async function executeAiAnalyze(
|
|
step: PipelineStep,
|
|
_context: PipelineContext,
|
|
_executionId: number
|
|
): Promise<StepExecutorResult> {
|
|
const userPrompt = step.config.prompt || '';
|
|
let systemPrompt = step.config.system_prompt || 'You are a helpful IT operations assistant.';
|
|
|
|
// If a prompt_template_id is provided, load from DB
|
|
if (step.config.prompt_template_id) {
|
|
const tplResult = await postgresClient.query(
|
|
`SELECT system_prompt, user_prompt_template, provider, model, temperature, max_tokens
|
|
FROM ai_prompt_templates WHERE id = $1 AND is_active = true`,
|
|
[step.config.prompt_template_id]
|
|
);
|
|
if (tplResult.rows.length > 0) {
|
|
systemPrompt = tplResult.rows[0].system_prompt || systemPrompt;
|
|
}
|
|
}
|
|
|
|
if (!userPrompt) {
|
|
return { success: false, error: 'Missing prompt for AI analysis' };
|
|
}
|
|
|
|
// Load AI settings
|
|
const settingsResult = await postgresClient.query(
|
|
`SELECT key, value FROM workflow_settings WHERE key IN ('default_ai_provider', 'openai_api_key', 'openai_model', 'anthropic_api_key', 'anthropic_model')`
|
|
);
|
|
|
|
const settings: Record<string, any> = {};
|
|
for (const row of settingsResult.rows) {
|
|
try { settings[row.key] = JSON.parse(row.value); } catch { settings[row.key] = row.value; }
|
|
}
|
|
|
|
const provider = step.config.provider || settings.default_ai_provider || 'openai';
|
|
const model = step.config.model || (provider === 'anthropic' ? settings.anthropic_model : settings.openai_model) || 'gpt-4o';
|
|
const apiKey = provider === 'anthropic' ? settings.anthropic_api_key : settings.openai_api_key;
|
|
|
|
if (!apiKey) {
|
|
return { success: false, error: `No API key configured for ${provider}` };
|
|
}
|
|
|
|
console.log(`[PIPELINE:ai_analyze] Calling ${provider}/${model}`);
|
|
|
|
let aiResponse: string;
|
|
|
|
if (provider === 'anthropic') {
|
|
const resp = await fetch('https://api.anthropic.com/v1/messages', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'x-api-key': apiKey,
|
|
'anthropic-version': '2023-06-01',
|
|
},
|
|
body: JSON.stringify({
|
|
model,
|
|
max_tokens: Number(step.config.max_tokens) || 2000,
|
|
system: systemPrompt,
|
|
messages: [{ role: 'user', content: userPrompt }],
|
|
}),
|
|
});
|
|
|
|
if (!resp.ok) {
|
|
const errText = await resp.text();
|
|
return { success: false, error: `Anthropic API error (${resp.status}): ${errText.substring(0, 200)}` };
|
|
}
|
|
|
|
const data = await resp.json();
|
|
aiResponse = data.content?.[0]?.text || '';
|
|
} else {
|
|
const resp = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${apiKey}`,
|
|
},
|
|
body: JSON.stringify({
|
|
model,
|
|
temperature: Number(step.config.temperature) || 0.3,
|
|
max_tokens: Number(step.config.max_tokens) || 2000,
|
|
messages: [
|
|
{ role: 'system', content: systemPrompt },
|
|
{ role: 'user', content: userPrompt },
|
|
],
|
|
}),
|
|
});
|
|
|
|
if (!resp.ok) {
|
|
const errText = await resp.text();
|
|
return { success: false, error: `OpenAI API error (${resp.status}): ${errText.substring(0, 200)}` };
|
|
}
|
|
|
|
const data = await resp.json();
|
|
aiResponse = data.choices?.[0]?.message?.content || '';
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
output: {
|
|
ai_response: aiResponse,
|
|
ai_provider: provider,
|
|
ai_model: model,
|
|
},
|
|
};
|
|
}
|
|
|
|
registerStepExecutor('ai_analyze', executeAiAnalyze);
|