seraph-agent
Version:
An extremely lightweight, SRE autonomous AI agent for seamless integration with common observability tasks.
233 lines (232 loc) • 9.18 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildKnowledgeBase = buildKnowledgeBase;
const faiss_1 = require("@langchain/community/vectorstores/faiss");
const text_splitter_1 = require("langchain/text_splitter");
const document_1 = require("langchain/document");
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const embedding_provider_1 = require("./llm/embedding-provider");
const html_to_text_1 = require("html-to-text");
async function fetchGitHubReleases(repository, token) {
if (!repository) {
return [];
}
try {
const url = `https://api.github.com/repos/${repository}/releases`;
const headers = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, { headers });
const releases = await response.json();
return releases.map((release) => new document_1.Document({
pageContent: release.body,
metadata: {
source: 'github-releases',
tag: release.tag_name,
published_at: release.published_at,
},
}));
}
catch (error) {
console.error(`Error fetching GitHub releases for ${repository}:`, error);
return [];
}
}
async function fetchGitHubCode(repository, token) {
if (!repository) {
return [];
}
try {
const url = `https://api.github.com/repos/${repository}/contents`;
const headers = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, { headers });
const files = await response.json();
const documentPromises = files
.filter(file => file.type === 'file')
.map(async (file) => {
const contentResponse = await fetch(file.download_url);
const content = await contentResponse.text();
return new document_1.Document({
pageContent: content,
metadata: {
source: 'github-code',
path: file.path,
},
});
});
return Promise.all(documentPromises);
}
catch (error) {
console.error(`Error fetching GitHub code for ${repository}:`, error);
return [];
}
}
async function fetchJiraTickets(config) {
const { instanceUrl, projectKey, jql, usernameVar, apiTokenVar } = config;
const username = process.env[usernameVar];
const apiToken = process.env[apiTokenVar];
if (!username || !apiToken) {
console.error('JIRA username or API token not found in environment variables.');
return [];
}
const auth = Buffer.from(`${username}:${apiToken}`).toString('base64');
const searchQuery = jql || `project = ${projectKey}`;
const url = `${instanceUrl}/rest/api/3/search?jql=${searchQuery}`;
try {
const response = await fetch(url, {
headers: {
Authorization: `Basic ${auth}`,
'Content-Type': 'application/json',
},
});
const data = await response.json();
return data.issues.map((issue) => new document_1.Document({
pageContent: `${issue.fields.summary}\n\n${issue.fields.description}\n\n${issue.fields.comment.comments.map((c) => c.body).join('\n\n')}`,
metadata: {
source: 'jira',
key: issue.key,
url: `${instanceUrl}/browse/${issue.key}`,
},
}));
}
catch (error) {
console.error('Error fetching JIRA tickets:', error);
return [];
}
}
async function fetchConfluenceDocs(config) {
const { instanceUrl, spaceKey, usernameVar, apiTokenVar } = config;
const username = process.env[usernameVar];
const apiToken = process.env[apiTokenVar];
if (!username || !apiToken) {
console.error('Confluence username or API token not found in environment variables.');
return [];
}
const auth = Buffer.from(`${username}:${apiToken}`).toString('base64');
const url = `${instanceUrl}/rest/api/content?spaceKey=${spaceKey}&expand=body.storage`;
try {
const response = await fetch(url, {
headers: {
Authorization: `Basic ${auth}`,
'Content-Type': 'application/json',
},
});
const data = await response.json();
return data.results.map((page) => new document_1.Document({
pageContent: (0, html_to_text_1.htmlToText)(page.body.storage.value),
metadata: {
source: 'confluence',
title: page.title,
url: `${instanceUrl}${page._links.webui}`,
},
}));
}
catch (error) {
console.error('Error fetching Confluence docs:', error);
return [];
}
}
async function buildKnowledgeBase(config, sourceName) {
if (!config.knowledge?.enabled) {
console.log('Knowledge base is disabled in the configuration.');
return;
}
console.log(`Building knowledge base${sourceName ? ` for source: ${sourceName}` : ''}...`);
const sourcesToBuild = sourceName
? config.knowledge.sources.filter((s) => s.name === sourceName)
: config.knowledge.sources;
const documents = [];
for (const source of sourcesToBuild) {
if (!source.enabled) {
continue;
}
let fetchedDocs = [];
switch (source.config.type) {
case 'github':
const token = source.config.authVar ? process.env[source.config.authVar] : undefined;
if (source.config.sourceType === 'releases') {
fetchedDocs = await fetchGitHubReleases(source.config.repository, token);
}
else if (source.config.sourceType === 'code') {
fetchedDocs = await fetchGitHubCode(source.config.repository, token);
}
break;
case 'jira':
fetchedDocs = await fetchJiraTickets(source.config);
break;
case 'confluence':
fetchedDocs = await fetchConfluenceDocs(source.config);
break;
default:
console.warn(`Unknown knowledge source type: ${source.config.type}`);
}
documents.push(...fetchedDocs);
}
if (documents.length === 0) {
console.log('No documents found to build the knowledge base.');
return;
}
const textSplitter = new text_splitter_1.RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const splitDocs = await textSplitter.splitDocuments(documents);
const embeddings = (0, embedding_provider_1.createEmbeddingProvider)(config);
const vectorStorePath = config.knowledge.vectorStore;
const vectorStoreDir = path.dirname(vectorStorePath);
await fs.mkdir(vectorStoreDir, { recursive: true });
try {
const vectorStore = await faiss_1.FaissStore.load(vectorStoreDir, embeddings);
if (sourceName) {
const idsToDelete = Array.from(vectorStore.docstore['_docs'].values())
.filter((doc) => doc.metadata.source === sourceName)
.map((doc) => doc.metadata.id);
await vectorStore.delete({ ids: idsToDelete });
}
await vectorStore.addDocuments(splitDocs);
await vectorStore.save(vectorStorePath);
}
catch (error) {
const vectorStore = await faiss_1.FaissStore.fromDocuments(splitDocs, embeddings);
await vectorStore.save(vectorStorePath);
}
console.log('Knowledge base built successfully.');
}