@sweetoburrito/backstage-plugin-ai-assistant-backend-module-ingestor-azure-devops 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/config.d.ts +42 -0
- package/dist/constants/module.cjs.js +6 -0
- package/dist/constants/module.cjs.js.map +1 -0
- package/dist/index.cjs.js +10 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/module.cjs.js +27 -0
- package/dist/module.cjs.js.map +1 -0
- package/dist/services/azure-devops.cjs.js +72 -0
- package/dist/services/azure-devops.cjs.js.map +1 -0
- package/dist/services/ingestor.cjs.js +100 -0
- package/dist/services/ingestor.cjs.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
package/config.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure DevOps ingestor configuration
|
|
3
|
+
* @visibility backend
|
|
4
|
+
*/
|
|
5
|
+
export interface Config {
|
|
6
|
+
aiAssistant: {
|
|
7
|
+
ingestors: {
|
|
8
|
+
azureDevOps: {
|
|
9
|
+
/**
|
|
10
|
+
* The Azure DevOps organization name
|
|
11
|
+
*/
|
|
12
|
+
organization: string;
|
|
13
|
+
/**
|
|
14
|
+
* The Azure DevOps project name
|
|
15
|
+
*/
|
|
16
|
+
project: string;
|
|
17
|
+
/**
|
|
18
|
+
* Personal Access Token for Azure DevOps
|
|
19
|
+
* @visibility secret
|
|
20
|
+
*/
|
|
21
|
+
token: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional list of file types to ingest (e.g., .md, .json). Defaults to .md and .json if not specified.
|
|
24
|
+
*/
|
|
25
|
+
fileTypes?: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Optional list of repositories to ingest. If not specified, all repositories in the project will be ingested.
|
|
28
|
+
*/
|
|
29
|
+
repositories?: {
|
|
30
|
+
/**
|
|
31
|
+
* The name of the repository to ingest
|
|
32
|
+
*/
|
|
33
|
+
name: string;
|
|
34
|
+
/**
|
|
35
|
+
* Optional list of file types to ingest for this repository. Overrides the global fileTypes setting for this repository only.
|
|
36
|
+
*/
|
|
37
|
+
fileTypes?: string[];
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.cjs.js","sources":["../../src/constants/module.ts"],"sourcesContent":["export const MODULE_ID = 'azure-devops';\n"],"names":[],"mappings":";;AAAO,MAAM,SAAA,GAAY;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var ingestor = require('./services/ingestor.cjs.js');
|
|
5
|
+
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
6
|
+
|
|
7
|
+
const aiAssistantModuleIngestorAzureDevops = backendPluginApi.createBackendModule({
|
|
8
|
+
pluginId: "ai-assistant",
|
|
9
|
+
moduleId: "ingestor-azure-devops",
|
|
10
|
+
register(reg) {
|
|
11
|
+
reg.registerInit({
|
|
12
|
+
deps: {
|
|
13
|
+
dataIngestor: backstagePluginAiAssistantNode.dataIngestorExtensionPoint,
|
|
14
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
15
|
+
logger: backendPluginApi.coreServices.logger
|
|
16
|
+
},
|
|
17
|
+
async init({ config, logger, dataIngestor }) {
|
|
18
|
+
dataIngestor.registerIngestor(
|
|
19
|
+
await ingestor.createAzureDevOpsIngestor({ config, logger })
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
exports.aiAssistantModuleIngestorAzureDevops = aiAssistantModuleIngestorAzureDevops;
|
|
27
|
+
//# sourceMappingURL=module.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["import {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { createAzureDevOpsIngestor } from './services/ingestor';\nimport { dataIngestorExtensionPoint } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nexport const aiAssistantModuleIngestorAzureDevops = createBackendModule({\n pluginId: 'ai-assistant',\n moduleId: 'ingestor-azure-devops',\n register(reg) {\n reg.registerInit({\n deps: {\n dataIngestor: dataIngestorExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n },\n async init({ config, logger, dataIngestor }) {\n dataIngestor.registerIngestor(\n await createAzureDevOpsIngestor({ config, logger }),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","dataIngestorExtensionPoint","coreServices","createAzureDevOpsIngestor"],"mappings":";;;;;;AAOO,MAAM,uCAAuCA,oCAAA,CAAoB;AAAA,EACtE,QAAA,EAAU,cAAA;AAAA,EACV,QAAA,EAAU,uBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,YAAA,EAAcC,yDAAA;AAAA,QACd,QAAQC,6BAAA,CAAa,UAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa;AAAA,OACvB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,cAAa,EAAG;AAC3C,QAAA,YAAA,CAAa,gBAAA;AAAA,UACX,MAAMC,kCAAA,CAA0B,EAAE,MAAA,EAAQ,QAAQ;AAAA,SACpD;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var azureDevopsNodeApi = require('azure-devops-node-api');
|
|
4
|
+
var GitInterfaces = require('azure-devops-node-api/interfaces/GitInterfaces');
|
|
5
|
+
|
|
6
|
+
const createAzureDevOpsService = async ({
|
|
7
|
+
config,
|
|
8
|
+
logger
|
|
9
|
+
}) => {
|
|
10
|
+
const organization = config.getString(
|
|
11
|
+
"aiAssistant.ingestors.azureDevOps.organization"
|
|
12
|
+
);
|
|
13
|
+
const project = config.getString("aiAssistant.ingestors.azureDevOps.project");
|
|
14
|
+
const token = config.getString("aiAssistant.ingestors.azureDevOps.token");
|
|
15
|
+
const orgUrl = `https://dev.azure.com/${organization}`;
|
|
16
|
+
logger.info(
|
|
17
|
+
`Connecting to Azure DevOps organization: ${organization}, project: ${project}`
|
|
18
|
+
);
|
|
19
|
+
if (!organization || !project || !token) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"Azure DevOps organization, project, and token are required"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const authHandler = azureDevopsNodeApi.getPersonalAccessTokenHandler(token);
|
|
25
|
+
const connection = new azureDevopsNodeApi.WebApi(orgUrl, authHandler);
|
|
26
|
+
logger.info(
|
|
27
|
+
`Connected to Azure DevOps organization: ${organization}, project: ${project}`
|
|
28
|
+
);
|
|
29
|
+
const gitApi = await connection.getGitApi();
|
|
30
|
+
const getRepos = async () => {
|
|
31
|
+
const repos = await gitApi.getRepositories(project);
|
|
32
|
+
logger.info(`Found ${repos.length} repositories in project ${project}`);
|
|
33
|
+
return repos;
|
|
34
|
+
};
|
|
35
|
+
const getRepoItems = async (repoId, fileTypes) => {
|
|
36
|
+
const items = await gitApi.getItems(
|
|
37
|
+
repoId,
|
|
38
|
+
project,
|
|
39
|
+
void 0,
|
|
40
|
+
GitInterfaces.VersionControlRecursionType.Full
|
|
41
|
+
);
|
|
42
|
+
logger.info(
|
|
43
|
+
`Found ${items.length} items in Azure DevOps repository ${repoId}`
|
|
44
|
+
);
|
|
45
|
+
if (fileTypes && fileTypes.length > 0) {
|
|
46
|
+
const filteredItems = items.filter(
|
|
47
|
+
(item) => !item.isFolder && fileTypes.some((type) => item.path?.endsWith(type))
|
|
48
|
+
);
|
|
49
|
+
logger.info(
|
|
50
|
+
`Filtered to ${filteredItems.length} items with types: ${fileTypes.join(
|
|
51
|
+
", "
|
|
52
|
+
)}`
|
|
53
|
+
);
|
|
54
|
+
return filteredItems;
|
|
55
|
+
}
|
|
56
|
+
return items;
|
|
57
|
+
};
|
|
58
|
+
const getRepoItemContent = async (repoId, path) => {
|
|
59
|
+
const itemContent = await gitApi.getItemContent(
|
|
60
|
+
repoId,
|
|
61
|
+
path,
|
|
62
|
+
project,
|
|
63
|
+
void 0,
|
|
64
|
+
GitInterfaces.VersionControlRecursionType.None
|
|
65
|
+
);
|
|
66
|
+
return itemContent;
|
|
67
|
+
};
|
|
68
|
+
return { organization, project, getRepos, getRepoItems, getRepoItemContent };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
exports.createAzureDevOpsService = createAzureDevOpsService;
|
|
72
|
+
//# sourceMappingURL=azure-devops.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure-devops.cjs.js","sources":["../../src/services/azure-devops.ts"],"sourcesContent":["import {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api';\nimport { VersionControlRecursionType } from 'azure-devops-node-api/interfaces/GitInterfaces';\n\nexport const createAzureDevOpsService = async ({\n config,\n logger,\n}: {\n config: RootConfigService;\n logger: LoggerService;\n}) => {\n // Get configuration values\n const organization = config.getString(\n 'aiAssistant.ingestors.azureDevOps.organization',\n );\n const project = config.getString('aiAssistant.ingestors.azureDevOps.project');\n const token = config.getString('aiAssistant.ingestors.azureDevOps.token');\n\n // Construct organization URL\n const orgUrl = `https://dev.azure.com/${organization}`;\n\n logger.info(\n `Connecting to Azure DevOps organization: ${organization}, project: ${project}`,\n );\n\n if (!organization || !project || !token) {\n throw new Error(\n 'Azure DevOps organization, project, and token are required',\n );\n }\n\n // Create authentication handler and connection\n const authHandler = getPersonalAccessTokenHandler(token);\n\n const connection = new WebApi(orgUrl, authHandler);\n\n logger.info(\n `Connected to Azure DevOps organization: ${organization}, project: ${project}`,\n );\n\n // Get Git API for repository operations\n const gitApi = await connection.getGitApi();\n\n /**\n * Get a list of repositories in the specified Azure DevOps project\n * @returns List of repositories in the specified Azure DevOps project\n */\n const getRepos = async () => {\n const repos = await gitApi.getRepositories(project);\n\n logger.info(`Found ${repos.length} repositories in project ${project}`);\n\n return repos;\n };\n\n /**\n * Get a list of items in the specified Azure DevOps repository\n * @param repoId The ID of the repository\n * @param fileTypes Optional list of file types to filter by\n * @returns List of items in the specified Azure DevOps repository\n */\n const getRepoItems = async (repoId: string, fileTypes?: string[]) => {\n const items = await gitApi.getItems(\n repoId,\n project,\n undefined,\n VersionControlRecursionType.Full,\n );\n\n logger.info(\n `Found ${items.length} items in Azure DevOps repository ${repoId}`,\n );\n\n if (fileTypes && fileTypes.length > 0) {\n const filteredItems = items.filter(\n item =>\n !item.isFolder && fileTypes.some(type => item.path?.endsWith(type)),\n );\n logger.info(\n `Filtered to ${filteredItems.length} items with types: ${fileTypes.join(\n ', ',\n )}`,\n );\n return filteredItems;\n }\n\n return items;\n };\n\n /**\n * Get the content of a specific item in an Azure DevOps repository\n * @param repoId The ID of the repository\n * @param path The path of the item\n * @returns The content of the item\n */\n const getRepoItemContent = async (repoId: string, path: string) => {\n const itemContent = await gitApi.getItemContent(\n repoId,\n path,\n project,\n undefined,\n VersionControlRecursionType.None,\n );\n\n return itemContent;\n };\n\n return { organization, project, getRepos, getRepoItems, getRepoItemContent };\n};\n"],"names":["getPersonalAccessTokenHandler","WebApi","VersionControlRecursionType"],"mappings":";;;;;AAOO,MAAM,2BAA2B,OAAO;AAAA,EAC7C,MAAA;AAAA,EACA;AACF,CAAA,KAGM;AAEJ,EAAA,MAAM,eAAe,MAAA,CAAO,SAAA;AAAA,IAC1B;AAAA,GACF;AACA,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,SAAA,CAAU,2CAA2C,CAAA;AAC5E,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,yCAAyC,CAAA;AAGxE,EAAA,MAAM,MAAA,GAAS,yBAAyB,YAAY,CAAA,CAAA;AAEpD,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,CAAA,yCAAA,EAA4C,YAAY,CAAA,WAAA,EAAc,OAAO,CAAA;AAAA,GAC/E;AAEA,EAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,OAAA,IAAW,CAAC,KAAA,EAAO;AACvC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,MAAM,WAAA,GAAcA,iDAA8B,KAAK,CAAA;AAEvD,EAAA,MAAM,UAAA,GAAa,IAAIC,yBAAA,CAAO,MAAA,EAAQ,WAAW,CAAA;AAEjD,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,CAAA,wCAAA,EAA2C,YAAY,CAAA,WAAA,EAAc,OAAO,CAAA;AAAA,GAC9E;AAGA,EAAA,MAAM,MAAA,GAAS,MAAM,UAAA,CAAW,SAAA,EAAU;AAM1C,EAAA,MAAM,WAAW,YAAY;AAC3B,IAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,eAAA,CAAgB,OAAO,CAAA;AAElD,IAAA,MAAA,CAAO,KAAK,CAAA,MAAA,EAAS,KAAA,CAAM,MAAM,CAAA,yBAAA,EAA4B,OAAO,CAAA,CAAE,CAAA;AAEtE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAQA,EAAA,MAAM,YAAA,GAAe,OAAO,MAAA,EAAgB,SAAA,KAAyB;AACnE,IAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,QAAA;AAAA,MACzB,MAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAA;AAAA,MACAC,yCAAA,CAA4B;AAAA,KAC9B;AAEA,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,CAAA,MAAA,EAAS,KAAA,CAAM,MAAM,CAAA,kCAAA,EAAqC,MAAM,CAAA;AAAA,KAClE;AAEA,IAAA,IAAI,SAAA,IAAa,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG;AACrC,MAAA,MAAM,gBAAgB,KAAA,CAAM,MAAA;AAAA,QAC1B,CAAA,IAAA,KACE,CAAC,IAAA,CAAK,QAAA,IAAY,SAAA,CAAU,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAA,CAAK,IAAA,EAAM,QAAA,CAAS,IAAI,CAAC;AAAA,OACtE;AACA,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,CAAA,YAAA,EAAe,aAAA,CAAc,MAAM,CAAA,mBAAA,EAAsB,SAAA,CAAU,IAAA;AAAA,UACjE;AAAA,SACD,CAAA;AAAA,OACH;AACA,MAAA,OAAO,aAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAQA,EAAA,MAAM,kBAAA,GAAqB,OAAO,MAAA,EAAgB,IAAA,KAAiB;AACjE,IAAA,MAAM,WAAA,GAAc,MAAM,MAAA,CAAO,cAAA;AAAA,MAC/B,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAA;AAAA,MACAA,yCAAA,CAA4B;AAAA,KAC9B;AAEA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO,EAAE,YAAA,EAAc,OAAA,EAAS,QAAA,EAAU,cAAc,kBAAA,EAAmB;AAC7E;;;;"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
4
|
+
var azureDevops = require('./azure-devops.cjs.js');
|
|
5
|
+
var module$1 = require('../constants/module.cjs.js');
|
|
6
|
+
|
|
7
|
+
const createAzureDevOpsIngestor = async ({
|
|
8
|
+
config,
|
|
9
|
+
logger
|
|
10
|
+
}) => {
|
|
11
|
+
const defaultFileTypes = [".md", ".json"];
|
|
12
|
+
const repositoriesFilter = config.getOptional("aiAssistant.ingestors.azureDevOps.repositories");
|
|
13
|
+
const fileTypes = config.getOptionalStringArray(
|
|
14
|
+
"aiAssistant.ingestors.azureDevOps.fileTypes"
|
|
15
|
+
) ?? defaultFileTypes;
|
|
16
|
+
const adoService = await azureDevops.createAzureDevOpsService({ config, logger });
|
|
17
|
+
const ingestAzureDevOpsBatch = async (saveDocumentsBatch) => {
|
|
18
|
+
const repositoriesList = await adoService.getRepos();
|
|
19
|
+
if (repositoriesList.length === 0) {
|
|
20
|
+
logger.warn("No repositories found in the Azure DevOps project");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
logger.info(
|
|
24
|
+
`Filtering for repositories: ${repositoriesFilter?.map((repo) => repo.name).join(", ")}`
|
|
25
|
+
);
|
|
26
|
+
const repositoriesToIngest = repositoriesFilter ? repositoriesList.filter(
|
|
27
|
+
(repo) => repositoriesFilter?.some(
|
|
28
|
+
(filteredRepo) => filteredRepo.name.toLowerCase() === repo.name.toLowerCase()
|
|
29
|
+
)
|
|
30
|
+
) : repositoriesList;
|
|
31
|
+
if (repositoriesToIngest.length === 0) {
|
|
32
|
+
logger.warn(
|
|
33
|
+
"No repositories found for ingestion after applying the filter"
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
logger.info(
|
|
38
|
+
`Ingesting ${repositoriesToIngest.length} repositories from Azure DevOps`
|
|
39
|
+
);
|
|
40
|
+
for (const repo of repositoriesToIngest) {
|
|
41
|
+
logger.info(
|
|
42
|
+
`Beginning ingestion for repository: ${repo.name} (${repo.id})`
|
|
43
|
+
);
|
|
44
|
+
const repositoryFileTypesFilter = repositoriesFilter?.find(
|
|
45
|
+
(r) => r.name.toLowerCase() === repo.name.toLowerCase()
|
|
46
|
+
)?.fileTypes ?? fileTypes;
|
|
47
|
+
logger.info(
|
|
48
|
+
`Processing file types for repository ${repo.name}: [${repositoryFileTypesFilter.join(", ")}]`
|
|
49
|
+
);
|
|
50
|
+
const items = await adoService.getRepoItems(
|
|
51
|
+
repo.id,
|
|
52
|
+
repositoryFileTypesFilter
|
|
53
|
+
);
|
|
54
|
+
if (items.length === 0) {
|
|
55
|
+
logger.warn(
|
|
56
|
+
`No items found for ingestion in the Azure DevOps repository ${repo.name} (${repo.id}) with the specified file types filter: [${repositoryFileTypesFilter.join(
|
|
57
|
+
", "
|
|
58
|
+
)}]`
|
|
59
|
+
);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
logger.debug(`Items: ${JSON.stringify(items, null, 2)}`);
|
|
63
|
+
const documents = [];
|
|
64
|
+
for (const item of items) {
|
|
65
|
+
const content = await adoService.getRepoItemContent(
|
|
66
|
+
repo.id,
|
|
67
|
+
item.path
|
|
68
|
+
);
|
|
69
|
+
logger.info(`Retrieved content for Azure DevOps item: ${item.path}`);
|
|
70
|
+
const text = await backstagePluginAiAssistantNode.streamToString(content);
|
|
71
|
+
const document = {
|
|
72
|
+
metadata: {
|
|
73
|
+
source: module$1.MODULE_ID,
|
|
74
|
+
id: `${repo.id}:${item.path}`,
|
|
75
|
+
url: item.url,
|
|
76
|
+
organization: adoService.organization,
|
|
77
|
+
project: adoService.project,
|
|
78
|
+
repository: repo.name
|
|
79
|
+
},
|
|
80
|
+
content: text
|
|
81
|
+
};
|
|
82
|
+
documents.push(document);
|
|
83
|
+
}
|
|
84
|
+
await saveDocumentsBatch(documents);
|
|
85
|
+
logger.info(
|
|
86
|
+
`${documents.length} documents ingested for Azure DevOps repository: ${repo.name}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const ingest = async ({ saveDocumentsBatch }) => {
|
|
91
|
+
await ingestAzureDevOpsBatch(saveDocumentsBatch);
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
id: module$1.MODULE_ID,
|
|
95
|
+
ingest
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
exports.createAzureDevOpsIngestor = createAzureDevOpsIngestor;
|
|
100
|
+
//# sourceMappingURL=ingestor.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingestor.cjs.js","sources":["../../src/services/ingestor.ts"],"sourcesContent":["import {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { streamToString } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { createAzureDevOpsService } from './azure-devops';\nimport {\n EmbeddingDocument,\n Ingestor,\n IngestorOptions,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { MODULE_ID } from '../constants/module';\nimport { Config } from '../../config';\n\nexport const createAzureDevOpsIngestor = async ({\n config,\n logger,\n}: {\n config: RootConfigService;\n logger: LoggerService;\n}): Promise<Ingestor> => {\n // Default to common file types if none are specified\n const defaultFileTypes = ['.md', '.json'];\n\n // Get configuration values\n const repositoriesFilter = config.getOptional<\n Config['aiAssistant']['ingestors']['azureDevOps']['repositories']\n >('aiAssistant.ingestors.azureDevOps.repositories');\n\n const fileTypes =\n config.getOptionalStringArray(\n 'aiAssistant.ingestors.azureDevOps.fileTypes',\n ) ?? defaultFileTypes;\n\n // Create Azure DevOps service\n const adoService = await createAzureDevOpsService({ config, logger });\n\n /** Ingest Azure DevOps repositories in batches */\n const ingestAzureDevOpsBatch = async (\n saveDocumentsBatch: IngestorOptions['saveDocumentsBatch'],\n ) => {\n const repositoriesList = await adoService.getRepos();\n\n if (repositoriesList.length === 0) {\n logger.warn('No repositories found in the Azure DevOps project');\n return;\n }\n\n logger.info(\n `Filtering for repositories: ${repositoriesFilter\n ?.map(repo => repo.name)\n .join(', ')}`,\n );\n\n // Filter repositories if a filter is provided in the config\n const repositoriesToIngest = repositoriesFilter\n ? repositoriesList.filter(repo =>\n repositoriesFilter?.some(\n filteredRepo =>\n filteredRepo.name.toLowerCase() === repo.name!.toLowerCase(),\n ),\n )\n : repositoriesList;\n\n if (repositoriesToIngest.length === 0) {\n logger.warn(\n 'No repositories found for ingestion after applying the filter',\n );\n return;\n }\n\n logger.info(\n `Ingesting ${repositoriesToIngest.length} repositories from Azure DevOps`,\n );\n\n // Get items from each repository and create documents to be embedded\n for (const repo of repositoriesToIngest) {\n logger.info(\n `Beginning ingestion for repository: ${repo.name} (${repo.id})`,\n );\n\n // Determine the file types to use for this repository or use default\n const repositoryFileTypesFilter =\n repositoriesFilter?.find(\n r => r.name.toLowerCase() === repo.name!.toLowerCase(),\n )?.fileTypes ?? fileTypes;\n\n logger.info(\n `Processing file types for repository ${\n repo.name\n }: [${repositoryFileTypesFilter.join(', ')}]`,\n );\n\n // Get the items to be ingested from the repository based on the file types filter\n const items = await adoService.getRepoItems(\n repo.id!,\n repositoryFileTypesFilter,\n );\n\n if (items.length === 0) {\n logger.warn(\n `No items found for ingestion in the Azure DevOps repository ${\n repo.name\n } (${\n repo.id\n }) with the specified file types filter: [${repositoryFileTypesFilter.join(\n ', ',\n )}]`,\n );\n continue;\n }\n\n logger.debug(`Items: ${JSON.stringify(items, null, 2)}`);\n\n // Generate embedding documents for each item\n const documents: EmbeddingDocument[] = [];\n\n for (const item of items) {\n const content = await adoService.getRepoItemContent(\n repo.id!,\n item.path!,\n );\n logger.info(`Retrieved content for Azure DevOps item: ${item.path}`);\n\n const text = await streamToString(content);\n\n const document: EmbeddingDocument = {\n metadata: {\n source: MODULE_ID,\n id: `${repo.id}:${item.path}`,\n url: item.url,\n organization: adoService.organization,\n project: adoService.project,\n repository: repo.name!,\n },\n content: text,\n };\n\n documents.push(document);\n }\n\n // Save the documents in batches\n await saveDocumentsBatch(documents);\n\n logger.info(\n `${documents.length} documents ingested for Azure DevOps repository: ${repo.name}`,\n );\n }\n };\n\n const ingest: Ingestor['ingest'] = async ({ saveDocumentsBatch }) => {\n await ingestAzureDevOpsBatch(saveDocumentsBatch);\n };\n\n return {\n id: MODULE_ID,\n ingest,\n };\n};\n"],"names":["createAzureDevOpsService","streamToString","MODULE_ID"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sweetoburrito/backstage-plugin-ai-assistant-backend-module-ingestor-azure-devops",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "The ingestor-azure-devops backend module for the ai-assistant plugin.",
|
|
6
|
+
"main": "dist/index.cjs.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"main": "dist/index.cjs.js",
|
|
11
|
+
"types": "dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"backstage": {
|
|
14
|
+
"role": "backend-plugin-module",
|
|
15
|
+
"pluginId": "ai-assistant",
|
|
16
|
+
"pluginPackage": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
|
|
17
|
+
"features": {
|
|
18
|
+
".": "@backstage/BackendFeature"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "backstage-cli package start",
|
|
23
|
+
"build": "backstage-cli package build",
|
|
24
|
+
"lint": "backstage-cli package lint",
|
|
25
|
+
"test": "backstage-cli package test",
|
|
26
|
+
"clean": "backstage-cli package clean",
|
|
27
|
+
"prepack": "backstage-cli package prepack",
|
|
28
|
+
"postpack": "backstage-cli package postpack"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@backstage/backend-plugin-api": "^1.4.1",
|
|
32
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.3.0",
|
|
33
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.4.0",
|
|
34
|
+
"azure-devops-node-api": "^15.1.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@backstage/backend-test-utils": "^1.7.0",
|
|
38
|
+
"@backstage/cli": "^0.33.1"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"config.d.ts"
|
|
43
|
+
],
|
|
44
|
+
"configSchema": "config.d.ts",
|
|
45
|
+
"typesVersions": {
|
|
46
|
+
"*": {
|
|
47
|
+
"package.json": [
|
|
48
|
+
"package.json"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|