diff --git a/CHANGELOG.md b/CHANGELOG.md index 6872856..707f8eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Added + +* Add command parser in text interactive mode +* Add command parser in translate interactive mode + ## [0.6.0] - 2024-10-16 ### Changed diff --git a/README.md b/README.md index a11f4b8..438ff8a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Projet to use ai api to generate text, image, etc. - claude - mistral +### Image Generation + +- openai + ### Translation - deepl @@ -94,6 +98,7 @@ Add the following line to your `.vimrc`: Plug 'LordPax/vim-aicli' ``` + diff --git a/commands/text.go b/commands/text.go index 2fd7c33..bf092d4 100644 --- a/commands/text.go +++ b/commands/text.go @@ -212,9 +212,7 @@ func textFlags() []cli.Flag { Category: "history", Action: func(c *cli.Context, value bool) error { text := sdk.GetSdkText() - for _, name := range text.GetHistoryNames() { - fmt.Println(name) - } + text.ListHistoryNames() os.Exit(0) return nil }, diff --git a/lang/en.go b/lang/en.go index aa5f540..0cd2d06 100644 --- a/lang/en.go +++ b/lang/en.go @@ -3,39 +3,58 @@ package lang import "github.com/LordPax/aicli/utils" var EN_STRINGS = LangString{ - "usage": "CLI toot to use ai model", - "output-desc": "Output directory", - "output-dir-empty": "Output directory is empty", - "silent": "Disable printing log to stdout", - "no-args": "No arguments provided", - "no-command": "No command provided", - "not-implemented": "Feature \"%s\" is not implemented", - "unknown-sdk": "Unknown sdk \"%s\"", - "sdk-model-usage": "Select a model", - "inerte-usage": "Do not make API call", - "text-usage": "Generate text from a prompt", - "sdk-usage": "Select a sdk", - "text-temp-usage": "Set temperature", - "text-system-usage": "Instruction to enter as context (use \"-\" for stdin)", - "text-history-usage": "Select a history", - "text-clear-usage": "Clear history", - "text-file-usage": "Text or image file to use", - "text-input": "(\"exit\" to quit) " + utils.Blue + "user> " + utils.Reset, - "translate-input": "(\"exit\" to quit) " + utils.Blue + "> " + utils.Reset, - "text-list-history-usage": "List history", - "text-list-history-name-usage": "List history names", - "text-url-usage": "URL of a web page", - "type-required": "Type is required", - "api-key-required": "API key is required", - "empty-file": "File \"%s\" is empty", - "empty-history": "History \"%s\" is empty\n", - "translate-usage": "Translate a text", - "translate-source-usage": "Source language", - "translate-target-usage": "Target language", - "translate-target-required": "Target language is required", - "image-usage": "Generate an image from a prompt", - "image-size-usage": "Set the size of the image", - "image-nb-usage": "Set the number of images", - "image-output-usage": "Set the name of the output file", - "image-save": "Image saved under \"%s\"\n", + "unknown-command": "Unknown command \"%s\"", + "text-history-selected": "History selected \"%s\"\n", + "text-history-cleared": "History \"%s\" cleared\n", + "text-command-help-usage": "/help Show help", + "text-command-exit-usage": "/exit, /e Exit the program", + "text-command-sdk-usage": "/sdk, /s [name] Select a sdk", + "text-command-model-usage": "/model, /m [name] Select a model", + "text-command-temp-usage": "/temp, /t [value] Set temperature", + "text-command-history-usage": "/history, /h [name] Select a history", + "text-command-clear-usage": "/clear, /c Clear history", + "text-command-show-usage": "/show, Affiche l'historique", + "usage": "CLI toot to use ai model", + "output-desc": "Output directory", + "output-dir-empty": "Output directory is empty", + "silent": "Disable printing log to stdout", + "no-args": "No arguments provided", + "no-command": "No command provided", + "not-implemented": "Feature \"%s\" is not implemented", + "unknown-sdk": "Unknown sdk \"%s\"", + "sdk-model-usage": "Select a model", + "inerte-usage": "Do not make API call", + "text-usage": "Generate text from a prompt", + "sdk-usage": "Select a sdk", + "text-temp-usage": "Set temperature", + "text-system-usage": "Instruction to enter as context (use \"-\" for stdin)", + "text-history-usage": "Select a history", + "text-clear-usage": "Clear history", + "text-file-usage": "Text or image file to use", + "text-input": "(\"/help\" to show help) " + utils.Blue + "user> " + utils.Reset, + "translate-input": "(\"/help\" to show help) " + utils.Blue + "> " + utils.Reset, + "text-list-history-usage": "List history", + "text-list-history-name-usage": "List history names", + "text-url-usage": "URL of a web page", + "type-required": "Type is required", + "api-key-required": "API key is required", + "empty-file": "File \"%s\" is empty", + "empty-history": "History \"%s\" is empty\n", + "translate-usage": "Translate a text", + "translate-source-usage": "Source language", + "translate-target-usage": "Target language", + "translate-target-required": "Target language is required", + "translate-detect": "Detect source language", + "translate-source-set": "Source language set to \"%s\"", + "translate-target-set": "Target language set to \"%s\"", + "translate-command-help-usage": "/help Show help", + "translate-command-exit-usage": "/exit, /e Exit the program", + "translate-command-sdk-usage": "/sdk, /s [name] Select a sdk", + "translate-command-source-usage": "/source, /so [lang] Set the source language (\"auto\" to detect)", + "translate-command-target-usage": "/target, /ta [lang] Set the target language", + "image-usage": "Generate an image from a prompt", + "image-size-usage": "Set the size of the image", + "image-nb-usage": "Set the number of images", + "image-output-usage": "Set the name of the output file", + "image-save": "Image saved under \"%s\"\n", } diff --git a/lang/fr.go b/lang/fr.go index 34997e0..c3f0c27 100644 --- a/lang/fr.go +++ b/lang/fr.go @@ -3,39 +3,58 @@ package lang import "github.com/LordPax/aicli/utils" var FR_STRINGS = LangString{ - "usage": "CLI pour utiliser des modèles d'IA", - "output-desc": "Répertoire de sortie", - "output-dir-empty": "Le répertoire de sortie est vide", - "silent": "Désactiver l'impression du journal sur stdout", - "no-args": "Aucun argument fourni", - "no-command": "Aucune commande fournie", - "not-implemented": "La fonctionnalité \"%s\" n'est pas implémentée", - "unknown-sdk": "Sdk inconnu \"%s\"", - "sdk-model-usage": "Sélectionner un modèle", - "inerte-usage": "N'effectue pas d'appel à l'API", - "text-usage": "Générer du texte à partir d'un prompt", - "sdk-usage": "Sélectionner un sdk", - "text-temp-usage": "Définir la température", - "text-system-usage": "Instruction à entrer comme context (utilisez \"-\" pour stdin)", - "text-history-usage": "Sélectionner un historique", - "text-clear-usage": "Effacer l'historique", - "text-file-usage": "Fichier texte ou image à utiliser", - "text-input": "(\"exit\" pour quitter) " + utils.Blue + "user> " + utils.Reset, - "translate-input": "(\"exit\" pour quitter) " + utils.Blue + "> " + utils.Reset, - "text-list-history-usage": "Lister l'historique", - "text-list-history-name-usage": "Lister les noms d'historique", - "text-url-usage": "URL d'une page web", - "type-required": "Le type est requis", - "api-key-required": "La clé API est requise", - "empty-file": "Le fichier \"%s\" est vide", - "empty-history": "L'historique \"%s\" est vide\n", - "translate-usage": "Traduire un texte", - "translate-source-usage": "Langue source", - "translate-target-usage": "Langue cible", - "translate-target-required": "La langue cible est requise", - "image-usage": "Générer une image à partir d'un prompt", - "image-size-usage": "Définir la taille de l'image", - "image-nb-usage": "Définir le nombre d'images", - "image-output-usage": "Définir le nom du fichier de sortie", - "image-save": "L'image a été enregistrée sous \"%s\"\n", + "unknown-command": "Commande inconnue \"%s\"", + "text-history-selected": "Historique sélectionné \"%s\"\n", + "text-history-cleared": "Historique \"%s\" effacé\n", + "text-command-help-usage": "/help Afficher l'aide", + "text-command-exit-usage": "/exit, /e Quitter le programme", + "text-command-sdk-usage": "/sdk, /s [nom] Sélectionner un sdk", + "text-command-model-usage": "/model, /m [nom] Sélectionner un modèle", + "text-command-temp-usage": "/temp, /t [valeur] Définir la température", + "text-command-history-usage": "/history, /h [nom] Sélectionner un historique et liste les noms d'historique", + "text-command-clear-usage": "/clear, /c Effacer l'historique", + "text-command-show-usage": "/show, Affiche l'historique", + "usage": "CLI pour utiliser des modèles d'IA", + "output-desc": "Répertoire de sortie", + "output-dir-empty": "Le répertoire de sortie est vide", + "silent": "Désactiver l'impression du journal sur stdout", + "no-args": "Aucun argument fourni", + "no-command": "Aucune commande fournie", + "not-implemented": "La fonctionnalité \"%s\" n'est pas implémentée", + "unknown-sdk": "Sdk inconnu \"%s\"", + "sdk-model-usage": "Sélectionner un modèle", + "inerte-usage": "N'effectue pas d'appel à l'API", + "text-usage": "Générer du texte à partir d'un prompt", + "sdk-usage": "Sélectionner un sdk", + "text-temp-usage": "Définir la température", + "text-system-usage": "Instruction à entrer comme context (utilisez \"-\" pour stdin)", + "text-history-usage": "Sélectionner un historique", + "text-clear-usage": "Effacer l'historique", + "text-file-usage": "Fichier texte ou image à utiliser", + "text-input": "(\"/help\" pour afficher l'aide) " + utils.Blue + "user> " + utils.Reset, + "translate-input": "(\"/help\" pour afficher l'aide) " + utils.Blue + "> " + utils.Reset, + "text-list-history-usage": "Lister l'historique", + "text-list-history-name-usage": "Lister les noms d'historique", + "text-url-usage": "URL d'une page web", + "type-required": "Le type est requis", + "api-key-required": "La clé API est requise", + "empty-file": "Le fichier \"%s\" est vide", + "empty-history": "L'historique \"%s\" est vide\n", + "translate-usage": "Traduire un texte", + "translate-source-usage": "Langue source", + "translate-target-usage": "Langue cible", + "translate-target-required": "La langue cible est requise", + "translate-detect": "Détecter la langue source", + "translate-source-set": "Langue source définie sur \"%s\"\n", + "translate-target-set": "Langue cible définie sur \"%s\"\n", + "translate-command-help-usage": "/help Afficher l'aide", + "translate-command-exit-usage": "/exit, /e Quitter le programme", + "translate-command-sdk-usage": "/sdk, /s [nom] Sélectionner un sdk", + "translate-command-source-usage": "/source, /so [lang] Définir la langue source (\"auto\" pour détecter)", + "translate-command-target-usage": "/target, /ta [lang] Définir la langue cible", + "image-usage": "Générer une image à partir d'un prompt", + "image-size-usage": "Définir la taille de l'image", + "image-nb-usage": "Définir le nombre d'images", + "image-output-usage": "Définir le nom du fichier de sortie", + "image-save": "L'image a été enregistrée sous \"%s\"\n", } diff --git a/sdk/history.go b/sdk/history.go index 6b9f79c..409337b 100644 --- a/sdk/history.go +++ b/sdk/history.go @@ -3,6 +3,7 @@ package sdk import ( "encoding/base64" "encoding/json" + "fmt" "os" "path" @@ -22,6 +23,7 @@ type ITextHistory interface { AppendTextMessage(index int, text ...string) Message AppendImageHistory(role, fileType string, file []byte) error GetHistoryNames() []string + ListHistoryNames() } type TextHistory struct { @@ -174,6 +176,16 @@ func (t *TextHistory) GetHistoryNames() []string { return names } +func (t *TextHistory) ListHistoryNames() { + for _, name := range t.GetHistoryNames() { + if name == t.GetSelectedHistory() { + fmt.Println(utils.Green + name + utils.Reset) + continue + } + fmt.Println(name) + } +} + func textContent(text ...string) []IContent { var content []IContent diff --git a/service/text.go b/service/text.go index 7353e87..fa3600a 100644 --- a/service/text.go +++ b/service/text.go @@ -4,6 +4,8 @@ import ( "fmt" "io" "os" + "strconv" + "strings" "github.com/LordPax/aicli/lang" "github.com/LordPax/aicli/sdk" @@ -39,6 +41,10 @@ func SendTextRequest(prompt string) error { func InteractiveMode() error { textSdk := sdk.GetSdkText() l := lang.GetLocalize() + log, err := utils.GetLog() + if err != nil { + return err + } if err := ListHistory(false); err != nil { return err @@ -46,8 +52,13 @@ func InteractiveMode() error { for { input := utils.Input(l.Get("text-input"), "", false) - if input == "exit" { - break + ok, err := ParseTextCommand(input) + if err != nil { + log.PrintfErr("%v\n", err) + continue + } + if ok { + continue } resp, err := textSdk.SendRequest(input) @@ -64,8 +75,100 @@ func InteractiveMode() error { fmt.Println(resp.GetContent()) fmt.Print("\n") } +} - return nil +func ParseTextCommand(command string) (bool, error) { + l := lang.GetLocalize() + + if command[0] != '/' { + return false, nil + } + + values := strings.Split(command, " ") + + switch values[0][1:] { + case "help": + HelpTextCommand() + return true, nil + case "exit", "e": + os.Exit(0) + return true, nil + case "sdk", "s": + textSdk := sdk.GetSdkText() + if len(values) < 2 { + fmt.Println(textSdk.GetName()) + return true, nil + } + + if err := sdk.InitSdkText(values[1]); err != nil { + return true, err + } + + return true, nil + case "model", "m": + textSdk := sdk.GetSdkText() + if len(values) < 2 { + fmt.Println(textSdk.GetModel()) + return true, nil + } + + textSdk.SetModel(values[1]) + + return true, nil + case "temp", "t": + textSdk := sdk.GetSdkText() + if len(values) < 2 { + fmt.Println(textSdk.GetTemp()) + return true, nil + } + + temp, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return true, err + } + + textSdk.SetTemp(temp) + return true, nil + case "history", "h": + textSdk := sdk.GetSdkText() + if len(values) < 2 { + textSdk.ListHistoryNames() + return true, nil + } + + textSdk.SetSelectedHistory(values[1]) + fmt.Printf(l.Get("text-history-selected"), values[1]) + return true, nil + case "show": + if err := ListHistory(true); err != nil { + return true, err + } + + return true, nil + case "clear", "c": + textSdk := sdk.GetSdkText() + textSdk.ClearHistory() + if err := textSdk.SaveHistory(); err != nil { + return true, err + } + + fmt.Printf(l.Get("text-history-cleared"), textSdk.GetSelectedHistory()) + return true, nil + } + + return false, fmt.Errorf(l.Get("unknown-command"), command) +} + +func HelpTextCommand() { + l := lang.GetLocalize() + fmt.Println(l.Get("text-command-help-usage")) + fmt.Println(l.Get("text-command-exit-usage")) + fmt.Println(l.Get("text-command-sdk-usage")) + fmt.Println(l.Get("text-command-model-usage")) + fmt.Println(l.Get("text-command-temp-usage")) + fmt.Println(l.Get("text-command-history-usage")) + fmt.Println(l.Get("text-command-clear-usage")) + fmt.Println(l.Get("text-command-show-usage")) } func ListHistory(showMsg bool) error { diff --git a/service/translate.go b/service/translate.go index 7a11b97..b452b0a 100644 --- a/service/translate.go +++ b/service/translate.go @@ -42,6 +42,10 @@ func TranslateText(text string) error { func TranslateInteractiveMode() error { translateSdk := sdk.GetSdkTranslate() l := lang.GetLocalize() + log, err := utils.GetLog() + if err != nil { + return err + } if translateSdk.GetTargetLang() == "" { return errors.New(l.Get("translate-target-required")) @@ -49,8 +53,13 @@ func TranslateInteractiveMode() error { for { input := utils.Input(l.Get("translate-input"), "", false) - if input == "exit" { - break + ok, err := ParseTranslateCommand(input) + if err != nil { + log.PrintfErr("%v\n", err) + continue + } + if ok { + continue } resp, err := translateSdk.SendRequest(input) @@ -62,6 +71,77 @@ func TranslateInteractiveMode() error { fmt.Println(utils.Red + "> " + utils.Reset + resp) fmt.Print("\n") } +} - return nil +func ParseTranslateCommand(command string) (bool, error) { + l := lang.GetLocalize() + + if command[0] != '/' { + return false, nil + } + + values := strings.Split(command, " ") + + switch values[0][1:] { + case "help": + HelpTranslateCommand() + return true, nil + case "exit", "e": + os.Exit(0) + return true, nil + case "sdk", "s": + translateSdk := sdk.GetSdkTranslate() + if len(values) < 2 { + fmt.Println(translateSdk.GetName()) + return true, nil + } + + if err := sdk.InitSdkTranslate(values[1]); err != nil { + return true, err + } + + return true, nil + case "source", "so": + translateSdk := sdk.GetSdkTranslate() + if len(values) < 2 { + if translateSdk.GetSourceLang() == "" { + fmt.Println(l.Get("translate-detect")) + return true, nil + } + + fmt.Println(translateSdk.GetSourceLang()) + return true, nil + } + + if values[1] == "auto" { + translateSdk.SetSourceLang("") + fmt.Println(l.Get("translate-detect")) + return true, nil + } + + translateSdk.SetSourceLang(values[1]) + fmt.Printf(l.Get("translate-source-set"), values[1]) + return true, nil + case "target", "ta": + translateSdk := sdk.GetSdkTranslate() + if len(values) < 2 { + fmt.Println(translateSdk.GetTargetLang()) + return true, nil + } + + translateSdk.SetTargetLang(values[1]) + fmt.Printf(l.Get("translate-target-set"), values[1]) + return true, nil + } + + return false, fmt.Errorf(l.Get("unknown-command"), command) +} + +func HelpTranslateCommand() { + l := lang.GetLocalize() + fmt.Println(l.Get("translate-command-help-usage")) + fmt.Println(l.Get("translate-command-exit-usage")) + fmt.Println(l.Get("translate-command-sdk-usage")) + fmt.Println(l.Get("translate-command-source-usage")) + fmt.Println(l.Get("translate-command-target-usage")) }