Full Code of baidu/unit-dmkit for AI

master 6b837ee07504 cached
97 files
849.3 KB
222.0k tokens
632 symbols
1 requests
Download .txt
Showing preview only (901K chars total). Download the full file or copy to clipboard to get everything.
Repository: baidu/unit-dmkit
Branch: master
Commit: 6b837ee07504
Files: 97
Total size: 849.3 KB

Directory structure:
gitextract_m7sg05uu/

├── .gitignore
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── NOTICE
├── README.md
├── conf/
│   ├── app/
│   │   ├── bot_tokens.json
│   │   ├── demo/
│   │   │   ├── book_hotel.json
│   │   │   ├── book_hotel.xml
│   │   │   ├── cellular_data.json
│   │   │   ├── cellular_data.xml
│   │   │   ├── quota_adjust.json
│   │   │   └── quota_adjust.xml
│   │   ├── products.json
│   │   └── remote_services.json
│   └── gflags.conf
├── deps.sh
├── docs/
│   ├── demo_book_hotel_pattern.txt
│   ├── demo_cellular_data_pattern.txt
│   ├── demo_quota_adjust_pattern.txt
│   ├── demo_skills.md
│   ├── faq.md
│   ├── tutorial.md
│   └── visual_tool.md
├── language_compiler/
│   ├── compiler_xml.py
│   ├── run.py
│   └── settings.cfg
├── proto/
│   └── http.proto
├── src/
│   ├── app_container.cpp
│   ├── app_container.h
│   ├── app_log.h
│   ├── application_base.h
│   ├── brpc.h
│   ├── butil.h
│   ├── dialog_manager.cpp
│   ├── dialog_manager.h
│   ├── file_watcher.cpp
│   ├── file_watcher.h
│   ├── policy.cpp
│   ├── policy.h
│   ├── policy_manager.cpp
│   ├── policy_manager.h
│   ├── qu_result.cpp
│   ├── qu_result.h
│   ├── rapidjson.h
│   ├── remote_service_manager.cpp
│   ├── remote_service_manager.h
│   ├── request_context.cpp
│   ├── request_context.h
│   ├── server.cpp
│   ├── thirdparty/
│   │   └── rapidjson/
│   │       ├── allocators.h
│   │       ├── document.h
│   │       ├── encodedstream.h
│   │       ├── encodings.h
│   │       ├── error/
│   │       │   ├── en.h
│   │       │   └── error.h
│   │       ├── filereadstream.h
│   │       ├── filewritestream.h
│   │       ├── fwd.h
│   │       ├── internal/
│   │       │   ├── biginteger.h
│   │       │   ├── diyfp.h
│   │       │   ├── dtoa.h
│   │       │   ├── ieee754.h
│   │       │   ├── itoa.h
│   │       │   ├── meta.h
│   │       │   ├── pow10.h
│   │       │   ├── regex.h
│   │       │   ├── stack.h
│   │       │   ├── strfunc.h
│   │       │   ├── strtod.h
│   │       │   └── swap.h
│   │       ├── istreamwrapper.h
│   │       ├── memorybuffer.h
│   │       ├── memorystream.h
│   │       ├── msinttypes/
│   │       │   ├── inttypes.h
│   │       │   └── stdint.h
│   │       ├── ostreamwrapper.h
│   │       ├── pointer.h
│   │       ├── prettywriter.h
│   │       ├── rapidjson.h
│   │       ├── reader.h
│   │       ├── schema.h
│   │       ├── stream.h
│   │       ├── stringbuffer.h
│   │       └── writer.h
│   ├── thread_data_base.h
│   ├── token_manager.cpp
│   ├── token_manager.h
│   ├── user_function/
│   │   ├── demo.cpp
│   │   ├── demo.h
│   │   ├── shared.cpp
│   │   └── shared.h
│   ├── user_function_manager.cpp
│   ├── user_function_manager.h
│   └── utils.h
└── tools/
    ├── bot_emulator.py
    └── mock_api_server.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/output
/Makefile
/brpc
/_build
/build
/.vscode
*.pyc
.DS_Store


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8.10)
project(dmkit C CXX)

execute_process(
    COMMAND bash -c "find ${CMAKE_SOURCE_DIR}/brpc -type d -regex \".*output/include$\" | xargs dirname | tr -d '\n'"
    OUTPUT_VARIABLE OUTPUT_PATH
)

set(CMAKE_PREFIX_PATH ${OUTPUT_PATH})

include(FindThreads)
include(FindProtobuf)
protobuf_generate_cpp(PROTO_SRC PROTO_HEADER proto/http.proto)
# include PROTO_HEADER
include_directories(${CMAKE_CURRENT_BINARY_DIR})

find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h)
find_library(BRPC_LIB NAMES libbrpc.a brpc)
if((NOT BRPC_INCLUDE_PATH) OR (NOT BRPC_LIB))
    message(FATAL_ERROR "Fail to find brpc")
endif()
include_directories(${BRPC_INCLUDE_PATH})

find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
if((NOT GFLAGS_INCLUDE_PATH) OR (NOT GFLAGS_LIBRARY))
    message(FATAL_ERROR "Fail to find gflags")
endif()
include_directories(${GFLAGS_INCLUDE_PATH})

execute_process(
    COMMAND bash -c "grep \"namespace [_A-Za-z0-9]\\+ {\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $2}' | tr -d '\n'"
    OUTPUT_VARIABLE GFLAGS_NS
)
if(${GFLAGS_NS} STREQUAL "GFLAGS_NAMESPACE")
    execute_process(
        COMMAND bash -c "grep \"#define GFLAGS_NAMESPACE [_A-Za-z0-9]\\+\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $3}' | tr -d '\n'"
        OUTPUT_VARIABLE GFLAGS_NS
    )
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    include(CheckFunctionExists)
    CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME)
    if(NOT HAVE_CLOCK_GETTIME)
        set(DEFINE_CLOCK_GETTIME "-DNO_CLOCK_GETTIME_IN_MAC")
    endif()
endif()

set(CMAKE_CPP_FLAGS "${DEFINE_CLOCK_GETTIME} ${CMAKE_CPP_FLAGS} -DGFLAGS_NS=${GFLAGS_NS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CPP_FLAGS} -DNDEBUG -O2 -D__const__= -pipe -W -Wall -Wno-unused-parameter -fPIC -fno-omit-frame-pointer")

if(CMAKE_VERSION VERSION_LESS "3.1.3")
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    endif()
    if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    endif()
else()
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
find_library(LEVELDB_LIB NAMES leveldb)
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
    message(FATAL_ERROR "Fail to find leveldb")
endif()
include_directories(${LEVELDB_INCLUDE_PATH})

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    set(OPENSSL_ROOT_DIR
        "/usr/local/opt/openssl"    # Homebrew installed OpenSSL
        )
endif()
include(FindOpenSSL)

find_library(CURL_LIB NAMES curl)
if (NOT CURL_LIB)
    message(FATAL_ERROR "Fail to find curl")
endif()

set(DYNAMIC_LIB
    ${CMAKE_THREAD_LIBS_INIT}
    ${GFLAGS_LIBRARY}
    ${PROTOBUF_LIBRARIES}
    ${LEVELDB_LIB}
    ${OPENSSL_LIBRARIES}
    ${OPENSSL_CRYPTO_LIBRARY}
    ${CURL_LIB}
    dl
    )

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    set(DYNAMIC_LIB ${DYNAMIC_LIB}
        pthread
        "-framework CoreFoundation"
        "-framework CoreGraphics"
        "-framework CoreData"
        "-framework CoreText"
        "-framework Security"
        "-framework Foundation"
        "-Wl,-U,_MallocExtension_ReleaseFreeMemory"
        "-Wl,-U,_ProfilerStart"
        "-Wl,-U,_ProfilerStop"
        "-Wl,-U,_RegisterThriftProtocol")
endif()

file(GLOB DMKIT_SRC
    "src/*.cpp"
    "src/*/*.cpp"
    )

include_directories(
    ${CMAKE_SOURCE_DIR}/src
    )

add_executable(dmkit ${DMKIT_SRC} ${PROTO_SRC} ${PROTO_HEADER})

target_link_libraries(dmkit ${BRPC_LIB} ${DYNAMIC_LIB})

add_custom_command(
    TARGET dmkit POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${CMAKE_SOURCE_DIR}/conf
    ${CMAKE_BINARY_DIR}/conf
    )


================================================
FILE: Dockerfile
================================================
FROM ubuntu:18.04

WORKDIR /unit-dmkit

COPY . /unit-dmkit

RUN apt-get update && apt-get install -y --no-install-recommends sudo cmake wget vim curl ca-certificates

RUN update-ca-certificates

RUN sh deps.sh ubuntu

RUN rm -rf _build && mkdir _build && cd _build && cmake .. && make -j8

EXPOSE 8010



================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: NOTICE
================================================
Copyright (c) 2018 Baidu, Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: README.md
================================================
# DMKit

DMKit作为UNIT的开源对话管理模块,可以无缝对接UNIT的理解能力,并赋予开发者多状态的复杂对话流程管理能力,还可以低成本对接外部知识库,迅速丰富话术信息量。

## 快速开始

### 编译DMKit

DMKit基于[brpc](https://github.com/brpc/brpc)开发并提供HTTP服务,支持MacOS,Ubuntu,Centos等系统环境,推荐使用Ubuntu 16.04或CentOS 7。在编译DMKit之前,需要先安装依赖并下载编译brpc:

```bash
sh deps.sh [OS]
```

其中[OS]参数指定系统类型用于安装对应系统依赖,支持取值包括ubuntu、mac、centos。如果已手动安装依赖,则传入none。

使用cmake编译DMKit:

```bash
mkdir _build && cd _build && cmake .. && make
```

### 运行示例技能

DMKit提供了示例场景技能,在运行示例技能之前,需要在UNIT平台配置实现技能的理解能力:[示例场景](docs/demo_skills.md)

根据UNIT平台创建的skill id修改编译产出_build目录下的conf/app/products.json文件,在其中配置所创建skill id与对应场景DMKit配置文件。例如,查询流量及续订场景,在UNIT平台创建skill id为12345,则对应的配置文件内容应为:

```JSON
{
    "default": {
        "12345": {
            "score": 1,
            "conf_path": "conf/app/demo/cellular_data.json"
        }
    }
}
```

在_build目录下运行DMKit:

```bash
./dmkit
```

可以通过tools目录下的bot_emulator.py程序模拟与技能进行交互,使用方法为:

```bash
python bot_emulator.py [skill id] [access token]
```

### 更多文档

* [DMKit快速上手](docs/tutorial.md)
* [可视化配置工具](docs/visual_tool.md)
* [常见问题](docs/faq.md)

### 多语言支持
* PHP:[PHP版本官方代码库](https://github.com/baidu/dm-kit-php)

## 如何贡献

* 提交issue可以是新需求也可以是bug,也可以是对某一个问题的讨论。
* 对于issues中的问题欢迎贡献并发起pull request。

## 讨论

* 提issue发起问题讨论,如果是问题选择类型为问题即可。
* 欢迎加入UNIT QQ群(584835350)交流讨论。


================================================
FILE: conf/app/bot_tokens.json
================================================
{
    "1234": {
        "api_key": "",
        "secret_key": ""  
    }
}


================================================
FILE: conf/app/demo/book_hotel.json
================================================
[
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "请问您要预订哪里的酒店"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "001"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "{%location%}附近的酒店有{%hotel_option%},请问你要预订哪一个?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "location", 
        "type": "slot_val", 
        "value": "user_location"
      }, 
      {
        "name": "hotel_option", 
        "type": "func_val", 
        "value": "service_http_get:hotel_service,/hotel/search?location={%location%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type", 
        "user_location"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "{%location%}附近的酒店有{%hotel_option%},请问你要预订哪一个?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "location", 
        "type": "slot_val", 
        "value": "user_location"
      }, 
      {
        "name": "hotel_option", 
        "type": "func_val", 
        "value": "service_http_get:hotel_service,/hotel/search?location={%location%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type", 
        "user_location"
      ], 
      "state": "001"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您选择了{%time%}{%hotel%}的{%room_type%},是否确认预订?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "hotel", 
        "type": "slot_val", 
        "value": "user_hotel"
      }, 
      {
        "name": "room_type", 
        "type": "slot_val", 
        "value": "user_room_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type", 
        "user_hotel"
      ], 
      "state": "001"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您选择了{%time%}{%hotel%}的{%room_type%},是否确认预订?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "hotel", 
        "type": "slot_val", 
        "value": "user_hotel"
      }, 
      {
        "name": "room_type", 
        "type": "slot_val", 
        "value": "user_room_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type", 
        "user_hotel"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "已为您预订{%time%}{%hotel%}的{%room_type%}"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "hotel", 
        "type": "slot_val", 
        "value": "user_hotel"
      }, 
      {
        "name": "room_type", 
        "type": "slot_val", 
        "value": "user_room_type"
      }, 
      {
        "name": "hotel_option", 
        "type": "func_val", 
        "value": "service_http_get:hotel_service,/hotel/book?time={%time%}&hotel={%hotel%}&room_type={%room_type%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "002"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "请告诉我您的新需求"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "005"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": "002"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您选择了{%time%}{%hotel%}的{%room_type%},是否确认预订?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "hotel", 
        "type": "slot_val", 
        "value": "user_hotel"
      }, 
      {
        "name": "room_type", 
        "type": "slot_val", 
        "value": "user_room_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_HOTEL", 
      "slots": [
        "user_time", 
        "user_room_type", 
        "user_location", 
        "user_hotel"
      ], 
      "state": ""
    }
  }
]


================================================
FILE: conf/app/demo/book_hotel.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="1640" dy="390" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="#ffffff" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="12" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="2" target="3" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="2" value="INTENT:INTENT_BOOK_HOTEL&lt;br&gt;SLOT:user_time,user_room_type&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="140" y="250" width="260" height="80" as="geometry"/></mxCell><mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="3" target="5" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=1;exitY=0.5;entryX=0;entryY=0.5;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="3" target="4" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="3" value="BOT:请问您要预订哪里的酒店" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="140" y="360" width="260" height="80" as="geometry"/></mxCell><mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="4" target="8" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="4" value="INTENT:INTENT_BOOK_HOTEL&lt;br&gt;SLOT:user_time,user_room_type,user_location&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="560" y="360" width="260" height="80" as="geometry"/></mxCell><mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="5" target="7" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="5" value="INTENT:INTENT_BOOK_HOTEL&lt;br&gt;SLOT:user_time,user_room_type,user_hotel&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="140" y="470" width="260" height="80" as="geometry"/></mxCell><mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="6" target="9" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="6" value="INTENT:INTENT_YES&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="140" y="710" width="260" height="80" as="geometry"/></mxCell><mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="7" target="6" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="17" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0;exitY=0.5;entryX=1;entryY=0.5;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="7" target="10" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="7" value="PARAM:slot_val:time=user_time&lt;br&gt;PARAM:slot_val:hotel=user_hotel&lt;br&gt;PARAM:slot_val:room_type=user_room_type&lt;br&gt;BOT:您选择了{%time%}{%hotel%}的{%room_type%},是否确认预订?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="65" y="590" width="410" height="80" as="geometry"/></mxCell><mxCell id="8" value="&lt;br&gt;PARAM:slot_val:location=user_location&lt;br&gt;PARAM:func_val:hotel_option=&lt;span&gt;service_http_get:hotel_service,/hotel/search?location={%location%}&lt;/span&gt;&lt;br&gt;BOT:{%location%}附近的酒店有{%hotel_option%},请问你要预订哪一个?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="440" y="470" width="500" height="80" as="geometry"/></mxCell><mxCell id="9" value="PARAM:slot_val:time=user_time&lt;br&gt;PARAM:slot_val:hotel=user_hotel&lt;br&gt;PARAM:slot_val:room_type=user_room_type&lt;br&gt;PARAM:func_val:hotel_option=service_http_get:hotel_service,/hotel/book?time={%time%}&amp;amp;hotel={%hotel%}&amp;amp;room_type={%room_type%}&lt;br&gt;BOT:已为您预订{%time%}{%hotel%}的{%room_type%}" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="-80" y="830" width="700" height="80" as="geometry"/></mxCell><mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=0;entryX=0.5;entryY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="10" target="11" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="10" value="INTENT:INTENT_NO&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="-170" y="590" width="190" height="80" as="geometry"/></mxCell><mxCell id="11" value="BOT:请告诉我您的新需求" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="-170" y="470" width="190" height="80" as="geometry"/></mxCell><mxCell id="22" value="" style="endArrow=classic;html=1;strokeWidth=2;entryX=0;entryY=0.5;" parent="1" target="5" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="100" y="240" as="sourcePoint"/><mxPoint x="490" y="335" as="targetPoint"/><Array as="points"><mxPoint x="100" y="510"/></Array></mxGeometry></mxCell><mxCell id="24" value="" style="endArrow=classic;html=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" target="4" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="690" y="270" as="sourcePoint"/><mxPoint x="750" y="260" as="targetPoint"/></mxGeometry></mxCell><mxCell id="25" value="" style="endArrow=classic;html=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" target="2" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="270" y="190" as="sourcePoint"/><mxPoint x="300" y="210" as="targetPoint"/></mxGeometry></mxCell><mxCell id="28" value="INTENT:INTENT_BOOK_HOTEL&lt;br&gt;SLOT:user_time,user_room_type,user_location,user_hotel&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="530" y="590" width="330" height="80" as="geometry"/></mxCell><mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;entryX=1;entryY=0.5;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" edge="1" parent="1" target="28"><mxGeometry relative="1" as="geometry"><mxPoint x="930" y="630" as="sourcePoint"/><mxPoint x="740" y="740" as="targetPoint"/></mxGeometry></mxCell><mxCell id="30" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0;exitY=0.5;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=1;entryY=0.5;" edge="1" parent="1" source="28" target="7"><mxGeometry relative="1" as="geometry"><mxPoint x="290" y="570" as="sourcePoint"/><mxPoint x="540" y="670" as="targetPoint"/></mxGeometry></mxCell></root></mxGraphModel>

================================================
FILE: conf/app/demo/cellular_data.json
================================================
[
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您想查询几月份的流量?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "001"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_CHECK_DATA_USAGE", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [
          {
            "type": "ge", 
            "value": "{%left%},1"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您的省内流量2GB,已用流量为{%usage%}GB;剩余流量为{%left%}G,全国流量包1GB,已用流量为1GB,剩余流量为0GB"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }, 
      {
        "assertion": [
          {
            "type": "gt", 
            "value": "1,{%left%}"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您的省内流量2GB,已用流量为{%usage%}GB,剩余流量为{%left%}G;全国流量包1GB,已用流量为1GB,剩余流量为0GB。发现您的流量余额已经不足了,您是否需要续订流量包呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "usage", 
        "type": "func_val", 
        "value": "demo_get_cellular_data_usage:{%time%}"
      }, 
      {
        "name": "left", 
        "type": "func_val", 
        "value": "demo_get_cellular_data_left:{%time%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_CHECK_DATA_USAGE", 
      "slots": [
        "user_time"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [
          {
            "type": "ge", 
            "value": "{%left%},1"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您的省内流量2GB,已用流量为{%usage%}GB;剩余流量为{%left%}G,全国流量包1GB,已用流量为1GB,剩余流量为0GB"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }, 
      {
        "assertion": [
          {
            "type": "gt", 
            "value": "1,{%left%}"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您的省内流量2GB,已用流量为{%usage%}GB,剩余流量为{%left%}G;全国流量包1GB,已用流量为1GB,剩余流量为0GB。发现您的流量余额已经不足了,您是否需要续订流量包呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "time", 
        "type": "slot_val", 
        "value": "user_time"
      }, 
      {
        "name": "usage", 
        "type": "func_val", 
        "value": "demo_get_cellular_data_usage:{%time%}"
      }, 
      {
        "name": "left", 
        "type": "func_val", 
        "value": "demo_get_cellular_data_left:{%time%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_CHECK_DATA_USAGE", 
      "slots": [
        "user_time"
      ], 
      "state": "001"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "王女士您好,我们有省内,全国,夜间三种流量包,您是要续订什么流量包?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "003"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问还有什么可以帮助您的吗"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "007"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": "003"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的,{%type%}有如下选择:{%options%}。请问您想续订哪种?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "005"
        }
      }
    ], 
    "params": [
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_package_type"
      }, 
      {
        "name": "options", 
        "type": "func_val", 
        "value": "demo_get_package_options:{%type%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_DATA_PACKAGE", 
      "slots": [
        "user_package_type"
      ], 
      "state": "004"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,您是想续订{%name%}的{%type%}吗?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "006"
        }
      }
    ], 
    "params": [
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_package_type"
      }, 
      {
        "name": "name", 
        "type": "slot_val", 
        "value": "user_package_name"
      }
    ], 
    "trigger": {
      "intent": "INTENT_BOOK_DATA_PACKAGE", 
      "slots": [
        "user_package_type", 
        "user_package_name"
      ], 
      "state": "005"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问还有什么可以帮助您的吗"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "007"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "006"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "王女士您好,我们有省内,全国,夜间三种流量包,您是要续订什么流量包?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_BOOK_DATA_PACKAGE", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "王女士您好,我们有省内,全国,夜间三种流量包,您是要续订什么流量包?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_BOOK_DATA_PACKAGE", 
      "slots": [], 
      "state": "003"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的,很高兴为您服务"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "008"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": "007"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "{%dmkit_param_last_tts%}"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "{%state%}"
        }
      }
    ], 
    "params": [
      {
        "name": "state", 
        "type": "session_state", 
        "value": ""
      }
    ], 
    "trigger": {
      "intent": "INTENT_REPEAT", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "嗯,您说?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "009"
        }
      }
    ], 
    "params": [
      {
        "name": "dmkit_param_context_state", 
        "type": "session_state", 
        "value": ""
      }, 
      {
        "name": "dmkit_param_context_tts", 
        "type": "string", 
        "value": "{%dmkit_param_last_tts%}"
      }
    ], 
    "trigger": {
      "intent": "INTENT_WAIT", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "{%dmkit_param_context_tts%}"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "{%dmkit_param_context_state%}"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_CONTINUE", 
      "slots": [], 
      "state": "009"
    }
  }
]


================================================
FILE: conf/app/demo/cellular_data.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="790" dy="376" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="#ffffff" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="4" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="2" target="7" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="310" y="220" as="targetPoint"/></mxGeometry></mxCell><mxCell id="47" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="2" target="7" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="2" value="INTENT:&lt;span&gt;INTENT_CHECK_DATA_USAGE&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="150" y="80" width="320" height="80" as="geometry"/></mxCell><mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="7" target="5" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="310" y="280" as="sourcePoint"/></mxGeometry></mxCell><mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="5" target="8" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="5" value="INTENT:INTENT_CHECK_DATA_USAGE&lt;br&gt;SLOT:&lt;span&gt;user_time&lt;/span&gt;&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="150" y="370" width="320" height="80" as="geometry"/></mxCell><mxCell id="7" value="BOT:您想查询几月份的流量?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="150" y="220" width="320" height="100" as="geometry"/></mxCell><mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="8" target="11" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="100" y="690" as="targetPoint"/></mxGeometry></mxCell><mxCell id="39" value="&lt;span&gt;ge:{%&lt;/span&gt;&lt;span&gt;left&lt;/span&gt;&lt;span&gt;%},1&lt;/span&gt;" style="text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;" parent="10" vertex="1" connectable="0"><mxGeometry x="0.1" y="2" relative="1" as="geometry"><mxPoint as="offset"/></mxGeometry></mxCell><mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="8" target="12" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="40" value="gt:1,{%left%}" style="text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;" parent="13" vertex="1" connectable="0"><mxGeometry x="-0.037" y="2" relative="1" as="geometry"><mxPoint as="offset"/></mxGeometry></mxCell><mxCell id="8" value="PARAM:slot_val:time=&lt;span&gt;user_time&lt;/span&gt;&lt;br&gt;PARAM:func_val:usage=&lt;span&gt;demo_get_cellular_data_usage&lt;/span&gt;&lt;span&gt;:{%time%}&lt;br&gt;&lt;/span&gt;PARAM:func_val:left=demo_get_cellular_data_left:{%time%}&lt;br&gt;" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="100" y="510" width="420" height="100" as="geometry"/></mxCell><mxCell id="11" value="BOT:您的省内流量2GB,已用流量为{%usage%}GB;剩余流量为{%left%}G,全国流量包1GB,已用流量为1GB,剩余流量为0GB&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="20" y="700" width="200" height="100" as="geometry"/></mxCell><mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.25;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="12" target="16" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="930" as="targetPoint"/></mxGeometry></mxCell><mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.75;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="12" target="17" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="700" y="920" as="targetPoint"/></mxGeometry></mxCell><mxCell id="58" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="12" target="50" edge="1"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="490" y="890"/><mxPoint x="574" y="890"/></Array></mxGeometry></mxCell><mxCell id="12" value="BOT:您的省内流量2GB,已用流量为{%usage%}GB,剩余流量为{%left%}G;全国流量包1GB,已用流量为1GB,剩余流量为0GB。发现您的流量余额已经不足了,您是否需要续订流量包呢?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="390" y="700" width="200" height="100" as="geometry"/></mxCell><mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="16" target="20" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="350" y="1080" as="targetPoint"/></mxGeometry></mxCell><mxCell id="16" value="INTENT:INTENT_YES" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="150" y="940" width="250" height="80" as="geometry"/></mxCell><mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=1;entryY=0.5;" parent="1" source="17" target="53" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="560" y="2375" as="targetPoint"/></mxGeometry></mxCell><mxCell id="17" value="INTENT:INTENT_NO" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="740" y="940" width="270" height="80" as="geometry"/></mxCell><mxCell id="25" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="20" target="23" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="20" value="&lt;span&gt;BOT:王女士您好,我们有省内,全国,夜间三种流量包,您是要续订什么流量包?&lt;br&gt;&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="150" y="1100" width="250" height="90" as="geometry"/></mxCell><mxCell id="30" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="23" target="31" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="275" y="1440" as="targetPoint"/></mxGeometry></mxCell><mxCell id="23" value="&lt;span&gt;INTENT:INTENT_BOOK_DATA_PACKAGE&lt;br&gt;SLOT:user_package_type&lt;br&gt;&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="130" y="1290" width="290" height="80" as="geometry"/></mxCell><mxCell id="33" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="31" target="32" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="31" value="PARAM:slot_val:type=user_package_type&lt;br&gt;PARAM:func_val:options=demo_get_package_options:{%type%}&lt;br&gt;BOT:好的,{%type%}有如下选择:{%options%}。请问您想续订哪种?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="90" y="1440" width="370" height="70" as="geometry"/></mxCell><mxCell id="34" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="32" target="35" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="275" y="1710" as="targetPoint"/></mxGeometry></mxCell><mxCell id="32" value="&lt;span&gt;INTENT:INTENT_BOOK_DATA_PACKAGE&lt;br&gt;SLOT:&lt;/span&gt;user_package_type,user_package_name&lt;span&gt;&lt;br&gt;&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="130" y="1580" width="290" height="80" as="geometry"/></mxCell><mxCell id="36" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="35" target="37" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="350" y="1810" as="targetPoint"/></mxGeometry></mxCell><mxCell id="35" value="PARAM:slot_val:type=user_package_type&lt;br&gt;PARAM:slot_val:name=user_package_name&lt;span&gt;&lt;br&gt;BOT:&lt;/span&gt;好的王女士,您是想续订{%name%}的{%type%}吗?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="100" y="1720" width="350" height="60" as="geometry"/></mxCell><mxCell id="38" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="37" target="53" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="275" y="1970" as="targetPoint"/></mxGeometry></mxCell><mxCell id="37" value="&lt;span&gt;INTENT:INTENT_YES&lt;br&gt;&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="130" y="1830" width="290" height="80" as="geometry"/></mxCell><mxCell id="48" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" target="2" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="310" y="20" as="sourcePoint"/><mxPoint x="320" y="230" as="targetPoint"/></mxGeometry></mxCell><mxCell id="49" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0;entryY=0.5;" parent="1" target="5" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="80" y="410" as="sourcePoint"/><mxPoint x="80" y="420" as="targetPoint"/></mxGeometry></mxCell><mxCell id="52" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=1;entryY=0.5;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="50" target="20" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="50" value="&lt;span&gt;INTENT:INTENT_BOOK_DATA_PACKAGE&lt;br&gt;&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="429" y="940" width="290" height="80" as="geometry"/></mxCell><mxCell id="54" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" source="53" target="55" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="275" y="2150" as="targetPoint"/></mxGeometry></mxCell><mxCell id="53" value="&lt;span&gt;BOT:好的王女士,请问还有什么可以帮助您的吗&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="125" y="2000" width="300" height="90" as="geometry"/></mxCell><mxCell id="57" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="55" target="56" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="55" value="&lt;span&gt;INTENT:INTENT_NO&lt;br&gt;&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="130" y="2150" width="290" height="80" as="geometry"/></mxCell><mxCell id="56" value="&lt;span&gt;BOT:好的,很高兴为您服务&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="125" y="2290" width="300" height="90" as="geometry"/></mxCell><mxCell id="59" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;jettySize=auto;orthogonalLoop=1;strokeWidth=2;entryX=1;entryY=0;" parent="1" target="50" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="750" y="810" as="targetPoint"/><mxPoint x="677" y="610" as="sourcePoint"/></mxGeometry></mxCell><mxCell id="63" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" parent="1" source="60" target="61" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="60" value="INTENT:INTENT_REPEAT" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="590" y="80" width="240" height="80" as="geometry"/></mxCell><mxCell id="61" value="PARAM:&lt;span&gt;session_state&lt;/span&gt;:state=&lt;span&gt;&lt;br&gt;BOT:{%&lt;/span&gt;&lt;span&gt;dmkit_param_last_tts&lt;/span&gt;&lt;span&gt;%}&lt;/span&gt;&lt;br&gt;&lt;span&gt;STATE:{%state%}&lt;br&gt;&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="580" y="230" width="260" height="60" as="geometry"/></mxCell><mxCell id="62" value="" style="endArrow=classic;html=1;strokeWidth=2;entryX=0.5;entryY=0;" parent="1" target="60" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="710" y="20" as="sourcePoint"/><mxPoint x="740" y="20" as="targetPoint"/></mxGeometry></mxCell><mxCell id="67" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" edge="1" parent="1" source="64" target="66"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="64" value="INTENT:INTENT_WAIT" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="960" y="80" width="240" height="80" as="geometry"/></mxCell><mxCell id="65" value="" style="endArrow=classic;html=1;strokeWidth=2;entryX=0.5;entryY=0;" edge="1" parent="1" target="64"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="1080" y="20" as="sourcePoint"/><mxPoint x="1069.5" y="70" as="targetPoint"/></mxGeometry></mxCell><mxCell id="71" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" edge="1" parent="1" source="66" target="70"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="66" value="PARAM:session_state:&lt;span&gt;dmkit_param_context_state&lt;/span&gt;&lt;span&gt;=&lt;br&gt;&lt;/span&gt;PARAM:string:dmkit_param_context_tts={%dmkit_param_last_tts%}&lt;br&gt;&lt;span&gt;BOT:&lt;/span&gt;&lt;span lang=&quot;ZH-CN&quot;&gt;嗯,您说?&lt;/span&gt;&lt;span&gt;&lt;br&gt;&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="880" y="230" width="400" height="60" as="geometry"/></mxCell><mxCell id="73" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;jettySize=auto;orthogonalLoop=1;strokeWidth=2;" edge="1" parent="1" source="70" target="72"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="70" value="INTENT:INTENT_CONTINUE" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="960" y="350" width="240" height="80" as="geometry"/></mxCell><mxCell id="72" value="&lt;span&gt;BOT:{%&lt;/span&gt;dmkit_param_context_tts&lt;span&gt;%}&lt;/span&gt;&lt;br&gt;&lt;span&gt;STATE:{%&lt;/span&gt;dmkit_param_context_state&lt;span&gt;%}&lt;br&gt;&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="880" y="500" width="400" height="60" as="geometry"/></mxCell></root></mxGraphModel>

