Skip to content

Commit 6557663

Browse files
Refactor commands
1 parent e3ccc3e commit 6557663

File tree

5 files changed

+129
-91
lines changed

5 files changed

+129
-91
lines changed

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@
5757
"@oclif/plugin-help",
5858
"@oclif/plugin-plugins"
5959
],
60-
"topicSeparator": " ",
6160
"topics": {
62-
"hello": {
63-
"description": "Say hello to the world and others"
61+
"profiles": {
62+
"description": "Manage LLM provider profiles"
63+
},
64+
"translate": {
65+
"description": "Translate content between languages"
6466
}
6567
}
6668
},
@@ -77,4 +79,4 @@
7779
},
7880
"types": "dist/index.d.ts",
7981
"packageManager": "[email protected]"
80-
}
82+
}

src/commands/markdown.ts

Lines changed: 0 additions & 80 deletions
This file was deleted.

src/commands/translate/base.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {BaseChatModel} from '@langchain/core/language_models/chat_models'
2+
import {Args, Command, Flags, Interfaces} from '@oclif/core'
3+
4+
import {Translator} from '../../core/translators/translator.js'
5+
import {createProviderFromProfile} from '../../lib/profile/factory.js'
6+
import {loadProfile} from '../../lib/profile/storage.js'
7+
8+
type TranslateFlags<T extends typeof Command> = Interfaces.InferredFlags<
9+
T['flags'] & typeof BaseTranslateCommand.baseFlags
10+
>
11+
type TranslateArgs<T extends typeof Command> = Interfaces.InferredArgs<T['args']>
12+
13+
export abstract class BaseTranslateCommand<T extends typeof Command> extends Command {
14+
static args = {
15+
input: Args.string({
16+
description: 'The text you want to translate',
17+
required: false,
18+
}),
19+
}
20+
static baseFlags = {
21+
from: Flags.string({
22+
description: 'Source language',
23+
required: true,
24+
}),
25+
profile: Flags.string({
26+
description: 'Profile to use for translation',
27+
required: true,
28+
}),
29+
stream: Flags.boolean({
30+
default: false,
31+
description: 'Stream the translation output',
32+
}),
33+
to: Flags.string({
34+
description: 'Target language',
35+
required: true,
36+
}),
37+
}
38+
protected args!: TranslateArgs<T>
39+
protected flags!: TranslateFlags<T>
40+
41+
abstract createTranslator(llm: BaseChatModel): Translator
42+
43+
public async init(): Promise<void> {
44+
await super.init()
45+
const {args, flags} = await this.parse({
46+
args: this.ctor.args,
47+
baseFlags: (super.ctor as typeof BaseTranslateCommand).baseFlags,
48+
flags: this.ctor.flags,
49+
strict: this.ctor.strict,
50+
})
51+
this.flags = flags as TranslateFlags<T>
52+
this.args = args as TranslateArgs<T>
53+
}
54+
55+
async run(): Promise<void> {
56+
let input: string
57+
58+
if (this.args.input) {
59+
input = this.args.input
60+
} else {
61+
const chunks: Buffer[] = []
62+
for await (const chunk of process.stdin) {
63+
chunks.push(chunk)
64+
}
65+
66+
input = Buffer.concat(chunks).toString('utf8')
67+
}
68+
69+
const llm = createProviderFromProfile(loadProfile(this.flags.profile))
70+
const translator = this.createTranslator(llm)
71+
72+
if (this.flags.stream) {
73+
for await (const chunk of translator.translateStream({
74+
content: input,
75+
sourceLanguage: this.flags.from,
76+
targetLanguage: this.flags.to,
77+
})) {
78+
process.stdout.write(chunk)
79+
}
80+
} else {
81+
const result = await translator.translate({
82+
content: input,
83+
sourceLanguage: this.flags.from,
84+
targetLanguage: this.flags.to,
85+
})
86+
87+
process.stdout.write(result)
88+
}
89+
}
90+
}

