{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

{- |
Module      : Langchain.Tool.WikipediaTool
Description : Tool for extracting wikipedia content.
Copyright   : (c) 2025 Tushar Adhatrao
License     : MIT
Maintainer  : Tushar Adhatrao <tusharadhatrao@gmail.com>
Stability   : experimental
-}
module Langchain.Tool.WikipediaTool
  ( -- * Configuration
    WikipediaTool (..)
  , defaultWikipediaTool

    -- * Parameters
  , defaultTopK
  , defaultDocMaxChars
  , defaultLanguageCode

    -- * Internal types
  , SearchQuery (..)
  , SearchResponse (..)
  , Page (..)
  , SearchResult (..)
  , Pages (..)
  , PageResponse (..)
  ) where

import Control.Exception (throwIO)
import Data.Aeson (FromJSON (..), decode, withObject, (.:))
import Data.Map (Map)
import qualified Data.Map as M
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics
import Langchain.Runnable.Core (Runnable (..))
import Langchain.Tool.Core
import Network.HTTP.Simple

{- |
Wikipedia search tool configuration
The tool uses Wikipedia's API to perform searches and retrieve page extracts.

Example configuration:

> customTool = WikipediaTool
>   { topK = 3
>   , docMaxChars = 1000
>   , languageCode = "es"
>   }
-}
data WikipediaTool = WikipediaTool
  { WikipediaTool -> Int
topK :: Int
  -- ^ Number of Wikipedia pages to include in the result.
  , WikipediaTool -> Int
docMaxChars :: Int
  -- ^ Number of characters to take from each page.
  , WikipediaTool -> Text
languageCode :: Text
  -- ^ Language code to use (e.g., "en" for English).
  }
  deriving (WikipediaTool -> WikipediaTool -> Bool
(WikipediaTool -> WikipediaTool -> Bool)
-> (WikipediaTool -> WikipediaTool -> Bool) -> Eq WikipediaTool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: WikipediaTool -> WikipediaTool -> Bool
== :: WikipediaTool -> WikipediaTool -> Bool
$c/= :: WikipediaTool -> WikipediaTool -> Bool
/= :: WikipediaTool -> WikipediaTool -> Bool
Eq, Int -> WikipediaTool -> ShowS
[WikipediaTool] -> ShowS
WikipediaTool -> [Char]
(Int -> WikipediaTool -> ShowS)
-> (WikipediaTool -> [Char])
-> ([WikipediaTool] -> ShowS)
-> Show WikipediaTool
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WikipediaTool -> ShowS
showsPrec :: Int -> WikipediaTool -> ShowS
$cshow :: WikipediaTool -> [Char]
show :: WikipediaTool -> [Char]
$cshowList :: [WikipediaTool] -> ShowS
showList :: [WikipediaTool] -> ShowS
Show)

-- | Default value for top K
defaultTopK :: Int
defaultTopK :: Int
defaultTopK = Int
2

-- | Default value for max chars
defaultDocMaxChars :: Int
defaultDocMaxChars :: Int
defaultDocMaxChars = Int
2000

-- | Default language
defaultLanguageCode :: Text
defaultLanguageCode :: Text
defaultLanguageCode = Text
"en"

{- |
Wikipedia search tool configuration
The tool uses Wikipedia's API to perform searches and retrieve page extracts.

Example configuration:

> customTool = WikipediaTool
>   { topK = 3
>   , docMaxChars = 1000
>   , languageCode = "es"
>   }
-}
defaultWikipediaTool :: WikipediaTool
defaultWikipediaTool :: WikipediaTool
defaultWikipediaTool =
  WikipediaTool
    { $sel:topK:WikipediaTool :: Int
topK = Int
defaultTopK
    , $sel:docMaxChars:WikipediaTool :: Int
docMaxChars = Int
defaultDocMaxChars
    , $sel:languageCode:WikipediaTool :: Text
languageCode = Text
defaultLanguageCode
    }

-- | Tool instance for WikipediaTool.
instance Tool WikipediaTool where
  type Input WikipediaTool = Text

  -- \^ Natural language search query (e.g., "Quantum computing")

  type Output WikipediaTool = Text

  -- \^ Concatenated page extracts with separators

  -- \|
  --  Returns "Wikipedia" as the tool identifier
  --
  --  >>> toolName (undefined :: WikipediaTool)
  --  "Wikipedia"
  --
  toolName :: WikipediaTool -> Text
toolName WikipediaTool
_ = Text
"Wikipedia"

  -- \|
  --  Provides a description for LLM agents:
  --
  --  >>> toolDescription (undefined :: WikipediaTool)
  --  "A wrapper around Wikipedia. Useful for answering..."
  --
  toolDescription :: WikipediaTool -> Text
toolDescription WikipediaTool
_ =
    Text
"A wrapper around Wikipedia. Useful for answering general questions about people, places, companies, facts, historical events, or other subjects. Input should be a search query."

  -- \|
  --  Executes Wikipedia search and content retrieval.
  --  Handles API calls and response parsing, returning concatenated extracts.
  --
  --  Example flow:
  --
  --  1. Perform search query
  --  2. Retrieve top K page IDs
  --  3. Fetch and truncate page content
  --  4. Combine results with separators
  --
  --  Throws exceptions on:
  --
  --  - API request failures
  --  - JSON parsing errors
  --  - Missing page content
  --
  runTool :: WikipediaTool -> Input WikipediaTool -> IO (Output WikipediaTool)
runTool WikipediaTool
tool Input WikipediaTool
q = WikipediaTool -> Text -> IO Text
searchWiki WikipediaTool
tool Text
Input WikipediaTool
q

-- | Perform a Wikipedia search and retrieve page extracts.
searchWiki :: WikipediaTool -> Text -> IO Text
searchWiki :: WikipediaTool -> Text -> IO Text
searchWiki WikipediaTool
tool Text
q = do
  SearchResponse {SearchQuery
query :: SearchQuery
$sel:query:SearchResponse :: SearchResponse -> SearchQuery
..} <- WikipediaTool -> Text -> IO SearchResponse
performSearch WikipediaTool
tool Text
q
  if [SearchResult] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (SearchQuery -> [SearchResult]
search SearchQuery
query)
    then Text -> IO Text
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Text
"no wikipedia pages found"
    else do
      let pageIds :: [Int]
pageIds = (SearchResult -> Int) -> [SearchResult] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map SearchResult -> Int
pageid (Int -> [SearchResult] -> [SearchResult]
forall a. Int -> [a] -> [a]
take (WikipediaTool -> Int
topK WikipediaTool
tool) (SearchQuery -> [SearchResult]
search SearchQuery
query))
      [Page]
pages <- (Int -> IO Page) -> [Int] -> IO [Page]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (WikipediaTool -> Int -> IO Page
getPage WikipediaTool
tool) [Int]
pageIds
      let extracts :: [Text]
extracts = (Page -> Text) -> [Page] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Text -> Text
T.take (WikipediaTool -> Int
docMaxChars WikipediaTool
tool) (Text -> Text) -> (Page -> Text) -> Page -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Page -> Text
extract) [Page]
pages
      Text -> IO Text
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> IO Text) -> Text -> IO Text
forall a b. (a -> b) -> a -> b
$ Text -> [Text] -> Text
T.intercalate Text
"\n\n" [Text]
extracts