================================================
FILE: conf/app/demo/quota_adjust.json
================================================
[
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": [
              "您需要调整临时额度还是固定额度?", 
              "请问您要调整固定额度还是临时额度呢?"
            ]
          }
        ], 
        "session": {
          "context": {}, 
          "state": "001"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [
          {
            "type": "eq", 
            "value": "{%type%},临时"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "临时额度只能提升,请问您是否需要提升临时额度?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "007"
        }
      }, 
      {
        "assertion": [
          {
            "type": "eq", 
            "value": "{%type%},固定"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您需要提升额度还是降低额度?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }
    ], 
    "params": [
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [
          {
            "type": "eq", 
            "value": "{%type%},临时"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "临时额度只能提升,请问您是否需要提升临时额度?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "007"
        }
      }, 
      {
        "assertion": [
          {
            "type": "eq", 
            "value": "{%type%},固定"
          }
        ], 
        "result": [
          {
            "type": "tts", 
            "value": "您需要提升额度还是降低额度?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "002"
        }
      }
    ], 
    "params": [
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type"
      ], 
      "state": "001"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问您要{%method%}到多少呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_method"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问您要{%method%}到多少呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_method"
      ], 
      "state": "010"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问您要{%method%}到多少呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "003"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_method"
      ], 
      "state": "002"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您确认要将银行卡{%type%}{%method%}至{%amount%}吗?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }, 
      {
        "name": "amount", 
        "type": "slot_val", 
        "value": "user_amount"
      }, 
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_method", 
        "user_amount"
      ], 
      "state": "003"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您确认要将银行卡{%type%}{%method%}至{%amount%}吗?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "004"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }, 
      {
        "name": "amount", 
        "type": "slot_val", 
        "value": "user_amount"
      }, 
      {
        "name": "type", 
        "type": "slot_val", 
        "value": "user_type"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_method", 
        "user_amount"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": [
              "很高兴为您服务,还有其他需要帮助的吗?", 
              "好的,请问还有其他能帮到您的吗?"
            ]
          }
        ], 
        "session": {
          "context": {}, 
          "state": "005"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "004"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": [
              "很高兴为您服务,还有其他需要帮助的吗?", 
              "好的,请问还有其他能帮到您的吗?"
            ]
          }
        ], 
        "session": {
          "context": {}, 
          "state": "005"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "011"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "很高兴为您服务,王女士再见!"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "006"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": "009"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "很高兴为您服务,王女士再见!"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "006"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": "005"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "好的王女士,请问您要提升到多少呢?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "008"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_YES", 
      "slots": [], 
      "state": "007"
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "不好意思,临时额度只能提升,请问还有其他需要帮助的吗?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "009"
        }
      }
    ], 
    "params": [], 
    "trigger": {
      "intent": "INTENT_NO", 
      "slots": [], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您是要{%method%}固定额度还是临时额度?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "010"
        }
      }
    ], 
    "params": [
      {
        "name": "method", 
        "type": "slot_val", 
        "value": "user_method"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_method"
      ], 
      "state": ""
    }
  }, 
  {
    "output": [
      {
        "assertion": [], 
        "result": [
          {
            "type": "tts", 
            "value": "您确认要将银行卡临时额度提升至{%amount%}吗?"
          }
        ], 
        "session": {
          "context": {}, 
          "state": "011"
        }
      }
    ], 
    "params": [
      {
        "name": "amount", 
        "type": "slot_val", 
        "value": "user_amount"
      }
    ], 
    "trigger": {
      "intent": "INTENT_ADJUST_QUOTA", 
      "slots": [
        "user_type", 
        "user_amount"
      ], 
      "state": "008"
    }
  }
]


================================================
FILE: conf/app/demo/quota_adjust.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="919" dy="541" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="1654" background="#ffffff" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="305" y="50" width="120" height="80" as="geometry"/></mxCell><mxCell id="4" value="BOT: 您需要调整临时额度还是固定额度?| 请问您要调整固定额度还是临时额度呢?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="305" y="160" width="120" height="60" as="geometry"/></mxCell><mxCell id="7" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="2" target="4" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="160" y="170" as="sourcePoint"/><mxPoint x="210" y="120" as="targetPoint"/></mxGeometry></mxCell><mxCell id="8" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="4" target="9" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="200" y="380" as="sourcePoint"/><mxPoint x="365" y="250" as="targetPoint"/></mxGeometry></mxCell><mxCell id="9" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;SLOT: user_type&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="305" y="260" width="120" height="80" as="geometry"/></mxCell><mxCell id="12" value="BOT: 您需要提升额度还是降低额度?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="186.5" y="520" width="120" height="60" as="geometry"/></mxCell><mxCell id="14" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;SLOT: user_type, user_method&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="186.5" y="630" width="120" height="80" as="geometry"/></mxCell><mxCell id="19" value="PARAM: slot_val: method = user_method&lt;br&gt;BOT: 好的王女士,请问您要{%method%} 到多少呢?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="186.5" y="767" width="120" height="60" as="geometry"/></mxCell><mxCell id="21" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;SLOT: user_type, user_method, user_amount&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1"><mxGeometry x="186.5" y="890" width="120" height="80" as="geometry"/></mxCell><mxCell id="23" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="14" target="19" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="101.5" y="850" as="sourcePoint"/><mxPoint x="151.5" y="800" as="targetPoint"/></mxGeometry></mxCell><mxCell id="24" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="19" target="21" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="131.5" y="1030" as="sourcePoint"/><mxPoint x="181.5" y="980" as="targetPoint"/></mxGeometry></mxCell><mxCell id="27" value="PARAM: slot_val: method = user_method&lt;br&gt;PARAM: slot_val: amount = user_amount&lt;br&gt;PARAM: slot_val: type&amp;nbsp;= user_type&lt;br&gt;BOT: 您确认要将银行卡{%type%} {%method%} 至 {%amount%} 吗?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="40" y="1060" width="411" height="60" as="geometry"/></mxCell><mxCell id="28" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;" parent="1" source="21" target="27" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="211.5" y="1200" as="sourcePoint"/><mxPoint x="261.5" y="1150" as="targetPoint"/></mxGeometry></mxCell><mxCell id="29" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="27" target="30" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="171.5" y="1200" as="sourcePoint"/><mxPoint x="271.5" y="1110" as="targetPoint"/></mxGeometry></mxCell><mxCell id="30" value="INTENT: INTENT_YES" style="ellipse;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1"><mxGeometry x="185" y="1200" width="120" height="80" as="geometry"/></mxCell><mxCell id="31" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="30" target="32" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="351.5" y="1240" as="sourcePoint"/><mxPoint x="271.5" y="1220" as="targetPoint"/></mxGeometry></mxCell><mxCell id="32" value="BOT: 很高兴为您服务,还有其他需要帮助的吗?| 好的,请问还有其他能帮到您的吗?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="185" y="1320" width="120" height="60" as="geometry"/></mxCell><mxCell id="38" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="32" target="39" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="411.5" y="1360" as="sourcePoint"/><mxPoint x="461.5" y="1310" as="targetPoint"/></mxGeometry></mxCell><mxCell id="39" value="INTENT: INTENT_NO" style="ellipse;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1"><mxGeometry x="185" y="1430" width="120" height="80" as="geometry"/></mxCell><mxCell id="40" value="BOT: 很高兴为您服务,王女士再见!&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="185" y="1560" width="120" height="60" as="geometry"/></mxCell><mxCell id="41" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="39" target="40" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="391.5" y="1420" as="sourcePoint"/><mxPoint x="441.5" y="1370" as="targetPoint"/></mxGeometry></mxCell><mxCell id="43" value="BOT: 临时额度只能提升,请问您是否需要提升临时额度?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="426.5" y="520" width="120" height="60" as="geometry"/></mxCell><mxCell id="44" value="INTENT: INTENT_YES&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1"><mxGeometry x="395" y="640" width="120" height="80" as="geometry"/></mxCell><mxCell id="47" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="44" target="48" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="791.5" y="820" as="sourcePoint"/><mxPoint x="721.5" y="760" as="targetPoint"/></mxGeometry></mxCell><mxCell id="48" value="BOT: 好的王女士,请问您要提升到多少呢?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="395" y="767" width="120" height="60" as="geometry"/></mxCell><mxCell id="53" value="INTENT: INTENT_NO&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1"><mxGeometry x="611.5" y="640" width="120" height="80" as="geometry"/></mxCell><mxCell id="54" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;exitX=0.5;exitY=1;" parent="1" source="53" target="55" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="700" y="740" as="sourcePoint"/><mxPoint x="671.5" y="760" as="targetPoint"/></mxGeometry></mxCell><mxCell id="55" value="BOT: 不好意思,临时额度只能提升,请问还有其他需要帮助的吗?&lt;br&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="611.5" y="770" width="120" height="60" as="geometry"/></mxCell><mxCell id="58" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="12" target="14" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="121.5" y="750" as="sourcePoint"/><mxPoint x="171.5" y="700" as="targetPoint"/></mxGeometry></mxCell><mxCell id="63" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;" parent="1" target="2" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="365" y="10" as="sourcePoint"/><mxPoint x="130" y="110" as="targetPoint"/></mxGeometry></mxCell><mxCell id="64" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;" parent="1" target="9" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="230" y="300" as="sourcePoint"/><mxPoint x="260" y="290" as="targetPoint"/></mxGeometry></mxCell><mxCell id="65" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="9" target="69" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="490" y="450" as="sourcePoint"/><mxPoint x="410" y="350" as="targetPoint"/></mxGeometry></mxCell><mxCell id="69" value="PARAM: slot_val: type = user_type" style="rhombus;whiteSpace=wrap;html=1;rounded=0;" parent="1" vertex="1"><mxGeometry x="275" y="400" width="180" height="80" as="geometry"/></mxCell><mxCell id="70" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="69" target="12" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="150" y="510" as="sourcePoint"/><mxPoint x="200" y="460" as="targetPoint"/></mxGeometry></mxCell><mxCell id="72" value="eq: {%type%}, 固定" style="text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;" parent="70" vertex="1" connectable="0"><mxGeometry x="0.1013" y="-1" relative="1" as="geometry"><mxPoint as="offset"/></mxGeometry></mxCell><mxCell id="71" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="69" target="43" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="470" y="490" as="sourcePoint"/><mxPoint x="520" y="440" as="targetPoint"/></mxGeometry></mxCell><mxCell id="73" value="eq: {%type%}, 临时&lt;br&gt;" style="text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;" parent="71" vertex="1" connectable="0"><mxGeometry x="-0.037" relative="1" as="geometry"><mxPoint as="offset"/></mxGeometry></mxCell><mxCell id="74" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;" parent="1" target="14" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="110" y="670" as="sourcePoint"/><mxPoint x="100" y="650" as="targetPoint"/></mxGeometry></mxCell><mxCell id="75" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;" parent="1" target="21" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="100" y="930" as="sourcePoint"/><mxPoint x="130" y="910" as="targetPoint"/></mxGeometry></mxCell><mxCell id="81" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="43" target="44" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="680" y="630" as="sourcePoint"/><mxPoint x="730" y="580" as="targetPoint"/></mxGeometry></mxCell><mxCell id="82" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;edgeStyle=orthogonalEdgeStyle;" parent="1" target="53" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="490" y="580" as="sourcePoint"/><mxPoint x="670" y="620" as="targetPoint"/><Array as="points"><mxPoint x="490" y="610"/><mxPoint x="672" y="610"/></Array></mxGeometry></mxCell><mxCell id="88" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=1;entryY=0.5;edgeStyle=orthogonalEdgeStyle;" parent="1" source="55" target="39" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="680" y="970" as="sourcePoint"/><mxPoint x="490" y="1190" as="targetPoint"/></mxGeometry></mxCell><mxCell id="89" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;SLOT: user_method&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=0;" parent="1" vertex="1"><mxGeometry x="540" y="260" width="120" height="80" as="geometry"/></mxCell><mxCell id="90" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;" parent="1" target="89" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="600" y="210" as="sourcePoint"/><mxPoint x="720" y="410" as="targetPoint"/></mxGeometry></mxCell><mxCell id="92" value="PARAM: slot_val: method = user_method&lt;br&gt;BOT:&amp;nbsp; 您是要{%method%}固定额度还是临时额度?" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="540" y="380" width="120" height="60" as="geometry"/></mxCell><mxCell id="93" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" parent="1" source="89" target="92" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="750" y="550" as="sourcePoint"/><mxPoint x="770" y="330" as="targetPoint"/></mxGeometry></mxCell><mxCell id="94" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=1;entryY=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="92" target="14" edge="1"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="680" y="560" as="sourcePoint"/><mxPoint x="730" y="510" as="targetPoint"/></mxGeometry></mxCell><mxCell id="95" value="INTENT: INTENT_ADJUST_QUOTA&lt;br&gt;SLOT: user_type, user_amount&lt;br&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="1"><mxGeometry x="395" y="890" width="120" height="80" as="geometry"/></mxCell><mxCell id="96" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" edge="1" parent="1" source="48" target="95"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="610" y="1000" as="sourcePoint"/><mxPoint x="660" y="950" as="targetPoint"/></mxGeometry></mxCell><mxCell id="97" value="&lt;span&gt;PARAM: slot_val: amount = user_amount&lt;/span&gt;&lt;br&gt;&lt;span&gt;BOT: 您确认要将银行卡临时额度提升至 {%amount%} 吗?&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="504" y="1060" width="335" height="60" as="geometry"/></mxCell><mxCell id="98" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;entryX=0.25;entryY=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="95" target="97"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="620" y="1020" as="sourcePoint"/><mxPoint x="670" y="970" as="targetPoint"/></mxGeometry></mxCell><mxCell id="99" value="" style="endArrow=classic;html=1;exitX=0.25;exitY=1;entryX=1;entryY=0.5;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="97" target="30"><mxGeometry width="50" height="50" relative="1" as="geometry"><mxPoint x="490" y="1290" as="sourcePoint"/><mxPoint x="540" y="1240" as="targetPoint"/></mxGeometry></mxCell></root></mxGraphModel>

================================================
FILE: conf/app/products.json
================================================
{
    "default": {
        "4050": {
            "score": 1,
            "conf_path": "conf/app/demo/cellular_data.json"
        },
        "1234": {
            "score": 1,
            "conf_path": "conf/app/demo/quota_adjust.json"
        },
        "11669": {
            "score": 1,
            "conf_path": "conf/app/demo/book_hotel.json"
        }
    }
}



================================================
FILE: conf/app/remote_services.json
================================================
{
    "unit_bot": {
        "naming_service_url": "https://aip.baidubce.com",
        "load_balancer_name": "",
        "protocol": "http",
        "client": "brpc",
        "timeout_ms": 3000,
        "retry": 1,
        "headers": {
            "Host": "aip.baidubce.com",
            "Content-Type": "application/json"
        }
    },
    "token_auth": {
        "naming_service_url": "https://aip.baidubce.com",
        "load_balancer_name": "",
        "protocol": "http",
        "client": "brpc",
        "timeout_ms": 3000,
        "retry": 1,
        "headers": {
            "Host": "aip.baidubce.com"
        }
    },
    "hotel_service": {
        "naming_service_url": "http://127.0.0.1:5000",
        "load_balancer_name": "random",
        "protocol": "http",
        "client": "brpc",
        "timeout_ms": 3000,
        "retry": 1,
        "headers": {
        }
    }
}


================================================
FILE: conf/gflags.conf
================================================
# TCP Port of this server
--port=8010

# Only allow builtin services at this port
--internal_port=8011

# Connection will be closed if there is no read/write operations during this time
--idle_timeout_s=-1

# Limit of requests processing in parallel
--max_concurrency=0

# Url path of the app
--url_path=/search

# Log to file
--log_to_file=true



================================================
FILE: deps.sh
================================================
#!/usr/bin/env bash

cd "$(dirname "$0")"
JOBS=8
OS=$1
case "$OS" in
mac)
    ;;
ubuntu)
    ;;
centos)
    ;;
none)
    ;;
*)
    echo "Usage: $0 [ubuntu|mac|centos|none]"
    exit 1
esac

if [ $OS = mac ]; then
    echo "Installing dependencies for MacOS..."
    brew install openssl git gnu-getopt gflags protobuf leveldb cmake openssl
elif [ $OS = ubuntu ]; then
    echo "Installing dependencies for Ubuntu..."
    sudo apt-get install -y git \
        g++ \
        make \
        libssl-dev \
        coreutils \
        libgflags-dev \
        libprotobuf-dev \
        libprotoc-dev \
        protobuf-compiler \
        libleveldb-dev \
        libsnappy-dev \
        libcurl4-openssl-dev \
        libgoogle-perftools-dev
elif [ $OS = centos ]; then
    echo "Installing dependencies for CentOS..."
    sudo yum install -y epel-release
    sudo yum install -y git gcc-c++ make openssl-devel libcurl-devel
    sudo yum install -y gflags-devel protobuf-devel protobuf-compiler leveldb-devel gperftools-devel
else
    echo "Skipping dependencies installation..."
fi


if [ ! -e brpc ]; then
    echo "Cloning brpc..."
    git clone https://github.com/brpc/brpc.git
fi
cd brpc
git checkout master
git pull
git checkout 2ae7f04ce513c6aee27545df49d5439a98ae3a3f

#build brpc
echo "Building brpc..."
rm -rf _build
mkdir -p _build
cd _build
cmake ..
make -j$JOBS

cd ../../



================================================
FILE: docs/demo_book_hotel_pattern.txt
================================================
INTENT_BOOK_HOTEL	0.8	[D:user_hotel]#@##0#@##1	1
INTENT_YES	0.7	[D:kw_yes]#@##0#@##1	1
INTENT_NO	0.7	[D:kw_no]#@##0#@##1	1
INTENT_BOOK_HOTEL	0.7	[D:kw_book]#@##0#@##1#@@##[D:kw_hotel]#@##0#@##1#@@##[D:user_time]#@##0#@##0#@@##[D:user_room_type]#@##0#@##0#@@##[D:user_hotel]#@##0#@##0#@@##[D:user_location]#@##0#@##0	1


================================================
FILE: docs/demo_cellular_data_pattern.txt
================================================
INTENT_CONTINUE	0.8	[D:kw_continue]#@##0#@##1	1
INTENT_WAIT	0.8	[D:kw_wait]#@##0#@##1	1
INTENT_CHECK_DATA_USAGE	0.8	[D:user_time]#@##0#@##1	1
INTENT_REPEAT	0.7	[D:kw_repeat]#@##0#@##1	1
INTENT_BOOK_DATA_PACKAGE	0.9	[D:user_package_type]#@##0#@##1	1
INTENT_BOOK_DATA_PACKAGE	0.9	[D:kw_book]#@##0#@##1#@@##[D:kw_package]#@##0#@##0#@@##[D:user_package_type]#@##0#@##0#@@##[D:user_package_name]#@##0#@##0	1
INTENT_CHECK_DATA_USAGE	0.9	[D:kw_check]#@##0#@##1#@@##[D:user_time]#@##0#@##0#@@##[D:kw_data_usage]#@##0#@##1	1
INTENT_NO	0.9	[D:kw_no]#@##0#@##1	1
INTENT_YES	0.9	[D:kw_yes]#@##0#@##1	1


================================================
FILE: docs/demo_quota_adjust_pattern.txt
================================================
INTENT_NO	0.9	[D:kw_no]#@##0#@##1	1
INTENT_YES	0.9	[D:kw_yes]#@##0#@##1	1
INTENT_ADJUST_QUOTA	0.9	[D:kw_adjust]#@##0#@##0#@@##[D:kw_quota]#@##0#@##0#@@##[D:user_type]#@##0#@##0#@@##[D:user_method]#@##0#@##0#@@##[D:user_amount]#@##0#@##0	1


================================================
FILE: docs/demo_skills.md
================================================
# DMKit示例场景

## 查询流量及续订

该场景实现一个简单的手机流量查询及续订流量包的技能。

### 查询流量及续订 - UNIT平台配置

一共分为以下几个步骤:

* 创建技能
* 配置意图
* 配置词槽
* 添加词槽的词典值
* 添加特征词列表
* 导入对话模板

#### 创建BOT

