mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
refactor: Update LLMBase.ts and OpenAI.ts to import CopilotActionPrompt from the new location
This commit is contained in:
@@ -62,7 +62,7 @@ export default class CopilotActionAPI extends BaseAPI<
|
||||
const copilotActionTypes: Array<CopilotActionTypePriority> =
|
||||
await CopilotActionTypePriorityService.findBy({
|
||||
query: {
|
||||
codeRepositoryId: codeRepository.id!
|
||||
codeRepositoryId: codeRepository.id!,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
@@ -134,7 +134,7 @@ export default class CopilotActionAPI extends BaseAPI<
|
||||
query: {
|
||||
codeRepositoryId: codeRepository.id!,
|
||||
serviceCatalogId: new ObjectID(serviceCatalogId),
|
||||
copilotActionStatus: CopilotActionStatus.IN_QUEUE
|
||||
copilotActionStatus: CopilotActionStatus.IN_QUEUE,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
|
||||
@@ -38,16 +38,28 @@ export class Service extends DatabaseService<Model> {
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingItem) {
|
||||
return {
|
||||
createBy: createBy,
|
||||
carryForward: null,
|
||||
};
|
||||
if (existingItem) {
|
||||
throw new BadDataException(
|
||||
"Action Type already exists for this repository.",
|
||||
);
|
||||
}
|
||||
|
||||
throw new BadDataException(
|
||||
"Action Type already exists for this repository.",
|
||||
);
|
||||
// check if the priority is in between 1 and 5.
|
||||
|
||||
const priority: number | undefined = createBy.data.priority;
|
||||
|
||||
if (!priority) {
|
||||
throw new BadDataException("Priority is required");
|
||||
}
|
||||
|
||||
if (priority < 1 || priority > 5) {
|
||||
throw new BadDataException("Priority must be between 1 and 5");
|
||||
}
|
||||
|
||||
return {
|
||||
createBy: createBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,16 @@ import Dictionary from "Common/Types/Dictionary";
|
||||
import ServiceLanguageUtil from "Common/Utils/TechStack";
|
||||
|
||||
export default class CodeRepositoryUtil {
|
||||
public static getCurrentCommitHash(data: {
|
||||
repoPath: string;
|
||||
}): Promise<string> {
|
||||
const command: string = `cd ${data.repoPath} && git rev-parse HEAD`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
return Execute.executeCommand(command);
|
||||
}
|
||||
|
||||
public static async addAllChangedFilesToGit(data: {
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
|
||||
@@ -6,6 +6,8 @@ type GetStringFunction = () => string;
|
||||
type GetStringOrNullFunction = () => string | null;
|
||||
type GetURLFunction = () => URL;
|
||||
|
||||
export const MIN_ITEMS_IN_QUEUE_PER_SERVICE_CATALOG: number = 10;
|
||||
|
||||
export const GetIsCopilotDisabled: () => boolean = () => {
|
||||
return process.env["DISABLE_COPILOT"] === "true";
|
||||
};
|
||||
@@ -65,10 +67,10 @@ export const GetLlmType: GetLlmTypeFunction = (): LlmType => {
|
||||
}
|
||||
|
||||
if (GetLlmServerUrl()) {
|
||||
return LlmType.LLM;
|
||||
return LlmType.ONEUPTIME_LLM;
|
||||
}
|
||||
|
||||
return LlmType.LLM;
|
||||
return LlmType.ONEUPTIME_LLM;
|
||||
};
|
||||
|
||||
export const FixNumberOfCodeEventsInEachRun: number = 5;
|
||||
|
||||
108
Copilot/Init.ts
108
Copilot/Init.ts
@@ -4,19 +4,15 @@ import CodeRepositoryUtil, {
|
||||
} from "./Utils/CodeRepository";
|
||||
import InitUtil from "./Utils/Init";
|
||||
import ServiceCopilotCodeRepositoryUtil from "./Utils/ServiceRepository";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import CodeRepositoryFile from "Common/Server/Utils/CodeRepository/CodeRepositoryFile";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import CopilotActionUtil from "./Utils/CopilotAction";
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotAction from "Common/Models/DatabaseModels/CopilotAction";
|
||||
import {
|
||||
FixNumberOfCodeEventsInEachRun,
|
||||
GetIsCopilotDisabled,
|
||||
GetLlmType,
|
||||
} from "./Config";
|
||||
import CopiotActionTypeOrder from "./Types/CopilotActionTypeOrder";
|
||||
import CopilotActionService, {
|
||||
CopilotExecutionResult,
|
||||
} from "./Service/CopilotActions/Index";
|
||||
@@ -25,7 +21,6 @@ import PullRequest from "Common/Types/CodeRepository/PullRequest";
|
||||
import ServiceCopilotCodeRepository from "Common/Models/DatabaseModels/ServiceCopilotCodeRepository";
|
||||
import CopilotActionProcessingException from "./Exceptions/CopilotActionProcessingException";
|
||||
import CopilotPullRequest from "Common/Models/DatabaseModels/CopilotPullRequest";
|
||||
import PullRequestState from "Common/Types/CodeRepository/PullRequestState";
|
||||
import ProcessUtil from "./Utils/Process";
|
||||
|
||||
let currentFixCount: number = 1;
|
||||
@@ -47,7 +42,7 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
const codeRepositoryResult: CodeRepositoryResult = await InitUtil.init();
|
||||
|
||||
// before cloning the repo, check if there are any services to improve.
|
||||
const servicesToImprove =
|
||||
const servicesToImprove: ServiceCopilotCodeRepository[] =
|
||||
await ServiceCopilotCodeRepositoryUtil.getServicesToImprove(
|
||||
codeRepositoryResult,
|
||||
);
|
||||
@@ -59,92 +54,17 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
await setUpRepository();
|
||||
|
||||
for (const serviceRepository of servicesToImprove) {
|
||||
|
||||
checkIfCurrentFixCountIsLessThanFixNumberOfCodeEventsInEachRun();
|
||||
|
||||
const filesInService: Dictionary<CodeRepositoryFile> =
|
||||
await ServiceCopilotCodeRepositoryUtil.getFilesInServiceDirectory({
|
||||
serviceRepository,
|
||||
const actionsToWorkOn: Array<CopilotAction> =
|
||||
await CopilotActionUtil.getActionsToWorkOn({
|
||||
serviceCatalogId: serviceRepository.serviceCatalog!.id!,
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`Files found in ${serviceRepository.serviceCatalog?.name}: ${
|
||||
Object.keys(filesInService).length
|
||||
}`,
|
||||
);
|
||||
|
||||
const files: Array<CodeRepositoryFile> = Object.values(filesInService);
|
||||
|
||||
for (const file of files) {
|
||||
for (const actionToWorkOn of actionsToWorkOn) {
|
||||
checkIfCurrentFixCountIsLessThanFixNumberOfCodeEventsInEachRun();
|
||||
// check copilot events for this file.
|
||||
|
||||
const copilotActions: Array<CopilotAction> =
|
||||
await CopilotActionUtil.getCopilotActions({
|
||||
serviceCatalogId: serviceRepository.serviceCatalog!.id!,
|
||||
filePath: file.filePath,
|
||||
});
|
||||
|
||||
// check if there's an open PR for this file.
|
||||
|
||||
const openPullRequests: Array<CopilotPullRequest> = copilotActions
|
||||
.filter((copilotAction: CopilotAction) => {
|
||||
return (
|
||||
copilotAction.copilotPullRequest &&
|
||||
copilotAction.copilotPullRequest.pullRequestId &&
|
||||
copilotAction.copilotPullRequest.copilotPullRequestStatus ===
|
||||
PullRequestState.Open
|
||||
);
|
||||
})
|
||||
.map((copilotAction: CopilotAction) => {
|
||||
return copilotAction.copilotPullRequest!;
|
||||
});
|
||||
|
||||
if (openPullRequests.length > 0) {
|
||||
const prNumbers: string = openPullRequests
|
||||
.map((pr: CopilotPullRequest) => {
|
||||
return `#${pr.pullRequestId?.toString()}`;
|
||||
})
|
||||
.join(", ");
|
||||
|
||||
// this file already has an open PR. Ignore this file and move to the next file.
|
||||
logger.info(
|
||||
`File ${file.filePath} already has an open PR ${prNumbers}. Moving to next file.`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const eventsCompletedOnThisFile: Array<CopilotActionType> = [];
|
||||
|
||||
for (const copilotAction of copilotActions) {
|
||||
if (
|
||||
copilotAction.copilotActionType &&
|
||||
eventsCompletedOnThisFile.includes(copilotAction.copilotActionType)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add to eventsCompletedOnThisFile
|
||||
eventsCompletedOnThisFile.push(copilotAction.copilotActionType!);
|
||||
}
|
||||
|
||||
let nextEventToFix: CopilotActionType | undefined = undefined;
|
||||
|
||||
for (const copilotActionType of CopiotActionTypeOrder) {
|
||||
if (!eventsCompletedOnThisFile.includes(copilotActionType)) {
|
||||
nextEventToFix = copilotActionType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextEventToFix) {
|
||||
logger.info(
|
||||
`All fixes completed on ${file.filePath}. Moving to next file.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let executionResult: CopilotExecutionResult | null = null;
|
||||
|
||||
let currentRetryCount: number = 0;
|
||||
@@ -154,9 +74,7 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
executionResult = await executeAction({
|
||||
serviceRepository,
|
||||
file,
|
||||
filesInService,
|
||||
nextEventToFix,
|
||||
copilotAction: actionToWorkOn,
|
||||
});
|
||||
break;
|
||||
} catch (e) {
|
||||
@@ -178,9 +96,7 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
|
||||
interface ExecuteActionData {
|
||||
serviceRepository: ServiceCopilotCodeRepository;
|
||||
file: CodeRepositoryFile;
|
||||
filesInService: Dictionary<CodeRepositoryFile>;
|
||||
nextEventToFix: CopilotActionType;
|
||||
copilotAction: CopilotAction;
|
||||
}
|
||||
|
||||
type ExecutionActionFunction = (
|
||||
@@ -190,20 +106,16 @@ type ExecutionActionFunction = (
|
||||
const executeAction: ExecutionActionFunction = async (
|
||||
data: ExecuteActionData,
|
||||
): Promise<CopilotExecutionResult | null> => {
|
||||
const { serviceRepository, file, filesInService, nextEventToFix } = data;
|
||||
const { serviceRepository, copilotAction } = data;
|
||||
|
||||
try {
|
||||
return await CopilotActionService.execute({
|
||||
serviceRepository: serviceRepository,
|
||||
copilotActionType: nextEventToFix,
|
||||
input: {
|
||||
currentFilePath: file.filePath, // this is the file path where optimization is needed or should start from.
|
||||
files: filesInService,
|
||||
},
|
||||
copilotAction: copilotAction,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof CopilotActionProcessingException) {
|
||||
// This is not a serious exception, so we just move on to the next file.
|
||||
// This is not a serious exception, so we just move on to the next action.
|
||||
logger.info(e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import NotImplementedException from "Common/Types/Exception/NotImplementedException";
|
||||
|
||||
export default class CopilotActionPropBase{
|
||||
public static async isActionRequired(data: {
|
||||
copilotActionBase
|
||||
}): Promise<boolean> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -4,46 +4,21 @@ import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import LLM from "../LLM/LLM";
|
||||
import { GetLlmType } from "../../Config";
|
||||
import Text from "Common/Types/Text";
|
||||
import LocalFile from "Common/Server/Utils/LocalFile";
|
||||
import CodeRepositoryFile from "Common/Server/Utils/CodeRepository/CodeRepositoryFile";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { CopilotPromptResult } from "../LLM/LLMBase";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import CodeRepositoryUtil, { RepoScriptType } from "../../Utils/CodeRepository";
|
||||
|
||||
export interface CopilotActionRunResult {
|
||||
files: Dictionary<CodeRepositoryFile>;
|
||||
}
|
||||
|
||||
export enum PromptRole {
|
||||
System = "system",
|
||||
User = "user",
|
||||
Assistant = "assistant",
|
||||
}
|
||||
|
||||
export interface Prompt {
|
||||
content: string;
|
||||
role: PromptRole;
|
||||
}
|
||||
|
||||
export interface CopilotActionPrompt {
|
||||
messages: Array<Prompt>;
|
||||
timeoutInMinutes?: number | undefined;
|
||||
}
|
||||
|
||||
export interface CopilotActionVars {
|
||||
currentFilePath: string;
|
||||
files: Dictionary<CodeRepositoryFile>;
|
||||
}
|
||||
|
||||
export interface CopilotProcess {
|
||||
result: CopilotActionRunResult;
|
||||
input: CopilotActionVars;
|
||||
}
|
||||
import CopilotActionProp from "Common/Types/Copilot/CopilotActionProps/Index";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import CopilotAction from "Common/Models/DatabaseModels/CopilotAction";
|
||||
import {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
CopilotProcessStart,
|
||||
} from "./Types";
|
||||
|
||||
export default class CopilotActionBase {
|
||||
public llmType: LlmType = LlmType.LLM; // temp value which will be overridden in the constructor
|
||||
public llmType: LlmType = LlmType.ONEUPTIME_LLM; // temp value which will be overridden in the constructor
|
||||
|
||||
public copilotActionType: CopilotActionType =
|
||||
CopilotActionType.IMPROVE_COMMENTS; // temp value which will be overridden in the constructor
|
||||
@@ -54,36 +29,40 @@ export default class CopilotActionBase {
|
||||
this.llmType = GetLlmType();
|
||||
}
|
||||
|
||||
public async validateExecutionStep(data: CopilotProcess): Promise<boolean> {
|
||||
protected async isActionRequired(_data: {
|
||||
copilotActionProp: CopilotActionProp;
|
||||
}): Promise<boolean> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public getActionsToQueue(_data: {
|
||||
serviceCatalogId: ObjectID;
|
||||
maxActionsToQueue: number;
|
||||
}): Promise<Array<CopilotAction>> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected async validateExecutionStep(
|
||||
_data: CopilotProcess,
|
||||
): Promise<boolean> {
|
||||
if (!this.copilotActionType) {
|
||||
throw new BadDataException("Copilot Action Type is not set");
|
||||
}
|
||||
|
||||
// check if the file extension is accepted or not
|
||||
|
||||
if (
|
||||
!this.acceptFileExtentions.find((item: string) => {
|
||||
return item.includes(
|
||||
LocalFile.getFileExtension(data.input.currentFilePath),
|
||||
);
|
||||
})
|
||||
) {
|
||||
logger.info(
|
||||
`The file extension ${data.input.currentFilePath.split(".").pop()} is not accepted by the copilot action ${this.copilotActionType}. Ignore this file...`,
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate by default.
|
||||
return true;
|
||||
}
|
||||
|
||||
public async onAfterExecute(data: CopilotProcess): Promise<CopilotProcess> {
|
||||
protected async onAfterExecute(
|
||||
data: CopilotProcess,
|
||||
): Promise<CopilotProcess> {
|
||||
// do nothing
|
||||
return data;
|
||||
}
|
||||
|
||||
public async onBeforeExecute(data: CopilotProcess): Promise<CopilotProcess> {
|
||||
protected async onBeforeExecute(
|
||||
data: CopilotProcess,
|
||||
): Promise<CopilotProcess> {
|
||||
// do nothing
|
||||
return data;
|
||||
}
|
||||
@@ -95,18 +74,15 @@ export default class CopilotActionBase {
|
||||
return Text.replaceAll(bracnhName, "--", "-");
|
||||
}
|
||||
|
||||
public async getPullRequestTitle(data: CopilotProcess): Promise<string> {
|
||||
return `[OneUptime Copilot] ${this.copilotActionType} on ${data.input.currentFilePath}`;
|
||||
public async getPullRequestTitle(_data: CopilotProcess): Promise<string> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async getPullRequestBody(data: CopilotProcess): Promise<string> {
|
||||
return `OneUptime Copilot: ${this.copilotActionType} on ${data.input.currentFilePath}
|
||||
|
||||
${await this.getDefaultPullRequestBody()}
|
||||
`;
|
||||
public async getPullRequestBody(_data: CopilotProcess): Promise<string> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async getDefaultPullRequestBody(): Promise<string> {
|
||||
protected async getDefaultPullRequestBody(): Promise<string> {
|
||||
return `
|
||||
|
||||
#### Warning
|
||||
@@ -118,28 +94,33 @@ If you have any feedback or suggestions, please let us know. We would love to h
|
||||
`;
|
||||
}
|
||||
|
||||
public async getCommitMessage(data: CopilotProcess): Promise<string> {
|
||||
return `OneUptime Copilot: ${this.copilotActionType} on ${data.input.currentFilePath}`;
|
||||
public async getCommitMessage(_data: CopilotProcess): Promise<string> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async onExecutionStep(data: CopilotProcess): Promise<CopilotProcess> {
|
||||
protected async onExecutionStep(
|
||||
data: CopilotProcess,
|
||||
): Promise<CopilotProcess> {
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
|
||||
public async isActionComplete(_data: CopilotProcess): Promise<boolean> {
|
||||
protected async isActionComplete(_data: CopilotProcess): Promise<boolean> {
|
||||
return true; // by default the action is completed
|
||||
}
|
||||
|
||||
public async getNextFilePath(_data: CopilotProcess): Promise<string | null> {
|
||||
protected async getNextFilePath(
|
||||
_data: CopilotProcess,
|
||||
): Promise<string | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async execute(data: CopilotProcess): Promise<CopilotProcess | null> {
|
||||
public async execute(
|
||||
data: CopilotProcessStart,
|
||||
): Promise<CopilotProcess | null> {
|
||||
logger.info(
|
||||
"Executing Copilot Action (this will take several minutes to complete): " +
|
||||
this.copilotActionType,
|
||||
);
|
||||
logger.info("Current File Path: " + data.input.currentFilePath);
|
||||
|
||||
const onBeforeExecuteActionScript: string | null =
|
||||
await CodeRepositoryUtil.getRepoScript({
|
||||
@@ -158,37 +139,46 @@ If you have any feedback or suggestions, please let us know. We would love to h
|
||||
logger.info("on-before-copilot-action script executed successfully");
|
||||
}
|
||||
|
||||
data = await this.onBeforeExecute(data);
|
||||
|
||||
if (!data.result) {
|
||||
data.result = {
|
||||
const processData: CopilotProcess = await this.onBeforeExecute({
|
||||
...data,
|
||||
result: {
|
||||
files: {},
|
||||
statusMessage: "",
|
||||
logs: [],
|
||||
},
|
||||
});
|
||||
|
||||
if (!processData.result) {
|
||||
processData.result = {
|
||||
files: {},
|
||||
statusMessage: "",
|
||||
logs: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.result.files) {
|
||||
data.result.files = {};
|
||||
if (!processData.result.files) {
|
||||
processData.result.files = {};
|
||||
}
|
||||
|
||||
let isActionComplete: boolean = false;
|
||||
|
||||
while (!isActionComplete) {
|
||||
if (!(await this.validateExecutionStep(data))) {
|
||||
if (!(await this.validateExecutionStep(processData))) {
|
||||
// execution step not valid
|
||||
// return data as it is
|
||||
|
||||
return data;
|
||||
return processData;
|
||||
}
|
||||
|
||||
data = await this.onExecutionStep(data);
|
||||
data = await this.onExecutionStep(processData);
|
||||
|
||||
isActionComplete = await this.isActionComplete(data);
|
||||
isActionComplete = await this.isActionComplete(processData);
|
||||
}
|
||||
|
||||
data = await this.onAfterExecute(data);
|
||||
data = await this.onAfterExecute(processData);
|
||||
|
||||
// write to disk.
|
||||
await this.writeToDisk({ data });
|
||||
await this.writeToDisk({ data: processData });
|
||||
|
||||
const onAfterExecuteActionScript: string | null =
|
||||
await CodeRepositoryUtil.getRepoScript({
|
||||
@@ -209,7 +199,7 @@ If you have any feedback or suggestions, please let us know. We would love to h
|
||||
logger.info("on-after-copilot-action script executed successfully");
|
||||
}
|
||||
|
||||
return data;
|
||||
return processData;
|
||||
}
|
||||
|
||||
protected async _getPrompt(
|
||||
@@ -228,24 +218,20 @@ If you have any feedback or suggestions, please let us know. We would love to h
|
||||
return prompt;
|
||||
}
|
||||
|
||||
public async getPrompt(
|
||||
protected async getPrompt(
|
||||
_data: CopilotProcess,
|
||||
_inputCode: string,
|
||||
): Promise<CopilotActionPrompt | null> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async askCopilot(
|
||||
protected async askCopilot(
|
||||
prompt: CopilotActionPrompt,
|
||||
): Promise<CopilotPromptResult> {
|
||||
return await LLM.getResponse(prompt);
|
||||
}
|
||||
|
||||
public async getInputCode(data: CopilotProcess): Promise<string> {
|
||||
return data.input.files[data.input.currentFilePath]?.fileContent as string;
|
||||
}
|
||||
|
||||
public async writeToDisk(data: { data: CopilotProcess }): Promise<void> {
|
||||
protected async writeToDisk(data: { data: CopilotProcess }): Promise<void> {
|
||||
// write all the modified files.
|
||||
|
||||
const processResult: CopilotProcess = data.data;
|
||||
@@ -267,15 +253,15 @@ If you have any feedback or suggestions, please let us know. We would love to h
|
||||
}
|
||||
}
|
||||
|
||||
public async discardAllChanges(): Promise<void> {
|
||||
protected async discardAllChanges(): Promise<void> {
|
||||
await CodeRepositoryUtil.discardAllChangesOnCurrentBranch();
|
||||
}
|
||||
|
||||
public async splitInputCode(data: {
|
||||
copilotProcess: CopilotProcess;
|
||||
protected async splitInputCode(data: {
|
||||
code: string;
|
||||
itemSize: number;
|
||||
}): Promise<string[]> {
|
||||
const inputCode: string = await this.getInputCode(data.copilotProcess);
|
||||
const inputCode: string = data.code;
|
||||
|
||||
const items: Array<string> = [];
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
|
||||
export default class FixGrammarAndSpelling extends CopilotActionBase {
|
||||
public constructor() {
|
||||
super();
|
||||
this.copilotActionType = CopilotActionType.FIX_GRAMMAR_AND_SPELLING;
|
||||
this.acceptFileExtentions = [
|
||||
...CodeRepositoryUtil.getCodeFileExtentions(),
|
||||
...CodeRepositoryUtil.getReadmeFileExtentions(),
|
||||
];
|
||||
}
|
||||
|
||||
public override async getPrompt(
|
||||
_data: CopilotProcess,
|
||||
): Promise<CopilotActionPrompt> {
|
||||
const prompt: string = `Please fix grammar and spelling in this file.
|
||||
|
||||
If you think the file is good and has no grammar or spelling mistakes, please reply with the following text:
|
||||
--all-good--
|
||||
|
||||
Here is the file content. This is in {{fileLanguage}}:
|
||||
|
||||
{{code}}
|
||||
`;
|
||||
|
||||
const systemPrompt: string = `You are an expert programmer. Here are your instructions:
|
||||
- You will follow the instructions given by the user strictly.
|
||||
- You will not deviate from the instructions given by the user.
|
||||
- You will not change the code unnecessarily. For example you will not change the code structure, logic, quotes around strings, or functionality.`;
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
content: systemPrompt,
|
||||
role: PromptRole.System,
|
||||
},
|
||||
{
|
||||
content: prompt,
|
||||
role: PromptRole.User,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CopilotActionBase from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
import TechStack from "Common/Types/ServiceCatalog/TechStack";
|
||||
import { CopilotPromptResult } from "../LLM/LLMBase";
|
||||
import CodeRepositoryFile from "Common/Server/Utils/CodeRepository/CodeRepositoryFile";
|
||||
import Text from "Common/Types/Text";
|
||||
import { CopilotActionPrompt, CopilotProcess } from "./Types";
|
||||
import { PromptRole } from "../LLM/Prompt";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
|
||||
export default class ImproveComments extends CopilotActionBase {
|
||||
public isRequirementsMet: boolean = false;
|
||||
@@ -97,7 +95,7 @@ export default class ImproveComments extends CopilotActionBase {
|
||||
// Action Prompt
|
||||
|
||||
const codeParts: string[] = await this.splitInputCode({
|
||||
copilotProcess: data,
|
||||
code: "",
|
||||
itemSize: 500,
|
||||
});
|
||||
|
||||
@@ -131,11 +129,14 @@ export default class ImproveComments extends CopilotActionBase {
|
||||
|
||||
newContent = newContent.trim();
|
||||
|
||||
// add to result.
|
||||
data.result.files[data.input.currentFilePath] = {
|
||||
...data.input.files[data.input.currentFilePath],
|
||||
fileContent: newContent,
|
||||
} as CodeRepositoryFile;
|
||||
logger.debug("New Content:");
|
||||
logger.debug(newContent);
|
||||
|
||||
// // add to result.
|
||||
// data.result.files[data.input.currentFilePath] = {
|
||||
// ...data.input.files[data.input.currentFilePath],
|
||||
// fileContent: newContent,
|
||||
// } as CodeRepositoryFile;
|
||||
|
||||
this.isRequirementsMet = true;
|
||||
return data;
|
||||
@@ -201,11 +202,13 @@ export default class ImproveComments extends CopilotActionBase {
|
||||
}
|
||||
|
||||
public override async getPrompt(
|
||||
data: CopilotProcess,
|
||||
_data: CopilotProcess,
|
||||
inputCode: string,
|
||||
): Promise<CopilotActionPrompt> {
|
||||
const fileLanguage: TechStack = data.input.files[data.input.currentFilePath]
|
||||
?.fileLanguage as TechStack;
|
||||
// const fileLanguage: TechStack = data.input.files[data.input.currentFilePath]
|
||||
// ?.fileLanguage as TechStack;
|
||||
|
||||
const fileLanguage: TechStack = TechStack.TypeScript;
|
||||
|
||||
const prompt: string = `Please improve the comments in this code. Please only add minimal comments and comment code which is hard to understand. Please add comments in new line and do not add inline comments.
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
|
||||
export default class ImproveVariableNames extends CopilotActionBase {
|
||||
public constructor() {
|
||||
super();
|
||||
this.copilotActionType = CopilotActionType.IMPROVE_VARIABLE_NAMES;
|
||||
this.acceptFileExtentions = CodeRepositoryUtil.getCodeFileExtentions();
|
||||
}
|
||||
|
||||
public override async getPrompt(
|
||||
_data: CopilotProcess,
|
||||
): Promise<CopilotActionPrompt> {
|
||||
const prompt: string = `Please improve this readme.
|
||||
|
||||
If you think the readme is already well commented, please reply with the following text:
|
||||
--all-good--
|
||||
|
||||
Here is the readme content. This is in {{fileLanguage}}:
|
||||
|
||||
{{code}}
|
||||
`;
|
||||
|
||||
const systemPrompt: string = `You are an expert programmer. Here are your instructions:
|
||||
- You will follow the instructions given by the user strictly.
|
||||
- You will not deviate from the instructions given by the user.
|
||||
- You will not change the code unnecessarily. For example you will not change the code structure, logic, quotes around strings, or functionality.`;
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
content: systemPrompt,
|
||||
role: PromptRole.System,
|
||||
},
|
||||
{
|
||||
content: prompt,
|
||||
role: PromptRole.User,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
|
||||
export default class ImproveReadme extends CopilotActionBase {
|
||||
public constructor() {
|
||||
super();
|
||||
this.copilotActionType = CopilotActionType.IMPROVE_README;
|
||||
this.acceptFileExtentions = CodeRepositoryUtil.getReadmeFileExtentions();
|
||||
}
|
||||
|
||||
public override async getPrompt(
|
||||
_data: CopilotProcess,
|
||||
): Promise<CopilotActionPrompt> {
|
||||
const prompt: string = `Please improve this readme.
|
||||
|
||||
If you think the readme is already well commented, please reply with the following text:
|
||||
--all-good--
|
||||
|
||||
Here is the readme content. This is in {{fileLanguage}}:
|
||||
|
||||
{{code}}
|
||||
`;
|
||||
|
||||
const systemPrompt: string = `You are an expert programmer. Here are your instructions:
|
||||
- You will follow the instructions given by the user strictly.
|
||||
- You will not deviate from the instructions given by the user.
|
||||
- You will not change the code unnecessarily. For example you will not change the code structure, logic, quotes around strings, or functionality.`;
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
content: systemPrompt,
|
||||
role: PromptRole.System,
|
||||
},
|
||||
{
|
||||
content: prompt,
|
||||
role: PromptRole.User,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import ImproveComments from "./ImproveComments";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionVars,
|
||||
CopilotProcess,
|
||||
} from "./CopilotActionsBase";
|
||||
import CopilotActionBase from "./CopilotActionsBase";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import CodeRepositoryUtil, { RepoScriptType } from "../../Utils/CodeRepository";
|
||||
import ServiceCopilotCodeRepository from "Common/Models/DatabaseModels/ServiceCopilotCodeRepository";
|
||||
@@ -12,26 +9,14 @@ import PullRequest from "Common/Types/CodeRepository/PullRequest";
|
||||
import CopilotAction from "Common/Models/DatabaseModels/CopilotAction";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import CopilotActionStatus from "Common/Types/Copilot/CopilotActionStatus";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { GetOneUptimeURL, GetRepositorySecretKey } from "../../Config";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import API from "Common/Utils/API";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import FixGrammarAndSpelling from "./FixGrammarAndSpelling";
|
||||
import RefactorCode from "./RefactorCode";
|
||||
import WriteUnitTests from "./WriteUnitTests";
|
||||
import ImproveReadme from "./ImroveReadme";
|
||||
import CopilotPullRequest from "Common/Models/DatabaseModels/CopilotPullRequest";
|
||||
import CopilotPullRequestService from "../CopilotPullRequest";
|
||||
import CopilotActionUtil from "../../Utils/CopilotAction";
|
||||
import { CopilotProcess } from "./Types";
|
||||
|
||||
const actionDictionary: Dictionary<typeof CopilotActionBase> = {
|
||||
export const ActionDictionary: Dictionary<typeof CopilotActionBase> = {
|
||||
[CopilotActionType.IMPROVE_COMMENTS]: ImproveComments,
|
||||
[CopilotActionType.FIX_GRAMMAR_AND_SPELLING]: FixGrammarAndSpelling,
|
||||
[CopilotActionType.REFACTOR_CODE]: RefactorCode,
|
||||
[CopilotActionType.WRITE_UNIT_TESTS]: WriteUnitTests,
|
||||
[CopilotActionType.IMPROVE_README]: ImproveReadme,
|
||||
};
|
||||
|
||||
export interface CopilotExecutionResult {
|
||||
@@ -42,8 +27,7 @@ export interface CopilotExecutionResult {
|
||||
export default class CopilotActionService {
|
||||
public static async execute(data: {
|
||||
serviceRepository: ServiceCopilotCodeRepository;
|
||||
copilotActionType: CopilotActionType;
|
||||
input: CopilotActionVars;
|
||||
copilotAction: CopilotAction;
|
||||
}): Promise<CopilotExecutionResult> {
|
||||
await CodeRepositoryUtil.discardAllChangesOnCurrentBranch();
|
||||
|
||||
@@ -51,20 +35,17 @@ export default class CopilotActionService {
|
||||
|
||||
await CodeRepositoryUtil.pullChanges();
|
||||
|
||||
const actionType: typeof CopilotActionBase | undefined =
|
||||
actionDictionary[data.copilotActionType];
|
||||
const ActionType: typeof CopilotActionBase | undefined =
|
||||
ActionDictionary[data.copilotAction.copilotActionType!];
|
||||
|
||||
if (!actionType) {
|
||||
if (!ActionType) {
|
||||
throw new BadDataException("Invalid CopilotActionType");
|
||||
}
|
||||
|
||||
const action: CopilotActionBase = new actionType() as CopilotActionBase;
|
||||
const action: CopilotActionBase = new ActionType() as CopilotActionBase;
|
||||
|
||||
const processResult: CopilotProcess | null = await action.execute({
|
||||
input: data.input,
|
||||
result: {
|
||||
files: {},
|
||||
},
|
||||
input: data.copilotAction.copilotActionProp!,
|
||||
});
|
||||
|
||||
let executionResult: CopilotExecutionResult = {
|
||||
@@ -186,33 +167,31 @@ export default class CopilotActionService {
|
||||
logger.info("No result obtained from Copilot Action");
|
||||
}
|
||||
|
||||
const fileCommitHash: string | undefined =
|
||||
data.input.files[data.input.currentFilePath]?.gitCommitHash;
|
||||
const getCurrentCommitHash: string =
|
||||
await CodeRepositoryUtil.getCurrentCommitHash();
|
||||
|
||||
if (!fileCommitHash) {
|
||||
throw new BadDataException("File commit hash not found");
|
||||
}
|
||||
|
||||
await CopilotActionService.addCopilotActionToDatabase({
|
||||
await CopilotActionService.updateCopilotAction({
|
||||
serviceCatalogId: data.serviceRepository.serviceCatalog!.id!,
|
||||
serviceRepositoryId: data.serviceRepository.id!,
|
||||
filePath: data.input.currentFilePath,
|
||||
commitHash: fileCommitHash,
|
||||
copilotActionType: data.copilotActionType,
|
||||
commitHash: getCurrentCommitHash,
|
||||
pullRequest: pullRequest,
|
||||
copilotActionStatus: executionResult.status,
|
||||
copilotActonId: data.copilotAction.id!,
|
||||
statusMessage: processResult?.result.statusMessage || "",
|
||||
logs: processResult?.result.logs || [],
|
||||
});
|
||||
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
private static async addCopilotActionToDatabase(data: {
|
||||
private static async updateCopilotAction(data: {
|
||||
copilotActonId: ObjectID;
|
||||
serviceCatalogId: ObjectID;
|
||||
serviceRepositoryId: ObjectID;
|
||||
filePath: string;
|
||||
commitHash: string;
|
||||
copilotActionType: CopilotActionType;
|
||||
pullRequest: PullRequest | null;
|
||||
statusMessage: string;
|
||||
logs: Array<string>;
|
||||
copilotActionStatus: CopilotActionStatus;
|
||||
}): Promise<void> {
|
||||
// add copilot action to the database.
|
||||
@@ -228,38 +207,12 @@ export default class CopilotActionService {
|
||||
});
|
||||
}
|
||||
|
||||
const copilotAction: CopilotAction = new CopilotAction();
|
||||
|
||||
copilotAction.serviceCatalogId = data.serviceCatalogId;
|
||||
copilotAction.serviceRepositoryId = data.serviceRepositoryId;
|
||||
copilotAction.filePath = data.filePath;
|
||||
copilotAction.commitHash = data.commitHash;
|
||||
copilotAction.copilotActionType = data.copilotActionType;
|
||||
copilotAction.copilotActionStatus = data.copilotActionStatus;
|
||||
|
||||
if (copilotPullRequest) {
|
||||
copilotAction.copilotPullRequestId = copilotPullRequest.id!;
|
||||
}
|
||||
|
||||
// send this to the API.
|
||||
const url: URL = URL.fromString(
|
||||
GetOneUptimeURL().toString() + "/api",
|
||||
).addRoute(
|
||||
`${new CopilotAction()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/add-copilot-action/${GetRepositorySecretKey()}`,
|
||||
);
|
||||
|
||||
const codeRepositoryResult: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(url, {
|
||||
copilotAction: CopilotAction.toJSON(copilotAction, CopilotAction),
|
||||
copilotPullRequest: copilotPullRequest
|
||||
? CopilotPullRequest.toJSON(copilotPullRequest, CopilotPullRequest)
|
||||
: null,
|
||||
});
|
||||
|
||||
if (codeRepositoryResult instanceof HTTPErrorResponse) {
|
||||
throw codeRepositoryResult;
|
||||
}
|
||||
await CopilotActionUtil.updateCopilotAction({
|
||||
actionStatus: data.copilotActionStatus,
|
||||
pullRequestId: copilotPullRequest ? copilotPullRequest.id! : undefined,
|
||||
commitHash: data.commitHash,
|
||||
statusMessage: data.statusMessage,
|
||||
logs: data.logs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
CopilotProcess,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
|
||||
export default class RefactorCode extends CopilotActionBase {
|
||||
public constructor() {
|
||||
super();
|
||||
this.copilotActionType = CopilotActionType.REFACTOR_CODE;
|
||||
this.acceptFileExtentions = CodeRepositoryUtil.getCodeFileExtentions();
|
||||
}
|
||||
|
||||
public override async getPrompt(
|
||||
_data: CopilotProcess,
|
||||
): Promise<CopilotActionPrompt> {
|
||||
const prompt: string = `Please refactor this code into smaller functions/methods if its not refactored properly.
|
||||
|
||||
If you think the code is refactored already, please reply with the following text:
|
||||
--all-good--
|
||||
|
||||
Here is the code. This is in {{fileLanguage}}:
|
||||
|
||||
{{code}}
|
||||
`;
|
||||
|
||||
const systemPrompt: string = `You are an expert programmer. Here are your instructions:
|
||||
- You will follow the instructions given by the user strictly.
|
||||
- You will not deviate from the instructions given by the user.
|
||||
- You will not change the code unnecessarily. For example you will not change the logic, quotes around strings, or functionality.`;
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
content: systemPrompt,
|
||||
role: PromptRole.System,
|
||||
},
|
||||
{
|
||||
content: prompt,
|
||||
role: PromptRole.User,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
28
Copilot/Service/CopilotActions/Types.ts
Normal file
28
Copilot/Service/CopilotActions/Types.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import CodeRepositoryFile from "Common/Server/Utils/CodeRepository/CodeRepositoryFile";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { Prompt } from "../LLM/Prompt";
|
||||
import CopilotActionProp from "Common/Types/Copilot/CopilotActionProps/Index";
|
||||
|
||||
export interface CopilotActionRunResult {
|
||||
files: Dictionary<CodeRepositoryFile>;
|
||||
statusMessage: string;
|
||||
logs: Array<string>;
|
||||
}
|
||||
|
||||
export interface CopilotActionPrompt {
|
||||
messages: Array<Prompt>;
|
||||
timeoutInMinutes?: number | undefined;
|
||||
}
|
||||
|
||||
export interface CopilotActionVars {
|
||||
currentFilePath: string;
|
||||
files: Dictionary<CodeRepositoryFile>;
|
||||
}
|
||||
|
||||
export interface CopilotProcessStart {
|
||||
input: CopilotActionProp;
|
||||
}
|
||||
|
||||
export interface CopilotProcess extends CopilotProcessStart {
|
||||
result: CopilotActionRunResult;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionBase, {
|
||||
CopilotActionPrompt,
|
||||
PromptRole,
|
||||
} from "./CopilotActionsBase";
|
||||
import CodeRepositoryUtil from "../../Utils/CodeRepository";
|
||||
|
||||
export default class WriteUnitTests extends CopilotActionBase {
|
||||
public constructor() {
|
||||
super();
|
||||
this.copilotActionType = CopilotActionType.WRITE_UNIT_TESTS;
|
||||
this.acceptFileExtentions = CodeRepositoryUtil.getCodeFileExtentions();
|
||||
}
|
||||
|
||||
public override async getPrompt(): Promise<CopilotActionPrompt> {
|
||||
const prompt: string = `Write unit tests for this file.
|
||||
|
||||
Here is the code. This is in {{fileLanguage}}:
|
||||
|
||||
{{code}}
|
||||
`;
|
||||
|
||||
const systemPrompt: string = `You are an expert programmer. Here are your instructions:
|
||||
- You will follow the instructions given by the user strictly.
|
||||
- You will not deviate from the instructions given by the user.
|
||||
- You will not change the code unnecessarily. For example you will not change the logic, quotes around strings, or functionality.`;
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
content: systemPrompt,
|
||||
role: PromptRole.System,
|
||||
},
|
||||
{
|
||||
content: prompt,
|
||||
role: PromptRole.User,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,15 @@ import { GetLlmType } from "../../Config";
|
||||
import LlmType from "../../Types/LlmType";
|
||||
import LlmBase, { CopilotPromptResult } from "./LLMBase";
|
||||
import LLMServer from "./LLMServer";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/CopilotActionsBase";
|
||||
|
||||
import OpenAI from "./OpenAI";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/Types";
|
||||
|
||||
export default class LLM extends LlmBase {
|
||||
public static override async getResponse(
|
||||
data: CopilotActionPrompt,
|
||||
): Promise<CopilotPromptResult> {
|
||||
if (GetLlmType() === LlmType.LLM) {
|
||||
if (GetLlmType() === LlmType.ONEUPTIME_LLM) {
|
||||
return await LLMServer.getResponse(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import NotImplementedException from "Common/Types/Exception/NotImplementedException";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/CopilotActionsBase";
|
||||
import { JSONValue } from "Common/Types/JSON";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/Types";
|
||||
|
||||
export interface CopilotPromptResult {
|
||||
output: JSONValue;
|
||||
|
||||
@@ -8,14 +8,12 @@ import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import BadRequestException from "Common/Types/Exception/BadRequestException";
|
||||
import Sleep from "Common/Types/Sleep";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import {
|
||||
CopilotActionPrompt,
|
||||
Prompt,
|
||||
} from "../CopilotActions/CopilotActionsBase";
|
||||
import ErrorGettingResponseFromLLM from "../../Exceptions/ErrorGettingResponseFromLLM";
|
||||
import BadOperationException from "Common/Types/Exception/BadOperationException";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import LLMTimeoutException from "../../Exceptions/LLMTimeoutException";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/Types";
|
||||
import { Prompt } from "./Prompt";
|
||||
|
||||
enum LlamaPromptStatus {
|
||||
Processed = "processed",
|
||||
|
||||
@@ -2,7 +2,7 @@ import OpenAI from "openai";
|
||||
import { GetOpenAIAPIKey, GetOpenAIModel } from "../../Config";
|
||||
import LlmBase, { CopilotPromptResult } from "./LLMBase";
|
||||
import BadRequestException from "Common/Types/Exception/BadRequestException";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/CopilotActionsBase";
|
||||
import { CopilotActionPrompt } from "../CopilotActions/Types";
|
||||
|
||||
export default class Llama extends LlmBase {
|
||||
public static openai: OpenAI | null = null;
|
||||
|
||||
10
Copilot/Service/LLM/Prompt.ts
Normal file
10
Copilot/Service/LLM/Prompt.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum PromptRole {
|
||||
System = "system",
|
||||
User = "user",
|
||||
Assistant = "assistant",
|
||||
}
|
||||
|
||||
export interface Prompt {
|
||||
content: string;
|
||||
role: PromptRole;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
|
||||
const CopiotActionTypeOrder: Array<CopilotActionType> = [
|
||||
CopilotActionType.IMPROVE_COMMENTS,
|
||||
// CopilotActionType.REFACTOR_CODE,
|
||||
// CopilotActionType.FIX_GRAMMAR_AND_SPELLING,
|
||||
// CopilotActionType.IMPROVE_VARIABLE_NAMES,
|
||||
// CopilotActionType.IMPROVE_README,
|
||||
// CopilotActionType.WRITE_UNIT_TESTS,
|
||||
];
|
||||
|
||||
export default CopiotActionTypeOrder;
|
||||
@@ -49,6 +49,12 @@ export default class CodeRepositoryUtil {
|
||||
public static gitHubUtil: GitHubUtil | null = null;
|
||||
public static folderNameOfClonedRepository: string | null = null;
|
||||
|
||||
public static async getCurrentCommitHash(): Promise<string> {
|
||||
return await CodeRepositoryServerUtil.getCurrentCommitHash({
|
||||
repoPath: this.getLocalRepositoryPath(),
|
||||
});
|
||||
}
|
||||
|
||||
public static isRepoCloned(): boolean {
|
||||
return Boolean(this.folderNameOfClonedRepository);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import CopilotAction from "Common/Models/DatabaseModels/CopilotAction";
|
||||
import { GetOneUptimeURL, GetRepositorySecretKey } from "../Config";
|
||||
import {
|
||||
GetOneUptimeURL,
|
||||
GetRepositorySecretKey,
|
||||
MIN_ITEMS_IN_QUEUE_PER_SERVICE_CATALOG,
|
||||
} from "../Config";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
@@ -9,11 +13,16 @@ import API from "Common/Utils/API";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority";
|
||||
import CopilotActionTypeUtil from "./CopilotActionTypes";
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import { ActionDictionary } from "../Service/CopilotActions/Index";
|
||||
import CopilotActionBase from "../Service/CopilotActions/CopilotActionsBase";
|
||||
import CopilotActionStatus from "Common/Types/Copilot/CopilotActionStatus";
|
||||
|
||||
export default class CopilotActionUtil {
|
||||
|
||||
public static async getActionTypesBasedOnPriority(): Promise<Array<CopilotActionTypePriority>> {
|
||||
|
||||
public static async getActionTypesBasedOnPriority(): Promise<
|
||||
Array<CopilotActionTypePriority>
|
||||
> {
|
||||
const repositorySecretKey: string | null = GetRepositorySecretKey();
|
||||
|
||||
const url: URL = URL.fromString(
|
||||
@@ -29,12 +38,15 @@ export default class CopilotActionUtil {
|
||||
throw actionTypesResult;
|
||||
}
|
||||
|
||||
const actionTypes: Array<CopilotActionTypePriority> = CopilotActionTypePriority.fromJSONArray(
|
||||
actionTypesResult.data["actionTypes"] as JSONArray,
|
||||
CopilotActionTypePriority,
|
||||
) || [];
|
||||
const actionTypes: Array<CopilotActionTypePriority> =
|
||||
CopilotActionTypePriority.fromJSONArray(
|
||||
actionTypesResult.data["actionTypes"] as JSONArray,
|
||||
CopilotActionTypePriority,
|
||||
) || [];
|
||||
|
||||
logger.debug(`Copilot action types based on priority: ${JSON.stringify(actionTypes, null, 2)}`);
|
||||
logger.debug(
|
||||
`Copilot action types based on priority: ${JSON.stringify(actionTypes, null, 2)}`,
|
||||
);
|
||||
|
||||
return actionTypes;
|
||||
}
|
||||
@@ -52,26 +64,138 @@ export default class CopilotActionUtil {
|
||||
throw new BadDataException("Repository Secret Key is required");
|
||||
}
|
||||
|
||||
// check actions in queue
|
||||
// check actions in queue
|
||||
|
||||
const actionsInQueue: Array<CopilotAction> = await CopilotActionUtil.getInQueueActions({
|
||||
serviceCatalogId: data.serviceCatalogId,
|
||||
});
|
||||
const actionsInQueue: Array<CopilotAction> =
|
||||
await CopilotActionUtil.getInQueueActions({
|
||||
serviceCatalogId: data.serviceCatalogId,
|
||||
});
|
||||
|
||||
if(actionsInQueue.length > 0) {
|
||||
logger.debug(`Actions in queue: ${JSON.stringify(actionsInQueue, null, 2)}`);
|
||||
if (actionsInQueue.length >= MIN_ITEMS_IN_QUEUE_PER_SERVICE_CATALOG) {
|
||||
logger.debug(
|
||||
`Actions in queue: ${JSON.stringify(actionsInQueue, null, 2)}`,
|
||||
);
|
||||
return actionsInQueue;
|
||||
}
|
||||
|
||||
const getEnabledActionsBasedOnPriority
|
||||
const actionTypePriorities: Array<CopilotActionTypePriority> =
|
||||
await CopilotActionTypeUtil.getEnabledActionTypesBasedOnPriority();
|
||||
|
||||
for (const actionTypePriority of actionTypePriorities) {
|
||||
// get items in queue based on priority
|
||||
const itemsInQueue: number =
|
||||
CopilotActionTypeUtil.getItemsInQueueByPriority(
|
||||
actionTypePriority.priority || 1,
|
||||
);
|
||||
|
||||
// get actions based on priority
|
||||
const actions: Array<CopilotAction> = await CopilotActionUtil.getActions({
|
||||
serviceCatalogId: data.serviceCatalogId,
|
||||
actionType: actionTypePriority.actionType!,
|
||||
itemsInQueue,
|
||||
});
|
||||
|
||||
// add these actions to the queue
|
||||
actionsInQueue.push(...actions);
|
||||
}
|
||||
|
||||
return actionsInQueue;
|
||||
}
|
||||
|
||||
public static async getActions(data: {
|
||||
serviceCatalogId: ObjectID;
|
||||
actionType: CopilotActionType;
|
||||
itemsInQueue: number;
|
||||
}): Promise<Array<CopilotAction>> {
|
||||
if (!data.serviceCatalogId) {
|
||||
throw new BadDataException("Service Catalog ID is required");
|
||||
}
|
||||
|
||||
if (!data.actionType) {
|
||||
throw new BadDataException("Action Type is required");
|
||||
}
|
||||
|
||||
const CopilotActionBaseType: typeof CopilotActionBase =
|
||||
ActionDictionary[data.actionType]!;
|
||||
const ActionBase: CopilotActionBase = new CopilotActionBaseType();
|
||||
|
||||
const actions: Array<CopilotAction> = await ActionBase.getActionsToQueue({
|
||||
serviceCatalogId: data.serviceCatalogId,
|
||||
maxActionsToQueue: data.itemsInQueue,
|
||||
});
|
||||
|
||||
const savedActions: Array<CopilotAction> = [];
|
||||
|
||||
// now these actions need to be saved.
|
||||
for (const action of actions) {
|
||||
try {
|
||||
const savedAction: CopilotAction =
|
||||
await CopilotActionUtil.addCopilotAction({
|
||||
copilotAction: action,
|
||||
});
|
||||
|
||||
savedActions.push(savedAction);
|
||||
} catch (error) {
|
||||
logger.error(`Error while adding copilot action: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return savedActions;
|
||||
}
|
||||
|
||||
public static async updateCopilotAction(data: {
|
||||
actionStatus: CopilotActionStatus;
|
||||
pullRequestId?: ObjectID | undefined;
|
||||
commitHash?: string | undefined;
|
||||
statusMessage?: string | undefined;
|
||||
logs?: Array<string> | undefined;
|
||||
}): Promise<void> {
|
||||
// send this to the API.
|
||||
const url: URL = URL.fromString(
|
||||
GetOneUptimeURL().toString() + "/api",
|
||||
).addRoute(
|
||||
`${new CopilotAction()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/update-copilot-action/${GetRepositorySecretKey()}`,
|
||||
);
|
||||
|
||||
const codeRepositoryResult: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(url, {
|
||||
...data,
|
||||
});
|
||||
|
||||
if (codeRepositoryResult instanceof HTTPErrorResponse) {
|
||||
throw codeRepositoryResult;
|
||||
}
|
||||
}
|
||||
|
||||
public static async addCopilotAction(data: {
|
||||
copilotAction: CopilotAction;
|
||||
}): Promise<CopilotAction> {
|
||||
// send this to the API.
|
||||
const url: URL = URL.fromString(
|
||||
GetOneUptimeURL().toString() + "/api",
|
||||
).addRoute(
|
||||
`${new CopilotAction()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/add-copilot-action/${GetRepositorySecretKey()}`,
|
||||
);
|
||||
|
||||
const codeRepositoryResult: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(url, {
|
||||
copilotAction: CopilotAction.toJSON(data.copilotAction, CopilotAction),
|
||||
});
|
||||
|
||||
if (codeRepositoryResult instanceof HTTPErrorResponse) {
|
||||
throw codeRepositoryResult;
|
||||
}
|
||||
|
||||
return CopilotAction.fromJSONObject(data.copilotAction, CopilotAction);
|
||||
}
|
||||
|
||||
public static async getInQueueActions(data: {
|
||||
serviceCatalogId: ObjectID;
|
||||
}): Promise<Array<CopilotAction>> {
|
||||
}): Promise<Array<CopilotAction>> {
|
||||
if (!data.serviceCatalogId) {
|
||||
throw new BadDataException("Service Catalog ID is required");
|
||||
}
|
||||
|
||||
@@ -1,52 +1,34 @@
|
||||
import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority";
|
||||
import CopilotActionType from "Common/Types/Copilot/CopilotActionType";
|
||||
import CopilotActionUtil from "./CopilotAction";
|
||||
import { ActionDictionary } from "../Service/CopilotActions/Index";
|
||||
|
||||
export default class CopilotActionTypeUtil {
|
||||
private static isActionEnabled(actionType: CopilotActionType): boolean {
|
||||
if (actionType === CopilotActionType.ADD_COMMENTS) {
|
||||
return true;
|
||||
}
|
||||
private static isActionEnabled(actionType: CopilotActionType): boolean {
|
||||
return Boolean(ActionDictionary[actionType]); // if action is not in dictionary then it is not enabled
|
||||
}
|
||||
|
||||
if (actionType === CopilotActionType.IMPROVE_COMMENTS) {
|
||||
return true;
|
||||
}
|
||||
public static async getEnabledActionTypesBasedOnPriority(): Promise<
|
||||
Array<CopilotActionTypePriority>
|
||||
> {
|
||||
// if there are no actions then, get actions based on priority
|
||||
const actionTypes: Array<CopilotActionTypePriority> =
|
||||
await CopilotActionUtil.getActionTypesBasedOnPriority();
|
||||
|
||||
if (actionType === CopilotActionType.ADD_LOGS) {
|
||||
return true;
|
||||
}
|
||||
const enabledActions: Array<CopilotActionTypePriority> = [];
|
||||
|
||||
if (actionType === CopilotActionType.IMPROVE_LOGS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// readme
|
||||
if (actionType === CopilotActionType.ADD_README) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actionType === CopilotActionType.IMPROVE_README) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
for (const actionType of actionTypes) {
|
||||
if (this.isActionEnabled(actionType.actionType!)) {
|
||||
enabledActions.push(actionType);
|
||||
}
|
||||
}
|
||||
|
||||
public static async getEnabledActionTypesBasedOnPriority(
|
||||
return enabledActions;
|
||||
}
|
||||
|
||||
): Promise<Array<CopilotActionType>> {
|
||||
|
||||
// if there are no actions then, get actions based on priority
|
||||
const actionTypes: Array<CopilotActionTypePriority> = await CopilotActionUtil.getActionTypesBasedOnPriority();
|
||||
|
||||
const enabledActions: Array<CopilotActionType> = [];
|
||||
|
||||
for (const actionType of actionTypes) {
|
||||
if (this.isActionEnabled(actionType.actionType!)) {
|
||||
enabledActions.push(actionType.actionType!);
|
||||
}
|
||||
}
|
||||
|
||||
return enabledActions;
|
||||
}
|
||||
}
|
||||
public static getItemsInQueueByPriority(priority: number): number {
|
||||
// so if the priority is 1, then there will be 5 items in queue. If the priority is 5, then there will be 1 item in queue.
|
||||
const itemsInQueue: number = 6;
|
||||
return itemsInQueue - priority;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user