فهرست منبع

Merge branch 'agile' of https://github.com/iapt-platform/mint into agile

visuddhinanda 2 سال پیش
والد
کامیت
1282551f1e
48فایلهای تغییر یافته به همراه10994 افزوده شده و 8 حذف شده
  1. 86 0
      dashboard/src/protocols/TulipServiceClientPb.ts
  2. 127 0
      dashboard/src/protocols/tulip_pb.d.ts
  3. 1000 0
      dashboard/src/protocols/tulip_pb.js
  4. 1 0
      deploy/roles/mint-v2/templates/v2/env.j2
  5. 1 2
      rpc/morus/morus/composer.json
  6. 36 0
      rpc/protocols/tulip.proto
  7. 24 1
      rpc/schema.sh
  8. 90 0
      rpc/sdk/cpp/tulip.grpc.pb.cc
  9. 244 0
      rpc/sdk/cpp/tulip.grpc.pb.h
  10. 1283 0
      rpc/sdk/cpp/tulip.pb.cc
  11. 1514 0
      rpc/sdk/cpp/tulip.pb.h
  12. 1159 0
      rpc/sdk/csharp/Tulip.cs
  13. 155 0
      rpc/sdk/csharp/TulipGrpc.cs
  14. 1 1
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/morus/v1/MarkdownGrpc.java
  15. 293 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchGrpc.java
  16. 1409 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchRequest.java
  17. 55 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchRequestOrBuilder.java
  18. 1935 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchResponse.java
  19. 54 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchResponseOrBuilder.java
  20. 92 0
      rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/Tulip.java
  21. 1 1
      rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/EpubGrpc.java
  22. 1 1
      rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/ExcelGrpc.java
  23. 1 1
      rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/S3Grpc.java
  24. 1 1
      rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/TexGrpc.java
  25. BIN
      rpc/sdk/php/GPBMetadata/Tulip.php
  26. 33 0
      rpc/sdk/php/Mint/Tulip/V1/SearchClient.php
  27. 124 0
      rpc/sdk/php/Mint/Tulip/V1/SearchRequest.php
  28. 88 0
      rpc/sdk/php/Mint/Tulip/V1/SearchRequest/Page.php
  29. 122 0
      rpc/sdk/php/Mint/Tulip/V1/SearchResponse.php
  30. 169 0
      rpc/sdk/php/Mint/Tulip/V1/SearchResponse/Item.php
  31. 41 0
      rpc/sdk/php/Mint/Tulip/V1/SearchStub.php
  32. 34 0
      rpc/sdk/python/tulip_pb2.py
  33. 66 0
      rpc/sdk/python/tulip_pb2_grpc.py
  34. 43 0
      rpc/sdk/ruby/tulip_pb.rb
  35. 26 0
      rpc/sdk/ruby/tulip_services_pb.rb
  36. 31 0
      rpc/tulip/Dockerfile
  37. 15 0
      rpc/tulip/build.sh
  38. 15 0
      rpc/tulip/start.sh
  39. 2 0
      rpc/tulip/tulip/.gitignore
  40. BIN
      rpc/tulip/tulip/GPBMetadata/Tulip.php
  41. 33 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchClient.php
  42. 124 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchRequest.php
  43. 88 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchRequest/Page.php
  44. 122 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchResponse.php
  45. 169 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchResponse/Item.php
  46. 41 0
      rpc/tulip/tulip/Mint/Tulip/V1/SearchStub.php
  47. 14 0
      rpc/tulip/tulip/composer.json
  48. 31 0
      rpc/tulip/tulip/server.php

+ 86 - 0
dashboard/src/protocols/TulipServiceClientPb.ts

@@ -0,0 +1,86 @@
+/**
+ * @fileoverview gRPC-Web generated client stub for mint.tulip.v1
+ * @enhanceable
+ * @public
+ */
+
+// Code generated by protoc-gen-grpc-web. DO NOT EDIT.
+// versions:
+// 	protoc-gen-grpc-web v1.4.2
+// 	protoc              v4.24.3
+// source: tulip.proto
+
+
+/* eslint-disable */
+// @ts-nocheck
+
+
+import * as grpcWeb from 'grpc-web';
+
+import * as tulip_pb from './tulip_pb';
+
+
+export class SearchClient {
+  client_: grpcWeb.AbstractClientBase;
+  hostname_: string;
+  credentials_: null | { [index: string]: string; };
+  options_: null | { [index: string]: any; };
+
+  constructor (hostname: string,
+               credentials?: null | { [index: string]: string; },
+               options?: null | { [index: string]: any; }) {
+    if (!options) options = {};
+    if (!credentials) credentials = {};
+    options['format'] = 'binary';
+
+    this.client_ = new grpcWeb.GrpcWebClientBase(options);
+    this.hostname_ = hostname.replace(/\/+$/, '');
+    this.credentials_ = credentials;
+    this.options_ = options;
+  }
+
+  methodDescriptorPali = new grpcWeb.MethodDescriptor(
+    '/mint.tulip.v1.Search/Pali',
+    grpcWeb.MethodType.UNARY,
+    tulip_pb.SearchRequest,
+    tulip_pb.SearchResponse,
+    (request: tulip_pb.SearchRequest) => {
+      return request.serializeBinary();
+    },
+    tulip_pb.SearchResponse.deserializeBinary
+  );
+
+  pali(
+    request: tulip_pb.SearchRequest,
+    metadata: grpcWeb.Metadata | null): Promise<tulip_pb.SearchResponse>;
+
+  pali(
+    request: tulip_pb.SearchRequest,
+    metadata: grpcWeb.Metadata | null,
+    callback: (err: grpcWeb.RpcError,
+               response: tulip_pb.SearchResponse) => void): grpcWeb.ClientReadableStream<tulip_pb.SearchResponse>;
+
+  pali(
+    request: tulip_pb.SearchRequest,
+    metadata: grpcWeb.Metadata | null,
+    callback?: (err: grpcWeb.RpcError,
+               response: tulip_pb.SearchResponse) => void) {
+    if (callback !== undefined) {
+      return this.client_.rpcCall(
+        this.hostname_ +
+          '/mint.tulip.v1.Search/Pali',
+        request,
+        metadata || {},
+        this.methodDescriptorPali,
+        callback);
+    }
+    return this.client_.unaryCall(
+    this.hostname_ +
+      '/mint.tulip.v1.Search/Pali',
+    request,
+    metadata || {},
+    this.methodDescriptorPali);
+  }
+
+}
+

+ 127 - 0
dashboard/src/protocols/tulip_pb.d.ts

@@ -0,0 +1,127 @@
+import * as jspb from 'google-protobuf'
+
+
+
+export class SearchRequest extends jspb.Message {
+  getKeywordsList(): Array<string>;
+  setKeywordsList(value: Array<string>): SearchRequest;
+  clearKeywordsList(): SearchRequest;
+  addKeywords(value: string, index?: number): SearchRequest;
+
+  getBook(): number;
+  setBook(value: number): SearchRequest;
+
+  getPage(): SearchRequest.Page | undefined;
+  setPage(value?: SearchRequest.Page): SearchRequest;
+  hasPage(): boolean;
+  clearPage(): SearchRequest;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): SearchRequest.AsObject;
+  static toObject(includeInstance: boolean, msg: SearchRequest): SearchRequest.AsObject;
+  static serializeBinaryToWriter(message: SearchRequest, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): SearchRequest;
+  static deserializeBinaryFromReader(message: SearchRequest, reader: jspb.BinaryReader): SearchRequest;
+}
+
+export namespace SearchRequest {
+  export type AsObject = {
+    keywordsList: Array<string>,
+    book: number,
+    page?: SearchRequest.Page.AsObject,
+  }
+
+  export class Page extends jspb.Message {
+    getIndex(): number;
+    setIndex(value: number): Page;
+
+    getSize(): number;
+    setSize(value: number): Page;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): Page.AsObject;
+    static toObject(includeInstance: boolean, msg: Page): Page.AsObject;
+    static serializeBinaryToWriter(message: Page, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): Page;
+    static deserializeBinaryFromReader(message: Page, reader: jspb.BinaryReader): Page;
+  }
+
+  export namespace Page {
+    export type AsObject = {
+      index: number,
+      size: number,
+    }
+  }
+
+
+  export enum PageCase { 
+    _PAGE_NOT_SET = 0,
+    PAGE = 99,
+  }
+}
+
+export class SearchResponse extends jspb.Message {
+  getItemsList(): Array<SearchResponse.Item>;
+  setItemsList(value: Array<SearchResponse.Item>): SearchResponse;
+  clearItemsList(): SearchResponse;
+  addItems(value?: SearchResponse.Item, index?: number): SearchResponse.Item;
+
+  getPage(): SearchRequest.Page | undefined;
+  setPage(value?: SearchRequest.Page): SearchResponse;
+  hasPage(): boolean;
+  clearPage(): SearchResponse;
+
+  getTotal(): number;
+  setTotal(value: number): SearchResponse;
+
+  serializeBinary(): Uint8Array;
+  toObject(includeInstance?: boolean): SearchResponse.AsObject;
+  static toObject(includeInstance: boolean, msg: SearchResponse): SearchResponse.AsObject;
+  static serializeBinaryToWriter(message: SearchResponse, writer: jspb.BinaryWriter): void;
+  static deserializeBinary(bytes: Uint8Array): SearchResponse;
+  static deserializeBinaryFromReader(message: SearchResponse, reader: jspb.BinaryReader): SearchResponse;
+}
+
+export namespace SearchResponse {
+  export type AsObject = {
+    itemsList: Array<SearchResponse.Item.AsObject>,
+    page?: SearchRequest.Page.AsObject,
+    total: number,
+  }
+
+  export class Item extends jspb.Message {
+    getRank(): number;
+    setRank(value: number): Item;
+
+    getHighlight(): string;
+    setHighlight(value: string): Item;
+
+    getBook(): number;
+    setBook(value: number): Item;
+
+    getParagraph(): number;
+    setParagraph(value: number): Item;
+
+    getContent(): string;
+    setContent(value: string): Item;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): Item.AsObject;
+    static toObject(includeInstance: boolean, msg: Item): Item.AsObject;
+    static serializeBinaryToWriter(message: Item, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): Item;
+    static deserializeBinaryFromReader(message: Item, reader: jspb.BinaryReader): Item;
+  }
+
+  export namespace Item {
+    export type AsObject = {
+      rank: number,
+      highlight: string,
+      book: number,
+      paragraph: number,
+      content: string,
+    }
+  }
+
+}
+

+ 1000 - 0
dashboard/src/protocols/tulip_pb.js

@@ -0,0 +1,1000 @@
+// source: tulip.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {missingRequire} reports error on implicit type usages.
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global =
+    (typeof globalThis !== 'undefined' && globalThis) ||
+    (typeof window !== 'undefined' && window) ||
+    (typeof global !== 'undefined' && global) ||
+    (typeof self !== 'undefined' && self) ||
+    (function () { return this; }).call(null) ||
+    Function('return this')();
+
+goog.exportSymbol('proto.mint.tulip.v1.SearchRequest', null, global);
+goog.exportSymbol('proto.mint.tulip.v1.SearchRequest.Page', null, global);
+goog.exportSymbol('proto.mint.tulip.v1.SearchResponse', null, global);
+goog.exportSymbol('proto.mint.tulip.v1.SearchResponse.Item', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.mint.tulip.v1.SearchRequest = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.mint.tulip.v1.SearchRequest.repeatedFields_, null);
+};
+goog.inherits(proto.mint.tulip.v1.SearchRequest, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.mint.tulip.v1.SearchRequest.displayName = 'proto.mint.tulip.v1.SearchRequest';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.mint.tulip.v1.SearchRequest.Page = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.mint.tulip.v1.SearchRequest.Page, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.mint.tulip.v1.SearchRequest.Page.displayName = 'proto.mint.tulip.v1.SearchRequest.Page';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.mint.tulip.v1.SearchResponse = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.mint.tulip.v1.SearchResponse.repeatedFields_, null);
+};
+goog.inherits(proto.mint.tulip.v1.SearchResponse, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.mint.tulip.v1.SearchResponse.displayName = 'proto.mint.tulip.v1.SearchResponse';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.mint.tulip.v1.SearchResponse.Item = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.mint.tulip.v1.SearchResponse.Item, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.mint.tulip.v1.SearchResponse.Item.displayName = 'proto.mint.tulip.v1.SearchResponse.Item';
+}
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.mint.tulip.v1.SearchRequest.repeatedFields_ = [1];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.toObject = function(opt_includeInstance) {
+  return proto.mint.tulip.v1.SearchRequest.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.mint.tulip.v1.SearchRequest} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchRequest.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    keywordsList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f,
+    book: jspb.Message.getFieldWithDefault(msg, 2, 0),
+    page: (f = msg.getPage()) && proto.mint.tulip.v1.SearchRequest.Page.toObject(includeInstance, f)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.mint.tulip.v1.SearchRequest}
+ */
+proto.mint.tulip.v1.SearchRequest.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.mint.tulip.v1.SearchRequest;
+  return proto.mint.tulip.v1.SearchRequest.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.mint.tulip.v1.SearchRequest} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.mint.tulip.v1.SearchRequest}
+ */
+proto.mint.tulip.v1.SearchRequest.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.addKeywords(value);
+      break;
+    case 2:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setBook(value);
+      break;
+    case 99:
+      var value = new proto.mint.tulip.v1.SearchRequest.Page;
+      reader.readMessage(value,proto.mint.tulip.v1.SearchRequest.Page.deserializeBinaryFromReader);
+      msg.setPage(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.mint.tulip.v1.SearchRequest.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.mint.tulip.v1.SearchRequest} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchRequest.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getKeywordsList();
+  if (f.length > 0) {
+    writer.writeRepeatedString(
+      1,
+      f
+    );
+  }
+  f = message.getBook();
+  if (f !== 0) {
+    writer.writeInt32(
+      2,
+      f
+    );
+  }
+  f = message.getPage();
+  if (f != null) {
+    writer.writeMessage(
+      99,
+      f,
+      proto.mint.tulip.v1.SearchRequest.Page.serializeBinaryToWriter
+    );
+  }
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.toObject = function(opt_includeInstance) {
+  return proto.mint.tulip.v1.SearchRequest.Page.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.mint.tulip.v1.SearchRequest.Page} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchRequest.Page.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    index: jspb.Message.getFieldWithDefault(msg, 1, 0),
+    size: jspb.Message.getFieldWithDefault(msg, 2, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.mint.tulip.v1.SearchRequest.Page}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.mint.tulip.v1.SearchRequest.Page;
+  return proto.mint.tulip.v1.SearchRequest.Page.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.mint.tulip.v1.SearchRequest.Page} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.mint.tulip.v1.SearchRequest.Page}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setIndex(value);
+      break;
+    case 2:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setSize(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.mint.tulip.v1.SearchRequest.Page.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.mint.tulip.v1.SearchRequest.Page} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchRequest.Page.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getIndex();
+  if (f !== 0) {
+    writer.writeInt32(
+      1,
+      f
+    );
+  }
+  f = message.getSize();
+  if (f !== 0) {
+    writer.writeInt32(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional int32 index = 1;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.getIndex = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchRequest.Page} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.setIndex = function(value) {
+  return jspb.Message.setProto3IntField(this, 1, value);
+};
+
+
+/**
+ * optional int32 size = 2;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.getSize = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchRequest.Page} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.Page.prototype.setSize = function(value) {
+  return jspb.Message.setProto3IntField(this, 2, value);
+};
+
+
+/**
+ * repeated string keywords = 1;
+ * @return {!Array<string>}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.getKeywordsList = function() {
+  return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1));
+};
+
+
+/**
+ * @param {!Array<string>} value
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.setKeywordsList = function(value) {
+  return jspb.Message.setField(this, 1, value || []);
+};
+
+
+/**
+ * @param {string} value
+ * @param {number=} opt_index
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.addKeywords = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 1, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.clearKeywordsList = function() {
+  return this.setKeywordsList([]);
+};
+
+
+/**
+ * optional int32 book = 2;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.getBook = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.setBook = function(value) {
+  return jspb.Message.setProto3IntField(this, 2, value);
+};
+
+
+/**
+ * optional Page page = 99;
+ * @return {?proto.mint.tulip.v1.SearchRequest.Page}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.getPage = function() {
+  return /** @type{?proto.mint.tulip.v1.SearchRequest.Page} */ (
+    jspb.Message.getWrapperField(this, proto.mint.tulip.v1.SearchRequest.Page, 99));
+};
+
+
+/**
+ * @param {?proto.mint.tulip.v1.SearchRequest.Page|undefined} value
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+*/
+proto.mint.tulip.v1.SearchRequest.prototype.setPage = function(value) {
+  return jspb.Message.setWrapperField(this, 99, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.mint.tulip.v1.SearchRequest} returns this
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.clearPage = function() {
+  return this.setPage(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.mint.tulip.v1.SearchRequest.prototype.hasPage = function() {
+  return jspb.Message.getField(this, 99) != null;
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.mint.tulip.v1.SearchResponse.repeatedFields_ = [1];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.toObject = function(opt_includeInstance) {
+  return proto.mint.tulip.v1.SearchResponse.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.mint.tulip.v1.SearchResponse} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchResponse.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    itemsList: jspb.Message.toObjectList(msg.getItemsList(),
+    proto.mint.tulip.v1.SearchResponse.Item.toObject, includeInstance),
+    page: (f = msg.getPage()) && proto.mint.tulip.v1.SearchRequest.Page.toObject(includeInstance, f),
+    total: jspb.Message.getFieldWithDefault(msg, 99, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.mint.tulip.v1.SearchResponse}
+ */
+proto.mint.tulip.v1.SearchResponse.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.mint.tulip.v1.SearchResponse;
+  return proto.mint.tulip.v1.SearchResponse.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.mint.tulip.v1.SearchResponse} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.mint.tulip.v1.SearchResponse}
+ */
+proto.mint.tulip.v1.SearchResponse.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = new proto.mint.tulip.v1.SearchResponse.Item;
+      reader.readMessage(value,proto.mint.tulip.v1.SearchResponse.Item.deserializeBinaryFromReader);
+      msg.addItems(value);
+      break;
+    case 98:
+      var value = new proto.mint.tulip.v1.SearchRequest.Page;
+      reader.readMessage(value,proto.mint.tulip.v1.SearchRequest.Page.deserializeBinaryFromReader);
+      msg.setPage(value);
+      break;
+    case 99:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setTotal(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.mint.tulip.v1.SearchResponse.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.mint.tulip.v1.SearchResponse} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchResponse.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getItemsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      1,
+      f,
+      proto.mint.tulip.v1.SearchResponse.Item.serializeBinaryToWriter
+    );
+  }
+  f = message.getPage();
+  if (f != null) {
+    writer.writeMessage(
+      98,
+      f,
+      proto.mint.tulip.v1.SearchRequest.Page.serializeBinaryToWriter
+    );
+  }
+  f = message.getTotal();
+  if (f !== 0) {
+    writer.writeInt32(
+      99,
+      f
+    );
+  }
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.toObject = function(opt_includeInstance) {
+  return proto.mint.tulip.v1.SearchResponse.Item.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.mint.tulip.v1.SearchResponse.Item} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchResponse.Item.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    rank: jspb.Message.getFieldWithDefault(msg, 1, 0),
+    highlight: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    book: jspb.Message.getFieldWithDefault(msg, 3, 0),
+    paragraph: jspb.Message.getFieldWithDefault(msg, 4, 0),
+    content: jspb.Message.getFieldWithDefault(msg, 5, "")
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.mint.tulip.v1.SearchResponse.Item;
+  return proto.mint.tulip.v1.SearchResponse.Item.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.mint.tulip.v1.SearchResponse.Item} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setRank(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setHighlight(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setBook(value);
+      break;
+    case 4:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setParagraph(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContent(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.mint.tulip.v1.SearchResponse.Item.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.mint.tulip.v1.SearchResponse.Item} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.mint.tulip.v1.SearchResponse.Item.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getRank();
+  if (f !== 0) {
+    writer.writeInt32(
+      1,
+      f
+    );
+  }
+  f = message.getHighlight();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getBook();
+  if (f !== 0) {
+    writer.writeInt32(
+      3,
+      f
+    );
+  }
+  f = message.getParagraph();
+  if (f !== 0) {
+    writer.writeInt32(
+      4,
+      f
+    );
+  }
+  f = message.getContent();
+  if (f.length > 0) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional int32 rank = 1;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.getRank = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.setRank = function(value) {
+  return jspb.Message.setProto3IntField(this, 1, value);
+};
+
+
+/**
+ * optional string highlight = 2;
+ * @return {string}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.getHighlight = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.setHighlight = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional int32 book = 3;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.getBook = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.setBook = function(value) {
+  return jspb.Message.setProto3IntField(this, 3, value);
+};
+
+
+/**
+ * optional int32 paragraph = 4;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.getParagraph = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.setParagraph = function(value) {
+  return jspb.Message.setProto3IntField(this, 4, value);
+};
+
+
+/**
+ * optional string content = 5;
+ * @return {string}
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.getContent = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.Item.prototype.setContent = function(value) {
+  return jspb.Message.setProto3StringField(this, 5, value);
+};
+
+
+/**
+ * repeated Item items = 1;
+ * @return {!Array<!proto.mint.tulip.v1.SearchResponse.Item>}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.getItemsList = function() {
+  return /** @type{!Array<!proto.mint.tulip.v1.SearchResponse.Item>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.mint.tulip.v1.SearchResponse.Item, 1));
+};
+
+
+/**
+ * @param {!Array<!proto.mint.tulip.v1.SearchResponse.Item>} value
+ * @return {!proto.mint.tulip.v1.SearchResponse} returns this
+*/
+proto.mint.tulip.v1.SearchResponse.prototype.setItemsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 1, value);
+};
+
+
+/**
+ * @param {!proto.mint.tulip.v1.SearchResponse.Item=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.mint.tulip.v1.SearchResponse.Item}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.addItems = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.mint.tulip.v1.SearchResponse.Item, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.mint.tulip.v1.SearchResponse} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.clearItemsList = function() {
+  return this.setItemsList([]);
+};
+
+
+/**
+ * optional SearchRequest.Page page = 98;
+ * @return {?proto.mint.tulip.v1.SearchRequest.Page}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.getPage = function() {
+  return /** @type{?proto.mint.tulip.v1.SearchRequest.Page} */ (
+    jspb.Message.getWrapperField(this, proto.mint.tulip.v1.SearchRequest.Page, 98));
+};
+
+
+/**
+ * @param {?proto.mint.tulip.v1.SearchRequest.Page|undefined} value
+ * @return {!proto.mint.tulip.v1.SearchResponse} returns this
+*/
+proto.mint.tulip.v1.SearchResponse.prototype.setPage = function(value) {
+  return jspb.Message.setWrapperField(this, 98, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.mint.tulip.v1.SearchResponse} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.clearPage = function() {
+  return this.setPage(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.hasPage = function() {
+  return jspb.Message.getField(this, 98) != null;
+};
+
+
+/**
+ * optional int32 total = 99;
+ * @return {number}
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.getTotal = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 99, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.mint.tulip.v1.SearchResponse} returns this
+ */
+proto.mint.tulip.v1.SearchResponse.prototype.setTotal = function(value) {
+  return jspb.Message.setProto3IntField(this, 99, value);
+};
+
+
+goog.object.extend(exports, proto.mint.tulip.v1);

+ 1 - 0
deploy/roles/mint-v2/templates/v2/env.j2

@@ -79,3 +79,4 @@ RABBITMQ_USER="{{ app_rabbitmq_user }}"
 RABBITMQ_PASSWORD="{{ app_rabbitmq_password }}"
 
 CDN_URLS={{ app_cdn_urls }}
+ATTACHMENTS_BUCKET_NAME={{ app_attachments_bucket_name }}

+ 1 - 2
rpc/morus/morus/composer.json

@@ -9,8 +9,7 @@
       "GPBMetadata\\": [
         "GPBMetadata/"
       ],
-      "Mint\\": "Mint/",
-      "Palm\\": "Palm/"
+      "Mint\\": "Mint/"
     }
   }
 }

+ 36 - 0
rpc/protocols/tulip.proto

@@ -0,0 +1,36 @@
+syntax = "proto3";
+option java_multiple_files = true;
+option java_package = "com.github.iapt_platform.mint.plugins.tulip.v1";
+package mint.tulip.v1;
+
+// ----------------------------------------------------------------------------
+message SearchRequest {
+  repeated string keywords = 1;
+  int32 book = 2;
+
+  message Page {
+    int32 index = 1;
+    int32 size = 2;
+  }
+  optional Page page = 99;
+}
+
+message SearchResponse {
+  message Item {
+    int32 rank = 1;
+    string highlight = 2;
+    int32 book = 3;
+    int32 paragraph = 4;
+    string content = 5;
+  }
+  repeated Item items = 1;
+
+  SearchRequest.Page page = 98;
+  int32 total = 99;
+}
+
+service Search {
+  rpc Pali(SearchRequest) returns (SearchResponse) {}
+}
+
+// ----------------------------------------------------------------------------

+ 24 - 1
rpc/schema.sh

@@ -50,7 +50,6 @@ function generate_for_morus() {
     local -a folders=(
         "GPBMetadata"
         "Mint"
-        "Palm"
     )
     for f in "${folders[@]}"
     do
@@ -90,6 +89,29 @@ function generate_for_lily() {
     sed -i 's/import lily_/from . import lily_/g' $target/lily_pb2_grpc.py
 }
 
+
+function generate_for_tulip() {
+    echo "generate code for tulip project"
+    local target=$WORKSPACE/tulip/tulip
+    local -a folders=(
+        "GPBMetadata"
+        "Mint"        
+    )
+    for f in "${folders[@]}"
+    do
+        local t=$target/$f
+        if [ -d $t ]
+        then
+            rm -r $t
+        fi
+    done
+    $PROTOBUF_ROOT/bin/protoc -I $WORKSPACE/protocols \
+        -I $PROTOBUF_ROOT/include/google/protobuf \
+        --php_out=$target --grpc_out=generate_server:$target \
+        --plugin=protoc-gen-grpc=$PROTOBUF_ROOT/bin/grpc_php_plugin \
+        $WORKSPACE/protocols/tulip.proto    
+}
+
 function generate_grpc_for_php() {
     if [ -d $1 ]
     then
@@ -121,6 +143,7 @@ generate_grpc_for_php $WORKSPACE/sdk/php
 
 generate_for_morus
 generate_for_lily
+generate_for_tulip
 
 generate_grpc_web $WORKSPACE/../dashboard
 

+ 90 - 0
rpc/sdk/cpp/tulip.grpc.pb.cc

@@ -0,0 +1,90 @@
+// Generated by the gRPC C++ plugin.
+// If you make any local change, they will be lost.
+// source: tulip.proto
+
+#include "tulip.pb.h"
+#include "tulip.grpc.pb.h"
+
+#include <functional>
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/impl/channel_interface.h>
+#include <grpcpp/impl/client_unary_call.h>
+#include <grpcpp/support/client_callback.h>
+#include <grpcpp/support/message_allocator.h>
+#include <grpcpp/support/method_handler.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/support/server_callback.h>
+#include <grpcpp/impl/server_callback_handlers.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/support/sync_stream.h>
+namespace mint {
+namespace tulip {
+namespace v1 {
+
+static const char* Search_method_names[] = {
+  "/mint.tulip.v1.Search/Pali",
+};
+
+std::unique_ptr< Search::Stub> Search::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) {
+  (void)options;
+  std::unique_ptr< Search::Stub> stub(new Search::Stub(channel, options));
+  return stub;
+}
+
+Search::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options)
+  : channel_(channel), rpcmethod_Pali_(Search_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
+  {}
+
+::grpc::Status Search::Stub::Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::mint::tulip::v1::SearchResponse* response) {
+  return ::grpc::internal::BlockingUnaryCall< ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_Pali_, context, request, response);
+}
+
+void Search::Stub::async::Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, std::function<void(::grpc::Status)> f) {
+  ::grpc::internal::CallbackUnaryCall< ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Pali_, context, request, response, std::move(f));
+}
+
+void Search::Stub::async::Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, ::grpc::ClientUnaryReactor* reactor) {
+  ::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Pali_, context, request, response, reactor);
+}
+
+::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>* Search::Stub::PrepareAsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+  return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::mint::tulip::v1::SearchResponse, ::mint::tulip::v1::SearchRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_Pali_, context, request);
+}
+
+::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>* Search::Stub::AsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+  auto* result =
+    this->PrepareAsyncPaliRaw(context, request, cq);
+  result->StartCall();
+  return result;
+}
+
+Search::Service::Service() {
+  AddMethod(new ::grpc::internal::RpcServiceMethod(
+      Search_method_names[0],
+      ::grpc::internal::RpcMethod::NORMAL_RPC,
+      new ::grpc::internal::RpcMethodHandler< Search::Service, ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
+          [](Search::Service* service,
+             ::grpc::ServerContext* ctx,
+             const ::mint::tulip::v1::SearchRequest* req,
+             ::mint::tulip::v1::SearchResponse* resp) {
+               return service->Pali(ctx, req, resp);
+             }, this)));
+}
+
+Search::Service::~Service() {
+}
+
+::grpc::Status Search::Service::Pali(::grpc::ServerContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response) {
+  (void) context;
+  (void) request;
+  (void) response;
+  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+}
+
+
+}  // namespace mint
+}  // namespace tulip
+}  // namespace v1
+

