code: def convert_response_to_doc(doc, semgrex_response):
      doc = copy.deepcopy(doc)
      try:
          for sent_idx, (sentence, ssurgeon_result) in enumerate(
              zip(doc.sentences, semgrex_response.result)
          ):
              # EditNode is currently bugged... :/
              # TODO: change this after next CoreNLP release (after 4.5.3)
              # if not ssurgeon_result.changed:
              #    continue
  
              ssurgeon_graph = ssurgeon_result.graph
              tokens = []
              for graph_node, graph_word in zip(
                  ssurgeon_graph.node, ssurgeon_graph.token
              ):
                  word_entry = build_word_entry(
                      graph_node.index, graph_word
                  )
                  tokens.append(word_entry)
              tokens.sort(key=lambda x: x[ID])
              for root in ssurgeon_graph.root:
                  tokens[root - 1][HEAD] = 0
                  tokens[root - 1][DEPREL] = "root"
              for edge in ssurgeon_graph.edge:
                  # can't do anything about the extra dependencies for now
                  # TODO: put them all in .deps
                  if edge.isExtra:
                      continue
                  tokens[edge.target - 1][HEAD] = edge.source
                  tokens[edge.target - 1][DEPREL] = edge.dep
  
              # for any MWT, produce a token_entry which represents the word range
              mwt_tokens = []
              for word_start_idx, word in enumerate(tokens):
                  if not word["is_first_mwt"]:
                      if word["is_mwt"]:
                          word[
                              MISC
                          ] = java_protobuf_requests.remove_space_misc(
                              word[MISC]
                          )
                      mwt_tokens.append(word)
                      continue
                  word_end_idx = word_start_idx + 1
                  while (
                      word_end_idx < len(tokens)
                      and tokens[word_end_idx]["is_mwt"]
                      and not tokens[word_end_idx]["is_first_mwt"]
                  ):
                      word_end_idx += 1
                  mwt_token_entry = {
                      # the tokens don't fencepost the way lists do
                      ID: (
                          tokens[word_start_idx][ID],
                          tokens[word_end_idx - 1][ID],
                      ),
                      TEXT: word["mwt_text"],
                      NER: word[NER],
                      # use the SpaceAfter=No (or not) from the last word in the token
                      MISC: None,
                  }
                  mwt_token_entry[
                      MISC
                  ] = java_protobuf_requests.misc_space_pieces(
                      tokens[word_end_idx - 1][MISC]
                  )
                  if tokens[word_end_idx - 1]["mwt_misc"]:
                      mwt_token_entry[
                          MISC
                      ] = java_protobuf_requests.substitute_space_misc(
                          tokens[word_end_idx - 1]["mwt_misc"],
                          mwt_token_entry[MISC],
                      )
                  word[MISC] = java_protobuf_requests.remove_space_misc(
                      word[MISC]
                  )
                  mwt_tokens.append(mwt_token_entry)
                  mwt_tokens.append(word)
  
              old_comments = list(sentence.comments)
              sentence = Sentence(mwt_tokens, doc)
  
              token_text = []
              for token_idx, token in enumerate(sentence.tokens):
                  token_text.append(token.text)
                  if token_idx == len(sentence.tokens) - 1:
                      break
                  token_space_after = misc_to_space_after(token.misc)
                  if token_space_after == " ":
                      # in some treebanks, the word might have more interesting
                      # space after annotations than the token
                      token_space_after = misc_to_space_after(
                          token.words[-1].misc
                      )
                  token_text.append(token_space_after)
  
              sentence_text = "".join(token_text)
  
              for comment in old_comments:
                  if (
                      comment.startswith("# text ")
                      or comment.startswith("#text ")
                      or comment.startswith("# text=")
                      or comment.startswith("#text=")
                  ):
                      sentence.add_comment("# text = " + sentence_text)
                  else:
                      sentence.add_comment(comment)
  
              doc.sentences[sent_idx] = sentence
  
              sentence.rebuild_dependencies()
      except Exception as e:
          raise RuntimeError(
              "Ssurgeon could not process sentence {}\nSsurgeon result:\n{}\nOriginal sentence:\n{:C}".format(
                  sent_idx, ssurgeon_result, sentence
              )
          ) from e
      return doc
  
imports: import argparse
  import copy
  import os
  import re
  import sys
  from stanza.models.common.utils import misc_to_space_after, space_after_to_misc
  from stanza.protobuf import SsurgeonRequest, SsurgeonResponse
  from stanza.server import java_protobuf_requests
  from stanza.utils.conll import CoNLL
  from stanza.models.common.doc import ID, TEXT, LEMMA, UPOS, XPOS, FEATS, HEAD, DEPREL, DEPS, MISC, START_CHAR, END_CHAR, NER, Word, Token, Sentence
label_desc: Use image node_id 9 for calling a global function with example usage: convert_response_to_doc(doc, semgrex_response) and returns: doc