pms-ui-request-0.0.3.0: src/PMS/UI/Request/DS/Core.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
module PMS.UI.Request.DS.Core where
import Control.Monad.Logger
import Data.Conduit
import qualified Data.ByteString.Lazy as B
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Lens
import Control.Monad.Reader
import qualified Control.Concurrent.STM as STM
import qualified Data.Text as T
--import Text.Parsec
import Data.Aeson
import Control.Monad.Except
import qualified PMS.Domain.Model.DM.Type as DM
import qualified PMS.Domain.Model.DS.Utility as DM
import qualified PMS.Domain.Model.DM.Constant as DM
import PMS.UI.Request.DM.Type
import PMS.UI.Request.DS.Utility
-- |
--
app :: AppContext ()
app = do
$logDebugS DM._LOGTAG "app called."
runConduit pipeline
where
pipeline :: ConduitM () Void AppContext ()
pipeline = src .| work .| sink
---------------------------------------------------------------------------------
-- |
--
src :: ConduitT () B.ByteString AppContext ()
src = lift go >>= yield >> src
where
go :: AppContext B.ByteString
go = do
hdl <- view inputHandleAppData <$> ask
bs <- readLineL hdl
$logDebugS DM._LOGTAG $ T.pack $ "src: " ++ DM.lbs2str bs
return bs
---------------------------------------------------------------------------------
-- |
--
work :: ConduitT B.ByteString DM.JsonRpcRequest AppContext ()
work = await >>= \case
Just reqBS -> flip catchError errHdl $ do
lift (go reqBS) >>= yield >> work
Nothing -> do
$logWarnS DM._LOGTAG "work: await returns nothing. skip."
work
where
errHdl :: String -> ConduitT B.ByteString DM.JsonRpcRequest AppContext ()
errHdl msg = do
$logWarnS DM._LOGTAG $ T.pack $ "work: exception. skip. " ++ msg
work
go :: B.ByteString -> AppContext DM.JsonRpcRequest
go = liftEither . eitherDecode
---------------------------------------------------------------------------------
-- |
--
sink :: ConduitT DM.JsonRpcRequest Void AppContext ()
sink = await >>= \case
Just req -> flip catchError errHdl $ do
lift (go req) >> sink
Nothing -> do
$logWarnS DM._LOGTAG "sink: await returns nothing. skip."
sink
where
errHdl :: String -> ConduitT DM.JsonRpcRequest Void AppContext ()
errHdl msg = do
$logWarnS DM._LOGTAG $ T.pack $ "sink: exception skip. " ++ msg
sink
go :: DM.JsonRpcRequest -> AppContext ()
go req = do
let method = req^.DM.methodJsonRpcRequest
rawJson = req^.DM.paramsJsonRpcRequest
reqDat <- decodeReq method rawJson req
enq reqDat
-- |
--
enq :: DM.McpRequest -> AppContext ()
enq req = do
queue <- view DM.requestQueueDomainData <$> lift ask
liftIO $ STM.atomically $ STM.writeTQueue queue req
-- |
--
-- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"roots":{"listChanged":true}},"clientInfo":{"name":"Visual Studio Code","version":"1.100.2"}}} @(pms-ui-request-0.1.0.0-d3d6f94ec8cad8f11e6c82557d1e8927ebf7b47db964c68a0c1101f4c8759996:PMS.UI.Request.DS.Core src/PTY/UI/Request/Core.hs:47:8)
-- {"method":"notifications/initialized","jsonrpc":"2.0"} @(pms-ui-request-0.1.0.0-d3d6f94ec8cad8f11e6c82557d1e8927ebf7b47db964c68a0c1101f4c8759996:PMS.UI.Request.DS.Core src/PTY/UI/Request/Core.hs:47:8)
-- {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}} @(pms-ui-request-0.1.0.0-d3d6f94ec8cad8f11e6c82557d1e8927ebf7b47db964c68a0c1101f4c8759996:PMS.UI.Request.DS.Core src/PTY/UI/Request/Core.hs:47:8)
-- {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"df","arguments":{"arg1":""},"_meta":{"progressToken":"df68feeb-d5b0-4807-abac-ad2eb5d628bd"}}} @(pms-ui-request-0.1.0.0-d3d6f94ec8cad8f11e6c82557d1e8927ebf7b47db964c68a0c1101f4c8759996:PMS.UI.Request.DS.Core src/PTY/UI/Request/Core.hs:47:8)
-- {"jsonrpc":"2.0","id":14,"method":"completion/complete","params":{"ref":{"type":"ref/prompt","name":"summarize_text"},"argument":{"name":"param2","value":"1200"},"context":{"arguments":{"param1":"abc"}}}}
-- {"method":"notifications/cancelled","params":{"requestId":14},"jsonrpc":"2.0"}
-- {"jsonrpc":"2.0","id":15,"method":"prompts/get","params":{"name":"summarize_text","arguments":{"param1":"abc","param2":"1200"}}}
--
decodeReq :: String -> Maybe DM.RawJsonByteString -> DM.JsonRpcRequest -> AppContext DM.McpRequest
decodeReq "initialize" (Just (DM.RawJsonByteString rawJson)) req = DM.McpInitializeRequest . DM.McpInitializeRequestData req <$> (liftEither (eitherDecode rawJson))
decodeReq "tools/list" _ req = pure . DM.McpToolsListRequest . DM.McpToolsListRequestData $ req
decodeReq "tools/call" (Just (DM.RawJsonByteString rawJson)) req = DM.McpToolsCallRequest . DM.McpToolsCallRequestData req <$> (liftEither (eitherDecode rawJson))
decodeReq "prompts/list" _ req = pure . DM.McpPromptsListRequest . DM.McpPromptsListRequestData $ req
decodeReq "notifications/initialized" Nothing req = pure . DM.McpInitializedNotification . DM.McpInitializedNotificationData $ req
decodeReq "notifications/cancelled" (Just (DM.RawJsonByteString rawJson)) req = DM.McpCancelledNotification . DM.McpCancelledNotificationData req <$> (liftEither (eitherDecode rawJson))
decodeReq "completion/complete" (Just (DM.RawJsonByteString rawJson)) req = DM.McpCompletionCompleteRequest . DM.McpCompletionCompleteRequestData req <$> (liftEither (eitherDecode rawJson))
decodeReq "prompts/get" (Just (DM.RawJsonByteString rawJson)) req = DM.McpPromptsGetRequest . DM.McpPromptsGetRequestData req <$> (liftEither (eitherDecode rawJson))
decodeReq _ _ req = throwError $ "unsupported method: " ++ show req