+ 244 - 0
rpc/sdk/cpp/tulip.grpc.pb.h

@@ -0,0 +1,244 @@
+// Generated by the gRPC C++ plugin.
+// If you make any local change, they will be lost.
+// source: tulip.proto
+#ifndef GRPC_tulip_2eproto__INCLUDED
+#define GRPC_tulip_2eproto__INCLUDED
+
+#include "tulip.pb.h"
+
+#include <functional>
+#include <grpcpp/generic/async_generic_service.h>
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/client_callback.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/support/message_allocator.h>
+#include <grpcpp/support/method_handler.h>
+#include <grpcpp/impl/proto_utils.h>
+#include <grpcpp/impl/rpc_method.h>
+#include <grpcpp/support/server_callback.h>
+#include <grpcpp/impl/server_callback_handlers.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/stub_options.h>
+#include <grpcpp/support/sync_stream.h>
+
+namespace mint {
+namespace tulip {
+namespace v1 {
+
+class Search final {
+ public:
+  static constexpr char const* service_full_name() {
+    return "mint.tulip.v1.Search";
+  }
+  class StubInterface {
+   public:
+    virtual ~StubInterface() {}
+    virtual ::grpc::Status Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::mint::tulip::v1::SearchResponse* response) = 0;
+    std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>> AsyncPali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+      return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>>(AsyncPaliRaw(context, request, cq));
+    }
+    std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>> PrepareAsyncPali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+      return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>>(PrepareAsyncPaliRaw(context, request, cq));
+    }
+    class async_interface {
+     public:
+      virtual ~async_interface() {}
+      virtual void Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, std::function<void(::grpc::Status)>) = 0;
+      virtual void Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, ::grpc::ClientUnaryReactor* reactor) = 0;
+    };
+    typedef class async_interface experimental_async_interface;
+    virtual class async_interface* async() { return nullptr; }
+    class async_interface* experimental_async() { return async(); }
+   private:
+    virtual ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>* AsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) = 0;
+    virtual ::grpc::ClientAsyncResponseReaderInterface< ::mint::tulip::v1::SearchResponse>* PrepareAsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) = 0;
+  };
+  class Stub final : public StubInterface {
+   public:
+    Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions());
+    ::grpc::Status Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::mint::tulip::v1::SearchResponse* response) override;
+    std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>> AsyncPali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+      return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>>(AsyncPaliRaw(context, request, cq));
+    }
+    std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>> PrepareAsyncPali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) {
+      return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>>(PrepareAsyncPaliRaw(context, request, cq));
+    }
+    class async final :
+      public StubInterface::async_interface {
+     public:
+      void Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, std::function<void(::grpc::Status)>) override;
+      void Pali(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response, ::grpc::ClientUnaryReactor* reactor) override;
+     private:
+      friend class Stub;
+      explicit async(Stub* stub): stub_(stub) { }
+      Stub* stub() { return stub_; }
+      Stub* stub_;
+    };
+    class async* async() override { return &async_stub_; }
+
+   private:
+    std::shared_ptr< ::grpc::ChannelInterface> channel_;
+    class async async_stub_{this};
+    ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>* AsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) override;
+    ::grpc::ClientAsyncResponseReader< ::mint::tulip::v1::SearchResponse>* PrepareAsyncPaliRaw(::grpc::ClientContext* context, const ::mint::tulip::v1::SearchRequest& request, ::grpc::CompletionQueue* cq) override;
+    const ::grpc::internal::RpcMethod rpcmethod_Pali_;
+  };
+  static std::unique_ptr<Stub> NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions());
+
+  class Service : public ::grpc::Service {
+   public:
+    Service();
+    virtual ~Service();
+    virtual ::grpc::Status Pali(::grpc::ServerContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response);
+  };
+  template <class BaseClass>
+  class WithAsyncMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithAsyncMethod_Pali() {
+      ::grpc::Service::MarkMethodAsync(0);
+    }
+    ~WithAsyncMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable synchronous version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    void RequestPali(::grpc::ServerContext* context, ::mint::tulip::v1::SearchRequest* request, ::grpc::ServerAsyncResponseWriter< ::mint::tulip::v1::SearchResponse>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) {
+      ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag);
+    }
+  };
+  typedef WithAsyncMethod_Pali<Service > AsyncService;
+  template <class BaseClass>
+  class WithCallbackMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithCallbackMethod_Pali() {
+      ::grpc::Service::MarkMethodCallback(0,
+          new ::grpc::internal::CallbackUnaryHandler< ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse>(
+            [this](
+                   ::grpc::CallbackServerContext* context, const ::mint::tulip::v1::SearchRequest* request, ::mint::tulip::v1::SearchResponse* response) { return this->Pali(context, request, response); }));}
+    void SetMessageAllocatorFor_Pali(
+        ::grpc::MessageAllocator< ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse>* allocator) {
+      ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(0);
+      static_cast<::grpc::internal::CallbackUnaryHandler< ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse>*>(handler)
+              ->SetMessageAllocator(allocator);
+    }
+    ~WithCallbackMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable synchronous version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    virtual ::grpc::ServerUnaryReactor* Pali(
+      ::grpc::CallbackServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/)  { return nullptr; }
+  };
+  typedef WithCallbackMethod_Pali<Service > CallbackService;
+  typedef CallbackService ExperimentalCallbackService;
+  template <class BaseClass>
+  class WithGenericMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithGenericMethod_Pali() {
+      ::grpc::Service::MarkMethodGeneric(0);
+    }
+    ~WithGenericMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable synchronous version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+  };
+  template <class BaseClass>
+  class WithRawMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithRawMethod_Pali() {
+      ::grpc::Service::MarkMethodRaw(0);
+    }
+    ~WithRawMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable synchronous version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    void RequestPali(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) {
+      ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag);
+    }
+  };
+  template <class BaseClass>
+  class WithRawCallbackMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithRawCallbackMethod_Pali() {
+      ::grpc::Service::MarkMethodRawCallback(0,
+          new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
+            [this](
+                   ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->Pali(context, request, response); }));
+    }
+    ~WithRawCallbackMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable synchronous version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    virtual ::grpc::ServerUnaryReactor* Pali(
+      ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/)  { return nullptr; }
+  };
+  template <class BaseClass>
+  class WithStreamedUnaryMethod_Pali : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service* /*service*/) {}
+   public:
+    WithStreamedUnaryMethod_Pali() {
+      ::grpc::Service::MarkMethodStreamed(0,
+        new ::grpc::internal::StreamedUnaryHandler<
+          ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse>(
+            [this](::grpc::ServerContext* context,
+                   ::grpc::ServerUnaryStreamer<
+                     ::mint::tulip::v1::SearchRequest, ::mint::tulip::v1::SearchResponse>* streamer) {
+                       return this->StreamedPali(context,
+                         streamer);
+                  }));
+    }
+    ~WithStreamedUnaryMethod_Pali() override {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable regular version of this method
+    ::grpc::Status Pali(::grpc::ServerContext* /*context*/, const ::mint::tulip::v1::SearchRequest* /*request*/, ::mint::tulip::v1::SearchResponse* /*response*/) override {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    // replace default version of method with streamed unary
+    virtual ::grpc::Status StreamedPali(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::mint::tulip::v1::SearchRequest,::mint::tulip::v1::SearchResponse>* server_unary_streamer) = 0;
+  };
+  typedef WithStreamedUnaryMethod_Pali<Service > StreamedUnaryService;
+  typedef Service SplitStreamedService;
+  typedef WithStreamedUnaryMethod_Pali<Service > StreamedService;
+};
+
+}  // namespace v1
+}  // namespace tulip
+}  // namespace mint
+
+
+#endif  // GRPC_tulip_2eproto__INCLUDED

+ 1283 - 0
rpc/sdk/cpp/tulip.pb.cc

