packages feed

seihou-cli-0.4.0.0: src/Seihou/CLI/AgentConfig.hs

module Seihou.CLI.AgentConfig
  ( AgentConfigInputs (..),
    agentProviderConfigKey,
    agentModelConfigKey,
    agentProviderEnvVar,
    agentModelEnvVar,
    resolveAgentModelConfig,
    loadAgentModelConfig,
  )
where

import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import Seihou.CLI.AgentCompletion
  ( AgentModelConfig (..),
    defaultAgentModelConfig,
    providerFromText,
  )
import Seihou.CLI.Shared (formatConfigError)
import Seihou.Effect.ConfigReader (readGlobalConfig, readLocalConfig)
import Seihou.Effect.ConfigReaderInterp (runConfigReader)
import Seihou.Prelude
import System.Environment (lookupEnv)

data AgentConfigInputs = AgentConfigInputs
  { cliProvider :: Maybe Text,
    cliModel :: Maybe Text,
    envProvider :: Maybe Text,
    envModel :: Maybe Text,
    localConfig :: Map Text Text,
    globalConfig :: Map Text Text
  }
  deriving stock (Eq, Show)

agentProviderConfigKey :: Text
agentProviderConfigKey = "agent.provider"

agentModelConfigKey :: Text
agentModelConfigKey = "agent.model"

agentProviderEnvVar :: String
agentProviderEnvVar = "SEIHOU_AGENT_PROVIDER"

agentModelEnvVar :: String
agentModelEnvVar = "SEIHOU_AGENT_MODEL"

resolveAgentModelConfig :: AgentConfigInputs -> Either Text AgentModelConfig
resolveAgentModelConfig inputs = do
  provider <-
    maybe
      (Right defaultAgentModelConfig.agentProvider)
      providerFromText
      (firstNonBlank [inputs.cliProvider, inputs.envProvider, configValue inputs.localConfig agentProviderConfigKey, configValue inputs.globalConfig agentProviderConfigKey])
  pure
    AgentModelConfig
      { agentProvider = provider,
        agentModel = firstNonBlank [inputs.cliModel, inputs.envModel, configValue inputs.localConfig agentModelConfigKey, configValue inputs.globalConfig agentModelConfigKey]
      }

loadAgentModelConfig :: Maybe Text -> Maybe Text -> IO (Either Text AgentModelConfig)
loadAgentModelConfig cliProvider cliModel = do
  envProvider <- fmap T.pack <$> lookupEnv agentProviderEnvVar
  envModel <- fmap T.pack <$> lookupEnv agentModelEnvVar
  (localResult, globalResult) <- runEff $ runConfigReader $ do
    local <- readLocalConfig
    global <- readGlobalConfig
    pure (local, global)
  pure $ do
    local <- first formatConfigError localResult
    global <- first formatConfigError globalResult
    resolveAgentModelConfig
      AgentConfigInputs
        { cliProvider = cliProvider,
          cliModel = cliModel,
          envProvider = envProvider,
          envModel = envModel,
          localConfig = local,
          globalConfig = global
        }

configValue :: Map Text Text -> Text -> Maybe Text
configValue config key = Map.lookup key config

firstNonBlank :: [Maybe Text] -> Maybe Text
firstNonBlank =
  foldr
    ( \candidate acc ->
        case T.strip <$> candidate of
          Just "" -> acc
          Just value -> Just value
          Nothing -> acc
    )
    Nothing