def raw_text_to_post(
        self,
        llm_output: Iterable[ChatMessageType],
        post_proxy: PostEventProxy,
        early_stop: Optional[Callable[[Union[AttachmentType, Literal["message", "send_to"]], str], bool]] = None,
        validation_func: Optional[Callable[[Post], None]] = None,
        use_v2_parser: bool = True,
    ) -> None:
        """
        Convert the raw text output from LLM to a Post object.
        """

        def stream_filter(s: Iterable[ChatMessageType]) -> Iterator[str]:
            full_llm_content = ""
            try:
                for c in s:
                    full_llm_content += c["content"]
                    yield c["content"]
            finally:
                if isinstance(s, types.GeneratorType):
                    try:
                        s.close()
                    except GeneratorExit:
                        pass
                output_size = self.tracing.count_tokens(full_llm_content)
                self.tracing.set_span_attribute("output_size", output_size)
                self.tracing.add_prompt_size(
                    size=output_size,
                    labels={
                        "direction": "output",
                    },
                )
                self.logger.info(f"LLM output: {full_llm_content}")

        value_buf: str = ""
        filtered_stream = stream_filter(llm_output)
        parser_stream = (
            self.parse_llm_output_stream_v2(filtered_stream)
            if use_v2_parser
            else self.parse_llm_output_stream(filtered_stream)
        )
        # parser_stream = self.parse_llm_output("".join([c["content"] for c in llm_output]))
        cur_attachment: Optional[Attachment] = None
        try:
            for type_str, value, is_end in parser_stream:
                value_buf += value
                type: Optional[AttachmentType] = None
                if type_str == "message":
                    post_proxy.update_message(value_buf, is_end=is_end)
                    value_buf = ""
                elif type_str == "send_to":
                    if is_end:
                        post_proxy.update_send_to(value_buf)  # type: ignore
                        value_buf = ""
                    else:
                        # collect the whole content before updating post
                        pass
                else:
                    try:
                        type = AttachmentType(type_str)
                        if cur_attachment is not None:
                            assert type == cur_attachment.type
                        cur_attachment = post_proxy.update_attachment(
                            value_buf,
                            type,
                            id=(cur_attachment.id if cur_attachment is not None else None),
                            is_end=is_end,
                        )
                        value_buf = ""
                        if is_end:
                            cur_attachment = None
                    except Exception as e:
                        self.logger.warning(
                            f"Failed to parse attachment: {type_str}-{value_buf} due to {str(e)}",
                        )
                        continue
                parsed_type = (
                    type
                    if type is not None
                    else "message"
                    if type_str == "message"
                    else "send_to"
                    if type_str == "send_to"
                    else None
                )
                assert parsed_type is not None, f"Invalid type: {type_str}"

                # check whether parsing should be triggered prematurely when each key parsing is finished
                if is_end and early_stop is not None and early_stop(parsed_type, value):
                    break
        finally:
            if isinstance(parser_stream, types.GeneratorType):
                try:
                    parser_stream.close()
                except GeneratorExit:
                    pass

        if validation_func is not None:
            validation_func(post_proxy.post)

        Convert the raw text output from LLM to a Post object.
        
taskweaver/role/translator.py