def _serve_metadata(self, request):
        run = request.args.get('run')
        if run is None:
            return Respond(request, 'query parameter "run" is required',
                           'text/plain', 400)

        name = request.args.get('name')
        if name is None:
            return Respond(request, 'query parameter "name" is required',
                           'text/plain', 400)

        num_rows = _parse_positive_int_param(request, 'num_rows')
        if num_rows == -1:
            return Respond(request,
                           'query parameter num_rows must be integer > 0',
                           'text/plain', 400)

        if run not in self.configs:
            return Respond(request, 'Unknown run: %s' % run, 'text/plain', 400)

        config = self.configs[run]
        fpath = self._get_metadata_file_for_tensor(name, config)
        if not fpath:
            return Respond(
                request,
                'No metadata file found for tensor %s in the config file %s' %
                (name, self.config_fpaths[run]), 'text/plain', 400)
        if not file_io.file_exists(fpath) or file_io.is_directory(fpath):
            return Respond(request, '%s is not a file' % fpath, 'text/plain',
                           400)

        num_header_rows = 0
        with file_io.FileIO(fpath, 'r') as f:
            lines = []
            # Stream reading the file with early break in case the file doesn't fit in
            # memory.
            for line in f:
                lines.append(line)
                if len(lines) == 1 and '\t' in lines[0]:
                    num_header_rows = 1
                if num_rows and len(lines) >= num_rows + num_header_rows:
                    break
        return Respond(request, ''.join(lines), 'text/plain')
 def _serve_runs(self, request):
     """Returns a list of runs that have embeddings."""
     return Respond(request, list(self.configs.keys()), 'application/json')