@@ -0,0 +1,1283 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+#include "tulip.pb.h"
+
+#include <algorithm>
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/extension_set.h"
+#include "google/protobuf/wire_format_lite.h"
+#include "google/protobuf/descriptor.h"
+#include "google/protobuf/generated_message_reflection.h"
+#include "google/protobuf/reflection_ops.h"
+#include "google/protobuf/wire_format.h"
+#include "google/protobuf/generated_message_tctable_impl.h"
+// @@protoc_insertion_point(includes)
+
+// Must be included last.
+#include "google/protobuf/port_def.inc"
+PROTOBUF_PRAGMA_INIT_SEG
+namespace _pb = ::google::protobuf;
+namespace _pbi = ::google::protobuf::internal;
+namespace _fl = ::google::protobuf::internal::field_layout;
+namespace mint {
+namespace tulip {
+namespace v1 {
+        template <typename>
+PROTOBUF_CONSTEXPR SearchRequest_Page::SearchRequest_Page(::_pbi::ConstantInitialized)
+    : _impl_{
+      /*decltype(_impl_.index_)*/ 0,
+      /*decltype(_impl_.size_)*/ 0,
+      /*decltype(_impl_._cached_size_)*/ {},
+    } {}
+struct SearchRequest_PageDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SearchRequest_PageDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SearchRequest_PageDefaultTypeInternal() {}
+  union {
+    SearchRequest_Page _instance;
+  };
+};
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
+    PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SearchRequest_PageDefaultTypeInternal _SearchRequest_Page_default_instance_;
+        template <typename>
+PROTOBUF_CONSTEXPR SearchRequest::SearchRequest(::_pbi::ConstantInitialized)
+    : _impl_{
+      /*decltype(_impl_._has_bits_)*/ {},
+      /*decltype(_impl_._cached_size_)*/ {},
+      /*decltype(_impl_.keywords_)*/ {},
+      /*decltype(_impl_.page_)*/ nullptr,
+      /*decltype(_impl_.book_)*/ 0,
+    } {}
+struct SearchRequestDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SearchRequestDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SearchRequestDefaultTypeInternal() {}
+  union {
+    SearchRequest _instance;
+  };
+};
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
+    PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SearchRequestDefaultTypeInternal _SearchRequest_default_instance_;
+        template <typename>
+PROTOBUF_CONSTEXPR SearchResponse_Item::SearchResponse_Item(::_pbi::ConstantInitialized)
+    : _impl_{
+      /*decltype(_impl_.highlight_)*/ {
+          &::_pbi::fixed_address_empty_string,
+          ::_pbi::ConstantInitialized{},
+      },
+      /*decltype(_impl_.content_)*/ {
+          &::_pbi::fixed_address_empty_string,
+          ::_pbi::ConstantInitialized{},
+      },
+      /*decltype(_impl_.rank_)*/ 0,
+      /*decltype(_impl_.book_)*/ 0,
+      /*decltype(_impl_.paragraph_)*/ 0,
+      /*decltype(_impl_._cached_size_)*/ {},
+    } {}
+struct SearchResponse_ItemDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SearchResponse_ItemDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SearchResponse_ItemDefaultTypeInternal() {}
+  union {
+    SearchResponse_Item _instance;
+  };
+};
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
+    PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SearchResponse_ItemDefaultTypeInternal _SearchResponse_Item_default_instance_;
+        template <typename>
+PROTOBUF_CONSTEXPR SearchResponse::SearchResponse(::_pbi::ConstantInitialized)
+    : _impl_{
+      /*decltype(_impl_._has_bits_)*/ {},
+      /*decltype(_impl_._cached_size_)*/ {},
+      /*decltype(_impl_.items_)*/ {},
+      /*decltype(_impl_.page_)*/ nullptr,
+      /*decltype(_impl_.total_)*/ 0,
+    } {}
+struct SearchResponseDefaultTypeInternal {
+  PROTOBUF_CONSTEXPR SearchResponseDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {}
+  ~SearchResponseDefaultTypeInternal() {}
+  union {
+    SearchResponse _instance;
+  };
+};
+
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
+    PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SearchResponseDefaultTypeInternal _SearchResponse_default_instance_;
+}  // namespace v1
+}  // namespace tulip
+}  // namespace mint
+static ::_pb::Metadata file_level_metadata_tulip_2eproto[4];
+static constexpr const ::_pb::EnumDescriptor**
+    file_level_enum_descriptors_tulip_2eproto = nullptr;
+static constexpr const ::_pb::ServiceDescriptor**
+    file_level_service_descriptors_tulip_2eproto = nullptr;
+const ::uint32_t TableStruct_tulip_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(
+    protodesc_cold) = {
+    ~0u,  // no _has_bits_
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest_Page, _internal_metadata_),
+    ~0u,  // no _extensions_
+    ~0u,  // no _oneof_case_
+    ~0u,  // no _weak_field_map_
+    ~0u,  // no _inlined_string_donated_
+    ~0u,  // no _split_
+    ~0u,  // no sizeof(Split)
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest_Page, _impl_.index_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest_Page, _impl_.size_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest, _impl_._has_bits_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest, _internal_metadata_),
+    ~0u,  // no _extensions_
+    ~0u,  // no _oneof_case_
+    ~0u,  // no _weak_field_map_
+    ~0u,  // no _inlined_string_donated_
+    ~0u,  // no _split_
+    ~0u,  // no sizeof(Split)
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest, _impl_.keywords_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest, _impl_.book_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchRequest, _impl_.page_),
+    ~0u,
+    ~0u,
+    0,
+    ~0u,  // no _has_bits_
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _internal_metadata_),
+    ~0u,  // no _extensions_
+    ~0u,  // no _oneof_case_
+    ~0u,  // no _weak_field_map_
+    ~0u,  // no _inlined_string_donated_
+    ~0u,  // no _split_
+    ~0u,  // no sizeof(Split)
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _impl_.rank_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _impl_.highlight_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _impl_.book_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _impl_.paragraph_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse_Item, _impl_.content_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse, _impl_._has_bits_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse, _internal_metadata_),
+    ~0u,  // no _extensions_
+    ~0u,  // no _oneof_case_
+    ~0u,  // no _weak_field_map_
+    ~0u,  // no _inlined_string_donated_
+    ~0u,  // no _split_
+    ~0u,  // no sizeof(Split)
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse, _impl_.items_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse, _impl_.page_),
+    PROTOBUF_FIELD_OFFSET(::mint::tulip::v1::SearchResponse, _impl_.total_),
+    ~0u,
+    0,
+    ~0u,
+};
+
+static const ::_pbi::MigrationSchema
+    schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+        {0, -1, -1, sizeof(::mint::tulip::v1::SearchRequest_Page)},
+        {10, 21, -1, sizeof(::mint::tulip::v1::SearchRequest)},
+        {24, -1, -1, sizeof(::mint::tulip::v1::SearchResponse_Item)},
+        {37, 48, -1, sizeof(::mint::tulip::v1::SearchResponse)},
+};
+
+static const ::_pb::Message* const file_default_instances[] = {
+    &::mint::tulip::v1::_SearchRequest_Page_default_instance_._instance,
+    &::mint::tulip::v1::_SearchRequest_default_instance_._instance,
+    &::mint::tulip::v1::_SearchResponse_Item_default_instance_._instance,
+    &::mint::tulip::v1::_SearchResponse_default_instance_._instance,
+};
+const char descriptor_table_protodef_tulip_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+    "\n\013tulip.proto\022\rmint.tulip.v1\"\223\001\n\rSearchR"
+    "equest\022\020\n\010keywords\030\001 \003(\t\022\014\n\004book\030\002 \001(\005\0224"
+    "\n\004page\030c \001(\0132!.mint.tulip.v1.SearchReque"
+    "st.PageH\000\210\001\001\032#\n\004Page\022\r\n\005index\030\001 \001(\005\022\014\n\004s"
+    "ize\030\002 \001(\005B\007\n\005_page\"\336\001\n\016SearchResponse\0221\n"
+    "\005items\030\001 \003(\0132\".mint.tulip.v1.SearchRespo"
+    "nse.Item\022/\n\004page\030b \001(\0132!.mint.tulip.v1.S"
+    "earchRequest.Page\022\r\n\005total\030c \001(\005\032Y\n\004Item"
+    "\022\014\n\004rank\030\001 \001(\005\022\021\n\thighlight\030\002 \001(\t\022\014\n\004boo"
+    "k\030\003 \001(\005\022\021\n\tparagraph\030\004 \001(\005\022\017\n\007content\030\005 "
+    "\001(\t2O\n\006Search\022E\n\004Pali\022\034.mint.tulip.v1.Se"
+    "archRequest\032\035.mint.tulip.v1.SearchRespon"
+    "se\"\000B2\n.com.github.iapt_platform.mint.pl"
+    "ugins.tulip.v1P\001b\006proto3"
+};
+static ::absl::once_flag descriptor_table_tulip_2eproto_once;
+const ::_pbi::DescriptorTable descriptor_table_tulip_2eproto = {
+    false,
+    false,
+    544,
+    descriptor_table_protodef_tulip_2eproto,
+    "tulip.proto",
+    &descriptor_table_tulip_2eproto_once,
+    nullptr,
+    0,
+    4,
+    schemas,
+    file_default_instances,
+    TableStruct_tulip_2eproto::offsets,
+    file_level_metadata_tulip_2eproto,
+    file_level_enum_descriptors_tulip_2eproto,
+    file_level_service_descriptors_tulip_2eproto,
+};
+
+// This function exists to be marked as weak.
+// It can significantly speed up compilation by breaking up LLVM's SCC
+// in the .pb.cc translation units. Large translation units see a
+// reduction of more than 35% of walltime for optimized builds. Without
+// the weak attribute all the messages in the file, including all the
+// vtables and everything they use become part of the same SCC through
+// a cycle like:
+// GetMetadata -> descriptor table -> default instances ->
+//   vtables -> GetMetadata
+// By adding a weak function here we break the connection from the
+// individual vtables back into the descriptor table.
+PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_tulip_2eproto_getter() {
+  return &descriptor_table_tulip_2eproto;
+}
+// Force running AddDescriptors() at dynamic initialization time.
+PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
+static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_tulip_2eproto(&descriptor_table_tulip_2eproto);
+namespace mint {
+namespace tulip {
+namespace v1 {
+// ===================================================================
+
+class SearchRequest_Page::_Internal {
+ public:
+};
+
+SearchRequest_Page::SearchRequest_Page(::google::protobuf::Arena* arena)
+    : ::google::protobuf::Message(arena) {
+  SharedCtor(arena);
+  // @@protoc_insertion_point(arena_constructor:mint.tulip.v1.SearchRequest.Page)
+}
+SearchRequest_Page::SearchRequest_Page(const SearchRequest_Page& from)
+    : ::google::protobuf::Message(), _impl_(from._impl_) {
+  _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(
+      from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:mint.tulip.v1.SearchRequest.Page)
+}
+inline void SearchRequest_Page::SharedCtor(::_pb::Arena* arena) {
+  (void)arena;
+  new (&_impl_) Impl_{
+      decltype(_impl_.index_){0},
+      decltype(_impl_.size_){0},
+      /*decltype(_impl_._cached_size_)*/ {},
+  };
+}
+SearchRequest_Page::~SearchRequest_Page() {
+  // @@protoc_insertion_point(destructor:mint.tulip.v1.SearchRequest.Page)
+  _internal_metadata_.Delete<::google::protobuf::UnknownFieldSet>();
+  SharedDtor();
+}
+inline void SearchRequest_Page::SharedDtor() {
+  ABSL_DCHECK(GetArenaForAllocation() == nullptr);
+}
+void SearchRequest_Page::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+PROTOBUF_NOINLINE void SearchRequest_Page::Clear() {
+// @@protoc_insertion_point(message_clear_start:mint.tulip.v1.SearchRequest.Page)
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  ::memset(&_impl_.index_, 0, static_cast<::size_t>(
+      reinterpret_cast<char*>(&_impl_.size_) -
+      reinterpret_cast<char*>(&_impl_.index_)) + sizeof(_impl_.size_));
+  _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>();
+}
+
+const char* SearchRequest_Page::_InternalParse(
+    const char* ptr, ::_pbi::ParseContext* ctx) {
+  ptr = ::_pbi::TcParser::ParseLoop(this, ptr, ctx, &_table_.header);
+  return ptr;
+}
+
+
+PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+const ::_pbi::TcParseTable<1, 2, 0, 0, 2> SearchRequest_Page::_table_ = {
+  {
+    0,  // no _has_bits_
+    0, // no _extensions_
+    2, 8,  // max_field_number, fast_idx_mask
+    offsetof(decltype(_table_), field_lookup_table),
+    4294967292,  // skipmap
+    offsetof(decltype(_table_), field_entries),
+    2,  // num_field_entries
+    0,  // num_aux_entries
+    offsetof(decltype(_table_), field_names),  // no aux_entries
+    &_SearchRequest_Page_default_instance_._instance,
+    ::_pbi::TcParser::GenericFallback,  // fallback
+  }, {{
+    // int32 size = 2;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchRequest_Page, _impl_.size_), 63>(),
+     {16, 63, 0, PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.size_)}},
+    // int32 index = 1;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchRequest_Page, _impl_.index_), 63>(),
+     {8, 63, 0, PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.index_)}},
+  }}, {{
+    65535, 65535
+  }}, {{
+    // int32 index = 1;
+    {PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.index_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+    // int32 size = 2;
+    {PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.size_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+  }},
+  // no aux_entries
+  {{
+  }},
+};
+
+::uint8_t* SearchRequest_Page::_InternalSerialize(
+    ::uint8_t* target,
+    ::google::protobuf::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mint.tulip.v1.SearchRequest.Page)
+  ::uint32_t cached_has_bits = 0;
+  (void)cached_has_bits;
+
+  // int32 index = 1;
+  if (this->_internal_index() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<1>(
+            stream, this->_internal_index(), target);
+  }
+
+  // int32 size = 2;
+  if (this->_internal_size() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<2>(
+            stream, this->_internal_size(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target =
+        ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+            _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mint.tulip.v1.SearchRequest.Page)
+  return target;
+}
+
+::size_t SearchRequest_Page::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mint.tulip.v1.SearchRequest.Page)
+  ::size_t total_size = 0;
+
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // int32 index = 1;
+  if (this->_internal_index() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_index());
+  }
+
+  // int32 size = 2;
+  if (this->_internal_size() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_size());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::google::protobuf::Message::ClassData SearchRequest_Page::_class_data_ = {
+    ::google::protobuf::Message::CopyWithSourceCheck,
+    SearchRequest_Page::MergeImpl
+};
+const ::google::protobuf::Message::ClassData*SearchRequest_Page::GetClassData() const { return &_class_data_; }
+
+
+void SearchRequest_Page::MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg) {
+  auto* const _this = static_cast<SearchRequest_Page*>(&to_msg);
+  auto& from = static_cast<const SearchRequest_Page&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:mint.tulip.v1.SearchRequest.Page)
+  ABSL_DCHECK_NE(&from, _this);
+  ::uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from._internal_index() != 0) {
+    _this->_internal_set_index(from._internal_index());
+  }
+  if (from._internal_size() != 0) {
+    _this->_internal_set_size(from._internal_size());
+  }
+  _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SearchRequest_Page::CopyFrom(const SearchRequest_Page& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mint.tulip.v1.SearchRequest.Page)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+PROTOBUF_NOINLINE bool SearchRequest_Page::IsInitialized() const {
+  return true;
+}
+
+void SearchRequest_Page::InternalSwap(SearchRequest_Page* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::google::protobuf::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.size_)
+      + sizeof(SearchRequest_Page::_impl_.size_)
+      - PROTOBUF_FIELD_OFFSET(SearchRequest_Page, _impl_.index_)>(
+          reinterpret_cast<char*>(&_impl_.index_),
+          reinterpret_cast<char*>(&other->_impl_.index_));
+}
+
+::google::protobuf::Metadata SearchRequest_Page::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_tulip_2eproto_getter, &descriptor_table_tulip_2eproto_once,
+      file_level_metadata_tulip_2eproto[0]);
+}
+// ===================================================================
+
+class SearchRequest::_Internal {
+ public:
+  using HasBits = decltype(std::declval<SearchRequest>()._impl_._has_bits_);
+  static constexpr ::int32_t kHasBitsOffset =
+    8 * PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_._has_bits_);
+  static const ::mint::tulip::v1::SearchRequest_Page& page(const SearchRequest* msg);
+  static void set_has_page(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+};
+
+const ::mint::tulip::v1::SearchRequest_Page& SearchRequest::_Internal::page(const SearchRequest* msg) {
+  return *msg->_impl_.page_;
+}
+SearchRequest::SearchRequest(::google::protobuf::Arena* arena)
+    : ::google::protobuf::Message(arena) {
+  SharedCtor(arena);
+  // @@protoc_insertion_point(arena_constructor:mint.tulip.v1.SearchRequest)
+}
+SearchRequest::SearchRequest(const SearchRequest& from) : ::google::protobuf::Message() {
+  SearchRequest* const _this = this;
+  (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_},
+      /*decltype(_impl_._cached_size_)*/ {},
+      decltype(_impl_.keywords_){from._impl_.keywords_},
+      decltype(_impl_.page_){nullptr},
+      decltype(_impl_.book_){},
+  };
+  _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(
+      from._internal_metadata_);
+  if ((from._impl_._has_bits_[0] & 0x00000001u) != 0) {
+    _this->_impl_.page_ = new ::mint::tulip::v1::SearchRequest_Page(*from._impl_.page_);
+  }
+  _this->_impl_.book_ = from._impl_.book_;
+
+  // @@protoc_insertion_point(copy_constructor:mint.tulip.v1.SearchRequest)
+}
+inline void SearchRequest::SharedCtor(::_pb::Arena* arena) {
+  (void)arena;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){},
+      /*decltype(_impl_._cached_size_)*/ {},
+      decltype(_impl_.keywords_){arena},
+      decltype(_impl_.page_){nullptr},
+      decltype(_impl_.book_){0},
+  };
+}
+SearchRequest::~SearchRequest() {
+  // @@protoc_insertion_point(destructor:mint.tulip.v1.SearchRequest)
+  _internal_metadata_.Delete<::google::protobuf::UnknownFieldSet>();
+  SharedDtor();
+}
+inline void SearchRequest::SharedDtor() {
+  ABSL_DCHECK(GetArenaForAllocation() == nullptr);
+  _internal_mutable_keywords()->~RepeatedPtrField();
+  if (this != internal_default_instance()) delete _impl_.page_;
+}
+void SearchRequest::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+PROTOBUF_NOINLINE void SearchRequest::Clear() {
+// @@protoc_insertion_point(message_clear_start:mint.tulip.v1.SearchRequest)
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _internal_mutable_keywords()->Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    ABSL_DCHECK(_impl_.page_ != nullptr);
+    _impl_.page_->Clear();
+  }
+  _impl_.book_ = 0;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>();
+}
+
+const char* SearchRequest::_InternalParse(
+    const char* ptr, ::_pbi::ParseContext* ctx) {
+  ptr = ::_pbi::TcParser::ParseLoop(this, ptr, ctx, &_table_.header);
+  return ptr;
+}
+
+
+PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+const ::_pbi::TcParseTable<2, 3, 1, 44, 7> SearchRequest::_table_ = {
+  {
+    PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_._has_bits_),
+    0, // no _extensions_
+    99, 24,  // max_field_number, fast_idx_mask
+    offsetof(decltype(_table_), field_lookup_table),
+    4294967292,  // skipmap
+    offsetof(decltype(_table_), field_entries),
+    3,  // num_field_entries
+    1,  // num_aux_entries
+    offsetof(decltype(_table_), aux_entries),
+    &_SearchRequest_default_instance_._instance,
+    ::_pbi::TcParser::GenericFallback,  // fallback
+  }, {{
+    {::_pbi::TcParser::MiniParse, {}},
+    // repeated string keywords = 1;
+    {::_pbi::TcParser::FastUR1,
+     {10, 63, 0, PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.keywords_)}},
+    // int32 book = 2;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchRequest, _impl_.book_), 63>(),
+     {16, 63, 0, PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.book_)}},
+    // optional .mint.tulip.v1.SearchRequest.Page page = 99;
+    {::_pbi::TcParser::FastMtS2,
+     {1690, 0, 0, PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.page_)}},
+  }}, {{
+    99, 0, 1,
+    65534, 2,
+    65535, 65535
+  }}, {{
+    // repeated string keywords = 1;
+    {PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.keywords_), -1, 0,
+    (0 | ::_fl::kFcRepeated | ::_fl::kUtf8String | ::_fl::kRepSString)},
+    // int32 book = 2;
+    {PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.book_), -1, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+    // optional .mint.tulip.v1.SearchRequest.Page page = 99;
+    {PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.page_), _Internal::kHasBitsOffset + 0, 0,
+    (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
+  }}, {{
+    {::_pbi::TcParser::GetTable<::mint::tulip::v1::SearchRequest_Page>()},
+  }}, {{
+    "\33\10\0\0\0\0\0\0"
+    "mint.tulip.v1.SearchRequest"
+    "keywords"
+  }},
+};
+
+::uint8_t* SearchRequest::_InternalSerialize(
+    ::uint8_t* target,
+    ::google::protobuf::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mint.tulip.v1.SearchRequest)
+  ::uint32_t cached_has_bits = 0;
+  (void)cached_has_bits;
+
+  // repeated string keywords = 1;
+  for (int i = 0, n = this->_internal_keywords_size(); i < n; ++i) {
+    const auto& s = this->_internal_keywords().Get(i);
+    ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+        s.data(), static_cast<int>(s.length()), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "mint.tulip.v1.SearchRequest.keywords");
+    target = stream->WriteString(1, s, target);
+  }
+
+  // int32 book = 2;
+  if (this->_internal_book() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<2>(
+            stream, this->_internal_book(), target);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // optional .mint.tulip.v1.SearchRequest.Page page = 99;
+  if (cached_has_bits & 0x00000001u) {
+    target = ::google::protobuf::internal::WireFormatLite::
+      InternalWriteMessage(99, _Internal::page(this),
+        _Internal::page(this).GetCachedSize(), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target =
+        ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+            _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mint.tulip.v1.SearchRequest)
+  return target;
+}
+
+::size_t SearchRequest::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mint.tulip.v1.SearchRequest)
+  ::size_t total_size = 0;
+
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated string keywords = 1;
+  total_size += 1 * ::google::protobuf::internal::FromIntSize(_internal_keywords().size());
+  for (int i = 0, n = _internal_keywords().size(); i < n; ++i) {
+    total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
+        _internal_keywords().Get(i));
+  }
+  // optional .mint.tulip.v1.SearchRequest.Page page = 99;
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    total_size += 2 +
+      ::google::protobuf::internal::WireFormatLite::MessageSize(
+        *_impl_.page_);
+  }
+
+  // int32 book = 2;
+  if (this->_internal_book() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_book());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::google::protobuf::Message::ClassData SearchRequest::_class_data_ = {
+    ::google::protobuf::Message::CopyWithSourceCheck,
+    SearchRequest::MergeImpl
+};
+const ::google::protobuf::Message::ClassData*SearchRequest::GetClassData() const { return &_class_data_; }
+
+
+void SearchRequest::MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg) {
+  auto* const _this = static_cast<SearchRequest*>(&to_msg);
+  auto& from = static_cast<const SearchRequest&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:mint.tulip.v1.SearchRequest)
+  ABSL_DCHECK_NE(&from, _this);
+  ::uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_internal_mutable_keywords()->MergeFrom(from._internal_keywords());
+  if ((from._impl_._has_bits_[0] & 0x00000001u) != 0) {
+    _this->_internal_mutable_page()->::mint::tulip::v1::SearchRequest_Page::MergeFrom(
+        from._internal_page());
+  }
+  if (from._internal_book() != 0) {
+    _this->_internal_set_book(from._internal_book());
+  }
+  _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SearchRequest::CopyFrom(const SearchRequest& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mint.tulip.v1.SearchRequest)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+PROTOBUF_NOINLINE bool SearchRequest::IsInitialized() const {
+  return true;
+}
+
+void SearchRequest::InternalSwap(SearchRequest* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.keywords_.InternalSwap(&other->_impl_.keywords_);
+  ::google::protobuf::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.book_)
+      + sizeof(SearchRequest::_impl_.book_)
+      - PROTOBUF_FIELD_OFFSET(SearchRequest, _impl_.page_)>(
+          reinterpret_cast<char*>(&_impl_.page_),
+          reinterpret_cast<char*>(&other->_impl_.page_));
+}
+
+::google::protobuf::Metadata SearchRequest::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_tulip_2eproto_getter, &descriptor_table_tulip_2eproto_once,
+      file_level_metadata_tulip_2eproto[1]);
+}
+// ===================================================================
+
+class SearchResponse_Item::_Internal {
+ public:
+};
+
+SearchResponse_Item::SearchResponse_Item(::google::protobuf::Arena* arena)
+    : ::google::protobuf::Message(arena) {
+  SharedCtor(arena);
+  // @@protoc_insertion_point(arena_constructor:mint.tulip.v1.SearchResponse.Item)
+}
+SearchResponse_Item::SearchResponse_Item(const SearchResponse_Item& from) : ::google::protobuf::Message() {
+  SearchResponse_Item* const _this = this;
+  (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_.highlight_){},
+      decltype(_impl_.content_){},
+      decltype(_impl_.rank_){},
+      decltype(_impl_.book_){},
+      decltype(_impl_.paragraph_){},
+      /*decltype(_impl_._cached_size_)*/ {},
+  };
+  _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(
+      from._internal_metadata_);
+  _impl_.highlight_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.highlight_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_highlight().empty()) {
+    _this->_impl_.highlight_.Set(from._internal_highlight(), _this->GetArenaForAllocation());
+  }
+  _impl_.content_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.content_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_content().empty()) {
+    _this->_impl_.content_.Set(from._internal_content(), _this->GetArenaForAllocation());
+  }
+  ::memcpy(&_impl_.rank_, &from._impl_.rank_,
+    static_cast<::size_t>(reinterpret_cast<char*>(&_impl_.paragraph_) -
+    reinterpret_cast<char*>(&_impl_.rank_)) + sizeof(_impl_.paragraph_));
+
+  // @@protoc_insertion_point(copy_constructor:mint.tulip.v1.SearchResponse.Item)
+}
+inline void SearchResponse_Item::SharedCtor(::_pb::Arena* arena) {
+  (void)arena;
+  new (&_impl_) Impl_{
+      decltype(_impl_.highlight_){},
+      decltype(_impl_.content_){},
+      decltype(_impl_.rank_){0},
+      decltype(_impl_.book_){0},
+      decltype(_impl_.paragraph_){0},
+      /*decltype(_impl_._cached_size_)*/ {},
+  };
+  _impl_.highlight_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.highlight_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.content_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.content_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+SearchResponse_Item::~SearchResponse_Item() {
+  // @@protoc_insertion_point(destructor:mint.tulip.v1.SearchResponse.Item)
+  _internal_metadata_.Delete<::google::protobuf::UnknownFieldSet>();
+  SharedDtor();
+}
+inline void SearchResponse_Item::SharedDtor() {
+  ABSL_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.highlight_.Destroy();
+  _impl_.content_.Destroy();
+}
+void SearchResponse_Item::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+PROTOBUF_NOINLINE void SearchResponse_Item::Clear() {
+// @@protoc_insertion_point(message_clear_start:mint.tulip.v1.SearchResponse.Item)
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _impl_.highlight_.ClearToEmpty();
+  _impl_.content_.ClearToEmpty();
+  ::memset(&_impl_.rank_, 0, static_cast<::size_t>(
+      reinterpret_cast<char*>(&_impl_.paragraph_) -
+      reinterpret_cast<char*>(&_impl_.rank_)) + sizeof(_impl_.paragraph_));
+  _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>();
+}
+
+const char* SearchResponse_Item::_InternalParse(
+    const char* ptr, ::_pbi::ParseContext* ctx) {
+  ptr = ::_pbi::TcParser::ParseLoop(this, ptr, ctx, &_table_.header);
+  return ptr;
+}
+
+
+PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+const ::_pbi::TcParseTable<3, 5, 0, 58, 2> SearchResponse_Item::_table_ = {
+  {
+    0,  // no _has_bits_
+    0, // no _extensions_
+    5, 56,  // max_field_number, fast_idx_mask
+    offsetof(decltype(_table_), field_lookup_table),
+    4294967264,  // skipmap
+    offsetof(decltype(_table_), field_entries),
+    5,  // num_field_entries
+    0,  // num_aux_entries
+    offsetof(decltype(_table_), field_names),  // no aux_entries
+    &_SearchResponse_Item_default_instance_._instance,
+    ::_pbi::TcParser::GenericFallback,  // fallback
+  }, {{
+    {::_pbi::TcParser::MiniParse, {}},
+    // int32 rank = 1;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchResponse_Item, _impl_.rank_), 63>(),
+     {8, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.rank_)}},
+    // string highlight = 2;
+    {::_pbi::TcParser::FastUS1,
+     {18, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.highlight_)}},
+    // int32 book = 3;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchResponse_Item, _impl_.book_), 63>(),
+     {24, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.book_)}},
+    // int32 paragraph = 4;
+    {::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(SearchResponse_Item, _impl_.paragraph_), 63>(),
+     {32, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.paragraph_)}},
+    // string content = 5;
+    {::_pbi::TcParser::FastUS1,
+     {42, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.content_)}},
+    {::_pbi::TcParser::MiniParse, {}},
+    {::_pbi::TcParser::MiniParse, {}},
+  }}, {{
+    65535, 65535
+  }}, {{
+    // int32 rank = 1;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.rank_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+    // string highlight = 2;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.highlight_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kUtf8String | ::_fl::kRepAString)},
+    // int32 book = 3;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.book_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+    // int32 paragraph = 4;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.paragraph_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+    // string content = 5;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.content_), 0, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kUtf8String | ::_fl::kRepAString)},
+  }},
+  // no aux_entries
+  {{
+    "\41\0\11\0\0\7\0\0"
+    "mint.tulip.v1.SearchResponse.Item"
+    "highlight"
+    "content"
+  }},
+};
+
+::uint8_t* SearchResponse_Item::_InternalSerialize(
+    ::uint8_t* target,
+    ::google::protobuf::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mint.tulip.v1.SearchResponse.Item)
+  ::uint32_t cached_has_bits = 0;
+  (void)cached_has_bits;
+
+  // int32 rank = 1;
+  if (this->_internal_rank() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<1>(
+            stream, this->_internal_rank(), target);
+  }
+
+  // string highlight = 2;
+  if (!this->_internal_highlight().empty()) {
+    const std::string& _s = this->_internal_highlight();
+    ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+        _s.data(), static_cast<int>(_s.length()), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "mint.tulip.v1.SearchResponse.Item.highlight");
+    target = stream->WriteStringMaybeAliased(2, _s, target);
+  }
+
+  // int32 book = 3;
+  if (this->_internal_book() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<3>(
+            stream, this->_internal_book(), target);
+  }
+
+  // int32 paragraph = 4;
+  if (this->_internal_paragraph() != 0) {
+    target = ::google::protobuf::internal::WireFormatLite::
+        WriteInt32ToArrayWithField<4>(
+            stream, this->_internal_paragraph(), target);
+  }
+
+  // string content = 5;
+  if (!this->_internal_content().empty()) {
+    const std::string& _s = this->_internal_content();
+    ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+        _s.data(), static_cast<int>(_s.length()), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "mint.tulip.v1.SearchResponse.Item.content");
+    target = stream->WriteStringMaybeAliased(5, _s, target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target =
+        ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+            _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mint.tulip.v1.SearchResponse.Item)
+  return target;
+}
+
+::size_t SearchResponse_Item::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mint.tulip.v1.SearchResponse.Item)
+  ::size_t total_size = 0;
+
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string highlight = 2;
+  if (!this->_internal_highlight().empty()) {
+    total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize(
+                                    this->_internal_highlight());
+  }
+
+  // string content = 5;
+  if (!this->_internal_content().empty()) {
+    total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize(
+                                    this->_internal_content());
+  }
+
+  // int32 rank = 1;
+  if (this->_internal_rank() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_rank());
+  }
+
+  // int32 book = 3;
+  if (this->_internal_book() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_book());
+  }
+
+  // int32 paragraph = 4;
+  if (this->_internal_paragraph() != 0) {
+    total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
+        this->_internal_paragraph());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::google::protobuf::Message::ClassData SearchResponse_Item::_class_data_ = {
+    ::google::protobuf::Message::CopyWithSourceCheck,
+    SearchResponse_Item::MergeImpl
+};
+const ::google::protobuf::Message::ClassData*SearchResponse_Item::GetClassData() const { return &_class_data_; }
+
+
+void SearchResponse_Item::MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg) {
+  auto* const _this = static_cast<SearchResponse_Item*>(&to_msg);
+  auto& from = static_cast<const SearchResponse_Item&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:mint.tulip.v1.SearchResponse.Item)
+  ABSL_DCHECK_NE(&from, _this);
+  ::uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (!from._internal_highlight().empty()) {
+    _this->_internal_set_highlight(from._internal_highlight());
+  }
+  if (!from._internal_content().empty()) {
+    _this->_internal_set_content(from._internal_content());
+  }
+  if (from._internal_rank() != 0) {
+    _this->_internal_set_rank(from._internal_rank());
+  }
+  if (from._internal_book() != 0) {
+    _this->_internal_set_book(from._internal_book());
+  }
+  if (from._internal_paragraph() != 0) {
+    _this->_internal_set_paragraph(from._internal_paragraph());
+  }
+  _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SearchResponse_Item::CopyFrom(const SearchResponse_Item& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mint.tulip.v1.SearchResponse.Item)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+PROTOBUF_NOINLINE bool SearchResponse_Item::IsInitialized() const {
+  return true;
+}
+
+void SearchResponse_Item::InternalSwap(SearchResponse_Item* other) {
+  using std::swap;
+  auto* lhs_arena = GetArenaForAllocation();
+  auto* rhs_arena = other->GetArenaForAllocation();
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.highlight_, lhs_arena,
+                                       &other->_impl_.highlight_, rhs_arena);
+  ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.content_, lhs_arena,
+                                       &other->_impl_.content_, rhs_arena);
+  ::google::protobuf::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.paragraph_)
+      + sizeof(SearchResponse_Item::_impl_.paragraph_)
+      - PROTOBUF_FIELD_OFFSET(SearchResponse_Item, _impl_.rank_)>(
+          reinterpret_cast<char*>(&_impl_.rank_),
+          reinterpret_cast<char*>(&other->_impl_.rank_));
+}
+
+::google::protobuf::Metadata SearchResponse_Item::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_tulip_2eproto_getter, &descriptor_table_tulip_2eproto_once,
+      file_level_metadata_tulip_2eproto[2]);
+}
+// ===================================================================
+
+class SearchResponse::_Internal {
+ public:
+  using HasBits = decltype(std::declval<SearchResponse>()._impl_._has_bits_);
+  static constexpr ::int32_t kHasBitsOffset =
+    8 * PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_._has_bits_);
+  static const ::mint::tulip::v1::SearchRequest_Page& page(const SearchResponse* msg);
+  static void set_has_page(HasBits* has_bits) {
+    (*has_bits)[0] |= 1u;
+  }
+};
+
+const ::mint::tulip::v1::SearchRequest_Page& SearchResponse::_Internal::page(const SearchResponse* msg) {
+  return *msg->_impl_.page_;
+}
+SearchResponse::SearchResponse(::google::protobuf::Arena* arena)
+    : ::google::protobuf::Message(arena) {
+  SharedCtor(arena);
+  // @@protoc_insertion_point(arena_constructor:mint.tulip.v1.SearchResponse)
+}
+SearchResponse::SearchResponse(const SearchResponse& from) : ::google::protobuf::Message() {
+  SearchResponse* const _this = this;
+  (void)_this;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){from._impl_._has_bits_},
+      /*decltype(_impl_._cached_size_)*/ {},
+      decltype(_impl_.items_){from._impl_.items_},
+      decltype(_impl_.page_){nullptr},
+      decltype(_impl_.total_){},
+  };
+  _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(
+      from._internal_metadata_);
+  if ((from._impl_._has_bits_[0] & 0x00000001u) != 0) {
+    _this->_impl_.page_ = new ::mint::tulip::v1::SearchRequest_Page(*from._impl_.page_);
+  }
+  _this->_impl_.total_ = from._impl_.total_;
+
+  // @@protoc_insertion_point(copy_constructor:mint.tulip.v1.SearchResponse)
+}
+inline void SearchResponse::SharedCtor(::_pb::Arena* arena) {
+  (void)arena;
+  new (&_impl_) Impl_{
+      decltype(_impl_._has_bits_){},
+      /*decltype(_impl_._cached_size_)*/ {},
+      decltype(_impl_.items_){arena},
+      decltype(_impl_.page_){nullptr},
+      decltype(_impl_.total_){0},
+  };
+}
+SearchResponse::~SearchResponse() {
+  // @@protoc_insertion_point(destructor:mint.tulip.v1.SearchResponse)
+  _internal_metadata_.Delete<::google::protobuf::UnknownFieldSet>();
+  SharedDtor();
+}
+inline void SearchResponse::SharedDtor() {
+  ABSL_DCHECK(GetArenaForAllocation() == nullptr);
+  _impl_.items_.~RepeatedPtrField();
+  if (this != internal_default_instance()) delete _impl_.page_;
+}
+void SearchResponse::SetCachedSize(int size) const {
+  _impl_._cached_size_.Set(size);
+}
+
+PROTOBUF_NOINLINE void SearchResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mint.tulip.v1.SearchResponse)
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  _internal_mutable_items()->Clear();
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    ABSL_DCHECK(_impl_.page_ != nullptr);
+    _impl_.page_->Clear();
+  }
+  _impl_.total_ = 0;
+  _impl_._has_bits_.Clear();
+  _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>();
+}
+
+const char* SearchResponse::_InternalParse(
+    const char* ptr, ::_pbi::ParseContext* ctx) {
+  ptr = ::_pbi::TcParser::ParseLoop(this, ptr, ctx, &_table_.header);
+  return ptr;
+}
+
+
+PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
+const ::_pbi::TcParseTable<2, 3, 2, 0, 7> SearchResponse::_table_ = {
+  {
+    PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_._has_bits_),
+    0, // no _extensions_
+    99, 24,  // max_field_number, fast_idx_mask
+    offsetof(decltype(_table_), field_lookup_table),
+    4294967294,  // skipmap
+    offsetof(decltype(_table_), field_entries),
+    3,  // num_field_entries
+    2,  // num_aux_entries
+    offsetof(decltype(_table_), aux_entries),
+    &_SearchResponse_default_instance_._instance,
+    ::_pbi::TcParser::GenericFallback,  // fallback
+  }, {{
+    {::_pbi::TcParser::MiniParse, {}},
+    // repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+    {::_pbi::TcParser::FastMtR1,
+     {10, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.items_)}},
+    // .mint.tulip.v1.SearchRequest.Page page = 98;
+    {::_pbi::TcParser::FastMtS2,
+     {1682, 0, 1, PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.page_)}},
+    // int32 total = 99;
+    {::_pbi::TcParser::FastV32S2,
+     {1688, 63, 0, PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.total_)}},
+  }}, {{
+    98, 0, 1,
+    65532, 1,
+    65535, 65535
+  }}, {{
+    // repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.items_), -1, 0,
+    (0 | ::_fl::kFcRepeated | ::_fl::kMessage | ::_fl::kTvTable)},
+    // .mint.tulip.v1.SearchRequest.Page page = 98;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.page_), _Internal::kHasBitsOffset + 0, 1,
+    (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
+    // int32 total = 99;
+    {PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.total_), -1, 0,
+    (0 | ::_fl::kFcSingular | ::_fl::kInt32)},
+  }}, {{
+    {::_pbi::TcParser::GetTable<::mint::tulip::v1::SearchResponse_Item>()},
+    {::_pbi::TcParser::GetTable<::mint::tulip::v1::SearchRequest_Page>()},
+  }}, {{
+  }},
+};
+
+::uint8_t* SearchResponse::_InternalSerialize(
+    ::uint8_t* target,
+    ::google::protobuf::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:mint.tulip.v1.SearchResponse)
+  ::uint32_t cached_has_bits = 0;
+  (void)cached_has_bits;
+
+  // repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+  for (unsigned i = 0,
+      n = static_cast<unsigned>(this->_internal_items_size()); i < n; i++) {
+    const auto& repfield = this->_internal_items().Get(i);
+    target = ::google::protobuf::internal::WireFormatLite::
+        InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+  }
+
+  cached_has_bits = _impl_._has_bits_[0];
+  // .mint.tulip.v1.SearchRequest.Page page = 98;
+  if (cached_has_bits & 0x00000001u) {
+    target = ::google::protobuf::internal::WireFormatLite::
+      InternalWriteMessage(98, _Internal::page(this),
+        _Internal::page(this).GetCachedSize(), target, stream);
+  }
+
+  // int32 total = 99;
+  if (this->_internal_total() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::_pbi::WireFormatLite::WriteInt32ToArray(
+        99, this->_internal_total(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target =
+        ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
+            _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:mint.tulip.v1.SearchResponse)
+  return target;
+}
+
+::size_t SearchResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mint.tulip.v1.SearchResponse)
+  ::size_t total_size = 0;
+
+  ::uint32_t cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+  total_size += 1UL * this->_internal_items_size();
+  for (const auto& msg : this->_internal_items()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormatLite::MessageSize(msg);
+  }
+  // .mint.tulip.v1.SearchRequest.Page page = 98;
+  cached_has_bits = _impl_._has_bits_[0];
+  if (cached_has_bits & 0x00000001u) {
+    total_size += 2 +
+      ::google::protobuf::internal::WireFormatLite::MessageSize(
+        *_impl_.page_);
+  }
+
+  // int32 total = 99;
+  if (this->_internal_total() != 0) {
+    total_size += 2 + ::_pbi::WireFormatLite::Int32Size(
+                                    this->_internal_total());
+  }
+
+  return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
+}
+
+const ::google::protobuf::Message::ClassData SearchResponse::_class_data_ = {
+    ::google::protobuf::Message::CopyWithSourceCheck,
+    SearchResponse::MergeImpl
+};
+const ::google::protobuf::Message::ClassData*SearchResponse::GetClassData() const { return &_class_data_; }
+
+
+void SearchResponse::MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg) {
+  auto* const _this = static_cast<SearchResponse*>(&to_msg);
+  auto& from = static_cast<const SearchResponse&>(from_msg);
+  // @@protoc_insertion_point(class_specific_merge_from_start:mint.tulip.v1.SearchResponse)
+  ABSL_DCHECK_NE(&from, _this);
+  ::uint32_t cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  _this->_internal_mutable_items()->MergeFrom(from._internal_items());
+  if ((from._impl_._has_bits_[0] & 0x00000001u) != 0) {
+    _this->_internal_mutable_page()->::mint::tulip::v1::SearchRequest_Page::MergeFrom(
+        from._internal_page());
+  }
+  if (from._internal_total() != 0) {
+    _this->_internal_set_total(from._internal_total());
+  }
+  _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_);
+}
+
+void SearchResponse::CopyFrom(const SearchResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mint.tulip.v1.SearchResponse)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+PROTOBUF_NOINLINE bool SearchResponse::IsInitialized() const {
+  return true;
+}
+
+void SearchResponse::InternalSwap(SearchResponse* other) {
+  using std::swap;
+  _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+  swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+  _impl_.items_.InternalSwap(&other->_impl_.items_);
+  ::google::protobuf::internal::memswap<
+      PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.total_)
+      + sizeof(SearchResponse::_impl_.total_)
+      - PROTOBUF_FIELD_OFFSET(SearchResponse, _impl_.page_)>(
+          reinterpret_cast<char*>(&_impl_.page_),
+          reinterpret_cast<char*>(&other->_impl_.page_));
+}
+
+::google::protobuf::Metadata SearchResponse::GetMetadata() const {
+  return ::_pbi::AssignDescriptors(
+      &descriptor_table_tulip_2eproto_getter, &descriptor_table_tulip_2eproto_once,
+      file_level_metadata_tulip_2eproto[3]);
+}
+// @@protoc_insertion_point(namespace_scope)
+}  // namespace v1
+}  // namespace tulip
+}  // namespace mint
+namespace google {
+namespace protobuf {
+}  // namespace protobuf
+}  // namespace google
+// @@protoc_insertion_point(global_scope)
+#include "google/protobuf/port_undef.inc"

+ 1514 - 0
rpc/sdk/cpp/tulip.pb.h