1. 进入[百度理解与交互(unit)平台](http://ai.baidu.com/unit/v2#/sceneliblist)
2. 新建一个技能给查询流量及续订的demo使用。

#### 配置意图

1. 点击进入技能,新建对话意图,以INTENT_CHECK_DATA_USAGE为例。
2. 意图名称填写INTENT_CHECK_DATA_USAGE。
3. 意图别名可以根据自己偏好填写,比如填写查询手机流量。
4. 添加词槽。INTENT_CHECK_DATA_USAGE 所需的词槽为 user_time;
5. 词槽别名可以根据自己偏好填写,比如填写查询时间,查询月份等。
6. 添加完词槽名和词槽别名以后,点击下一步,进入到选择词典环节,user_time选择系统词槽词典中的 **sys_time(时间)** 词槽。
7. 点击下一步,词槽必填选项选择非必填,点击确认。

** 其他两个user_package_type和user_package_name的词槽使用自定义词典,将下方词槽对应的词典内容自行粘贴到txt中上传即可**

全部意图包括列表如下:

* INTENT_CHECK_DATA_USAGE,所需词槽为 user_time
* INTENT_BOOK_DATA_PACKAGE,所需词槽为 user_package_type,user_package_name
* INTENT_YES
* INTENT_NO
* INTENT_REPEAT
* INTENT_WAIT
* INTENT_CONTINUE

词槽列表:

* user_time,使用系统时间词槽

* user_package_type
  
    ```text
    省内
    #省内流量
    全国
    #全国流量
    夜间
    #夜间流量
    ```
* user_package_name
    ```text
    10元100M
    #10元100兆
    #十元一百M
    #十元一百兆
    20元300M
    #20元300兆
    #二十元三百M
    #二十元三百兆
    50元1G
    #五十元1G
    ```

#### 新增特征词

点击**效果优化**中的**对话模板**,选择下方的新建特征词,将名称和词典值依次填入。eg:kw_check即为名称,名称下方为词典值,直接复制粘贴即可。

特征词列表如下:

* kw_check
    ```text
    查一查
    查询
    ```
* kw_data_usage
    ```text
    流量
    流量情况
    流量使用情况
    流量使用
    ```
* kw_book
    ```text
    续定
    续订
    预定
    预订
    ```
* kw_package
    ```text
    流量包
    流量套餐
    ```
* kw_yes
    ```text
    是
    好
    对
    想
    要
    是的
    好的
    对的
    我想
    我要
    可以
    行的
    需要
    没问题
    ```
* kw_no
    ```text
    不
    不想
    不要
    不行
    别
    没有
    没了
    没
    不用
    不需要
    没有了
    ```
* kw_repeat
    ```text
    没听清楚
    再说一次
    ```
* kw_wait
    ```text
    稍等
    等等
    稍等一下
    等一下
    等一等
    ```
* kw_continue
    ```text
    你继续
    您继续
    继续
    ```

#### 导入对话模板

* 完成以上步骤后,再进行该步骤,不然系统会报错
* 将文件[demo_cellular_data_pattern.txt](demo_cellular_data_pattern.txt)下载导入即可

### 查询流量及续订 - DMKit配置

该场景DMKit配置为conf/app/demo/cellular_data.json文件,该文件由同目录下对应的.xml文件生成,可以将.xml文件在 <https://www.draw.io> 中导入查看。

## 调整银行卡额度

该场景实现一个简单的银行卡固定额度及临时额度调整的技能。

### 调整银行卡额度 - UNIT平台配置

平台配置参考查询流量的配置,所需的配置内容见下图。

所需意图包括列表:

* INTENT_ADJUST_QUOTA, 所需词槽为user_type, user_method, user_amount
* INTENT_YES
* INTENT_NO

词槽列表:

* user_amount, 复用系统sys_num词槽

* user_type
    ```text
    固定
    #固定额度
    临时
    #临时额度

    ```

* user_method
    ```text
    提升
    #提高
    #增加
    降低
    #减少
    #下调
    ```

特征词列表:

* kw_adjust
    ```text
    调整
    调整一下
    改变
    ```
* kw_quota
    ```text
    额度
    银行卡额度
    信用卡额度
    ```

* kw_yes
    ```text
    是
    好
    对
    想
    要
    是的
    好的
    对的
    我想
    我要
    可以
    行的
    没问题
    ```
* kw_no
    ```text
    不
    不想
    不要
    不对
    错了
    不是
    别
    没有
    没了
    没
    不用
    没有了
    ```

对话模板:

* 将文件[demo_quota_adjust_pattern.txt](demo_quota_adjust_pattern.txt)下载导入即可

### 调整银行卡额度 - DMKit配置

该场景DMKit配置为conf/app/demo/quota_adjust.json文件,该文件由同目录下对应的.xml文件生成,可以将.xml文件在 <https://www.draw.io> 中导入查看。

## 预订酒店

该场景实现一个简单的预订酒店的技能。

### 预订酒店 - UNIT平台配置

平台配置参考查询流量的配置,所需的配置内容见下图。

所需意图包括列表:

* INTENT_BOOK_HOTEL, 所需词槽为user_time, user_room_type, user_location, user_hotel
* INTENT_YES
* INTENT_NO

词槽列表:

* user_time, 复用系统sys_time词槽。需要设置为必填词槽,澄清话术配置为『请问您要预订哪一天的酒店?』

* user_room_type。需要设置为必填词槽,澄清话术配置为『请问您要预订哪个房型?』
    ```text
    标间
    大床房
    单人间
    双人间
    双床房
    标准房
    标准间
    ```

* user_hotel, 复用系统sys_loc_hotel词槽

* user_location, 复用系统sys_loc词槽

特征词列表:

* kw_book
    ```text
    定
    预定
    订
    预订
    ```
* kw_hotel
    ```text
    旅馆
    酒店
    ```

* kw_yes
    ```text
    是
    好
    对
    想
    要
    是的
    好的
    对的
    我想
    我要
    可以
    行的
    没问题
    确认
    ```

* kw_no
    ```text
    不
    不想
    不要
    不对
    不是
    不用
    不行
    不可以
    别
    没
    没有
    没了
    没有了
    错了
    ```

对话模板:

* 将文件[demo_book_hotel_pattern.txt](demo_book_hotel_pattern.txt)下载导入即可

### 预订酒店 - DMKit配置

该场景DMKit配置为conf/app/demo/book_hotel.json文件,该文件由同目录下对应的.xml文件生成,可以将.xml文件在 <https://www.draw.io> 中导入查看。


================================================
FILE: docs/faq.md
================================================
# DMKit 常见问题

## 编译brpc失败

参考BRPC官方文档 <https://github.com/brpc/brpc/>,检查是否已安装所需依赖库。建议使用系统Ubuntu 16.04或者CentOS 7。更多BRPC相关问题请在BRPC github库提起issue。

## 返回错误信息 DM policy resolve failed

DMKit通过UNIT云端技能解析出的用户query的意图和词槽之后,需要根据对话意图结合当前对话状态在DMKit配置中对应的policy处理对话流程。当用户query解析出的意图结合当前对话状态未能找到可选的policy或者选中policy执行流程出错时,DMKit返回错误信息DM policy resolve failed。开发者需要检查:1)当前技能配置是否在products.json文件中进行注册;2)当前query解析结果意图在技能配置中是否配置了policy,详细配置说明参考[DMKit快速上手](tutorial.md);3)检查DMKit日志查看policy执行流程是否出错,。

## 返回错误信息 Failed to call unit bot api

DMKit访问UNIT云端失败。具体原因需要查看DMKit服务日志,常见原因是请求超时。
对于请求超时的情况,先检查DMKit所在服务器网络连接云端(默认地址为 aip.baidubce.com)是否畅通,尝试修改conf/app/remote_services.json文件中unit_bot服务对应超时时间。如果连接没有问题且增大超时时间无效,则尝试切换请求client:DMKit默认使用BRPC client请求UNIT云端,目前发现偶然情况下HTTPS访问云端出现卡死而返回超时错误。DMKit支持切换为curl方式访问云端,将conf/app/remote_services.json配置中client值由brpc修改为curl即可。需要注意使用curl方式时,建议升级openssl版本不低于1.1.0,libcurl版本不低于7.32。

## 返回错误信息 Unsupported action type satisfy

使用DMKit需要将UNIT平台中【技能设置->高级设置】中【对话回应设置】一项设置为『使用DMKit配置』。设置该选项之后,UNIT云端使用DMKit支持的数据协议。如设置为『在UNIT平台上配置』, DMKit无法识别UNIT云端数据协议,将返回错误Unsupported action type satisfy。

## DMKit如何支持FAQ问答对

目前UNIT平台中将【对话回应】设置为【使用DMKit配置】之后,如果对话触发了平台配置的FAQ问答集,平台返回结果不会将答案赋值给response中的say字段,但是会将答案赋值给名为qid的词槽值。因此,结合DMKit配置可以从词槽qid解析出问答回复后进行返回。例如,平台创建问题意图FAQ_HELLO之后,可以在DMKit对应技能的policy配置中添加一下policy支持FAQ_HELLO问答意图下的所有问答集:

```json
{
  "trigger": {
    "intent": "FAQ_HELLO",
    "slots": [],
    "state": ""
  },
  "params": [
    {
      "name": "answer_list",
      "type": "slot_val",
      "value": "qid"
    },
    {
      "name": "faq_answer",
      "type": "func_val",
      "value": "split_and_choose:{%answer_list%},|,random"
    }
  ],
  "output": [
    {
      "assertion": [],
      "session": {
        "context": {},
        "state": "001"
      },
      "result": [
        {
          "type": "tts",
          "value": "{%faq_answer%}"
        }
      ]
    }
  ]
}
```


================================================
FILE: docs/tutorial.md
================================================
# DMKit 快速上手

## 简介

在任务型对话系统(Task-Oriented Dialogue System)中,一般包括了以下几个模块:

* Automatic Speech Recognition(ASR),即语音识别模块,将音频转化为文本输入。
* Natural Language Understanding(NLU),即自然语言理解模块,通过分析文本输入,解析得到对话意图与槽位(Intent + Slots)。
* Dialog Manager(DM),即对话管理模块,根据NLU模块分析得到的意图+槽位值,结合当前对话状态,执行对应的动作并返回结果。其中执行的动作可能涉及到对内部或外部知识库的查询。
* Natural Language Generation(NLG),即自然语言生成。目前一般采用模板的形式。
* Text To Speech(TTS),即文字转语音模块,将对话系统的文本输出转化为音频。

DMKit关注其中的对话管理模块(Dialog Manager),解决对话系统中状态管理、对话逻辑处理等问题。在实际应用中,单个技能下对话逻辑一般都是根据NLU结果中意图与槽位值,结合当前对话状态,确定需要进行处理的子流程。子流程或者返回固定话术结果,或者根据NLU中槽位值与对话状态访问内部或外部知识库获取资源数据并生成话术结果返回,在返回结果的同时也对对话状态进行更新。我们将这部分对话处理逻辑进行抽象,提供一个通过配置快速构建对话流程,可复用的对话管理模块,即Reusable Dialog Manager。

## 架构

![DMKit架构](dmkit.png)

如上图所示,系统核心是一个对话管理引擎,在对话管理引擎的基础上,每个技能的实现都是通过一个配置文件来对对话逻辑和流程进行描述,这样每个技能仅需要关注自身对话逻辑,不需要重复开发框架代码。一个技能的配置包括了一系列的policy,每个policy包括三部分:触发条件(trigger),参数变量(params),以及输出(output)。

* 触发条件(trigger)包括了NLU解析得到的意图+槽位值,以及当前的对话状态,限定了该policy被触发的条件;

* 参数变量(params)是该policy运行需要的一些数据变量的定义,可以包括NLU解析结果中的槽位值、session中的变量值以及函数运行结果等。这里的函数需要在系统中进行注册,例如发送网络请求获取数据这样的函数,这些通用的函数在各个技能间都能共享,特殊情况下个别技能会需要定制化注册自己的函数;

* 输出结果(output)即为该policy运行返回作为对话系统的结果,可以包括话术tts及指令,同时还可对对话状态进行更新以及写入session变量。这里的结果可以使用已定义的参数变量进行模板填充。

在技能基础配置之上,还衍生出了一系列扩展功能。例如我们对一些仅需要触发条件及输出的技能,我们可以设计更精简的语法,使用更简洁的配置描述对话policy;对于多状态跳转的场景,我们引入了可视化的编辑工具,来描述对话跳转逻辑。精简语法表示及可视化编辑都可以自动转化为对话管理引擎可执行的配置,在系统中运行。

## 使用DMKit搭建对话技能的一般步骤

DMKit依托UNIT提供的自然语言理解能力,在此基础上搭建对话技能的一般步骤为:

* 通过UNIT平台创建技能,配置技能对话流程所需的意图解析能力;

