@jpetit/toolkit 3.0.22 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/assets/prompts/creators/create-solution.tpl.txt +10 -0
  2. package/assets/prompts/creators/create-statement.tpl.txt +21 -0
  3. package/assets/prompts/creators/create-translation.tpl.txt +5 -0
  4. package/assets/prompts/creators/private-test-cases.txt +6 -0
  5. package/assets/prompts/creators/sample-test-cases.txt +6 -0
  6. package/assets/prompts/creators/system-prompt.txt +2 -0
  7. package/assets/prompts/examples/statement-coda.tex +7 -0
  8. package/assets/prompts/examples/statement.tex +19 -0
  9. package/assets/prompts/generators/efficiency.md +41 -0
  10. package/assets/prompts/generators/hard.md +47 -0
  11. package/assets/prompts/generators/random.md +39 -0
  12. package/assets/prompts/proglangs/cc.md +3 -0
  13. package/assets/prompts/proglangs/py.md +40 -0
  14. package/package.json +48 -11
  15. package/toolkit/index.ts +32 -21
  16. package/lib/ai.ts +0 -144
  17. package/lib/cleaner.ts +0 -66
  18. package/lib/compilers/base.ts +0 -103
  19. package/lib/compilers/clojure.ts +0 -76
  20. package/lib/compilers/gcc.ts +0 -68
  21. package/lib/compilers/ghc.ts +0 -75
  22. package/lib/compilers/gxx.ts +0 -68
  23. package/lib/compilers/index.ts +0 -72
  24. package/lib/compilers/python3.ts +0 -105
  25. package/lib/compilers/run-haskell.ts +0 -113
  26. package/lib/compilers/run-python.ts +0 -109
  27. package/lib/data.ts +0 -19
  28. package/lib/doctor.ts +0 -158
  29. package/lib/generate.ts +0 -329
  30. package/lib/maker.ts +0 -700
  31. package/lib/settings.ts +0 -42
  32. package/lib/tui.ts +0 -142
  33. package/lib/types.ts +0 -20
  34. package/lib/utils.ts +0 -133
  35. package/toolkit/ai.ts +0 -30
  36. package/toolkit/clean.ts +0 -37
  37. package/toolkit/compilers.ts +0 -29
  38. package/toolkit/create-jutge-ai.ts +0 -101
  39. package/toolkit/create-template.ts +0 -55
  40. package/toolkit/create-wizard.ts +0 -6
  41. package/toolkit/create.ts +0 -65
  42. package/toolkit/doctor.ts +0 -18
  43. package/toolkit/generate.ts +0 -116
  44. package/toolkit/init.ts +0 -56
  45. package/toolkit/make.ts +0 -118
  46. package/toolkit/verify.ts +0 -19
