• chevron_right

      LocalAI – L’alternative open source puissante à OpenAI

      news.movim.eu / Korben · Tuesday, 19 March - 08:00 · 2 minutes

    Aujourd’hui, j’aimerais vous présenter LocalAI , une alternative open source à OpenAI. En tout cas, c’est comme ça que le créateur du projet le présente. Il s’agit d’une solution idéale pour tous ceux qui cherchent une API REST compatible avec les spécifications de l’API OpenAI pour l’inférence locale.

    Grâce à LocalAI, vous pouvez exécuter des modèles linguistiques, générer des images, de l’audio et bien d’autres choses encore, localement ou sur site avec du matériel grand public, et ce, sans avoir besoin d’un GPU ! Le projet a pour principal objectif de rendre l’IA accessible à tous.

    Pour résumer, voici les principales caractéristiques de LocalAI :

    • Une API REST locale, alternative à OpenAI. Comme ça, vous gardez bien au chaud vos propres données.
    • Pas besoin de GPU. Pas besoin d’accès internet non plus. Toutefois, l’accélération GPU est possible en option.
    • Prise en charge de plusieurs modèles.
    • Dès qu’ils sont chargés une première fois, les modèles restent en mémoire pour une inférence plus rapide.
    • N’utilise pas de shell, mais des liaisons directes pour une inférence plus rapide et de meilleures performances.

    En termes de fonctionnalités, LocalAI offre une large gamme d’options, parmi lesquelles :

    • La génération de texte avec les modèles GPT (comme llama.cpp ou gpt4all.cpp ).
    • La conversion de texte en audio.
    • La transcription audio en texte avec whisper.cpp .
    • La génération d’images avec Stable Diffusion.
    • Les dernières fonctionnalités d’OpenAI récemment ajoutées comme l’API Vision par exemple.
    • La génération d’embeddings pour les bases de données vectorielles.
    • Les grammaires contraintes .
    • Le téléchargement de modèles directement à partir de Huggingface.

    LocalAI est bien sûr un projet communautaire donc n’hésitez pas si vous souhaitez vous impliquer !

    Pour commencer rapidement avec LocalAI, vous pouvez consulter leur guide Getting Started qui décrit les différentes méthodes d’installation et les exigences matérielles ou aller consulter les guides de la communauté . Je vous ferais aussi probablement un tutoriel prochainement si mon emploi du temps me le permet.

    LocalAI est disponible sous forme d’image conteneur et de binaire, compatible avec divers moteurs de conteneurs tels que Docker, Podman et Kubernetes. Les images de conteneurs sont publiées sur quay.io et Docker Hub , et les binaires peuvent être téléchargés à partir de GitHub.

    Concernant les exigences matérielles, ça varie en fonction de la taille du modèle et de la méthode de quantification utilisée mais pour choper quelques repères de performance avec différents backends, comme llama.cpp , vous pouvez consulter ce lien .

    Maintenant pour en savoir plus, vous pouvez explorer le site localai.io . Vous y trouverez de nombreuses informations et des exemples d’utilisation pour vous aider à tirer le meilleur parti de LocalAI.

    Merci à Lorenper

    • chevron_right

      SFTPGo – Le serveur SFTP ultime pour vos transferts de fichiers

      news.movim.eu / Korben · Wednesday, 17 January - 08:00 · 2 minutes

    Imaginez que vous deviez partager des fichiers avec vos collaborateurs, travailler sur des projets avec des personnes situées aux quatre coins du monde ou simplement sauvegarder vos photos de vacances, mais que vous ne savez pas par quoi commencer ?

    Et bien, ne cherchez plus, SFTPGo est là pour vous faciliter la vie.

    SFTPGo c’est un serveur SFTP open source, complet et hautement configurable qui supporte HTTP/S, FTP/S ou encore WebDAV. Il prend en charge plusieurs backends de stockage : du système de fichiers local avec ou sans chiffrement, du stockage d’objets S3, du Google Cloud Storage, de l’Azure Blob Storage, et bien sûr, SFTP.

    L’outil est gratuit, mais si vous voulez le soutenir ou profiter d’un support technique, il faudra porter la main à la bourse ^^. C’est donc plus Open Bourse qu’Open Source.

    Parmi les fonctionnalités offertes, on trouve la possibilité de créer des dossiers virtuels privés ou partagés, des commandes personnalisables, des comptes virtuels stockés, de l’isolement chroot, des autorisations virtuelles par utilisateur et par répertoire, ainsi qu’une API REST , un gestionnaire d’événements, une interface d’administration…etc.

    SFTPGo facilite le paramétrage d’une authentification multifacteur personnalisable . Authentification partielle, par utilisateur et avec plusieurs méthodes, incluant l’authentification LDAP/Active Directory et des programmes externes. Il supporte l’administration des utilisateurs, le chiffrement des données, la modification dynamique des utilisateurs, les quotas, la limitation de bande passante et de débit .

    Pour sécuriser vos données, SFTPGo propose une limitation du nombre de sessions simultanées avec ou sans filtrage par IP ou dossier. Terminaison automatique des connexions inactives, gestion de liste de blocage, filtrage Geo-IP et il est bien sûr compatible avec Git, SCP, rsync, FTP/S et WebDAV.

    Pour vous faciliter la vie, SFTPGo propre aussi des analyses de performance, des logs ultra-précis et un support Infrastructure as Code (IaC) via Terraform. Il est compatible avec Linux, macOS, Windows et FreeBSD et nécessite Go ainsi qu’un serveur SQL. Des binaires et une image Docker officielle sont également disponibles.

    SFTPGo est disponible sur plusieurs plateformes, y compris les dépôt Ubuntu, Void Linux, AWS Marketplace, Azure Marketplace et Elest.io. Il est également disponible pour Windows et macOS via des packages tels que winget, Chocolatey et Homebrew. Et il existe des portages pour FreeBSD et DragonFlyBSD.

    Voilà, si vous cherchez un serveur SFTP totalement configurable (y’a même un système de plugins) et supportant S3, Google Cloud, Azure et j’en passe, celui-ci est un must-have !

    • chevron_right

      Organisez vos comics, magazines et livres avec Komga

      news.movim.eu / Korben · Sunday, 2 April, 2023 - 07:00 · 1 minute

    Si vous cherchez un bon moyen d’auto-héberger vos magazines, vos livres et bandes dessinées, ne cherchez plus, Komga est la solution !

    Si les comics vous intéressent et que vous avez tout ça au format numérique, c’est forcement un peu galère pour organiser tout ça et s’y retrouver. Grâce à Komga vous pourrez vous sortir de ce pétrin et créer des bibliothèques pour vos BDs afin d’organiser totalement vos collections.

    Komga est donc un serveur de comics / mangas open source et gratuit. Vous pouvez même y mettre vos magazine PDF ou vos livres. Il supporte les formats epub, pdf, cbz et cbr et une fois en place, vous profiterez d’une jolie d’une interface web responsive.

    Organisez vos comics avec Komga

    Vous pourrez ainsi organiser votre bibliothèque avec des collections et des listes de lecture, et même modifier les métadonnées de vos séries de BDs et de vos livres. Komga permet également d’importer automatiquement les métadonnées intégrées à ces fichiers et vous pouvez tout lire sans quitter votre navigateur via le lecteur web.

    Gérez vos magazines avec Komga

    D’ailleurs plusieurs modes de lecture sont dispo et vous pouvez même gérer plusieurs users avec un contrôle d’accès par bibliothèque, mais également des restrictions d’âge.

    En plus de cela, il dispose d’une API REST et de nombreux outils et scripts développés par la communauté qui sont capables d’interagir avec Komga.

    Vous pourrez, d’un clic, télécharger les fichiers à l’unité ou des séries entières de BDs entières et si vous avez des petits soucis de mémoire lors de vos imports, l’outil est également capable de détecter les fichiers en double et même les pages en double. Komga peut également importer automatiquement les BDs déposées dans un répertoire.

    Stockez vos livres avec Komga

    Le plus beau là-dedans, c’est que ça s’installe très facilement avec Docker Compose, ou lancé directement via le .jar fourni (java). Donc vous l’aurez compris, ça peut tourner sur un Windows, un Linux mais également un NAS.

    Si vous voulez tester par vous-même, une démo est accessible ici : https://demo.komga.org/

    • Login: demo@komga.org
    • Password: komga-demo
    • chevron_right

      Comment coder une API RESTful ?

      news.movim.eu / Korben · Thursday, 20 October, 2022 - 07:01 · 12 minutes

    — Article en partenariat avec talent.io —

    Certains pensent que le monde se divise en deux avec d’un côté le bien et de l’autre le mal. Mais la vérité est ailleurs surtout pour les développeurs qui savent que le monde se divise en réalité entre le Frontend et le Backend.

    Et pour que ces 2 faces d’une même pièce puissent « discuter », il nous faut des API. API, ça veut dire Application Programming Interface et c’est tout simplement un ensemble d’instructions permettant aux applications de communiquer entre elles. Maintenant des types d’API, il y en a des tonnes, mais je vous propose qu’aujourd’hui on s’intéresse aux API qui respectent le standard architectural REST.

    En effet, quand on travaille en tant que développeur, on ne peut pas passer à côté des API REST, tant cette pratique est très répandue. Si vous débutez dans votre métier, c’est forcement quelque chose que vous devez apprendre et maitriser. De la même manière, quand on débute sa carrière de développeur ou qu’on a déjà beaucoup d’années au compteur mais qu’on ne connaît pas sa valeur sur le marché de l’emploi, il est bon de connaître la grille des salaires en France. Ça tombe bien puisque talent.io a mis en ligne une étude des salaires de la tech en 2022 pour que vous puissiez déterminer si votre salaire est correct ou sous évalué.

    Je vous invite à vous plonger dedans afin de savoir où vous vous situez sur cette grille des salaires.

    C’est quoi une API RESTful ?

    REST, ça veut dire « Representational State Transfer ». Lorsqu’une API respecte les contraintes de REST, on parle alors d’une API RESTful.

    Quand un développeur veut créer une API RESTful, il doit alors respecter les contraintes édictées par REST, notamment sur le fait que son API doit fonctionner au travers du protocole HTTP et de ses verbes (GET, POST…etc.) et que les requêtes et les réponses de l’API soient dans un format texte tels que JSON, HTML, XML…etc.

    Les API RESTful sont alors mises à disposition des applications via ce qu’on appelle des endpoints. Un endpoint est un assemblage de 2 choses : une URI et un verbe HTTP.

    Admettons que j’ai une API qui permette de lister des utilisateurs, mais également d’en créer des nouveaux.

    L’URI de mon API est la suivante :

    https://url.com/api/user

    Le but du jeu, c’est d’appeler par exemple ce endpoint avec le verbe GET pour obtenir une liste de tous les utilisateurs.

    GET: /user/

    Ou pour ajouter un nouvel utilisateur, on peut utiliser le verbe POST comme ceci, en transmettant un bout de JSON contenant toutes les informations liées à l’utilisateur qu’on souhaite créer (nom, prénom…etc.) :

    POST: /user/

    L’idée là, c’est de vous montrer qu’on peut interagir avec l’API à l’aide de différents verbes HTTP. Get peut être assimilé au verbe « lire », POST au verbe « créer », PUT et PATCH au verbe « Mettre à jour » et DELETE au verbe « supprimer ». C’est aussi simple que cela. Et en retour le serveur peut alors répondre avec des codes de status HTTP classiques comme 200, 201, 2002…etc. pour dire que tout est OK, ou des codes de redirection 3xx, d’erreur client 4xx ou d’erreur serveur 5xx.

    En ce qui concerne le format utilisé par les requêtes et les réponses, vous pouvez utiliser du texte, peu importe son format (html, xml.etc.), mais par convention, on utilise surtout du JSON (JavaScript Object Notation).

    Et JSON ?

    Le JSON a la particularité d’être facile à lire et à écrire pour les humains et est utilisé pour enregistrer des données dans des bases de données, mais également pour transmettre ces données entre les applications. C’est pourquoi on l’utilise dans les API RESTful.

    Le JSON est une collection de paires nom / valeur qui sont ordonnées dans une liste. Ainsi les valeurs contenues dans le JSON peuvent être une chaine de caractère (string), un nombre, mais également des objets JSON, des tableaux de valeurs (Array), des booléens (true / false) ou la fameuse valeur null.

    Les contrôles hypermedia

    Je ne vais pas les aborder en détail pour ne pas alourdir cette initiation débutant mais sachez que pour qu’une API soit RESTful, elle doit en plus de tous ces aspects, intégrer également la notion de contrôle hypermedia (Hypermedia as the Engine of Application State – HATEOAS). C’est un attribut de REST qui permet de renvoyer des liens hypertextes afin d’indiquer les actions disponibles directement dans les réponses JSON

    Voilà concernant les grands principes des API RESTful.

    Comment créer votre première API RESTful ?

    Maintenant je vous propose qu’on apprenne à en créer une de zéro. Vous allez voir, c’est super simple.

    Pour cette démo, j’ai choisi d’utiliser Python comme d’habitude. Et je vais importer dans mon code le framework Flask qui va me permettre de faire monter mon API RESTful très facilement. Installez Flask comme ceci :

    pip3 install flask-restful

    Pour tester l’API, nous allons utiliser un outil qui permet à la fois de balancer nos requêtes et de voir si les réponses correspondent bien. L’un des plus connus s’appelle Postman et c’est gratuit en version de base pour tester. L’application Desktop de Postman est disponible ici .

    Ensuite, nous allons définir ce que va faire notre API. Ce sera quelque chose d’assez simple permettant de créer, supprimer, lire et mettre à jour des données. Admettons que j’ai un site sur lequel je dois référencer des produits. Chacun de ces produits aura un id, un nom, un prix et une catégorie.

    Le code de votre première API

    Je vais donc créer un fichier productAPI.py et je vais importer Flask comme ceci avec ses imports spécifiques aux API et au parsing JSON :

    from flask import Flask
    from flask_restful import Resource, Api, reqparse
    
    app = Flask(__name__)
    api = Api(app)

    Puis je vais créer une espèce de mini base de données JSON directement dans mon code afin d’y référencer mes produits :

    products = [
        { "id": 1, "name": "Stylo 4 couleurs", "price": 10, "category": "Papeterie" },
        { "id": 2, "name": "Cahier vert", "price": 20, "category": "Papeterie" },
        { "id": 3, "name": "Télévision 4K", "price": 30, "category": "Tech" },
        { "id": 4, "name": "Souris sans fil", "price": 40, "category": "Tech" },
        { "id": 5, "name": "Ananas", "price": 50, "category": "Alimentaire" },
        { "id": 6, "name": "Kinder Bueno", "price": 60, "category": "Alimentaire" },
        { "id": 7, "name": "Chemise de président", "price": 70, "category": "Textile" },
        { "id": 8, "name": "T-shirt de punk", "price": 80, "category": "Textile" },
        { "id": 9, "name": "Tournevis bleu", "price": 90, "category": "Bricolage" },
        { "id": 10, "name": "Marteau rouge", "price": 100, "category": "Bricolage" }
        ]

    Maintenant on va créer une classe Product qui sera vu comme un endpoint au niveau de notre API. Et dans cette class, nous allons placer nos 4 fonctions correspondantes aux 4 verbes HTTP dont je vous ai parlé plus haut.

    class Product(Resource):
    
        def get(self, id):
    
        def post(self, id):
    
        def put(self, id):
    
        def delete(self, id):

    La structure de base de notre API est OK. Maintenant, on va définir chacune de ces fonctions en commençant par la fonction get utilisée pour récupérer les informations d’un produit à partir de son ID. Comme vous pouvez le voir, on fait une boucle « for » qui va parcourir l’ensemble des produits et si l’ID correspond à ce qui spécifié dans la requête, on renvoie l’objet « product » avec son code HTTP 200 et si ce n’est pas le cas, on renvoie un message « Produit introuvable » accompagné d’un code d’erreur HTTP 404 qui correspond à une ressource non trouvée.

        def get(self, id):
            for product in products:
                if (id == product["id"]):
                    return product, 200
            return "Produit introuvable", 404

    La fonction suivante va nous permettre de créer un nouveau produit. La donnée utilisée dans la requête étant structurée en JSON, cette fonction va utiliser le parseur de Flask pour extraire toutes les données du JSON et les ajouter à notre tableau de produits :

        def post(self, id):
            parser = reqparse.RequestParser()
            parser.add_argument("name")
            parser.add_argument("price")
            parser.add_argument("category")
            args = parser.parse_args()
    
            for product in products:
                if (id == product["id"]):
                    return "Ce produit {} existe deja".format(id), 400
    
            product = {
                "id": id,
                "name": args["name"],
                "price": args["price"],
                "category": args["category"]
            }
            products.append(product)
            return product, 201

    Une fois que le produit est créé, on retourne le code 201 qui veut dire que la création s’est bien déroulée. Et si le produit existait déjà avec cet ID, on renvoie le code 400 avec un message d’erreur.

    La fonction put suivante est assez similaire puisqu’elle permet à la fois de créer un nouveau produit (si l’id n’est pas encore utilisé) ou de mettre à jour un produit existant.

        def put(self, id):
            parser = reqparse.RequestParser()
            parser.add_argument("name")
            parser.add_argument("price")
            parser.add_argument("category")
            args = parser.parse_args()
    
            for product in products:
                if (id == product["id"]):
                    product["name"] = args["name"]
                    product["price"] = args["price"]
                    product["category"] = args["category"]
                    return product, 200
    
            product = {
                "id": id,
                "name": args["name"],
                "price": args["price"],
                "category": args["category"]
            }
            products.append(product)
            return product, 201

    Enfin, vous l’aurez compris, la fonction delete permettra de supprimer un enregistrement à partir de son ID.

        def delete(self, id):
            global products
            products = [product for product in products if product["id"] != id]
            return "{} is deleted.".format(id), 200

    Une fois ces fonctions définies, il ne reste plus qu’à spécifier le format de l’URI qui sera utilisé pour appeler l’API et lancer l’API (en mode debug pour démarrer). Le paramètre <int:id> permet d’indiquer que dans le chemin du endpoint, on peut ajouter une variable acceptée par l’API (ici l’ID du produit).

    api.add_resource(Product, "/product/<int:id>")
    app.run(debug=True)

    Tester l’API avec Postman

    Enfin, il ne reste plus qu’à lancer le script python à l’aide de la commande suivante :

    python3 productAPI.py

    Le serveur web de Flask se lancera alors et vous verrez apparaitre une URL locale que vous pourrez appeler dans Postman pour ensuite tester votre API.

    Voici le code complet du script pour information.

    from flask import Flask
    from flask_restful import Resource, Api, reqparse
    
    app = Flask(__name__)
    api = Api(app)
    
    products = [
        { "id": 1, "name": "Stylo 4 couleurs", "price": 10, "category": "Papeterie" },
        { "id": 2, "name": "Cahier vert", "price": 20, "category": "Papeterie" },
        { "id": 3, "name": "Télévision 4K", "price": 30, "category": "Tech" },
        { "id": 4, "name": "Souris sans fil", "price": 40, "category": "Tech" },
        { "id": 5, "name": "Ananas", "price": 50, "category": "Alimentaire" },
        { "id": 6, "name": "Kinder Bueno", "price": 60, "category": "Alimentaire" },
        { "id": 7, "name": "Chemise de président", "price": 70, "category": "Textile" },
        { "id": 8, "name": "T-shirt de punk", "price": 80, "category": "Textile" },
        { "id": 9, "name": "Tournevis bleu", "price": 90, "category": "Bricolage" },
        { "id": 10, "name": "Marteau rouge", "price": 100, "category": "Bricolage" }
        ]
    
    class Product(Resource):
        def get(self, id):
            for product in products:
                if (id == product["id"]):
                    return product, 200
            return "Product not found", 404
    
        def post(self, id):
            parser = reqparse.RequestParser()
            parser.add_argument("name")
            parser.add_argument("price")
            parser.add_argument("category")
            args = parser.parse_args()
    
            for product in products:
                if (id == product["id"]):
                    return "Ce produit {} existe deja".format(id), 400
    
            product = {
                "id": id,
                "name": args["name"],
                "price": args["price"],
                "category": args["category"]
            }
            products.append(product)
            return product, 201
    
        def put(self, id):
            parser = reqparse.RequestParser()
            parser.add_argument("name")
            parser.add_argument("price")
            parser.add_argument("category")
            args = parser.parse_args()
    
            for product in products:
                if (id == product["id"]):
                    product["name"] = args["name"]
                    product["price"] = args["price"]
                    product["category"] = args["category"]
                    return product, 200
    
            product = {
                "id": id,
                "name": args["name"],
                "price": args["price"],
                "category": args["category"]
            }
            products.append(product)
            return product, 201
    
        def delete(self, id):
            global products
            products = [product for product in products if product["id"] != id]
            return "{} is deleted.".format(id), 200
    
    api.add_resource(Product, "/product/<int:id>")
    app.run(debug=True)

    Côté Postman, un simple GET se forme ainsi et renvoie l’intégralité du JSON propre au produit (grâce à son ID) :

    Pour faire un POST, il faut bien penser à indiquer qu’on souhaite transmettre un contenu de type « RAW » au format JSON et appeler le endpoint avec l’ID de notre nouveau produit (ce n’est pas l’idéal bien sûr, car le principe d’un nouvel ID c’est qu’il soit généré directement par le code de l’API, mais c’est pour illustrer mon exemple).

    Ensuite pour la mise à jour avec le verbe PUT, ça donne ça…

    Et pour la suppression, on obtient ce genre de retour :

    Et ensuite ?

    Vous l’aurez compris, ce n’est vraiment pas compliqué à développer. Ici on est sur un exemple simple, mais ensuite, vous devrez pourquoi pas sortir vos données d’une base de données, penser à correctement gérer les erreurs et faire preuve de rigueur dans la structure de vos endpoints. Par exemple, dans mon endpoint, l’URI est xxxx/product/. J’aurais pu faire mieux en respectant la convention et en mettant/products/ au pluriel. Car en cas de GET, je peux aussi demander à récupérer l’ensemble des produits existants. Dans ce cas, je dois modifier ma fonction GET comme ceci :

        def get(self, id):
            if (id == 0):
                return products, 200
            else:
                for product in products:
                    if (id == product["id"]):
                        return product, 200
                return "Product not found", 404

    Ainsi, en passant l’id 0 lors de mon GET, je récupérerai alors tous les produits.

    N’oubliez pas également de documenter l’ensemble de votre API afin de vous y retrouver et surtout d’indiquer à d’autres développeurs, comment l’intégrer. Penchez vous également sur l’aspect HATEOAS pour intégrer les actions disponibles dans les réponses de l’API REST. Pour notre exemple, on pourrait ainsi avoir quelque chose qui ressemble à ça :

    "links": [ 
        {"rel": "product", "method":"post", "href":"http://example.org/product/1"},
        {"rel": "product", "method":"put", "href":"http://example.org/product/1"}, ... 
    ]

    Voilà, j’espère que cette petite initiation et explications aux API RESTful vous aura intéressé et donné envie de vous y mettre plus sérieusement. Peut-être changer un peu votre façon de coder, voire carrément changer de job pour trouver une équipe plus agile et plus au fait des pratiques de dev modernes. Et pourquoi pas en profiter pour continuer à évoluer dans les technologies, ou obtenir un meilleur salaire. C’est une démarche qui peut s’avérer assez compliquée, stressante parfois, tant on a envie de trouver un travail qui nous corresponde. On peut souhaiter avoir un travail plus proche de chez soi voire en télétravail complet, un meilleur salaire, une meilleure ambiance au travail, ou même tout ça à la fois (mais ce n’est pas forcément facile à trouver).

    Heureusement, talent.io est là pour vous aider. Il vous suffit de créer un compte sur la plateforme talent.io en quelques clics pour ensuite recevoir des offres de la part d’entreprises qui correspondent à vos critères précis et qui affichent un salaire d’entrée de jeu. talent.io est vraiment le moyen le plus simple de trouver son prochain job tech, d’ailleurs les inscrits trouvent leur emploi en 20 jours en moyenne.