bloodhound-1.0.0.0: tests/Test/MLProfileSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.MLProfileSpec (spec) where
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Map.Strict qualified as M
import Database.Bloodhound.OpenSearch3.Types
import TestsUtils.Import
import Prelude
-- | A profile response with two nodes, one carrying full per-model stats
-- and the other a bare worker_nodes routing entry. Drawn from the shape
-- documented at <https://docs.opensearch.org/latest/ml-commons-plugin/api/profile/>.
sampleProfileResponse :: LBS.ByteString
sampleProfileResponse =
"{\
\ \"nodes\": {\
\ \"qTduw0FJTrmGrqMrxH0dcA\": {\
\ \"models\": {\
\ \"WWQI44MBbzI2oUKAvNUt\": {\"worker_nodes\": [\"KzONM8c8T4Od-NoUANQNGg\"]}\
\ }\
\ },\
\ \"KzONM8c8T4Od-NoUANQNGg\": {\
\ \"models\": {\
\ \"WWQI44MBbzI2oUKAvNUt\": {\
\ \"model_state\": \"DEPLOYED\",\
\ \"predictor\": \"org.opensearch.ml.engine.algorithms.text_embedding.TextEmbeddingModel@592814c9\",\
\ \"worker_nodes\": [\"KzONM8c8T4Od-NoUANQNGg\"],\
\ \"predict_request_stats\": {\
\ \"count\": 2,\
\ \"max\": 89.978681,\
\ \"min\": 5.402,\
\ \"average\": 47.6903405,\
\ \"p50\": 47.6903405,\
\ \"p90\": 81.5210129,\
\ \"p99\": 89.13291418999998\
\ }\
\ }\
\ }\
\ }\
\ }\
\}"
spec :: Spec
spec = describe "ML Commons profile API" $ do
describe "MLProfileResponse" $ do
it "decodes the doc sample (two nodes, one with full predict stats)" $ do
let decoded = decode sampleProfileResponse :: Maybe MLProfileResponse
case decoded of
Just resp -> do
-- Two nodes in the envelope.
length (mlProfileResponseNodes resp) `shouldBe` 2
-- The fully-populated node's model should report DEPLOYED and a count of 2.
let nodes = M.elems (mlProfileResponseNodes resp)
deployedModels =
[ m
| n <- nodes,
m <- M.elems (mlProfileNodeModels n),
mlProfileModelModelState m == Just ModelStateDeployed
]
case deployedModels of
(m : _) ->
(mlProfileModelPredictRequestStats m >>= mlProfileStatsCount)
`shouldBe` Just 2
[] -> expectationFailure "expected a DEPLOYED model profile"
Nothing -> expectationFailure "expected MLProfileResponse decode"
it "decodes an empty envelope" $
decode "{\"nodes\":{}}" `shouldBe` Just (MLProfileResponse {mlProfileResponseNodes = M.empty})
describe "MLProfileModel" $
it "round-trips a model with stats" $ do
let m =
MLProfileModel
{ mlProfileModelModelState = Just ModelStateDeployed,
mlProfileModelPredictor = Just "cls@1",
mlProfileModelWorkerNodes = Just ["n1"],
mlProfileModelPredictRequestStats =
Just
MLProfilePredictRequestStats
{ mlProfileStatsCount = Just 1,
mlProfileStatsMax = Just 1.0,
mlProfileStatsMin = Just 1.0,
mlProfileStatsAverage = Just 1.0,
mlProfileStatsP50 = Just 1.0,
mlProfileStatsP90 = Just 1.0,
mlProfileStatsP99 = Just 1.0
}
}
decode (encode m) `shouldBe` Just m
describe "MLProfileRequest" $
it "encodes only the supplied filters" $ do
let req =
MLProfileRequest
{ mlProfileRequestNodeIds = Just ["KzONM8c8T4Od-NoUANQNGg"],
mlProfileRequestModelIds = Nothing,
mlProfileRequestTaskIds = Nothing,
mlProfileRequestReturnAllTasks = Just True,
mlProfileRequestReturnAllModels = Just True
}
encode req
`shouldBe` "{\"node_ids\":[\"KzONM8c8T4Od-NoUANQNGg\"],\"return_all_models\":true,\"return_all_tasks\":true}"