@@ -1,109 +0,0 @@
1
- import tui from '../tui'
2
- import { execa } from 'execa'
3
- import { cp, exists, rm } from 'fs/promises'
4
- import { join } from 'path'
5
- import { readText, writeText } from '../utils'
6
- import { Compiler } from './base'
7
- import type { Handler } from '../types'
8
-
9
- export class RunPython_Compiler extends Compiler {
10
- id(): string {
11
- return 'RunPython'
12
- }
13
-
14
- name(): string {
15
- return 'RunPython'
16
- }
17
-
18
- type(): string {
19
- return 'interpreter'
20
- }
21
-
22
- language(): string {
23
- return 'Python'
24
- }
25
-
26
- async version(): Promise<string> {
27
- return await this.getVersion('python3 --version', 0)
28
- }
29
-
30
- flags1(): string {
31
- return ''
32
- }
33
-
34
- flags2(): string {
35
- return ''
36
- }
37
-
38
- extension(): string {
39
- return 'py'
40
- }
41
-
42
- async compile(handler: Handler, directory: string, sourcePath: string): Promise<void> {
43
- const exePath = sourcePath + '.exe'
44
-
45
- if (handler.source_modifier === 'none') {
46
- tui.command(`cp ${sourcePath} ${exePath}`)
47
- await cp(join(directory, sourcePath), join(directory, exePath))
48
- } else {
49
- throw new Error(`Unknown source modifier: ${handler.source_modifier as string}`)
50
- }
51
-
52
- tui.command(`python3 -m py_compile ${exePath}`)
53
-
54
- const { exitCode } = await execa({
55
- reject: false,
56
- stderr: 'inherit',
57
- stdout: 'inherit',
58
- cwd: directory,
59
- })`python3 -m py_compile ${exePath}`
60
-
61
- if (exitCode !== 0) throw new Error(`Compilation failed for ${sourcePath}`)
62
- }
63
-
64
- override async execute(
65
- handler: Handler,
66
- directory: string,
67
- sourcePath: string,
68
- inputPath: string,
69
- outputPath: string,
70
- ): Promise<void> {
71
- const exePath = 'solution.py.exe'
72
- if (!(await exists(join(directory, exePath)))) {
73
- throw new Error(`Executable file ${exePath} does not exist in directory ${directory}`)
74
- }
75
-
76
- const mergedPath = `solution-${inputPath}.py.exe`
77
-
78
- tui.command(`Merging ${exePath} and ${inputPath} into ${mergedPath}`, { italic: true })
79
-
80
- await this.mergeScripts(directory, exePath, inputPath, mergedPath)
81
-
82
- const fullOutputPath = join(directory, outputPath)
83
-
84
- await rm(fullOutputPath, { force: true })
85
-
86
- tui.command(`python3 ${mergedPath} > ${outputPath}`)
87
-
88
- const { exitCode } = await execa({
89
- reject: false,
90
- stdout: { file: fullOutputPath },
91
- stderr: 'inherit',
92
- cwd: directory,
93
- })`python3 ${mergedPath}`
94
-
95
- if (exitCode !== 0) throw new Error(`Execution failed for ${mergedPath}`)
96
- }
97
-
98
- async mergeScripts(
99
- directory: string,
100
- scriptPath1: string,
101
- scriptPath2: string,
102
- outputScriptPath: string,
103
- ): Promise<void> {
104
- const script1 = await readText(join(directory, scriptPath1))
105
- const script2 = await readText(join(directory, scriptPath2))
106
- const mergedScript = script1 + '\n\n\n' + script2
107
- await writeText(join(directory, outputScriptPath), mergedScript)
108
- }
109
- }
package/lib/data.ts DELETED
@@ -1,19 +0,0 @@
1
- import { invert } from 'radash'
2
-
3
- export const languageNames: Record<string, string> = {
4
- en: 'English',
5
- ca: 'Catalan',
6
- es: 'Spanish',
7
- fr: 'French',
8
- de: 'German',
9
- }
10
-
11
- export const proglangNames: Record<string, string> = {
12
- c: 'C',
13
- cc: 'C++',
14
- py: 'Python3',
15
- hs: 'Haskell',
16
- clj: 'Clojure',
17
- }
18
-
19
- export const proglangExtensions: Record<string, string> = invert(proglangNames)
package/lib/doctor.ts DELETED
@@ -1,158 +0,0 @@
1
- import tui from './tui'
2
- import { execa } from 'execa'
3
- import { nothing } from './utils'
4
- import terminalLink from 'terminal-link'
5
-
6
- export async function probePython3(showInfo: boolean = false): Promise<boolean> {
7
- if (showInfo) tui.command('python3 --version')
8
- const { stdout } = await execa({ reject: false })`python3 --version`
9
- const version = stdout.trim()
10
- if (showInfo) console.log(version)
11
- return version.startsWith('Python 3')
12
- }
13
-
14
- export async function probePythonModule(showInfo: boolean = false, module: string): Promise<boolean> {
15
- if (showInfo) tui.command(`python3 -m pip show ${module}`)
16
- const { exitCode } = await execa({ reject: false })`python3 -m pip show ${module}`
17
- if (showInfo) {
18
- if (exitCode === 0) {
19
- tui.success(`Module ${module} is installed`)
20
- } else {
21
- tui.warning(`Module ${module} is not installed`)
22
- }
23
- }
24
- return exitCode === 0
25
- }
26
-
27
- export async function probeGCC(showInfo: boolean = false): Promise<boolean> {
28
- if (showInfo) tui.command('g++ --version')
29
- const { stdout } = await execa({ reject: false })`g++ --version`
30
- const version = stdout.split('\n')[0]!.trim()
31
- if (showInfo) console.log(version)
32
- return stdout.startsWith('Apple clang') || stdout.startsWith('g++')
33
- }
34
-
35
- export async function probePdfLaTeX(showInfo: boolean = false): Promise<boolean> {
36
- if (showInfo) tui.command('pdflatex --version')
37
- const { stdout } = await execa({ reject: false })`pdflatex --version`
38
- const version = stdout.split('\n')[0]!.trim()
39
- if (showInfo) console.log(version)
40
- return stdout.startsWith('pdfTeX')
41
- }
42
-
43
- export async function probeXeLaTeX(showInfo: boolean = false): Promise<boolean> {
44
- if (showInfo) tui.command('xelatex --version')
45
- const { stdout } = await execa({ reject: false })`xelatex --version`
46
- const version = stdout.split('\n')[0]!.trim()
47
- if (showInfo) console.log(version)
48
- return stdout.startsWith('XeTeX')
49
- }
50
-
51
- export async function probePandoc(showInfo: boolean = false): Promise<boolean> {
52
- if (showInfo) tui.command('pandoc --version')
53
- const { stdout } = await execa({ reject: false })`pandoc --version`
54
- const version = stdout.split('\n')[0]!.trim()
55
- if (showInfo) console.log(version)
56
- return stdout.startsWith('pandoc') && stdout.includes('+lua')
57
- }
58
-
59
- export async function probeImageMagick(showInfo: boolean = false): Promise<boolean> {
60
- if (showInfo) tui.command('magick --version')
61
- const { stdout } = await execa({ reject: false })`magick --version`
62
- const version = stdout.split('\n')[0]!.trim()
63
- if (showInfo) console.log(version)
64
- return stdout.startsWith('Version: ImageMagick')
65
- }
66
-
67
- export async function checkPython3(): Promise<void> {
68
- if (await probePython3(true)) {
69
- tui.success('Python3 seems installed')
70
- const modules = 'turtle-pil yogi easyinput'.split(' ')
71
- for (const m of modules) {
72
- await probePythonModule(true, m)
73
- }
74
- } else {
75
- tui.warning('Python3 does not appear to be installed')
76
- tui.print('This is not a problem if you do not plan to use Python solutions')
77
- tui.print('See https://www.python.org/downloads/')
78
- }
79
- }
80
-
81
- export async function checkGCC(): Promise<void> {
82
- if (await probeGCC(true)) {
83
- tui.success('C/C++ seems installed')
84
- } else {
85
- tui.warning('C/C++ does not appear to be installed')
86
- tui.print('This is not a problem if you do not plan to use C or C++ solutions')
87
- tui.print('Please install GCC or Clang')
88
- }
89
- }
90
-
91
- export async function checkPdfLaTeX(): Promise<void> {
92
- if (await probePdfLaTeX(true)) {
93
- tui.success('LaTeX seems installed')
94
- } else {
95
- tui.warning('LaTeX does not appear to be installed')
96
- tui.print('You will not be able to generate PDF statements')
97
- tui.print('TODO: Provide instructions for installing LaTeX')
98
- }
99
- }
100
-
101
- export async function checkXeLaTeX(): Promise<void> {
102
- if (await probeXeLaTeX(true)) {
103
- tui.success('XeLaTeX seems installed')
104
- } else {
105
- tui.warning('XeLaTeX does not appear to be installed')
106
- tui.print('You will not be able to generate PDF statements with Unicode support')
107
- tui.print('TODO: Provide instructions for installing XeLaTeX')
108
- }
109
- }
110
-
111
- export async function checkPandoc(): Promise<void> {
112
- if (await probePandoc(true)) {
113
- tui.success('Pandoc with Lua support seems installed')
114
- } else {
115
- tui.warning('Pandoc with Lua support does not appear to be installed')
116
- tui.print('You will not be able to generate text statements (HTML, TXT, MD)')
117
- tui.print('See https://pandoc.org/installing.html')
118
- }
119
- }
120
-
121
- export async function checkImageMagick(): Promise<void> {
122
- if (await probeImageMagick(true)) {
123
- tui.success('ImageMagick seems installed')
124
- } else {
125
- tui.warning('ImageMagick does not appear to be installed')
126
- tui.print('You will not be able to convert images for text statements')
127
- tui.print('See https://imagemagick.org/script/download.php')
128
- }
129
- }
130
-
131
- export async function checkEnvVars(): Promise<void> {
132
- await nothing()
133
- const vars = ['OPENAI_API_KEY', 'GEMINI_API_KEY']
134
- for (const v of vars) {
135
- if (process.env[v]) {
136
- tui.success(`${v} is set`)
137
- } else {
138
- tui.warning(`${v} is not set`)
139
- tui.print(`You will not be able to use related AI models`)
140
- }
141
- }
142
- }
143
-
144
- export async function checkTerminal(): Promise<void> {
145
- await nothing()
146
- if (process.stdout.isTTY) {
147
- tui.success('Terminal supports TTY')
148
- } else {
149
- tui.warning('Terminal does not support TTY')
150
- tui.print('Some output features may not work as expected')
151
- }
152
- if (terminalLink.isSupported) {
153
- tui.success('Terminal supports hyperlinks')
154
- } else {
155
- tui.warning('Terminal does not support hyperlinks')
156
- tui.print('Links to files may not be clickable')
157
- }
158
- }
package/lib/generate.ts DELETED
@@ -1,329 +0,0 @@
1
- import { ChatBot, cleanMardownCodeString, estimatePowerConsumption } from './ai'
2
- import tui from './tui'
3
- import chalk from 'chalk'
4
- import { appendFile, mkdir } from 'fs/promises'
5
- import { oraPromise } from 'ora'
6
- import { join } from 'path'
7
- import { z } from 'zod'
8
- import { paths } from './settings'
9
- import { proglangNames, languageNames } from './data'
10
- import { nothing, writeText, writeYaml } from './utils'
11
- import YAML from 'yaml'
12
-
13
- const showPrompt = false
14
- const showAnswer = false
15
-
16
- export const ZProblemData = z.object({
17
- title: z.string(),
18
- description: z.string(),
19
- author: z.string(),
20
- email: z.string().email(),
21
- type: z.string(),
22
- proglangs: z.array(z.string()),
23
- languages: z.array(z.string()),
24
- model: z.string(),
25
- outputDir: z.string(),
26
- })
27
-
28
- export type ProblemData = z.infer<typeof ZProblemData>
29
-
30
- export async function generateProblemWithJutgeAI(info: ProblemData): Promise<void> {
31
- const generator = new ProblemGenerator(info)
32
- await generator.run()
33
- await generator.save(info.outputDir)
34
- }
35
-
36
- class ProblemGenerator {
37
- private bot: ChatBot
38
- private info: ProblemData
39
-
40
- // generated problem parts
41
- private problemStatement: string = ''
42
- private problemSampleTests: string = ''
43
- private problemPrivateTests: string = ''
44
- private problemSolutions: Record<string, string> = {}
45
- private problemTranslations: Record<string, string> = {}
46
- private problemReadme: string = ''
47
-
48
- private latexExample = `
49
- \\Problem{Which one is missing?}
50
-
51
- \\Statement
52
-
53
- Johny has a list of objects, labeled between 1 and $n$, but he lost one of them. He wants to know which one is missing.
54
-
55
- \\medskip
56
-
57
- Write a program that reads sequences with all the numbers between 1 and $n$ but one, and tells which one is missing.
58
-
59
- \\Input
60
-
61
- Input consists of several sequences.
62
- Every sequence begins with a number $n$ between~1 and~$10^4$ followed by $n - 1$ natural numbers.
63
- Every number between 1 and $n$ appears exactly once, except one of them, which is missing.
64
-
65
- \\Output
66
-
67
- For every sequence, print the missing number.
68
- `
69
-
70
- private systemPrompt = `
71
- You are an expert in coding and writing programmming problems.
72
- You provide accurate information and write following the instructed format.
73
- `
74
-
75
- private statementCoda = `
76
-
77
- \\Observation
78
-
79
- This problem has been generated by Jutge$^{\\text{AI}}$. Remove this observation after reviewing it carefully.
80
-
81
- \\Sample
82
-
83
- `
84
- constructor(info: ProblemData) {
85
- this.info = info
86
- this.bot = new ChatBot(info.model, this.systemPrompt)
87
- }
88
-
89
- async generateStatement(): Promise<string> {
90
- return await tui.section('Generating problem statement', async () => {
91
- const statementPrompt = `
92
- You are to write the statement of a programming problem
93
-
94
- The statement must be written in LaTeX, using a few predefined macros.
95
- Use LaTeX math syntax for formulas and variables.
96
- Use dollars for inline maths and use \\[ and \\] for display maths.
97
- Do not add input/output test cases in the statement.
98
- Separate paragraphs by a blank line and \\medskip macro.
99
-
100
- Write in the style of Salvador Roura, the famous problem setter from Jutge.org.
101
-
102
- Here is an example for an unrelated problem, follow its structure and macros:
103
-
104
- ${this.latexExample}
105
-
106
- The title for this problem is:
107
-
108
- ${this.info.title}
109
-
110
- Here is the description of this problem and some additional instructions:
111
-
112
- ${this.info.description}
113
- `
114
- const answer = cleanMardownCodeString(await this.complete(statementPrompt)) + this.statementCoda
115
- return answer
116
- })
117
- }
118
-
119
- async generateSampleTests(): Promise<string> {
120
- return await tui.section('Generating sample test cases', async () => {
121
- const sampleTestCasesPrompt = `
122
- Now, write a sample test case file to illustrate the input that the program must read according to the problem statement.
123
-
124
- Only the input should be given, the output will be computed from it.
125
- Sample test cases should be relatively small but cover interesting cases but not all edge cases.
126
- `
127
- const answer = cleanMardownCodeString(await this.complete(sampleTestCasesPrompt))
128
- return answer
129
- })
130
- }
131
-
132
- async generatePrivateTests(): Promise<string> {
133
- return await tui.section('Generating private test cases', async () => {
134
- const privateTestCasesPrompt = `
135
- Now, write a private test case file to check the correctness and efficiency of the program.
136
-
137
- Only the input should be given, the output will be computed from it.
138
- The private test cases should cover edge cases.
139
- Limit the number of test cases to 20.
140
- `
141
- const answer = cleanMardownCodeString(await this.complete(privateTestCasesPrompt))
142
- return answer
143
- })
144
- }
145
-
146
- async generateSolutions(): Promise<Record<string, string>> {
147
- return await tui.section('Generating solutions', async () => {
148
- const solutions: Record<string, string> = {}
149
- for (const proglang of this.info.proglangs) {
150
- await tui.section(`Generating solution in ${proglangNames[proglang]}`, async () => {
151
- const solutionPrompt = `
152
- Now, write a solution in ${proglangNames[proglang]} to solve this problem
153
-
154
- It must be written in an idiomatic way and only include relevant comments.
155
- The code must be efficient and handle all edge cases.
156
- The code must read from standard input and write to standard output.
157
- The code must be well written and easy to understand to novices.
158
- The code does not have to check the preconditions stated in the probleM
159
- Do not use any non-standard libraries.
160
- ${proglang === 'cpp' ? 'Do not use fast input/output methods.' : ''}
161
- ${proglang === 'cpp' ? 'Add a using namespace std; declaration after the includes and do not use std:: prefixes.' : ''}
162
- ${proglang === 'py' ? 'Use type annotations.' : ''}
163
- `
164
- const answer = cleanMardownCodeString(await this.complete(solutionPrompt))
165
- this.bot.forgetLastInteraction()
166
- solutions[proglang] = answer
167
- })
168
- }
169
- return solutions
170
- })
171
- }
172
-
173
- async translateStatements(): Promise<Record<string, string>> {
174
- return await tui.section('Translating problem statements', async () => {
175
- const translations: Record<string, string> = {}
176
- for (const language of this.info.languages) {
177
- await tui.section(`Translating to ${languageNames[language]}`, async () => {
178
- const translationPrompt = `
179
- Now, translate the problem statement to ${languageNames[language]}.
180
-
181
- The translation must be accurate and use proper technical terminology.
182
- Maintain the LaTeX formatting and macros.
183
- The texts that the program must read and write should not be translated.
184
- `
185
- const answer = cleanMardownCodeString(await this.complete(translationPrompt)) + this.statementCoda
186
- this.bot.forgetLastInteraction()
187
- translations[language] = answer
188
- })
189
- }
190
- return translations
191
- })
192
- }
193
-
194
- async generateReadme(): Promise<string> {
195
- return tui.section('Generating README.md', async () => {
196
- await nothing()
197
- const readme = `
198
- # ${this.info.title}
199
-
200
- This programming problem for Jutge.org was generated by Jutge<sup>AI</sup> through the Jutge.org API using ${this.info.model} and a prompt by ${this.info.author}.
201
-
202
- **Warning**: This problem may contain inaccuracies or errors. Review the problem statements, test cases, and solutions carefully before using them in a real setting. Output tests and statement PDFs have not been generated, use \`jutge-make-problem\` to generate them.
203
-
204
- ## Author
205
-
206
- ${this.info.author}
207
-
208
- ## Original prompt
209
-
210
- ${this.info.description}
211
-
212
- ## Generated solutions
213
-
214
- - ${this.info.proglangs.map((proglang) => proglangNames[proglang]).join('\n- ')}
215
-
216
- ## Generated languages
217
-
218
- - English
219
- - ${this.info.languages.map((language) => languageNames[language]).join('\n- ')}
220
-
221
- ## Model information
222
-
223
- \`\`\`yaml
224
- ${YAML.stringify(this.bot.modelInformation(), null, 2)}
225
- \`\`\`
226
-
227
- ## Estimated cost
228
-
229
- The following information is based on estimations from token counts and do not reflect the actual costs incurred. Using GPT-5 pricing as reference.
230
-
231
- - Total input tokens: ${this.bot.totalInputTokens}
232
- - Total output tokens: ${this.bot.totalOutputTokens}
233
- - Total input cost: ${this.bot.totalInputCost.toFixed(6)} USD
234
- - Total output cost: ${this.bot.totalOutputCost.toFixed(6)} USD
235
- - Total estimated cost: ${(this.bot.totalInputCost + this.bot.totalOutputCost).toFixed(6)} USD
236
- - Energy: ${estimatePowerConsumption(this.bot.totalInputTokens, this.bot.totalOutputTokens).wattHours.toFixed(6)} Wh
237
- - CO₂ emissions: ${estimatePowerConsumption(this.bot.totalInputTokens, this.bot.totalOutputTokens).co2Grams.toFixed(6)} g CO₂
238
-
239
- `
240
- return readme
241
- })
242
- }
243
-
244
- async run() {
245
- tui.title('Generating problem with JutgeAI')
246
- await this.bot.init()
247
- this.problemStatement = await this.generateStatement()
248
- this.problemSampleTests = await this.generateSampleTests()
249
- this.problemPrivateTests = await this.generatePrivateTests()
250
- this.problemSolutions = await this.generateSolutions() // these are forgotten inside
251
- this.bot.forgetLastInteraction() // forget private tests
252
- this.bot.forgetLastInteraction() // forget sample tests
253
- this.problemTranslations = await this.translateStatements()
254
- this.problemReadme = await this.generateReadme()
255
- }
256
-
257
- async save(path: string) {
258
- await tui.section(`Saving problem to ${path}`, async () => {
259
- await mkdir(path, { recursive: true })
260
- await writeText(join(path, 'problem.en.tex'), this.problemStatement)
261
-
262
- const yml = {
263
- title: this.info.title,
264
- author: this.info.author,
265
- email: this.info.email,
266
- model: this.info.model,
267
- }
268
- await writeYaml(`${path}/problem.en.yml`, yml)
269
-
270
- for (const [lang, translation] of Object.entries(this.problemTranslations)) {
271
- await writeText(join(path, `problem.${lang}.tex`), translation)
272
- const yml = {
273
- title: getTitleFromStatement(translation) || this.info.title,
274
- translator: this.info.author,
275
- translator_email: this.info.email,
276
- original_language: 'en',
277
- model: this.info.model,
278
- }
279
- await writeYaml(join(path, `problem.${lang}.yml`), yml)
280
- }
281
-
282
- await writeText(join(path, 'sample.inp'), this.problemSampleTests)
283
-
284
- await writeText(join(path, 'test.inp'), this.problemPrivateTests)
285
-
286
- for (const [proglang, solution] of Object.entries(this.problemSolutions)) {
287
- const ext = proglang
288
- await writeText(join(path, `solution.${ext}`), solution)
289
- }
290
-
291
- const handlerYml: any = {
292
- handler: 'std',
293
- }
294
- await writeYaml(join(path, 'handler.yml'), handlerYml)
295
-
296
- await writeText(join(path, 'README.md'), this.problemReadme)
297
- })
298
- }
299
-
300
- async complete(prompt: string): Promise<string> {
301
- if (showPrompt) console.log(chalk.gray(prompt))
302
- await appendFile(join(paths.log, 'jutge-ai.log'), '------------------- PROMPT -------------------\n\n')
303
- await appendFile(join(paths.log, 'jutge-ai.log'), prompt + '\n\n')
304
-
305
- const answer = await oraPromise(
306
- async () => {
307
- return await this.bot.complete(prompt)
308
- },
309
- {
310
- text: `JutgeAI is thinking`,
311
- },
312
- )
313
-
314
- if (showAnswer) console.log(chalk.gray(answer))
315
- await appendFile(join(paths.log, 'jutge-ai.log'), '------------------- ANSWER -------------------\n\n')
316
- await appendFile(join(paths.log, 'jutge-ai.log'), answer + '\n\n')
317
-
318
- return answer
319
- }
320
- }
321
-
322
- export function getTitleFromStatement(statement: string): string | null {
323
- const pattern = /\\Problem\{(.*?)\}/
324
- const match = statement.match(pattern)
325
- if (match) {
326
- return match[1]!.trim()
327
- }
328
- return null
329
- }