packages feed

seihou-cli-0.5.0.0: src/Seihou/CLI/AgentModels.hs

module Seihou.CLI.AgentModels
  ( availableAgentModels,
    providersForModel,
    filterAgentModels,
    formatAgentModels,
  )
where

import Baikai.Model (Model)
import Baikai.Model qualified as BaikaiModel
import Baikai.Models.Generated qualified as Models
import Data.List (sortOn)
import Data.Text (Text)
import Data.Text qualified as Text
import Seihou.CLI.AgentCompletion
  ( AgentProvider (..),
    providerToText,
  )

availableAgentModels :: [Model]
availableAgentModels =
  [ Models.anthropic_claude_fable_5,
    Models.anthropic_claude_haiku_4_5,
    Models.anthropic_claude_opus_4_5,
    Models.anthropic_claude_opus_4_6,
    Models.anthropic_claude_opus_4_7,
    Models.anthropic_claude_opus_4_8,
    Models.anthropic_claude_sonnet_4_5,
    Models.anthropic_claude_sonnet_4_6,
    Models.anthropic_claude_sonnet_5,
    Models.openai_gpt_4_1,
    Models.openai_gpt_4_1_mini,
    Models.openai_gpt_4_1_nano,
    Models.openai_gpt_4o,
    Models.openai_gpt_4o_mini,
    Models.openai_gpt_5,
    Models.openai_gpt_5_1,
    Models.openai_gpt_5_2,
    Models.openai_gpt_5_4,
    Models.openai_gpt_5_4_mini,
    Models.openai_gpt_5_4_nano,
    Models.openai_gpt_5_5,
    Models.openai_gpt_5_6,
    Models.openai_gpt_5_6_luna,
    Models.openai_gpt_5_6_sol,
    Models.openai_gpt_5_6_terra,
    Models.openai_gpt_5_mini,
    Models.openai_gpt_5_nano,
    Models.openai_o1,
    Models.openai_o3,
    Models.openai_o3_mini,
    Models.openai_o4_mini
  ]

providersForModel :: Model -> [AgentProvider]
providersForModel model =
  case BaikaiModel.provider model of
    "anthropic" -> [AgentProviderAnthropic, AgentProviderClaudeCli]
    "openai" -> [AgentProviderOpenAI, AgentProviderCodexCli]
    _ -> []

filterAgentModels :: Maybe AgentProvider -> [Model] -> [Model]
filterAgentModels Nothing = id
filterAgentModels (Just provider) =
  filter (elem provider . providersForModel)

formatAgentModels :: Maybe AgentProvider -> [Model] -> Text
formatAgentModels providerFilter models =
  Text.unlines $
    [ "Available agent models:",
      "",
      formatRow modelWidth nameWidth "MODEL" "NAME" "PROVIDERS"
    ]
      <> map formatModel sortedModels
      <> [ "",
           Text.pack (show (length sortedModels)) <> " models found.",
           "Provider-specific aliases and custom model IDs remain accepted by --model."
         ]
  where
    sortedModels =
      sortOn
        (\model -> (BaikaiModel.provider model, BaikaiModel.modelId model))
        (filterAgentModels providerFilter models)
    modelWidth = maximum (Text.length "MODEL" : map (Text.length . BaikaiModel.modelId) sortedModels)
    nameWidth = maximum (Text.length "NAME" : map (Text.length . BaikaiModel.name) sortedModels)
    formatModel model =
      formatRow
        modelWidth
        nameWidth
        (BaikaiModel.modelId model)
        (BaikaiModel.name model)
        (Text.intercalate ", " (map providerToText (providersForModel model)))

formatRow :: Int -> Int -> Text -> Text -> Text -> Text
formatRow modelWidth nameWidth model name providers =
  padRight modelWidth model
    <> "  "
    <> padRight nameWidth name
    <> "  "
    <> providers

padRight :: Int -> Text -> Text
padRight width value = value <> Text.replicate (width - Text.length value) " "