From 22cd15f4980332fa2b4bffbd681d2ffcf56ae6d9 Mon Sep 17 00:00:00 2001 From: rpidanny Date: Wed, 3 Jul 2024 16:38:54 +0200 Subject: [PATCH] refactor: args and flags into reusable modules --- cspell.json | 2 + src/base.command.ts | 5 +- src/commands/chat/index.ts | 53 ++------- src/commands/download/papers.ts | 29 ++--- src/commands/search/accession.ts | 108 +++++------------- src/commands/search/papers.ts | 100 +++++----------- src/containers/chat.container.ts | 4 +- src/containers/search.container.ts | 10 +- src/inputs/args/keywords.arg.ts | 7 ++ .../flags/accession-number-regex.flag.ts | 11 ++ src/inputs/flags/char.ts | 11 ++ src/inputs/flags/concurrency.flag.ts | 10 ++ src/inputs/flags/count.flag.ts | 11 ++ src/inputs/flags/filter.flag.ts | 10 ++ src/inputs/flags/headless.flag.ts | 9 ++ src/inputs/flags/legacy.flag.ts | 7 ++ src/inputs/flags/llm-provider.flag.ts | 19 +++ src/inputs/flags/output.flag.ts | 11 ++ src/inputs/flags/skip-captcha.flag.ts | 9 ++ src/inputs/flags/summary.flag.ts | 11 ++ 20 files changed, 216 insertions(+), 221 deletions(-) create mode 100644 src/inputs/args/keywords.arg.ts create mode 100644 src/inputs/flags/accession-number-regex.flag.ts create mode 100644 src/inputs/flags/char.ts create mode 100644 src/inputs/flags/concurrency.flag.ts create mode 100644 src/inputs/flags/count.flag.ts create mode 100644 src/inputs/flags/filter.flag.ts create mode 100644 src/inputs/flags/headless.flag.ts create mode 100644 src/inputs/flags/legacy.flag.ts create mode 100644 src/inputs/flags/llm-provider.flag.ts create mode 100644 src/inputs/flags/output.flag.ts create mode 100644 src/inputs/flags/skip-captcha.flag.ts create mode 100644 src/inputs/flags/summary.flag.ts diff --git a/cspell.json b/cspell.json index 004b305..ff18829 100644 --- a/cspell.json +++ b/cspell.json @@ -6,6 +6,7 @@ // words - list of words to be always considered correct "words": [ "camelcase", + "Caproiciproducens", "Colidextribacter", "commitlint", "crispr", @@ -25,6 +26,7 @@ "posttest", "PRJNA", "rpidanny", + "tcell", "typedi", "vectorstore", "vectorstores" diff --git a/src/base.command.ts b/src/base.command.ts index 6ba3d1f..64cb898 100644 --- a/src/base.command.ts +++ b/src/base.command.ts @@ -11,6 +11,7 @@ import prettyMilliseconds from 'pretty-ms' import { CONFIG_FILE_NAME } from './config/constants.js' import { ConfigSchema, TConfig } from './config/schema.js' +import { FlagChar } from './inputs/flags/char.js' import { Metric } from './utils/analytics/metric.js' export type Flags = Interfaces.InferredFlags< @@ -25,10 +26,12 @@ export abstract class BaseCommand extends Command { // define flags that can be inherited by any command that extends BaseCommand static baseFlags = { 'log-level': Flags.option({ + char: FlagChar.LogLevel, default: LogLevel.INFO, helpGroup: 'GLOBAL', + helpValue: Object.values(LogLevel).join('|'), options: Object.values(LogLevel), - summary: 'Specify level for logging.', + summary: 'Specify logging level.', })(), } diff --git a/src/commands/chat/index.ts b/src/commands/chat/index.ts index e18f6ac..0b95d9b 100644 --- a/src/commands/chat/index.ts +++ b/src/commands/chat/index.ts @@ -3,76 +3,45 @@ import { Odysseus } from '@rpidanny/odysseus/dist/odysseus.js' import { Container } from 'typedi' import { BaseCommand } from '../../base.command.js' -import { LLMProvider } from '../../config/schema.js' import { initChatContainer } from '../../containers/chat.container.js' +import concurrencyFlag from '../../inputs/flags/concurrency.flag.js' +import legacyFlag from '../../inputs/flags/legacy.flag.js' +import llmProviderFlag from '../../inputs/flags/llm-provider.flag.js' +import skipCaptchaFlag from '../../inputs/flags/skip-captcha.flag.js' import { ChatService } from '../../services/chat/chat.service.js' export default class Chat extends BaseCommand { service!: ChatService odysseus!: Odysseus - static summary = - 'Chat with Darwin. Can be used to instruct Darwin to do things in natural language.' + static summary = 'Chat with Darwin using natural language.' static examples = ['<%= config.bin %> <%= command.id %>'] static flags = { - concurrency: oclif.Flags.integer({ - char: 'p', - summary: 'The number papers to process in parallel.', - required: false, - default: 10, - }), + concurrency: concurrencyFlag, logs: oclif.Flags.boolean({ char: 'l', summary: 'Include application logs along with the chat conversations.', required: false, default: false, }), - 'skip-captcha': oclif.Flags.boolean({ - char: 's', - summary: 'Skip captcha on paper URLs. Note: Google Scholar captcha still needs to be solved.', - required: false, - default: false, - }), - 'legacy-processing': oclif.Flags.boolean({ - summary: - 'Enable legacy processing of papers that only extracts text from the main URL. The new method attempts to extract text from the source URLs (pdf or html) and falls back to the main URL.', - required: false, - default: false, - }), - 'llm-provider': oclif.Flags.custom({ - summary: 'The LLM provider to use for generating summaries.', - options: Object.values(LLMProvider) as string[], - default: LLMProvider.Ollama, - parse: async (input: string): Promise => { - if (Object.values(LLMProvider).includes(input as LLMProvider)) { - return input as LLMProvider - } else { - throw new Error( - `Invalid LLM provider: ${input}. Must be one of ${Object.values(LLMProvider).join(', ')}`, - ) - } - }, - })(), + 'skip-captcha': skipCaptchaFlag, + legacy: legacyFlag, + llm: llmProviderFlag, } async init() { await super.init() - const { - concurrency, - 'llm-provider': llmProvider, - 'skip-captcha': skipCaptcha, - 'legacy-processing': legacyProcessing, - } = this.flags + const { concurrency, llm: llmProvider, 'skip-captcha': skipCaptcha, legacy } = this.flags initChatContainer( { concurrency, llmProvider, skipCaptcha, - legacyProcessing, + legacy, }, this.localConfig, this.logger, diff --git a/src/commands/download/papers.ts b/src/commands/download/papers.ts index 6ce79bd..905df75 100644 --- a/src/commands/download/papers.ts +++ b/src/commands/download/papers.ts @@ -4,6 +4,10 @@ import { Container } from 'typedi' import { BaseCommand } from '../../base.command.js' import { initDownloadContainer } from '../../containers/download.container.js' +import keywordsArg from '../../inputs/args/keywords.arg.js' +import { FlagChar } from '../../inputs/flags/char.js' +import countFlag from '../../inputs/flags/count.flag.js' +import headlessFlag from '../../inputs/flags/headless.flag.js' import { PaperDownloadService } from '../../services/download/paper-download.service.js' export default class DownloadPapers extends BaseCommand { @@ -14,36 +18,21 @@ export default class DownloadPapers extends BaseCommand { static examples = [ '<%= config.bin %> <%= command.id %> --help', - '<%= config.bin %> <%= command.id %> "crispr cas9" -o papers/ -c 100 --log-level debug', + '<%= config.bin %> <%= command.id %> "crispr cas9" --output papers/ --count 100 --log-level debug', ] static args = { - keywords: oclif.Args.string({ - name: 'keywords', - required: true, - description: 'The keywords to search for', - }), + keywords: keywordsArg, } static flags = { - count: oclif.Flags.integer({ - char: 'c', - summary: - 'The minimum number of papers to search for. (When running concurrently, the actual number of papers may be a bit higher)', - required: false, - default: 10, - }), + count: countFlag, output: oclif.Flags.string({ - char: 'o', + char: FlagChar.Output, summary: 'The path to save the downloaded papers.', required: true, }), - headless: oclif.Flags.boolean({ - char: 'h', - summary: 'Run the browser in headless mode (no UI).', - required: false, - default: false, - }), + headless: headlessFlag, } async init(): Promise { diff --git a/src/commands/search/accession.ts b/src/commands/search/accession.ts index 0c1786f..8d137e0 100644 --- a/src/commands/search/accession.ts +++ b/src/commands/search/accession.ts @@ -3,9 +3,17 @@ import { Odysseus } from '@rpidanny/odysseus' import { Container } from 'typedi' import { BaseCommand } from '../../base.command.js' -import { LLMProvider } from '../../config/schema.js' import { initSearchContainer } from '../../containers/search.container.js' -import { AccessionPattern } from '../../services/search/constants.js' +import keywordsArg from '../../inputs/args/keywords.arg.js' +import accessionNumberRegexFlag from '../../inputs/flags/accession-number-regex.flag.js' +import concurrencyFlag from '../../inputs/flags/concurrency.flag.js' +import countFlag from '../../inputs/flags/count.flag.js' +import headlessFlag from '../../inputs/flags/headless.flag.js' +import legacyFlag from '../../inputs/flags/legacy.flag.js' +import llmProviderFlag from '../../inputs/flags/llm-provider.flag.js' +import outputFlag from '../../inputs/flags/output.flag.js' +import skipCaptchaFlag from '../../inputs/flags/skip-captcha.flag.js' +import summaryFlag from '../../inputs/flags/summary.flag.js' import { PaperSearchService } from '../../services/search/paper-search.service.js' export default class SearchAccession extends BaseCommand { @@ -22,78 +30,23 @@ export default class SearchAccession extends BaseCommand static examples = [ '<%= config.bin %> <%= command.id %> --help', - '<%= config.bin %> <%= command.id %> "mocrobiome, nRNA" -o output.csv -n 5 -c 1 --log-level DEBUG', + '<%= config.bin %> <%= command.id %> "mocrobiome, nRNA" --output ./ --count 10 --log-level DEBUG', ] static args = { - keywords: oclif.Args.string({ - name: 'keywords', - required: true, - description: 'The keywords to search for', - }), + keywords: keywordsArg, } static flags = { - count: oclif.Flags.integer({ - char: 'c', - summary: - 'The minimum number of papers to search for. (When running concurrently, the actual number of papers may be a bit higher)', - default: 10, - }), - concurrency: oclif.Flags.integer({ - char: 'p', - summary: 'The number papers to process in parallel.', - default: 10, - }), - output: oclif.Flags.string({ - char: 'o', - summary: - 'Specify the output destination for the CSV file. If a folder path is given, the filename is auto-generated; if a file path is given, it is used directly.', - default: '.', - }), - 'accession-number-regex': oclif.Flags.string({ - char: 'a', - summary: - 'Regex to match accession numbers. Defaults to matching BioProject accession numbers.', - default: AccessionPattern.BioProject, - }), - 'skip-captcha': oclif.Flags.boolean({ - char: 's', - summary: 'Skip captcha on paper URLs. Note: Google Scholar captcha still needs to be solved.', - default: false, - }), - 'legacy-processing': oclif.Flags.boolean({ - summary: - 'Enable legacy processing of papers that only extracts text from the main URL. The new method attempts to extract text from the source URLs (pdf or html) and falls back to the main URL.', - default: false, - }), - headless: oclif.Flags.boolean({ - char: 'h', - summary: 'Run the browser in headless mode (no UI).', - default: false, - }), - 'include-summary': oclif.Flags.boolean({ - char: 'S', - summary: - '[LLM Required] Include the paper summary in the output CSV file. When enabled, concurrency is set to 1.', - description: - 'Summaries are generated using LLM so make sure LLMs are configured by running `darwin config set`', - default: false, - }), - 'llm-provider': oclif.Flags.custom({ - summary: 'The LLM provider to use for generating summaries.', - options: Object.values(LLMProvider) as string[], - default: LLMProvider.Ollama, - parse: async (input: string): Promise => { - if (Object.values(LLMProvider).includes(input as LLMProvider)) { - return input as LLMProvider - } else { - throw new Error( - `Invalid LLM provider: ${input}. Must be one of ${Object.values(LLMProvider).join(', ')}`, - ) - } - }, - })(), + count: countFlag, + concurrency: concurrencyFlag, + output: outputFlag, + 'accession-number-regex': accessionNumberRegexFlag, + 'skip-captcha': skipCaptchaFlag, + legacy: legacyFlag, + headless: headlessFlag, + summary: summaryFlag, + llm: llmProviderFlag, } async init(): Promise { @@ -102,20 +55,20 @@ export default class SearchAccession extends BaseCommand const { headless, concurrency, - 'include-summary': summarize, - 'llm-provider': llmProvider, + summary, + llm: llmProvider, 'skip-captcha': skipCaptcha, - 'legacy-processing': legacyProcessing, + legacy, } = this.flags initSearchContainer( { headless, concurrency, - summarize, + summary, llmProvider, skipCaptcha, - legacyProcessing, + legacy, }, this.localConfig, this.logger, @@ -133,12 +86,7 @@ export default class SearchAccession extends BaseCommand } public async run(): Promise { - const { - count, - output, - 'accession-number-regex': filterPattern, - 'include-summary': summarize, - } = this.flags + const { count, output, 'accession-number-regex': filterPattern, summary } = this.flags const { keywords } = this.args this.logger.info(`Searching papers with Accession Numbers (${filterPattern}) for: ${keywords}`) @@ -147,7 +95,7 @@ export default class SearchAccession extends BaseCommand keywords, minItemCount: count, filterPattern, - summarize, + summarize: summary, }) this.logger.info(`Exported papers list to: ${outputPath}`) diff --git a/src/commands/search/papers.ts b/src/commands/search/papers.ts index e2d6ac5..0271f20 100644 --- a/src/commands/search/papers.ts +++ b/src/commands/search/papers.ts @@ -1,10 +1,18 @@ -import * as oclif from '@oclif/core' import { Odysseus } from '@rpidanny/odysseus' import { Container } from 'typedi' import { BaseCommand } from '../../base.command.js' -import { LLMProvider } from '../../config/schema.js' import { initSearchContainer } from '../../containers/search.container.js' +import keywordsArg from '../../inputs/args/keywords.arg.js' +import concurrencyFlag from '../../inputs/flags/concurrency.flag.js' +import countFlag from '../../inputs/flags/count.flag.js' +import filterFlag from '../../inputs/flags/filter.flag.js' +import headlessFlag from '../../inputs/flags/headless.flag.js' +import legacyFlag from '../../inputs/flags/legacy.flag.js' +import llmProviderFlag from '../../inputs/flags/llm-provider.flag.js' +import outputFlag from '../../inputs/flags/output.flag.js' +import skipCaptchaFlag from '../../inputs/flags/skip-captcha.flag.js' +import summaryFlag from '../../inputs/flags/summary.flag.js' import { PaperSearchService } from '../../services/search/paper-search.service.js' export default class SearchPapers extends BaseCommand { @@ -15,74 +23,24 @@ export default class SearchPapers extends BaseCommand { static examples = [ '<%= config.bin %> <%= command.id %> --help', - '<%= config.bin %> <%= command.id %> "crispr cas9" -o crispr_cas9.csv -c 20 --log-level DEBUG', - '<%= config.bin %> <%= command.id %> "crispr cas9" -o crispr_cas9.csv -c 5 -p 1 -f "tcell" --log-level DEBUG', + '<%= config.bin %> <%= command.id %> "crispr cas9" --output crispr_cas9.csv --count 20', + '<%= config.bin %> <%= command.id %> "crispr cas9" --output crispr_cas9.csv --filter "tcell" --log-level DEBUG', ] static args = { - keywords: oclif.Args.string({ - name: 'keywords', - required: true, - description: 'The keywords to search for', - }), + keywords: keywordsArg, } static flags = { - count: oclif.Flags.integer({ - char: 'c', - summary: 'The minimum number of papers to search for.', - default: 10, - }), - concurrency: oclif.Flags.integer({ - char: 'p', - summary: 'The number of papers to process in parallel.', - default: 10, - }), - output: oclif.Flags.string({ - char: 'o', - summary: 'Specify the output destination for the CSV file.', - default: '.', - }), - filter: oclif.Flags.string({ - char: 'f', - summary: 'Case-insensitive regex to filter papers by content.', - }), - 'skip-captcha': oclif.Flags.boolean({ - char: 's', - summary: 'Skip captcha on paper URLs.', - default: false, - }), - 'legacy-processing': oclif.Flags.boolean({ - summary: - 'Enable legacy processing of papers that only extracts text from the main URL. The new method attempts to extract text from the source URLs (pdf or html) and falls back to the main URL.', - default: false, - }), - headless: oclif.Flags.boolean({ - char: 'h', - summary: 'Run the browser in headless mode.', - default: false, - }), - 'include-summary': oclif.Flags.boolean({ - char: 'S', - summary: '[LLM Required] Include the paper summary in the output CSV file.', - description: - 'Summaries are generated using LLM so make sure LLMs are configured by running `darwin config set`', - default: false, - }), - 'llm-provider': oclif.Flags.custom({ - summary: 'The LLM provider to use for generating summaries.', - options: Object.values(LLMProvider) as string[], - default: LLMProvider.Ollama, - parse: async (input: string): Promise => { - if (Object.values(LLMProvider).includes(input as LLMProvider)) { - return input as LLMProvider - } else { - throw new Error( - `Invalid LLM provider: ${input}. Must be one of ${Object.values(LLMProvider).join(', ')}`, - ) - } - }, - })(), + count: countFlag, + concurrency: concurrencyFlag, + output: outputFlag, + filter: filterFlag, + 'skip-captcha': skipCaptchaFlag, + legacy: legacyFlag, + headless: headlessFlag, + summary: summaryFlag, + llm: llmProviderFlag, } async init(): Promise { @@ -91,20 +49,20 @@ export default class SearchPapers extends BaseCommand { const { headless, concurrency, - 'include-summary': summarize, - 'llm-provider': llmProvider, + summary, + llm: llmProvider, 'skip-captcha': skipCaptcha, - 'legacy-processing': legacyProcessing, + legacy, } = this.flags initSearchContainer( { headless, concurrency, - summarize, + summary, llmProvider, skipCaptcha, - legacyProcessing, + legacy, }, this.localConfig, this.logger, @@ -122,7 +80,7 @@ export default class SearchPapers extends BaseCommand { } public async run(): Promise { - const { count, output, filter, 'include-summary': summarize } = this.flags + const { count, output, filter, summary } = this.flags const { keywords } = this.args this.logger.info(`Searching papers for: ${keywords}`) @@ -131,7 +89,7 @@ export default class SearchPapers extends BaseCommand { keywords, minItemCount: count, filterPattern: filter, - summarize, + summarize: summary, }) this.logger.info(`Exported papers list to: ${outputFile}`) diff --git a/src/containers/chat.container.ts b/src/containers/chat.container.ts index 577ba68..33ff959 100644 --- a/src/containers/chat.container.ts +++ b/src/containers/chat.container.ts @@ -11,7 +11,7 @@ export function initChatContainer( concurrency: number llmProvider: LLMProvider skipCaptcha: boolean - legacyProcessing: boolean + legacy: boolean }, config: TConfig, logger: Quill, @@ -20,7 +20,7 @@ export function initChatContainer( { ...opts, headless: false, - summarize: false, + summary: false, }, config, logger, diff --git a/src/containers/search.container.ts b/src/containers/search.container.ts index 7b78923..ec0d604 100644 --- a/src/containers/search.container.ts +++ b/src/containers/search.container.ts @@ -14,25 +14,25 @@ export function initSearchContainer( opts: { headless: boolean concurrency: number - summarize: boolean + summary: boolean llmProvider: LLMProvider skipCaptcha: boolean - legacyProcessing: boolean + legacy: boolean }, config: TConfig, logger: Quill, ) { - const { headless, concurrency, summarize, llmProvider, skipCaptcha, legacyProcessing } = opts + const { headless, concurrency, summary, llmProvider, skipCaptcha, legacy } = opts Container.set( Odysseus, new Odysseus({ headless, waitOnCaptcha: true, initHtml: getInitPageContent() }), ) Container.set(Quill, logger) - Container.set(PaperSearchConfig, { concurrency: summarize ? 1 : concurrency }) + Container.set(PaperSearchConfig, { concurrency: summary ? 1 : concurrency }) Container.set(PaperServiceConfig, { skipCaptcha, - legacyProcessing, + legacyProcessing: legacy, }) Container.set(BaseLanguageModel, Container.get(LLMFactory).getLLM(llmProvider, config)) diff --git a/src/inputs/args/keywords.arg.ts b/src/inputs/args/keywords.arg.ts new file mode 100644 index 0000000..a9c44cc --- /dev/null +++ b/src/inputs/args/keywords.arg.ts @@ -0,0 +1,7 @@ +import * as oclif from '@oclif/core' + +export default oclif.Args.string({ + name: 'keywords', + required: true, + description: 'The keywords to search for. (Example: "crispr cas9")', +}) diff --git a/src/inputs/flags/accession-number-regex.flag.ts b/src/inputs/flags/accession-number-regex.flag.ts new file mode 100644 index 0000000..c16541a --- /dev/null +++ b/src/inputs/flags/accession-number-regex.flag.ts @@ -0,0 +1,11 @@ +import * as oclif from '@oclif/core' + +import { AccessionPattern } from '../../services/search/constants.js' +import { FlagChar } from './char.js' + +export default oclif.Flags.string({ + char: FlagChar.AccessionNumberRegex, + helpValue: 'REGEX', + summary: 'Regex to match accession numbers. Defaults to matching BioProject accession numbers.', + default: AccessionPattern.BioProject, +}) diff --git a/src/inputs/flags/char.ts b/src/inputs/flags/char.ts new file mode 100644 index 0000000..c280e62 --- /dev/null +++ b/src/inputs/flags/char.ts @@ -0,0 +1,11 @@ +export enum FlagChar { + Count = 'c', + Concurrency = 'p', + Output = 'o', + AccessionNumberRegex = 'a', + SkipCaptcha = 's', + Filter = 'f', + Headless = 'h', + IncludeSummary = 'S', + LogLevel = 'l', +} diff --git a/src/inputs/flags/concurrency.flag.ts b/src/inputs/flags/concurrency.flag.ts new file mode 100644 index 0000000..a00b903 --- /dev/null +++ b/src/inputs/flags/concurrency.flag.ts @@ -0,0 +1,10 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.integer({ + char: FlagChar.Concurrency, + helpValue: 'NUMBER', + summary: 'The number papers to process in parallel.', + default: 10, +}) diff --git a/src/inputs/flags/count.flag.ts b/src/inputs/flags/count.flag.ts new file mode 100644 index 0000000..337f5e2 --- /dev/null +++ b/src/inputs/flags/count.flag.ts @@ -0,0 +1,11 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.integer({ + char: FlagChar.Count, + helpValue: 'NUMBER', + summary: + 'Minimum number of papers to search for. Actual number may be slightly higher with concurrency.', + default: 10, +}) diff --git a/src/inputs/flags/filter.flag.ts b/src/inputs/flags/filter.flag.ts new file mode 100644 index 0000000..ce56e3f --- /dev/null +++ b/src/inputs/flags/filter.flag.ts @@ -0,0 +1,10 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.string({ + char: FlagChar.Filter, + helpValue: 'REGEX', + summary: + 'Case-insensitive regex to filter papers by content. (Example: "Colidextribacter|Caproiciproducens")', +}) diff --git a/src/inputs/flags/headless.flag.ts b/src/inputs/flags/headless.flag.ts new file mode 100644 index 0000000..cf396bc --- /dev/null +++ b/src/inputs/flags/headless.flag.ts @@ -0,0 +1,9 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.boolean({ + char: FlagChar.Headless, + summary: 'Run the browser in headless mode (no UI).', + default: false, +}) diff --git a/src/inputs/flags/legacy.flag.ts b/src/inputs/flags/legacy.flag.ts new file mode 100644 index 0000000..d0c5258 --- /dev/null +++ b/src/inputs/flags/legacy.flag.ts @@ -0,0 +1,7 @@ +import * as oclif from '@oclif/core' + +export default oclif.Flags.boolean({ + summary: + 'Enable legacy processing which extracts text only from the main URL. The new method attempts to extract text from the source URLs (pdf or html) and falls back to the main URL.', + default: false, +}) diff --git a/src/inputs/flags/llm-provider.flag.ts b/src/inputs/flags/llm-provider.flag.ts new file mode 100644 index 0000000..97da9ea --- /dev/null +++ b/src/inputs/flags/llm-provider.flag.ts @@ -0,0 +1,19 @@ +import * as oclif from '@oclif/core' + +import { LLMProvider } from '../../config/schema.js' + +export default oclif.Flags.custom({ + summary: 'The LLM provider to use for generating summaries.', + options: Object.values(LLMProvider) as string[], + helpValue: Object.values(LLMProvider).join('|'), + default: LLMProvider.Ollama, + parse: async (input: string): Promise => { + if (Object.values(LLMProvider).includes(input as LLMProvider)) { + return input as LLMProvider + } else { + throw new Error( + `Invalid LLM provider: ${input}. Must be one of ${Object.values(LLMProvider).join(', ')}`, + ) + } + }, +})() diff --git a/src/inputs/flags/output.flag.ts b/src/inputs/flags/output.flag.ts new file mode 100644 index 0000000..8a8ad57 --- /dev/null +++ b/src/inputs/flags/output.flag.ts @@ -0,0 +1,11 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.string({ + char: FlagChar.Output, + helpValue: 'PATH', + summary: + 'Destination for the CSV file. Specify folder path for auto-generated filename or file path for direct use.', + default: '.', +}) diff --git a/src/inputs/flags/skip-captcha.flag.ts b/src/inputs/flags/skip-captcha.flag.ts new file mode 100644 index 0000000..a72ddb3 --- /dev/null +++ b/src/inputs/flags/skip-captcha.flag.ts @@ -0,0 +1,9 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.boolean({ + char: FlagChar.SkipCaptcha, + summary: 'Skip captcha on paper URLs. Note: Google Scholar captcha still needs to be solved.', + default: false, +}) diff --git a/src/inputs/flags/summary.flag.ts b/src/inputs/flags/summary.flag.ts new file mode 100644 index 0000000..8587745 --- /dev/null +++ b/src/inputs/flags/summary.flag.ts @@ -0,0 +1,11 @@ +import * as oclif from '@oclif/core' + +import { FlagChar } from './char.js' + +export default oclif.Flags.boolean({ + char: FlagChar.IncludeSummary, + summary: 'Include summaries in the output CSV (requires LLM, sets concurrency to 1)', + description: + 'Summaries are generated using LLM. Ensure LLMs are configured by running `darwin config set`.', + default: false, +})