Compare commits

...

3 commits

21 changed files with 501 additions and 218 deletions

View file

@ -29,7 +29,6 @@ Eve se balade dans les jardins d'Eden. Le soleil est haut dans le ciel.
@@before
Eve a chaud. Elle se refraichirait bien dans la riviere.
Elle descend dans
@@after
Elle descend dans @@after
This text will not be used in the chat

91
specs/test_data/course.md Normal file
View file

@ -0,0 +1,91 @@
@@systeme
## Contexte
Un cours d'excellent niveau et adapté pour le MIT, l'université de Standford, l'Ecole Polytechinque, l'ENS Ulm.
Le professeur est un expert SSL/TLS avec 20 ans d'expérience.
## Objectifs
* Comprendre la theorie qui permet le fonctionnement de SSL/TLS
* Savoir utiliser openssl et configurer SSL/TLS dans les logiciels les plus répandus (apache, nginx, etc.)
* Connaitre les différentes bibliotheques SSL/TLS disponibles et savoir les utiliser dans le code.
## Public
* Des développeurs applicatif.
@@before
## Programme de formation
1. Introduction
1.1 Qu'est-ce que SSL/TLS ?
- Définition de SSL/TLS
- Fonctionnement général de SSL/TLS
1.2 Pourquoi utiliser SSL/TLS ?
- Les avantages de l'utilisation d'un protocole sécurisé comme SSL/TLS
- Les risques associés à l'utilisation d'une connexion non-sécurisée
1.3 Histoire de SSL/TLS
- Origine et évolution du protocole TLS/SSL au fil du temps.
2. Cryptographie asymétrique et symétrique
2.1 Principes de base de la cryptographie asymétrique et symétrique
2.2 Fonctionnement des algorithmes de chiffrement symétriques (AES, DES, etc.)
2.3 Fonctionnement des algorithmes de chiffrement asymétriques (RSA, DH, etc.)
3. Les protocoles SSL/TLS
3.1 TLS 1.x vs TLS 2.x : différences fondamentales
3.2 Protocole Handshake : établissement d'une connexion sécurisée
3.3 Protocole Record : échange de données entre les parties en toute sécurité
4.Configuration d'un serveur web sécurisé avec Apache ou Nginx
4.A Configuration d'Apache avec mod_ssl
4.B Configurationde Nginx avec ssl_module
5.Utilisation du logiciel OpenSSL pour le test et le débuggage.
5.A Utilisation basique: creation CA/Server Certificat/Clefs
5.B Utilisation avancée : révocation de certificat, création de certificat auto-signé, utilisation d'extensions personnalisées.
6. Les bibliothèques SSL/TLS
6.A Présentation des différentes bibliothèques disponibles (OpenSSL, GnuTLS, NSS, etc.)
6.B Utilisation de la bibliothèque OpenSSL dans le code C/Python
7. Sécurité et bonnes pratiques en matière de SSL/TLS
7.A Comprendre les principales vulnérabilités associées à SSL/TLS (POODLE, Heartbleed, etc.)
7.B Bonnes pratiques pour renforcer la sécurité du protocole.
8. Conclusion
8.A Récapitulation des principaux points abordés dans ce cours.
8.B Perspectives d'avenir pour SSL/TLS et la sécurité en ligne.
### Request
Merci.
Suit la structure du PROGRAMME DE FORMATION et concentre toi sur le chapitre « 1. Introduction ».
Focalise toi plus spécifiquement sur les sections suivantes de ce chapitre:
«
1.1 Qu'est-ce que SSL/TLS ?
1.2 Pourquoi utiliser SSL/TLS ?
1.3 Histoire de SSL/TLS
»
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.
### Answer
Voici la table des matières détaillée pour les sections 1.1, 1.2 et 1.3:
#### 1. Introduction
#### Travaux pratiques possibles :
- Installation et configuration d'un serveur Apache ou Nginx avec le module mod_ssl pour la prise en charge de HTTPS sur un site web simple.
- Générer une paire clé / certificat auto-signé avec OpenSSL ;
- Mettre en place Certbot (Lets encrypt) pour obtenir gratuitement un certificat valide sur votre domaine et intégrer ce dernier dans votre configuration Apache/Nginx@@after

View file

@ -1,6 +0,0 @@
module Builder
abstract class PromptGeneric
abstract def build(prompt : Prompt)
end
end

14
src/config.cr Normal file
View file

