never executed always true always false
    1 module PureClaw.Providers.Class
    2   ( -- * Message types
    3     Role (..)
    4   , ContentBlock (..)
    5   , Message (..)
    6   , roleToText
    7     -- * Convenience constructors
    8   , textMessage
    9   , toolResultMessage
   10     -- * Content block queries
   11   , responseText
   12   , toolUseCalls
   13     -- * Tool definitions
   14   , ToolDefinition (..)
   15   , ToolChoice (..)
   16     -- * Request and response
   17   , CompletionRequest (..)
   18   , CompletionResponse (..)
   19   , Usage (..)
   20     -- * Provider typeclass
   21   , Provider (..)
   22     -- * Existential wrapper
   23   , SomeProvider (..)
   24   ) where
   25 
   26 import Data.Aeson (Value)
   27 import Data.Text (Text)
   28 import Data.Text qualified as T
   29 
   30 import PureClaw.Core.Types
   31 
   32 -- | Role in a conversation. System prompts are handled separately
   33 -- via 'CompletionRequest._cr_systemPrompt' rather than as messages,
   34 -- since providers differ on how they handle system content.
   35 data Role = User | Assistant
   36   deriving stock (Show, Eq, Ord)
   37 
   38 -- | A single content block within a message. Messages contain one or
   39 -- more content blocks, allowing mixed text and tool interactions.
   40 data ContentBlock
   41   = TextBlock Text
   42   | ToolUseBlock
   43       { _tub_id    :: ToolCallId
   44       , _tub_name  :: Text
   45       , _tub_input :: Value
   46       }
   47   | ToolResultBlock
   48       { _trb_toolUseId :: ToolCallId
   49       , _trb_content   :: Text
   50       , _trb_isError   :: Bool
   51       }
   52   deriving stock (Show, Eq)
   53 
   54 -- | A single message in a conversation. Content is a list of blocks
   55 -- to support tool use/result interleaving with text.
   56 data Message = Message
   57   { _msg_role    :: Role
   58   , _msg_content :: [ContentBlock]
   59   }
   60   deriving stock (Show, Eq)
   61 
   62 -- | Convert a role to its API text representation.
   63 roleToText :: Role -> Text
   64 roleToText User      = "user"
   65 roleToText Assistant  = "assistant"
   66 
   67 -- | Create a simple text message (the common case).
   68 textMessage :: Role -> Text -> Message
   69 textMessage role txt = Message role [TextBlock txt]
   70 
   71 -- | Create a tool result message (user role with tool results).
   72 toolResultMessage :: [(ToolCallId, Text, Bool)] -> Message
   73 toolResultMessage results = Message User
   74   [ ToolResultBlock callId content isErr
   75   | (callId, content, isErr) <- results
   76   ]
   77 
   78 -- | Extract concatenated text from a response's content blocks.
   79 responseText :: CompletionResponse -> Text
   80 responseText resp =
   81   let texts = [t | TextBlock t <- _crsp_content resp]
   82   in T.intercalate "\n" texts
   83 
   84 -- | Extract tool use calls from a response's content blocks.
   85 toolUseCalls :: CompletionResponse -> [(ToolCallId, Text, Value)]
   86 toolUseCalls resp =
   87   [ (_tub_id b, _tub_name b, _tub_input b)
   88   | b@ToolUseBlock {} <- _crsp_content resp
   89   ]
   90 
   91 -- | Tool definition for offering tools to the provider.
   92 data ToolDefinition = ToolDefinition
   93   { _td_name        :: Text
   94   , _td_description :: Text
   95   , _td_inputSchema :: Value
   96   }
   97   deriving stock (Show, Eq)
   98 
   99 -- | Tool choice constraint for the provider.
  100 data ToolChoice
  101   = AutoTool
  102   | AnyTool
  103   | SpecificTool Text
  104   deriving stock (Show, Eq)
  105 
  106 -- | Request to an LLM provider.
  107 data CompletionRequest = CompletionRequest
  108   { _cr_model        :: ModelId
  109   , _cr_messages     :: [Message]
  110   , _cr_systemPrompt :: Maybe Text
  111   , _cr_maxTokens    :: Maybe Int
  112   , _cr_tools        :: [ToolDefinition]
  113   , _cr_toolChoice   :: Maybe ToolChoice
  114   }
  115   deriving stock (Show, Eq)
  116 
  117 -- | Token usage information.
  118 data Usage = Usage
  119   { _usage_inputTokens  :: Int
  120   , _usage_outputTokens :: Int
  121   }
  122   deriving stock (Show, Eq)
  123 
  124 -- | Response from an LLM provider.
  125 data CompletionResponse = CompletionResponse
  126   { _crsp_content :: [ContentBlock]
  127   , _crsp_model   :: ModelId
  128   , _crsp_usage   :: Maybe Usage
  129   }
  130   deriving stock (Show, Eq)
  131 
  132 -- | LLM provider interface. Each provider (Anthropic, OpenAI, etc.)
  133 -- implements this typeclass.
  134 class Provider p where
  135   complete :: p -> CompletionRequest -> IO CompletionResponse
  136 
  137 -- | Existential wrapper for runtime provider selection (e.g. from config).
  138 data SomeProvider where
  139   MkProvider :: Provider p => p -> SomeProvider
  140 
  141 instance Provider SomeProvider where
  142   complete (MkProvider p) = complete p