@@ -0,0 +1,1514 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_tulip_2eproto_2epb_2eh
+#define GOOGLE_PROTOBUF_INCLUDED_tulip_2eproto_2epb_2eh
+
+#include <limits>
+#include <string>
+#include <type_traits>
+
+#include "google/protobuf/port_def.inc"
+#if PROTOBUF_VERSION < 4024000
+#error "This file was generated by a newer version of protoc which is"
+#error "incompatible with your Protocol Buffer headers. Please update"
+#error "your headers."
+#endif  // PROTOBUF_VERSION
+
+#if 4024003 < PROTOBUF_MIN_PROTOC_VERSION
+#error "This file was generated by an older version of protoc which is"
+#error "incompatible with your Protocol Buffer headers. Please"
+#error "regenerate this file with a newer version of protoc."
+#endif  // PROTOBUF_MIN_PROTOC_VERSION
+#include "google/protobuf/port_undef.inc"
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/arena.h"
+#include "google/protobuf/arenastring.h"
+#include "google/protobuf/generated_message_tctable_decl.h"
+#include "google/protobuf/generated_message_util.h"
+#include "google/protobuf/metadata_lite.h"
+#include "google/protobuf/generated_message_reflection.h"
+#include "google/protobuf/message.h"
+#include "google/protobuf/repeated_field.h"  // IWYU pragma: export
+#include "google/protobuf/extension_set.h"  // IWYU pragma: export
+#include "google/protobuf/unknown_field_set.h"
+// @@protoc_insertion_point(includes)
+
+// Must be included last.
+#include "google/protobuf/port_def.inc"
+
+#define PROTOBUF_INTERNAL_EXPORT_tulip_2eproto
+
+namespace google {
+namespace protobuf {
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_tulip_2eproto {
+  static const ::uint32_t offsets[];
+};
+extern const ::google::protobuf::internal::DescriptorTable
+    descriptor_table_tulip_2eproto;
+namespace mint {
+namespace tulip {
+namespace v1 {
+class SearchRequest;
+struct SearchRequestDefaultTypeInternal;
+extern SearchRequestDefaultTypeInternal _SearchRequest_default_instance_;
+class SearchRequest_Page;
+struct SearchRequest_PageDefaultTypeInternal;
+extern SearchRequest_PageDefaultTypeInternal _SearchRequest_Page_default_instance_;
+class SearchResponse;
+struct SearchResponseDefaultTypeInternal;
+extern SearchResponseDefaultTypeInternal _SearchResponse_default_instance_;
+class SearchResponse_Item;
+struct SearchResponse_ItemDefaultTypeInternal;
+extern SearchResponse_ItemDefaultTypeInternal _SearchResponse_Item_default_instance_;
+}  // namespace v1
+}  // namespace tulip
+}  // namespace mint
+namespace google {
+namespace protobuf {
+}  // namespace protobuf
+}  // namespace google
+
+namespace mint {
+namespace tulip {
+namespace v1 {
+
+// ===================================================================
+
+
+// -------------------------------------------------------------------
+
+class SearchRequest_Page final :
+    public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:mint.tulip.v1.SearchRequest.Page) */ {
+ public:
+  inline SearchRequest_Page() : SearchRequest_Page(nullptr) {}
+  ~SearchRequest_Page() override;
+  template<typename = void>
+  explicit PROTOBUF_CONSTEXPR SearchRequest_Page(::google::protobuf::internal::ConstantInitialized);
+
+  SearchRequest_Page(const SearchRequest_Page& from);
+  SearchRequest_Page(SearchRequest_Page&& from) noexcept
+    : SearchRequest_Page() {
+    *this = ::std::move(from);
+  }
+
+  inline SearchRequest_Page& operator=(const SearchRequest_Page& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SearchRequest_Page& operator=(SearchRequest_Page&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance);
+  }
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>();
+  }
+
+  static const ::google::protobuf::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::google::protobuf::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::google::protobuf::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SearchRequest_Page& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SearchRequest_Page* internal_default_instance() {
+    return reinterpret_cast<const SearchRequest_Page*>(
+               &_SearchRequest_Page_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(SearchRequest_Page& a, SearchRequest_Page& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SearchRequest_Page* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::google::protobuf::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SearchRequest_Page* other) {
+    if (other == this) return;
+    ABSL_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SearchRequest_Page* New(::google::protobuf::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SearchRequest_Page>(arena);
+  }
+  using ::google::protobuf::Message::CopyFrom;
+  void CopyFrom(const SearchRequest_Page& from);
+  using ::google::protobuf::Message::MergeFrom;
+  void MergeFrom( const SearchRequest_Page& from) {
+    SearchRequest_Page::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  ::size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::google::protobuf::internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* target, ::google::protobuf::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::google::protobuf::Arena* arena);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SearchRequest_Page* other);
+
+  private:
+  friend class ::google::protobuf::internal::AnyMetadata;
+  static ::absl::string_view FullMessageName() {
+    return "mint.tulip.v1.SearchRequest.Page";
+  }
+  protected:
+  explicit SearchRequest_Page(::google::protobuf::Arena* arena);
+  public:
+
+  static const ClassData _class_data_;
+  const ::google::protobuf::Message::ClassData*GetClassData() const final;
+
+  ::google::protobuf::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kIndexFieldNumber = 1,
+    kSizeFieldNumber = 2,
+  };
+  // int32 index = 1;
+  void clear_index() ;
+  ::int32_t index() const;
+  void set_index(::int32_t value);
+
+  private:
+  ::int32_t _internal_index() const;
+  void _internal_set_index(::int32_t value);
+
+  public:
+  // int32 size = 2;
+  void clear_size() ;
+  ::int32_t size() const;
+  void set_size(::int32_t value);
+
+  private:
+  ::int32_t _internal_size() const;
+  void _internal_set_size(::int32_t value);
+
+  public:
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchRequest.Page)
+ private:
+  class _Internal;
+
+  friend class ::google::protobuf::internal::TcParser;
+  static const ::google::protobuf::internal::TcParseTable<1, 2, 0, 0, 2> _table_;
+  template <typename T> friend class ::google::protobuf::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::int32_t index_;
+    ::int32_t size_;
+    mutable ::google::protobuf::internal::CachedSize _cached_size_;
+    PROTOBUF_TSAN_DECLARE_MEMBER
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_tulip_2eproto;
+};// -------------------------------------------------------------------
+
+class SearchRequest final :
+    public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:mint.tulip.v1.SearchRequest) */ {
+ public:
+  inline SearchRequest() : SearchRequest(nullptr) {}
+  ~SearchRequest() override;
+  template<typename = void>
+  explicit PROTOBUF_CONSTEXPR SearchRequest(::google::protobuf::internal::ConstantInitialized);
+
+  SearchRequest(const SearchRequest& from);
+  SearchRequest(SearchRequest&& from) noexcept
+    : SearchRequest() {
+    *this = ::std::move(from);
+  }
+
+  inline SearchRequest& operator=(const SearchRequest& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SearchRequest& operator=(SearchRequest&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance);
+  }
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>();
+  }
+
+  static const ::google::protobuf::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::google::protobuf::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::google::protobuf::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SearchRequest& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SearchRequest* internal_default_instance() {
+    return reinterpret_cast<const SearchRequest*>(
+               &_SearchRequest_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(SearchRequest& a, SearchRequest& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SearchRequest* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::google::protobuf::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SearchRequest* other) {
+    if (other == this) return;
+    ABSL_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SearchRequest* New(::google::protobuf::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SearchRequest>(arena);
+  }
+  using ::google::protobuf::Message::CopyFrom;
+  void CopyFrom(const SearchRequest& from);
+  using ::google::protobuf::Message::MergeFrom;
+  void MergeFrom( const SearchRequest& from) {
+    SearchRequest::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  ::size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::google::protobuf::internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* target, ::google::protobuf::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::google::protobuf::Arena* arena);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SearchRequest* other);
+
+  private:
+  friend class ::google::protobuf::internal::AnyMetadata;
+  static ::absl::string_view FullMessageName() {
+    return "mint.tulip.v1.SearchRequest";
+  }
+  protected:
+  explicit SearchRequest(::google::protobuf::Arena* arena);
+  public:
+
+  static const ClassData _class_data_;
+  const ::google::protobuf::Message::ClassData*GetClassData() const final;
+
+  ::google::protobuf::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef SearchRequest_Page Page;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kKeywordsFieldNumber = 1,
+    kPageFieldNumber = 99,
+    kBookFieldNumber = 2,
+  };
+  // repeated string keywords = 1;
+  int keywords_size() const;
+  private:
+  int _internal_keywords_size() const;
+
+  public:
+  void clear_keywords() ;
+  const std::string& keywords(int index) const;
+  std::string* mutable_keywords(int index);
+  void set_keywords(int index, const std::string& value);
+  void set_keywords(int index, std::string&& value);
+  void set_keywords(int index, const char* value);
+  void set_keywords(int index, const char* value, std::size_t size);
+  void set_keywords(int index, absl::string_view value);
+  std::string* add_keywords();
+  void add_keywords(const std::string& value);
+  void add_keywords(std::string&& value);
+  void add_keywords(const char* value);
+  void add_keywords(const char* value, std::size_t size);
+  void add_keywords(absl::string_view value);
+  const ::google::protobuf::RepeatedPtrField<std::string>& keywords() const;
+  ::google::protobuf::RepeatedPtrField<std::string>* mutable_keywords();
+
+  private:
+  const ::google::protobuf::RepeatedPtrField<std::string>& _internal_keywords() const;
+  ::google::protobuf::RepeatedPtrField<std::string>* _internal_mutable_keywords();
+
+  public:
+  // optional .mint.tulip.v1.SearchRequest.Page page = 99;
+  bool has_page() const;
+  void clear_page() ;
+  const ::mint::tulip::v1::SearchRequest_Page& page() const;
+  PROTOBUF_NODISCARD ::mint::tulip::v1::SearchRequest_Page* release_page();
+  ::mint::tulip::v1::SearchRequest_Page* mutable_page();
+  void set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value);
+  void unsafe_arena_set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value);
+  ::mint::tulip::v1::SearchRequest_Page* unsafe_arena_release_page();
+
+  private:
+  const ::mint::tulip::v1::SearchRequest_Page& _internal_page() const;
+  ::mint::tulip::v1::SearchRequest_Page* _internal_mutable_page();
+
+  public:
+  // int32 book = 2;
+  void clear_book() ;
+  ::int32_t book() const;
+  void set_book(::int32_t value);
+
+  private:
+  ::int32_t _internal_book() const;
+  void _internal_set_book(::int32_t value);
+
+  public:
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchRequest)
+ private:
+  class _Internal;
+
+  friend class ::google::protobuf::internal::TcParser;
+  static const ::google::protobuf::internal::TcParseTable<2, 3, 1, 44, 7> _table_;
+  template <typename T> friend class ::google::protobuf::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::google::protobuf::internal::HasBits<1> _has_bits_;
+    mutable ::google::protobuf::internal::CachedSize _cached_size_;
+    ::google::protobuf::RepeatedPtrField<std::string> keywords_;
+    ::mint::tulip::v1::SearchRequest_Page* page_;
+    ::int32_t book_;
+    PROTOBUF_TSAN_DECLARE_MEMBER
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_tulip_2eproto;
+};// -------------------------------------------------------------------
+
+class SearchResponse_Item final :
+    public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:mint.tulip.v1.SearchResponse.Item) */ {
+ public:
+  inline SearchResponse_Item() : SearchResponse_Item(nullptr) {}
+  ~SearchResponse_Item() override;
+  template<typename = void>
+  explicit PROTOBUF_CONSTEXPR SearchResponse_Item(::google::protobuf::internal::ConstantInitialized);
+
+  SearchResponse_Item(const SearchResponse_Item& from);
+  SearchResponse_Item(SearchResponse_Item&& from) noexcept
+    : SearchResponse_Item() {
+    *this = ::std::move(from);
+  }
+
+  inline SearchResponse_Item& operator=(const SearchResponse_Item& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SearchResponse_Item& operator=(SearchResponse_Item&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance);
+  }
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>();
+  }
+
+  static const ::google::protobuf::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::google::protobuf::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::google::protobuf::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SearchResponse_Item& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SearchResponse_Item* internal_default_instance() {
+    return reinterpret_cast<const SearchResponse_Item*>(
+               &_SearchResponse_Item_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(SearchResponse_Item& a, SearchResponse_Item& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SearchResponse_Item* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::google::protobuf::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SearchResponse_Item* other) {
+    if (other == this) return;
+    ABSL_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SearchResponse_Item* New(::google::protobuf::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SearchResponse_Item>(arena);
+  }
+  using ::google::protobuf::Message::CopyFrom;
+  void CopyFrom(const SearchResponse_Item& from);
+  using ::google::protobuf::Message::MergeFrom;
+  void MergeFrom( const SearchResponse_Item& from) {
+    SearchResponse_Item::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  ::size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::google::protobuf::internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* target, ::google::protobuf::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::google::protobuf::Arena* arena);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SearchResponse_Item* other);
+
+  private:
+  friend class ::google::protobuf::internal::AnyMetadata;
+  static ::absl::string_view FullMessageName() {
+    return "mint.tulip.v1.SearchResponse.Item";
+  }
+  protected:
+  explicit SearchResponse_Item(::google::protobuf::Arena* arena);
+  public:
+
+  static const ClassData _class_data_;
+  const ::google::protobuf::Message::ClassData*GetClassData() const final;
+
+  ::google::protobuf::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kHighlightFieldNumber = 2,
+    kContentFieldNumber = 5,
+    kRankFieldNumber = 1,
+    kBookFieldNumber = 3,
+    kParagraphFieldNumber = 4,
+  };
+  // string highlight = 2;
+  void clear_highlight() ;
+  const std::string& highlight() const;
+  template <typename Arg_ = const std::string&, typename... Args_>
+  void set_highlight(Arg_&& arg, Args_... args);
+  std::string* mutable_highlight();
+  PROTOBUF_NODISCARD std::string* release_highlight();
+  void set_allocated_highlight(std::string* ptr);
+
+  private:
+  const std::string& _internal_highlight() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_highlight(
+      const std::string& value);
+  std::string* _internal_mutable_highlight();
+
+  public:
+  // string content = 5;
+  void clear_content() ;
+  const std::string& content() const;
+  template <typename Arg_ = const std::string&, typename... Args_>
+  void set_content(Arg_&& arg, Args_... args);
+  std::string* mutable_content();
+  PROTOBUF_NODISCARD std::string* release_content();
+  void set_allocated_content(std::string* ptr);
+
+  private:
+  const std::string& _internal_content() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_content(
+      const std::string& value);
+  std::string* _internal_mutable_content();
+
+  public:
+  // int32 rank = 1;
+  void clear_rank() ;
+  ::int32_t rank() const;
+  void set_rank(::int32_t value);
+
+  private:
+  ::int32_t _internal_rank() const;
+  void _internal_set_rank(::int32_t value);
+
+  public:
+  // int32 book = 3;
+  void clear_book() ;
+  ::int32_t book() const;
+  void set_book(::int32_t value);
+
+  private:
+  ::int32_t _internal_book() const;
+  void _internal_set_book(::int32_t value);
+
+  public:
+  // int32 paragraph = 4;
+  void clear_paragraph() ;
+  ::int32_t paragraph() const;
+  void set_paragraph(::int32_t value);
+
+  private:
+  ::int32_t _internal_paragraph() const;
+  void _internal_set_paragraph(::int32_t value);
+
+  public:
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchResponse.Item)
+ private:
+  class _Internal;
+
+  friend class ::google::protobuf::internal::TcParser;
+  static const ::google::protobuf::internal::TcParseTable<3, 5, 0, 58, 2> _table_;
+  template <typename T> friend class ::google::protobuf::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::google::protobuf::internal::ArenaStringPtr highlight_;
+    ::google::protobuf::internal::ArenaStringPtr content_;
+    ::int32_t rank_;
+    ::int32_t book_;
+    ::int32_t paragraph_;
+    mutable ::google::protobuf::internal::CachedSize _cached_size_;
+    PROTOBUF_TSAN_DECLARE_MEMBER
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_tulip_2eproto;
+};// -------------------------------------------------------------------
+
+class SearchResponse final :
+    public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:mint.tulip.v1.SearchResponse) */ {
+ public:
+  inline SearchResponse() : SearchResponse(nullptr) {}
+  ~SearchResponse() override;
+  template<typename = void>
+  explicit PROTOBUF_CONSTEXPR SearchResponse(::google::protobuf::internal::ConstantInitialized);
+
+  SearchResponse(const SearchResponse& from);
+  SearchResponse(SearchResponse&& from) noexcept
+    : SearchResponse() {
+    *this = ::std::move(from);
+  }
+
+  inline SearchResponse& operator=(const SearchResponse& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline SearchResponse& operator=(SearchResponse&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance);
+  }
+  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>();
+  }
+
+  static const ::google::protobuf::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::google::protobuf::Descriptor* GetDescriptor() {
+    return default_instance().GetMetadata().descriptor;
+  }
+  static const ::google::protobuf::Reflection* GetReflection() {
+    return default_instance().GetMetadata().reflection;
+  }
+  static const SearchResponse& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const SearchResponse* internal_default_instance() {
+    return reinterpret_cast<const SearchResponse*>(
+               &_SearchResponse_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    3;
+
+  friend void swap(SearchResponse& a, SearchResponse& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(SearchResponse* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::google::protobuf::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(SearchResponse* other) {
+    if (other == this) return;
+    ABSL_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  SearchResponse* New(::google::protobuf::Arena* arena = nullptr) const final {
+    return CreateMaybeMessage<SearchResponse>(arena);
+  }
+  using ::google::protobuf::Message::CopyFrom;
+  void CopyFrom(const SearchResponse& from);
+  using ::google::protobuf::Message::MergeFrom;
+  void MergeFrom( const SearchResponse& from) {
+    SearchResponse::MergeImpl(*this, from);
+  }
+  private:
+  static void MergeImpl(::google::protobuf::Message& to_msg, const ::google::protobuf::Message& from_msg);
+  public:
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  ::size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::google::protobuf::internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* target, ::google::protobuf::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::google::protobuf::Arena* arena);
+  void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(SearchResponse* other);
+
+  private:
+  friend class ::google::protobuf::internal::AnyMetadata;
+  static ::absl::string_view FullMessageName() {
+    return "mint.tulip.v1.SearchResponse";
+  }
+  protected:
+  explicit SearchResponse(::google::protobuf::Arena* arena);
+  public:
+
+  static const ClassData _class_data_;
+  const ::google::protobuf::Message::ClassData*GetClassData() const final;
+
+  ::google::protobuf::Metadata GetMetadata() const final;
+
+  // nested types ----------------------------------------------------
+
+  typedef SearchResponse_Item Item;
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kItemsFieldNumber = 1,
+    kPageFieldNumber = 98,
+    kTotalFieldNumber = 99,
+  };
+  // repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+  int items_size() const;
+  private:
+  int _internal_items_size() const;
+
+  public:
+  void clear_items() ;
+  ::mint::tulip::v1::SearchResponse_Item* mutable_items(int index);
+  ::google::protobuf::RepeatedPtrField< ::mint::tulip::v1::SearchResponse_Item >*
+      mutable_items();
+  private:
+  const ::google::protobuf::RepeatedPtrField<::mint::tulip::v1::SearchResponse_Item>& _internal_items() const;
+  ::google::protobuf::RepeatedPtrField<::mint::tulip::v1::SearchResponse_Item>* _internal_mutable_items();
+  public:
+  const ::mint::tulip::v1::SearchResponse_Item& items(int index) const;
+  ::mint::tulip::v1::SearchResponse_Item* add_items();
+  const ::google::protobuf::RepeatedPtrField< ::mint::tulip::v1::SearchResponse_Item >&
+      items() const;
+  // .mint.tulip.v1.SearchRequest.Page page = 98;
+  bool has_page() const;
+  void clear_page() ;
+  const ::mint::tulip::v1::SearchRequest_Page& page() const;
+  PROTOBUF_NODISCARD ::mint::tulip::v1::SearchRequest_Page* release_page();
+  ::mint::tulip::v1::SearchRequest_Page* mutable_page();
+  void set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value);
+  void unsafe_arena_set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value);
+  ::mint::tulip::v1::SearchRequest_Page* unsafe_arena_release_page();
+
+  private:
+  const ::mint::tulip::v1::SearchRequest_Page& _internal_page() const;
+  ::mint::tulip::v1::SearchRequest_Page* _internal_mutable_page();
+
+  public:
+  // int32 total = 99;
+  void clear_total() ;
+  ::int32_t total() const;
+  void set_total(::int32_t value);
+
+  private:
+  ::int32_t _internal_total() const;
+  void _internal_set_total(::int32_t value);
+
+  public:
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchResponse)
+ private:
+  class _Internal;
+
+  friend class ::google::protobuf::internal::TcParser;
+  static const ::google::protobuf::internal::TcParseTable<2, 3, 2, 0, 7> _table_;
+  template <typename T> friend class ::google::protobuf::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::google::protobuf::internal::HasBits<1> _has_bits_;
+    mutable ::google::protobuf::internal::CachedSize _cached_size_;
+    ::google::protobuf::RepeatedPtrField< ::mint::tulip::v1::SearchResponse_Item > items_;
+    ::mint::tulip::v1::SearchRequest_Page* page_;
+    ::int32_t total_;
+    PROTOBUF_TSAN_DECLARE_MEMBER
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_tulip_2eproto;
+};
+
+// ===================================================================
+
+
+
+
+// ===================================================================
+
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// SearchRequest_Page
+
+// int32 index = 1;
+inline void SearchRequest_Page::clear_index() {
+  _impl_.index_ = 0;
+}
+inline ::int32_t SearchRequest_Page::index() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchRequest.Page.index)
+  return _internal_index();
+}
+inline void SearchRequest_Page::set_index(::int32_t value) {
+  _internal_set_index(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchRequest.Page.index)
+}
+inline ::int32_t SearchRequest_Page::_internal_index() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.index_;
+}
+inline void SearchRequest_Page::_internal_set_index(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.index_ = value;
+}
+
+// int32 size = 2;
+inline void SearchRequest_Page::clear_size() {
+  _impl_.size_ = 0;
+}
+inline ::int32_t SearchRequest_Page::size() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchRequest.Page.size)
+  return _internal_size();
+}
+inline void SearchRequest_Page::set_size(::int32_t value) {
+  _internal_set_size(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchRequest.Page.size)
+}
+inline ::int32_t SearchRequest_Page::_internal_size() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.size_;
+}
+inline void SearchRequest_Page::_internal_set_size(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.size_ = value;
+}
+
+// -------------------------------------------------------------------
+
+// SearchRequest
+
+// repeated string keywords = 1;
+inline int SearchRequest::_internal_keywords_size() const {
+  return _internal_keywords().size();
+}
+inline int SearchRequest::keywords_size() const {
+  return _internal_keywords_size();
+}
+inline void SearchRequest::clear_keywords() {
+  _internal_mutable_keywords()->Clear();
+}
+inline std::string* SearchRequest::add_keywords() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  std::string* _s = _internal_mutable_keywords()->Add();
+  // @@protoc_insertion_point(field_add_mutable:mint.tulip.v1.SearchRequest.keywords)
+  return _s;
+}
+inline const std::string& SearchRequest::keywords(int index) const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchRequest.keywords)
+  return _internal_keywords().Get(index);
+}
+inline std::string* SearchRequest::mutable_keywords(int index) {
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchRequest.keywords)
+  return _internal_mutable_keywords()->Mutable(index);
+}
+inline void SearchRequest::set_keywords(int index, const std::string& value) {
+  _internal_mutable_keywords()->Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::set_keywords(int index, std::string&& value) {
+  _internal_mutable_keywords()->Mutable(index)->assign(std::move(value));
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::set_keywords(int index, const char* value) {
+  ABSL_DCHECK(value != nullptr);
+  _internal_mutable_keywords()->Mutable(index)->assign(value);
+  // @@protoc_insertion_point(field_set_char:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::set_keywords(int index, const char* value,
+                              std::size_t size) {
+  _internal_mutable_keywords()->Mutable(index)->assign(
+      reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_set_pointer:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::set_keywords(int index, absl::string_view value) {
+  _internal_mutable_keywords()->Mutable(index)->assign(value.data(),
+                                                     value.size());
+  // @@protoc_insertion_point(field_set_string_piece:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::add_keywords(const std::string& value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _internal_mutable_keywords()->Add()->assign(value);
+  // @@protoc_insertion_point(field_add:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::add_keywords(std::string&& value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _internal_mutable_keywords()->Add(std::move(value));
+  // @@protoc_insertion_point(field_add:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::add_keywords(const char* value) {
+  ABSL_DCHECK(value != nullptr);
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _internal_mutable_keywords()->Add()->assign(value);
+  // @@protoc_insertion_point(field_add_char:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::add_keywords(const char* value, std::size_t size) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _internal_mutable_keywords()->Add()->assign(
+      reinterpret_cast<const char*>(value), size);
+  // @@protoc_insertion_point(field_add_pointer:mint.tulip.v1.SearchRequest.keywords)
+}
+inline void SearchRequest::add_keywords(absl::string_view value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _internal_mutable_keywords()->Add()->assign(value.data(), value.size());
+  // @@protoc_insertion_point(field_add_string_piece:mint.tulip.v1.SearchRequest.keywords)
+}
+inline const ::google::protobuf::RepeatedPtrField<std::string>&
+SearchRequest::keywords() const {
+  // @@protoc_insertion_point(field_list:mint.tulip.v1.SearchRequest.keywords)
+  return _internal_keywords();
+}
+inline ::google::protobuf::RepeatedPtrField<std::string>* SearchRequest::mutable_keywords() {
+  // @@protoc_insertion_point(field_mutable_list:mint.tulip.v1.SearchRequest.keywords)
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  return _internal_mutable_keywords();
+}
+inline const ::google::protobuf::RepeatedPtrField<std::string>&
+SearchRequest::_internal_keywords() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.keywords_;
+}
+inline ::google::protobuf::RepeatedPtrField<std::string>*
+SearchRequest::_internal_mutable_keywords() {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return &_impl_.keywords_;
+}
+
+// int32 book = 2;
+inline void SearchRequest::clear_book() {
+  _impl_.book_ = 0;
+}
+inline ::int32_t SearchRequest::book() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchRequest.book)
+  return _internal_book();
+}
+inline void SearchRequest::set_book(::int32_t value) {
+  _internal_set_book(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchRequest.book)
+}
+inline ::int32_t SearchRequest::_internal_book() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.book_;
+}
+inline void SearchRequest::_internal_set_book(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.book_ = value;
+}
+
+// optional .mint.tulip.v1.SearchRequest.Page page = 99;
+inline bool SearchRequest::has_page() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.page_ != nullptr);
+  return value;
+}
+inline void SearchRequest::clear_page() {
+  if (_impl_.page_ != nullptr) _impl_.page_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mint::tulip::v1::SearchRequest_Page& SearchRequest::_internal_page() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  const ::mint::tulip::v1::SearchRequest_Page* p = _impl_.page_;
+  return p != nullptr ? *p : reinterpret_cast<const ::mint::tulip::v1::SearchRequest_Page&>(::mint::tulip::v1::_SearchRequest_Page_default_instance_);
+}
+inline const ::mint::tulip::v1::SearchRequest_Page& SearchRequest::page() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchRequest.page)
+  return _internal_page();
+}
+inline void SearchRequest::unsafe_arena_set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::google::protobuf::MessageLite*>(_impl_.page_);
+  }
+  _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value);
+  if (value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mint.tulip.v1.SearchRequest.page)
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchRequest::release_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::mint::tulip::v1::SearchRequest_Page* released = _impl_.page_;
+  _impl_.page_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old = reinterpret_cast<::google::protobuf::MessageLite*>(released);
+  released = ::google::protobuf::internal::DuplicateIfNonNull(released);
+  if (GetArenaForAllocation() == nullptr) {
+    delete old;
+  }
+#else   // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    released = ::google::protobuf::internal::DuplicateIfNonNull(released);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return released;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchRequest::unsafe_arena_release_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  // @@protoc_insertion_point(field_release:mint.tulip.v1.SearchRequest.page)
+
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::mint::tulip::v1::SearchRequest_Page* temp = _impl_.page_;
+  _impl_.page_ = nullptr;
+  return temp;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchRequest::_internal_mutable_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _impl_._has_bits_[0] |= 0x00000001u;
+  if (_impl_.page_ == nullptr) {
+    auto* p = CreateMaybeMessage<::mint::tulip::v1::SearchRequest_Page>(GetArenaForAllocation());
+    _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(p);
+  }
+  return _impl_.page_;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchRequest::mutable_page() {
+  ::mint::tulip::v1::SearchRequest_Page* _msg = _internal_mutable_page();
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchRequest.page)
+  return _msg;
+}
+inline void SearchRequest::set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value) {
+  ::google::protobuf::Arena* message_arena = GetArenaForAllocation();
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  if (message_arena == nullptr) {
+    delete reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(_impl_.page_);
+  }
+
+  if (value != nullptr) {
+    ::google::protobuf::Arena* submessage_arena =
+        ::google::protobuf::Arena::InternalGetOwningArena(reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value));
+    if (message_arena != submessage_arena) {
+      value = ::google::protobuf::internal::GetOwnedMessage(message_arena, value, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+
+  _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value);
+  // @@protoc_insertion_point(field_set_allocated:mint.tulip.v1.SearchRequest.page)
+}
+
+// -------------------------------------------------------------------
+
+// SearchResponse_Item
+
+// int32 rank = 1;
+inline void SearchResponse_Item::clear_rank() {
+  _impl_.rank_ = 0;
+}
+inline ::int32_t SearchResponse_Item::rank() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.Item.rank)
+  return _internal_rank();
+}
+inline void SearchResponse_Item::set_rank(::int32_t value) {
+  _internal_set_rank(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.Item.rank)
+}
+inline ::int32_t SearchResponse_Item::_internal_rank() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.rank_;
+}
+inline void SearchResponse_Item::_internal_set_rank(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.rank_ = value;
+}
+
+// string highlight = 2;
+inline void SearchResponse_Item::clear_highlight() {
+  _impl_.highlight_.ClearToEmpty();
+}
+inline const std::string& SearchResponse_Item::highlight() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.Item.highlight)
+  return _internal_highlight();
+}
+template <typename Arg_, typename... Args_>
+inline PROTOBUF_ALWAYS_INLINE void SearchResponse_Item::set_highlight(Arg_&& arg,
+                                                     Args_... args) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.highlight_.Set(static_cast<Arg_&&>(arg), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.Item.highlight)
+}
+inline std::string* SearchResponse_Item::mutable_highlight() {
+  std::string* _s = _internal_mutable_highlight();
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchResponse.Item.highlight)
+  return _s;
+}
+inline const std::string& SearchResponse_Item::_internal_highlight() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.highlight_.Get();
+}
+inline void SearchResponse_Item::_internal_set_highlight(const std::string& value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.highlight_.Set(value, GetArenaForAllocation());
+}
+inline std::string* SearchResponse_Item::_internal_mutable_highlight() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  return _impl_.highlight_.Mutable( GetArenaForAllocation());
+}
+inline std::string* SearchResponse_Item::release_highlight() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  // @@protoc_insertion_point(field_release:mint.tulip.v1.SearchResponse.Item.highlight)
+  return _impl_.highlight_.Release();
+}
+inline void SearchResponse_Item::set_allocated_highlight(std::string* value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _impl_.highlight_.SetAllocated(value, GetArenaForAllocation());
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        if (_impl_.highlight_.IsDefault()) {
+          _impl_.highlight_.Set("", GetArenaForAllocation());
+        }
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:mint.tulip.v1.SearchResponse.Item.highlight)
+}
+
+// int32 book = 3;
+inline void SearchResponse_Item::clear_book() {
+  _impl_.book_ = 0;
+}
+inline ::int32_t SearchResponse_Item::book() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.Item.book)
+  return _internal_book();
+}
+inline void SearchResponse_Item::set_book(::int32_t value) {
+  _internal_set_book(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.Item.book)
+}
+inline ::int32_t SearchResponse_Item::_internal_book() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.book_;
+}
+inline void SearchResponse_Item::_internal_set_book(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.book_ = value;
+}
+
+// int32 paragraph = 4;
+inline void SearchResponse_Item::clear_paragraph() {
+  _impl_.paragraph_ = 0;
+}
+inline ::int32_t SearchResponse_Item::paragraph() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.Item.paragraph)
+  return _internal_paragraph();
+}
+inline void SearchResponse_Item::set_paragraph(::int32_t value) {
+  _internal_set_paragraph(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.Item.paragraph)
+}
+inline ::int32_t SearchResponse_Item::_internal_paragraph() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.paragraph_;
+}
+inline void SearchResponse_Item::_internal_set_paragraph(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.paragraph_ = value;
+}
+
+// string content = 5;
+inline void SearchResponse_Item::clear_content() {
+  _impl_.content_.ClearToEmpty();
+}
+inline const std::string& SearchResponse_Item::content() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.Item.content)
+  return _internal_content();
+}
+template <typename Arg_, typename... Args_>
+inline PROTOBUF_ALWAYS_INLINE void SearchResponse_Item::set_content(Arg_&& arg,
+                                                     Args_... args) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.content_.Set(static_cast<Arg_&&>(arg), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.Item.content)
+}
+inline std::string* SearchResponse_Item::mutable_content() {
+  std::string* _s = _internal_mutable_content();
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchResponse.Item.content)
+  return _s;
+}
+inline const std::string& SearchResponse_Item::_internal_content() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.content_.Get();
+}
+inline void SearchResponse_Item::_internal_set_content(const std::string& value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.content_.Set(value, GetArenaForAllocation());
+}
+inline std::string* SearchResponse_Item::_internal_mutable_content() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  return _impl_.content_.Mutable( GetArenaForAllocation());
+}
+inline std::string* SearchResponse_Item::release_content() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  // @@protoc_insertion_point(field_release:mint.tulip.v1.SearchResponse.Item.content)
+  return _impl_.content_.Release();
+}
+inline void SearchResponse_Item::set_allocated_content(std::string* value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _impl_.content_.SetAllocated(value, GetArenaForAllocation());
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        if (_impl_.content_.IsDefault()) {
+          _impl_.content_.Set("", GetArenaForAllocation());
+        }
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:mint.tulip.v1.SearchResponse.Item.content)
+}
+
+// -------------------------------------------------------------------
+
+// SearchResponse
+
+// repeated .mint.tulip.v1.SearchResponse.Item items = 1;
+inline int SearchResponse::_internal_items_size() const {
+  return _internal_items().size();
+}
+inline int SearchResponse::items_size() const {
+  return _internal_items_size();
+}
+inline void SearchResponse::clear_items() {
+  _internal_mutable_items()->Clear();
+}
+inline ::mint::tulip::v1::SearchResponse_Item* SearchResponse::mutable_items(int index) {
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchResponse.items)
+  return _internal_mutable_items()->Mutable(index);
+}
+inline ::google::protobuf::RepeatedPtrField< ::mint::tulip::v1::SearchResponse_Item >*
+SearchResponse::mutable_items() {
+  // @@protoc_insertion_point(field_mutable_list:mint.tulip.v1.SearchResponse.items)
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  return _internal_mutable_items();
+}
+inline const ::mint::tulip::v1::SearchResponse_Item& SearchResponse::items(int index) const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.items)
+    return _internal_items().Get(index);
+}
+inline ::mint::tulip::v1::SearchResponse_Item* SearchResponse::add_items() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ::mint::tulip::v1::SearchResponse_Item* _add = _internal_mutable_items()->Add();
+  // @@protoc_insertion_point(field_add:mint.tulip.v1.SearchResponse.items)
+  return _add;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mint::tulip::v1::SearchResponse_Item >&
+SearchResponse::items() const {
+  // @@protoc_insertion_point(field_list:mint.tulip.v1.SearchResponse.items)
+  return _internal_items();
+}
+inline const ::google::protobuf::RepeatedPtrField<::mint::tulip::v1::SearchResponse_Item>&
+SearchResponse::_internal_items() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.items_;
+}
+inline ::google::protobuf::RepeatedPtrField<::mint::tulip::v1::SearchResponse_Item>*
+SearchResponse::_internal_mutable_items() {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return &_impl_.items_;
+}
+
+// .mint.tulip.v1.SearchRequest.Page page = 98;
+inline bool SearchResponse::has_page() const {
+  bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+  PROTOBUF_ASSUME(!value || _impl_.page_ != nullptr);
+  return value;
+}
+inline void SearchResponse::clear_page() {
+  if (_impl_.page_ != nullptr) _impl_.page_->Clear();
+  _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mint::tulip::v1::SearchRequest_Page& SearchResponse::_internal_page() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  const ::mint::tulip::v1::SearchRequest_Page* p = _impl_.page_;
+  return p != nullptr ? *p : reinterpret_cast<const ::mint::tulip::v1::SearchRequest_Page&>(::mint::tulip::v1::_SearchRequest_Page_default_instance_);
+}
+inline const ::mint::tulip::v1::SearchRequest_Page& SearchResponse::page() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.page)
+  return _internal_page();
+}
+inline void SearchResponse::unsafe_arena_set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  if (GetArenaForAllocation() == nullptr) {
+    delete reinterpret_cast<::google::protobuf::MessageLite*>(_impl_.page_);
+  }
+  _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value);
+  if (value != nullptr) {
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+  // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mint.tulip.v1.SearchResponse.page)
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchResponse::release_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::mint::tulip::v1::SearchRequest_Page* released = _impl_.page_;
+  _impl_.page_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+  auto* old = reinterpret_cast<::google::protobuf::MessageLite*>(released);
+  released = ::google::protobuf::internal::DuplicateIfNonNull(released);
+  if (GetArenaForAllocation() == nullptr) {
+    delete old;
+  }
+#else   // PROTOBUF_FORCE_COPY_IN_RELEASE
+  if (GetArenaForAllocation() != nullptr) {
+    released = ::google::protobuf::internal::DuplicateIfNonNull(released);
+  }
+#endif  // !PROTOBUF_FORCE_COPY_IN_RELEASE
+  return released;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchResponse::unsafe_arena_release_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  // @@protoc_insertion_point(field_release:mint.tulip.v1.SearchResponse.page)
+
+  _impl_._has_bits_[0] &= ~0x00000001u;
+  ::mint::tulip::v1::SearchRequest_Page* temp = _impl_.page_;
+  _impl_.page_ = nullptr;
+  return temp;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchResponse::_internal_mutable_page() {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  _impl_._has_bits_[0] |= 0x00000001u;
+  if (_impl_.page_ == nullptr) {
+    auto* p = CreateMaybeMessage<::mint::tulip::v1::SearchRequest_Page>(GetArenaForAllocation());
+    _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(p);
+  }
+  return _impl_.page_;
+}
+inline ::mint::tulip::v1::SearchRequest_Page* SearchResponse::mutable_page() {
+  ::mint::tulip::v1::SearchRequest_Page* _msg = _internal_mutable_page();
+  // @@protoc_insertion_point(field_mutable:mint.tulip.v1.SearchResponse.page)
+  return _msg;
+}
+inline void SearchResponse::set_allocated_page(::mint::tulip::v1::SearchRequest_Page* value) {
+  ::google::protobuf::Arena* message_arena = GetArenaForAllocation();
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  if (message_arena == nullptr) {
+    delete reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(_impl_.page_);
+  }
+
+  if (value != nullptr) {
+    ::google::protobuf::Arena* submessage_arena =
+        ::google::protobuf::Arena::InternalGetOwningArena(reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value));
+    if (message_arena != submessage_arena) {
+      value = ::google::protobuf::internal::GetOwnedMessage(message_arena, value, submessage_arena);
+    }
+    _impl_._has_bits_[0] |= 0x00000001u;
+  } else {
+    _impl_._has_bits_[0] &= ~0x00000001u;
+  }
+
+  _impl_.page_ = reinterpret_cast<::mint::tulip::v1::SearchRequest_Page*>(value);
+  // @@protoc_insertion_point(field_set_allocated:mint.tulip.v1.SearchResponse.page)
+}
+
+// int32 total = 99;
+inline void SearchResponse::clear_total() {
+  _impl_.total_ = 0;
+}
+inline ::int32_t SearchResponse::total() const {
+  // @@protoc_insertion_point(field_get:mint.tulip.v1.SearchResponse.total)
+  return _internal_total();
+}
+inline void SearchResponse::set_total(::int32_t value) {
+  _internal_set_total(value);
+  // @@protoc_insertion_point(field_set:mint.tulip.v1.SearchResponse.total)
+}
+inline ::int32_t SearchResponse::_internal_total() const {
+  PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race);
+  return _impl_.total_;
+}
+inline void SearchResponse::_internal_set_total(::int32_t value) {
+  PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race);
+  ;
+  _impl_.total_ = value;
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+}  // namespace v1
+}  // namespace tulip
+}  // namespace mint
+
+
+// @@protoc_insertion_point(global_scope)
+
+#include "google/protobuf/port_undef.inc"
+
+#endif  // GOOGLE_PROTOBUF_INCLUDED_tulip_2eproto_2epb_2eh