@ -0,0 +1,14 @@
require "./openai_config"
class AppConfig
property input_file = STDIN
property input_file_path = ""
property output_file = STDOUT
property output_file_path = ""
property past_characters_limit = 1000
property use_color = true
property make_request = true
property verbose = false
property gpt_config = AIUtils::GptConfig.new
end

View file

@ -0,0 +1,6 @@
module ContextBuilder
abstract class GenericContextBuilder
abstract def build(prompt : Prompt)
end
end

View file

@ -14,10 +14,12 @@ module Builder
# skip prelude_zone
# skip future_zone
def build(prompt : Prompt) : Chat
def build(prompt : Prompt, context_token_limit : UInt32) : Chat
chat = [] of Message
token_limit = 2_700
# token_limit = 10_000
token_limit = (0.65 * context_token_limit).to_i
STDERR.puts "messages_token_limit = #{token_limit}"
mandatory_token_count = (
prompt.system_zone.token_count +
prompt.present_zone.token_count

View file

@ -4,8 +4,7 @@ require "colorize"
module Builder
class OpenAIInsert
alias Message = NamedTuple(role: String, content: String)
alias Chat = Array(Message)
alias InsertBuffer = NamedTuple(before: String, after: String)
getter verbose : Bool
def initialize(@verbose)
@ -14,8 +13,8 @@ module Builder
# skip prelude_zone
# skip future_zone
def build(prompt : Prompt) : Chat
chat = [] of Message
def build(prompt : Prompt) : InsertBuffer
insert_data : InsertBuffer = { before: "", after: "" }
token_limit = 2_900
mandatory_token_count = (
@ -25,7 +24,7 @@ module Builder
## Build mandatory system messages
prompt.system_zone.each do |content|
chat << { role: "system", content: content }
insert_data["before"] += content
end
## Build mandatory system messages

View file

@ -0,0 +1,45 @@
require "colorize"
require "../config"
require "./generic_builder"
module Builder
class PromptDir < PromptGeneric
SEPARATOR = "\n\n"
getter use_color : Bool
def initialize(@use_color)
Colorize.enabled = @use_color
end
def build(prompt : Prompt)
str = ""
str += prompt.prelude_zone.content.join(SEPARATOR)
if ! prompt.system_zone.content.empty?
str += "@@system".colorize(:yellow).to_s
str += prompt.system_zone.content.join(SEPARATOR)
end
if ! prompt.past_zone.content.empty?
str += "@@before".colorize(:yellow).to_s
str += prompt.past_zone.content.join(SEPARATOR)
end
if ! prompt.present_zone.content.empty?
# str += "@@current".colorize(:yellow).to_s
str += prompt.present_zone.content.join(SEPARATOR).colorize(:light_cyan).to_s
end
if ! prompt.future_zone.content.empty?
str += "@@after".colorize(:yellow).to_s
str += prompt.future_zone.content.join("\n")
end
str
end
end
end

View file

@ -1,10 +1,10 @@
require "colorize"
require "./generic"
require "./generic_builder"
module Builder
class PromptString < PromptGeneric
module ContextBuilder
class PromptString < GenericContextBuilder
SEPARATOR = "\n\n"
@ -29,7 +29,7 @@ module Builder
end
if ! prompt.present_zone.content.empty?
str += "@@current".colorize(:yellow).to_s
# str += "@@current".colorize(:yellow).to_s
str += prompt.present_zone.content.join(SEPARATOR).colorize(:light_cyan).to_s
end

View file

@ -3,204 +3,15 @@ require "pretty_print"
require "openai"
require "spinner"
require "./zone"
require "./prompts/zone"
require "./parsers/prompt_string"
require "./builders/prompt_string"
require "./builders/openai_chat"
require "./builders/openai_insert"
require "./context_builders/prompt_string"
# require "./context_builders/prompt_dir"
require "./context_builders/openai_chat"
require "./context_builders/openai_insert"
require "./storyteller"
class Storyteller
storyteller = Storyteller.new()
storyteller.prepare()
storyteller.run()
enum OpenAIMode
Chat
Complete
Insert
def self.from_s(str_mode)
return OpenAIMode.values.find do |openai_mode|
openai_mode.to_s.downcase == str_mode.downcase
end
end
end
def initialize()
end
def self.start(argv)
input_file = STDIN
input_file_path = ""
output_file = STDOUT
output_file_path = ""
past_characters_limit = 1000
use_color = true
make_request = true
gpt_mode = OpenAIMode::Chat
verbose = false
gpt_temperature = 0.82
gpt_presence_penalty = 1
gpt_frequency_penalty = 1
gpt_max_tokens = 256
parser = OptionParser.new do |parser|
parser.banner = "Usage: storyteller [options]"
parser.separator("Options:")
parser.on("-i FILE", "--input=FILE", "Path to input file") do |file|
input_file_path = file
end
parser.on("-h", "--help", "Show this help") do
puts parser
exit
end
parser.on("-n", "--no-color", "Disable color output") do
use_color = false
end
parser.on("-o FILE", "--output=FILE", "Path to output file") do |file|
use_color = false
output_file_path = file
end
parser.on("-v", "--verbose", "Be verbose (cumulative)") do
verbose = true
end
parser.on("--dry-run", "Don't call the API") do
make_request = false
end
parser.separator("GPT options")
parser.on("--gpt-mode MODE", "GPT mode (chat,insert,complete) (default: chat)") do |chosen_mode|
result_mode = OpenAIMode.from_s(chosen_mode.downcase)
if result_mode.nil?
STDERR.puts "ERROR: unknown mode #{chosen_mode}"
exit 1
end
gpt_mode = result_mode unless result_mode.nil?
end
parser.on("--gpt-temperature TEMPERATURE", "GPT Temperature") do |temperature|
gpt_temperature = temperature
end
parser.on("--gpt-presence-penalty PENALTY", "GPT Presence Penalty") do |presence_penalty|
gpt_presence_penalty = presence_penalty
end
parser.on("--gpt-frequency-penalty PENALTY", "GPT Frequency Penalty") do |frequency_penalty|
gpt_frequency_penalty = frequency_penalty
end
parser.on("--gpt-max-tokens TOKENS", "GPT Max Tokens") do |max_tokens|
gpt_max_tokens = max_tokens
end
end
parser.parse(ARGV)
# Create Storyteller instance
storyteller = Storyteller.new()
# Read file and initialize zones
if !input_file_path.empty?
# puts "d: Using input file #{input_file_path}"
input_file = File.open(input_file_path)
end
prompt = storyteller.read_file(input_file)
input_file.close
# Build GPT-3 request
prompt = storyteller.complete(prompt, make_request, verbose)
pp prompt if verbose
exit 0 if !make_request
if !output_file_path.empty?
# puts "d: Using output file #{input_file_path}"
output_file = File.open(output_file_path, "w")
end
storyteller.write_file(output_file, prompt, use_color)
output_file.close
rescue ex : OptionParser::InvalidOption
STDERR.puts parser
STDERR.puts "\nERROR: #{ex.message}"
exit 1
end
def complete(prompt : Prompt, make_request : Bool, verbose : Bool)
builder = Builder::OpenAIChat.new(verbose: verbose)
messages = builder.build(prompt)
STDERR.puts messages if verbose
return prompt if !make_request
channel_ready = Channel(Bool).new
channel_tick = Channel(Bool).new
# sp = Spin.new(0.5, Spinner::Charset[:progress])
spawn do
tick = 0
loop do
tick += 1
print "."
if tick > (3 * 60)
print "(timeout)"
exit 1
end
channel_tick.send(true)
sleep 1.seconds
end
end
spawn do
openai = OpenAI::Client.new(access_token: ENV.fetch("OPENAI_API_KEY"))
result = openai.chat(
"gpt-3.5-turbo",
messages,
{
"temperature" => 0.82,
"presence_penalty" => 1,
"frequency_penalty" => 1,
"max_tokens" => 256
}
)
prompt.past_zone.content << "\n" + result.choices.first["message"]["content"] + "\n"
channel_ready.send(true)
end
# sp.start
channel_ready.receive
channel_ready.close
# sp.stop
prompt
end
def read_file(input_file : IO::FileDescriptor)
content = input_file.gets_to_end
# puts "d: building parser"
parser = Parser::PromptString.new
# puts "d: parsing"
prompt = parser.parse(content)
# pp prompt
end
def write_file(output_file : IO::FileDescriptor, prompt : Prompt, use_color : Bool)
# STDERR.puts "d: building builder"
builder = Builder::PromptString.new(use_color)
# STDERR.puts "d: building"
text = builder.build(prompt)
output_file.write_string(text.to_slice)
end
def display_completion(completion : String)
# Code pour afficher la complétion
end
end
Storyteller.start(ARGV)

11
src/modifiers/readme.md Normal file
View file

@ -0,0 +1,11 @@
Des classes qui modifient un prompt d'apres des contraintes
truncate(max_tokens) # le prompt aux X derniers tokens
summarize() # réduis la longueur du prompt
worldinfo() # introduis les éléments du monde

64
src/openai_config.cr Normal file
View file

@ -0,0 +1,64 @@
module AIUtils
enum OpenAIModel
Gpt_4
Gpt_3_5_Turbo
Gpt_3_5_Turbo_16k
def to_s
vals = [
"gpt-4",
"gpt-3.5-turbo",
"gpt-3.5-turbo-16k"
]
vals[self.value]
end
def self.from_s?(str_model : String) : OpenAIModel?
return OpenAIModel.values.find do |openai_model|
openai_model.to_s.downcase == str_model.downcase
end
end
end
enum OpenAIMode
Chat
Complete
Insert
def self.from_s?(str_mode) : OpenAIMode?
return OpenAIMode.parse?(str_mode)
# return OpenAIMode.values.find do |openai_mode|
# openai_mode.to_s.downcase == str_mode.downcase
# end
end
end
def self.context_token_limit(model : OpenAIModel) : UInt32
hash = {
OpenAIModel::Gpt_3_5_Turbo => 1024_u32 * 4_u32,
OpenAIModel::Gpt_3_5_Turbo_16k => 1024_u32 * 16_u32,
OpenAIModel::Gpt_4 => 1024_u32 * 8_u32,
}
return 0_u32 unless hash[model]?
return hash[model]
end
class GptConfig
# defaults
# property temperature = 0.82
property model : OpenAIModel = OpenAIModel::Gpt_3_5_Turbo
# property model = "gpt-3.5-turbo-16k"
# property prompt_size = 10_000
property prompt_size = 2_700
property temperature = 0.8
property presence_penalty = 1.0
property frequency_penalty = 1.0
property max_tokens = 384
property mode = OpenAIMode::Chat
property openai_key : String = ENV.fetch("OPENAI_API_KEY")
end
end

View file

@ -1,5 +1,5 @@
require "../prompt"
require "../prompts/generic_prompt"
module Parser
class PromptString

7
src/requests/chat.cr Normal file
View file

@ -0,0 +1,7 @@
require "./generic_request"
module Requests
class ChatRequest < GenericRequest
end
end

8
src/requests/complete.cr Normal file
View file

@ -0,0 +1,8 @@
require "./generic_request"
module Requests
class CompleteRequest < GenericRequest
end
end

View file

@ -0,0 +1,23 @@
module Requests
class GenericRequest
getter context_size : Int32 = 0
def initialize(@config : AIUtils::GptConfig)
end
def perform(messages)
openai = OpenAI::Client.new(access_token: @config.openai_key)
result = openai.chat(
@config.model.to_s,
messages,
{
"temperature" => @config.temperature,
"presence_penalty" => @config.presence_penalty,
"frequency_penalty" => @config.frequency_penalty,
"max_tokens" => @config.max_tokens,
}
)
end
end
end

8
src/requests/insert.cr Normal file
View file

@ -0,0 +1,8 @@
require "./generic_request"
module Requests
class InsertRequest < GenericRequest
end
end

201
src/storyteller.cr Normal file
View file

@ -0,0 +1,201 @@
require "./config"
require "./requests/chat"
class Storyteller
property config = AppConfig.new
def initialize()
end
def prepare()
self._parse_config()
self._parse_command_line(ARGV)
end
private def _parse_config()
end
private def _parse_command_line(args)
parser = OptionParser.new do |parser|
parser.banner = "Usage: storyteller [options]"
parser.separator("Options:")
parser.on("-i FILE", "--input=FILE", "Path to input file") do |file|
@config.input_file_path = file
end
parser.on("-h", "--help", "Show this help") do
puts parser
exit
end
parser.on("-n", "--no-color", "Disable color output") do
@config.use_color = false
end
parser.on("-o FILE", "--output=FILE", "Path to output file") do |file|
@config.use_color = false
@config.output_file_path = file
end
parser.on("-v", "--verbose", "Be verbose (cumulative)") do
@config.verbose = true
end
parser.on("--dry-run", "Don't call the API") do
@config.make_request = false
end
parser.separator("GPT options")
parser.on("--gpt-model MODEL", "GPT model (...) (default: gpt-3.5-turbo)") do |chosen_model|
result_model = AIUtils::OpenAIModel.from_s?(chosen_model.downcase)
if result_model.nil?
STDERR.puts "ERROR: unknown model #{chosen_model}"
exit 1
end
@config.gpt_config.model = result_model unless result_model.nil?
end
# parser.on("--gpt-mode MODE", "GPT mode (chat,insert,complete) (default: chat)") do |chosen_mode|
# result_mode = AIUtils::OpenAIMode.from_s(chosen_mode.downcase)
# if result_mode.nil?
# STDERR.puts "ERROR: unknown mode #{chosen_mode}"
# exit 1
# end
# @config.gpt_config.mode = result_mode unless result_mode.nil?
# end
parser.on("--gpt-temperature TEMPERATURE", "GPT Temperature (default #{@config.gpt_config.temperature})") do |temperature|
@config.gpt_config.temperature = temperature.to_f
end
parser.on("--gpt-presence-penalty PENALTY", "GPT Presence Penalty (default #{@config.gpt_config.presence_penalty})") do |presence_penalty|
@config.gpt_config.presence_penalty = presence_penalty.to_f
end
parser.on("--gpt-frequency-penalty PENALTY", "GPT Frequency Penalty (default #{@config.gpt_config.frequency_penalty})") do |frequency_penalty|
@config.gpt_config.frequency_penalty = frequency_penalty.to_f
end
parser.on("--gpt-max-tokens TOKENS", "GPT Max Tokens (default #{@config.gpt_config.max_tokens})") do |max_tokens|
@config.gpt_config.max_tokens = max_tokens.to_i
end
end
parser.parse(args)
end
def load_file()
# Read file and initialize zones
if !@config.input_file_path.empty?
# puts "d: Using input file #{input_file_path}"
@config.input_file = File.open(@config.input_file_path)
end
prompt = self.read_file(@config.input_file)
@config.input_file.close
prompt
end
def run()
original_prompt = self.load_file()
# Build GPT-3 request
prompt = self.complete(original_prompt, @config.make_request, @config.verbose)
pp prompt if @config.verbose
exit 0 if !@config.make_request
if !@config.output_file_path.empty?
# puts "d: Using output file #{input_file_path}"
@config.output_file = File.open(@config.output_file_path, "w")
end
self.write_file(@config.output_file, prompt, @config.use_color)
@config.output_file.close
end
def complete(prompt : Prompt, make_request : Bool, verbose : Bool)
builder = Builder::OpenAIChat.new(verbose: verbose)
context_token_limit = AIUtils.context_token_limit(@config.gpt_config.model)
messages = builder.build(prompt, context_token_limit)
STDERR.puts "model = #{@config.gpt_config.model}"
STDERR.puts "context_token_limit = #{context_token_limit}"
STDERR.puts messages if verbose
return prompt if !make_request
channel_ready = Channel(Bool).new
channel_tick = Channel(Bool).new
# sp = Spin.new(0.5, Spinner::Charset[:progress])
spawn do
tick = 0
loop do
tick += 1
print "."
if tick > (2 * 60)
print "(timeout)"
exit 1
end
# channel_tick.send(true)
sleep 1.seconds
end
end
# TODO:
# request = Request::Chat.new(config: @config.gpt_config)
# result = request.perform(message)
spawn do
request = Requests::ChatRequest.new(config: @config.gpt_config)
result = request.perform(messages)
pp result.choices if @config.verbose
prompt.present_zone.content << result.choices.first["message"]["content"]
channel_ready.send(true)
rescue ex: KeyError
puts "(openai error)"
STDERR.puts "ERROR: #{ex.message}"
exit 1
rescue ex: IO::Error
puts "(network error)"
STDERR.puts "ERROR: #{ex.message}"
exit 1
rescue ex: Socket::Addrinfo::Error
puts "(network error)"
STDERR.puts "ERROR: #{ex.message}"
exit 1
end
# sp.start
channel_ready.receive
channel_ready.close
# sp.stop
prompt
end
def read_file(input_file : IO::FileDescriptor)
content = input_file.gets_to_end
# puts "d: building parser"
parser = Parser::PromptString.new
# puts "d: parsing"
prompt = parser.parse(content)
# pp prompt
end
def write_file(output_file : IO::FileDescriptor, prompt : Prompt, use_color : Bool)
# STDERR.puts "d: building builder"
builder = ContextBuilder::PromptString.new(use_color)
# STDERR.puts "d: building"
text = builder.build(prompt)
output_file.write_string(text.to_slice)
end
def display_completion(completion : String)
# Code pour afficher la complétion
end
end