Skip to content

Commit

Permalink
Change filter option to optionalTaskProps (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroki0525 authored Nov 4, 2023
1 parent fda760c commit 9863008
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 53 deletions.
6 changes: 6 additions & 0 deletions .changeset/tame-dingos-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@dandori/core": patch
"@dandori/cli": patch
---

Change filter option to optionalTaskProps
41 changes: 40 additions & 1 deletion packages/cli/src/core/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
it,
} from "vitest";
import DandoriCoreCli from "../index";
import generateDandoriTasks, { DandoriTask } from "@dandori/core";
import generateDandoriTasks, {
ChatGPTFunctionCallModel,
DandoriTask,
} from "@dandori/core";

const tasks: DandoriTask[] = [
{
Expand Down Expand Up @@ -64,6 +67,23 @@ describe("DandoriCoreCli", () => {
});
});

describe("with -m option", () => {
const model: ChatGPTFunctionCallModel = "gpt-4-0613";

beforeEach(async () => {
loadProcessArgv(["-m", model]);
await new DandoriCoreCli().run();
});

it("call generateDandoriTasks with envFilePath", () => {
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
envFilePath: undefined,
chatGPTModel: model,
optionalTaskProps: undefined,
});
});
});

describe("with -e option", () => {
const envFilePath = "/test/.env";

Expand All @@ -75,6 +95,25 @@ describe("DandoriCoreCli", () => {
it("call generateDandoriTasks with envFilePath", () => {
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
envFilePath,
chatGPTModel: undefined,
optionalTaskProps: undefined,
});
});
});

describe("with -o option", () => {
const optionalTaskProps = "deadline,description";

beforeEach(async () => {
loadProcessArgv(["-o", optionalTaskProps]);
await new DandoriCoreCli().run();
});

it("call generateDandoriTasks with envFilePath", () => {
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
envFilePath: undefined,
chatGPTModel: undefined,
optionalTaskProps: optionalTaskProps.split(","),
});
});
});
Expand Down
28 changes: 24 additions & 4 deletions packages/cli/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import packageJson from "../../package.json";
import { Command } from "commander";
import chalk from "chalk";
import generateDandoriTasks, { DandoriTask } from "@dandori/core";
import generateDandoriTasks, {
ChatGPTFunctionCallModel,
DandoriTask,
OptionalTaskPropsOption,
} from "@dandori/core";
import { readFile } from "fs/promises";
import { generateDandoriFilePath } from "@dandori/libs";

Expand All @@ -24,19 +28,35 @@ export default class DandoriCoreCli {
.version(packageJson.version)
.argument("<input-file>")
.usage(`${chalk.green("<input-file>")} [options]`)
.option("-e, --env-file <env-file>")
.option("-e, --env-file <env-file>", "env file path")
.option(
"-m, --model <model>",
"Chat GPT model which supports function_calling",
)
.option(
"-o, --optional-task-props <optional-task-props>",
"optional output task props which delimiter is a comma",
)
.action((iFile) => {
this.inputFile = iFile;
});
}

