packages feed

huggingface-0.1.0.0: examples/Main.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      : Main
-- Description : Small command-line examples of the Hugging Face client
-- Copyright   : (c) 2026 Emily Pillmore
-- License     : BSD-3-Clause
-- Maintainer  : emilypi@cohomolo.gy
-- Stability   : experimental
-- Portability : GHC
module Main (main) where

import Data.Bifunctor (first)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import HuggingFace
import qualified Options.Applicative as Options
import System.Environment (lookupEnv)
import System.Exit (die)

data Command
    = SearchModels Text
    | ShowModel RepoId
    | RunChat InferenceModel Text

commandInfo :: Options.ParserInfo Command
commandInfo = Options.info (Options.helper <*> commandParser)
    ( Options.fullDesc
    <> Options.progDesc "Query the Hugging Face Hub or run a hosted chat completion"
    <> Options.footer "Authentication: set HF_TOKEN for authenticated requests. The chat command may incur provider charges."
    )

commandParser :: Options.Parser Command
commandParser = Options.hsubparser
    ( Options.command "models" (Options.info searchModels (Options.progDesc "Search models (up to ten results)"))
    <> Options.command "model" (Options.info showModel (Options.progDesc "Show repository metadata"))
    <> Options.command "chat" (Options.info runChat (Options.progDesc "Run a hosted chat completion"))
    )
  where
    searchModels = SearchModels <$> Options.strArgument
        (Options.metavar "SEARCH" <> Options.help "Model search text")
    showModel = ShowModel <$> Options.argument (domainReader parseRepoId)
        (Options.metavar "OWNER/NAME" <> Options.help "Hub repository ID (a bare name is also accepted)")
    runChat = RunChat
        <$> Options.argument (domainReader parseInferenceModel)
            (Options.metavar "MODEL" <> Options.help "Model repository with an optional provider or routing suffix")
        <*> Options.strArgument (Options.metavar "PROMPT" <> Options.help "Prompt text; quote multiword prompts")

domainReader :: (Text -> Either HuggingFaceError a) -> Options.ReadM a
domainReader parse = Options.eitherReader (first show . parse . T.pack)

-- | Run a Hub query or an explicitly requested hosted chat completion.
main :: IO ()
main = do
    command <- Options.customExecParser (Options.prefs Options.showHelpOnEmpty) commandInfo
    result <- runHuggingFaceM $ do
        tokenText <- liftIO (lookupEnv "HF_TOKEN")
        token <- liftEither (traverse (parseToken . T.pack) tokenText)
        client <- liftIO $ newClient defaultClientConfig
          { accessToken = token
          }
        case command of
            SearchModels query -> do
                limit <- liftEither (mkPositiveInt 10)
                page <- listModels client defaultListOptions {listSearch = Just query, listLimit = Just limit}
                liftIO $ mapM_ (T.putStrLn . repoIdText . repoId) (pageItems page)
            ShowModel repository -> do
                info <- modelInfo client repository Nothing
                liftIO (print info)
            RunChat target prompt -> do
                completion <- chatCompletion client (defaultChatRequest target (userMessage prompt :| []))
                liftIO $ mapM_ (print . choiceMessage) (completionChoices completion)
    either (die . show) pure result