Repository: mlc-ai/mlc-llm
File: cpp/serve/grammar/json_schema_converter.cc (C++)

Code before:
 * \file serve/grammar/json_schema_converter.cc
 */
#include <picojson.h>
#include <tvm/runtime/container/array.h>
#include <tvm/runtime/logging.h>
#include <tvm/runtime/registry.h>

#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <unordered_set>
#include <vector>

namespace mlc {
namespace llm {
namespace serve {

using namespace tvm::runtime;

/*!
 * \brief Manage the indent and separator for the generation of EBNF grammar.
 * \param indent The number of spaces for each indent. If it is std::nullopt, there will be no
 * indent or newline.
 * \param separator The separator between different elements in json. Examples include "," and ", ".
 */
class IndentManager {
 public:
  IndentManager(std::optional<int> indent, const std::string& separator)
      : enable_newline_(indent.has_value()),
        indent_(indent.value_or(0)),
        separator_(separator),
        total_indent_(0),
        is_first_({true}) {}

  /*! \brief Enter a new indent level. */
  void StartIndent() {
    total_indent_ += indent_;
    is_first_.push_back(true);
  }

  /*! \brief Exit the current indent level. */
  void EndIndent() {
    total_indent_ -= indent_;
    is_first_.pop_back();
  }

  /*!

Review comment (suggestion):
For some reason without this, EMCC would throw error complaining about invalid operands to binary expression (despite this is defined in `picojson.h`):
![image](https://github.com/mlc-ai/mlc-llm/assets/53290280/2c5543d3-811e-4f10-8b8a-9c90e06ffc17)

If I define any other dummy functions in `picojson.h` and call it in this file, it would get picked up by EMCC; but this << overload function cannot be picked up. So I redefined it here, wrapped in `COMPILE_MLC_WASM_RUNTIME` so that it does not affect other flows.

I also tried to include `picojson` in `web/emcc/mlc_wasm_runtime.cc` but still the same behavior is observed.

cc @tqchen Do you happen to know what might be the cause? Thank you!

Code after:
 * \file serve/grammar/json_schema_converter.cc
 */
#include <picojson.h>
#include <tvm/runtime/container/array.h>
#include <tvm/runtime/logging.h>
#include <tvm/runtime/registry.h>

#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <unordered_set>
#include <vector>

namespace mlc {
namespace llm {
namespace serve {

using namespace tvm::runtime;

// EMCC somehow cannot pickup operator overload from picojson.h, so we copy here.
#ifdef COMPILE_MLC_WASM_RUNTIME
inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) {
  x.serialize(std::ostream_iterator<char>(os));
  return os;
}
#endif

/*!
 * \brief Manage the indent and separator for the generation of EBNF grammar.
 * \param indent The number of spaces for each indent. If it is std::nullopt, there will be no
 * indent or newline.
 * \param separator The separator between different elements in json. Examples include "," and ", ".
 */
class IndentManager {
 public:
  IndentManager(std::optional<int> indent, const std::string& separator)
      : enable_newline_(indent.has_value()),
        indent_(indent.value_or(0)),
        separator_(separator),
        total_indent_(0),
        is_first_({true}) {}

  /*! \brief Enter a new indent level. */
  void StartIndent() {
    total_indent_ += indent_;
    is_first_.push_back(true);
  }