From 5689929c83efebb183516d2e7265ac214dfb75a0 Mon Sep 17 00:00:00 2001 From: "Glenn Y. Rolland" Date: Thu, 27 Apr 2023 09:51:17 +0200 Subject: [PATCH] feat(write): Add basic support for prompts management --- data/prompts/01-each-chapter--build-toc.tpl | 24 +++ data/prompts/prompt-01-chapter-loop.md | 18 -- shard.lock | 4 + shard.yml | 3 + src/write/file_storage.cr | 7 + src/write/run.cr | 172 +++++++++++++++++--- 6 files changed, 183 insertions(+), 45 deletions(-) create mode 100644 data/prompts/01-each-chapter--build-toc.tpl delete mode 100644 data/prompts/prompt-01-chapter-loop.md create mode 100644 src/write/file_storage.cr diff --git a/data/prompts/01-each-chapter--build-toc.tpl b/data/prompts/01-each-chapter--build-toc.tpl new file mode 100644 index 0000000..d547a9f --- /dev/null +++ b/data/prompts/01-each-chapter--build-toc.tpl @@ -0,0 +1,24 @@ +{# GOAL: Build sections object containing subsections #} + +Merci. + +Suit la structure du PROGRAMME DE FORMATION. + +Concentre toi sur le chapitre « {{chapter.title}} ». + +Commence par écrire "boundary: {{ delimiter }}". +Ensuite rédige la table des matière détaillée de ce chapitre en respectant la structure suivante: + +« +{ + "type": "section", + "title": "" + "subsections": [ + { "type": "subsection", "title": "...", "keywords": ["...", "..."] }, + { "type": "subsection", "title": "...", "keywords": ["...", "..."] }, + { "type": "subsection", "title": "...", "keywords": ["...", "..."] } + ] +} +» + +Termine en écrivant "boundary: {{ delimiter }}". diff --git a/data/prompts/prompt-01-chapter-loop.md b/data/prompts/prompt-01-chapter-loop.md deleted file mode 100644 index 8699f1f..0000000 --- a/data/prompts/prompt-01-chapter-loop.md +++ /dev/null @@ -1,18 +0,0 @@ -GOAL: Build sections object containing subsections - -PROMPT: - - Merci. - - Suit la structure du PROGRAMME DE FORMATION. - - Concentre toi sur le chapitre « {{this.title}} ». - - Focalise toi plus spécifiquement sur les sections suivantes « - {% for child in this.children %} - * {{ child }} - {% endfor %} - » de ce chapitre sur lequel on se concentre. - - Rédige la table des matière détaillée de ces différentes sections. - Ajoute également à la fin les différents travaux pratiques possibles pour chaque section. diff --git a/shard.lock b/shard.lock index 4de6f9f..c2f4a12 100644 --- a/shard.lock +++ b/shard.lock @@ -1,5 +1,9 @@ version: 2.0 shards: + baked_file_system: + git: https://github.com/schovi/baked_file_system.git + version: 0.10.0 + cor: git: https://github.com/watzon/cor.git version: 0.1.0+git.commit.9c9e51ac6168f3bd4fdc51d679b65de09ef76cac diff --git a/shard.yml b/shard.yml index e5550d1..48d65e1 100644 --- a/shard.yml +++ b/shard.yml @@ -17,6 +17,9 @@ dependencies: github: crystal-term/prompt crinja: github: straight-shoota/crinja + baked_file_system: + github: schovi/baked_file_system + version: 0.10.0 # FIXME: for prompts rendering diff --git a/src/write/file_storage.cr b/src/write/file_storage.cr new file mode 100644 index 0000000..90ddb40 --- /dev/null +++ b/src/write/file_storage.cr @@ -0,0 +1,7 @@ +require "baked_file_system" + +class FileStorage + extend BakedFileSystem + + bake_folder "../../data/prompts" +end diff --git a/src/write/run.cr b/src/write/run.cr index 0c2afd5..815a7b0 100644 --- a/src/write/run.cr +++ b/src/write/run.cr @@ -1,60 +1,178 @@ # Core require "file_utils" +require "path" # Internal require "./config" +require "./nodes/root_node" # Shards require "term-prompt" +require "crinja" module DocMachine::Write class Run private property config : DocMachine::Write::Config + property root = Nodes::RootNode.new def initialize(@config) end - # Verify parameters - def prepare() + def validate_build_dir() if ! File.directory? @config.target_directory Log.error { "Target must be a directory" } exit 1 end Log.info { "Target directory: #{@config.target_directory}" } - - if !@config.force - prompt = Term::Prompt.new - confirm = prompt.no?("Are you sure you want to proceed?") - exit 1 if !confirm - end end - def start() - Log.info { "== Writeing #{@config.target_directory}" } - p = Path.new(@config.target_directory) - cwd = Dir.current - ["docs", "slides", "images"].each do |dir| - p_sub = p.join(dir) - Log.info { "-- creating #{p_sub}" } - FileUtils.mkdir_p(p_sub) + def ask_confirmation + # if !@config.force + # prompt = Term::Prompt.new + # confirm = prompt.no?("Are you sure you want to proceed?") + # exit 1 if !confirm + # end + end + + def load_templates + pp @config + + context_file = Path[@config.target_directory] / "CONTEXT.tpl" + if ! File.exists? context_file + raise "Context file #{context_file} is missing" end - ["docs", "slides"].each do |dir| - p_sub = p.join(dir) - FileUtils.cd(p_sub) - Log.info { "-- creating link to images in #{p_sub}" } - if File.symlink? "images" - FileUtils.rm "images" - end - FileUtils.ln_sf(Path.new("..","images"), Path.new("images")) - FileUtils.cd(cwd) + @root.context = File.read(context_file) + + audience_file = Path[@config.target_directory] / "AUDIENCE.tpl" + if ! File.exists? audience_file + raise "Audience file #{audience_file} is missing" end - Log.info { "-- creating README.md" } - FileUtils.touch("README.md") + @root.audience = File.read(audience_file) + + goals_file = Path[@config.target_directory] / "GOALS.tpl" + if ! File.exists? goals_file + raise "Audience file #{goals_file} is missing" + end + @root.goals = File.read(goals_file) + + constraints_file = Path[@config.target_directory] / "CONSTRAINTS.tpl" + if ! File.exists? constraints_file + raise "Audience file #{constraints_file} is missing" + end + @root.constraints = File.read(constraints_file) end # Verify parameters + def prepare() + validate_build_dir() + ask_confirmation() + load_templates() + Log.debug { "done" } + end + + ## + ## ContentNode + ## type: ... + ## title: ... + ## keywords: ... + ## content: ... + ## + + def start() + @root.chapters = root.build_chapters() + + @root.chapters.each do |chapter| + chapter.sections = chapter.build_sections() + + chapter.sections.each do |section| + section.subsections = section.build_subsections() + # FIXME(later): section.exercises = section.build_exercises() + + section.subsections.each do |subsection| + subsection.content = subsection.build_content() + subsection.content = subsection.fix_content() + # FIXME(later): subsection.exercises = subsection.build_exercises() + end + end + end + end + + + ## Level 0 - each topic : build TOC (chapter list) + def from_topic_build_chapters + chapter_build_toc_template = FileStorage.get("./../data/prompts/01-each-chapter--build-toc.tpl") + chapters = [{ "title": "Terraform on Azure" }] + + end + + + ## Level 1 - each chapter : build TOC (section list) + # 1. build chat + # - (system) quality & style guidance + # - (user) context + # - (user) audience + # - (user) objectives + # - (user) main toc (chapters) + def from_chapter_build_sections() + delimiter = "34e127df" # FIXME: random 8 bytes hex string + chapters.each do |chapter| + template_vars = { + delimiter: delimiter, + chapter: chapter + } + render = Crinja.render(chapter_build_toc_template, template_vars) + puts render + end + end + + ## Level 2 - each section : build TOC (subsection list) + # 1. build chat + # - (system) quality & style guidance + # - (user) context + # - (user) audience + # - (user) objectives + # - (user) main toc (chapters) + # - (user) chapter toc (sections) + # 2. make openai request + # 3. validate result structure + # 4. create section objects in memory + def from_section_build_subsections() + section_build_toc_tpl = File.read(DOCMACHINE_DATA_PATH / "prompts" / "02-each-section--build-toc.tpl") + end + + + ## Level 2 - each section : build EXERCISES + # 1. build chat + # - (system) quality & style guidance + # - (user) context + # - (user) audience + # - (user) objectives + # - (user) main toc (chapters) + # - (user) chapter toc (sections) + # 2. make openai request + # 3. validate result structure + # 4. create exercises objects in memory + def from_section_build_exercises() + section_build_toc_tpl = File.read(DOCMACHINE_DATA_PATH / "prompts" / "02-each-section--build-exercises.tpl") + end + + def from_subsection_build_content() + ## Level 3 - each subsection : build CONTENT + section_build_toc_tpl = File.read(DOCMACHINE_DATA_PATH / "prompts" / "02-each-subsection--build-content.tpl") + end + + def from_subsection_fix_content() + ## Level 4 - each subsection : build FIXED CONTENT + section_build_toc_tpl = File.read(DOCMACHINE_DATA_PATH / "prompts" / "02-each-subsection--fix-content.tpl") + end + + def from_subsection_build_exercises() + ## Level 1 - each subsection EXERCICES + section_build_toc_tpl = File.read(DOCMACHINE_DATA_PATH / "prompts" / "02-each-subsection--build-exercises.tpl") + end + def wait() end end