code: def functionary_chat_handler(
      llama: llama.Llama,
      messages: List[llama_types.ChatCompletionRequestMessage],
      functions: Optional[
          List[llama_types.ChatCompletionFunction]
      ] = None,
      function_call: Optional[
          llama_types.ChatCompletionRequestFunctionCall
      ] = None,
      tools: Optional[List[llama_types.ChatCompletionTool]] = None,
      tool_choice: Optional[
          llama_types.ChatCompletionToolChoiceOption
      ] = None,
      temperature: float = 0.2,
      top_p: float = 0.95,
      top_k: int = 40,
      min_p: float = 0.05,
      typical_p: float = 1.0,
      stream: bool = False,
      stop: Optional[Union[str, List[str]]] = [],
      response_format: Optional[
          llama_types.ChatCompletionRequestResponseFormat
      ] = None,
      max_tokens: Optional[int] = None,
      presence_penalty: float = 0.0,
      frequency_penalty: float = 0.0,
      repeat_penalty: float = 1.1,
      tfs_z: float = 1.0,
      mirostat_mode: int = 0,
      mirostat_tau: float = 5.0,
      mirostat_eta: float = 0.1,
      model: Optional[str] = None,
      logits_processor: Optional[llama.LogitsProcessorList] = None,
      grammar: Optional[llama.LlamaGrammar] = None,
      **kwargs,  # type: ignore
  ) -> Union[
      llama_types.ChatCompletion,
      Iterator[llama_types.ChatCompletionChunk],
  ]:
      SYSTEM_MESSAGE = """A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions. The assistant calls functions with appropriate input when necessary"""
  
      def generate_type_definition(
          param: Dict[str, llama_types.JsonType],
          indent_level: int,
          shared_defs,
      ) -> str:
          indent = "  " * indent_level
          if "$ref" in param:
              # Reference to a shared definition
              ref_name = param["$ref"].split("/")[
                  -1
              ]  # Extract the type name from the reference
              return ref_name
          elif param.get("type") == "array":
              items = param.get("items", {})
              item_type = generate_type_definition(
                  items, indent_level + 1, shared_defs
              )
              return f"Array<{item_type}>"
          elif param.get("type") == "object":
              properties = param.get("properties", {})
              nested_schema = "{\n"
              for nested_param_name, nested_param in properties.items():
                  nested_param_type = generate_type_definition(
                      nested_param, indent_level + 1, shared_defs
                  )
                  nested_schema += f"{indent}  {nested_param_name}: {nested_param_type},\n"
              nested_schema += indent + "}"
              return nested_schema
          elif "enum" in param:
              # Enum type
              return " | ".join(
                  [f'"{enum_value}"' for enum_value in param["enum"]]
              )
          else:
              # Simple type
              return param.get("type", "any")
  
      def generate_shared_definitions(
          shared_defs, indent_level: int
      ) -> str:
          indent = "  " * indent_level
          shared_definitions = ""
          for def_name, def_properties in shared_defs.items():
              shared_definitions += f"{indent}type {def_name} = "
              if def_properties.get("type") == "object":
                  shared_definitions += generate_type_definition(
                      def_properties, indent_level, shared_defs
                  )
              elif "enum" in def_properties:
                  # Enum type
                  shared_definitions += " | ".join(
                      [
                          f'"{enum_value}"'
                          for enum_value in def_properties["enum"]
                      ]
                  )
              shared_definitions += ";\n"
          return shared_definitions
  
      def generate_schema_from_functions(
          functions, namespace="functions"
      ) -> str:
          schema = "// Supported function definitions that should be called when necessary.\n"
          schema += f"namespace {namespace} {{\n\n"
  
          # Generate shared definitions
          shared_definitions = {}
          for function in functions:
              parameters = function.get("parameters", {})
              shared_definitions.update(parameters.get("$defs", {}))
  
          schema += generate_shared_definitions(shared_definitions, 1)
  
          for function in functions:
              function_name = function["name"]
              description = function.get("description", "")
              parameters = function.get("parameters", {})
              required_params = parameters.get("required", [])
  
              schema += f"  // {description}\n"
              schema += f"  type {function_name} = (_: {{\n"
  
              for param_name, param in parameters.get(
                  "properties", {}
              ).items():
                  param_description = param.get("description", "")
                  param_type = generate_type_definition(
                      param, 2, shared_definitions
                  )
                  optional_indicator = (
                      "" if param_name in required_params else "?"
                  )
                  schema += f"    // {param_description}\n"
                  schema += f"    {param_name}{optional_indicator}: {param_type},\n"
              schema += "  }) => any;\n\n"
  
          schema += "}} // namespace {}\n".format(namespace)
          return schema
  
      def prepare_messages_for_inference(
          messages: List[llama_types.ChatCompletionRequestMessage],
          functions: Optional[
              List[llama_types.ChatCompletionFunctions]
          ] = None,
          tools: Optional[List[llama_types.ChatCompletionTool]] = None,
      ):
          all_messages: List[
              llama_types.ChatCompletionRequestMessage
          ] = []
          if functions is not None:
              all_messages.append(
                  llama_types.ChatCompletionRequestSystemMessage(
                      role="system",
                      content=generate_schema_from_functions(functions),
                  )
              )
  
          if tools is not None:
              all_messages.append(
                  llama_types.ChatCompletionRequestSystemMessage(
                      role="system",
                      content=generate_schema_from_functions(
                          [
                              tool["function"]
                              for tool in tools
                              if tool["type"] == "function"
                          ]
                      ),
                  )
              )
  
          all_messages.append(
              llama_types.ChatCompletionRequestSystemMessage(
                  role="system", content=SYSTEM_MESSAGE
              )
          )
  
          for message in messages:
              # Function call responses
              if message["role"] == "function" and "name" in message:
                  message["name"] = f"functions.{message['name']}"
              # Function call requests by assistant
              if "function_call" in message:
                  message["function_call"][
                      "name"
                  ] = f"functions.{message['function_call']['name']}"
              all_messages.append(message)
  
          all_messages.append(
              llama_types.ChatCompletionRequestAssistantMessage(
                  role="assistant", content=None
              )
          )
  
          def message_to_str(
              msg: llama_types.ChatCompletionRequestMessage,
          ):
              if msg["role"] == "system":
                  return f"system:\n{msg['content']}\n"
  
              elif msg["role"] == "function" and "name" in msg:
                  return f"function name={msg['name']}:\n{msg['content']}\n"
              elif msg["role"] == "function" and "function_call" in msg:
                  return f"function name={msg['function_call']['name']}:\n{msg['function_call']['arguments']}\n"
              elif msg["role"] == "tool":
                  if msg["content"] is not None:
                      return f"function name={msg['tool_call_id']}:\n{msg['content']}\n"
                  else:
                      return f"function name={msg['tool_call_id']}\n"
              elif msg["role"] == "user":
                  if msg["content"] is None:
                      return "user:\n</s></s>\n"
                  else:
                      return f"user:\n</s>{msg['content']}</s>\n"
              elif msg["role"] == "assistant":
                  if (
                      msg["content"] is not None
                      and "function_call" in msg
                  ):
                      return f"assistant:\n{msg['content']}\nassistant to={msg['function_call']['name']}:\n{msg['function_call']['arguments']}</s>\n"
                  elif "function_call" in msg:
                      return f"assistant to={msg['function_call']['name']}:\n{msg['function_call']['arguments']}</s>\n"
                  elif (
                      "tool_calls" in msg and len(msg["tool_calls"]) > 0
                  ):
                      for tool_call in msg[
                          "tool_calls"
                      ]:  # NOTE: probably doesn't work with the functionary model
                          return f"assistant to={tool_call['id']}:\n{tool_call['function']['arguments']}</s>\n"
                  elif msg["content"] is None:
                      return "assistant"
                  else:
                      return f"assistant:\n{msg['content']}\n"
              else:
                  raise ValueError(f"Unsupported role: {msg['role']}")
  
          return "".join([message_to_str(msg) for msg in all_messages])
  
      if tools is not None:
          functions = [
              tool["function"]
              for tool in tools
              if tool["type"] == "function"
          ]
  
      if tool_choice is not None:
          function_call = (
              tool_choice
              if isinstance(tool_choice, str)
              else tool_choice["function"]
          )
  
      prompt = prepare_messages_for_inference(
          messages, functions, tools
      )
  
      if function_call is None and (
          functions is None or len(functions) == 0
      ):
          completion_or_completion_chunks = llama.create_completion(
              prompt=prompt + ":\n",
              temperature=temperature,
              top_p=top_p,
              top_k=top_k,
              min_p=min_p,
              typical_p=typical_p,
              stream=stream,
              stop=["user:", "</s>"],
              max_tokens=max_tokens,
              presence_penalty=presence_penalty,
              frequency_penalty=frequency_penalty,
              repeat_penalty=repeat_penalty,
              tfs_z=tfs_z,
              mirostat_mode=mirostat_mode,
              mirostat_tau=mirostat_tau,
              mirostat_eta=mirostat_eta,
              model=model,
              logits_processor=logits_processor,
              grammar=grammar,
          )
          return _convert_completion_to_chat(completion_or_completion_chunks, stream=stream)  # type: ignore
  
      if function_call is None or (
          isinstance(function_call, str) and function_call == "auto"
      ):
          stop = "\n"
          completion: llama_types.Completion = llama.create_completion(
              prompt=prompt, stop=stop, stream=False
          )  # type: ignore
          completion_text = completion["choices"][0]["text"]
          # strip " to=functions." and ending ":"
          function_call = completion_text.split(".")[-1][:-1]
          new_prompt = prompt + completion_text + stop
      elif isinstance(function_call, str) and function_call != "none":
          new_prompt = prompt + f":\n"
      elif isinstance(function_call, dict):
          new_prompt = (
              prompt + f" to=functions.{function_call['name']}:\n"
          )
          function_call = function_call["name"]
      else:
          new_prompt = prompt + f":\n"
  
      function_body = None
      for function in functions or []:
          if function["name"] == function_call:
              function_body = function["parameters"]
              break
      for tool in tools or []:
          if (
              tool["type"] == "function"
              and tool["function"]["name"] == function_call
          ):
              function_body = tool["function"]["parameters"]
              break
  
      if function_body is not None:
          try:
              with suppress_stdout_stderr(disable=llama.verbose):
                  grammar_text = llama_grammar.json_schema_to_gbnf(
                      json.dumps(function_body)
                  )
                  grammar = llama_grammar.LlamaGrammar.from_string(
                      llama_grammar.json_schema_to_gbnf(
                          json.dumps(function_body)
                      )
                  )
                  print(grammar_text)
          except Exception as e:
              if llama.verbose:
                  print(
                      "Failed to parse function body as JSON schema, falling back to default grammar"
                  )
                  print(e)
              with suppress_stdout_stderr(disable=llama.verbose):
                  grammar = llama_grammar.LlamaGrammar.from_string(
                      llama_grammar.JSON_GBNF
                  )
      else:
          with suppress_stdout_stderr(disable=llama.verbose):
              grammar = llama_grammar.LlamaGrammar.from_string(
                  llama_grammar.JSON_GBNF
              )
  
      completion: llama_types.Completion = llama.create_completion(
          prompt=new_prompt,
          stop=["user:", "</s>"],
          stream=False,
          grammar=grammar,
          max_tokens=max_tokens,
          temperature=temperature,
          top_p=top_p,
          top_k=top_k,
          min_p=min_p,
          typical_p=typical_p,
          presence_penalty=presence_penalty,
          frequency_penalty=frequency_penalty,
          repeat_penalty=repeat_penalty,
          tfs_z=tfs_z,
          mirostat_mode=mirostat_mode,
          mirostat_tau=mirostat_tau,
          mirostat_eta=mirostat_eta,
          model=model,
          logits_processor=logits_processor,
      )  # type: ignore
  
      assert "usage" in completion
      assert isinstance(function_call, str)
      assert stream is False  # TODO: support stream mode
  
      if llama.verbose:
          print(new_prompt)
          print(completion["choices"][0]["text"])
  
      # TODO: support stream mode
      return llama_types.CreateChatCompletionResponse(
          id="chat" + completion["id"],
          object="chat.completion",
          created=completion["created"],
          model=completion["model"],
          choices=[
              {
                  "index": 0,
                  "message": {
                      "role": "assistant",
                      "content": None,
                      "function_call": {
                          "name": function_call,
                          "arguments": completion["choices"][0]["text"],
                      },
                      "tool_calls": [
                          {
                              "id": function_call,
                              "type": "function",
                              "function": {
                                  "name": function_call,
                                  "arguments": completion["choices"][0][
                                      "text"
                                  ],
                              },
                          }
                      ],
                  },
                  "finish_reason": "tool_calls",
              }
          ],
          usage=completion["usage"],
      )
  
imports: from __future__ import annotations
  import os
  import json
  import ctypes
  import dataclasses
  from typing import Any, Dict, Iterator, List, Optional, Tuple, Union, Protocol
  import llama_cpp.llama
  import llama_cpp.llama_types
  import llama_cpp.llama_grammar
  from ._utils import suppress_stdout_stderr
label_desc: Use image node_id 38 for calling a global function with example usage: functionary_chat_handler(llama, messages, functions, function_call, tools, tool_choice, temperature, top_p, top_k, min_p, typical_p, stream, stop, response_format, max_tokens, presence_penalty, frequency_penalty, repeat_penalty, tfs_z, mirostat_mode, mirostat_tau, mirostat_eta, model, logits_processor, grammar) and returns: llama_types, shared_definitions, schema, str, _convert_completion_to_chat, ref_name, str, item_type, str, str, msg, str, nested_schema, str, msg, str, msg, str, str, param, str, msg, str, msg, str, str, msg, str, msg, str, str, msg, str, str, str, msg, str, str, msg, str, msg, str, msg, str, str, msg, str, msg, str, str, tool_call, str, tool_call, str, str, str, msg, str