Compare commits

..

No commits in common. "a2272230e2b82070f4e0571c62a45958ae788f62" and "3f985f27514f7c046d0461d082e7d2411a726687" have entirely different histories.

16 changed files with 63 additions and 329 deletions

View file

@ -1,24 +0,0 @@
{# 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 }}".

View file

@ -0,0 +1,18 @@
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.

View file

@ -1,9 +1,5 @@
version: 2.0 version: 2.0
shards: shards:
baked_file_system:
git: https://github.com/schovi/baked_file_system.git
version: 0.10.0
cor: cor:
git: https://github.com/watzon/cor.git git: https://github.com/watzon/cor.git
version: 0.1.0+git.commit.9c9e51ac6168f3bd4fdc51d679b65de09ef76cac version: 0.1.0+git.commit.9c9e51ac6168f3bd4fdc51d679b65de09ef76cac

View file

@ -17,9 +17,6 @@ dependencies:
github: crystal-term/prompt github: crystal-term/prompt
crinja: crinja:
github: straight-shoota/crinja github: straight-shoota/crinja
baked_file_system:
github: schovi/baked_file_system
version: 0.10.0
# FIXME: for prompts rendering # FIXME: for prompts rendering

View file

@ -55,35 +55,23 @@ module DocMachine::Build
Process.run("docker", ["pull", @docker_image], output: STDOUT) Process.run("docker", ["pull", @docker_image], output: STDOUT)
Log.info { "Building cache for image (#{data_cache_dir})" } Log.info { "Building cache for image (#{data_cache_dir})" }
FileUtils.mkdir_p(data_cache_dir) FileUtils.mkdir_p(data_cache_dir)
status = Process.run( Process.run(
"docker", "docker",
["image", "save", @docker_image, "-o", data_cache_file.to_s], ["image", "save", @docker_image, "-o", data_cache_file.to_s],
output: STDOUT output: STDOUT
) )
if status.success?
Log.info { "done" } Log.info { "done" }
else
Log.error { "Unable to save cache image" }
exit 1
end
else else
Log.info { "Cache already exist. Skipping." } Log.info { "Cache already exist. Skipping." }
end end
Log.info { "Loading #{@docker_image} image from cache..." } Log.info { "Loading #{@docker_image} image from cache..." }
docker_image_loaded = false docker_image_loaded = false
status = Process.run( Process.run(
"docker", "docker",
["image", "load", "-i", data_cache_file.to_s], ["image", "load", @docker_image, "-i", data_cache_file.to_s],
output: STDOUT output: STDOUT
) )
if status.success?
Log.info { "done" }
else
Log.error { "Unable to load cache image" }
exit 1
end
end end
def start() def start()

View file

@ -2,7 +2,6 @@ require "option_parser"
require "digest/sha256" require "digest/sha256"
require "colorize" require "colorize"
require "./log"
require "./config" require "./config"
require "./build/cli" require "./build/cli"
require "./build/run" require "./build/run"
@ -15,8 +14,6 @@ require "./write/run"
module DocMachine module DocMachine
class Cli class Cli
Log = DocMachine::Log.for("cli")
def initialize def initialize
end end
@ -31,18 +28,12 @@ module DocMachine
"Main options:" "Main options:"
].join("\n") ].join("\n")
opts.on("-v", "--verbosity LEVEL", "Change verbosity level to LEVEL (0..3)") do |verbose| opts.on("-v", "--verbose", "Enable verbosity") do |verbose|
verbose_i = verbose.to_i config.verbose = true
verbose_i = 0 if verbose.to_i < 0
verbose_i = 3 if verbose.to_i > 3
config.verbosity = ::Log::Severity.from_value(3 - verbose_i)
rescue ex: ArgumentError
Log.error { "Wrong value for parameter --verbosity" }
exit 1
end end
opts.on("-h", "--help", "Show this help") do opts.on("-h", "--help", "Show this help") do
Log.notice { opts } Log.info { opts }
exit exit
end end
@ -56,17 +47,15 @@ module DocMachine
end end
parser.parse(args) parser.parse(args)
::Log.setup(config.verbosity, ::Log::IOBackend.new(formatter: BaseFormat))
Log.notice { "verbosity level = #{config.verbosity}" }
Log.debug { "commands = #{commands}" } Log.debug { "commands = #{commands}" }
if commands.size < 1 if commands.size < 1
Log.error { parser.to_s } Log.error { parser.to_s }
Log.error { "No command defined" } Log.error { "ERROR: no command defined" }
end end
commands.each do |command| commands.each do |command|
Log.debug { "== Running #{command}" } # Log.info { "== Running #{command}" }
command.call() command.call()
end end
end end

View file

@ -5,7 +5,7 @@ require "./module"
module DocMachine module DocMachine
class Config class Config
property verbosity = ::Log::Severity::Notice property verbose : Bool = false
def initialize def initialize
end end

View file

@ -1,38 +0,0 @@
require "log"
require "colorize"
struct DebugFormat < Log::StaticFormatter
def run
string @entry.severity.label[0].downcase
string ": "
source
string ": "
message
end
end
struct BaseFormat < Log::StaticFormatter
def run
io = ::IO::Memory.new
color = case @entry.severity
when ::Log::Severity::Error
Colorize.colorize.red.bold
when ::Log::Severity::Warn
Colorize.colorize.red.yellow
when ::Log::Severity::Notice
Colorize.colorize.bold
else
Colorize.colorize
end
color.surround(io) do
io << @entry.message
end
string io.to_s
end
end
# Log.define_formatter BaseFormat, "#{severity.to_s.lstrip}(#{source}): #{message}"

View file