* 编写技能的policy配置,policy配置文件语法见[技能配置](#技能配置)。对于对话状态状态繁多,跳转复杂的技能,可以借助[可视化配置工具](visual_tool.md)进行可视化编辑并导出技能配置。

* 将UNIT平台所创建技能的id与其对应policy配置文件注册于DMKit全局注册文件,注册文件配置项见[技能注册](#技能注册)。完成之后编译运行DMKit主程序,访问DMKit[服务接口](#服务接口)即可测试对话效果。

## 详细配置说明

本节详细介绍实现技能功能的配置语法。所有技能的配置均位于模块源码conf/app/目录下。

### 技能注册

products.json为全局注册配置文件,默认采用以"default"为key的配置项,该配置项中每个技能以skill id为key,注册添加技能详细配置,配置字段解释如下:

| 字段             |含义                         |
|-----------------|-----------------------------|
|conf_path        |技能policy配置文件地址          |
|score            |技能排序静态分数,可设置为固定值1  |

### 技能配置

单个技能配置文件包括了一系列policy,每个policy字段说明如下:

| 字段                             | 类型         |说明                            |
|---------------------------------|--------------|-------------------------------|
|trigger                          |object        | 触发节点,如果一个query满足多个policy的触发条件,则优先取state匹配的policy,再根据slot取覆盖个数最多的 |
|+intent                          |string        | 触发所需NLU意图,意图由UNIT云端对话理解获得。此外,DMKit定义了以下预留意图:<br>dmkit_intent_fallback: 当云端返回意图在DMKit配置中未找到匹配policy时,DMKit将尝试使用该意图触发policy |
|+slot                            |list          | 触发所需槽位值列表|
|+state                           |string        | 触发所需状态值,即上一轮对话session中保存的state字段值 |
|params                           |list          | 变量列表 |
|+params[].name                   |string        | 变量名,后定义的变量可以使用已定义的变量进行模板填充,result节点中的值也可以使用变量进行模板填充。变量的使用格式为{%name%}。当name=dmkit_param_context_xxx时可以直接将该变量以xxx为key存入本轮session结果context中 |
|+params[].type                   |string        | 变量类型,可能的类型为slot_val,request_param,session_context,func_val等,详细类型列表及说明可参照[params类型及说明](#params中变量类型列表及其说明) |
|+params[].value                  |string        | 变量定义值 |
|+params[].required               |bool          | 是否必须,如果必须的变量为空值时,该policy将不会返回结果 |
|output                           |list          | 返回结果节点,可定义多个output,最终输出会按顺序选择第一个满足assertion条件的output |
|+output[].assertion              |list          | 使用该output的前提条件列表 |
|+output[].assertion[].type       |string        | 条件类型,详细列表及说明可参照[assertion类型及说明](#result中assertion类型说明) |
|+output[].assertion[].value      |string        | 条件值 |
|+output[].session                |object        | 需要保存的session数据,用于更新对话状态及记录上下文 |
|+output[].session.state          |string        | 更新的对话状态值 |
|+output[].session.context        |kvdict        | 写入session的变量节点,该节点下的key+value数据会存入session,再下一轮中可以在变量定义中使用 |
|+output[].result                 |list          | 返回结果中result节点,多个result作为数组元素一起返回 |
|+output[].result[].type          |string        | result类型 |
|+output[].result[].value         |string        | result值            |

#### params中变量类型列表及其说明:

| type     |说明           |
|----------|--------------|
| slot_val | 从qu结果中取对应的slot值,有归一化值优先取归一化值。当对应tag值存在多个slot时,value值支持tag后按分隔符","添加下标i取对应tag的第i个值(索引从0开始) |
| request_param | 取请求参数对应的字段。这里的请求参数对应请求数据request.client_session字段中包含的K-V对,仅支持V类型为string的参数。例如request.client_session字段值为{"param_name1": "param_value1", "param_name2": "param_value2"},定义type为request_param,value为"param_name1"的变量,该变量将赋值为"param_value1" |
| session_context | 上一轮对话session结果中context结构体中对应的字段,例如上一轮output中session结构体保存了变量: {"context": {"param_name": "{%param_name%}", "state": ""}}, 本轮可定义变量{"name": "param_name", "type": "session_context", "value": "param_name"} |
| func_val | 调用开发者定义的函数。用户定义函数位于src/user_function目录下,并需要在user_function_manager.cpp文件中进行注册。value值为","连接的参数,其中第一个元素为函数名,第二个元素开始为函数参数 |
| qu_intent | NLU结果中的intent值 |
| session_state | 当前对话session中的state值 |
| string | 字符串值,可以使用已定义变量进行模板填充 |

特别的,开发者可以添加注册自定义函数,定义func_val类型的变量调用自定义函数实现功能扩展、定制化对话逻辑。DMKit默认内置提供了包括以下函数:

| 函数名          |函数说明              | 参数  |
|----------------|--------------|----------------------|
| service_http_get                   | 通过HTTP GET的方式请求知识库、第三方API等服务,服务地址需配置于conf/app/remote_services.json中     |参数1:remote_services.json中配置的服务名 <br>参数2:服务请求的路径,例如"/baidu/unit-dmkit"    |
| service_http_post                  | 通过HTTP POST的方式请求知识库、第三方API等服务,服务地址需配置于conf/app/remote_services.json中。注意:如果请求路径包含中文,需要先对中文进行URL编码后再拼接URL     |参数1:remote_services.json中配置的服务名 <br>参数2:服务请求的路径,例如"/baidu/unit-dmkit" <br>参数3:POST数据内容   |
| json_get_value                     | 根据提供的路径从json字符串中获取对应的字段值       |参数1:json字符串 <br>参数2:所需获取的字段在json字符串中的路径。例如{"data":{"str":"hello", "arr":[{"str": "world"}]}}中路径data.str对应字段值为"hello", 路径data.arr.0.str对应字段值"world"。|
| url_encode                         | 对输入字符串进行url编码操作       |参数1:进行编码的字符串|

另外,DMKit默认定义提供以下变量:

| 变量名                           | 说明                                                                      |
|---------------------------------|--------------------------------------------------------------------------|
| dmkit_param_last_tts            | 上一轮返回结果result中第一个type为tts的元素value值,如果不存在则为空字符串         |
| dmkit_param_context_xxxxx       | 上一轮session结果context中key为xxxx的值,同时如果用户定义了名为dmkit_param_context_xxxxx的变量,dmkit自动将该变量以xxxxx为key存入本轮session结果context|
| dmkit_param_slot_xxxxx          | qu结果中tag为xxxxx的slot值, 如果存在多个相同tag的slot,则取第一个|

#### result中assertion类型说明:

| type     |说明                                               |
|----------|--------------------------------------------------|
| empty    | value值为空                                       |
| not_empty| value值非空                                       |
| in       | value值以","切分,第一个元素在从第二个元素开始的列表中   |
| not_in   | value值以","切分,第一个元素不在从第二个元素开始的列表中 |
| eq       |  value值以","切分,第一个元素等于第二个元素            |
| gt       | value值以","切分,第一个数字大于第二个数字             |
| ge       | value值以","切分,第一个数字大于等于第二个数字         |

### 精简语法及可视化配置

* 在默认基础配置之上,有能力的开发者可以自行设计使用更简洁的配置描述对话policy并转化为基础配置进行加载。
* 对于多状态跳转的场景,可以引入了可视化的编辑工具,来描述对话跳转逻辑。这里我们提供了一个使用[mxgraph](https://github.com/jgraph/mxgraph)进行可视化配置的样例,文档参考:[可视化配置工具](visual_tool.md)

## DMKit服务接口

* DMKit服务监听端口及访问路径等参数可通过conf/gflags.conf文件进行配置,默认请求链接为http://<HOST>:8010/search, 其中<HOST>为DMKit服务所在机器IP,请求方式为POST
* 服务接收POST数据协议与[UNIT2.0接口协议](http://ai.baidu.com/docs#/UNIT-v2-API/top)兼容。开发者按照协议组装JSON数据请求DMKit,DMKit按照该协议返回JSON数据,同时DMKit定义返回结果action_list中custom_reply类型为DM_RESULT时,返回内容为DMKit输出的output结果。


================================================
FILE: docs/visual_tool.md
================================================
# 对话流可视化配置

针对状态繁多、跳转复杂的垂类,DMKit支持通过可视化编辑工具进行状态跳转流程的编辑设计,并同步转化为对话基础配置供对话管理引擎加载执行。

## 基于开源可视化工具的配置样例

这里的可视化编辑工具使用开源的[mxgraph](https://github.com/jgraph/mxgraph)可视化库,对话开发者可在可视化工具上进行图编辑,而该可视化库支持从图转化为xml文件,我们再利用转换框架实现对应的编译器将xml文件转化为对话基础配置加载执行。以demo场景【查询流量及续订】为例,步骤为:

* 在[draw.io](https://www.draw.io/)中按照[编辑规则](#编辑规则)进行图编辑
* 在编辑好的图导出为xml文件,放置于conf/app/demo目录下
* 运行language_compiler/run.py程序,该程序调用对应的转换器将conf/app/demo目录下的xml文件转化为json文件
* 将json文件注册于conf/app/products.json文件后,运行DMKit加载执行

### 编辑规则

规定使用以下构图元素进行编辑:

* 单箭头连线,单箭头连线是路程图中最基本的元素之一,用来表示状态的跳转。注意在使用连线的时候,连线的两端需要出现蓝色的 x 标识,以确保这个连线成功连接了两个框。
* 椭圆,用户节点,椭圆中存放的是用户的意图,以及槽位值(可选),内部语言格式为:
    ```text
    INTENT: intent_xxx
    SLOT:user_a,user_b
    ```
该节点表示用户输入query的NLU解析结果,结合指向该节点的BOT节点,构成了DMKit基础配置中一个完成trigger条件

* 圆角矩形,BOT节点,圆角矩形中存放的是BOT的回复,内部格式为:
    ```text
    PARAM:param_type:param_name1=param_value2
    PARAM:param_type:param_name1=param_value2
    BOT:XXXXXXX{%param_name1%}XXX{%param_name2%}
    ```
该节点表示BOT应该执行的回复,同时节点中可以定义参数并对回复进行模板填充。

在这里定义`dmkit_param_context_xxxx`变量时,dmkit自动将该变量以`xxxx`为key存入本轮session结果context。下一轮可以定义type=session_context,value=xxxx的变量来读取,也可以直接使用value={%dmkit_param_context_xxxx%}来获取,具体可参考[params类型及说明](tutorial.md#params中变量类型列表及其说明) 

* 菱形,条件节点,在节点中可定义需要进行判断的变量:
    ```text
    PARAM:param_type:param_name1=param_value2
    PARAM:param_type:param_name1=param_value2
    ```
    同时对该节点连出的单箭头连线可以添加描述跳转条件,条件可使用在菱形中定义的变量,例如:
    ```text
    ge:{%param1%},1
    ```
    跳转条件描述中,可用&&或||来连接多个条件以表达“和”或“或”,例如:
    ```text
    ge:{%param1%},1 || eq: {%param1%}, 10
    ```
    ||和&&不可同时出现在一个条件描述中。

另外规定:

* 一个椭圆仅可以连向一个圆角矩形或者一个菱形
* 一个圆角矩形可以连向多个椭圆
* 一个菱形可以连向多个圆角矩形

详细使用示例参考conf/app/demo目录下demo场景的xml文件,该xml文件可在[draw.io](https://www.draw.io/)中导入查看


================================================
FILE: language_compiler/compiler_xml.py
================================================
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""
Parser for .xml file 

"""
import xml.etree.ElementTree as ET
import re 
import codecs
import json
import sys


class Node(object):
    """
    this class is used to denote the node in .xml file, which will be used in XmlParser
    """
    def __init__(self, node_id, node_type, node_status, node_nlg, state, params):
        self.node_id = node_id
        self.node_type = node_type
        self.node_status = node_status # intent for user node, empty for server node 
        self.node_nlg = node_nlg # slots for customer node, nlg for server node
        self.state = state
        self.from_nodes = [] # (node_type, node_id, arrow_value)
        self.to_nodes = [] # (node_type, node_id, arrow_value)
        self.params = params  # parameters for server node


class Arrow(object):
    """
    this class is used to denote the arrow in .xml file, which will be used in XmlParser
    """
    def __init__(self, arrow_source, arrow_target):
        self.arrow_source = arrow_source # cell id 
        self.arrow_target = arrow_target # cell id 
        self.arrow_text = ""


class XmlParser(object):
    """
    this class is used to represent the graph in .xml file as a class object, 
    all necessary information about the graph will be extracted from the .xml file.
    """
    def __init__(self, xml_data):
        # initialize the elementtree object
        self.root = ET.fromstring("".join(xml_data))

        # collections of nodes, arrows, policies and nlg responses. 
        self.__nodes = {}
        self.__arrows = {}
        self.__nlgs = []
        self.__user_nodes = []
        self.__policies = []  # the output json

        # methods to parse the element tree
        self.__parse_cells() # parse every cell to extract info of node and arrow 
        self.__connect_nodes() # connect nodes with the info from arrow 
        self.__extract_policy()

    def __clean_noise(self, value, stri='', noise=None):
        if not noise:
            noise = ['&nbsp;', '&nbsp', 'nbsp', '<br>', '<div>', '</div>', '<span>', '</span>', '<span lang="ZH-CN">']
        for n in noise:
            value = value.replace(n, stri)
        if not stri:
            value = value.replace('&amp;', '&')
        return value.strip()

    def __clean_span(self, value, stri=''):
        return self.__clean_noise(value, '', ['<span>', '</span>', '<span lang="ZH-CN">'])

    def __parse_cells(self):
        todo_cells = []
        state = 0
        for cell in self.root[0]:
            style = cell.get('style')
            cell_id = cell.get('id')
            # bot node 
            if style and style.startswith('rounded=1'):
                value = cell.get('value') + '<'
                value = self.__clean_span(value)
                re_nlg = re.compile(r'BOT.*?<')
                re_params = re.compile(r'PARAM.*?<')
                re_state = re.compile(r'STATE.*?<')
                nlgs = []
                if re_nlg.findall(value): 
                    for n in re_nlg.findall(value):
                        n = self.__clean_noise(n[4:-1])
                        nlgs.append(n)
                params = []
                if re_params.findall(value):
                    for p in re_params.findall(value):
                        p = self.__clean_noise(p[6:-1])
                        params.append(p)
                node_state = None
                if re_state.findall(value):
                    for s in re_state.findall(value):
                        node_state = self.__clean_noise(s[6:-1])
                        break
                if 'BOT' not in value:
                    raise Exception('wrong shape is used in cell with text: ', nlg)                    
                if value:
                    if not node_state:
                        state += 1
                        node_state = (3 - len(str(state))) * '0' + str(state)
                    node = Node(cell_id, 'server', '', nlgs, node_state, params)
                    self.__nodes[cell_id] = node
                continue
            # customer node
            if style and style.startswith('ellipse'):
                value = cell.get('value') + '<'
                value = self.__clean_span(value)
                re_status = re.compile(r'INTENT.*?<')
                status = re_status.findall(value)[0] 
                status = self.__clean_noise(status[status.find(':') + 1:-1])
                re_nlg = re.compile(r'SLOT.*?<')
                if re_nlg.findall(value):
                    nlg = self.__clean_noise(re_nlg.findall(value)[0][5:-1])
                else:
                    nlg = ""
                if 'INTENT' not in value:
                    raise Exception('wrong shape is used in cell with text: ', nlg)  
                if value:
                    node = Node(cell_id, 'customer', status, nlg, '', [])
                    self.__nodes[cell_id] = node
                    self.__user_nodes.append(node)
                continue 
            # arrow node
            if cell.get('edge'):
                source = cell.get('source')
                target = cell.get('target')
                text = cell.get('value')
                if not target:
                    raise Exception('some arrow is not pointing to anything')  
                else:
                    arrow = Arrow(source, target)
                    if text:
                        arrow.arrow_text = self.__clean_noise(text)
                    self.__arrows[cell_id] = arrow
                continue
            # judge node
            if style and style.startswith('rhombus'):
                value = cell.get('value') + '<'
                value = self.__clean_span(value)
                re_params = re.compile(r'PARAM.*?<')
                params = []
                if re_params.findall(value):
                    for p in re_params.findall(value):
                        p = self.__clean_noise(p[6:-1])
                        params.append(p)
                if 'PARAM' not in value:
                    raise Exception('wrong shape is used in cell with text: ', value)                    
                if value:
                    node = Node(cell_id, 'judge', '', '', '', params)
                    self.__nodes[cell_id] = node 
                continue
            todo_cells.append(cell)
        # false initial
        self.__false_initial = Node(-1, "", "", "", "", "")
        self.__nodes[-1] = self.__false_initial 

        for cell in todo_cells:
            style = cell.get('style')
            parent_id = cell.get('parent')
            value = cell.get('value')
            if style and style.startswith('text'):
                if parent_id in self.__arrows:
                    self.__arrows[parent_id].arrow_text = self.__clean_noise(value)
                else:
                    raise Exception("there is a bad cell with text", value)

    def __connect_nodes(self):
        for (arrow_id, arrow) in self.__arrows.items():
            if not arrow.arrow_source:
                source_node = self.__false_initial
            else:
                source_node = self.__nodes[arrow.arrow_source]
            target_node = self.__nodes[arrow.arrow_target]
            # update source node and target node 
            source_node.to_nodes.append((target_node.node_type, target_node.node_id, 
                        arrow.arrow_text))
            self.__nodes[arrow.arrow_source] = source_node 
            target_node.from_nodes.append((source_node.node_type, 
                                           source_node.node_id, arrow.arrow_text))
            self.__nodes[arrow.arrow_target] = target_node

    def __extract_policy(self):
        for node in self.__user_nodes:
            intent = node.node_status
            slots = node.node_nlg.replace(' ', '').split(',')
            if slots[0] == "":
                slots = []
            params = []
            output = []
            if len(node.to_nodes) > 0:
                dir_to_node_info = node.to_nodes[0]
                dir_to_node = self.__nodes[dir_to_node_info[1]]
                # points to one branch
                if dir_to_node.node_type == "server":
                    for p in dir_to_node.params:                        
                        p = p.replace(" ", "")
                        c1 = p.find(":")
                        c2 = p.find("=")
                        pname = p[c1 + 1:c2]                      
                        ptype = p[0:c1]                        
                        pvalue = p[c2 + 1:]                        
                        param = {"name":pname, "type":ptype, "value":pvalue}
                        params.append(param)
                    next = {}
                    next["assertion"] = []
                    next["session"] = {"state": dir_to_node.state, "context": {}}
                    results = []
                    for n in dir_to_node.node_nlg:
                        result = {}
                        result["type"] = "tts"
                        alt = n.replace(" ", "").split('|')
                        if len(alt) == 1:
                            result["value"] = alt[0]
                        else:
                            result["value"] = alt
                        results.append(result)
                    next["result"] = results
                    output.append(next)
                # points to multiple branch
                elif dir_to_node.node_type == "judge":
                    # add params in judge nodes
                    for p in dir_to_node.params:                        
                        p = p.replace(" ", "")
                        c1 = p.find(":")
                        c2 = p.find("=")
                        pname = p[c1 + 1:c2]                      
                        ptype = p[0:c1]                        
                        pvalue = p[c2 + 1:]
                        param = {"name":pname, "type":ptype, "value":pvalue}
                        params.append(param)
                    # extract policies from bot nodes pointed to by the judge node
                    for to_node_info in dir_to_node.to_nodes:
                        to_node = self.__nodes[to_node_info[1]]
                        nexts = []
                        condition = to_node_info[2].replace(" ", "")
                        # A condition can contain either '&&'s or '||'s, but not both
                        if '&&' in condition:
                            conditions = condition.split('&&')
                            assertions = []
                            for cond in conditions:
                                cut = cond.find(":")
                                assertion = {}
                                assertion["type"] = cond[0:cut]
                                assertion["value"] = cond[cut + 1:]
                                assertions.append(assertion)
                            next = {}
                            next["assertion"] = assertions
                            next["session"] = {"state": to_node.state, "context": {}}
                            nexts.append(next)
                        elif '||' in condition:
                            conditions = condition.split('||')
                            for cond in conditions:
                                next = {}
                                assertion = {}
                                cut = cond.find(":")
                                assertion["type"] = cond[0:cut]
                                assertion["value"] = cond[cut + 1:]
                                next["assertion"] = [assertion]
                                next["session"] = {"state": to_node.state, "context": {}}
                                nexts.append(next)
                        else:
                            assertion = {}
                            cut = condition.find(":")
                            assertion["type"] = condition[0:cut]
                            assertion["value"] = condition[cut + 1:]
                            next = {}
                            next["assertion"] = [assertion]
                            next["session"] = {"state": to_node.state, "context": {}}
                            nexts.append(next)

                        results = []
                        for n in to_node.node_nlg:
                            result = {}
                            result["type"] = "tts"
                            alt = n.replace(" ", "").split('|')
                            if len(alt) == 1:
                                result["value"] = alt[0]
                            else:
                                result["value"] = alt
                            results.append(result)
                        for nxt in nexts:
                            nxt["result"] = results
                            output.append(nxt)
                        for p in to_node.params:                        
                            p = p.replace(" ", "")
                            c1 = p.find(":")
                            c2 = p.find("=")
                            pname = p[c1 + 1:c2]                      
                            ptype = p[0:c1]                        
                            pvalue = p[c2 + 1:]                        
                            param = {"name":pname, "type":ptype, "value":pvalue}
                            # merge
                            if param not in params:
                                params.append(param)

            for from_node_info in node.from_nodes:
                if from_node_info:
                    policy = {}
                    from_node = self.__nodes[from_node_info[1]]
                    trigger = {}
                    trigger["intent"] = intent
                    trigger["slots"] = slots 
                    trigger["state"] = from_node.state
                    policy["trigger"] = trigger
                    policy["params"] = params
                    policy["output"] = output
                    self.__policies.append(policy)

    def write_json(self):
        """
        this method is used to return the parsed json for the .xml input
        """
        return [json.dumps(self.__policies, ensure_ascii=False,\
                indent=2, sort_keys=True).encode('utf8')]


def run(data):
    """
    runs the parser and returns the parsed json
    """
    ps = XmlParser(data)
    return ps.write_json()


================================================
FILE: language_compiler/run.py
================================================
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""
Compiler Executor.

"""

import codecs
import ConfigParser
import importlib
import os
import sys


def main():
    """
    Compiler Executor
    process files in data_path and generate system json configuration file

    """
    current_dir = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))

    config = ConfigParser.ConfigParser()
    config.read(current_dir + "/settings.cfg")
    
    compile_types = config.get('compiler', 'compile_types')
    compile_types = compile_types.replace(' ', '').split(',')

    data_path = current_dir + '/' + config.get('data', 'data_path')
    for dirpath, dirnames, filenames in os.walk(data_path):
        for filename in filenames:
            filepath = os.path.join(dirpath, filename)
            fname, extension = os.path.splitext(filename)
            extension = extension[1:]
            if extension not in compile_types:
                continue
            input_lines = []
            with open(filepath, 'r') as f:
                input_lines = f.readlines()
            compiler_module = importlib.import_module('compiler_' + extension)
            compiler_function = getattr(compiler_module, 'run')
            output_lines = compiler_function(input_lines)
            with open(os.path.join(dirpath, fname + ".json"), 'w') as f:
                for line in output_lines:
                    f.write("%s\n" % line)

if __name__ == "__main__":
    main()


================================================
FILE: language_compiler/settings.cfg
================================================
[data]
data_path=../conf/app

[compiler]
compile_types=xml
exclude_file=




================================================
FILE: proto/http.proto
================================================
syntax="proto2";
package dmkit;

option cc_generic_services = true;

message HttpRequest { };
message HttpResponse { };

service HttpService {
  rpc run(HttpRequest) returns (HttpResponse);
};


================================================
FILE: src/app_container.cpp
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "app_container.h"
#include <chrono>
#include "app_log.h"
#include "brpc.h"
#include "dialog_manager.h"

namespace dmkit {

ThreadLocalDataFactory::ThreadLocalDataFactory(ApplicationBase* application) {
    this->_application = application;
}

ThreadLocalDataFactory::~ThreadLocalDataFactory() {
    this->_application = nullptr;
}

void* ThreadLocalDataFactory::CreateData() const {
    return this->_application->create_thread_data();
}

void ThreadLocalDataFactory::DestroyData(void* d) const {
    this->_application->destroy_thread_data(d);
}

AppContainer::AppContainer() {
    this->_application = nullptr;
    this->_data_factory = nullptr;
}

AppContainer::~AppContainer() {
    delete this->_application;
    this->_application = nullptr;

    delete this->_data_factory;
    this->_data_factory = nullptr;
}

int AppContainer::load_application() {
    if (nullptr != this->_application) {
        APP_LOG(ERROR) << "an application has already be loaded";
        return -1;
    }

    // The real application is created here
    this->_application = new DialogManager();

    if (nullptr == this->_application || 0 != this->_application->init()) {
        APP_LOG(ERROR) << "failed to init application!!!";
        return -1;
    }

    this->_data_factory = new ThreadLocalDataFactory(this->_application);

    return 0;
}

ThreadLocalDataFactory* AppContainer::get_thread_local_data_factory() {
    if (nullptr == this->_data_factory) {
        APP_LOG(ERROR) << "Data factory has not been initialized!!!";
        return nullptr;
    }

    return this->_data_factory;
}

int AppContainer::run(BRPC_NAMESPACE::Controller* cntl) {
    if (nullptr == this->_application) {
        APP_LOG(ERROR) << "No application is not loaded for processing!!!";
        return -1;
    }

    auto time_start = std::chrono::steady_clock::now();

    // Need to reset thread data status before running the application
    ThreadDataBase* tls = static_cast<ThreadDataBase*>(BRPC_NAMESPACE::thread_local_data());
    tls->reset();
    APP_LOG(TRACE) << "Running application";
    int result = this->_application->run(cntl);

    auto time_end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = std::chrono::duration_cast<std::chrono::duration<double>>(time_end - time_start);
    double total_cost = diff.count() * 1000;
    APP_LOG(TRACE) << "Application run cost(ms): " << total_cost;
    tls->add_notice_log("tm", std::to_string(total_cost));
    APP_LOG(NOTICE) << tls->get_notice_log();

    return result;
}

} // namespace dmkit



================================================
FILE: src/app_container.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_APP_CONTAINER_H
#define DMKIT_APP_CONTAINER_H

#include "application_base.h"
#include "brpc.h"

namespace dmkit {

class ThreadLocalDataFactory : public BRPC_NAMESPACE::DataFactory {
public:
    ThreadLocalDataFactory(ApplicationBase* application);
    ~ThreadLocalDataFactory();
    void* CreateData() const;
    void DestroyData(void* d) const;

private:
    ApplicationBase* _application;
};

// Container class which manages the instances of application,
// as well as a thread data factory instance.
class AppContainer {
public:
    AppContainer();
    ~AppContainer();
    int load_application();
    ThreadLocalDataFactory* get_thread_local_data_factory();
    int run(BRPC_NAMESPACE::Controller* cntl);

private:
    // The application instance is shared for all rpc threads
    ApplicationBase* _application;

    ThreadLocalDataFactory* _data_factory;
};

} // namespace dmkit

#endif  //DMKIT_APP_CONTAINER_H


================================================
FILE: src/app_log.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_APP_LOG_H
#define DMKIT_APP_LOG_H

#include "brpc.h"
#include "butil.h"
#include "thread_data_base.h"

namespace dmkit {

// Wrapper for application logging to include trace id for each log during a request.
#define APP_LOG(severity)  \
    LOG(severity) << "logid=" << (BRPC_NAMESPACE::thread_local_data() == nullptr ? "" : \
      (static_cast<dmkit::ThreadDataBase*>(BRPC_NAMESPACE::thread_local_data()))->get_log_id()) \
      << " "

} // namespace dmkit

#endif  //DMKIT_APP_LOG_H


================================================
FILE: src/application_base.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_APPLICATION_BASE_H
#define DMKIT_APPLICATION_BASE_H

#include "brpc.h"
#include "thread_data_base.h"

namespace dmkit {

// Base class for applications
// Notice that the same application instance will be used for all request thread,
// This call should be thread safe.
class ApplicationBase {
public:
    ApplicationBase() {};
    virtual ~ApplicationBase() {};

    // Interface for application to do global initialization.
    // This method will be invoke only once when server starts.
    virtual int init() = 0;

    // Interface for application to handle requests, it should be thread safe.
    virtual int run(BRPC_NAMESPACE::Controller* cntl) = 0;

    // Interface for application to register customized thread data.
    virtual void* create_thread_data() const {
        return new ThreadDataBase();
    }

    // Interface to destroy thread data, the data instance was created by create_thread_data.
    virtual void destroy_thread_data(void* d) const {
        delete static_cast<ThreadDataBase*>(d);
    }

    // Set log id for current request,
    // application should set log id as early as possible when processing request
    virtual void set_log_id(const std::string& log_id) {
        ThreadDataBase* tls = static_cast<ThreadDataBase*>(BRPC_NAMESPACE::thread_local_data());
        tls->set_log_id(log_id);
    }

    // Add a key/value notice log which will be logged when finish processing request
    virtual void add_notice_log(const std::string& key, const std::string& value) {
        ThreadDataBase* tls = static_cast<ThreadDataBase*>(BRPC_NAMESPACE::thread_local_data());
        tls->add_notice_log(key, value);
    }
};

} // namespace dmkit

#endif  //DMKIT_APPLICATION_BASE_H



================================================
FILE: src/brpc.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_BRPC_H
#define DMKIT_BRPC_H

#ifndef BRPC_INCLUDE_PREFIX
#define BRPC_INCLUDE_PREFIX <brpc
#endif

#ifndef BRPC_NAMESPACE
#define BRPC_NAMESPACE brpc
#endif

#include BRPC_INCLUDE_PREFIX/data_factory.h>
#include BRPC_INCLUDE_PREFIX/channel.h>
#include BRPC_INCLUDE_PREFIX/controller.h>
#include BRPC_INCLUDE_PREFIX/restful.h>
#include BRPC_INCLUDE_PREFIX/server.h>

#endif  //DMKIT_BRPC_H


================================================
FILE: src/butil.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_BUTIL_H
#define DMKIT_BUTIL_H

#ifndef BUTIL_INCLUDE_PREFIX
#define BUTIL_INCLUDE_PREFIX <butil
#endif

#ifndef BUTIL_NAMESPACE
#define BUTIL_NAMESPACE butil
#endif

#include BUTIL_INCLUDE_PREFIX/iobuf.h>
#ifdef BUTIL_ENABLE_COMLOG_SINK
#include BUTIL_INCLUDE_PREFIX/comlog_sink.h>
#endif
#include BUTIL_INCLUDE_PREFIX/containers/flat_map.h>
#include BUTIL_INCLUDE_PREFIX/logging.h>

#endif  //DMKIT_BUTIL_H


================================================
FILE: src/dialog_manager.cpp
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "dialog_manager.h"
#include <memory>
#include <string>
#include "app_log.h"
#include "butil.h"
#include "rapidjson.h"
#include "request_context.h"
#include "utils.h"

namespace dmkit {

DialogManager::DialogManager() {
    this->_remote_service_manager = new RemoteServiceManager();
    this->_policy_manager = new PolicyManager();
    this->_token_manager = new TokenManager();
}

DialogManager::~DialogManager() {
    delete this->_policy_manager;
    this->_policy_manager = nullptr;
    delete this->_remote_service_manager;
    this->_remote_service_manager = nullptr;
    delete this->_token_manager;
    this->_token_manager = nullptr;
}

int DialogManager::init() {
    if (0 != this->_remote_service_manager->init("conf/app", "remote_services.json")) {
        APP_LOG(ERROR) << "Failed to init _remote_service_manager";
        return -1;
    }
    APP_LOG(TRACE) << "_remote_service_manager init done";

    if (0 != this->_policy_manager->init("conf/app", "products.json")) {
        APP_LOG(ERROR) << "Failed to init _policy_manager";
        return -1;
    }

    if (0 != this->_token_manager->init("conf/app", "bot_tokens.json")) {
        APP_LOG(ERROR) << "Failed to init _token_manager";
        return -1;
    }
    APP_LOG(TRACE) << "_policy_manager init done";

    return 0;
}

int DialogManager::run(BRPC_NAMESPACE::Controller* cntl) {
    std::string request_json = cntl->request_attachment().to_string();
    APP_LOG(TRACE) << "received request: " << request_json;
    this->add_notice_log("req", request_json);

    rapidjson::Document request_doc;
    // In the case we cannot parse the request json, it is not a valid request.
    if (request_doc.Parse(request_json.c_str()).HasParseError() || !request_doc.IsObject()) {
        APP_LOG(WARNING) << "Failed to parse request data to json";
        cntl->http_response().set_status_code(400);
        return 0;
    }

    // Need to set log_id as soon as we can get it to avoid missing log_id in logs.
    if (!request_doc.HasMember("log_id") || !request_doc["log_id"].IsString()) {
        APP_LOG(WARNING) << "Missing log_id";
        this->send_json_response(cntl, this->get_error_response(-1, "Missing log_id"));
        return 0;
    }
    std::string log_id = request_doc["log_id"].GetString();
    std::string dmkit_log_id_prefix = "dmkit_";
    log_id = dmkit_log_id_prefix + log_id;
    this->set_log_id(log_id);
    request_doc["log_id"].SetString(log_id.c_str(), log_id.length(), request_doc.GetAllocator());

    // Parsing bot_id from request json
    if (!request_doc.HasMember("bot_id") || !request_doc["bot_id"].IsString()) {
        APP_LOG(WARNING) << "Missing bot_id";
        this->send_json_response(cntl, this->get_error_response(-1, "Missing bot_id"));
        return 0;
    }
    std::string bot_id = request_doc["bot_id"].GetString();

    if (!request_doc.HasMember("request") || !request_doc["request"].HasMember("query")
            || !request_doc["request"]["query"].IsString()) {
        APP_LOG(WARNING) << "Missing query";
        this->send_json_response(cntl, this->get_error_response(-1, "Missing query"));
        return 0;
    }
    std::string query = request_doc["request"]["query"].GetString();
    std::string rewrite_query;
    if (request_doc["request"].HasMember("rewrite_query") 
            && request_doc["request"]["rewrite_query"].IsString()) {
        rewrite_query = request_doc["request"]["rewrite_query"].GetString();
        request_doc["request"].RemoveMember("rewrite_query");
    }

    // Get dmkit session from request. We saved it in the bot session in latest response.
    std::string dm_session;
    if (request_doc.HasMember("bot_session")) {
        std::string request_bot_session = request_doc["bot_session"].GetString();
        rapidjson::Document request_bot_session_doc;
        if (request_bot_session.empty()
                || request_bot_session_doc.Parse(request_bot_session.c_str()).HasParseError()
                || !request_bot_session_doc.IsObject()
                || !request_bot_session_doc.HasMember("bot_id")
                || !request_bot_session_doc["bot_id"].IsString()
                || request_bot_session_doc["bot_id"].GetString() != bot_id
                || !request_bot_session_doc.HasMember("session_id")
                || !request_bot_session_doc.HasMember("dialog_state")
                || !request_bot_session_doc["dialog_state"].HasMember("contexts")
                || !request_bot_session_doc["dialog_state"]["contexts"].HasMember("dmkit")
                || !request_bot_session_doc["dialog_state"]["contexts"]["dmkit"].HasMember("session")) {
            // Not a valid session from DMKit
            request_doc["bot_session"].SetString("", 0, request_doc.GetAllocator());
        } else {
            dm_session =
                request_bot_session_doc["dialog_state"]["contexts"]["dmkit"]["session"].GetString();
        }
    }
    APP_LOG(TRACE) << "dm session: " << dm_session;
    PolicyOutputSession session = PolicyOutputSession::from_json_str(dm_session);

    // Get access_token from request uri.
    std::string access_token;
    const std::string* access_token_ptr = cntl->http_request().uri().GetQuery("access_token");
    if (access_token_ptr != nullptr) {
        access_token = *access_token_ptr;
    }
    if (access_token.empty() && this->_token_manager->get_access_token(
                bot_id, this->_remote_service_manager, access_token) != 0) {
        APP_LOG(ERROR) << "Failed to get access token";
        this->send_json_response(cntl, this->get_error_response(-1, "Failed to get access token"));
        return 0;
    }

    std::string query_response;
    bool is_dmkit_response = false;
    this->process_request(request_doc, dm_session, access_token, query_response, is_dmkit_response);
    if (is_dmkit_response || rewrite_query.empty()) {
        this->send_json_response(cntl, query_response);
        return 0;
    }
    std::string rewrite_query_response;
    request_doc["request"]["query"].SetString(rewrite_query.c_str(), 
        rewrite_query.length(), request_doc.GetAllocator());
    this->process_request(request_doc, dm_session, access_token, 
        rewrite_query_response, is_dmkit_response);
    if (is_dmkit_response) {
        this->send_json_response(cntl, rewrite_query_response);
        return 0;
    }
    this->send_json_response(cntl, query_response);
    return 0;
}

int DialogManager::process_request(const rapidjson::Document& request_doc,
                                   const std::string& dm_session,
                                   const std::string& access_token,
                                   std::string& json_response,
                                   bool& is_dmkit_response) {
    std::string bot_id = request_doc["bot_id"].GetString();
    std::string log_id = request_doc["log_id"].GetString();
    std::string query = request_doc["request"]["query"].GetString();
    is_dmkit_response = false;
    std::string request_json = utils::json_to_string(request_doc);
    // Call unit bot api with the request json as dmkit use the same data contract.
    std::string unit_bot_result;
    if (this->call_unit_bot(access_token, request_json, unit_bot_result) != 0) {
        APP_LOG(ERROR) << "Failed to call unit bot api";
        json_response = get_error_response(-1, "Failed to call unit bot api");
        return 0;
    }
    APP_LOG(TRACE) << "unit bot result: " << unit_bot_result;

    // Parse unit bot response.
    // In the case something wrong with unit bot response, informs users.
    rapidjson::Document unit_response_doc;
    if (unit_response_doc.Parse(unit_bot_result.c_str()).HasParseError()
            || !unit_response_doc.IsObject()) {
        APP_LOG(ERROR) << "Failed to parse unit bot result: " << unit_bot_result;
        json_response = get_error_response(-1, "Failed to parse unit bot result");
        return -1;
    }
    if (!unit_response_doc.HasMember("error_code")
            || !unit_response_doc["error_code"].IsInt()
            || unit_response_doc["error_code"].GetInt() != 0) {
        json_response = unit_bot_result;
        return 0;
    }

    // The bot status is included in bot_session
    std::string bot_session = unit_response_doc["result"]["bot_session"].GetString();
    rapidjson::Document bot_session_doc;
    if (bot_session_doc.Parse(bot_session.c_str()).HasParseError()
            || !bot_session_doc.IsObject()) {
        APP_LOG(ERROR) << "Failed to parse bot session: " << bot_session;
        json_response = get_error_response(-1, "Failed to parse bot session");
        return 0;
    }
    if (this->handle_unsatisfied_intent(unit_response_doc, 
            bot_session_doc, dm_session, json_response) == 0) {
        return 0;
    }

    // Handle satify/understood intents
    QuResult* qu_result = QuResult::parse_from_dialog_state(
        bot_id, bot_session_doc["dialog_state"]);
    if (qu_result == nullptr) {
        json_response = this->get_error_response(-1, "Failed to parse qu_result");
        return 0;
    }
    auto qu_map = new BUTIL_NAMESPACE::FlatMap<std::string, QuResult*>();
    // 2: bucket_count, initial count of buckets, big enough to avoid resize.
    // 50: load_factor, element_count * 100 / bucket_count.
    qu_map->init(2, 50);
    qu_map->insert(bot_id, qu_result);
    // Parsing request params from client_session, only string value is accepted
    std::unordered_map<std::string, std::string> request_params;
    std::string product = "default";
    if (request_doc["request"].HasMember("client_session")) {
        std::string client_session = request_doc["request"]["client_session"].GetString();
        rapidjson::Document client_session_doc;
        if (!client_session_doc.Parse(client_session.c_str()).HasParseError() && client_session_doc.IsObject()) {
            for (auto& m_param: client_session_doc.GetObject()) {
                if (!m_param.value.IsString()) {
                    continue;
                }
                std::string param_name = m_param.name.GetString();
                std::string param_value = m_param.value.GetString();
                if (param_name == "product") {
                    product = param_value;
                }
                request_params[param_name] = param_value;
            }
        }
    }
    PolicyOutputSession session = PolicyOutputSession::from_json_str(dm_session);
    RequestContext context(this->_remote_service_manager, log_id, request_params);
    PolicyOutput* policy_output = this->_policy_manager->resolve(
        product, qu_map, session, context);
    for (auto iter = qu_map->begin(); iter != qu_map->end(); ++iter) {
        QuResult* qu_ptr = iter->second;
        if (qu_ptr != nullptr) {
            delete qu_ptr;
        }
    }
    delete qu_map;

    if (policy_output == nullptr) {
        json_response = this->get_error_response(-1, "DM policy resolve failed");
        return 0;
    }
    bool has_query = false;
    for (auto const& meta: policy_output->meta) {
        if (meta.key == "query") {
            has_query = true;
        }
    }
    if (!has_query) {
        KVPair meta_query;
        meta_query.key = "query";
        meta_query.value = query;
        policy_output->meta.push_back(meta_query);
    }
    this->set_dm_response(unit_response_doc, bot_session_doc, policy_output);
    delete policy_output;
    is_dmkit_response = true;
    json_response = utils::json_to_string(unit_response_doc);
    return 0;
}

int DialogManager::call_unit_bot(const std::string& access_token,
                                         const std::string& payload,
                                         std::string& result) {
    std::string url = "/rpc/2.0/unit/bot/chat?access_token=";
    url += access_token;
    RemoteServiceParam rsp = {
        url,
        HTTP_METHOD_POST,
        payload
    };
    RemoteServiceResult rsr;
    APP_LOG(TRACE) << "Calling unit bot service, url: "<< url;
    APP_LOG(TRACE) <<  payload;
    // unit_bot is a remote service configured in conf/app/remote_services.json
    if (this->_remote_service_manager->call("unit_bot", rsp, rsr) !=0) {
        APP_LOG(ERROR) << "Failed to get unit bot result" ;
        return -1;
    }
    APP_LOG(TRACE) << "Got unit bot result";

    result = rsr.result;

    return 0;
}

int DialogManager::handle_unsatisfied_intent(rapidjson::Document& unit_response_doc,
                                             rapidjson::Document& bot_session_doc,
                                             const std::string& dm_session,
                                             std::string& response) {
    std::string action_type;
    if (unit_response_doc.HasMember("result")
            && unit_response_doc["result"].HasMember("response")
            && unit_response_doc["result"]["response"].HasMember("action_list")
            && unit_response_doc["result"]["response"]["action_list"].Size() > 0
            && unit_response_doc["result"]["response"]["action_list"][0].HasMember("type")) {
        action_type = unit_response_doc["result"]["response"]["action_list"][0]["type"].GetString();
    } else {
        APP_LOG(WARNING) << "Failed to parse action type from unit bot response: "
            << utils::json_to_string(unit_response_doc);
    }

    if (action_type == "satisfy") {
        response = this->get_error_response(-1, "Unsupported action type satisfy");
        return 0;
    }
    if (action_type != "understood") {
        // DM session should be saved
        rapidjson::Value dm_session_json;
        dm_session_json.SetString(
            dm_session.c_str(), dm_session.length(), bot_session_doc.GetAllocator());
        if (!bot_session_doc.HasMember("dialog_state")) {
            rapidjson::Value dialog_state_json(rapidjson::kObjectType);
            bot_session_doc.AddMember("dialog_state", dialog_state_json, bot_session_doc.GetAllocator());
        }
        if (!bot_session_doc["dialog_state"].HasMember("contexts")) {
            rapidjson::Value contexts_json(rapidjson::kObjectType);
            bot_session_doc["dialog_state"].AddMember("contexts", contexts_json, bot_session_doc.GetAllocator());
        }
        if (!bot_session_doc["dialog_state"]["contexts"].HasMember("dmkit")) {
            rapidjson::Value contexts_json(rapidjson::kObjectType);
            bot_session_doc["dialog_state"]["contexts"].AddMember("dmkit", contexts_json, bot_session_doc.GetAllocator());
        }
        if (bot_session_doc["dialog_state"]["contexts"]["dmkit"].HasMember("session")) {
            bot_session_doc["dialog_state"]["contexts"]["dmkit"].RemoveMember("session");
        }

        bot_session_doc["dialog_state"]["contexts"]["dmkit"].AddMember(
            "session", dm_session_json, bot_session_doc.GetAllocator());

        std::string bot_session = utils::json_to_string(bot_session_doc);
        //unit_response_doc.AddMember("debug", bot_session_doc, unit_response_doc.GetAllocator());
        unit_response_doc["result"]["bot_session"].SetString(
            bot_session.c_str(), bot_session.length(), unit_response_doc.GetAllocator());
        response = utils::json_to_string(unit_response_doc);
        return 0;
    }

    return -1;
}

void DialogManager::set_dm_response(rapidjson::Document& unit_response_doc,
                                            rapidjson::Document& bot_session_doc,
                                            const PolicyOutput* policy_output) {
    std::string session_str = PolicyOutputSession::to_json_str(policy_output->session);
    rapidjson::Value dm_session;
    dm_session.SetString(session_str.c_str(), session_str.length(), bot_session_doc.GetAllocator());

    
    if (!bot_session_doc.HasMember("dialog_state")) {
        rapidjson::Value dialog_state_json(rapidjson::kObjectType);
        bot_session_doc.AddMember("dialog_state", dialog_state_json, bot_session_doc.GetAllocator());
    }   
    if (!bot_session_doc["dialog_state"].HasMember("contexts")) {
        rapidjson::Value contexts_json(rapidjson::kObjectType);
        bot_session_doc["dialog_state"].AddMember("contexts", contexts_json, bot_session_doc.GetAllocator());    
    }
    if (!bot_session_doc["dialog_state"]["contexts"].HasMember("dmkit")) {    
        rapidjson::Value contexts_json(rapidjson::kObjectType);
        bot_session_doc["dialog_state"]["contexts"].AddMember("dmkit", contexts_json, bot_session_doc.GetAllocator());
    }
    if (bot_session_doc["dialog_state"]["contexts"]["dmkit"].HasMember("session")) {
        bot_session_doc["dialog_state"]["contexts"]["dmkit"].RemoveMember("session");    
    }
    bot_session_doc["dialog_state"]["contexts"]["dmkit"].AddMember(
        "session", dm_session, bot_session_doc.GetAllocator());
    
    // DMKit result as a custom reply
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    writer.StartObject();
    writer.Key("event_name");
    writer.String("DM_RESULT");
    writer.Key("result");
    std::string policy_output_str = PolicyOutput::to_json_str(*policy_output);
    writer.String(policy_output_str.c_str(), policy_output_str.length());
    writer.EndObject();
    std::string custom_reply = buffer.GetString();
    APP_LOG(TRACE) << "custom_reply: " << custom_reply;

    bot_session_doc["interactions"][0]["response"]["action_list"][0]["type"] = "event";
    bot_session_doc["interactions"][0]["response"]["action_list"][0]["say"] = "";
    bot_session_doc["interactions"][0]["response"]["action_list"][0]["custom_reply"].SetString(
        custom_reply.c_str(), custom_reply.length(), unit_response_doc.GetAllocator());

    unit_response_doc["result"]["response"]["action_list"][0]["type"] = "event";
    unit_response_doc["result"]["response"]["action_list"][0]["say"] = "";
    unit_response_doc["result"]["response"]["action_list"][0]["custom_reply"].SetString(
        custom_reply.c_str(), custom_reply.length(), unit_response_doc.GetAllocator());

    std::string bot_session = utils::json_to_string(bot_session_doc);
    //unit_response_doc.AddMember("debug", bot_session_doc, unit_response_doc.GetAllocator());
    unit_response_doc["result"]["bot_session"].SetString(
        bot_session.c_str(), bot_session.length(), unit_response_doc.GetAllocator());
}

std::string DialogManager::get_error_response(int error_code, const std::string& error_msg) {
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    writer.StartObject();
    writer.Key("error_code");
    writer.Int(error_code);
    writer.Key("error_msg");
    writer.String(error_msg.c_str(), error_msg.length());
    writer.EndObject();

    return buffer.GetString();
}

void DialogManager::send_json_response(BRPC_NAMESPACE::Controller* cntl,
                                       const std::string& data) {
    cntl->http_response().set_content_type("application/json;charset=UTF-8");
    this->add_notice_log("ret", data);
    cntl->response_attachment().append(data);
}

} // namespace dmkit



================================================
FILE: src/dialog_manager.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "application_base.h"
#include "policy.h"
#include "policy_manager.h"
#include "qu_result.h"
#include "rapidjson.h"
#include "remote_service_manager.h"
#include "token_manager.h"

#ifndef DMKIT_DIALOG_MANAGER_H
#define DMKIT_DIALOG_MANAGER_H

namespace dmkit {

// The Dialog Manager application
class DialogManager : public ApplicationBase {
public:
    DialogManager();
    virtual ~DialogManager();
    virtual int init();
    virtual int run(BRPC_NAMESPACE::Controller* cntl);

private:
    int process_request(const rapidjson::Document& request_doc,
                        const std::string& dm_session,
                        const std::string& access_token,
                        std::string& json_response,
                        bool& is_dmkit_response);

    int call_unit_bot(const std::string& access_token,
                      const std::string& payload,
                      std::string& result);

    int handle_unsatisfied_intent(rapidjson::Document& unit_response_doc,
                                  rapidjson::Document& bot_session_doc,
                                  const std::string& dm_session,
                                  std::string& response);

    std::string get_error_response(int error_code, const std::string& error_msg);

    void send_json_response(BRPC_NAMESPACE::Controller* cntl, const std::string& data);

    void set_dm_response(rapidjson::Document& unit_response_doc,
                         rapidjson::Document& bot_session_doc,
                         const PolicyOutput* policy_output);

    RemoteServiceManager* _remote_service_manager;
    PolicyManager* _policy_manager;
    TokenManager* _token_manager;
};

} // namespace dmkit

#endif  //DMKIT_DIALOG_MANAGER_H


================================================
FILE: src/file_watcher.cpp
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "file_watcher.h"
#include <sys/time.h>
#include <sys/stat.h>
#include "app_log.h"
#include "utils.h"

namespace dmkit {

const int FileWatcher::CHECK_INTERVAL_IN_MILLS;

FileWatcher&  FileWatcher::get_instance() {
    static FileWatcher instance;
    return instance;
}

FileWatcher::FileWatcher() {
}

FileWatcher::~FileWatcher() {
    this->_is_running = false;
    if (this->_watcher_thread.joinable()) {
        this->_watcher_thread.join();
    }
}

static int get_file_last_modified_time(const std::string& file_path, std::string& mtime_str) {
    struct stat f_stat;
    if (stat(file_path.c_str(), &f_stat) != 0) {
        LOG(WARNING) << "Failed to get file modified time" << file_path;
        return -1;
    }
    mtime_str = ctime(&f_stat.st_mtime);
    return 0;
}

int FileWatcher::register_file(const std::string file_path,
                               FileChangeCallback cb,
                               void* param,
                               bool level_trigger) {
    LOG(TRACE) << "FileWatcher registering file " << file_path;
    std::string last_modified_time;
    if (get_file_last_modified_time(file_path, last_modified_time) != 0) {
        return -1;
    }

    FileStatus file_status = {file_path, last_modified_time, cb, param, level_trigger};
    std::lock_guard<std::mutex> lock(this->_mutex);
    this->_file_info[file_path] = file_status;
    if (!this->_is_running) {
        this->_is_running = true;
        this->_watcher_thread = std::thread(&FileWatcher::watcher_thread_func, this);
    }
    return 0;
}

int FileWatcher::unregister_file(const std::string file_path) {
    LOG(TRACE) << "FileWatcher unregistering file " << file_path;
    std::lock_guard<std::mutex> lock(this->_mutex);
    if (this->_file_info.erase(file_path) != 1) {
        return -1;
    }
    if (this->_file_info.size() == 0
        && this->_is_running
        && this->_watcher_thread.joinable()) {
        this->_is_running = false;
        this->_watcher_thread.join();
    }
    return 0;
}

void FileWatcher::watcher_thread_func() {
    LOG(TRACE) << "Watcher thread starting...";
    while (this->_is_running) {
        {
            std::lock_guard<std::mutex> lock(this->_mutex);
            for (const auto& file : this->_file_info) {
                std::string last_modified_time;
                get_file_last_modified_time(file.first, last_modified_time);
                if (last_modified_time != file.second.last_modified_time) {
                    LOG(TRACE) << "File Changed. " << file.first << " modified time " << last_modified_time;
                    if (file.second.callback(file.second.param) == 0
                            || !file.second.level_trigger) {
                        if (this->_file_info.find(file.first) != this->_file_info.end()) {
                            this->_file_info[file.first].last_modified_time = last_modified_time;
                        }
                    }
                }
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(FileWatcher::CHECK_INTERVAL_IN_MILLS));
    }
    LOG(TRACE) << "Watcher thread stopping...";
}

} // namespace dmkit


================================================
FILE: src/file_watcher.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_FILE_WATCHER_H
#define DMKIT_FILE_WATCHER_H

#include <atomic>
#include <mutex>
#include <thread>
#include <string>
#include <unordered_map>

namespace dmkit {

// Callback function when file changed
typedef int (*FileChangeCallback)(void* param);

struct FileStatus {
    std::string file_path;
    std::string last_modified_time;
    FileChangeCallback callback;
    void* param;
    bool level_trigger;
};

// A file watcher singleton implemention
class FileWatcher {
public:
    static FileWatcher& get_instance();

    int register_file(const std::string file_path,
                      FileChangeCallback cb,
                      void* param,
                      bool level_trigger=false);

    int unregister_file(const std::string file_path);

    // Do not need copy constructor and assignment operator for a singleton class
    FileWatcher(FileWatcher const&) = delete;
    void operator=(FileWatcher const&) = delete;
private:
    FileWatcher();
    virtual ~FileWatcher();

    void watcher_thread_func();

    std::mutex _mutex;
    std::atomic<bool> _is_running;
    std::thread _watcher_thread;
    std::unordered_map<std::string, FileStatus> _file_info;
    static const int CHECK_INTERVAL_IN_MILLS = 1000;
};

} // namespace dmkit

#endif  //DMKIT_FILE_WATCHER_H


================================================
FILE: src/policy.cpp
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "policy.h"
#include "app_log.h"
#include "utils.h"

namespace dmkit {

Policy::Policy(const PolicyTrigger& trigger, 
               const std::vector<PolicyParam>& params, 
               const std::vector<PolicyOutput>& outputs)
    : _trigger(trigger), _params(params), _outputs(outputs) {
}

const PolicyTrigger& Policy::trigger() const {
    return this->_trigger;
}

const std::vector<PolicyParam>& Policy::params() const {
    return this->_params;
}

const std::vector<PolicyOutput>& Policy::outputs() const {
    return this->_outputs;
}

// Parse a policy from json configuration.
// A sample policy is as following:
//    {
//        "trigger": {
//            "intent": "INTENT_XXX",
//            "slots": [
//                "slot_1",
//                "slot_2"
//            ],
//            "state": "001"
//        },
//        "params": [
//            {
//                "name": "param_name", 
//                "type": "slot_val", 
//                "value": "slot_tag"
//            }
//        ],
//        "output": [
//            {
//                "assertion": [
//                    {
//                        "type": "eq", 
//                        "value": "1,{%param_name%}"
//                    }
//                ],
//                "session": {
//                    "state": "002"
//                    "context": {
//                        "key1": "value1",
//                        "key2": "value2"
//                    }
//                },
//                "meta":{
//                    "key1": "value1",
//                    "key2": "value2"
//                },
//                "result": [
//                    {
//                        "type":"tts",
//                        "value": "Hello World"
//                    }
//                ]
//            }
//        ]
//    }
Policy* Policy::parse_from_json_value(const rapidjson::Value& value) {
    if (!value.IsObject()) {
        LOG(WARNING) << "Failed to parse policy from json, not an object: " << utils::json_to_string(value);
        return nullptr;
    }

    if (!value.HasMember("trigger") || !value["trigger"].IsObject()
            || !value["trigger"].HasMember("intent") || !value["trigger"]["intent"].IsString()) {
        LOG(WARNING) << "Failed to parse policy from json, invalid trigger";
        return nullptr;
    }
    PolicyTrigger trigger;
    trigger.intent = value["trigger"]["intent"].GetString();
    
    if (value["trigger"].HasMember("slots") && value["trigger"]["slots"].IsArray()) {
        for (auto& v : value["trigger"]["slots"].GetArray()) {
            trigger.slots.push_back(v.GetString());
        }
    }
    if (value["trigger"].HasMember("state") && value["trigger"]["state"].IsString()) {
        trigger.state = value["trigger"]["state"].GetString();
    }

    std::vector<PolicyParam> params;
    if (value.HasMember("params") && value["params"].IsArray()) {
        for (auto& v : value["params"].GetArray()) {
            PolicyParam param;
            param.name = v["name"].GetString();
            param.type = v["type"].GetString();
            param.value = v["value"].GetString();
            if (v.HasMember("required") && v["required"].IsBool()) {
                param.required = v["required"].GetBool();
            } else {
                param.required = false;
            }
            if (v.HasMember("default") && v["default"].IsString()) {
                param.default_value = v["default"].GetString();
            }
            params.push_back(param);
        }
    }
    
    std::vector<PolicyOutput> outputs;
    if (value.HasMember("output") && value["output"].IsArray()) {
        for (auto& v : value["output"].GetArray()) {
            PolicyOutput output;
            if (v.HasMember("assertion") && v["assertion"].IsArray()) {
                for (auto& v_assertion: v["assertion"].GetArray()) {
                    std::string assertion_type = v_assertion["type"].GetString();
                    std::string assertion_value = v_assertion["value"].GetString();
                    PolicyOutputAssertion assertion = {assertion_type, assertion_value};
                    output.assertions.push_back(assertion);
                }
            }
            if (v.HasMember("session") && v["session"].IsObject()) {
                if (v["session"].HasMember("state") && v["session"]["state"].IsString()) {
                    output.session.state = v["session"]["state"].GetString();
                }
                if (v["session"].HasMember("context") && v["session"]["context"].IsObject()) {
                    for (auto& m_context: v["session"]["context"].GetObject()) {
                        std::string context_key = m_context.name.GetString();
                        std::string context_value = m_context.value.GetString();
                        KVPair context = {context_key, context_value};
                        output.session.context.push_back(context);
                    }
                }
            }
            if (v.HasMember("meta") && v["meta"].IsObject()) {
                for (auto& m_meta: v["meta"].GetObject()) {
                    std::string meta_key = m_meta.name.GetString();
                    std::string meta_value = m_meta.value.GetString();
                    KVPair meta = {meta_key, meta_value};
                    output.meta.push_back(meta);
                }
            }
            for (auto& v_result : v["result"].GetArray()) {
                PolicyOutputResult result;
                result.type = v_result["type"].GetString();
                if (v_result["value"].IsString()) {
                    result.values.push_back(v_result["value"].GetString());
                } else if (v_result["value"].IsArray()) {
                    for (auto& v_result_value : v_result["value"].GetArray()) {
                        result.values.push_back(v_result_value.GetString());
                    }
                }
                if (v_result.HasMember("extra") && v_result["extra"].IsString()) {
                    result.extra = v_result["extra"].GetString();
                }
                output.results.push_back(result);
            }

            outputs.push_back(output);
        }
    }
    
    return new Policy(trigger, params, outputs);;
}

std::string PolicyOutputSession::to_json_str(const PolicyOutputSession& session) {
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    writer.StartObject();
    writer.Key("domain");
    writer.String(session.domain.c_str(), session.domain.length());
    writer.Key("state");
    writer.String(session.state.c_str(), session.state.length());
    writer.Key("context");
    writer.StartObject();
    for (auto const& object: session.context) {
        writer.Key(object.key.c_str(), object.key.length());
        writer.String(object.value.c_str(), object.value.length());
    }
    writer.EndObject();
    writer.EndObject();
    return buffer.GetString();
}

PolicyOutputSession PolicyOutputSession::from_json_str(const std::string& json_str) {
    PolicyOutputSession session;
    rapidjson::Document session_doc;
    if (session_doc.Parse(json_str.c_str()).HasParseError() || !session_doc.IsObject()) {
        return session;
    }
    if (session_doc.HasMember("domain")) {
        session.domain = session_doc["domain"].GetString();
    }
    if (session_doc.HasMember("state")) {
        session.state = session_doc["state"].GetString();
    }
    if (!session_doc.HasMember("context")) {
        return session;
    }
    for (auto& m_object: session_doc["context"].GetObject()) {
        std::string context_key = m_object.name.GetString();
        std::string context_value = m_object.value.GetString();
        KVPair context = {context_key, context_value};
        session.context.push_back(context);
    }

    return session;
}

std::string PolicyOutput::to_json_str(const PolicyOutput& output) {
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    writer.StartObject();
    writer.Key("meta");
    writer.StartObject();
    for (auto const& meta: output.meta) {
        writer.Key(meta.key.c_str(), meta.key.length());
        writer.String(meta.value.c_str(), meta.value.length());
    }
    writer.EndObject();
    writer.Key("result");
    writer.StartArray();
    for (auto const& result: output.results) {
        writer.StartObject();
        writer.Key("type");
        writer.String(result.type.c_str(), result.type.length());
        writer.Key("value");
        writer.String(result.values[0].c_str(), result.values[0].length());
        if (!result.extra.empty()) {
            rapidjson::Document extra_doc;
            if (!extra_doc.Parse(result.extra.c_str()).HasParseError() && extra_doc.IsObject()) {
                for (auto& v_extra: extra_doc.GetObject()) {
                    std::string extra_key = v_extra.name.GetString();
                    if (extra_key == "type" || extra_key == "value") {
                        LOG(WARNING) << "Unsupported extra key " << extra_key;
                    }
                    if (v_extra.value.IsString()) {
                        std::string extra_value = v_extra.value.GetString();
                        writer.Key(extra_key.c_str());
                        writer.String(extra_value.c_str(), extra_value.length());
                    } else if (v_extra.value.IsBool()) {
                        writer.Key(extra_key.c_str());
                        writer.Bool(v_extra.value.GetBool());
                    } else if (v_extra.value.IsInt()) {
                        writer.Key(extra_key.c_str());
                        writer.Int(v_extra.value.GetInt());
                    } else if (v_extra.value.IsDouble()) {
                        writer.Key(extra_key.c_str());
                        writer.Double(v_extra.value.GetDouble());
                    } else {
                        LOG(WARNING) << "Unknown extra value type " << v_extra.value.GetType();
                    }
                }
            } else {
                LOG(WARNING) << "Failed to parse result extra json: " <<  result.extra;
            }
        }
        writer.EndObject();
    }
    writer.EndArray();

    writer.EndObject();
    return buffer.GetString();
}

} // namespace dmkit


================================================
FILE: src/policy.h
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef DMKIT_POLICY_H
#define DMKIT_POLICY_H

#include <string>
#include <vector>
#include "rapidjson.h"

namespace dmkit {

struct KVPair {
    std::string key;
    std::string value;
};

// A trigger define the condition under which a policy is choosen,
// including intent and slots from qu result, as well as the current dm state.
struct PolicyTrigger {
    std::string intent;
    std::vector<std::string> slots;
    std::string state;
};

// The parameter required in a policy.
struct PolicyParam {
    std::string name;
    std::string type;
    std::string value;
    std::string default_value;
    bool required;
};

// Session for policy output, including current domain, user defines contexts
// and a state which DM will move to.
struct PolicyOutputSession {
    std::string domain;
    std::string state;
    std::vector<KVPair> context;

    static std::string to_json_str(const PolicyOutputSession& session);

    static PolicyOutputSession from_json_str(const std::string& json_str);
};

// A result item of dm output.
struct PolicyOutputResult {
    std::string type;
    std::vector<std::string> values;
    std::string extra;
};

struct PolicyOutputQuSlot {
    std::string key;
    std::string value;
    std::string normalized_value;
};

// In case the Qu result is required as well, currently not used.
struct PolicyOutputQu {
    std::string domain;
    std::string intent;
    std::vector<PolicyOutputQuSlot> slots;
};

// An assertion defines the condition under which a result is choosen.
struct PolicyOutputAssertion {
    std::string type;
    std::string value;
};

// Schema for DMKit output
struct PolicyOutput {
    std::vector<PolicyOutputAssertion> assertions;
    PolicyOutputQu qu;
    std::vector<KVPair> meta;
    PolicyOutputSession session;
    std::vector<PolicyOutputResult> results;

    static std::string to_json_str(const PolicyOutput& output);
};

// A policy defines a processing(params) & response(output),
// given a trigger(intent+slots+state) condition.
class Policy {
public:
    Policy(const PolicyTrigger& trigger,
           const std::vector<PolicyParam>& params, 
           const std::vector<PolicyOutput>& outputs);
    const PolicyTrigger& trigger() const;
    const std::vector<PolicyParam>& params() const;
    const std::vector<PolicyOutput>& outputs() const;

    static Policy* parse_from_json_value(const rapidjson::Value& value);

private:
    PolicyTrigger _trigger;
    std::vector<PolicyParam> _params;
    std::vector<PolicyOutput> _outputs;
};

} // namespace dmkit

#endif  //DMKIT_POLICY_H


================================================
FILE: src/policy_manager.cpp
================================================
// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "policy_manager.h"
#include <ctime>
#include <forward_list>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unordered_set>
#include <unordered_map>
#include <unistd.h>
#include <utility>
#include "app_log.h"
#include "file_watcher.h"
#include "utils.h"

namespace dmkit {

DomainPolicy::DomainPolicy(const std::string& name, int score, IntentPolicyMap* intent_policy_map)
    : _name(name), _score(score), _intent_policy_map(intent_policy_map) {
}

DomainPolicy::~DomainPolicy() {
    if (this->_intent_policy_map == nullptr) {
        return;
    }
    for (IntentPolicyMap::iterator iter = this->_intent_policy_map->begin();
            iter != this->_intent_policy_map->end(); ++iter) {
        auto policy_vector = iter->second;
        if (policy_vector == nullptr) {
            continue;
        }
        for (PolicyVector::iterator iter2 = policy_vector->begin();
                iter2 != policy_vector->end(); ++iter2) {
            auto policy = *iter2;
            if (policy == nullptr) {
                continue;
            }
            delete policy;
            *iter2 = nullptr;
        }
        delete policy_vector;
        iter->second = nullptr;
    }
    delete this->_intent_policy_map;
    this->_intent_policy_map = nullptr;
}

int DomainPolicy::score() {
    return this->_score;
}

const std::string& DomainPolicy::name() {
    return this->_name;
}

IntentPolicyMap* DomainPolicy::intent_policy_map() {
    return this->_intent_policy_map;
}

PolicyManager::PolicyManager() {
    this->_user_function_manager = nullptr;
}

PolicyManager::~PolicyManager() {
    if (this->_user_function_manager != nullptr) {
        delete this->_user_function_manager;
        this->_user_function_manager = nullptr;
    }

    FileWatcher::get_instance().unregister_file(this->_conf_file_path);
    this->_p_policy_dict.reset();
}

static inline void destroy_policy_dict(ProductPolicyMap* policy_dict) {
    LOG(TRACE) << "Destroying policy dict";
    if (policy_dict == nullptr) {
        return;
    }
    for (ProductPolicyMap::iterator iter = policy_dict->begin(); iter != policy_dict->end();
         ++iter) {
        auto domain_policy_map = iter->second;
        if (domain_policy_map == nullptr) {
            continue;
        }
        for (DomainPolicyMap::iterator iter2 = domain_policy_map->begin();
             iter2 != domain_policy_map->end(); ++iter2) {
            auto domain_policy = iter2->second;
            if (domain_policy == nullptr) {
                continue;
            }
            delete domain_policy;
            iter2->second = nullptr;
        }
        delete domain_policy_map;
        iter->second = nullptr;
    }
    delete policy_dict;
    policy_dict = nullptr;
}

// Loads policies from JSON configuration files.
int PolicyManager::init(const char* dir_path, const char* conf_file) {
    std::string file_path;
    if (dir_path != nullptr) {
        file_path += dir_path;
    }
    if (!file_path.empty() && file_path[file_path.length() - 1] != '/') {
        file_path += '/';
    }
    if (conf_file != nullptr) {
        file_path += conf_file;
    }
    this->_conf_file_path = file_path;

    ProductPolicyMap* policy_dict = this->load_policy_dict();
    if (policy_dict == nullptr) {
        APP_LOG(ERROR) << "Failed to init policy dict";
        return -1;
    }
    this->_p_policy_dict.reset(policy_dict, [](ProductPolicyMap* p) { destroy_policy_dict(p); });
    FileWatcher::get_instance().register_file(
        this->_conf_file_path, PolicyManager::policy_conf_change_callback, this, true);

    this->_user_function_manager = new UserFunctionManager();
    if (this->_user_function_manager->init() != 0) {
        APP_LOG(ERROR) << "Failed to init UserFunctionManager";
        return -1;
    }

    return 0;
}

int PolicyManager::reload() {
    LOG(TRACE) << "Reloading policy dict";
    ProductPolicyMap * policy_dict = this->load_policy_dict();
    if (policy_dict == nullptr) {
        LOG(WARNING) << "Cannot reload policy! Policy dict load failed.";
        return -1;
    }

    this->_p_policy_dict.reset(policy_dict, [](ProductPolicyMap* p) { destroy_policy_dict(p); });
    APP_LOG(TRACE) << "Reload finished.";
    return 0;
}

int PolicyManager::policy_conf_change_callback(void* param) {
    PolicyManager* pm = (PolicyManager*)param;
    return pm->reload();
}

PolicyOutput* PolicyManager::resolve(const std::string& product,
                                     BUTIL_NAMESPACE::FlatMap<std::string, QuResult*>* qu_result,
                                     const PolicyOutputSession& session,
                                     const RequestContext& context) {
    std::shared_ptr<ProductPolicyMap> p_policy_map(this->_p_policy_dict);
    if (p_policy_map == nullptr) {
        APP_LOG(ERROR) << "Policy resolve failed, empty policy dict";
        return nullptr;
    }

    DomainPolicyMap** seek_result = p_policy_map->seek(product);
    if (seek_result == nullptr) {
        APP_LOG(WARNING) << "unkown product " << product;
        return nullptr;
    }

    DomainPolicyMap* domain_policy_map = *seek_result;
    DomainPolicy* state_domain_policy = nullptr;
    Policy* state_policy = nullptr;
    std::forward_list<std::pair<DomainPolicy*, Policy*>> ranked_policies;
    std::vector<Slot> empty_slots;
    QuResult empty_qu("", "", empty_slots);
    std::string request_domain;
    context.try_get_param("domain", request_domain);
    for (DomainPolicyMap::iterator iter = domain_policy_map->begin();
            iter != domain_policy_map->end(); ++iter) {
        auto domain_policy = iter->second;
        const std::string& domain_name = domain_policy->name();
        if (!request_domain.empty() && domain_name != request_domain) {
            continue;
        }
        QuResult** qu_seek_result = qu_result->seek(domain_name);
        QuResult* qu = qu_seek_result != nullptr ? *qu_seek_result : &empty_qu;

        // Find a best policy give the qu result in current domain
        Policy* find_result = this->find_best_policy(domain_policy, qu, session, context);
        if (find_result == nullptr) {
            continue;
        }

        // Policy with a domain and trigger state matches current DMKit domain&state is ranked top
        if (!session.domain.empty() && (domain_name == session.domain)
            && !session.state.empty() && (find_result->trigger().state == session.state)) {
            state_domain_policy = domain_policy;
            state_policy = find_result;;
            continue;
        }

        // In case there are multiple domain results, the policies a ranked by static domain score
        auto previous_it = ranked_policies.before_begin();
        for (auto it = ranked_policies.begin(); it != ranked_policies.end(); ++it) {
            if (domain_policy->score() > it->first->score()) {
                break;
            }
            previous_it = it;
        }
        std::pair<DomainPolicy*, Policy*> p(domain_policy, find_result);
        ranked_policies.insert_after(previous_it, p);
    }

    if (state_domain_policy != nullptr && state_policy != nullptr) {
        std::pair<DomainPolicy*, Policy*> p(state_domain_policy, state_policy);
        ranked_policies.insert_after(ranked_policies.before_begin(), p);
    }

    // Resolve policy output and returns the first output resolved successfully.
    for (auto& p: ranked_policies) {
        const std::string& domain_name = p.first->name();
        APP_LOG(TRACE) << "Resolving policy output for domain [" << domain_name << "]";
        QuResult** qu_seek_result = qu_result->seek(domain_name);
        QuResult* qu = qu_seek_result != nullptr ? *qu_seek_result : &empty_qu;
        PolicyOutput* result = this->resolve_policy_output(domain_name,
                p.second, qu, session, context);
        if (result != nullptr) {
            APP_LOG(TRACE) << "Final result domain [" << domain_name << "]";
            return result;
        }
    }

    return nullptr;
}

ProductPolicyMap* PolicyManager::load_policy_dict() {
    ProductPolicyMap* product_policy_map = new ProductPolicyMap();
    // 10: bucket_count, initial count of buckets, big enough to avoid resize.
    // 80: load_factor, element_count * 100 / bucket_count.
    product_policy_map->init(10, 80);

    FILE* fp = fopen(this->_conf_file_path.c_str(), "r");
    if (fp == nullptr) {
        APP_LOG(ERROR) << "Failed to open file " << this->_conf_file_path;
        destroy_policy_dict(product_policy_map);
        return nullptr;
    }
    char read_buffer[1024];
    rapidjson::FileReadStream is(fp, read_buffer, sizeof(read_buffer));
    rapidjson::Document doc;
    doc.ParseStream(is);
    fclose(fp);

    if (doc.HasParseError() || !doc.IsObject()) {
        APP_LOG(ERROR) << "Failed to parse products.json file";
        destroy_policy_dict(product_policy_map);
        return nullptr;
    }

    for (rapidjson::Value::ConstMemberIterator prod_iter = doc.MemberBegin();
            prod_iter != doc.MemberEnd(); ++prod_iter) {
        std::string prod_name = prod_iter->name.GetString();
        if (!prod_iter->value.IsObject()) {
            APP_LOG(ERROR) << "Invalid product conf for " << prod_name;
            destroy_policy_dict(product_policy_map);
            return nullptr;
        }
        DomainPolicyMap* domain_policy_map = this->load_domain_policy_map(prod_name, prod_iter->value);
        if (domain_policy_map == nullptr) {
            APP_LOG(ERROR) << "Failed to load policies for product " << prod_name;
            destroy_policy_dict(product_policy_map);
            return nullptr;
        }
        product_policy_map->insert(prod_name, domain_policy_map);
    }

    return product_policy_map;
}

DomainPolicyMap* PolicyManager::load_domain_policy_map(const std::string& product_name,
                                                        const rapidjson::Value& product_json) {
    DomainPolicyMap* domain_policy_map = new DomainPolicyMap();
    // 10: bucket_count, initial count of buckets, big enough to avoid resize.
    // 80: load_factor, element_count * 100 / bucket_count.
    domain_policy_map->init(10, 80);

    APP_LOG(TRACE) << "Loading policies for product: " << product_name;
    for (rapidjson::Value::ConstMemberIterator domain_iter = product_json.MemberBegin();
            domain_iter != product_json.MemberEnd(); ++domain_iter) {
        std::string domain_name = domain_iter->name.GetString();
        const rapidjson::Value& domain_json = domain_iter->value;
        rapidjson::Value::ConstMemberIterator setting_iter;
        setting_iter = domain_json.FindMember("score");
        if (setting_iter == domain_json.MemberEnd() || !setting_iter->value.IsInt()) {
            APP_LOG(WARNING) << "Failed to parse score for domain "
                << domain_name << " in product " << product_name << ", skipped";
            continue;
        }
        int score = setting_iter->value.GetInt();

        setting_iter = domain_json.FindMember("conf_path");
        if (setting_iter == domain_json.MemberEnd() || !setting_iter->value.IsString()) {
            APP_LOG(WARNING) << "Failed to parse conf_path for domain "
                << domain_name << " in product " << product_name << ", skipped";
            continue;
        }
        std::string conf_path = setting_iter->value.GetString();

        APP_LOG(TRACE) << "Loading policies for domain " << domain_name << " from " << conf_path;
        DomainPolicy* domain_policy = this->load_domain_policy(domain_name, score, conf_path);
        if (domain_policy == nullptr) {
            APP_LOG(WARNING) << "Failed to load policy for domain "
                << domain_name << " in product " << product_name << ", skipped";
            continue;
        }
        APP_LOG(TRACE) << "Loaded policies for domain " << domain_name;

        domain_policy_map->insert(domain_name, domain_policy);
    }

    return domain_policy_map;
}

DomainPolicy* PolicyManager::load_domain_policy(const std::string& domain_name,
                                                int score,
                                                const std::string& conf_path) {
    FILE* fp = fopen(conf_path.c_str(), "r");
    if (fp == nullptr) {
        APP_LOG(ERROR) << "Failed to open file " << conf_path;
        return nullptr;
    }
    char read_buffer[1024];
    rapidjson::FileReadStream is(fp, read_buffer, sizeof(read_buffer));
    rapidjson::Document doc;
    doc.ParseStream(is);
    fclose(fp);
    if (doc.HasParseError() || !doc.IsArray()) {
        APP_LOG(ERROR) << "Failed to parse domain conf " << conf_path;
        return nullptr;
    }
    IntentPolicyMap* intent_policy_map = new IntentPolicyMap();
    // 10: bucket_count, initial count of buckets, big enough to avoid resize.
    // 80: load_factor, element_count * 100 / bucket_count.
    intent_policy_map->init(10, 80);
    for (rapidjson::Value::ConstValueIterator policy_iter = doc.Begin();
            policy_iter != doc.End(); ++policy_iter) {
        APP_LOG(TRACE) << "loading policy...";
        Policy* policy = Policy::parse_from_json_value(*policy_iter);
        if (policy == nullptr) {
            APP_LOG(WARNING) << "Found invalid policy conf in path " << conf_path << ", skipped";
            continue;
        }
        const std::string& trigger_intent = policy->trigger().intent;
        if (intent_policy_map->seek(trigger_intent) == nullptr) {
            intent_policy_map->insert(trigger_intent, new PolicyVector);
        }
        (*intent_policy_map)[trigger_intent]->push_back(policy);
    }
    APP_LOG(TRACE) << "initializing domain policy...";
    DomainPolicy* domain_policy = new DomainPolicy(domain_name, score, intent_policy_map);
    APP_LOG(TRACE) << "finish initializing domain policy...";
    return domain_policy;
}

// When multiple policies satisfy the current intent,
// the following strategy is applies to find the best one:
// 1. The policies with none empty trigger state, it should match current DM state.
// 2. Policy with maximum number of matched trigger slot is ranked top.
static Policy* find_best_policy_from_candidates(PolicyVector& policy_vector,
                                                std::string& state,
                                                std::unordered_multiset<std::string>& qu_slot_set) {
    Policy* policy_result = nullptr;
    for (auto const& policy: policy_vector) {
        if (!policy->trigger().state.empty() && policy->trigger().state != state) {
            continue;
        }

        bool missing_slot = false;
        std::unordered_multiset<std::string> trigger_slot_set;
        for (auto const& slot: policy->trigger().slots) {
            trigger_slot_set.insert(slot);
        }
        for (auto iter = trigger_slot_set.begin(); iter != trigger_slot_set.end();) {
            int trigger_slot_cnt = trigger_slot_set.count(*iter);
            int qu_slot_cnt = qu_slot_set.count(*iter);
            if (qu_slot_cnt < trigger_slot_cnt) {
                missing_slot = true;
                break;
            }
            std::advance(iter, trigger_slot_cnt);
        }
        if (missing_slot) {
            continue;
        }

        if (policy_result == nullptr) {
            policy_result = policy;
            continue;
        }

        if (!policy_result->trigger().state.empty() && policy->trigger().state.empty()) {
            continue;
        }

        if (policy_result->trigger().state.empty() && !policy->trigger().state.empty()) {
            policy_result = policy;
            continue;
        }

        if (policy_result->trigger().slots.size() < policy->trigger().slots.size()) {
            policy_result = policy;
        }
    }

    return policy_result;
}

Policy* PolicyManager::find_best_policy(DomainPolicy* domain_policy,
                                        QuResult* qu_result,
                                        const PolicyOutputSession& session,
                                        const RequestContext& context) {
    (void) context;

    std::string state;
    if (domain_policy->name() == session.domain) {
        state = session.state;
    }

    std::unordered_multiset<std::string> qu_slot_set;
    IntentPolicyMap* intent_policy_map = domain_policy->intent_policy_map();
    PolicyVector** policy_vector_seek = nullptr;
    Policy* policy_result = nullptr;

    for (auto const& slot: qu_result->slots()) {
        qu_slot_set.insert(slot.key());
    }
    // Policy with matching intent.
    const std::string& intent = qu_result->intent();
    policy_vector_seek = intent_policy_map->seek(intent);
    if (policy_vector_seek != nullptr) {
        APP_LOG(TRACE) << "intent [" << intent << "] candidate count [" << (*policy_vector_seek)->size() << "]";
        policy_result = find_best_policy_from_candidates(**policy_vector_seek, state, qu_slot_set);
    }

    // Fallback policy when none of the policies match intent.
    if (policy_result == nullptr) {
        const std::string fallback_intent = "dmkit_intent_fallback";
        policy_vector_seek = intent_policy_map->seek(fallback_intent);
        if (policy_vector_seek != nullptr) {
            APP_LOG(TRACE) << "dmkit_intent_fallback candidate count [" << (*policy_vector_seek)->size() << "]";
            policy_result = find_best_policy_from_candidates(**policy_vector_seek, state, qu_slot_set);
        }
    }

    return policy_result;
}

// Resolve a string with params in it.
static bool try_resolve_params(std::string& unresolved,
                              const std::unordered_map<std::string, std::string>& param_map) {

    if (unresolved.empty()) {
        return true;
    }

    std::string resolved;
    bool is_param = false;
    unsigned int last_index = 0;
    for (unsigned int i = 0; i < unresolved.length(); ++i) {
        if (unresolved[i] == '{' && i + 1 < unresolved.length() && unresolved[i + 1] == '%' ) {
            resolved += unresolved.substr(last_index, i - last_index);
            last_index = i + 2;
            is_param = true;
            ++i;
        }
        if (unresolved[i] == '%' && i + 1 < unresolved.length() && unresolved[i + 1] == '}') {
            if (!is_param) {
                APP_LOG(WARNING) << "Cannot resolve params in string, invalid format. " << unresolved;
                return false;
            }
            std::string param_name = unresolved.substr(last_index, i - last_index);
            std::unordered_map<std::string, std::string>::const_iterator find_res = param_map.find(param_name);
            if (find_res == param_map.end()) {
                APP_LOG(WARNING) << "Cannot resolve params in string, unknow param. "
                    << unresolved << " " << param_name;
                return false;
            }
            resolved += find_res->second;
            last_index = i + 2;
            is_param = false;
            ++i;
        }
    }
    if (is_param) {
        APP_LOG(WARNING) << "Cannot resolve params in string, invalid format. " << unresolved;
        return false;
    }
    if (last_index < unresolved.length()) {
        resolved += unresolved.substr(last_index);
    }

    unresolved = resolved;
    return true;
}

// Resolve a string with delimiter and params in it
static bool try_resolve_param_list(const std::string& unresolved,
                                   const char delimiter,
                                   const std::unordered_map<std::string, std::string>& param_map,
                                   std::vector<std::string> &result) {
    std::size_t pos = 0;
    std::size_t last_pos = 0;
    result.clear();
    while (last_pos < unresolved.length() && (pos = unresolved.find(delimiter, last_pos)) != std::string::npos) {
        std::string part = unresolved.substr(last_pos, pos - last_pos);
        utils::trim(part);
        if (!try_resolve_params(part, param_map)) {
            result.clear();
            return false;
        }
        result.push_back(part);
        last_pos = pos + 1;
    }
    if (last_pos < unresolved.length()) {
        std::string part = unresolved.substr(last_pos);
        utils::trim(part);
        if (!try_resolve_params(part, param_map)) {
            result.clear();
            return false;
        }
        result.push_back(part);
    }

    return true;
}

PolicyOutput* PolicyManager::resolve_policy_output(const std::string& domain,
                                                   Policy* policy,
                                                   QuResult* qu_result,
                                                   const PolicyOutputSession& session,
                                                   const RequestContext& context) {
    // Process parameters
    std::unordered_map<std::string, std::string> param_map;
    // Default parameters
    for (auto const& context: session.context) {
        if (context.key == "dmkit_param_last_tts") {
            param_map[context.key] =  context.value;
            continue;
        }
        std::string param_key = "dmkit_param_context_";
        param_key += context.key;
        param_map[param_key] =  context.value;
    }
    for (auto const& slot: qu_result->slots()) {
        std::string param_key = "dmkit_param_slot_";
        param_key += slot.key();
        if (param_map.find(param_key) != param_map.end()) {
            continue;
        }
        std::string param_value = slot.normalized_value();
        if (param_value.empty()) {
            param_value = slot.value();
        }
        param_map[param_key] = param_value;
    }
    for (auto const& param: policy->params()) {
        APP_LOG(TRACE) << "resolving parameter [" << param.name << "]";
        std::string value;
        if (param.type == "slot_val" || param.type == "slot_val_ori") {
            bool success = false;
            std::vector<std::string> args;
            utils::split(param.value, ',', args);
            int index = 0;
            if (args.size() >= 2 && !utils::try_atoi(args[1], index)) {
                APP_LOG(WARNING) << "Invalid index for slot_val parameter: " << param.value;
                index = -1;
            }
            for (auto const& slot: qu_result->slots()) {
                if (index < 0) {
                    break;
                }
                if (slot.key() == args[0]) {
                    if (index > 0) {
                        index--;
                        continue;
                    }
                    if (param.type == "slot_val" && !slot.normalized_value().empty()) {
                        value = slot.normalized_value();
                        success = true;
                        break;
                    }
                    value = slot.value();
                    success = true;
                    break;
                }
            }
            if (!success) {
                if (param.required) {
                    return nullptr;
                }
                value = param.default_value;
            }
        } else if (param.type == "qu_intent") {
            value = qu_result->intent();
        } else if (param.type == "session_state") {
            value = session.state;
        } else if (param.type == "session_context") {
            bool success = false;
            for (auto const& obj: session.context) {
                if (obj.key == param.value) {
                    value = obj.value;
                    success = true;
                    break;
                }
            }
            if (!success) {
                if (param.required) {
                    return nullptr;
                }
                value = param.default_value;
            }
        }else if (param.type == "const") {
            value = param.value;
        } else if (param.type == "string") {
            value = param.value;
            if (!try_resolve_params(value, param_map)) {
                if (param.required) {
                    return nullptr;
                }
                value = param.default_value;
            }
        } else if (param.type == "request_param") {
            const std::unordered_map<std::string, std::string> request_params = context.params();
            std::unordered_map<std::string, std::string>::const_iterator find_res
                = request_params.find(param.value);
            bool success = false;
            if (find_res != request_params.end()) {
                value = find_res->second;
                success = true;
            }
            if (!success) {
                if (param.required) {
                    return nullptr;
                }
                value = param.default_value;
            }
        } else if (param.type == "func_val") {
            std::string func_val = param.value;
            std::string func_name;
            std::vector<std::string> args;
            std::size_t pos = func_val.find(':');
            bool has_error = false;
            if (pos == std::string::npos || pos == func_val.length() - 1) {
                func_name = func_val;
                if (!try_resolve_params(func_name, param_map)) {
                    has_error = true;
                }
            } else {
                func_name = func_val.substr(0, pos);
                if (!try_resolve_params(func_name, param_map)) {
                    has_error = true;;
                }
                std::string arg_list = func_val.substr(pos + 1);
                if (!try_resolve_param_list(arg_list, ',', param_map, args)) {
                    has_error = true;
                }
            }
            utils::trim(func_name);
            if (has_error || this->_user_function_manager->call_user_function(func_name, args, context, value) != 0) {
                has_error = true;
            }
            if (has_error) {
                if (param.required) {
                    return nullptr;
                }
                value = param.default_value;
            }
        } else {
            APP_LOG(WARNING) << "Unknown param type " << param.type;
            if (param.required) {
                return nullptr;
            }
            value = param.default_value;
        }
        param_map[param.name] =  value;
        APP_LOG(TRACE) << "Parameter value [" << value << "]";
    }

    int selected_output_index = -1;
    for (unsigned int i = 0; i < policy->outputs().size(); ++i) {
        bool failed = false;
        // Process assertions
        APP_LOG(TRACE) << "Candidate output size [" << policy->outputs().size() << "]";
        for (unsigned int j = 0; j < policy->outputs()[i]
Download .txt
gitextract_m7sg05uu/

├── .gitignore
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── NOTICE
├── README.md
├── conf/
│   ├── app/
│   │   ├── bot_tokens.json
│   │   ├── demo/
│   │   │   ├── book_hotel.json
│   │   │   ├── book_hotel.xml
│   │   │   ├── cellular_data.json
│   │   │   ├── cellular_data.xml
│   │   │   ├── quota_adjust.json
│   │   │   └── quota_adjust.xml
│   │   ├── products.json
│   │   └── remote_services.json
│   └── gflags.conf
├── deps.sh
├── docs/
│   ├── demo_book_hotel_pattern.txt
│   ├── demo_cellular_data_pattern.txt
│   ├── demo_quota_adjust_pattern.txt
│   ├── demo_skills.md
│   ├── faq.md
│   ├── tutorial.md
│   └── visual_tool.md
├── language_compiler/
│   ├── compiler_xml.py
│   ├── run.py
│   └── settings.cfg
├── proto/
│   └── http.proto
├── src/
│   ├── app_container.cpp
│   ├── app_container.h
│   ├── app_log.h
│   ├── application_base.h
│   ├── brpc.h
│   ├── butil.h
│   ├── dialog_manager.cpp
│   ├── dialog_manager.h
│   ├── file_watcher.cpp
│   ├── file_watcher.h
│   ├── policy.cpp
│   ├── policy.h
│   ├── policy_manager.cpp
│   ├── policy_manager.h
│   ├── qu_result.cpp
│   ├── qu_result.h
│   ├── rapidjson.h
│   ├── remote_service_manager.cpp
│   ├── remote_service_manager.h
│   ├── request_context.cpp
│   ├── request_context.h
│   ├── server.cpp
│   ├── thirdparty/
│   │   └── rapidjson/
│   │       ├── allocators.h
│   │       ├── document.h
│   │       ├── encodedstream.h
│   │       ├── encodings.h
│   │       ├── error/
│   │       │   ├── en.h
│   │       │   └── error.h
│   │       ├── filereadstream.h
│   │       ├── filewritestream.h
│   │       ├── fwd.h
│   │       ├── internal/
│   │       │   ├── biginteger.h
│   │       │   ├── diyfp.h
│   │       │   ├── dtoa.h
│   │       │   ├── ieee754.h
│   │       │   ├── itoa.h
│   │       │   ├── meta.h
│   │       │   ├── pow10.h
│   │       │   ├── regex.h
│   │       │   ├── stack.h
│   │       │   ├── strfunc.h
│   │       │   ├── strtod.h
│   │       │   └── swap.h
│   │       ├── istreamwrapper.h
│   │       ├── memorybuffer.h
│   │       ├── memorystream.h
│   │       ├── msinttypes/
│   │       │   ├── inttypes.h
│   │       │   └── stdint.h
│   │       ├── ostreamwrapper.h
│   │       ├── pointer.h
│   │       ├── prettywriter.h
│   │       ├── rapidjson.h
│   │       ├── reader.h
│   │       ├── schema.h
│   │       ├── stream.h
│   │       ├── stringbuffer.h
│   │       └── writer.h
│   ├── thread_data_base.h
│   ├── token_manager.cpp
│   ├── token_manager.h
│   ├── user_function/
│   │   ├── demo.cpp
│   │   ├── demo.h
│   │   ├── shared.cpp
│   │   └── shared.h
│   ├── user_function_manager.cpp
│   ├── user_function_manager.h
│   └── utils.h
└── tools/
    ├── bot_emulator.py
    └── mock_api_server.py
Download .txt
SYMBOL INDEX (632 symbols across 66 files)

FILE: language_compiler/compiler_xml.py
  class Node (line 29) | class Node(object):
    method __init__ (line 33) | def __init__(self, node_id, node_type, node_status, node_nlg, state, p...
  class Arrow (line 44) | class Arrow(object):
    method __init__ (line 48) | def __init__(self, arrow_source, arrow_target):
  class XmlParser (line 54) | class XmlParser(object):
    method __init__ (line 59) | def __init__(self, xml_data):
    method __clean_noise (line 75) | def __clean_noise(self, value, stri='', noise=None):
    method __clean_span (line 84) | def __clean_span(self, value, stri=''):
    method __parse_cells (line 87) | def __parse_cells(self):
    method __connect_nodes (line 187) | def __connect_nodes(self):
    method __extract_policy (line 202) | def __extract_policy(self):
    method write_json (line 329) | def write_json(self):
  function run (line 337) | def run(data):

FILE: language_compiler/run.py
  function main (line 30) | def main():

FILE: src/app_container.cpp
  type dmkit (line 21) | namespace dmkit {
    function ThreadLocalDataFactory (line 71) | ThreadLocalDataFactory* AppContainer::get_thread_local_data_factory() {

FILE: src/app_container.h
  function namespace (line 21) | namespace dmkit {

FILE: src/app_log.h
  function namespace (line 22) | namespace dmkit {

FILE: src/application_base.h
  function namespace (line 21) | namespace dmkit {

FILE: src/dialog_manager.cpp
  type dmkit (line 24) | namespace dmkit {

FILE: src/dialog_manager.h
  function namespace (line 26) | namespace dmkit {

FILE: src/file_watcher.cpp
  type dmkit (line 21) | namespace dmkit {
    function FileWatcher (line 25) | FileWatcher&  FileWatcher::get_instance() {
    function get_file_last_modified_time (line 40) | static int get_file_last_modified_time(const std::string& file_path, s...

FILE: src/file_watcher.h
  function namespace (line 24) | namespace dmkit {

FILE: src/policy.cpp
  type dmkit (line 19) | namespace dmkit {
    function PolicyTrigger (line 27) | const PolicyTrigger& Policy::trigger() const {
    function Policy (line 85) | Policy* Policy::parse_from_json_value(const rapidjson::Value& value) {
    function PolicyOutputSession (line 202) | PolicyOutputSession PolicyOutputSession::from_json_str(const std::stri...

FILE: src/policy.h
  function namespace (line 22) | namespace dmkit {

FILE: src/policy_manager.cpp
  type dmkit (line 29) | namespace dmkit {
    function IntentPolicyMap (line 69) | IntentPolicyMap* DomainPolicy::intent_policy_map() {
    function destroy_policy_dict (line 87) | static inline void destroy_policy_dict(ProductPolicyMap* policy_dict) {
    function PolicyOutput (line 164) | PolicyOutput* PolicyManager::resolve(const std::string& product,
    function ProductPolicyMap (line 246) | ProductPolicyMap* PolicyManager::load_policy_dict() {
    function DomainPolicyMap (line 290) | DomainPolicyMap* PolicyManager::load_domain_policy_map(const std::stri...
    function DomainPolicy (line 334) | DomainPolicy* PolicyManager::load_domain_policy(const std::string& dom...
    function Policy (line 379) | static Policy* find_best_policy_from_candidates(PolicyVector& policy_v...
    function Policy (line 428) | Policy* PolicyManager::find_best_policy(DomainPolicy* domain_policy,
    function try_resolve_params (line 469) | static bool try_resolve_params(std::string& unresolved,
    function try_resolve_param_list (line 517) | static bool try_resolve_param_list(const std::string& unresolved,
    function PolicyOutput (line 547) | PolicyOutput* PolicyManager::resolve_policy_output(const std::string& ...

FILE: src/policy_manager.h
  function namespace (line 29) | namespace dmkit {

FILE: src/qu_result.cpp
  type dmkit (line 19) | namespace dmkit {
    function QuResult (line 44) | QuResult* QuResult::parse_from_dialog_state(const std::string& domain,

FILE: src/qu_result.h
  function class (line 24) | class Slot {

FILE: src/remote_service_manager.cpp
  type dmkit (line 24) | namespace dmkit {
    function destroy_channel_map (line 34) | static inline void destroy_channel_map(ChannelMap* p) {
    function ChannelMap (line 160) | ChannelMap* RemoteServiceManager::load_channel_map() {
    function curl_write_callback (line 346) | static size_t curl_write_callback(void *contents, size_t size, size_t ...
    type curl_slist (line 366) | struct curl_slist

FILE: src/remote_service_manager.h
  function namespace (line 26) | namespace dmkit {

FILE: src/request_context.cpp
  type dmkit (line 17) | namespace dmkit {
    function RemoteServiceManager (line 29) | const RemoteServiceManager* RequestContext::remote_service_manager() c...

FILE: src/request_context.h
  function namespace (line 22) | namespace dmkit {

FILE: src/server.cpp
  type dmkit (line 28) | namespace dmkit {
    class HttpServiceImpl (line 31) | class HttpServiceImpl : public HttpService {
      method HttpServiceImpl (line 33) | HttpServiceImpl() {}
      method init (line 37) | int init() {
      method ThreadLocalDataFactory (line 41) | ThreadLocalDataFactory* get_thread_local_data_factory() {
      method run (line 45) | void run(google::protobuf::RpcController* cntl_base,
  function main (line 63) | int main(int argc, char* argv[]) {

FILE: src/thirdparty/rapidjson/allocators.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 20) | RAPIDJSON_NAMESPACE_BEGIN
  function Clear (line 145) | void Clear() {
  function Capacity (line 158) | size_t Capacity() const {
  function Free (line 225) | static void Free(void *ptr) { (void)ptr; }
  function AddChunk (line 237) | bool AddChunk(size_t capacity) {
  type ChunkHeader (line 256) | struct ChunkHeader {

FILE: src/thirdparty/rapidjson/document.h
  type GenericMember (line 108) | typedef GenericMember<Encoding,Allocator> PlainType;
  type typename (line 109) | typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
  type std (line 110) | typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
  type typename (line 121) | typedef typename BaseType::pointer         Pointer;
  type typename (line 123) | typedef typename BaseType::reference       Reference;
  type typename (line 125) | typedef typename BaseType::difference_type DifferenceType;
  function ptr_ (line 149) | GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {}
  function ConstIterator (line 171) | bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
  function ConstIterator (line 172) | bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
  function ConstIterator (line 173) | bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
  function ConstIterator (line 174) | bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
  function operator (line 175) | bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
  function operator (line 176) | bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
  function Reference (line 181) | Reference operator*() const { return *ptr_; }
  function Pointer (line 182) | Pointer   operator->() const { return ptr_; }
  function Reference (line 183) | Reference operator[](DifferenceType n) const { return ptr_[n]; }
  type CharType (line 250) | typedef CharType Ch;
  function explicit (line 302) | explicit GenericStringRef(const CharType* str)
  function operator (line 322) | operator const Ch *() const { return s; }
  function namespace (line 391) | namespace internal {
  function namespace (line 408) | namespace internal {
  function Is (line 482) | static bool Is(const ValueType& v) { return v.IsString(); }
  function StringType (line 483) | static StringType Get(const ValueType& v) { return StringType(v.GetStrin...
  type typename (line 490) | typedef typename ValueType::Array ArrayType;
  function Is (line 491) | static bool Is(const ValueType& v) { return v.IsArray(); }
  function ArrayType (line 492) | static ArrayType Get(ValueType& v) { return v.GetArray(); }
  type typename (line 499) | typedef typename ValueType::ConstArray ArrayType;
  function Is (line 500) | static bool Is(const ValueType& v) { return v.IsArray(); }
  function ArrayType (line 501) | static ArrayType Get(const ValueType& v) { return v.GetArray(); }
  type typename (line 506) | typedef typename ValueType::Object ObjectType;
  function Is (line 507) | static bool Is(const ValueType& v) { return v.IsObject(); }
  function ObjectType (line 508) | static ObjectType Get(ValueType& v) { return v.GetObject(); }
  type typename (line 515) | typedef typename ValueType::ConstObject ObjectType;
  function Is (line 516) | static bool Is(const ValueType& v) { return v.IsObject(); }
  function ObjectType (line 517) | static ObjectType Get(const ValueType& v) { return v.GetObject(); }
  type Encoding (line 544) | typedef Encoding EncodingType;
  type Allocator (line 545) | typedef Allocator AllocatorType;
  type typename (line 546) | typedef typename Encoding::Ch Ch;
  type GenericStringRef (line 547) | typedef GenericStringRef<Ch> StringRefType;
  type typename (line 548) | typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterat...
  type typename (line 549) | typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterato...
  type GenericValue (line 550) | typedef GenericValue* ValueIterator;
  type GenericValue (line 551) | typedef const GenericValue* ConstValueIterator;
  type GenericValue (line 552) | typedef GenericValue<Encoding, Allocator> ValueType;
  function explicit (line 625) | explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
  function explicit (line 634) | explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
  function explicit (line 640) | explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
  function explicit (line 646) | explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
  function explicit (line 661) | explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
  function explicit (line 673) | explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d...
  function explicit (line 679) | explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { Se...
  function data_ (line 700) | GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
  function data_ (line 711) | GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
  type GenericValue (line 853) | typedef GenericValue<Encoding, SourceAllocator> RhsType;
  function rhs (line 900) | bool operator==(const std::basic_string<Ch>& rhs) const { return *this =...
  function GetBool (line 1001) | bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags ...
  function SizeType (line 1016) | SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data...
  function GenericValue (line 1070) | const GenericValue& operator[](const std::basic_string<Ch>& name) const ...
  function ConstMemberIterator (line 1075) | ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); ...
  function MemberIterator (line 1081) | MemberIterator MemberBegin()            { RAPIDJSON_ASSERT(IsObject()); ...
  function MemberIterator (line 1084) | MemberIterator MemberEnd()              { RAPIDJSON_ASSERT(IsObject()); ...
  function HasMember (line 1094) | bool HasMember(const Ch* name) const { return FindMember(name) != Member...
  function HasMember (line 1105) | bool HasMember(const std::basic_string<Ch>& name) const { return FindMem...
  function MemberIterator (line 1132) | MemberIterator FindMember(const Ch* name) {
  function ConstMemberIterator (line 1137) | ConstMemberIterator FindMember(const Ch* name) const { return const_cast...
  function MemberIterator (line 1172) | MemberIterator FindMember(const std::basic_string<Ch>& name) { return Fi...
  function ConstMemberIterator (line 1173) | ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const ...
  function RemoveAllMembers (line 1337) | void RemoveAllMembers() {
  function RemoveMember (line 1352) | bool RemoveMember(const Ch* name) {
  function MemberIterator (line 1380) | MemberIterator RemoveMember(MemberIterator m) {
  function MemberIterator (line 1404) | MemberIterator EraseMember(ConstMemberIterator pos) {
  function MemberIterator (line 1417) | MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterato...
  function EraseMember (line 1438) | bool EraseMember(const Ch* name) {
  function Object (line 1458) | Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
  function ConstObject (line 1459) | ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return Con...
  function SizeType (line 1471) | SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
  function Empty (line 1477) | bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size ==...
  function GenericValue (line 1501) | const GenericValue& operator[](SizeType index) const { return const_cast...
  function ValueIterator (line 1505) | ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsP...
  function ValueIterator (line 1508) | ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPoi...
  function ValueIterator (line 1610) | ValueIterator Erase(ConstValueIterator pos) {
  function ValueIterator (line 1622) | ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
  function Array (line 1637) | Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
  function ConstArray (line 1638) | ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstA...
  function GetDouble (line 1653) | double GetDouble() const {
  function Ch (line 1681) | const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data...
  function SizeType (line 1686) | SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ...
  type Flag (line 1845) | struct Flag {
  type String (line 1856) | struct String {
  function SetLength (line 1870) | struct ShortString {
  type I (line 1882) | struct I {
  type U (line 1886) | struct U {
  type I (line 1891) | struct I {
  type U (line 1895) | struct U {
  type ObjectData (line 1905) | struct ObjectData {
  type ArrayData (line 1911) | struct ArrayData {
  function RAPIDJSON_FORCEINLINE (line 1926) | RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJ...
  function RAPIDJSON_FORCEINLINE (line 1927) | RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return...
  function RAPIDJSON_FORCEINLINE (line 1928) | RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return ...
  function RAPIDJSON_FORCEINLINE (line 1929) | RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* ele...
  function RAPIDJSON_FORCEINLINE (line 1930) | RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJS...
  function RAPIDJSON_FORCEINLINE (line 1931) | RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { retur...
  function SetArrayRaw (line 1934) | void SetArrayRaw(GenericValue* values, SizeType count, Allocator& alloca...
  function SetObjectRaw (line 1947) | void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
  function SetStringRaw (line 1960) | void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
  function SetStringRaw (line 1967) | void SetStringRaw(StringRefType s, Allocator& allocator) {
  function RawAssign (line 1984) | void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
  type GenericValue (line 2010) | typedef GenericValue<UTF8<> > Value;
  type GenericValue (line 2027) | typedef GenericValue<Encoding, Allocator> ValueType;
  type Allocator (line 2028) | typedef Allocator AllocatorType;
  type ClearStackOnExit (line 2318) | struct ClearStackOnExit {
  function Bool (line 2334) | bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b)...
  function Int (line 2335) | bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); ...
  function Uint (line 2336) | bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueTyp...
  function Int64 (line 2337) | bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueTyp...
  function Uint64 (line 2338) | bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueT...
  function Double (line 2339) | bool Double(double d) { new (stack_.template Push<ValueType>()) ValueTyp...
  function RawNumber (line 2341) | bool RawNumber(const Ch* str, SizeType length, bool copy) {
  function String (line 2349) | bool String(const Ch* str, SizeType length, bool copy) {
  function StartObject (line 2357) | bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(k...
  function Key (line 2359) | bool Key(const Ch* str, SizeType length, bool copy) { return String(str,...
  function EndObject (line 2361) | bool EndObject(SizeType memberCount) {
  function StartArray (line 2367) | bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kA...
  function EndArray (line 2369) | bool EndArray(SizeType elementCount) {
  function ClearStack (line 2381) | void ClearStack() {
  function Destroy (line 2390) | void Destroy() {
  type GenericDocument (line 2402) | typedef GenericDocument<UTF8<> > Document;
  type ValueT (line 2443) | typedef ValueT PlainType;
  type typename (line 2444) | typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
  type ValueType (line 2445) | typedef ValueType* ValueIterator;
  type ValueT (line 2446) | typedef const ValueT* ConstValueIterator;
  type typename (line 2447) | typedef typename ValueType::AllocatorType AllocatorType;
  type typename (line 2448) | typedef typename ValueType::StringRefType StringRefType;
  function value_ (line 2453) | GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
  function GenericArray (line 2464) | GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) con...
  function GenericArray (line 2465) | GenericArray PushBack(ValueType& value, AllocatorType& allocator) const ...
  function GenericArray (line 2467) | GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const...
  function GenericArray (line 2469) | GenericArray PushBack(StringRefType value, AllocatorType& allocator) con...
  function ValueIterator (line 2472) | ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(...
  function ValueIterator (line 2473) | ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) c...
  function value_ (line 2482) | value_(value) {}
  type ValueT (line 2496) | typedef ValueT PlainType;
  type typename (line 2497) | typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
  type GenericMemberIterator (line 2498) | typedef GenericMemberIterator<Const, typename
  type GenericMemberIterator (line 2499) | typedef GenericMemberIterator<true, typename
  type typename (line 2500) | typedef typename ValueType::AllocatorType AllocatorType;
  type typename (line 2501) | typedef typename ValueType::StringRefType StringRefType;
  type typename (line 2502) | typedef typename ValueType::EncodingType EncodingType;
  type typename (line 2503) | typedef typename ValueType::Ch Ch;
  function value_ (line 2508) | GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
  function HasMember (line 2521) | bool HasMember(const Ch* name) const { return value_.HasMember(name); }
  function MemberIterator (line 2526) | MemberIterator FindMember(const Ch* name) const { return value_.FindMemb...
  function GenericObject (line 2534) | GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, A...
  function GenericObject (line 2538) | GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorTy...
  function GenericObject (line 2539) | GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorTyp...
  function GenericObject (line 2540) | GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorTyp...
  function GenericObject (line 2541) | GenericObject AddMember(StringRefType name, ValueType&& value, Allocator...
  function GenericObject (line 2543) | GenericObject AddMember(StringRefType name, ValueType& value, AllocatorT...
  function GenericObject (line 2544) | GenericObject AddMember(StringRefType name, StringRefType value, Allocat...
  function RemoveMember (line 2547) | bool RemoveMember(const Ch* name) const { return value_.RemoveMember(nam...
  function value_ (line 2568) | value_(value) {}

FILE: src/thirdparty/rapidjson/encodedstream.h
  function RAPIDJSON_DIAG_PUSH (line 27) | RAPIDJSON_DIAG_PUSH
  function Ch (line 49) | Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
  function Put (line 53) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 54) | void Flush() { RAPIDJSON_ASSERT(false); }
  function Ch (line 55) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function PutEnd (line 56) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  function is_ (line 72) | is_(is) {
  function Ch (line 78) | Ch Take() { return is_.Take(); }
  function Put (line 82) | void Put(Ch) {}
  function Flush (line 83) | void Flush() {}
  function Ch (line 84) | Ch* PutBegin() { return 0; }
  function PutEnd (line 85) | size_t PutEnd(Ch*) { return 0; }
  function os_ (line 105) | os_(os) {
  function Put (line 110) | void Put(Ch c) { Encoding::Put(os_, c);  }
  function Flush (line 111) | void Flush() { os_.Flush(); }
  function Ch (line 115) | Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
  function Tell (line 116) | size_t Tell() const { RAPIDJSON_ASSERT(false);  return 0; }
  function Ch (line 157) | Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
  function Put (line 161) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 162) | void Flush() { RAPIDJSON_ASSERT(false); }
  function Ch (line 163) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function PutEnd (line 164) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  function DetectType (line 171) | void DetectType() {
  type Ch (line 219) | typedef Ch (*TakeFunc)(InputByteStream& is);
  function Put (line 260) | void Put(Ch c) { putFunc_(*os_, c); }
  function Flush (line 261) | void Flush() { os_->Flush(); }
  function Ch (line 265) | Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
  function Tell (line 266) | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
  function PutBOM (line 274) | void PutBOM() {

FILE: src/thirdparty/rapidjson/encodings.h
  function RAPIDJSON_DIAG_PUSH (line 25) | RAPIDJSON_DIAG_PUSH
  function else (line 131) | else if (codepoint <= 0xFFFF) {
  function GetRange (line 204) | static unsigned char GetRange(unsigned char c) {
  type CharType (line 270) | typedef CharType Ch;
  type CharType (line 419) | typedef CharType Ch;
  type CharType (line 543) | typedef CharType Ch;
  type UTFType (line 603) | enum UTFType {
  type CharType (line 616) | typedef CharType Ch;
  function Encode (line 623) | static void Encode(OutputStream& os, unsigned codepoint) {
  function EncodeUnsafe (line 630) | static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
  function Decode (line 637) | static bool Decode(InputStream& is, unsigned* codepoint) {
  function Validate (line 644) | static bool Validate(InputStream& is, OutputStream& os) {
  function Transcode (line 661) | static bool Transcode(InputStream& is, OutputStream& os) {
  function TranscodeUnsafe (line 670) | static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
  function Validate (line 680) | static bool Validate(InputStream& is, OutputStream& os) {
  function Transcode (line 693) | static bool Transcode(InputStream& is, OutputStream& os) {
  function TranscodeUnsafe (line 699) | static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
  function Validate (line 705) | static bool Validate(InputStream& is, OutputStream& os) {

FILE: src/thirdparty/rapidjson/error/en.h
  function RAPIDJSON_DIAG_PUSH (line 21) | RAPIDJSON_DIAG_PUSH

FILE: src/thirdparty/rapidjson/error/error.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 55) | RAPIDJSON_NAMESPACE_BEGIN
  type RAPIDJSON_ERROR_CHARTYPE (line 147) | typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorC...

FILE: src/thirdparty/rapidjson/filereadstream.h
  function RAPIDJSON_DIAG_PUSH (line 22) | RAPIDJSON_DIAG_PUSH
  function Ch (line 51) | Ch Take() { Ch c = *current_; Read(); return c; }
  function Put (line 55) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 56) | void Flush() { RAPIDJSON_ASSERT(false); }
  function Ch (line 57) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function PutEnd (line 58) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  function Ch (line 61) | const Ch* Peek4() const {

FILE: src/thirdparty/rapidjson/filewritestream.h
  function RAPIDJSON_DIAG_PUSH (line 22) | RAPIDJSON_DIAG_PUSH
  function Put (line 40) | void Put(char c) {
  function PutN (line 47) | void PutN(char c, size_t n) {
  function Flush (line 63) | void Flush() {
  function Peek (line 75) | char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
  function Tell (line 77) | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
  function PutEnd (line 79) | size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
  function PutN (line 94) | inline void PutN(FileWriteStream& stream, char c, size_t n) {

FILE: src/thirdparty/rapidjson/fwd.h
  type GenericStringStream (line 49) | typedef GenericStringStream<UTF8<char> > StringStream;
  type GenericInsituStringStream (line 54) | typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
  type GenericStringBuffer (line 61) | typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
  type GenericMemoryBuffer (line 76) | typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
  type MemoryStream (line 80) | struct MemoryStream
  type GenericReader (line 90) | typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
  type GenericValue (line 116) | typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
  type GenericDocument (line 121) | typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, C...
  type GenericPointer (line 128) | typedef GenericPointer<Value, CrtAllocator> Pointer;
  type GenericSchemaDocument (line 138) | typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
  type IGenericRemoteSchemaDocumentProvider (line 139) | typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSche...
  type GenericSchemaValidator (line 147) | typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<ch...

FILE: src/thirdparty/rapidjson/internal/biginteger.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 25) | RAPIDJSON_NAMESPACE_BEGIN
  function Difference (line 186) | bool Difference(const BigInteger& rhs, BigInteger* out) const {
  function Compare (line 208) | int Compare(const BigInteger& rhs) const {
  function Type (line 220) | Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); re...
  function PushBack (line 234) | void PushBack(Type digit) {
  function ParseUint64 (line 239) | static uint64_t ParseUint64(const char* begin, const char* end) {
  function MulAdd64 (line 249) | static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* o...

FILE: src/thirdparty/rapidjson/internal/diyfp.h
  function RAPIDJSON_DIAG_PUSH (line 39) | RAPIDJSON_DIAG_PUSH
  type uint128 (line 78) | __extension__ typedef unsigned __int128 uint128;
  function DiyFp (line 119) | DiyFp NormalizeBoundary() const {
  function NormalizedBoundaries (line 130) | void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
  function ToDouble (line 139) | double ToDouble() const {
  function DiyFp (line 226) | inline DiyFp GetCachedPower(int e, int* K) {
  function DiyFp (line 240) | inline DiyFp GetCachedPower10(int exp, int *outExp) {

FILE: src/thirdparty/rapidjson/internal/dtoa.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 26) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/ieee754.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 20) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/itoa.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 20) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/meta.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 34) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/pow10.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 20) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/regex.h
  function RAPIDJSON_DIAG_PUSH (line 23) | RAPIDJSON_DIAG_PUSH
  function AddState (line 646) | bool AddState(Stack<Allocator>& l, SizeType index) const {
  function MatchRange (line 661) | bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
  type GenericRegex (line 688) | typedef GenericRegex<UTF8<> > Regex;

FILE: src/thirdparty/rapidjson/internal/stack.h
  function RAPIDJSON_DIAG_PUSH (line 22) | RAPIDJSON_DIAG_PUSH
  function T (line 149) | T* Top() const {
  function T (line 158) | T* End() const { return reinterpret_cast<T*>(stackTop_); }
  function T (line 164) | T* Bottom() const { return reinterpret_cast<T*>(stack_); }
  function Resize (line 199) | void Resize(size_t newCapacity) {
  function Destroy (line 206) | void Destroy() {

FILE: src/thirdparty/rapidjson/internal/strfunc.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 20) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/strtod.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 23) | RAPIDJSON_NAMESPACE_BEGIN

FILE: src/thirdparty/rapidjson/internal/swap.h
  function RAPIDJSON_DIAG_PUSH (line 21) | RAPIDJSON_DIAG_PUSH

FILE: src/thirdparty/rapidjson/istreamwrapper.h
  function Ch (line 60) | Ch Take() {
  function Ch (line 73) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function Put (line 74) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 75) | void Flush() { RAPIDJSON_ASSERT(false); }
  function PutEnd (line 76) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  function Ch (line 79) | const Ch* Peek4() const {
  type BasicIStreamWrapper (line 106) | typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
  type BasicIStreamWrapper (line 107) | typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;

FILE: src/thirdparty/rapidjson/memorystream.h
  function RAPIDJSON_DIAG_PUSH (line 21) | RAPIDJSON_DIAG_PUSH
  function Ch (line 46) | Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
  function Ch (line 49) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function Put (line 50) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 51) | void Flush() { RAPIDJSON_ASSERT(false); }
  function PutEnd (line 52) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  function Ch (line 55) | const Ch* Peek4() const {

FILE: src/thirdparty/rapidjson/msinttypes/inttypes.h
  type imaxdiv_t (line 57) | typedef struct {
  function _inline (line 286) | static

FILE: src/thirdparty/rapidjson/msinttypes/stdint.h
  type int_least8_t (line 140) | typedef int8_t    int_least8_t;
  type int_least16_t (line 141) | typedef int16_t   int_least16_t;
  type int_least32_t (line 142) | typedef int32_t   int_least32_t;
  type int_least64_t (line 143) | typedef int64_t   int_least64_t;
  type uint_least8_t (line 144) | typedef uint8_t   uint_least8_t;
  type uint_least16_t (line 145) | typedef uint16_t  uint_least16_t;
  type uint_least32_t (line 146) | typedef uint32_t  uint_least32_t;
  type uint_least64_t (line 147) | typedef uint64_t  uint_least64_t;
  type int_fast8_t (line 150) | typedef int8_t    int_fast8_t;
  type int_fast16_t (line 151) | typedef int16_t   int_fast16_t;
  type int_fast32_t (line 152) | typedef int32_t   int_fast32_t;
  type int_fast64_t (line 153) | typedef int64_t   int_fast64_t;
  type uint_fast8_t (line 154) | typedef uint8_t   uint_fast8_t;
  type uint_fast16_t (line 155) | typedef uint16_t  uint_fast16_t;
  type uint_fast32_t (line 156) | typedef uint32_t  uint_fast32_t;
  type uint_fast64_t (line 157) | typedef uint64_t  uint_fast64_t;
  type intmax_t (line 169) | typedef int64_t   intmax_t;
  type uintmax_t (line 170) | typedef uint64_t  uintmax_t;

FILE: src/thirdparty/rapidjson/ostreamwrapper.h
  function RAPIDJSON_DIAG_PUSH (line 22) | RAPIDJSON_DIAG_PUSH
  function Put (line 50) | void Put(Ch c) {
  function Flush (line 54) | void Flush() {
  function Peek (line 59) | char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
  function Tell (line 61) | size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
  function PutEnd (line 63) | size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
  type BasicOStreamWrapper (line 72) | typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
  type BasicOStreamWrapper (line 73) | typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;

FILE: src/thirdparty/rapidjson/pointer.h
  function RAPIDJSON_DIAG_PUSH (line 27) | RAPIDJSON_DIAG_PUSH
  function Token (line 320) | const Token* GetTokens() const { return tokens_; }
  function Erase (line 711) | bool Erase(ValueType& root) const {
  function NeedPercentEncode (line 790) | bool NeedPercentEncode(Ch c) const {
  function Parse (line 802) | void Parse(const Ch* source, size_t length) {
  function class (line 980) | class PercentDecodeStream {
  function Put (line 1028) | void Put(char c) { // UTF-8 must be byte
  type GenericPointer (line 1049) | typedef GenericPointer<Value> Pointer;

FILE: src/thirdparty/rapidjson/prettywriter.h
  function Null (line 85) | bool Null()                 { PrettyPrefix(kNullType);   return Base::Wr...
  function Bool (line 86) | bool Bool(bool b)           { PrettyPrefix(b ? kTrueType : kFalseType); ...
  function Int (line 87) | bool Int(int i)             { PrettyPrefix(kNumberType); return Base::Wr...
  function Uint (line 88) | bool Uint(unsigned u)       { PrettyPrefix(kNumberType); return Base::Wr...
  function Int64 (line 89) | bool Int64(int64_t i64)     { PrettyPrefix(kNumberType); return Base::Wr...
  function Uint64 (line 90) | bool Uint64(uint64_t u64)   { PrettyPrefix(kNumberType); return Base::Wr...
  function Double (line 91) | bool Double(double d)       { PrettyPrefix(kNumberType); return Base::Wr...
  function StartObject (line 111) | bool StartObject() {
  function StartArray (line 143) | bool StartArray() {
  function String (line 173) | bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
  function Key (line 174) | bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
  function RawValue (line 187) | bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(t...
  function WriteIndent (line 234) | void WriteIndent()  {

FILE: src/thirdparty/rapidjson/rapidjson.h
  type STATIC_ASSERTION_FAILURE (line 415) | struct STATIC_ASSERTION_FAILURE

FILE: src/thirdparty/rapidjson/reader.h
  function RAPIDJSON_DIAG_PUSH (line 39) | RAPIDJSON_DIAG_PUSH
  type ParseFlag (line 145) | enum ParseFlag {
  type typename (line 197) | typedef typename Encoding::Ch Ch;
  type typename (line 199) | typedef typename internal::SelectIf<internal::IsSame<Derived, void>, Bas...
  function Default (line 201) | bool Default() { return true; }
  function Null (line 202) | bool Null() { return static_cast<Override&>(*this).Default(); }
  function Bool (line 203) | bool Bool(bool) { return static_cast<Override&>(*this).Default(); }
  function Int (line 204) | bool Int(int) { return static_cast<Override&>(*this).Default(); }
  function Uint (line 205) | bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); }
  function Int64 (line 206) | bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
  function Uint64 (line 207) | bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
  function Double (line 208) | bool Double(double) { return static_cast<Override&>(*this).Default(); }
  function RawNumber (line 210) | bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_c...
  function String (line 211) | bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*...
  function StartObject (line 212) | bool StartObject() { return static_cast<Override&>(*this).Default(); }
  function Key (line 213) | bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Ov...
  function EndObject (line 214) | bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(...
  function StartArray (line 215) | bool StartArray() { return static_cast<Override&>(*this).Default(); }
  function EndArray (line 216) | bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
  function namespace (line 222) | namespace internal {
  function SkipWhitespace (line 432) | inline void SkipWhitespace(InsituStringStream& is) {
  function SkipWhitespace (line 437) | inline void SkipWhitespace(StringStream& is) {
  function SkipWhitespace (line 441) | inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) {
  function ClearStack (line 547) | void ClearStack() { stack_.Clear(); }
  type ClearStackOnExit (line 550) | struct ClearStackOnExit {
  function Consume (line 744) | bool Consume(InputStream& is, typename InputStream::Ch expect) {
  function RAPIDJSON_FORCEINLINE (line 782) | RAPIDJSON_FORCEINLINE void Put(Ch c) {
  function RAPIDJSON_FORCEINLINE (line 787) | RAPIDJSON_FORCEINLINE void* Push(SizeType count) {
  function Ch (line 794) | Ch* Pop() {
  type typename (line 1075) | typedef typename InputStream::Ch Ch;
  function is (line 1077) | is(s) { (void)reader;  }
  function RAPIDJSON_FORCEINLINE (line 1081) | RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
  function RAPIDJSON_FORCEINLINE (line 1082) | RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
  function RAPIDJSON_FORCEINLINE (line 1083) | RAPIDJSON_FORCEINLINE void Push(char) {}
  function Tell (line 1085) | size_t Tell() { return is.Tell(); }
  function Length (line 1086) | size_t Length() { return 0; }
  function RAPIDJSON_FORCEINLINE (line 1102) | RAPIDJSON_FORCEINLINE Ch TakePush() {
  function RAPIDJSON_FORCEINLINE (line 1107) | RAPIDJSON_FORCEINLINE void Push(char c) {
  function Length (line 1111) | size_t Length() { return stackStream.Length(); }
  function RAPIDJSON_FORCEINLINE (line 1129) | RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
  type IterativeParsingState (line 1410) | enum IterativeParsingState {
  type Token (line 1436) | enum Token {
  function RAPIDJSON_FORCEINLINE (line 1455) | RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
  function RAPIDJSON_FORCEINLINE (line 1482) | RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingStat...
  function IterativeParsingState (line 1649) | IterativeParsingState Transit(IterativeParsingState src, Token token, It...
  type GenericReader (line 1862) | typedef GenericReader<UTF8<>, UTF8<> > Reader;

FILE: src/thirdparty/rapidjson/schema.h
  function namespace (line 78) | namespace internal {
  function namespace (line 130) | namespace internal {
  type typename (line 341) | typedef typename SchemaDocumentType::AllocatorType AllocatorType;
  type typename (line 342) | typedef typename SchemaDocumentType::PointerType PointerType;
  type typename (line 343) | typedef typename ValueType::EncodingType EncodingType;
  type typename (line 344) | typedef typename EncodingType::Ch Ch;
  type SchemaValidationContext (line 345) | typedef SchemaValidationContext<SchemaDocumentType> Context;
  type Schema (line 346) | typedef Schema<SchemaDocumentType> SchemaType;
  type GenericValue (line 347) | typedef GenericValue<EncodingType, AllocatorType> SValue;
  type typename (line 383) | typedef typename ValueType::ConstValueIterator ConstValueIterator;
  type typename (line 384) | typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
  type Hasher (line 402) | typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
  function BeginValue (line 600) | bool BeginValue(Context& context) const {
  function Null (line 692) | bool Null(Context& context) const {
  function Bool (line 698) | bool Bool(Context& context, bool) const {
  function Int (line 704) | bool Int(Context& context, int i) const {
  function Uint (line 710) | bool Uint(Context& context, unsigned u) const {
  function Int64 (line 716) | bool Int64(Context& context, int64_t i) const {
  function Uint64 (line 722) | bool Uint64(Context& context, uint64_t u) const {
  function Double (line 728) | bool Double(Context& context, double d) const {
  function String (line 744) | bool String(Context& context, const Ch* str, SizeType length, bool) const {
  function StartObject (line 764) | bool StartObject(Context& context) const {
  function Key (line 783) | bool Key(Context& context, const Ch* str, SizeType len, bool) const {
  function EndObject (line 828) | bool EndObject(Context& context, SizeType memberCount) const {
  function StartArray (line 858) | bool StartArray(Context& context) const {
  function EndArray (line 868) | bool EndArray(Context& context, SizeType elementCount) const {
  type SchemaValueType (line 925) | enum SchemaValueType {
  type internal (line 937) | typedef internal::GenericRegex<EncodingType> RegexType;
  type std (line 939) | typedef std::basic_regex<Ch> RegexType;
  type RegexType (line 941) | typedef char RegexType;
  type SchemaArray (line 944) | struct SchemaArray {
  function SchemaType (line 952) | static const SchemaType* GetTypeless() {
  function ValueType (line 966) | static const ValueType* GetMember(const ValueType& value, const ValueTyp...
  function AssignIfExist (line 971) | static void AssignIfExist(bool& out, const ValueType& value, const Value...
  function AssignIfExist (line 977) | static void AssignIfExist(SizeType& out, const ValueType& value, const V...
  function AssignIfExist (line 983) | void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument,...
  function IsPatternMatch (line 1013) | static bool IsPatternMatch(const RegexType* pattern, const Ch *str, Size...
  function IsPatternMatch (line 1028) | static bool IsPatternMatch(const RegexType* pattern, const Ch *str, Size...
  function FindPropertyIndex (line 1086) | bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
  function CheckInt (line 1099) | bool CheckInt(Context& context, int64_t i) const {
  function CheckUint (line 1138) | bool CheckUint(Context& context, uint64_t i) const {
  function CheckDoubleMinimum (line 1176) | bool CheckDoubleMinimum(Context& context, double d) const {
  function CheckDoubleMaximum (line 1182) | bool CheckDoubleMaximum(Context& context, double d) const {
  function CheckDoubleMultipleOf (line 1188) | bool CheckDoubleMultipleOf(Context& context, double d) const {
  type Property (line 1197) | struct Property {
  type PatternProperty (line 1208) | struct PatternProperty {
  function virtual (line 1303) | virtual ~IGenericRemoteSchemaDocumentProvider() {}
  type IGenericRemoteSchemaDocumentProvider (line 1323) | typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRem...
  type Allocator (line 1324) | typedef Allocator AllocatorType;
  type typename (line 1325) | typedef typename ValueType::EncodingType EncodingType;
  type typename (line 1326) | typedef typename EncodingType::Ch Ch;
  type internal (line 1327) | typedef internal::Schema<GenericSchemaDocument> SchemaType;
  type GenericPointer (line 1328) | typedef GenericPointer<ValueType, Allocator> PointerType;
  type SchemaRefEntry (line 1409) | struct SchemaRefEntry {
  function CreateSchemaRecursive (line 1429) | void CreateSchemaRecursive(const SchemaType** schema, const PointerType&...
  function CreateSchema (line 1446) | void CreateSchema(const SchemaType** schema, const PointerType& pointer,...
  function HandleRefSchema (line 1458) | bool HandleRefSchema(const PointerType& source, const SchemaType** schem...
  function SchemaType (line 1504) | const SchemaType* GetSchema(const PointerType& pointer) const {
  function PointerType (line 1511) | PointerType GetPointer(const SchemaType* schema) const {
  type GenericSchemaDocument (line 1530) | typedef GenericSchemaDocument<Value> SchemaDocument;
  type IGenericRemoteSchemaDocumentProvider (line 1532) | typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSche...
  type typename (line 1559) | typedef typename SchemaDocumentType::PointerType PointerType;
  type typename (line 1560) | typedef typename SchemaType::EncodingType EncodingType;
  type typename (line 1561) | typedef typename EncodingType::Ch Ch;
  function Reset (line 1625) | void Reset() {
  function Ch (line 1642) | const Ch* GetInvalidSchemaKeyword() const {
  function Null (line 1689) | bool Null()             { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null,   (Curren...
  function Bool (line 1690) | bool Bool(bool b)       { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool,   (Curren...
  function Int (line 1691) | bool Int(int i)         { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int,    (Curren...
  function Uint (line 1692) | bool Uint(unsigned u)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint,   (Curren...
  function Int64 (line 1693) | bool Int64(int64_t i)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64,  (Curren...
  function Uint64 (line 1694) | bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (Curren...
  function Double (line 1695) | bool Double(double d)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (Curren...
  function RawNumber (line 1696) | bool RawNumber(const Ch* str, SizeType length, bool copy)
  function String (line 1698) | bool String(const Ch* str, SizeType length, bool copy)
  function StartObject (line 1701) | bool StartObject() {
  function Key (line 1707) | bool Key(const Ch* str, SizeType len, bool copy) {
  function EndObject (line 1715) | bool EndObject(SizeType memberCount) {
  function StartArray (line 1722) | bool StartArray() {
  function EndArray (line 1728) | bool EndArray(SizeType elementCount) {
  function virtual (line 1741) | virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
  function virtual (line 1749) | virtual void DestroySchemaValidator(ISchemaValidator* validator) {
  function virtual (line 1755) | virtual void* CreateHasher() {
  function virtual (line 1759) | virtual uint64_t GetHashCode(void* hasher) {
  function virtual (line 1763) | virtual void DestroryHasher(void* hasher) {
  function virtual (line 1769) | virtual void* MallocState(size_t size) {
  function virtual (line 1773) | virtual void FreeState(void* p) {
  type GenericValue (line 1779) | typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
  type internal (line 1780) | typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
  function BeginValue (line 1812) | bool BeginValue() {
  function EndValue (line 1843) | bool EndValue() {
  function AppendToken (line 1880) | void AppendToken(const Ch* str, SizeType len) {
  function RAPIDJSON_FORCEINLINE (line 1897) | RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (s...
  function RAPIDJSON_FORCEINLINE (line 1899) | RAPIDJSON_FORCEINLINE void PopSchema() {
  function OutputHandler (line 1912) | static OutputHandler& GetNullHandler() {
  type GenericSchemaValidator (line 1932) | typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
  type typename (line 1956) | typedef typename InputStream::Ch Ch;
  function Ch (line 1989) | const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }

FILE: src/thirdparty/rapidjson/stream.h
  function RAPIDJSON_NAMESPACE_BEGIN (line 22) | RAPIDJSON_NAMESPACE_BEGIN
  type typename (line 111) | typedef typename Encoding::Ch Ch;
  function Ch (line 116) | Ch Take() { return *src_++; }
  function Ch (line 119) | Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
  function Put (line 120) | void Put(Ch) { RAPIDJSON_ASSERT(false); }
  function Flush (line 121) | void Flush() { RAPIDJSON_ASSERT(false); }
  function PutEnd (line 122) | size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
  type GenericStringStream (line 134) | typedef GenericStringStream<UTF8<> > StringStream;
  type typename (line 145) | typedef typename Encoding::Ch Ch;
  function Ch (line 150) | Ch Peek() { return *src_; }
  function Ch (line 151) | Ch Take() { return *src_++; }
  function Tell (line 152) | size_t Tell() { return static_cast<size_t>(src_ - head_); }
  function Put (line 155) | void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
  function Ch (line 157) | Ch* PutBegin() { return dst_ = src_; }
  function PutEnd (line 158) | size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
  function Flush (line 159) | void Flush() {}
  function Ch (line 161) | Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
  function Pop (line 162) | void Pop(size_t count) { dst_ -= count; }
  type GenericInsituStringStream (line 175) | typedef GenericInsituStringStream<UTF8<> > InsituStringStream;

FILE: src/thirdparty/rapidjson/writer.h
  type WriteFlag (line 63) | enum WriteFlag {
  function SetMaxDecimalPlaces (line 163) | void SetMaxDecimalPlaces(int maxDecimalPlaces) {
  function Null (line 172) | bool Null()                 { Prefix(kNullType);   return EndValue(Write...
  function Bool (line 173) | bool Bool(bool b)           { Prefix(b ? kTrueType : kFalseType); return...
  function Int (line 174) | bool Int(int i)             { Prefix(kNumberType); return EndValue(Write...
  function Uint (line 175) | bool Uint(unsigned u)       { Prefix(kNumberType); return EndValue(Write...
  function Int64 (line 176) | bool Int64(int64_t i64)     { Prefix(kNumberType); return EndValue(Write...
  function Uint64 (line 177) | bool Uint64(uint64_t u64)   { Prefix(kNumberType); return EndValue(Write...
  function Double (line 184) | bool Double(double d)       { Prefix(kNumberType); return EndValue(Write...
  function StartObject (line 204) | bool StartObject() {
  function StartArray (line 220) | bool StartArray() {
  function String (line 239) | bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
  function Key (line 240) | bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
  function RawValue (line 252) | bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); ...
  type Level (line 256) | struct Level {
  function WriteNull (line 264) | bool WriteNull()  {
  function WriteBool (line 269) | bool WriteBool(bool b)  {
  function WriteInt (line 281) | bool WriteInt(int i) {
  function WriteUint (line 290) | bool WriteUint(unsigned u) {
  function WriteInt64 (line 299) | bool WriteInt64(int64_t i64) {
  function WriteUint64 (line 308) | bool WriteUint64(uint64_t u64) {
  function WriteDouble (line 317) | bool WriteDouble(double d) {
  function WriteString (line 345) | bool WriteString(const Ch* str, SizeType length)  {
  function ScanWriteUnescapedString (line 419) | bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, s...
  function WriteStartObject (line 423) | bool WriteStartObject() { os_->Put('{'); return true; }
  function WriteEndObject (line 424) | bool WriteEndObject()   { os_->Put('}'); return true; }
  function WriteStartArray (line 425) | bool WriteStartArray()  { os_->Put('['); return true; }
  function WriteEndArray (line 426) | bool WriteEndArray()    { os_->Put(']'); return true; }
  function WriteRawValue (line 428) | bool WriteRawValue(const Ch* json, size_t length) {
  function Prefix (line 437) | void Prefix(Type type) {
  function EndValue (line 458) | bool EndValue(bool ret) {
  function WriteInt (line 478) | bool Writer<StringBuffer>::WriteInt(int i) {
  function WriteUint (line 486) | inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
  function WriteInt64 (line 494) | inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
  function WriteUint64 (line 502) | inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
  function WriteDouble (line 510) | inline bool Writer<StringBuffer>::WriteDouble(double d) {

FILE: src/thread_data_base.h
  function class (line 23) | class ThreadDataBase {

FILE: src/token_manager.cpp
  type dmkit (line 19) | namespace dmkit {
    function ClientKeyMap (line 101) | ClientKeyMap* TokenManager::load_client_key_map() {

FILE: src/token_manager.h
  function namespace (line 25) | namespace dmkit {

FILE: src/user_function/demo.cpp
  type dmkit (line 18) | namespace dmkit {
    type user_function (line 19) | namespace user_function {
      type demo (line 20) | namespace demo {
        function get_cellular_data_usage (line 26) | int get_cellular_data_usage(const std::vector<std::string>& args,
        function get_cellular_data_left (line 44) | int get_cellular_data_left(const std::vector<std::string>& args,
        function get_package_options (line 62) | int get_package_options(const std::vector<std::string>& args,

FILE: src/user_function/demo.h
  function namespace (line 22) | namespace dmkit {

FILE: src/user_function/shared.cpp
  type dmkit (line 28) | namespace dmkit {
    type user_function (line 29) | namespace user_function {
      function json_get_value (line 43) | int json_get_value(const std::vector<std::string>& args,
      function replace (line 136) | int replace(const std::vector<std::string>& args,
      function split_and_choose (line 158) | int split_and_choose(const std::vector<std::string>& args,
      function number_add (line 190) | int number_add(const std::vector<std::string>& args,
      function float_mul (line 214) | int float_mul(const std::vector<std::string>& args,
      function choose_if_equal (line 240) | int choose_if_equal(const std::vector<std::string>& args,
      function url_encode (line 257) | int url_encode(const std::vector<std::string>& args,
      function service_http_get (line 289) | int service_http_get(const std::vector<std::string>& args,
      function service_http_post (line 315) | int service_http_post(const std::vector<std::string>& args,
      function now_strftime (line 348) | int now_strftime(const std::vector<std::string>& args,

FILE: src/user_function/shared.h
  function namespace (line 22) | namespace dmkit {

FILE: src/user_function_manager.cpp
  type dmkit (line 21) | namespace dmkit {

FILE: src/user_function_manager.h
  function namespace (line 23) | namespace dmkit {

FILE: src/utils.h
  function namespace (line 23) | namespace dmkit {

FILE: tools/bot_emulator.py
  function main (line 30) | def main(bot_id, token):

FILE: tools/mock_api_server.py
  function hotel_search (line 28) | def hotel_search():
  function hotel_book (line 33) | def hotel_book():
Condensed preview — 97 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (922K chars).
[
  {
    "path": ".gitignore",
    "chars": 64,
    "preview": "/output\n/Makefile\n/brpc\n/_build\n/build\n/.vscode\n*.pyc\n.DS_Store\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 3819,
    "preview": "cmake_minimum_required(VERSION 2.8.10)\nproject(dmkit C CXX)\n\nexecute_process(\n    COMMAND bash -c \"find ${CMAKE_SOURCE_D"
  },
  {
    "path": "Dockerfile",
    "chars": 303,
    "preview": "FROM ubuntu:18.04\n\nWORKDIR /unit-dmkit\n\nCOPY . /unit-dmkit\n\nRUN apt-get update && apt-get install -y --no-install-recomm"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "NOTICE",
    "chars": 601,
    "preview": "Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n"
  },
  {
    "path": "README.md",
    "chars": 1278,
    "preview": "# DMKit\n\nDMKit作为UNIT的开源对话管理模块,可以无缝对接UNIT的理解能力,并赋予开发者多状态的复杂对话流程管理能力,还可以低成本对接外部知识库,迅速丰富话术信息量。\n\n## 快速开始\n\n### 编译DMKit\n\nDMKit"
  },
  {
    "path": "conf/app/bot_tokens.json",
    "chars": 74,
    "preview": "{\n    \"1234\": {\n        \"api_key\": \"\",\n        \"secret_key\": \"\"  \n    }\n}\n"
  },
  {
    "path": "conf/app/demo/book_hotel.json",
    "chars": 5841,
    "preview": "[\n  {\n    \"output\": [\n      {\n        \"assertion\": [], \n        \"result\": [\n          {\n            \"type\": \"tts\", \n    "
  },
  {
    "path": "conf/app/demo/book_hotel.xml",
    "chars": 7090,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mxGraphModel dx=\"1640\" dy=\"390\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" c"
  },
  {
    "path": "conf/app/demo/cellular_data.json",
    "chars": 8590,
    "preview": "[\n  {\n    \"output\": [\n      {\n        \"assertion\": [], \n        \"result\": [\n          {\n            \"type\": \"tts\", \n    "
  },
  {
    "path": "conf/app/demo/cellular_data.xml",
    "chars": 15448,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mxGraphModel dx=\"790\" dy=\"376\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" co"
  },
  {
    "path": "conf/app/demo/quota_adjust.json",
    "chars": 9413,
    "preview": "[\n  {\n    \"output\": [\n      {\n        \"assertion\": [], \n        \"result\": [\n          {\n            \"type\": \"tts\", \n    "
  },
  {
    "path": "conf/app/demo/quota_adjust.xml",
    "chars": 14721,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<mxGraphModel dx=\"919\" dy=\"541\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" co"
  },
  {
    "path": "conf/app/products.json",
    "chars": 363,
    "preview": "{\n    \"default\": {\n        \"4050\": {\n            \"score\": 1,\n            \"conf_path\": \"conf/app/demo/cellular_data.json\""
  },
  {
    "path": "conf/app/remote_services.json",
    "chars": 889,
    "preview": "{\n    \"unit_bot\": {\n        \"naming_service_url\": \"https://aip.baidubce.com\",\n        \"load_balancer_name\": \"\",\n        "
  },
  {
    "path": "conf/gflags.conf",
    "chars": 347,
    "preview": "# TCP Port of this server\n--port=8010\n\n# Only allow builtin services at this port\n--internal_port=8011\n\n# Connection wil"
  },
  {
    "path": "deps.sh",
    "chars": 1379,
    "preview": "#!/usr/bin/env bash\n\ncd \"$(dirname \"$0\")\"\nJOBS=8\nOS=$1\ncase \"$OS\" in\nmac)\n    ;;\nubuntu)\n    ;;\ncentos)\n    ;;\nnone)\n   "
  },
  {
    "path": "docs/demo_book_hotel_pattern.txt",
    "chars": 318,
    "preview": "INTENT_BOOK_HOTEL\t0.8\t[D:user_hotel]#@##0#@##1\t1\nINTENT_YES\t0.7\t[D:kw_yes]#@##0#@##1\t1\nINTENT_NO\t0.7\t[D:kw_no]#@##0#@##1"
  },
  {
    "path": "docs/demo_cellular_data_pattern.txt",
    "chars": 590,
    "preview": "INTENT_CONTINUE\t0.8\t[D:kw_continue]#@##0#@##1\t1\nINTENT_WAIT\t0.8\t[D:kw_wait]#@##0#@##1\t1\nINTENT_CHECK_DATA_USAGE\t0.8\t[D:u"
  },
  {
    "path": "docs/demo_quota_adjust_pattern.txt",
    "chars": 239,
    "preview": "INTENT_NO\t0.9\t[D:kw_no]#@##0#@##1\t1\nINTENT_YES\t0.9\t[D:kw_yes]#@##0#@##1\t1\nINTENT_ADJUST_QUOTA\t0.9\t[D:kw_adjust]#@##0#@##"
  },
  {
    "path": "docs/demo_skills.md",
    "chars": 4045,
    "preview": "# DMKit示例场景\n\n## 查询流量及续订\n\n该场景实现一个简单的手机流量查询及续订流量包的技能。\n\n### 查询流量及续订 - UNIT平台配置\n\n一共分为以下几个步骤:\n\n* 创建技能\n* 配置意图\n* 配置词槽\n* 添加词槽的词典"
  },
  {
    "path": "docs/faq.md",
    "chars": 1902,
    "preview": "# DMKit 常见问题\n\n## 编译brpc失败\n\n参考BRPC官方文档 <https://github.com/brpc/brpc/>,检查是否已安装所需依赖库。建议使用系统Ubuntu 16.04或者CentOS 7。更多BRPC相关"
  },
  {
    "path": "docs/tutorial.md",
    "chars": 7460,
    "preview": "# DMKit 快速上手\n\n## 简介\n\n在任务型对话系统(Task-Oriented Dialogue System)中,一般包括了以下几个模块:\n\n* Automatic Speech Recognition(ASR),即语音识别模块,"
  },
  {
    "path": "docs/visual_tool.md",
    "chars": 1730,
    "preview": "# 对话流可视化配置\n\n针对状态繁多、跳转复杂的垂类,DMKit支持通过可视化编辑工具进行状态跳转流程的编辑设计,并同步转化为对话基础配置供对话管理引擎加载执行。\n\n## 基于开源可视化工具的配置样例\n\n这里的可视化编辑工具使用开源的[mx"
  },
  {
    "path": "language_compiler/compiler_xml.py",
    "chars": 15138,
    "preview": "# -*- coding: utf-8 -*-\n#\n# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n# \n# Licensed under the Apache License, "
  },
  {
    "path": "language_compiler/run.py",
    "chars": 2103,
    "preview": "# -*- coding: utf-8 -*-\n#\n# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n# \n# Licensed under the Apache License, "
  },
  {
    "path": "language_compiler/settings.cfg",
    "chars": 75,
    "preview": "[data]\ndata_path=../conf/app\n\n[compiler]\ncompile_types=xml\nexclude_file=\n\n\n"
  },
  {
    "path": "proto/http.proto",
    "chars": 193,
    "preview": "syntax=\"proto2\";\npackage dmkit;\n\noption cc_generic_services = true;\n\nmessage HttpRequest { };\nmessage HttpResponse { };\n"
  },
  {
    "path": "src/app_container.cpp",
    "chars": 3180,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/app_container.h",
    "chars": 1548,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/app_log.h",
    "chars": 1115,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/application_base.h",
    "chars": 2340,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/brpc.h",
    "chars": 1017,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/butil.h",
    "chars": 1036,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/dialog_manager.cpp",
    "chars": 19682,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/dialog_manager.h",
    "chars": 2354,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/file_watcher.cpp",
    "chars": 3787,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/file_watcher.h",
    "chars": 1912,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/policy.cpp",
    "chars": 11003,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/policy.h",
    "chars": 3183,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/policy_manager.cpp",
    "chars": 34329,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/policy_manager.h",
    "chars": 3308,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/qu_result.cpp",
    "chars": 3818,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/qu_result.h",
    "chars": 1803,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/rapidjson.h",
    "chars": 1803,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/remote_service_manager.cpp",
    "chars": 17012,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/remote_service_manager.h",
    "chars": 3776,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/request_context.cpp",
    "chars": 1766,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/request_context.h",
    "chars": 1682,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/server.cpp",
    "chars": 3937,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/thirdparty/rapidjson/allocators.h",
    "chars": 10311,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/document.h",
    "chars": 113794,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/encodedstream.h",
    "chars": 10686,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/encodings.h",
    "chars": 28553,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/error/en.h",
    "chars": 3870,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/error/error.h",
    "chars": 5824,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/filereadstream.h",
    "chars": 2988,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/filewritestream.h",
    "chars": 3139,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/fwd.h",
    "chars": 4035,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/biginteger.h",
    "chars": 9139,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/diyfp.h",
    "chars": 11512,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/dtoa.h",
    "chars": 8172,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/ieee754.h",
    "chars": 3022,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/itoa.h",
    "chars": 10306,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/meta.h",
    "chars": 6572,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/pow10.h",
    "chars": 3595,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/regex.h",
    "chars": 24825,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/stack.h",
    "chars": 7026,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/strfunc.h",
    "chars": 1897,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/strtod.h",
    "chars": 8672,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/internal/swap.h",
    "chars": 1419,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n//\n// Copyright (C) 2015 THL A"
  },
  {
    "path": "src/thirdparty/rapidjson/istreamwrapper.h",
    "chars": 3576,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/memorybuffer.h",
    "chars": 2560,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/memorystream.h",
    "chars": 2667,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/msinttypes/inttypes.h",
    "chars": 8372,
    "preview": "// ISO C9x  compliant inttypes.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) "
  },
  {
    "path": "src/thirdparty/rapidjson/msinttypes/stdint.h",
    "chars": 9386,
    "preview": "// ISO C9x  compliant stdint.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG"
  },
  {
    "path": "src/thirdparty/rapidjson/ostreamwrapper.h",
    "chars": 2331,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/pointer.h",
    "chars": 58465,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/prettywriter.h",
    "chars": 9601,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/rapidjson.h",
    "chars": 21461,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/reader.h",
    "chars": 79760,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n//\n// Copyright (C) 2015 THL A"
  },
  {
    "path": "src/thirdparty/rapidjson/schema.h",
    "chars": 80014,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available->\n// \n// Copyright (C) 2015 THL"
  },
  {
    "path": "src/thirdparty/rapidjson/stream.h",
    "chars": 5466,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/stringbuffer.h",
    "chars": 3798,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thirdparty/rapidjson/writer.h",
    "chars": 23261,
    "preview": "// Tencent is pleased to support the open source community by making RapidJSON available.\n// \n// Copyright (C) 2015 THL "
  },
  {
    "path": "src/thread_data_base.h",
    "chars": 1907,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/token_manager.cpp",
    "chars": 6844,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/token_manager.h",
    "chars": 2302,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/user_function/demo.cpp",
    "chars": 2237,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/user_function/demo.h",
    "chars": 1490,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/user_function/shared.cpp",
    "chars": 10009,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/user_function/shared.h",
    "chars": 2470,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/user_function_manager.cpp",
    "chars": 3486,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"Licens"
  },
  {
    "path": "src/user_function_manager.h",
    "chars": 1492,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "src/utils.h",
    "chars": 2390,
    "preview": "// Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "tools/bot_emulator.py",
    "chars": 3173,
    "preview": "# -*- coding: utf-8 -*-\n#\n# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n# \n# Licensed under the Apache License, "
  },
  {
    "path": "tools/mock_api_server.py",
    "chars": 1093,
    "preview": "# -*- coding: utf-8 -*-\n#\n# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.\n# \n# Licensed under the Apache License, "
  }
]

About this extraction

This page contains the full source code of the baidu/unit-dmkit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 97 files (849.3 KB), approximately 222.0k tokens, and a symbol index with 632 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!