packages feed

bloodhound-1.0.0.0: tests/Test/MLMemorySpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.MLMemorySpec (spec) where

import Data.ByteString.Lazy.Char8 qualified as LBS
import Database.Bloodhound.OpenSearch3.Types
import TestsUtils.Import
import Prelude

-- | Create memory response: @{memory_id}@.
sampleCreateMemoryResponse :: LBS.ByteString
sampleCreateMemoryResponse = "{\"memory_id\":\"gW8Aa40BfUsSoeNTvOKI\"}"

-- | Get memory response. Note timestamps are RFC 3339 strings.
sampleGetMemory :: LBS.ByteString
sampleGetMemory =
  "{\
  \  \"memory_id\": \"ZnQ0JY0BfUsSoeNTqfN3\",\
  \  \"create_time\": \"2024-02-02T18:07:06.887061463Z\",\
  \  \"updated_time\": \"2024-02-02T18:07:06.887061463Z\",\
  \  \"name\": \"Conversation for a RAG pipeline\",\
  \  \"user\": \"admin\"\
  \}"

-- | List memories response: @{"memories": [...]}@.
sampleListMemories :: LBS.ByteString
sampleListMemories = "{\"memories\": []}"

-- | Delete memory response: @{success: <bool>}@.
sampleDeleteMemory :: LBS.ByteString
sampleDeleteMemory = "{\"success\":true}"

-- | Create message response: @{message_id}@.
sampleCreateMessageResponse :: LBS.ByteString
sampleCreateMessageResponse = "{\"message_id\":\"sxa2cY0BfUsSoeNTz-8m\"}"

-- | A message with trace fields (@parent_message_id@, @trace_number@).
sampleTraceMessage :: LBS.ByteString
sampleTraceMessage =
  "{\
  \  \"memory_id\": \"m1\",\
  \  \"message_id\": \"t1\",\
  \  \"parent_message_id\": \"p1\",\
  \  \"trace_number\": 0,\
  \  \"response\": \"intermediate tool output\"\
  \}"

-- | Get traces response: @{"traces": [...]}@.
sampleTraces :: LBS.ByteString
sampleTraces = "{\"traces\": []}"

spec :: Spec
spec = describe "ML Commons memory and message APIs" $ do
  describe "Memory" $ do
    it "decodes the doc get sample (RFC 3339 timestamps as opaque text)" $
      decode sampleGetMemory
        `shouldBe` Just
          Memory
            { memoryId = Just "ZnQ0JY0BfUsSoeNTqfN3",
              memoryCreateTime = Just "2024-02-02T18:07:06.887061463Z",
              memoryUpdatedTime = Just "2024-02-02T18:07:06.887061463Z",
              memoryName = Just "Conversation for a RAG pipeline",
              memoryUser = Just "admin",
              memoryApplicationType = Nothing
            }
    it "round-trips a sparse memory" $ do
      let m = Memory {memoryId = Just "x", memoryCreateTime = Nothing, memoryUpdatedTime = Nothing, memoryName = Just "n", memoryUser = Nothing, memoryApplicationType = Nothing}
      decode (encode m) `shouldBe` Just m

  describe "CreateMemoryResponse" $
    it "decodes the doc sample" $
      decode sampleCreateMemoryResponse
        `shouldBe` Just (CreateMemoryResponse {createMemoryResponseId = Just "gW8Aa40BfUsSoeNTvOKI"})

  describe "ListMemoriesResponse" $
    it "decodes an empty list envelope" $
      decode sampleListMemories `shouldBe` Just (ListMemoriesResponse {listMemoriesResponseMemories = []})

  describe "DeleteMemoryResponse" $
    it "decodes {success: true}" $
      decode sampleDeleteMemory `shouldBe` Just (DeleteMemoryResponse {deleteMemoryResponseSuccess = Just True})

  describe "MemoryMessage" $ do
    it "decodes a trace message (parent_message_id + trace_number present)" $
      decode sampleTraceMessage
        `shouldBe` Just
          MemoryMessage
            { memoryMessageMemoryId = Just "m1",
              memoryMessageId = Just "t1",
              memoryMessageCreateTime = Nothing,
              memoryMessageUpdatedTime = Nothing,
              memoryMessageInput = Nothing,
              memoryMessagePromptTemplate = Nothing,
              memoryMessageResponse = Just "intermediate tool output",
              memoryMessageOrigin = Nothing,
              memoryMessageAdditionalInfo = Nothing,
              memoryMessageParentMessageId = Just "p1",
              memoryMessageTraceNumber = Just 0
            }
    it "round-trips a message with additional_info" $ do
      let msg =
            MemoryMessage
              { memoryMessageMemoryId = Just "m",
                memoryMessageId = Just "x",
                memoryMessageCreateTime = Nothing,
                memoryMessageUpdatedTime = Nothing,
                memoryMessageInput = Just "hello",
                memoryMessagePromptTemplate = Nothing,
                memoryMessageResponse = Just "world",
                memoryMessageOrigin = Just "bot",
                memoryMessageAdditionalInfo = Just (object ["k" .= (1 :: Int)]),
                memoryMessageParentMessageId = Nothing,
                memoryMessageTraceNumber = Nothing
              }
      decode (encode msg) `shouldBe` Just msg

  describe "CreateMemoryMessageResponse" $
    it "decodes the doc sample" $
      decode sampleCreateMessageResponse
        `shouldBe` Just (CreateMemoryMessageResponse {createMemoryMessageResponseId = Just "sxa2cY0BfUsSoeNTz-8m"})

  describe "MemoryMessageTraces" $
    it "decodes an empty traces envelope" $
      decode sampleTraces `shouldBe` Just (MemoryMessageTraces {memoryMessageTracesTraces = []})

  describe "CreateMemoryMessageRequest" $
    it "encodes only the supplied fields" $ do
      let req =
            CreateMemoryMessageRequest
              { createMemoryMessageInput = Just "How do I make an interaction?",
                createMemoryMessagePromptTemplate = Nothing,
                createMemoryMessageResponse = Nothing,
                createMemoryMessageOrigin = Nothing,
                createMemoryMessageAdditionalInfo = Nothing
              }
      encode req `shouldBe` "{\"input\":\"How do I make an interaction?\"}"