+ 1159 - 0
rpc/sdk/csharp/Tulip.cs

@@ -0,0 +1,1159 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: tulip.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Mint.Tulip.V1 {
+
+  /// <summary>Holder for reflection information generated from tulip.proto</summary>
+  public static partial class TulipReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for tulip.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static TulipReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cgt0dWxpcC5wcm90bxINbWludC50dWxpcC52MSKTAQoNU2VhcmNoUmVxdWVz",
+            "dBIQCghrZXl3b3JkcxgBIAMoCRIMCgRib29rGAIgASgFEjQKBHBhZ2UYYyAB",
+            "KAsyIS5taW50LnR1bGlwLnYxLlNlYXJjaFJlcXVlc3QuUGFnZUgAiAEBGiMK",
+            "BFBhZ2USDQoFaW5kZXgYASABKAUSDAoEc2l6ZRgCIAEoBUIHCgVfcGFnZSLe",
+            "AQoOU2VhcmNoUmVzcG9uc2USMQoFaXRlbXMYASADKAsyIi5taW50LnR1bGlw",
+            "LnYxLlNlYXJjaFJlc3BvbnNlLkl0ZW0SLwoEcGFnZRhiIAEoCzIhLm1pbnQu",
+            "dHVsaXAudjEuU2VhcmNoUmVxdWVzdC5QYWdlEg0KBXRvdGFsGGMgASgFGlkK",
+            "BEl0ZW0SDAoEcmFuaxgBIAEoBRIRCgloaWdobGlnaHQYAiABKAkSDAoEYm9v",
+            "axgDIAEoBRIRCglwYXJhZ3JhcGgYBCABKAUSDwoHY29udGVudBgFIAEoCTJP",
+            "CgZTZWFyY2gSRQoEUGFsaRIcLm1pbnQudHVsaXAudjEuU2VhcmNoUmVxdWVz",
+            "dBodLm1pbnQudHVsaXAudjEuU2VhcmNoUmVzcG9uc2UiAEIyCi5jb20uZ2l0",
+            "aHViLmlhcHRfcGxhdGZvcm0ubWludC5wbHVnaW5zLnR1bGlwLnYxUAFiBnBy",
+            "b3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Mint.Tulip.V1.SearchRequest), global::Mint.Tulip.V1.SearchRequest.Parser, new[]{ "Keywords", "Book", "Page" }, new[]{ "Page" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Mint.Tulip.V1.SearchRequest.Types.Page), global::Mint.Tulip.V1.SearchRequest.Types.Page.Parser, new[]{ "Index", "Size" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Mint.Tulip.V1.SearchResponse), global::Mint.Tulip.V1.SearchResponse.Parser, new[]{ "Items", "Page", "Total" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Mint.Tulip.V1.SearchResponse.Types.Item), global::Mint.Tulip.V1.SearchResponse.Types.Item.Parser, new[]{ "Rank", "Highlight", "Book", "Paragraph", "Content" }, null, null, null, null)})
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  /// <summary>
+  /// ----------------------------------------------------------------------------
+  /// </summary>
+  public sealed partial class SearchRequest : pb::IMessage<SearchRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<SearchRequest> _parser = new pb::MessageParser<SearchRequest>(() => new SearchRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<SearchRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Mint.Tulip.V1.TulipReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchRequest(SearchRequest other) : this() {
+      keywords_ = other.keywords_.Clone();
+      book_ = other.book_;
+      page_ = other.page_ != null ? other.page_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchRequest Clone() {
+      return new SearchRequest(this);
+    }
+
+    /// <summary>Field number for the "keywords" field.</summary>
+    public const int KeywordsFieldNumber = 1;
+    private static readonly pb::FieldCodec<string> _repeated_keywords_codec
+        = pb::FieldCodec.ForString(10);
+    private readonly pbc::RepeatedField<string> keywords_ = new pbc::RepeatedField<string>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<string> Keywords {
+      get { return keywords_; }
+    }
+
+    /// <summary>Field number for the "book" field.</summary>
+    public const int BookFieldNumber = 2;
+    private int book_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Book {
+      get { return book_; }
+      set {
+        book_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "page" field.</summary>
+    public const int PageFieldNumber = 99;
+    private global::Mint.Tulip.V1.SearchRequest.Types.Page page_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Mint.Tulip.V1.SearchRequest.Types.Page Page {
+      get { return page_; }
+      set {
+        page_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as SearchRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(SearchRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!keywords_.Equals(other.keywords_)) return false;
+      if (Book != other.Book) return false;
+      if (!object.Equals(Page, other.Page)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= keywords_.GetHashCode();
+      if (Book != 0) hash ^= Book.GetHashCode();
+      if (page_ != null) hash ^= Page.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      keywords_.WriteTo(output, _repeated_keywords_codec);
+      if (Book != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Book);
+      }
+      if (page_ != null) {
+        output.WriteRawTag(154, 6);
+        output.WriteMessage(Page);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      keywords_.WriteTo(ref output, _repeated_keywords_codec);
+      if (Book != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Book);
+      }
+      if (page_ != null) {
+        output.WriteRawTag(154, 6);
+        output.WriteMessage(Page);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += keywords_.CalculateSize(_repeated_keywords_codec);
+      if (Book != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Book);
+      }
+      if (page_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(Page);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(SearchRequest other) {
+      if (other == null) {
+        return;
+      }
+      keywords_.Add(other.keywords_);
+      if (other.Book != 0) {
+        Book = other.Book;
+      }
+      if (other.page_ != null) {
+        if (page_ == null) {
+          Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+        }
+        Page.MergeFrom(other.Page);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            keywords_.AddEntriesFrom(input, _repeated_keywords_codec);
+            break;
+          }
+          case 16: {
+            Book = input.ReadInt32();
+            break;
+          }
+          case 794: {
+            if (page_ == null) {
+              Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+            }
+            input.ReadMessage(Page);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            keywords_.AddEntriesFrom(ref input, _repeated_keywords_codec);
+            break;
+          }
+          case 16: {
+            Book = input.ReadInt32();
+            break;
+          }
+          case 794: {
+            if (page_ == null) {
+              Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+            }
+            input.ReadMessage(Page);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+    #region Nested types
+    /// <summary>Container for nested types declared in the SearchRequest message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static partial class Types {
+      public sealed partial class Page : pb::IMessage<Page>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<Page> _parser = new pb::MessageParser<Page>(() => new Page());
+        private pb::UnknownFieldSet _unknownFields;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<Page> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::Mint.Tulip.V1.SearchRequest.Descriptor.NestedTypes[0]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Page() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Page(Page other) : this() {
+          index_ = other.index_;
+          size_ = other.size_;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Page Clone() {
+          return new Page(this);
+        }
+
+        /// <summary>Field number for the "index" field.</summary>
+        public const int IndexFieldNumber = 1;
+        private int index_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int Index {
+          get { return index_; }
+          set {
+            index_ = value;
+          }
+        }
+
+        /// <summary>Field number for the "size" field.</summary>
+        public const int SizeFieldNumber = 2;
+        private int size_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int Size {
+          get { return size_; }
+          set {
+            size_ = value;
+          }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as Page);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(Page other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (Index != other.Index) return false;
+          if (Size != other.Size) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (Index != 0) hash ^= Index.GetHashCode();
+          if (Size != 0) hash ^= Size.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (Index != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Index);
+          }
+          if (Size != 0) {
+            output.WriteRawTag(16);
+            output.WriteInt32(Size);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (Index != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Index);
+          }
+          if (Size != 0) {
+            output.WriteRawTag(16);
+            output.WriteInt32(Size);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (Index != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(Index);
+          }
+          if (Size != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(Size);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(Page other) {
+          if (other == null) {
+            return;
+          }
+          if (other.Index != 0) {
+            Index = other.Index;
+          }
+          if (other.Size != 0) {
+            Size = other.Size;
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 8: {
+                Index = input.ReadInt32();
+                break;
+              }
+              case 16: {
+                Size = input.ReadInt32();
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 8: {
+                Index = input.ReadInt32();
+                break;
+              }
+              case 16: {
+                Size = input.ReadInt32();
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+      }
+
+    }
+    #endregion
+
+  }
+
+  public sealed partial class SearchResponse : pb::IMessage<SearchResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<SearchResponse> _parser = new pb::MessageParser<SearchResponse>(() => new SearchResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<SearchResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Mint.Tulip.V1.TulipReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchResponse(SearchResponse other) : this() {
+      items_ = other.items_.Clone();
+      page_ = other.page_ != null ? other.page_.Clone() : null;
+      total_ = other.total_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SearchResponse Clone() {
+      return new SearchResponse(this);
+    }
+
+    /// <summary>Field number for the "items" field.</summary>
+    public const int ItemsFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Mint.Tulip.V1.SearchResponse.Types.Item> _repeated_items_codec
+        = pb::FieldCodec.ForMessage(10, global::Mint.Tulip.V1.SearchResponse.Types.Item.Parser);
+    private readonly pbc::RepeatedField<global::Mint.Tulip.V1.SearchResponse.Types.Item> items_ = new pbc::RepeatedField<global::Mint.Tulip.V1.SearchResponse.Types.Item>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Mint.Tulip.V1.SearchResponse.Types.Item> Items {
+      get { return items_; }
+    }
+
+    /// <summary>Field number for the "page" field.</summary>
+    public const int PageFieldNumber = 98;
+    private global::Mint.Tulip.V1.SearchRequest.Types.Page page_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Mint.Tulip.V1.SearchRequest.Types.Page Page {
+      get { return page_; }
+      set {
+        page_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "total" field.</summary>
+    public const int TotalFieldNumber = 99;
+    private int total_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Total {
+      get { return total_; }
+      set {
+        total_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as SearchResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(SearchResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!items_.Equals(other.items_)) return false;
+      if (!object.Equals(Page, other.Page)) return false;
+      if (Total != other.Total) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= items_.GetHashCode();
+      if (page_ != null) hash ^= Page.GetHashCode();
+      if (Total != 0) hash ^= Total.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      items_.WriteTo(output, _repeated_items_codec);
+      if (page_ != null) {
+        output.WriteRawTag(146, 6);
+        output.WriteMessage(Page);
+      }
+      if (Total != 0) {
+        output.WriteRawTag(152, 6);
+        output.WriteInt32(Total);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      items_.WriteTo(ref output, _repeated_items_codec);
+      if (page_ != null) {
+        output.WriteRawTag(146, 6);
+        output.WriteMessage(Page);
+      }
+      if (Total != 0) {
+        output.WriteRawTag(152, 6);
+        output.WriteInt32(Total);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += items_.CalculateSize(_repeated_items_codec);
+      if (page_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(Page);
+      }
+      if (Total != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeInt32Size(Total);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(SearchResponse other) {
+      if (other == null) {
+        return;
+      }
+      items_.Add(other.items_);
+      if (other.page_ != null) {
+        if (page_ == null) {
+          Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+        }
+        Page.MergeFrom(other.Page);
+      }
+      if (other.Total != 0) {
+        Total = other.Total;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            items_.AddEntriesFrom(input, _repeated_items_codec);
+            break;
+          }
+          case 786: {
+            if (page_ == null) {
+              Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+            }
+            input.ReadMessage(Page);
+            break;
+          }
+          case 792: {
+            Total = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            items_.AddEntriesFrom(ref input, _repeated_items_codec);
+            break;
+          }
+          case 786: {
+            if (page_ == null) {
+              Page = new global::Mint.Tulip.V1.SearchRequest.Types.Page();
+            }
+            input.ReadMessage(Page);
+            break;
+          }
+          case 792: {
+            Total = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+    #region Nested types
+    /// <summary>Container for nested types declared in the SearchResponse message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static partial class Types {
+      public sealed partial class Item : pb::IMessage<Item>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<Item> _parser = new pb::MessageParser<Item>(() => new Item());
+        private pb::UnknownFieldSet _unknownFields;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<Item> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::Mint.Tulip.V1.SearchResponse.Descriptor.NestedTypes[0]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Item() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Item(Item other) : this() {
+          rank_ = other.rank_;
+          highlight_ = other.highlight_;
+          book_ = other.book_;
+          paragraph_ = other.paragraph_;
+          content_ = other.content_;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Item Clone() {
+          return new Item(this);
+        }
+
+        /// <summary>Field number for the "rank" field.</summary>
+        public const int RankFieldNumber = 1;
+        private int rank_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int Rank {
+          get { return rank_; }
+          set {
+            rank_ = value;
+          }
+        }
+
+        /// <summary>Field number for the "highlight" field.</summary>
+        public const int HighlightFieldNumber = 2;
+        private string highlight_ = "";
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public string Highlight {
+          get { return highlight_; }
+          set {
+            highlight_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+          }
+        }
+
+        /// <summary>Field number for the "book" field.</summary>
+        public const int BookFieldNumber = 3;
+        private int book_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int Book {
+          get { return book_; }
+          set {
+            book_ = value;
+          }
+        }
+
+        /// <summary>Field number for the "paragraph" field.</summary>
+        public const int ParagraphFieldNumber = 4;
+        private int paragraph_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int Paragraph {
+          get { return paragraph_; }
+          set {
+            paragraph_ = value;
+          }
+        }
+
+        /// <summary>Field number for the "content" field.</summary>
+        public const int ContentFieldNumber = 5;
+        private string content_ = "";
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public string Content {
+          get { return content_; }
+          set {
+            content_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+          }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as Item);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(Item other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (Rank != other.Rank) return false;
+          if (Highlight != other.Highlight) return false;
+          if (Book != other.Book) return false;
+          if (Paragraph != other.Paragraph) return false;
+          if (Content != other.Content) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (Rank != 0) hash ^= Rank.GetHashCode();
+          if (Highlight.Length != 0) hash ^= Highlight.GetHashCode();
+          if (Book != 0) hash ^= Book.GetHashCode();
+          if (Paragraph != 0) hash ^= Paragraph.GetHashCode();
+          if (Content.Length != 0) hash ^= Content.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (Rank != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Rank);
+          }
+          if (Highlight.Length != 0) {
+            output.WriteRawTag(18);
+            output.WriteString(Highlight);
+          }
+          if (Book != 0) {
+            output.WriteRawTag(24);
+            output.WriteInt32(Book);
+          }
+          if (Paragraph != 0) {
+            output.WriteRawTag(32);
+            output.WriteInt32(Paragraph);
+          }
+          if (Content.Length != 0) {
+            output.WriteRawTag(42);
+            output.WriteString(Content);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (Rank != 0) {
+            output.WriteRawTag(8);
+            output.WriteInt32(Rank);
+          }
+          if (Highlight.Length != 0) {
+            output.WriteRawTag(18);
+            output.WriteString(Highlight);
+          }
+          if (Book != 0) {
+            output.WriteRawTag(24);
+            output.WriteInt32(Book);
+          }
+          if (Paragraph != 0) {
+            output.WriteRawTag(32);
+            output.WriteInt32(Paragraph);
+          }
+          if (Content.Length != 0) {
+            output.WriteRawTag(42);
+            output.WriteString(Content);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (Rank != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(Rank);
+          }
+          if (Highlight.Length != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeStringSize(Highlight);
+          }
+          if (Book != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(Book);
+          }
+          if (Paragraph != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(Paragraph);
+          }
+          if (Content.Length != 0) {
+            size += 1 + pb::CodedOutputStream.ComputeStringSize(Content);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(Item other) {
+          if (other == null) {
+            return;
+          }
+          if (other.Rank != 0) {
+            Rank = other.Rank;
+          }
+          if (other.Highlight.Length != 0) {
+            Highlight = other.Highlight;
+          }
+          if (other.Book != 0) {
+            Book = other.Book;
+          }
+          if (other.Paragraph != 0) {
+            Paragraph = other.Paragraph;
+          }
+          if (other.Content.Length != 0) {
+            Content = other.Content;
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 8: {
+                Rank = input.ReadInt32();
+                break;
+              }
+              case 18: {
+                Highlight = input.ReadString();
+                break;
+              }
+              case 24: {
+                Book = input.ReadInt32();
+                break;
+              }
+              case 32: {
+                Paragraph = input.ReadInt32();
+                break;
+              }
+              case 42: {
+                Content = input.ReadString();
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 8: {
+                Rank = input.ReadInt32();
+                break;
+              }
+              case 18: {
+                Highlight = input.ReadString();
+                break;
+              }
+              case 24: {
+                Book = input.ReadInt32();
+                break;
+              }
+              case 32: {
+                Paragraph = input.ReadInt32();
+                break;
+              }
+              case 42: {
+                Content = input.ReadString();
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+      }
+
+    }
+    #endregion
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code

+ 155 - 0
rpc/sdk/csharp/TulipGrpc.cs

@@ -0,0 +1,155 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: tulip.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981, 0612
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Mint.Tulip.V1 {
+  public static partial class Search
+  {
+    static readonly string __ServiceName = "mint.tulip.v1.Search";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Mint.Tulip.V1.SearchRequest> __Marshaller_mint_tulip_v1_SearchRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Mint.Tulip.V1.SearchRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Mint.Tulip.V1.SearchResponse> __Marshaller_mint_tulip_v1_SearchResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Mint.Tulip.V1.SearchResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Mint.Tulip.V1.SearchRequest, global::Mint.Tulip.V1.SearchResponse> __Method_Pali = new grpc::Method<global::Mint.Tulip.V1.SearchRequest, global::Mint.Tulip.V1.SearchResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Pali",
+        __Marshaller_mint_tulip_v1_SearchRequest,
+        __Marshaller_mint_tulip_v1_SearchResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Mint.Tulip.V1.TulipReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of Search</summary>
+    [grpc::BindServiceMethod(typeof(Search), "BindService")]
+    public abstract partial class SearchBase
+    {
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Mint.Tulip.V1.SearchResponse> Pali(global::Mint.Tulip.V1.SearchRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for Search</summary>
+    public partial class SearchClient : grpc::ClientBase<SearchClient>
+    {
+      /// <summary>Creates a new client for Search</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public SearchClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for Search that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public SearchClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected SearchClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected SearchClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Mint.Tulip.V1.SearchResponse Pali(global::Mint.Tulip.V1.SearchRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Pali(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Mint.Tulip.V1.SearchResponse Pali(global::Mint.Tulip.V1.SearchRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Pali, null, options, request);
+      }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Mint.Tulip.V1.SearchResponse> PaliAsync(global::Mint.Tulip.V1.SearchRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return PaliAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Mint.Tulip.V1.SearchResponse> PaliAsync(global::Mint.Tulip.V1.SearchRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Pali, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override SearchClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new SearchClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(SearchBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Pali, serviceImpl.Pali).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, SearchBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Pali, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Mint.Tulip.V1.SearchRequest, global::Mint.Tulip.V1.SearchResponse>(serviceImpl.Pali));
+    }
+
+  }
+}
+#endregion

+ 1 - 1
rpc/sdk/java/com/github/iapt_platform/mint/plugins/morus/v1/MarkdownGrpc.java

@@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName;
  * </pre>
  */
 @javax.annotation.Generated(
-    value = "by gRPC proto compiler (version 1.58.0)",
+    value = "by gRPC proto compiler (version 1.59.0)",
     comments = "Source: morus.proto")
 @io.grpc.stub.annotations.GrpcGenerated
 public final class MarkdownGrpc {

+ 293 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchGrpc.java

@@ -0,0 +1,293 @@
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+
+/**
+ */
+@javax.annotation.Generated(
+    value = "by gRPC proto compiler (version 1.59.0)",
+    comments = "Source: tulip.proto")
+@io.grpc.stub.annotations.GrpcGenerated
+public final class SearchGrpc {
+
+  private SearchGrpc() {}
+
+  public static final java.lang.String SERVICE_NAME = "mint.tulip.v1.Search";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest,
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> getPaliMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "Pali",
+      requestType = com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.class,
+      responseType = com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest,
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> getPaliMethod() {
+    io.grpc.MethodDescriptor<com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> getPaliMethod;
+    if ((getPaliMethod = SearchGrpc.getPaliMethod) == null) {
+      synchronized (SearchGrpc.class) {
+        if ((getPaliMethod = SearchGrpc.getPaliMethod) == null) {
+          SearchGrpc.getPaliMethod = getPaliMethod =
+              io.grpc.MethodDescriptor.<com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Pali"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new SearchMethodDescriptorSupplier("Pali"))
+              .build();
+        }
+      }
+    }
+    return getPaliMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static SearchStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<SearchStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<SearchStub>() {
+        @java.lang.Override
+        public SearchStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new SearchStub(channel, callOptions);
+        }
+      };
+    return SearchStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static SearchBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<SearchBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<SearchBlockingStub>() {
+        @java.lang.Override
+        public SearchBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new SearchBlockingStub(channel, callOptions);
+        }
+      };
+    return SearchBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static SearchFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<SearchFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<SearchFutureStub>() {
+        @java.lang.Override
+        public SearchFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new SearchFutureStub(channel, callOptions);
+        }
+      };
+    return SearchFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public interface AsyncService {
+
+    /**
+     */
+    default void pali(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest request,
+        io.grpc.stub.StreamObserver<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getPaliMethod(), responseObserver);
+    }
+  }
+
+  /**
+   * Base class for the server implementation of the service Search.
+   */
+  public static abstract class SearchImplBase
+      implements io.grpc.BindableService, AsyncService {
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return SearchGrpc.bindService(this);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do asynchronous rpc calls to service Search.
+   */
+  public static final class SearchStub
+      extends io.grpc.stub.AbstractAsyncStub<SearchStub> {
+    private SearchStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected SearchStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new SearchStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public void pali(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest request,
+        io.grpc.stub.StreamObserver<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getPaliMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do synchronous rpc calls to service Search.
+   */
+  public static final class SearchBlockingStub
+      extends io.grpc.stub.AbstractBlockingStub<SearchBlockingStub> {
+    private SearchBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected SearchBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new SearchBlockingStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse pali(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getPaliMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do ListenableFuture-style rpc calls to service Search.
+   */
+  public static final class SearchFutureStub
+      extends io.grpc.stub.AbstractFutureStub<SearchFutureStub> {
+    private SearchFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected SearchFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new SearchFutureStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse> pali(
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getPaliMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_PALI = 0;
+
+  private static final class MethodHandlers<Req, Resp> implements
+      io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
+    private final AsyncService serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(AsyncService serviceImpl, int methodId) {
+      this.serviceImpl = serviceImpl;
+      this.methodId = methodId;
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        case METHODID_PALI:
+          serviceImpl.pali((com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest) request,
+              (io.grpc.stub.StreamObserver<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse>) responseObserver);
+          break;
+        default:
+          throw new AssertionError();
+      }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver<Req> invoke(
+        io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        default:
+          throw new AssertionError();
+      }
+    }
+  }
+
+  public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
+    return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+        .addMethod(
+          getPaliMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest,
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse>(
+                service, METHODID_PALI)))
+        .build();
+  }
+
+  private static abstract class SearchBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    SearchBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("Search");
+    }
+  }
+
+  private static final class SearchFileDescriptorSupplier
+      extends SearchBaseDescriptorSupplier {
+    SearchFileDescriptorSupplier() {}
+  }
+
+  private static final class SearchMethodDescriptorSupplier
+      extends SearchBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final java.lang.String methodName;
+
+    SearchMethodDescriptorSupplier(java.lang.String methodName) {
+      this.methodName = methodName;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+      return getServiceDescriptor().findMethodByName(methodName);
+    }
+  }
+
+  private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+  public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+    io.grpc.ServiceDescriptor result = serviceDescriptor;
+    if (result == null) {
+      synchronized (SearchGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new SearchFileDescriptorSupplier())
+              .addMethod(getPaliMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}

+ 1409 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchRequest.java

@@ -0,0 +1,1409 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+/**
+ * <pre>
+ * ----------------------------------------------------------------------------
+ * </pre>
+ *
+ * Protobuf type {@code mint.tulip.v1.SearchRequest}
+ */
+public final class SearchRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:mint.tulip.v1.SearchRequest)
+    SearchRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use SearchRequest.newBuilder() to construct.
+  private SearchRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private SearchRequest() {
+    keywords_ =
+        com.google.protobuf.LazyStringArrayList.emptyList();
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new SearchRequest();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Builder.class);
+  }
+
+  public interface PageOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:mint.tulip.v1.SearchRequest.Page)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>int32 index = 1;</code>
+     * @return The index.
+     */
+    int getIndex();
+
+    /**
+     * <code>int32 size = 2;</code>
+     * @return The size.
+     */
+    int getSize();
+  }
+  /**
+   * Protobuf type {@code mint.tulip.v1.SearchRequest.Page}
+   */
+  public static final class Page extends
+      com.google.protobuf.GeneratedMessageV3 implements
+      // @@protoc_insertion_point(message_implements:mint.tulip.v1.SearchRequest.Page)
+      PageOrBuilder {
+  private static final long serialVersionUID = 0L;
+    // Use Page.newBuilder() to construct.
+    private Page(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+      super(builder);
+    }
+    private Page() {
+    }
+
+    @java.lang.Override
+    @SuppressWarnings({"unused"})
+    protected java.lang.Object newInstance(
+        UnusedPrivateParameter unused) {
+      return new Page();
+    }
+
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_Page_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_Page_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder.class);
+    }
+
+    public static final int INDEX_FIELD_NUMBER = 1;
+    private int index_ = 0;
+    /**
+     * <code>int32 index = 1;</code>
+     * @return The index.
+     */
+    @java.lang.Override
+    public int getIndex() {
+      return index_;
+    }
+
+    public static final int SIZE_FIELD_NUMBER = 2;
+    private int size_ = 0;
+    /**
+     * <code>int32 size = 2;</code>
+     * @return The size.
+     */
+    @java.lang.Override
+    public int getSize() {
+      return size_;
+    }
+
+    private byte memoizedIsInitialized = -1;
+    @java.lang.Override
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    @java.lang.Override
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      if (index_ != 0) {
+        output.writeInt32(1, index_);
+      }
+      if (size_ != 0) {
+        output.writeInt32(2, size_);
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    @java.lang.Override
+    public int getSerializedSize() {
+      int size = memoizedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (index_ != 0) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, index_);
+      }
+      if (size_ != 0) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, size_);
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSize = size;
+      return size;
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page)) {
+        return super.equals(obj);
+      }
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page other = (com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page) obj;
+
+      if (getIndex()
+          != other.getIndex()) return false;
+      if (getSize()
+          != other.getSize()) return false;
+      if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+      return true;
+    }
+
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptor().hashCode();
+      hash = (37 * hash) + INDEX_FIELD_NUMBER;
+      hash = (53 * hash) + getIndex();
+      hash = (37 * hash) + SIZE_FIELD_NUMBER;
+      hash = (53 * hash) + getSize();
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        java.nio.ByteBuffer data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        java.nio.ByteBuffer data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input);
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    @java.lang.Override
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+    }
+    @java.lang.Override
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
+    }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code mint.tulip.v1.SearchRequest.Page}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:mint.tulip.v1.SearchRequest.Page)
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_Page_descriptor;
+      }
+
+      @java.lang.Override
+      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_Page_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder.class);
+      }
+
+      // Construct using com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.newBuilder()
+      private Builder() {
+
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+        super(parent);
+
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        bitField0_ = 0;
+        index_ = 0;
+        size_ = 0;
+        return this;
+      }
+
+      @java.lang.Override
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_Page_descriptor;
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getDefaultInstanceForType() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance();
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page build() {
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page buildPartial() {
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page result = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page(this);
+        if (bitField0_ != 0) { buildPartial0(result); }
+        onBuilt();
+        return result;
+      }
+
+      private void buildPartial0(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page result) {
+        int from_bitField0_ = bitField0_;
+        if (((from_bitField0_ & 0x00000001) != 0)) {
+          result.index_ = index_;
+        }
+        if (((from_bitField0_ & 0x00000002) != 0)) {
+          result.size_ = size_;
+        }
+      }
+
+      @java.lang.Override
+      public Builder clone() {
+        return super.clone();
+      }
+      @java.lang.Override
+      public Builder setField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.setField(field, value);
+      }
+      @java.lang.Override
+      public Builder clearField(
+          com.google.protobuf.Descriptors.FieldDescriptor field) {
+        return super.clearField(field);
+      }
+      @java.lang.Override
+      public Builder clearOneof(
+          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+        return super.clearOneof(oneof);
+      }
+      @java.lang.Override
+      public Builder setRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          int index, java.lang.Object value) {
+        return super.setRepeatedField(field, index, value);
+      }
+      @java.lang.Override
+      public Builder addRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.addRepeatedField(field, value);
+      }
+      @java.lang.Override
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page) {
+          return mergeFrom((com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page other) {
+        if (other == com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance()) return this;
+        if (other.getIndex() != 0) {
+          setIndex(other.getIndex());
+        }
+        if (other.getSize() != 0) {
+          setSize(other.getSize());
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        onChanged();
+        return this;
+      }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        if (extensionRegistry == null) {
+          throw new java.lang.NullPointerException();
+        }
+        try {
+          boolean done = false;
+          while (!done) {
+            int tag = input.readTag();
+            switch (tag) {
+              case 0:
+                done = true;
+                break;
+              case 8: {
+                index_ = input.readInt32();
+                bitField0_ |= 0x00000001;
+                break;
+              } // case 8
+              case 16: {
+                size_ = input.readInt32();
+                bitField0_ |= 0x00000002;
+                break;
+              } // case 16
+              default: {
+                if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                  done = true; // was an endgroup tag
+                }
+                break;
+              } // default:
+            } // switch (tag)
+          } // while (!done)
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw e.unwrapIOException();
+        } finally {
+          onChanged();
+        } // finally
+        return this;
+      }
+      private int bitField0_;
+
+      private int index_ ;
+      /**
+       * <code>int32 index = 1;</code>
+       * @return The index.
+       */
+      @java.lang.Override
+      public int getIndex() {
+        return index_;
+      }
+      /**
+       * <code>int32 index = 1;</code>
+       * @param value The index to set.
+       * @return This builder for chaining.
+       */
+      public Builder setIndex(int value) {
+
+        index_ = value;
+        bitField0_ |= 0x00000001;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>int32 index = 1;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearIndex() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        index_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private int size_ ;
+      /**
+       * <code>int32 size = 2;</code>
+       * @return The size.
+       */
+      @java.lang.Override
+      public int getSize() {
+        return size_;
+      }
+      /**
+       * <code>int32 size = 2;</code>
+       * @param value The size to set.
+       * @return This builder for chaining.
+       */
+      public Builder setSize(int value) {
+
+        size_ = value;
+        bitField0_ |= 0x00000002;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>int32 size = 2;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearSize() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        size_ = 0;
+        onChanged();
+        return this;
+      }
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
+      }
+
+      @java.lang.Override
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.mergeUnknownFields(unknownFields);
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:mint.tulip.v1.SearchRequest.Page)
+    }
+
+    // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchRequest.Page)
+    private static final com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page();
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    private static final com.google.protobuf.Parser<Page>
+        PARSER = new com.google.protobuf.AbstractParser<Page>() {
+      @java.lang.Override
+      public Page parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        Builder builder = newBuilder();
+        try {
+          builder.mergeFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw e.setUnfinishedMessage(builder.buildPartial());
+        } catch (com.google.protobuf.UninitializedMessageException e) {
+          throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+        } catch (java.io.IOException e) {
+          throw new com.google.protobuf.InvalidProtocolBufferException(e)
+              .setUnfinishedMessage(builder.buildPartial());
+        }
+        return builder.buildPartial();
+      }
+    };
+
+    public static com.google.protobuf.Parser<Page> parser() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<Page> getParserForType() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  private int bitField0_;
+  public static final int KEYWORDS_FIELD_NUMBER = 1;
+  @SuppressWarnings("serial")
+  private com.google.protobuf.LazyStringArrayList keywords_ =
+      com.google.protobuf.LazyStringArrayList.emptyList();
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @return A list containing the keywords.
+   */
+  public com.google.protobuf.ProtocolStringList
+      getKeywordsList() {
+    return keywords_;
+  }
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @return The count of keywords.
+   */
+  public int getKeywordsCount() {
+    return keywords_.size();
+  }
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @param index The index of the element to return.
+   * @return The keywords at the given index.
+   */
+  public java.lang.String getKeywords(int index) {
+    return keywords_.get(index);
+  }
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @param index The index of the value to return.
+   * @return The bytes of the keywords at the given index.
+   */
+  public com.google.protobuf.ByteString
+      getKeywordsBytes(int index) {
+    return keywords_.getByteString(index);
+  }
+
+  public static final int BOOK_FIELD_NUMBER = 2;
+  private int book_ = 0;
+  /**
+   * <code>int32 book = 2;</code>
+   * @return The book.
+   */
+  @java.lang.Override
+  public int getBook() {
+    return book_;
+  }
+
+  public static final int PAGE_FIELD_NUMBER = 99;
+  private com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page page_;
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   * @return Whether the page field is set.
+   */
+  @java.lang.Override
+  public boolean hasPage() {
+    return ((bitField0_ & 0x00000001) != 0);
+  }
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   * @return The page.
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage() {
+    return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+  }
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder() {
+    return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    for (int i = 0; i < keywords_.size(); i++) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, keywords_.getRaw(i));
+    }
+    if (book_ != 0) {
+      output.writeInt32(2, book_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      output.writeMessage(99, getPage());
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    {
+      int dataSize = 0;
+      for (int i = 0; i < keywords_.size(); i++) {
+        dataSize += computeStringSizeNoTag(keywords_.getRaw(i));
+      }
+      size += dataSize;
+      size += 1 * getKeywordsList().size();
+    }
+    if (book_ != 0) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt32Size(2, book_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(99, getPage());
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest)) {
+      return super.equals(obj);
+    }
+    com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest other = (com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest) obj;
+
+    if (!getKeywordsList()
+        .equals(other.getKeywordsList())) return false;
+    if (getBook()
+        != other.getBook()) return false;
+    if (hasPage() != other.hasPage()) return false;
+    if (hasPage()) {
+      if (!getPage()
+          .equals(other.getPage())) return false;
+    }
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    if (getKeywordsCount() > 0) {
+      hash = (37 * hash) + KEYWORDS_FIELD_NUMBER;
+      hash = (53 * hash) + getKeywordsList().hashCode();
+    }
+    hash = (37 * hash) + BOOK_FIELD_NUMBER;
+    hash = (53 * hash) + getBook();
+    if (hasPage()) {
+      hash = (37 * hash) + PAGE_FIELD_NUMBER;
+      hash = (53 * hash) + getPage().hashCode();
+    }
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * <pre>
+   * ----------------------------------------------------------------------------
+   * </pre>
+   *
+   * Protobuf type {@code mint.tulip.v1.SearchRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:mint.tulip.v1.SearchRequest)
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Builder.class);
+    }
+
+    // Construct using com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getPageFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      keywords_ =
+          com.google.protobuf.LazyStringArrayList.emptyList();
+      book_ = 0;
+      page_ = null;
+      if (pageBuilder_ != null) {
+        pageBuilder_.dispose();
+        pageBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest getDefaultInstanceForType() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest build() {
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest buildPartial() {
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest result = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        keywords_.makeImmutable();
+        result.keywords_ = keywords_;
+      }
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.book_ = book_;
+      }
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000004) != 0)) {
+        result.page_ = pageBuilder_ == null
+            ? page_
+            : pageBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      result.bitField0_ |= to_bitField0_;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest) {
+        return mergeFrom((com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest other) {
+      if (other == com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.getDefaultInstance()) return this;
+      if (!other.keywords_.isEmpty()) {
+        if (keywords_.isEmpty()) {
+          keywords_ = other.keywords_;
+          bitField0_ |= 0x00000001;
+        } else {
+          ensureKeywordsIsMutable();
+          keywords_.addAll(other.keywords_);
+        }
+        onChanged();
+      }
+      if (other.getBook() != 0) {
+        setBook(other.getBook());
+      }
+      if (other.hasPage()) {
+        mergePage(other.getPage());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              java.lang.String s = input.readStringRequireUtf8();
+              ensureKeywordsIsMutable();
+              keywords_.add(s);
+              break;
+            } // case 10
+            case 16: {
+              book_ = input.readInt32();
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 16
+            case 794: {
+              input.readMessage(
+                  getPageFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000004;
+              break;
+            } // case 794
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private com.google.protobuf.LazyStringArrayList keywords_ =
+        com.google.protobuf.LazyStringArrayList.emptyList();
+    private void ensureKeywordsIsMutable() {
+      if (!keywords_.isModifiable()) {
+        keywords_ = new com.google.protobuf.LazyStringArrayList(keywords_);
+      }
+      bitField0_ |= 0x00000001;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @return A list containing the keywords.
+     */
+    public com.google.protobuf.ProtocolStringList
+        getKeywordsList() {
+      keywords_.makeImmutable();
+      return keywords_;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @return The count of keywords.
+     */
+    public int getKeywordsCount() {
+      return keywords_.size();
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param index The index of the element to return.
+     * @return The keywords at the given index.
+     */
+    public java.lang.String getKeywords(int index) {
+      return keywords_.get(index);
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param index The index of the value to return.
+     * @return The bytes of the keywords at the given index.
+     */
+    public com.google.protobuf.ByteString
+        getKeywordsBytes(int index) {
+      return keywords_.getByteString(index);
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param index The index to set the value at.
+     * @param value The keywords to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeywords(
+        int index, java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      ensureKeywordsIsMutable();
+      keywords_.set(index, value);
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param value The keywords to add.
+     * @return This builder for chaining.
+     */
+    public Builder addKeywords(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      ensureKeywordsIsMutable();
+      keywords_.add(value);
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param values The keywords to add.
+     * @return This builder for chaining.
+     */
+    public Builder addAllKeywords(
+        java.lang.Iterable<java.lang.String> values) {
+      ensureKeywordsIsMutable();
+      com.google.protobuf.AbstractMessageLite.Builder.addAll(
+          values, keywords_);
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeywords() {
+      keywords_ =
+        com.google.protobuf.LazyStringArrayList.emptyList();
+      bitField0_ = (bitField0_ & ~0x00000001);;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>repeated string keywords = 1;</code>
+     * @param value The bytes of the keywords to add.
+     * @return This builder for chaining.
+     */
+    public Builder addKeywordsBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      ensureKeywordsIsMutable();
+      keywords_.add(value);
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+
+    private int book_ ;
+    /**
+     * <code>int32 book = 2;</code>
+     * @return The book.
+     */
+    @java.lang.Override
+    public int getBook() {
+      return book_;
+    }
+    /**
+     * <code>int32 book = 2;</code>
+     * @param value The book to set.
+     * @return This builder for chaining.
+     */
+    public Builder setBook(int value) {
+
+      book_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int32 book = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearBook() {
+      bitField0_ = (bitField0_ & ~0x00000002);
+      book_ = 0;
+      onChanged();
+      return this;
+    }
+
+    private com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page page_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder> pageBuilder_;
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @return Whether the page field is set.
+     */
+    public boolean hasPage() {
+      return ((bitField0_ & 0x00000004) != 0);
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @return The page.
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage() {
+      if (pageBuilder_ == null) {
+        return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+      } else {
+        return pageBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public Builder setPage(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page value) {
+      if (pageBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        page_ = value;
+      } else {
+        pageBuilder_.setMessage(value);
+      }
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public Builder setPage(
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder builderForValue) {
+      if (pageBuilder_ == null) {
+        page_ = builderForValue.build();
+      } else {
+        pageBuilder_.setMessage(builderForValue.build());
+      }
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public Builder mergePage(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page value) {
+      if (pageBuilder_ == null) {
+        if (((bitField0_ & 0x00000004) != 0) &&
+          page_ != null &&
+          page_ != com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance()) {
+          getPageBuilder().mergeFrom(value);
+        } else {
+          page_ = value;
+        }
+      } else {
+        pageBuilder_.mergeFrom(value);
+      }
+      if (page_ != null) {
+        bitField0_ |= 0x00000004;
+        onChanged();
+      }
+      return this;
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public Builder clearPage() {
+      bitField0_ = (bitField0_ & ~0x00000004);
+      page_ = null;
+      if (pageBuilder_ != null) {
+        pageBuilder_.dispose();
+        pageBuilder_ = null;
+      }
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder getPageBuilder() {
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return getPageFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder() {
+      if (pageBuilder_ != null) {
+        return pageBuilder_.getMessageOrBuilder();
+      } else {
+        return page_ == null ?
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+      }
+    }
+    /**
+     * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder> 
+        getPageFieldBuilder() {
+      if (pageBuilder_ == null) {
+        pageBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder>(
+                getPage(),
+                getParentForChildren(),
+                isClean());
+        page_ = null;
+      }
+      return pageBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:mint.tulip.v1.SearchRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchRequest)
+  private static final com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest();
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<SearchRequest>
+      PARSER = new com.google.protobuf.AbstractParser<SearchRequest>() {
+    @java.lang.Override
+    public SearchRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<SearchRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<SearchRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 55 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchRequestOrBuilder.java

@@ -0,0 +1,55 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+public interface SearchRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:mint.tulip.v1.SearchRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @return A list containing the keywords.
+   */
+  java.util.List<java.lang.String>
+      getKeywordsList();
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @return The count of keywords.
+   */
+  int getKeywordsCount();
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @param index The index of the element to return.
+   * @return The keywords at the given index.
+   */
+  java.lang.String getKeywords(int index);
+  /**
+   * <code>repeated string keywords = 1;</code>
+   * @param index The index of the value to return.
+   * @return The bytes of the keywords at the given index.
+   */
+  com.google.protobuf.ByteString
+      getKeywordsBytes(int index);
+
+  /**
+   * <code>int32 book = 2;</code>
+   * @return The book.
+   */
+  int getBook();
+
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   * @return Whether the page field is set.
+   */
+  boolean hasPage();
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   * @return The page.
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage();
+  /**
+   * <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder();
+}

+ 1935 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchResponse.java

@@ -0,0 +1,1935 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+/**
+ * Protobuf type {@code mint.tulip.v1.SearchResponse}
+ */
+public final class SearchResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:mint.tulip.v1.SearchResponse)
+    SearchResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use SearchResponse.newBuilder() to construct.
+  private SearchResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private SearchResponse() {
+    items_ = java.util.Collections.emptyList();
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new SearchResponse();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Builder.class);
+  }
+
+  public interface ItemOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:mint.tulip.v1.SearchResponse.Item)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>int32 rank = 1;</code>
+     * @return The rank.
+     */
+    int getRank();
+
+    /**
+     * <code>string highlight = 2;</code>
+     * @return The highlight.
+     */
+    java.lang.String getHighlight();
+    /**
+     * <code>string highlight = 2;</code>
+     * @return The bytes for highlight.
+     */
+    com.google.protobuf.ByteString
+        getHighlightBytes();
+
+    /**
+     * <code>int32 book = 3;</code>
+     * @return The book.
+     */
+    int getBook();
+
+    /**
+     * <code>int32 paragraph = 4;</code>
+     * @return The paragraph.
+     */
+    int getParagraph();
+
+    /**
+     * <code>string content = 5;</code>
+     * @return The content.
+     */
+    java.lang.String getContent();
+    /**
+     * <code>string content = 5;</code>
+     * @return The bytes for content.
+     */
+    com.google.protobuf.ByteString
+        getContentBytes();
+  }
+  /**
+   * Protobuf type {@code mint.tulip.v1.SearchResponse.Item}
+   */
+  public static final class Item extends
+      com.google.protobuf.GeneratedMessageV3 implements
+      // @@protoc_insertion_point(message_implements:mint.tulip.v1.SearchResponse.Item)
+      ItemOrBuilder {
+  private static final long serialVersionUID = 0L;
+    // Use Item.newBuilder() to construct.
+    private Item(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+      super(builder);
+    }
+    private Item() {
+      highlight_ = "";
+      content_ = "";
+    }
+
+    @java.lang.Override
+    @SuppressWarnings({"unused"})
+    protected java.lang.Object newInstance(
+        UnusedPrivateParameter unused) {
+      return new Item();
+    }
+
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_Item_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_Item_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder.class);
+    }
+
+    public static final int RANK_FIELD_NUMBER = 1;
+    private int rank_ = 0;
+    /**
+     * <code>int32 rank = 1;</code>
+     * @return The rank.
+     */
+    @java.lang.Override
+    public int getRank() {
+      return rank_;
+    }
+
+    public static final int HIGHLIGHT_FIELD_NUMBER = 2;
+    @SuppressWarnings("serial")
+    private volatile java.lang.Object highlight_ = "";
+    /**
+     * <code>string highlight = 2;</code>
+     * @return The highlight.
+     */
+    @java.lang.Override
+    public java.lang.String getHighlight() {
+      java.lang.Object ref = highlight_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        highlight_ = s;
+        return s;
+      }
+    }
+    /**
+     * <code>string highlight = 2;</code>
+     * @return The bytes for highlight.
+     */
+    @java.lang.Override
+    public com.google.protobuf.ByteString
+        getHighlightBytes() {
+      java.lang.Object ref = highlight_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        highlight_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    public static final int BOOK_FIELD_NUMBER = 3;
+    private int book_ = 0;
+    /**
+     * <code>int32 book = 3;</code>
+     * @return The book.
+     */
+    @java.lang.Override
+    public int getBook() {
+      return book_;
+    }
+
+    public static final int PARAGRAPH_FIELD_NUMBER = 4;
+    private int paragraph_ = 0;
+    /**
+     * <code>int32 paragraph = 4;</code>
+     * @return The paragraph.
+     */
+    @java.lang.Override
+    public int getParagraph() {
+      return paragraph_;
+    }
+
+    public static final int CONTENT_FIELD_NUMBER = 5;
+    @SuppressWarnings("serial")
+    private volatile java.lang.Object content_ = "";
+    /**
+     * <code>string content = 5;</code>
+     * @return The content.
+     */
+    @java.lang.Override
+    public java.lang.String getContent() {
+      java.lang.Object ref = content_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        content_ = s;
+        return s;
+      }
+    }
+    /**
+     * <code>string content = 5;</code>
+     * @return The bytes for content.
+     */
+    @java.lang.Override
+    public com.google.protobuf.ByteString
+        getContentBytes() {
+      java.lang.Object ref = content_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        content_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    private byte memoizedIsInitialized = -1;
+    @java.lang.Override
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized == 1) return true;
+      if (isInitialized == 0) return false;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    @java.lang.Override
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      if (rank_ != 0) {
+        output.writeInt32(1, rank_);
+      }
+      if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(highlight_)) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 2, highlight_);
+      }
+      if (book_ != 0) {
+        output.writeInt32(3, book_);
+      }
+      if (paragraph_ != 0) {
+        output.writeInt32(4, paragraph_);
+      }
+      if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(content_)) {
+        com.google.protobuf.GeneratedMessageV3.writeString(output, 5, content_);
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    @java.lang.Override
+    public int getSerializedSize() {
+      int size = memoizedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (rank_ != 0) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, rank_);
+      }
+      if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(highlight_)) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, highlight_);
+      }
+      if (book_ != 0) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(3, book_);
+      }
+      if (paragraph_ != 0) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(4, paragraph_);
+      }
+      if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(content_)) {
+        size += com.google.protobuf.GeneratedMessageV3.computeStringSize(5, content_);
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSize = size;
+      return size;
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item)) {
+        return super.equals(obj);
+      }
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item other = (com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item) obj;
+
+      if (getRank()
+          != other.getRank()) return false;
+      if (!getHighlight()
+          .equals(other.getHighlight())) return false;
+      if (getBook()
+          != other.getBook()) return false;
+      if (getParagraph()
+          != other.getParagraph()) return false;
+      if (!getContent()
+          .equals(other.getContent())) return false;
+      if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+      return true;
+    }
+
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptor().hashCode();
+      hash = (37 * hash) + RANK_FIELD_NUMBER;
+      hash = (53 * hash) + getRank();
+      hash = (37 * hash) + HIGHLIGHT_FIELD_NUMBER;
+      hash = (53 * hash) + getHighlight().hashCode();
+      hash = (37 * hash) + BOOK_FIELD_NUMBER;
+      hash = (53 * hash) + getBook();
+      hash = (37 * hash) + PARAGRAPH_FIELD_NUMBER;
+      hash = (53 * hash) + getParagraph();
+      hash = (37 * hash) + CONTENT_FIELD_NUMBER;
+      hash = (53 * hash) + getContent().hashCode();
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        java.nio.ByteBuffer data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        java.nio.ByteBuffer data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input);
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input);
+    }
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return com.google.protobuf.GeneratedMessageV3
+          .parseWithIOException(PARSER, input, extensionRegistry);
+    }
+
+    @java.lang.Override
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder() {
+      return DEFAULT_INSTANCE.toBuilder();
+    }
+    public static Builder newBuilder(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item prototype) {
+      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+    }
+    @java.lang.Override
+    public Builder toBuilder() {
+      return this == DEFAULT_INSTANCE
+          ? new Builder() : new Builder().mergeFrom(this);
+    }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code mint.tulip.v1.SearchResponse.Item}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:mint.tulip.v1.SearchResponse.Item)
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_Item_descriptor;
+      }
+
+      @java.lang.Override
+      protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_Item_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder.class);
+      }
+
+      // Construct using com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.newBuilder()
+      private Builder() {
+
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+        super(parent);
+
+      }
+      @java.lang.Override
+      public Builder clear() {
+        super.clear();
+        bitField0_ = 0;
+        rank_ = 0;
+        highlight_ = "";
+        book_ = 0;
+        paragraph_ = 0;
+        content_ = "";
+        return this;
+      }
+
+      @java.lang.Override
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_Item_descriptor;
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getDefaultInstanceForType() {
+        return com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.getDefaultInstance();
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item build() {
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      @java.lang.Override
+      public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item buildPartial() {
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item result = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item(this);
+        if (bitField0_ != 0) { buildPartial0(result); }
+        onBuilt();
+        return result;
+      }
+
+      private void buildPartial0(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item result) {
+        int from_bitField0_ = bitField0_;
+        if (((from_bitField0_ & 0x00000001) != 0)) {
+          result.rank_ = rank_;
+        }
+        if (((from_bitField0_ & 0x00000002) != 0)) {
+          result.highlight_ = highlight_;
+        }
+        if (((from_bitField0_ & 0x00000004) != 0)) {
+          result.book_ = book_;
+        }
+        if (((from_bitField0_ & 0x00000008) != 0)) {
+          result.paragraph_ = paragraph_;
+        }
+        if (((from_bitField0_ & 0x00000010) != 0)) {
+          result.content_ = content_;
+        }
+      }
+
+      @java.lang.Override
+      public Builder clone() {
+        return super.clone();
+      }
+      @java.lang.Override
+      public Builder setField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.setField(field, value);
+      }
+      @java.lang.Override
+      public Builder clearField(
+          com.google.protobuf.Descriptors.FieldDescriptor field) {
+        return super.clearField(field);
+      }
+      @java.lang.Override
+      public Builder clearOneof(
+          com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+        return super.clearOneof(oneof);
+      }
+      @java.lang.Override
+      public Builder setRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          int index, java.lang.Object value) {
+        return super.setRepeatedField(field, index, value);
+      }
+      @java.lang.Override
+      public Builder addRepeatedField(
+          com.google.protobuf.Descriptors.FieldDescriptor field,
+          java.lang.Object value) {
+        return super.addRepeatedField(field, value);
+      }
+      @java.lang.Override
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item) {
+          return mergeFrom((com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item other) {
+        if (other == com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.getDefaultInstance()) return this;
+        if (other.getRank() != 0) {
+          setRank(other.getRank());
+        }
+        if (!other.getHighlight().isEmpty()) {
+          highlight_ = other.highlight_;
+          bitField0_ |= 0x00000002;
+          onChanged();
+        }
+        if (other.getBook() != 0) {
+          setBook(other.getBook());
+        }
+        if (other.getParagraph() != 0) {
+          setParagraph(other.getParagraph());
+        }
+        if (!other.getContent().isEmpty()) {
+          content_ = other.content_;
+          bitField0_ |= 0x00000010;
+          onChanged();
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        onChanged();
+        return this;
+      }
+
+      @java.lang.Override
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      @java.lang.Override
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        if (extensionRegistry == null) {
+          throw new java.lang.NullPointerException();
+        }
+        try {
+          boolean done = false;
+          while (!done) {
+            int tag = input.readTag();
+            switch (tag) {
+              case 0:
+                done = true;
+                break;
+              case 8: {
+                rank_ = input.readInt32();
+                bitField0_ |= 0x00000001;
+                break;
+              } // case 8
+              case 18: {
+                highlight_ = input.readStringRequireUtf8();
+                bitField0_ |= 0x00000002;
+                break;
+              } // case 18
+              case 24: {
+                book_ = input.readInt32();
+                bitField0_ |= 0x00000004;
+                break;
+              } // case 24
+              case 32: {
+                paragraph_ = input.readInt32();
+                bitField0_ |= 0x00000008;
+                break;
+              } // case 32
+              case 42: {
+                content_ = input.readStringRequireUtf8();
+                bitField0_ |= 0x00000010;
+                break;
+              } // case 42
+              default: {
+                if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                  done = true; // was an endgroup tag
+                }
+                break;
+              } // default:
+            } // switch (tag)
+          } // while (!done)
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw e.unwrapIOException();
+        } finally {
+          onChanged();
+        } // finally
+        return this;
+      }
+      private int bitField0_;
+
+      private int rank_ ;
+      /**
+       * <code>int32 rank = 1;</code>
+       * @return The rank.
+       */
+      @java.lang.Override
+      public int getRank() {
+        return rank_;
+      }
+      /**
+       * <code>int32 rank = 1;</code>
+       * @param value The rank to set.
+       * @return This builder for chaining.
+       */
+      public Builder setRank(int value) {
+
+        rank_ = value;
+        bitField0_ |= 0x00000001;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>int32 rank = 1;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearRank() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        rank_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private java.lang.Object highlight_ = "";
+      /**
+       * <code>string highlight = 2;</code>
+       * @return The highlight.
+       */
+      public java.lang.String getHighlight() {
+        java.lang.Object ref = highlight_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          highlight_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>string highlight = 2;</code>
+       * @return The bytes for highlight.
+       */
+      public com.google.protobuf.ByteString
+          getHighlightBytes() {
+        java.lang.Object ref = highlight_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          highlight_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>string highlight = 2;</code>
+       * @param value The highlight to set.
+       * @return This builder for chaining.
+       */
+      public Builder setHighlight(
+          java.lang.String value) {
+        if (value == null) { throw new NullPointerException(); }
+        highlight_ = value;
+        bitField0_ |= 0x00000002;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>string highlight = 2;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearHighlight() {
+        highlight_ = getDefaultInstance().getHighlight();
+        bitField0_ = (bitField0_ & ~0x00000002);
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>string highlight = 2;</code>
+       * @param value The bytes for highlight to set.
+       * @return This builder for chaining.
+       */
+      public Builder setHighlightBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) { throw new NullPointerException(); }
+        checkByteStringIsUtf8(value);
+        highlight_ = value;
+        bitField0_ |= 0x00000002;
+        onChanged();
+        return this;
+      }
+
+      private int book_ ;
+      /**
+       * <code>int32 book = 3;</code>
+       * @return The book.
+       */
+      @java.lang.Override
+      public int getBook() {
+        return book_;
+      }
+      /**
+       * <code>int32 book = 3;</code>
+       * @param value The book to set.
+       * @return This builder for chaining.
+       */
+      public Builder setBook(int value) {
+
+        book_ = value;
+        bitField0_ |= 0x00000004;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>int32 book = 3;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearBook() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        book_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private int paragraph_ ;
+      /**
+       * <code>int32 paragraph = 4;</code>
+       * @return The paragraph.
+       */
+      @java.lang.Override
+      public int getParagraph() {
+        return paragraph_;
+      }
+      /**
+       * <code>int32 paragraph = 4;</code>
+       * @param value The paragraph to set.
+       * @return This builder for chaining.
+       */
+      public Builder setParagraph(int value) {
+
+        paragraph_ = value;
+        bitField0_ |= 0x00000008;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>int32 paragraph = 4;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearParagraph() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        paragraph_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private java.lang.Object content_ = "";
+      /**
+       * <code>string content = 5;</code>
+       * @return The content.
+       */
+      public java.lang.String getContent() {
+        java.lang.Object ref = content_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          content_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>string content = 5;</code>
+       * @return The bytes for content.
+       */
+      public com.google.protobuf.ByteString
+          getContentBytes() {
+        java.lang.Object ref = content_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          content_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>string content = 5;</code>
+       * @param value The content to set.
+       * @return This builder for chaining.
+       */
+      public Builder setContent(
+          java.lang.String value) {
+        if (value == null) { throw new NullPointerException(); }
+        content_ = value;
+        bitField0_ |= 0x00000010;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>string content = 5;</code>
+       * @return This builder for chaining.
+       */
+      public Builder clearContent() {
+        content_ = getDefaultInstance().getContent();
+        bitField0_ = (bitField0_ & ~0x00000010);
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>string content = 5;</code>
+       * @param value The bytes for content to set.
+       * @return This builder for chaining.
+       */
+      public Builder setContentBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) { throw new NullPointerException(); }
+        checkByteStringIsUtf8(value);
+        content_ = value;
+        bitField0_ |= 0x00000010;
+        onChanged();
+        return this;
+      }
+      @java.lang.Override
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.setUnknownFields(unknownFields);
+      }
+
+      @java.lang.Override
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return super.mergeUnknownFields(unknownFields);
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:mint.tulip.v1.SearchResponse.Item)
+    }
+
+    // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchResponse.Item)
+    private static final com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item DEFAULT_INSTANCE;
+    static {
+      DEFAULT_INSTANCE = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item();
+    }
+
+    public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getDefaultInstance() {
+      return DEFAULT_INSTANCE;
+    }
+
+    private static final com.google.protobuf.Parser<Item>
+        PARSER = new com.google.protobuf.AbstractParser<Item>() {
+      @java.lang.Override
+      public Item parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        Builder builder = newBuilder();
+        try {
+          builder.mergeFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw e.setUnfinishedMessage(builder.buildPartial());
+        } catch (com.google.protobuf.UninitializedMessageException e) {
+          throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+        } catch (java.io.IOException e) {
+          throw new com.google.protobuf.InvalidProtocolBufferException(e)
+              .setUnfinishedMessage(builder.buildPartial());
+        }
+        return builder.buildPartial();
+      }
+    };
+
+    public static com.google.protobuf.Parser<Item> parser() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<Item> getParserForType() {
+      return PARSER;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getDefaultInstanceForType() {
+      return DEFAULT_INSTANCE;
+    }
+
+  }
+
+  private int bitField0_;
+  public static final int ITEMS_FIELD_NUMBER = 1;
+  @SuppressWarnings("serial")
+  private java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> items_;
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  @java.lang.Override
+  public java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> getItemsList() {
+    return items_;
+  }
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  @java.lang.Override
+  public java.util.List<? extends com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder> 
+      getItemsOrBuilderList() {
+    return items_;
+  }
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  @java.lang.Override
+  public int getItemsCount() {
+    return items_.size();
+  }
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getItems(int index) {
+    return items_.get(index);
+  }
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder getItemsOrBuilder(
+      int index) {
+    return items_.get(index);
+  }
+
+  public static final int PAGE_FIELD_NUMBER = 98;
+  private com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page page_;
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   * @return Whether the page field is set.
+   */
+  @java.lang.Override
+  public boolean hasPage() {
+    return ((bitField0_ & 0x00000001) != 0);
+  }
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   * @return The page.
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage() {
+    return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+  }
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   */
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder() {
+    return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+  }
+
+  public static final int TOTAL_FIELD_NUMBER = 99;
+  private int total_ = 0;
+  /**
+   * <code>int32 total = 99;</code>
+   * @return The total.
+   */
+  @java.lang.Override
+  public int getTotal() {
+    return total_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    for (int i = 0; i < items_.size(); i++) {
+      output.writeMessage(1, items_.get(i));
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      output.writeMessage(98, getPage());
+    }
+    if (total_ != 0) {
+      output.writeInt32(99, total_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    for (int i = 0; i < items_.size(); i++) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(1, items_.get(i));
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(98, getPage());
+    }
+    if (total_ != 0) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt32Size(99, total_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse)) {
+      return super.equals(obj);
+    }
+    com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse other = (com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse) obj;
+
+    if (!getItemsList()
+        .equals(other.getItemsList())) return false;
+    if (hasPage() != other.hasPage()) return false;
+    if (hasPage()) {
+      if (!getPage()
+          .equals(other.getPage())) return false;
+    }
+    if (getTotal()
+        != other.getTotal()) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    if (getItemsCount() > 0) {
+      hash = (37 * hash) + ITEMS_FIELD_NUMBER;
+      hash = (53 * hash) + getItemsList().hashCode();
+    }
+    if (hasPage()) {
+      hash = (37 * hash) + PAGE_FIELD_NUMBER;
+      hash = (53 * hash) + getPage().hashCode();
+    }
+    hash = (37 * hash) + TOTAL_FIELD_NUMBER;
+    hash = (53 * hash) + getTotal();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code mint.tulip.v1.SearchResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:mint.tulip.v1.SearchResponse)
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.class, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Builder.class);
+    }
+
+    // Construct using com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getItemsFieldBuilder();
+        getPageFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      if (itemsBuilder_ == null) {
+        items_ = java.util.Collections.emptyList();
+      } else {
+        items_ = null;
+        itemsBuilder_.clear();
+      }
+      bitField0_ = (bitField0_ & ~0x00000001);
+      page_ = null;
+      if (pageBuilder_ != null) {
+        pageBuilder_.dispose();
+        pageBuilder_ = null;
+      }
+      total_ = 0;
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.Tulip.internal_static_mint_tulip_v1_SearchResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse getDefaultInstanceForType() {
+      return com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse build() {
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse buildPartial() {
+      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse result = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse(this);
+      buildPartialRepeatedFields(result);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartialRepeatedFields(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse result) {
+      if (itemsBuilder_ == null) {
+        if (((bitField0_ & 0x00000001) != 0)) {
+          items_ = java.util.Collections.unmodifiableList(items_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.items_ = items_;
+      } else {
+        result.items_ = itemsBuilder_.build();
+      }
+    }
+
+    private void buildPartial0(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse result) {
+      int from_bitField0_ = bitField0_;
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.page_ = pageBuilder_ == null
+            ? page_
+            : pageBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      if (((from_bitField0_ & 0x00000004) != 0)) {
+        result.total_ = total_;
+      }
+      result.bitField0_ |= to_bitField0_;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse) {
+        return mergeFrom((com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse other) {
+      if (other == com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.getDefaultInstance()) return this;
+      if (itemsBuilder_ == null) {
+        if (!other.items_.isEmpty()) {
+          if (items_.isEmpty()) {
+            items_ = other.items_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureItemsIsMutable();
+            items_.addAll(other.items_);
+          }
+          onChanged();
+        }
+      } else {
+        if (!other.items_.isEmpty()) {
+          if (itemsBuilder_.isEmpty()) {
+            itemsBuilder_.dispose();
+            itemsBuilder_ = null;
+            items_ = other.items_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+            itemsBuilder_ = 
+              com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ?
+                 getItemsFieldBuilder() : null;
+          } else {
+            itemsBuilder_.addAllMessages(other.items_);
+          }
+        }
+      }
+      if (other.hasPage()) {
+        mergePage(other.getPage());
+      }
+      if (other.getTotal() != 0) {
+        setTotal(other.getTotal());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item m =
+                  input.readMessage(
+                      com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.parser(),
+                      extensionRegistry);
+              if (itemsBuilder_ == null) {
+                ensureItemsIsMutable();
+                items_.add(m);
+              } else {
+                itemsBuilder_.addMessage(m);
+              }
+              break;
+            } // case 10
+            case 786: {
+              input.readMessage(
+                  getPageFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 786
+            case 792: {
+              total_ = input.readInt32();
+              bitField0_ |= 0x00000004;
+              break;
+            } // case 792
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> items_ =
+      java.util.Collections.emptyList();
+    private void ensureItemsIsMutable() {
+      if (!((bitField0_ & 0x00000001) != 0)) {
+        items_ = new java.util.ArrayList<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item>(items_);
+        bitField0_ |= 0x00000001;
+       }
+    }
+
+    private com.google.protobuf.RepeatedFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder> itemsBuilder_;
+
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> getItemsList() {
+      if (itemsBuilder_ == null) {
+        return java.util.Collections.unmodifiableList(items_);
+      } else {
+        return itemsBuilder_.getMessageList();
+      }
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public int getItemsCount() {
+      if (itemsBuilder_ == null) {
+        return items_.size();
+      } else {
+        return itemsBuilder_.getCount();
+      }
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getItems(int index) {
+      if (itemsBuilder_ == null) {
+        return items_.get(index);
+      } else {
+        return itemsBuilder_.getMessage(index);
+      }
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder setItems(
+        int index, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item value) {
+      if (itemsBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureItemsIsMutable();
+        items_.set(index, value);
+        onChanged();
+      } else {
+        itemsBuilder_.setMessage(index, value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder setItems(
+        int index, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder builderForValue) {
+      if (itemsBuilder_ == null) {
+        ensureItemsIsMutable();
+        items_.set(index, builderForValue.build());
+        onChanged();
+      } else {
+        itemsBuilder_.setMessage(index, builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder addItems(com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item value) {
+      if (itemsBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureItemsIsMutable();
+        items_.add(value);
+        onChanged();
+      } else {
+        itemsBuilder_.addMessage(value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder addItems(
+        int index, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item value) {
+      if (itemsBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureItemsIsMutable();
+        items_.add(index, value);
+        onChanged();
+      } else {
+        itemsBuilder_.addMessage(index, value);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder addItems(
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder builderForValue) {
+      if (itemsBuilder_ == null) {
+        ensureItemsIsMutable();
+        items_.add(builderForValue.build());
+        onChanged();
+      } else {
+        itemsBuilder_.addMessage(builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder addItems(
+        int index, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder builderForValue) {
+      if (itemsBuilder_ == null) {
+        ensureItemsIsMutable();
+        items_.add(index, builderForValue.build());
+        onChanged();
+      } else {
+        itemsBuilder_.addMessage(index, builderForValue.build());
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder addAllItems(
+        java.lang.Iterable<? extends com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> values) {
+      if (itemsBuilder_ == null) {
+        ensureItemsIsMutable();
+        com.google.protobuf.AbstractMessageLite.Builder.addAll(
+            values, items_);
+        onChanged();
+      } else {
+        itemsBuilder_.addAllMessages(values);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder clearItems() {
+      if (itemsBuilder_ == null) {
+        items_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        onChanged();
+      } else {
+        itemsBuilder_.clear();
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public Builder removeItems(int index) {
+      if (itemsBuilder_ == null) {
+        ensureItemsIsMutable();
+        items_.remove(index);
+        onChanged();
+      } else {
+        itemsBuilder_.remove(index);
+      }
+      return this;
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder getItemsBuilder(
+        int index) {
+      return getItemsFieldBuilder().getBuilder(index);
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder getItemsOrBuilder(
+        int index) {
+      if (itemsBuilder_ == null) {
+        return items_.get(index);  } else {
+        return itemsBuilder_.getMessageOrBuilder(index);
+      }
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public java.util.List<? extends com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder> 
+         getItemsOrBuilderList() {
+      if (itemsBuilder_ != null) {
+        return itemsBuilder_.getMessageOrBuilderList();
+      } else {
+        return java.util.Collections.unmodifiableList(items_);
+      }
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder addItemsBuilder() {
+      return getItemsFieldBuilder().addBuilder(
+          com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.getDefaultInstance());
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder addItemsBuilder(
+        int index) {
+      return getItemsFieldBuilder().addBuilder(
+          index, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.getDefaultInstance());
+    }
+    /**
+     * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    public java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder> 
+         getItemsBuilderList() {
+      return getItemsFieldBuilder().getBuilderList();
+    }
+    private com.google.protobuf.RepeatedFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder> 
+        getItemsFieldBuilder() {
+      if (itemsBuilder_ == null) {
+        itemsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3<
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder>(
+                items_,
+                ((bitField0_ & 0x00000001) != 0),
+                getParentForChildren(),
+                isClean());
+        items_ = null;
+      }
+      return itemsBuilder_;
+    }
+
+    private com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page page_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder> pageBuilder_;
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @return Whether the page field is set.
+     */
+    public boolean hasPage() {
+      return ((bitField0_ & 0x00000002) != 0);
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @return The page.
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage() {
+      if (pageBuilder_ == null) {
+        return page_ == null ? com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+      } else {
+        return pageBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public Builder setPage(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page value) {
+      if (pageBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        page_ = value;
+      } else {
+        pageBuilder_.setMessage(value);
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public Builder setPage(
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder builderForValue) {
+      if (pageBuilder_ == null) {
+        page_ = builderForValue.build();
+      } else {
+        pageBuilder_.setMessage(builderForValue.build());
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public Builder mergePage(com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page value) {
+      if (pageBuilder_ == null) {
+        if (((bitField0_ & 0x00000002) != 0) &&
+          page_ != null &&
+          page_ != com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance()) {
+          getPageBuilder().mergeFrom(value);
+        } else {
+          page_ = value;
+        }
+      } else {
+        pageBuilder_.mergeFrom(value);
+      }
+      if (page_ != null) {
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      return this;
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public Builder clearPage() {
+      bitField0_ = (bitField0_ & ~0x00000002);
+      page_ = null;
+      if (pageBuilder_ != null) {
+        pageBuilder_.dispose();
+        pageBuilder_ = null;
+      }
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder getPageBuilder() {
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return getPageFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    public com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder() {
+      if (pageBuilder_ != null) {
+        return pageBuilder_.getMessageOrBuilder();
+      } else {
+        return page_ == null ?
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.getDefaultInstance() : page_;
+      }
+    }
+    /**
+     * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder> 
+        getPageFieldBuilder() {
+      if (pageBuilder_ == null) {
+        pageBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page.Builder, com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder>(
+                getPage(),
+                getParentForChildren(),
+                isClean());
+        page_ = null;
+      }
+      return pageBuilder_;
+    }
+
+    private int total_ ;
+    /**
+     * <code>int32 total = 99;</code>
+     * @return The total.
+     */
+    @java.lang.Override
+    public int getTotal() {
+      return total_;
+    }
+    /**
+     * <code>int32 total = 99;</code>
+     * @param value The total to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTotal(int value) {
+
+      total_ = value;
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int32 total = 99;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTotal() {
+      bitField0_ = (bitField0_ & ~0x00000004);
+      total_ = 0;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:mint.tulip.v1.SearchResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:mint.tulip.v1.SearchResponse)
+  private static final com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse();
+  }
+
+  public static com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<SearchResponse>
+      PARSER = new com.google.protobuf.AbstractParser<SearchResponse>() {
+    @java.lang.Override
+    public SearchResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<SearchResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<SearchResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 54 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/SearchResponseOrBuilder.java

@@ -0,0 +1,54 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+public interface SearchResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:mint.tulip.v1.SearchResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  java.util.List<com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item> 
+      getItemsList();
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.Item getItems(int index);
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  int getItemsCount();
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  java.util.List<? extends com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder> 
+      getItemsOrBuilderList();
+  /**
+   * <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchResponse.ItemOrBuilder getItemsOrBuilder(
+      int index);
+
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   * @return Whether the page field is set.
+   */
+  boolean hasPage();
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   * @return The page.
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.Page getPage();
+  /**
+   * <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+   */
+  com.github.iapt_platform.mint.plugins.tulip.v1.SearchRequest.PageOrBuilder getPageOrBuilder();
+
+  /**
+   * <code>int32 total = 99;</code>
+   * @return The total.
+   */
+  int getTotal();
+}

+ 92 - 0
rpc/sdk/java/com/github/iapt_platform/mint/plugins/tulip/v1/Tulip.java

@@ -0,0 +1,92 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tulip.proto
+
+package com.github.iapt_platform.mint.plugins.tulip.v1;
+
+public final class Tulip {
+  private Tulip() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_mint_tulip_v1_SearchRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_mint_tulip_v1_SearchRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_mint_tulip_v1_SearchRequest_Page_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_mint_tulip_v1_SearchRequest_Page_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_mint_tulip_v1_SearchResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_mint_tulip_v1_SearchResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_mint_tulip_v1_SearchResponse_Item_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_mint_tulip_v1_SearchResponse_Item_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\013tulip.proto\022\rmint.tulip.v1\"\223\001\n\rSearchR" +
+      "equest\022\020\n\010keywords\030\001 \003(\t\022\014\n\004book\030\002 \001(\005\0224" +
+      "\n\004page\030c \001(\0132!.mint.tulip.v1.SearchReque" +
+      "st.PageH\000\210\001\001\032#\n\004Page\022\r\n\005index\030\001 \001(\005\022\014\n\004s" +
+      "ize\030\002 \001(\005B\007\n\005_page\"\336\001\n\016SearchResponse\0221\n" +
+      "\005items\030\001 \003(\0132\".mint.tulip.v1.SearchRespo" +
+      "nse.Item\022/\n\004page\030b \001(\0132!.mint.tulip.v1.S" +
+      "earchRequest.Page\022\r\n\005total\030c \001(\005\032Y\n\004Item" +
+      "\022\014\n\004rank\030\001 \001(\005\022\021\n\thighlight\030\002 \001(\t\022\014\n\004boo" +
+      "k\030\003 \001(\005\022\021\n\tparagraph\030\004 \001(\005\022\017\n\007content\030\005 " +
+      "\001(\t2O\n\006Search\022E\n\004Pali\022\034.mint.tulip.v1.Se" +
+      "archRequest\032\035.mint.tulip.v1.SearchRespon" +
+      "se\"\000B2\n.com.github.iapt_platform.mint.pl" +
+      "ugins.tulip.v1P\001b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        });
+    internal_static_mint_tulip_v1_SearchRequest_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_mint_tulip_v1_SearchRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_mint_tulip_v1_SearchRequest_descriptor,
+        new java.lang.String[] { "Keywords", "Book", "Page", "Page", });
+    internal_static_mint_tulip_v1_SearchRequest_Page_descriptor =
+      internal_static_mint_tulip_v1_SearchRequest_descriptor.getNestedTypes().get(0);
+    internal_static_mint_tulip_v1_SearchRequest_Page_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_mint_tulip_v1_SearchRequest_Page_descriptor,
+        new java.lang.String[] { "Index", "Size", });
+    internal_static_mint_tulip_v1_SearchResponse_descriptor =
+      getDescriptor().getMessageTypes().get(1);
+    internal_static_mint_tulip_v1_SearchResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_mint_tulip_v1_SearchResponse_descriptor,
+        new java.lang.String[] { "Items", "Page", "Total", });
+    internal_static_mint_tulip_v1_SearchResponse_Item_descriptor =
+      internal_static_mint_tulip_v1_SearchResponse_descriptor.getNestedTypes().get(0);
+    internal_static_mint_tulip_v1_SearchResponse_Item_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_mint_tulip_v1_SearchResponse_Item_descriptor,
+        new java.lang.String[] { "Rank", "Highlight", "Book", "Paragraph", "Content", });
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 1 - 1
rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/EpubGrpc.java

@@ -5,7 +5,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName;
 /**
  */
 @javax.annotation.Generated(
-    value = "by gRPC proto compiler (version 1.58.0)",
+    value = "by gRPC proto compiler (version 1.59.0)",
     comments = "Source: lily.proto")
 @io.grpc.stub.annotations.GrpcGenerated
 public final class EpubGrpc {

+ 1 - 1
rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/ExcelGrpc.java

@@ -5,7 +5,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName;
 /**
  */
 @javax.annotation.Generated(
-    value = "by gRPC proto compiler (version 1.58.0)",
+    value = "by gRPC proto compiler (version 1.59.0)",
     comments = "Source: lily.proto")
 @io.grpc.stub.annotations.GrpcGenerated
 public final class ExcelGrpc {

+ 1 - 1
rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/S3Grpc.java

@@ -5,7 +5,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName;
 /**
  */
 @javax.annotation.Generated(
-    value = "by gRPC proto compiler (version 1.58.0)",
+    value = "by gRPC proto compiler (version 1.59.0)",
     comments = "Source: lily.proto")
 @io.grpc.stub.annotations.GrpcGenerated
 public final class S3Grpc {

+ 1 - 1
rpc/sdk/java/com/github/saturn_xiv/palm/plugins/lily/v1/TexGrpc.java

@@ -5,7 +5,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName;
 /**
  */
 @javax.annotation.Generated(
-    value = "by gRPC proto compiler (version 1.58.0)",
+    value = "by gRPC proto compiler (version 1.59.0)",
     comments = "Source: lily.proto")
 @io.grpc.stub.annotations.GrpcGenerated
 public final class TexGrpc {

BIN
rpc/sdk/php/GPBMetadata/Tulip.php


+ 33 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchClient.php

@@ -0,0 +1,33 @@
+<?php
+// GENERATED CODE -- DO NOT EDIT!
+
+namespace Mint\Tulip\V1;
+
+/**
+ */
+class SearchClient extends \Grpc\BaseStub {
+
+    /**
+     * @param string $hostname hostname
+     * @param array $opts channel options
+     * @param \Grpc\Channel $channel (optional) re-use channel object
+     */
+    public function __construct($hostname, $opts, $channel = null) {
+        parent::__construct($hostname, $opts, $channel);
+    }
+
+    /**
+     * @param \Mint\Tulip\V1\SearchRequest $argument input argument
+     * @param array $metadata metadata
+     * @param array $options call options
+     * @return \Grpc\UnaryCall
+     */
+    public function Pali(\Mint\Tulip\V1\SearchRequest $argument,
+      $metadata = [], $options = []) {
+        return $this->_simpleRequest('/mint.tulip.v1.Search/Pali',
+        $argument,
+        ['\Mint\Tulip\V1\SearchResponse', 'decode'],
+        $metadata, $options);
+    }
+
+}

+ 124 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchRequest.php

@@ -0,0 +1,124 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * ----------------------------------------------------------------------------
+ *
+ * Generated from protobuf message <code>mint.tulip.v1.SearchRequest</code>
+ */
+class SearchRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     */
+    private $keywords;
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     */
+    protected $book = 0;
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    protected $page = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type array<string>|\Google\Protobuf\Internal\RepeatedField $keywords
+     *     @type int $book
+     *     @type \Mint\Tulip\V1\SearchRequest\Page $page
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getKeywords()
+    {
+        return $this->keywords;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     * @param array<string>|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setKeywords($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->keywords = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     * @return int
+     */
+    public function getBook()
+    {
+        return $this->book;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBook($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->book = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @return \Mint\Tulip\V1\SearchRequest\Page|null
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    public function hasPage()
+    {
+        return isset($this->page);
+    }
+
+    public function clearPage()
+    {
+        unset($this->page);
+    }
+
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @param \Mint\Tulip\V1\SearchRequest\Page $var
+     * @return $this
+     */
+    public function setPage($var)
+    {
+        GPBUtil::checkMessage($var, \Mint\Tulip\V1\SearchRequest\Page::class);
+        $this->page = $var;
+
+        return $this;
+    }
+
+}
+

+ 88 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchRequest/Page.php

@@ -0,0 +1,88 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1\SearchRequest;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchRequest.Page</code>
+ */
+class Page extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     */
+    protected $index = 0;
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     */
+    protected $size = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $index
+     *     @type int $size
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     * @return int
+     */
+    public function getIndex()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setIndex($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->index = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     * @return int
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->size = $var;
+
+        return $this;
+    }
+
+}
+
+// Adding a class alias for backwards compatibility with the previous class name.
+class_alias(Page::class, \Mint\Tulip\V1\SearchRequest_Page::class);
+

+ 122 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchResponse.php

@@ -0,0 +1,122 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchResponse</code>
+ */
+class SearchResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    private $items;
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    protected $page = null;
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     */
+    protected $total = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type array<\Mint\Tulip\V1\SearchResponse\Item>|\Google\Protobuf\Internal\RepeatedField $items
+     *     @type \Mint\Tulip\V1\SearchRequest\Page $page
+     *     @type int $total
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getItems()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     * @param array<\Mint\Tulip\V1\SearchResponse\Item>|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setItems($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Mint\Tulip\V1\SearchResponse\Item::class);
+        $this->items = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @return \Mint\Tulip\V1\SearchRequest\Page|null
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    public function hasPage()
+    {
+        return isset($this->page);
+    }
+
+    public function clearPage()
+    {
+        unset($this->page);
+    }
+
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @param \Mint\Tulip\V1\SearchRequest\Page $var
+     * @return $this
+     */
+    public function setPage($var)
+    {
+        GPBUtil::checkMessage($var, \Mint\Tulip\V1\SearchRequest\Page::class);
+        $this->page = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     * @return int
+     */
+    public function getTotal()
+    {
+        return $this->total;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotal($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->total = $var;
+
+        return $this;
+    }
+
+}
+

+ 169 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchResponse/Item.php

@@ -0,0 +1,169 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1\SearchResponse;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchResponse.Item</code>
+ */
+class Item extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     */
+    protected $rank = 0;
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     */
+    protected $highlight = '';
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     */
+    protected $book = 0;
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     */
+    protected $paragraph = 0;
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     */
+    protected $content = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $rank
+     *     @type string $highlight
+     *     @type int $book
+     *     @type int $paragraph
+     *     @type string $content
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     * @return int
+     */
+    public function getRank()
+    {
+        return $this->rank;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setRank($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->rank = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     * @return string
+     */
+    public function getHighlight()
+    {
+        return $this->highlight;
+    }
+
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHighlight($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->highlight = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     * @return int
+     */
+    public function getBook()
+    {
+        return $this->book;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBook($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->book = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     * @return int
+     */
+    public function getParagraph()
+    {
+        return $this->paragraph;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setParagraph($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->paragraph = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     * @return string
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContent($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->content = $var;
+
+        return $this;
+    }
+
+}
+
+// Adding a class alias for backwards compatibility with the previous class name.
+class_alias(Item::class, \Mint\Tulip\V1\SearchResponse_Item::class);
+

+ 41 - 0
rpc/sdk/php/Mint/Tulip/V1/SearchStub.php

@@ -0,0 +1,41 @@
+<?php
+// GENERATED CODE -- DO NOT EDIT!
+
+namespace Mint\Tulip\V1;
+
+/**
+ */
+class SearchStub {
+
+    /**
+     * @param \Mint\Tulip\V1\SearchRequest $request client request
+     * @param \Grpc\ServerContext $context server request context
+     * @return \Mint\Tulip\V1\SearchResponse for response data, null if if error occured
+     *     initial metadata (if any) and status (if not ok) should be set to $context
+     */
+    public function Pali(
+        \Mint\Tulip\V1\SearchRequest $request,
+        \Grpc\ServerContext $context
+    ): ?\Mint\Tulip\V1\SearchResponse {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        return null;
+    }
+
+    /**
+     * Get the method descriptors of the service for server registration
+     *
+     * @return array of \Grpc\MethodDescriptor for the service methods
+     */
+    public final function getMethodDescriptors(): array
+    {
+        return [
+            '/mint.tulip.v1.Search/Pali' => new \Grpc\MethodDescriptor(
+                $this,
+                'Pali',
+                '\Mint\Tulip\V1\SearchRequest',
+                \Grpc\MethodDescriptor::UNARY_CALL
+            ),
+        ];
+    }
+
+}

+ 34 - 0
rpc/sdk/python/tulip_pb2.py

@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0btulip.proto\x12\rmint.tulip.v1\"\x93\x01\n\rSearchRequest\x12\x10\n\x08keywords\x18\x01 \x03(\t\x12\x0c\n\x04\x62ook\x18\x02 \x01(\x05\x12\x34\n\x04page\x18\x63 \x01(\x0b\x32!.mint.tulip.v1.SearchRequest.PageH\x00\x88\x01\x01\x1a#\n\x04Page\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x0c\n\x04size\x18\x02 \x01(\x05\x42\x07\n\x05_page\"\xde\x01\n\x0eSearchResponse\x12\x31\n\x05items\x18\x01 \x03(\x0b\x32\".mint.tulip.v1.SearchResponse.Item\x12/\n\x04page\x18\x62 \x01(\x0b\x32!.mint.tulip.v1.SearchRequest.Page\x12\r\n\x05total\x18\x63 \x01(\x05\x1aY\n\x04Item\x12\x0c\n\x04rank\x18\x01 \x01(\x05\x12\x11\n\thighlight\x18\x02 \x01(\t\x12\x0c\n\x04\x62ook\x18\x03 \x01(\x05\x12\x11\n\tparagraph\x18\x04 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\t2O\n\x06Search\x12\x45\n\x04Pali\x12\x1c.mint.tulip.v1.SearchRequest\x1a\x1d.mint.tulip.v1.SearchResponse\"\x00\x42\x32\n.com.github.iapt_platform.mint.plugins.tulip.v1P\x01\x62\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tulip_pb2', _globals)
+if _descriptor._USE_C_DESCRIPTORS == False:
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n.com.github.iapt_platform.mint.plugins.tulip.v1P\001'
+  _globals['_SEARCHREQUEST']._serialized_start=31
+  _globals['_SEARCHREQUEST']._serialized_end=178
+  _globals['_SEARCHREQUEST_PAGE']._serialized_start=134
+  _globals['_SEARCHREQUEST_PAGE']._serialized_end=169
+  _globals['_SEARCHRESPONSE']._serialized_start=181
+  _globals['_SEARCHRESPONSE']._serialized_end=403
+  _globals['_SEARCHRESPONSE_ITEM']._serialized_start=314
+  _globals['_SEARCHRESPONSE_ITEM']._serialized_end=403
+  _globals['_SEARCH']._serialized_start=405
+  _globals['_SEARCH']._serialized_end=484
+# @@protoc_insertion_point(module_scope)

+ 66 - 0
rpc/sdk/python/tulip_pb2_grpc.py

@@ -0,0 +1,66 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+"""Client and server classes corresponding to protobuf-defined services."""
+import grpc
+
+import tulip_pb2 as tulip__pb2
+
+
+class SearchStub(object):
+    """Missing associated documentation comment in .proto file."""
+
+    def __init__(self, channel):
+        """Constructor.
+
+        Args:
+            channel: A grpc.Channel.
+        """
+        self.Pali = channel.unary_unary(
+                '/mint.tulip.v1.Search/Pali',
+                request_serializer=tulip__pb2.SearchRequest.SerializeToString,
+                response_deserializer=tulip__pb2.SearchResponse.FromString,
+                )
+
+
+class SearchServicer(object):
+    """Missing associated documentation comment in .proto file."""
+
+    def Pali(self, request, context):
+        """Missing associated documentation comment in .proto file."""
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+
+def add_SearchServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+            'Pali': grpc.unary_unary_rpc_method_handler(
+                    servicer.Pali,
+                    request_deserializer=tulip__pb2.SearchRequest.FromString,
+                    response_serializer=tulip__pb2.SearchResponse.SerializeToString,
+            ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+            'mint.tulip.v1.Search', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+ # This class is part of an EXPERIMENTAL API.
+class Search(object):
+    """Missing associated documentation comment in .proto file."""
+
+    @staticmethod
+    def Pali(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/mint.tulip.v1.Search/Pali',
+            tulip__pb2.SearchRequest.SerializeToString,
+            tulip__pb2.SearchResponse.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

+ 43 - 0
rpc/sdk/ruby/tulip_pb.rb

@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+require 'google/protobuf'
+
+
+descriptor_data = "\n\x0btulip.proto\x12\rmint.tulip.v1\"\x93\x01\n\rSearchRequest\x12\x10\n\x08keywords\x18\x01 \x03(\t\x12\x0c\n\x04\x62ook\x18\x02 \x01(\x05\x12\x34\n\x04page\x18\x63 \x01(\x0b\x32!.mint.tulip.v1.SearchRequest.PageH\x00\x88\x01\x01\x1a#\n\x04Page\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x0c\n\x04size\x18\x02 \x01(\x05\x42\x07\n\x05_page\"\xde\x01\n\x0eSearchResponse\x12\x31\n\x05items\x18\x01 \x03(\x0b\x32\".mint.tulip.v1.SearchResponse.Item\x12/\n\x04page\x18\x62 \x01(\x0b\x32!.mint.tulip.v1.SearchRequest.Page\x12\r\n\x05total\x18\x63 \x01(\x05\x1aY\n\x04Item\x12\x0c\n\x04rank\x18\x01 \x01(\x05\x12\x11\n\thighlight\x18\x02 \x01(\t\x12\x0c\n\x04\x62ook\x18\x03 \x01(\x05\x12\x11\n\tparagraph\x18\x04 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\t2O\n\x06Search\x12\x45\n\x04Pali\x12\x1c.mint.tulip.v1.SearchRequest\x1a\x1d.mint.tulip.v1.SearchResponse\"\x00\x42\x32\n.com.github.iapt_platform.mint.plugins.tulip.v1P\x01\x62\x06proto3"
+
+pool = Google::Protobuf::DescriptorPool.generated_pool
+
+begin
+  pool.add_serialized_file(descriptor_data)
+rescue TypeError => e
+  # Compatibility code: will be removed in the next major version.
+  require 'google/protobuf/descriptor_pb'
+  parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data)
+  parsed.clear_dependency
+  serialized = parsed.class.encode(parsed)
+  file = pool.add_serialized_file(serialized)
+  warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}"
+  imports = [
+  ]
+  imports.each do |type_name, expected_filename|
+    import_file = pool.lookup(type_name).file_descriptor
+    if import_file.name != expected_filename
+      warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}"
+    end
+  end
+  warn "Each proto file must use a consistent fully-qualified name."
+  warn "This will become an error in the next major version."
+end
+
+module Mint
+  module Tulip
+    module V1
+      SearchRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("mint.tulip.v1.SearchRequest").msgclass
+      SearchRequest::Page = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("mint.tulip.v1.SearchRequest.Page").msgclass
+      SearchResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("mint.tulip.v1.SearchResponse").msgclass
+      SearchResponse::Item = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("mint.tulip.v1.SearchResponse.Item").msgclass
+    end
+  end
+end

+ 26 - 0
rpc/sdk/ruby/tulip_services_pb.rb

@@ -0,0 +1,26 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# Source: tulip.proto for package 'mint.tulip.v1'
+
+require 'grpc'
+require 'tulip_pb'
+
+module Mint
+  module Tulip
+    module V1
+      module Search
+        class Service
+
+          include ::GRPC::GenericService
+
+          self.marshal_class_method = :encode
+          self.unmarshal_class_method = :decode
+          self.service_name = 'mint.tulip.v1.Search'
+
+          rpc :Pali, ::Mint::Tulip::V1::SearchRequest, ::Mint::Tulip::V1::SearchResponse
+        end
+
+        Stub = Service.rpc_stub_class
+      end
+    end
+  end
+end

+ 31 - 0
rpc/tulip/Dockerfile

@@ -0,0 +1,31 @@
+FROM ubuntu:latest
+LABEL maintainer="Jeremy Zheng"
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt update
+RUN apt install -y apt-utils debian-keyring debian-archive-keyring apt-transport-https software-properties-common gnupg
+RUN LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php
+RUN apt -y upgrade
+ENV PHP_VERSION="8.2"
+RUN apt install -y sudo wget unzip \
+    php${PHP_VERSION}-zip php${PHP_VERSION}-grpc php${PHP_VERSION}-protobuf
+RUN apt clean
+
+# https://getcomposer.org/download/
+RUN wget https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -O - -q | php -- --quiet --install-dir=/usr/local/bin --filename=composer
+
+RUN useradd -s /bin/bash -m deploy
+RUN passwd -l deploy
+RUN echo 'deploy ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/101-deploy
+RUN mkdir /opt/tulip
+RUN chown deploy:deploy /opt/tulip
+USER deploy
+
+COPY tulip /opt/tulip/
+RUN cd /opt/tulip && composer install
+RUN echo "$(date -u +%4Y%m%d%H%M%S)" | sudo tee /VERSION
+
+WORKDIR /opt/tulip
+
+CMD ["/bin/bash", "-l"]

+ 15 - 0
rpc/tulip/build.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+export VERSION=$(date "+%4Y%m%d%H%M%S")
+export CODE="mint-tulip"
+
+buildah pull ubuntu:latest
+buildah bud --layers -t $CODE .
+podman save --format=oci-archive -o $CODE-$VERSION.tar $CODE
+md5sum $CODE-$VERSION.tar > md5.txt
+
+echo "done($CODE-$VERSION.tar)."
+
+exit 0

+ 15 - 0
rpc/tulip/start.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+export CODE="mint-tulip"
+
+if [ "$#" -ne 1 ]
+then
+    echo "USAGE: $0 PORT"
+    exit 1
+fi
+
+podman run -d --rm --events-backend=file --hostname=palm --network host $CODE /usr/bin/php server.php --port $1
+
+exit 0

+ 2 - 0
rpc/tulip/tulip/.gitignore

@@ -0,0 +1,2 @@
+/vendor/
+/composer.lock

BIN
rpc/tulip/tulip/GPBMetadata/Tulip.php


+ 33 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchClient.php

@@ -0,0 +1,33 @@
+<?php
+// GENERATED CODE -- DO NOT EDIT!
+
+namespace Mint\Tulip\V1;
+
+/**
+ */
+class SearchClient extends \Grpc\BaseStub {
+
+    /**
+     * @param string $hostname hostname
+     * @param array $opts channel options
+     * @param \Grpc\Channel $channel (optional) re-use channel object
+     */
+    public function __construct($hostname, $opts, $channel = null) {
+        parent::__construct($hostname, $opts, $channel);
+    }
+
+    /**
+     * @param \Mint\Tulip\V1\SearchRequest $argument input argument
+     * @param array $metadata metadata
+     * @param array $options call options
+     * @return \Grpc\UnaryCall
+     */
+    public function Pali(\Mint\Tulip\V1\SearchRequest $argument,
+      $metadata = [], $options = []) {
+        return $this->_simpleRequest('/mint.tulip.v1.Search/Pali',
+        $argument,
+        ['\Mint\Tulip\V1\SearchResponse', 'decode'],
+        $metadata, $options);
+    }
+
+}

+ 124 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchRequest.php

@@ -0,0 +1,124 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * ----------------------------------------------------------------------------
+ *
+ * Generated from protobuf message <code>mint.tulip.v1.SearchRequest</code>
+ */
+class SearchRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     */
+    private $keywords;
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     */
+    protected $book = 0;
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     */
+    protected $page = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type array<string>|\Google\Protobuf\Internal\RepeatedField $keywords
+     *     @type int $book
+     *     @type \Mint\Tulip\V1\SearchRequest\Page $page
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getKeywords()
+    {
+        return $this->keywords;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string keywords = 1;</code>
+     * @param array<string>|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setKeywords($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->keywords = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     * @return int
+     */
+    public function getBook()
+    {
+        return $this->book;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBook($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->book = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @return \Mint\Tulip\V1\SearchRequest\Page|null
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    public function hasPage()
+    {
+        return isset($this->page);
+    }
+
+    public function clearPage()
+    {
+        unset($this->page);
+    }
+
+    /**
+     * Generated from protobuf field <code>optional .mint.tulip.v1.SearchRequest.Page page = 99;</code>
+     * @param \Mint\Tulip\V1\SearchRequest\Page $var
+     * @return $this
+     */
+    public function setPage($var)
+    {
+        GPBUtil::checkMessage($var, \Mint\Tulip\V1\SearchRequest\Page::class);
+        $this->page = $var;
+
+        return $this;
+    }
+
+}
+

+ 88 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchRequest/Page.php

@@ -0,0 +1,88 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1\SearchRequest;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchRequest.Page</code>
+ */
+class Page extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     */
+    protected $index = 0;
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     */
+    protected $size = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $index
+     *     @type int $size
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     * @return int
+     */
+    public function getIndex()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 index = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setIndex($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->index = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     * @return int
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 size = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->size = $var;
+
+        return $this;
+    }
+
+}
+
+// Adding a class alias for backwards compatibility with the previous class name.
+class_alias(Page::class, \Mint\Tulip\V1\SearchRequest_Page::class);
+

+ 122 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchResponse.php

@@ -0,0 +1,122 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchResponse</code>
+ */
+class SearchResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     */
+    private $items;
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     */
+    protected $page = null;
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     */
+    protected $total = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type array<\Mint\Tulip\V1\SearchResponse\Item>|\Google\Protobuf\Internal\RepeatedField $items
+     *     @type \Mint\Tulip\V1\SearchRequest\Page $page
+     *     @type int $total
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getItems()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .mint.tulip.v1.SearchResponse.Item items = 1;</code>
+     * @param array<\Mint\Tulip\V1\SearchResponse\Item>|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setItems($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Mint\Tulip\V1\SearchResponse\Item::class);
+        $this->items = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @return \Mint\Tulip\V1\SearchRequest\Page|null
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    public function hasPage()
+    {
+        return isset($this->page);
+    }
+
+    public function clearPage()
+    {
+        unset($this->page);
+    }
+
+    /**
+     * Generated from protobuf field <code>.mint.tulip.v1.SearchRequest.Page page = 98;</code>
+     * @param \Mint\Tulip\V1\SearchRequest\Page $var
+     * @return $this
+     */
+    public function setPage($var)
+    {
+        GPBUtil::checkMessage($var, \Mint\Tulip\V1\SearchRequest\Page::class);
+        $this->page = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     * @return int
+     */
+    public function getTotal()
+    {
+        return $this->total;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 total = 99;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotal($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->total = $var;
+
+        return $this;
+    }
+
+}
+

+ 169 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchResponse/Item.php

@@ -0,0 +1,169 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: tulip.proto
+
+namespace Mint\Tulip\V1\SearchResponse;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>mint.tulip.v1.SearchResponse.Item</code>
+ */
+class Item extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     */
+    protected $rank = 0;
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     */
+    protected $highlight = '';
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     */
+    protected $book = 0;
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     */
+    protected $paragraph = 0;
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     */
+    protected $content = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $rank
+     *     @type string $highlight
+     *     @type int $book
+     *     @type int $paragraph
+     *     @type string $content
+     * }
+     */
+    public function __construct($data = NULL) {
+        \GPBMetadata\Tulip::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     * @return int
+     */
+    public function getRank()
+    {
+        return $this->rank;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 rank = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setRank($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->rank = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     * @return string
+     */
+    public function getHighlight()
+    {
+        return $this->highlight;
+    }
+
+    /**
+     * Generated from protobuf field <code>string highlight = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHighlight($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->highlight = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     * @return int
+     */
+    public function getBook()
+    {
+        return $this->book;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 book = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBook($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->book = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     * @return int
+     */
+    public function getParagraph()
+    {
+        return $this->paragraph;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 paragraph = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setParagraph($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->paragraph = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     * @return string
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Generated from protobuf field <code>string content = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContent($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->content = $var;
+
+        return $this;
+    }
+
+}
+
+// Adding a class alias for backwards compatibility with the previous class name.
+class_alias(Item::class, \Mint\Tulip\V1\SearchResponse_Item::class);
+

+ 41 - 0
rpc/tulip/tulip/Mint/Tulip/V1/SearchStub.php

@@ -0,0 +1,41 @@
+<?php
+// GENERATED CODE -- DO NOT EDIT!
+
+namespace Mint\Tulip\V1;
+
+/**
+ */
+class SearchStub {
+
+    /**
+     * @param \Mint\Tulip\V1\SearchRequest $request client request
+     * @param \Grpc\ServerContext $context server request context
+     * @return \Mint\Tulip\V1\SearchResponse for response data, null if if error occured
+     *     initial metadata (if any) and status (if not ok) should be set to $context
+     */
+    public function Pali(
+        \Mint\Tulip\V1\SearchRequest $request,
+        \Grpc\ServerContext $context
+    ): ?\Mint\Tulip\V1\SearchResponse {
+        $context->setStatus(\Grpc\Status::unimplemented());
+        return null;
+    }
+
+    /**
+     * Get the method descriptors of the service for server registration
+     *
+     * @return array of \Grpc\MethodDescriptor for the service methods
+     */
+    public final function getMethodDescriptors(): array
+    {
+        return [
+            '/mint.tulip.v1.Search/Pali' => new \Grpc\MethodDescriptor(
+                $this,
+                'Pali',
+                '\Mint\Tulip\V1\SearchRequest',
+                \Grpc\MethodDescriptor::UNARY_CALL
+            ),
+        ];
+    }
+
+}

+ 14 - 0
rpc/tulip/tulip/composer.json

@@ -0,0 +1,14 @@
+{
+  "require": {
+    "grpc/grpc": "v1.57.0",
+    "google/protobuf": "v3.23.2"
+  },
+  "autoload": {
+    "psr-4": {
+      "GPBMetadata\\": [
+        "GPBMetadata/"
+      ],
+      "Mint\\": "Mint/"
+    }
+  }
+}

+ 31 - 0
rpc/tulip/tulip/server.php

@@ -0,0 +1,31 @@
+<?php
+
+require dirname(__FILE__) . '/vendor/autoload.php';
+
+class Greeter extends \Mint\Tulip\V1\SearchStub
+{
+    public function Pali(
+        \Mint\Tulip\V1\SearchRequest $request,
+        \Grpc\ServerContext $context
+    ): ?\Mint\Tulip\V1\SearchResponse {
+        $text = $request->getPayload();
+        echo 'Received request: ' . $text . PHP_EOL;
+        $response = new \Mint\Morus\V1\MarkdownToHtmlResponse();
+        $Parsedown = new Parsedown();
+        $response->setPayload($Parsedown->text($text));
+        return $response;
+    }
+}
+
+$param = getopt('p:');
+
+if (!isset($param['p'])) {
+    echo 'parameter port is required. -p 9999  ';
+    return;
+}
+$port = $param['p'];
+$server = new \Grpc\RpcServer();
+$server->addHttp2Port('0.0.0.0:' . $port);
+$server->handle(new Greeter());
+echo 'Listening on port :' . $port . PHP_EOL;
+$server->run();