-- | Perform a search on Wikipedia.
performSearch :: WikipediaTool -> Text -> IO SearchResponse
performSearch :: WikipediaTool -> Text -> IO SearchResponse
performSearch WikipediaTool
tool Text
q = do
  let params :: Map [Char] [Char]
params =
        [([Char], [Char])] -> Map [Char] [Char]
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList
          [ ([Char]
"format", [Char]
"json")
          , ([Char]
"action", [Char]
"query")
          , ([Char]
"list", [Char]
"search")
          , ([Char]
"srsearch", Text -> [Char]
T.unpack Text
q)
          , ([Char]
"srlimit", Int -> [Char]
forall a. Show a => a -> [Char]
show (WikipediaTool -> Int
topK WikipediaTool
tool))
          ]
      url :: Text
url =
        [Char] -> Text
T.pack ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$
          [Char]
"https://" [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
T.unpack (WikipediaTool -> Text
languageCode WikipediaTool
tool) [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
".wikipedia.org/w/api.php?" [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Map [Char] [Char] -> [Char]
urlEncode Map [Char] [Char]
params
  Request
request <- [Char] -> IO Request
forall (m :: * -> *). MonadThrow m => [Char] -> m Request
parseRequest (Text -> [Char]
T.unpack Text
url)
  Response ByteString
response <- Request -> IO (Response ByteString)
forall (m :: * -> *).
MonadIO m =>
Request -> m (Response ByteString)
httpLbs Request
request
  let body :: ByteString
body = Response ByteString -> ByteString
forall a. Response a -> a
getResponseBody Response ByteString
response
  case ByteString -> Maybe SearchResponse
forall a. FromJSON a => ByteString -> Maybe a
decode ByteString
body of
    Just SearchResponse
result -> SearchResponse -> IO SearchResponse
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return SearchResponse
result
    Maybe SearchResponse
Nothing -> IOError -> IO SearchResponse
forall e a. Exception e => e -> IO a
throwIO (IOError -> IO SearchResponse) -> IOError -> IO SearchResponse
forall a b. (a -> b) -> a -> b
$ [Char] -> IOError
userError [Char]
"Failed to decode search response"

-- | Get a page extract from Wikipedia.
getPage :: WikipediaTool -> Int -> IO Page
getPage :: WikipediaTool -> Int -> IO Page
getPage WikipediaTool
tool Int
pageId = do
  let params :: Map [Char] [Char]
params =
        [([Char], [Char])] -> Map [Char] [Char]
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList
          [ ([Char]
"format", [Char]
"json")
          , ([Char]
"action", [Char]
"query")
          , ([Char]
"prop", [Char]
"extracts")
          , ([Char]
"pageids", Int -> [Char]
forall a. Show a => a -> [Char]
show Int
pageId)
          ]
      url :: Text
url =
        [Char] -> Text
T.pack ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$
          [Char]
"https://" [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
T.unpack (WikipediaTool -> Text
languageCode WikipediaTool
tool) [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
".wikipedia.org/w/api.php?" [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Map [Char] [Char] -> [Char]
urlEncode Map [Char] [Char]
params
  Request
request <- [Char] -> IO Request
forall (m :: * -> *). MonadThrow m => [Char] -> m Request
parseRequest (Text -> [Char]
T.unpack Text
url)
  Response ByteString
response <- Request -> IO (Response ByteString)
forall (m :: * -> *).
MonadIO m =>
Request -> m (Response ByteString)
httpLbs Request
request
  let body :: ByteString
body = Response ByteString -> ByteString
forall a. Response a -> a
getResponseBody Response ByteString
response
  case ByteString -> Maybe PageResponse
forall a. FromJSON a => ByteString -> Maybe a
decode ByteString
body of
    Just (PageResponse (Pages Map [Char] Page
p)) -> case [Char] -> Map [Char] Page -> Maybe Page
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup (Int -> [Char]
forall a. Show a => a -> [Char]
show Int
pageId) Map [Char] Page
p of
      Just Page
page -> Page -> IO Page
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Page
page
      Maybe Page
Nothing -> IOError -> IO Page
forall e a. Exception e => e -> IO a
throwIO (IOError -> IO Page) -> IOError -> IO Page
forall a b. (a -> b) -> a -> b
$ [Char] -> IOError
userError [Char]
"Page not found in response"
    Maybe PageResponse
Nothing -> IOError -> IO Page
forall e a. Exception e => e -> IO a
throwIO (IOError -> IO Page) -> IOError -> IO Page
forall a b. (a -> b) -> a -> b
$ [Char] -> IOError
userError [Char]
"Failed to decode page response"

-- | URL encode a map of parameters.
urlEncode :: Map String String -> String
urlEncode :: Map [Char] [Char] -> [Char]
urlEncode = (([Char], [Char]) -> [Char]) -> [([Char], [Char])] -> [Char]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\([Char]
k, [Char]
v) -> [Char]
k [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"=" [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
v [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
"&") ([([Char], [Char])] -> [Char])
-> (Map [Char] [Char] -> [([Char], [Char])])
-> Map [Char] [Char]
-> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map [Char] [Char] -> [([Char], [Char])]
forall k a. Map k a -> [(k, a)]
M.toList

-- | Data types for JSON parsing.
data SearchResponse = SearchResponse
  { SearchResponse -> SearchQuery
query :: SearchQuery
  }
  deriving (Int -> SearchResponse -> ShowS
[SearchResponse] -> ShowS
SearchResponse -> [Char]
(Int -> SearchResponse -> ShowS)
-> (SearchResponse -> [Char])
-> ([SearchResponse] -> ShowS)
-> Show SearchResponse
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SearchResponse -> ShowS
showsPrec :: Int -> SearchResponse -> ShowS
$cshow :: SearchResponse -> [Char]
show :: SearchResponse -> [Char]
$cshowList :: [SearchResponse] -> ShowS
showList :: [SearchResponse] -> ShowS
Show, (forall x. SearchResponse -> Rep SearchResponse x)
-> (forall x. Rep SearchResponse x -> SearchResponse)
-> Generic SearchResponse
forall x. Rep SearchResponse x -> SearchResponse
forall x. SearchResponse -> Rep SearchResponse x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SearchResponse -> Rep SearchResponse x
from :: forall x. SearchResponse -> Rep SearchResponse x
$cto :: forall x. Rep SearchResponse x -> SearchResponse
to :: forall x. Rep SearchResponse x -> SearchResponse
Generic, Maybe SearchResponse
Value -> Parser [SearchResponse]
Value -> Parser SearchResponse
(Value -> Parser SearchResponse)
-> (Value -> Parser [SearchResponse])
-> Maybe SearchResponse
-> FromJSON SearchResponse
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser SearchResponse
parseJSON :: Value -> Parser SearchResponse
$cparseJSONList :: Value -> Parser [SearchResponse]
parseJSONList :: Value -> Parser [SearchResponse]
$comittedField :: Maybe SearchResponse
omittedField :: Maybe SearchResponse
FromJSON)

-- | Type for list of search result
data SearchQuery = SearchQuery
  { SearchQuery -> [SearchResult]
search :: [SearchResult]
  }
  deriving (Int -> SearchQuery -> ShowS
[SearchQuery] -> ShowS
SearchQuery -> [Char]
(Int -> SearchQuery -> ShowS)
-> (SearchQuery -> [Char])
-> ([SearchQuery] -> ShowS)
-> Show SearchQuery
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SearchQuery -> ShowS
showsPrec :: Int -> SearchQuery -> ShowS
$cshow :: SearchQuery -> [Char]
show :: SearchQuery -> [Char]
$cshowList :: [SearchQuery] -> ShowS
showList :: [SearchQuery] -> ShowS
Show)

instance FromJSON SearchQuery where
  parseJSON :: Value -> Parser SearchQuery
parseJSON = [Char]
-> (Object -> Parser SearchQuery) -> Value -> Parser SearchQuery
forall a. [Char] -> (Object -> Parser a) -> Value -> Parser a
withObject [Char]
"SearchQuery" ((Object -> Parser SearchQuery) -> Value -> Parser SearchQuery)
-> (Object -> Parser SearchQuery) -> Value -> Parser SearchQuery
forall a b. (a -> b) -> a -> b
$ \Object
v ->
    [SearchResult] -> SearchQuery
SearchQuery
      ([SearchResult] -> SearchQuery)
-> Parser [SearchResult] -> Parser SearchQuery
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser [SearchResult]
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"search"

-- | Result of SearchResult
data SearchResult = SearchResult
  { SearchResult -> Int
ns :: Int
  , SearchResult -> Text
title_ :: Text
  , SearchResult -> Int
pageid :: Int
  , SearchResult -> Int
size :: Int
  , SearchResult -> Int
wordcount :: Int
  , SearchResult -> Text
snippet :: Text
  , SearchResult -> Text
timestamp :: Text
  }
  deriving (Int -> SearchResult -> ShowS
[SearchResult] -> ShowS
SearchResult -> [Char]
(Int -> SearchResult -> ShowS)
-> (SearchResult -> [Char])
-> ([SearchResult] -> ShowS)
-> Show SearchResult
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SearchResult -> ShowS
showsPrec :: Int -> SearchResult -> ShowS
$cshow :: SearchResult -> [Char]
show :: SearchResult -> [Char]
$cshowList :: [SearchResult] -> ShowS
showList :: [SearchResult] -> ShowS
Show)

instance FromJSON SearchResult where
  parseJSON :: Value -> Parser SearchResult
parseJSON = [Char]
-> (Object -> Parser SearchResult) -> Value -> Parser SearchResult
forall a. [Char] -> (Object -> Parser a) -> Value -> Parser a
withObject [Char]
"SearchResult" ((Object -> Parser SearchResult) -> Value -> Parser SearchResult)
-> (Object -> Parser SearchResult) -> Value -> Parser SearchResult
forall a b. (a -> b) -> a -> b
$ \Object
v ->
    Int -> Text -> Int -> Int -> Int -> Text -> Text -> SearchResult
SearchResult
      (Int -> Text -> Int -> Int -> Int -> Text -> Text -> SearchResult)
-> Parser Int
-> Parser
     (Text -> Int -> Int -> Int -> Text -> Text -> SearchResult)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"ns"
      Parser (Text -> Int -> Int -> Int -> Text -> Text -> SearchResult)
-> Parser Text
-> Parser (Int -> Int -> Int -> Text -> Text -> SearchResult)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"title"
      Parser (Int -> Int -> Int -> Text -> Text -> SearchResult)
-> Parser Int
-> Parser (Int -> Int -> Text -> Text -> SearchResult)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"pageid"
      Parser (Int -> Int -> Text -> Text -> SearchResult)
-> Parser Int -> Parser (Int -> Text -> Text -> SearchResult)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"size"
      Parser (Int -> Text -> Text -> SearchResult)
-> Parser Int -> Parser (Text -> Text -> SearchResult)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"wordcount"
      Parser (Text -> Text -> SearchResult)
-> Parser Text -> Parser (Text -> SearchResult)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"snippet"
      Parser (Text -> SearchResult) -> Parser Text -> Parser SearchResult
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"timestamp"

-- | Wikipedia response
data PageResponse = PageResponse
  { PageResponse -> Pages
query :: Pages
  }
  deriving ((forall x. PageResponse -> Rep PageResponse x)
-> (forall x. Rep PageResponse x -> PageResponse)
-> Generic PageResponse
forall x. Rep PageResponse x -> PageResponse
forall x. PageResponse -> Rep PageResponse x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PageResponse -> Rep PageResponse x
from :: forall x. PageResponse -> Rep PageResponse x
$cto :: forall x. Rep PageResponse x -> PageResponse
to :: forall x. Rep PageResponse x -> PageResponse
Generic, PageResponse -> PageResponse -> Bool
(PageResponse -> PageResponse -> Bool)
-> (PageResponse -> PageResponse -> Bool) -> Eq PageResponse
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PageResponse -> PageResponse -> Bool
== :: PageResponse -> PageResponse -> Bool
$c/= :: PageResponse -> PageResponse -> Bool
/= :: PageResponse -> PageResponse -> Bool
Eq, Int -> PageResponse -> ShowS
[PageResponse] -> ShowS
PageResponse -> [Char]
(Int -> PageResponse -> ShowS)
-> (PageResponse -> [Char])
-> ([PageResponse] -> ShowS)
-> Show PageResponse
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PageResponse -> ShowS
showsPrec :: Int -> PageResponse -> ShowS
$cshow :: PageResponse -> [Char]
show :: PageResponse -> [Char]
$cshowList :: [PageResponse] -> ShowS
showList :: [PageResponse] -> ShowS
Show, Maybe PageResponse
Value -> Parser [PageResponse]
Value -> Parser PageResponse
(Value -> Parser PageResponse)
-> (Value -> Parser [PageResponse])
-> Maybe PageResponse
-> FromJSON PageResponse
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser PageResponse
parseJSON :: Value -> Parser PageResponse
$cparseJSONList :: Value -> Parser [PageResponse]
parseJSONList :: Value -> Parser [PageResponse]
$comittedField :: Maybe PageResponse
omittedField :: Maybe PageResponse
FromJSON)

-- | Collection of Wikipedia pages, where key is page id
data Pages = Pages
  { Pages -> Map [Char] Page
pages :: Map String Page
  }
  deriving ((forall x. Pages -> Rep Pages x)
-> (forall x. Rep Pages x -> Pages) -> Generic Pages
forall x. Rep Pages x -> Pages
forall x. Pages -> Rep Pages x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Pages -> Rep Pages x
from :: forall x. Pages -> Rep Pages x
$cto :: forall x. Rep Pages x -> Pages
to :: forall x. Rep Pages x -> Pages
Generic, Pages -> Pages -> Bool
(Pages -> Pages -> Bool) -> (Pages -> Pages -> Bool) -> Eq Pages
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Pages -> Pages -> Bool
== :: Pages -> Pages -> Bool
$c/= :: Pages -> Pages -> Bool
/= :: Pages -> Pages -> Bool
Eq, Int -> Pages -> ShowS
[Pages] -> ShowS
Pages -> [Char]
(Int -> Pages -> ShowS)
-> (Pages -> [Char]) -> ([Pages] -> ShowS) -> Show Pages
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Pages -> ShowS
showsPrec :: Int -> Pages -> ShowS
$cshow :: Pages -> [Char]
show :: Pages -> [Char]
$cshowList :: [Pages] -> ShowS
showList :: [Pages] -> ShowS
Show, Maybe Pages
Value -> Parser [Pages]
Value -> Parser Pages
(Value -> Parser Pages)
-> (Value -> Parser [Pages]) -> Maybe Pages -> FromJSON Pages
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser Pages
parseJSON :: Value -> Parser Pages
$cparseJSONList :: Value -> Parser [Pages]
parseJSONList :: Value -> Parser [Pages]
$comittedField :: Maybe Pages
omittedField :: Maybe Pages
FromJSON)

-- | Represents wikipedia page
data Page = Page
  { Page -> Text
title :: Text
  , Page -> Text
extract :: Text
  }
  deriving (Int -> Page -> ShowS
[Page] -> ShowS
Page -> [Char]
(Int -> Page -> ShowS)
-> (Page -> [Char]) -> ([Page] -> ShowS) -> Show Page
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Page -> ShowS
showsPrec :: Int -> Page -> ShowS
$cshow :: Page -> [Char]
show :: Page -> [Char]
$cshowList :: [Page] -> ShowS
showList :: [Page] -> ShowS
Show, Page -> Page -> Bool
(Page -> Page -> Bool) -> (Page -> Page -> Bool) -> Eq Page
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Page -> Page -> Bool
== :: Page -> Page -> Bool
$c/= :: Page -> Page -> Bool
/= :: Page -> Page -> Bool
Eq)

instance FromJSON Page where
  parseJSON :: Value -> Parser Page
parseJSON = [Char] -> (Object -> Parser Page) -> Value -> Parser Page
forall a. [Char] -> (Object -> Parser a) -> Value -> Parser a
withObject [Char]
"Page" ((Object -> Parser Page) -> Value -> Parser Page)
-> (Object -> Parser Page) -> Value -> Parser Page
forall a b. (a -> b) -> a -> b
$ \Object
v ->
    Text -> Text -> Page
Page
      (Text -> Text -> Page) -> Parser Text -> Parser (Text -> Page)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"title"
      Parser (Text -> Page) -> Parser Text -> Parser Page
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"extract"

{- |
Implements Runnable compatibility layer
Note: The current implementation returns 'Right' values only,
though the type signature allows for future error handling.

Example usage:

> response <- invoke defaultWikipediaTool "Artificial intelligence"
> case response of
>   Right content -> putStrLn content
>   Left err -> print err
-}
instance Runnable WikipediaTool where
  type RunnableInput WikipediaTool = Text
  type RunnableOutput WikipediaTool = Text

  -- TODO: runTool should return an Either
  invoke :: WikipediaTool
-> RunnableInput WikipediaTool
-> IO (Either [Char] (RunnableOutput WikipediaTool))
invoke WikipediaTool
tool RunnableInput WikipediaTool
input = (Text -> Either [Char] (RunnableOutput WikipediaTool))
-> IO Text -> IO (Either [Char] (RunnableOutput WikipediaTool))
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Either [Char] Text
Text -> Either [Char] (RunnableOutput WikipediaTool)
forall a b. b -> Either a b
Right (IO Text -> IO (Either [Char] (RunnableOutput WikipediaTool)))
-> IO Text -> IO (Either [Char] (RunnableOutput WikipediaTool))
forall a b. (a -> b) -> a -> b
$ WikipediaTool -> Input WikipediaTool -> IO (Output WikipediaTool)
forall a. Tool a => a -> Input a -> IO (Output a)
runTool WikipediaTool
tool RunnableInput WikipediaTool
Input WikipediaTool
input