protected async generateDandoriTasks(): Promise<DandoriTask[]> {
this.program.parse(process.argv);
const opts = this.program.opts();
const source = await readFile(generateDandoriFilePath(this.inputFile));
const { envFile } = opts;
const { envFile, optionalTaskProps, model } = this.program.opts<{
envFile?: string;
optionalTaskProps?: string;
model?: ChatGPTFunctionCallModel;
}>();
// TODO: Error Handling of invalid options
return generateDandoriTasks(source.toString(), {
envFilePath: envFile,
chatGPTModel: model,
optionalTaskProps: optionalTaskProps?.split(
",",
) as OptionalTaskPropsOption,
});
}
}
84 changes: 41 additions & 43 deletions packages/core/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import generateDandoriTasks, {
DandoriTaskOptionalProperty,
DandoriTaskProperty,
DandoriTaskRequiredProperty,
OptionalAllDandoriTaskPropertiesName,
} from "../index";
import {
describe,
Expand Down Expand Up @@ -189,7 +190,7 @@ describe("generateDandoriTasks", () => {
},
},
} as const;
const requiredProperties: readonly DandoriTaskRequiredProperty[] = [
const requiredProperties: DandoriTaskRequiredProperty[] = [
"id",
"name",
"fromTaskIdList",
Expand All @@ -198,7 +199,7 @@ describe("generateDandoriTasks", () => {
const createOpenAiChatGptArguments = ({
source,
model = "gpt-3.5-turbo-0613",
filter = Object.keys(functionCallTaskProperties) as DandoriTaskProperty[],
filter = requiredProperties,
}: {
source: string;
model?: ChatGPTFunctionCallModel;
Expand Down Expand Up @@ -234,6 +235,8 @@ describe("generateDandoriTasks", () => {
],
});

let result: Awaited<ReturnType<typeof generateDandoriTasks>>;

beforeEach(() => {
process.env[openApiKeyPropName] = apiKey;
});
Expand All @@ -243,7 +246,6 @@ describe("generateDandoriTasks", () => {
});

describe("with options which include model argument", () => {
let result: Awaited<ReturnType<typeof generateDandoriTasks>>;
const source = "with model argument";
const model = "gpt-4-0613";

Expand All @@ -258,59 +260,55 @@ describe("generateDandoriTasks", () => {
createOpenAiChatGptArguments({ source, model }),
);
});

it("called logger.debug with valid arguments", () => {
expect(logger.debug).toBeCalledWith(openAiResArguments.tasks);
});

it("return tasks", () => {
expect(result).toStrictEqual(openAiResArguments.tasks);
});

it("called log with valid statement", () => {
expect(runPromisesSequentiallyMock.mock.calls[0][1]).toContain(
logPrefix,
);
});
});

describe("with options which include filter argument", () => {
let result: Awaited<ReturnType<typeof generateDandoriTasks>>;
const source = "with filter argument";
const filter: DandoriTaskOptionalProperty[] = ["deadline"];
describe("with options which include optionalProps argument", () => {
describe("without all argument", () => {
const source = "without all argument";
const optionalTaskProps: DandoriTaskOptionalProperty[] = ["deadline"];

beforeEach(async () => {
result = await generateDandoriTasks(source, {
filter,
beforeEach(async () => {
result = await generateDandoriTasks(source, {
optionalTaskProps,
});
});
});

it("called chat.completions.create with valid arguments", () => {
expect(openAI.chat.completions.create).toBeCalledWith(
createOpenAiChatGptArguments({
source,
filter: [...filter, ...requiredProperties],
}),
);
it("called chat.completions.create with valid arguments", () => {
expect(openAI.chat.completions.create).toBeCalledWith(
createOpenAiChatGptArguments({
source,
filter: [...optionalTaskProps, ...requiredProperties],
}),
);
});
});

it("called logger.debug with valid arguments", () => {
expect(logger.debug).toBeCalledWith(openAiResArguments.tasks);
});
describe("with all argument", () => {
const source = "with all argument";
const optionalTaskProps: OptionalAllDandoriTaskPropertiesName[] = [
"all",
];

it("return tasks", () => {
expect(result).toStrictEqual(openAiResArguments.tasks);
});
beforeEach(async () => {
result = await generateDandoriTasks(source, {
optionalTaskProps,
});
});

it("called log with valid statement", () => {
expect(runPromisesSequentiallyMock.mock.calls[0][1]).toContain(
logPrefix,
);
it("called chat.completions.create with valid arguments", () => {
expect(openAI.chat.completions.create).toBeCalledWith(
createOpenAiChatGptArguments({
source,
filter: Object.keys(
functionCallTaskProperties,
) as DandoriTaskProperty[],
}),
);
});
});
});

describe("without options", () => {
let result: Awaited<ReturnType<typeof generateDandoriTasks>>;
const source = "without model argument";

beforeEach(async () => {
Expand Down
27 changes: 22 additions & 5 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,22 @@ export type DandoriTaskOptionalProperty = Exclude<
DandoriTaskRequiredProperty
>;

export type OptionalAllDandoriTaskPropertiesName = "all";
const optionalAllDandoriTaskPropertiesName: OptionalAllDandoriTaskPropertiesName =
"all";
export type OptionalTaskPropsOption = (
| DandoriTaskOptionalProperty
| OptionalAllDandoriTaskPropertiesName
)[];
const notIncludeAdditionalAllPropsName = (
props: OptionalTaskPropsOption,
): props is DandoriTaskOptionalProperty[] =>
!props.includes(optionalAllDandoriTaskPropertiesName);

export type GenerateDandoriTasksOptions = {
chatGPTModel?: ChatGPTFunctionCallModel;
envFilePath?: string;
filter?: DandoriTaskOptionalProperty[];
optionalTaskProps?: OptionalTaskPropsOption;
};

type FunctionCallValue = {
Expand Down Expand Up @@ -128,11 +140,16 @@ export default async function generateDandoriTasks(
const openai = new OpenAI();
const model: ChatGPTFunctionCallModel =
options?.chatGPTModel ?? defaultChatGPTFunctionCallModel;
const filterProperties = options?.filter ?? optionalProperties;
const optionalTaskProps = options?.optionalTaskProps ?? [];
const additionalProperties = notIncludeAdditionalAllPropsName(
optionalTaskProps,
)
? optionalTaskProps
: optionalProperties;
const filteredOptionalFunctionCallTaskProperties = Object.fromEntries(
filterProperties.map((filterProperty) => [
filterProperty,
optionalFunctionCallTaskProperties[filterProperty],
additionalProperties.map((additionalProperty) => [
additionalProperty,
optionalFunctionCallTaskProperties[additionalProperty],
]),
);
const [completion] = await runPromisesSequentially(
Expand Down

0 comments on commit 9863008

Please sign in to comment.