@ -1,9 +1,10 @@
require "./cli" require "./cli"
require "./log"
Log.define_formatter BaseFormat, "#{message}"
::Log.setup(:notice, Log::IOBackend.new(formatter: BaseFormat)) ::Log.setup(:notice, Log::IOBackend.new(formatter: BaseFormat))
::Log.progname = "(root)"
app = DocMachine::Cli.new app = DocMachine::Cli.new
app.start(ARGV) app.start(ARGV)

View file

@ -1,9 +1,9 @@
require "./config" require "./config"
require "./run" require "./run"
require "./module"
module DocMachine::Write module DocMachine::Write
Log = DocMachine::Log.for("write")
class Cli class Cli
Log = DocMachine::Write::Log.for("cli") Log = DocMachine::Write::Log.for("cli")
@ -11,31 +11,22 @@ module DocMachine::Write
config = Config.new(parent_config) config = Config.new(parent_config)
opts.on("write", "Write content target for plan (beta)") do opts.on("write", "Write content target for plan (beta)") do
opts.banner = "Usage: #{PROGRAM_NAME} write [options] TARGET" opts.banner = "Usage: #{PROGRAM_NAME} scaffold [options] TARGET"
opts.on("-f", "--force", "Don't ask for confirmation") do opts.on("-f", "--force", "Don't ask for confirmation") do
config.force = true config.force = true
end end
opts.on("-t", "--template TEMPLATE", "Use given template") do |template_name|
config.template_name = template_name
end
commands << ->() : Nil do commands << ->() : Nil do
Log.debug { "before any" }
if args.size < 1 if args.size < 1
Log.error { "No target given!" } Log.error { "ERROR: No target given!" }
exit 1 exit 1
end end
config.target_directory = args[0] config.target_directory = args[0]
Log.debug { "before new" }
app = DocMachine::Write::Run.new(config) app = DocMachine::Write::Run.new(config)
Log.debug { "before prepare" }
app.prepare app.prepare
Log.debug { "before start" }
app.start app.start
Log.debug { "before wait" }
app.wait app.wait
end end
end end

View file

@ -6,7 +6,6 @@ module DocMachine::Write
property target_directory : String = "." property target_directory : String = "."
property force : Bool = false property force : Bool = false
property template_name : String = ""
def initialize(@parent : DocMachine::Config) def initialize(@parent : DocMachine::Config)
end end

View file

@ -1,7 +0,0 @@
require "baked_file_system"
class FileStorage
extend BakedFileSystem
bake_folder "../../data/prompts"
end

View file

@ -1,6 +0,0 @@
require "../module"
module DocMachine::Write
Log = DocMachine::Log.for("write")
end

View file

@ -1,6 +0,0 @@
require "../module"
module DocMachine::Write::Nodes
Log = DocMachine::Write::Log.for("nodes")
end

View file

@ -1,46 +0,0 @@
require "./module"
module DocMachine::Write::Nodes
class RootNode
property context : String = ""
property audience : String = ""
property goals : String = ""
property constraints : String = ""
property chapters = [] of ChapterNode
def build_chapters()
[] of ChapterNode
end
end
class ChapterNode
property sections = [] of SectionNode
def build_sections()
[] of SectionNode
end
end
class SectionNode
property subsections = [] of SubsectionNode
def build_subsections()
[] of SubsectionNode
end
end
class SubsectionNode
property content = [] of String
def build_content()
[] of String
end
def fix_content()
[] of String
end
end
end

View file

@ -1,178 +1,60 @@
# Core # Core
require "file_utils" require "file_utils"
require "path"
# Internal # Internal
require "./config" require "./config"
require "./nodes/root_node"
# Shards # Shards
require "term-prompt" require "term-prompt"
require "crinja"
module DocMachine::Write module DocMachine::Write
class Run class Run
private property config : DocMachine::Write::Config private property config : DocMachine::Write::Config
property root = Nodes::RootNode.new
def initialize(@config) def initialize(@config)
end end
def validate_build_dir() # Verify parameters
def prepare()
if ! File.directory? @config.target_directory if ! File.directory? @config.target_directory
Log.error { "Target must be a directory" } Log.error { "ERROR: target must be a directory" }
exit 1 exit 1
end end
Log.info { "Target directory: #{@config.target_directory}" } 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 end
def ask_confirmation def start()
# if !@config.force Log.info { "== Writeing #{@config.target_directory}" }
# prompt = Term::Prompt.new p = Path.new(@config.target_directory)
# confirm = prompt.no?("Are you sure you want to proceed?") cwd = Dir.current
# exit 1 if !confirm ["docs", "slides", "images"].each do |dir|
# end p_sub = p.join(dir)
Log.info { "-- creating #{p_sub}" }
FileUtils.mkdir_p(p_sub)
end end
["docs", "slides"].each do |dir|
def load_templates p_sub = p.join(dir)
pp @config FileUtils.cd(p_sub)
Log.info { "-- creating link to images in #{p_sub}" }
context_file = Path[@config.target_directory] / "CONTEXT.tpl" if File.symlink? "images"
if ! File.exists? context_file FileUtils.rm "images"
raise "Context file #{context_file} is missing"
end end
@root.context = File.read(context_file) FileUtils.ln_sf(Path.new("..","images"), Path.new("images"))
FileUtils.cd(cwd)
audience_file = Path[@config.target_directory] / "AUDIENCE.tpl"
if ! File.exists? audience_file
raise "Audience file #{audience_file} is missing"
end end
@root.audience = File.read(audience_file) Log.info { "-- creating README.md" }
FileUtils.touch("README.md")
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 end
# Verify parameters # 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() def wait()
end end
end end