src/commands/translate/markdown.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {BaseChatModel} from '@langchain/core/language_models/chat_models'
2+
3+
import {MARKDOWN_SYSTEM_PROMPT} from '../../core/prompts/markdown.js'
4+
import {MarkdownSplitter} from '../../core/splitters/markdown.js'
5+
import {Translator} from '../../core/translators/translator.js'
6+
import {BaseTranslateCommand} from './base.js'
7+
8+
export default class TranslateMarkdown extends BaseTranslateCommand<typeof TranslateMarkdown> {
9+
static args = {
10+
...BaseTranslateCommand.args,
11+
}
12+
static description = 'Translate markdown'
13+
static examples = [
14+
'<%= config.bin %> <%= command.id %> --profile default-openai --from EN --to ES "Hello"',
15+
'<%= config.bin %> <%= command.id %> --profile default-openai --from EN --to ES --stream "Hello"',
16+
'cat doc.md | <%= config.bin %> <%= command.id %> --profile default-openai --from EN --to ES',
17+
'echo "# Hello" | <%= config.bin %> <%= command.id %> --profile default-openai --from EN --to ES',
18+
]
19+
static flags = {
20+
...BaseTranslateCommand.baseFlags,
21+
}
22+
23+
createTranslator(llm: BaseChatModel): Translator {
24+
return new Translator(llm, new MarkdownSplitter(), MARKDOWN_SYSTEM_PROMPT)
25+
}
26+
}

test/commands/markdown.test.ts renamed to test/commands/translate/markdown.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {afterAll, beforeAll, describe, expect, it} from '@jest/globals'
22
import {runCommand} from '@oclif/test'
33

4-
import {setupTestProfile, teardownTestProfile} from '../helpers/profile-setup.js'
4+
import {setupTestProfile, teardownTestProfile} from '../../helpers/profile-setup.js'
55

6-
describe('markdown command', () => {
6+
describe('translate:markdown command', () => {
77
beforeAll(() => {
88
setupTestProfile()
99
})
@@ -15,13 +15,13 @@ describe('markdown command', () => {
1515
describe('basic usage', () => {
1616
it('translates markdown', async () => {
1717
await expect(
18-
runCommand(['markdown', '--profile', 'test-profile', '--from', 'EN', '--to', 'ES', 'Hello']),
18+
runCommand(['translate:markdown', '--profile', 'test-profile', '--from', 'EN', '--to', 'ES', 'Hello']),
1919
).resolves.not.toThrow()
2020
})
2121

2222
it('streams translated markdown', async () => {
2323
await expect(
24-
runCommand(['markdown', '--profile', 'test-profile', '--from', 'EN', '--to', 'ES', '--stream', 'Hello']),
24+
runCommand(['translate:markdown', '--profile', 'test-profile', '--from', 'EN', '--to', 'ES', '--stream', 'Hello']),
2525
).resolves.not.toThrow()
2626
})
2727
})
@@ -31,7 +31,7 @@ describe('markdown command', () => {
3131
setupTestProfile('test-profile', ['Hóla'])
3232

3333
const {stdout} = await runCommand([
34-
'markdown',
34+
'translate:markdown',
3535
'--profile',
3636
'test-profile',
3737
'--from',
@@ -47,7 +47,7 @@ describe('markdown command', () => {
4747
setupTestProfile('test-profile', ['Hóla'])
4848

4949
const {stdout} = await runCommand([
50-
'markdown',
50+
'translate:markdown',
5151
'--profile',
5252
'test-profile',
5353
'--from',
@@ -64,7 +64,7 @@ describe('markdown command', () => {
6464
setupTestProfile('test-profile', ['# Page 1\nbonjour', '# Page 2\nmonde'])
6565

6666
const {stdout} = await runCommand([
67-
'markdown',
67+
'translate:markdown',
6868
'--profile',
6969
'test-profile',
7070
'--from',

0 commit comments

Comments
 (0)