class_docstr: Test LLMCompareFunction.
class_docstr_tok: ["Test","LLMCompareFunction","."]
docstr: Test custom comparison function.
docstr_tok: ["Test","custom","comparison","function","."]
code: def test_custom_compare(self):
      """Test custom comparison function."""
  
      def length_fn(
          rows: Sequence[LLMFnOutputRowView],
      ) -> Sequence[int]:
          return [len(str(row.result_value())) for row in rows]
  
      lhs_fn = LLMFunctionImpl(
          model=model_lib.EchoModel(),
          prompts=["{word}_{word}"],
      ).add_post_process_add_fn(name="length", fn=length_fn)
      rhs_fn = LLMFunctionImpl(
          model=model_lib.EchoModel(),
          prompts=["abcd_{word}"],
      ).add_post_process_add_fn(name="length", fn=length_fn)
  
      # We deliberately have our custom fn take Mapping[str, Any] instead
      # of LLMFnOutputRowView to make sure the typechecker allows this
      # as well.
      # Note that this function returns a non-string as well.
      def _is_length_less_than(
          lhs: Mapping[str, Any], rhs: Mapping[str, Any]
      ) -> bool:
          return lhs["length"] < rhs["length"]
  
      def _is_length_greater_than(
          lhs: Mapping[str, Any], rhs: Mapping[str, Any]
      ) -> bool:
          return lhs["length"] > rhs["length"]
  
      # Batch-based comparison function for post-processing.
      def _sum_of_lengths(
          rows: Sequence[tuple[Mapping[str, Any], Mapping[str, Any]]]
      ) -> Sequence[int]:
          return [lhs["length"] + rhs["length"] for lhs, rhs in rows]
  
      compare_fn = LLMCompareFunction(
          lhs_name_and_fn=("lhs", lhs_fn),
          rhs_name_and_fn=("rhs", rhs_fn),
          compare_name_and_fns=[
              ("is_shorter_than", _is_length_less_than),
              ("is_longer_than", _is_length_greater_than),
          ],
      ).add_compare_post_process_add_fn(
          name="sum_of_lengths", fn=_sum_of_lengths
      )
  
      results = compare_fn(
          {
              "word": ["hey", "world"],
          }
      )
      # Name collisions are resolved.
      expected_results = {
          "Prompt Num": [0, 0],
          "Input Num": [0, 1],
          "Result Num": [0, 0],
          "Prompt vars": [{"word": "hey"}, {"word": "world"}],
          "lhs_length": [7, 11],
          "lhs_text_result": ["hey_hey", "world_world"],
          "rhs_length": [8, 10],
          "rhs_text_result": ["abcd_hey", "abcd_world"],
          "is_shorter_than": [True, False],
          "sum_of_lengths": [15, 21],
          "is_longer_than": [False, True],
      }
      self.assertEqual(expected_results, results.as_dict())
      self.assertEqual(
          list(expected_results.keys()), list(results.as_dict().keys())
      )
  
imports: from __future__ import annotations
  from typing import Any, Callable, Mapping, Sequence
  from absl.testing import absltest
  from google.generativeai.notebook.lib import llm_function
  from google.generativeai.notebook.lib import llmfn_inputs_source
  from google.generativeai.notebook.lib import llmfn_output_row
  from google.generativeai.notebook.lib import llmfn_outputs
  from google.generativeai.notebook.lib import model
label_desc: Use image node_id 4 for calling the LLMCompareFunctionTest obj's underlying member method code with example usage: obj.test_custom_compare() and returns: unknown, unknown, unknown, unknown