Инструменты пользователя

Инструменты сайта


products:pyrog-plugins:deepseek-extractor:manual:dev

Расширенное руководство

Данное руководство нацелено на тех, кому не хватает встроенных представлений для отображения ветки сообщений. Для разработки собственных представлений необходимо иметь базовые знания языка Python.

Разработка представления ветки сообщений

Представление выполняет преобразование исходных данных сообщений в требуемый текстовый формат. Для начала нужно познакомиться со структурой данных, с которыми предстоит работать. Представлению на вход подается список из экземпляров датакласса DeepSeekMessage.

Класс DeepSeekMessage это датакласс, который содержит в себе информацию о сообщении Deepseek. Он состоит из следующих полей:

  • model - модель нейросети (константа типа Model)
  • inserted_datetime - время добавления сообщения (объект класса datetime)
  • fragments - фрагменты сообщения (список экземпляров класса Fragment)

Класс Model, вложенный в DeepSeekMessage представляет из себя класс-перечисление, кодирует модель нейросети, которая обрабатывала запрос, имеет следующие значения:

  • UNKNOWN - неизвестная модель
  • DEEPSEEK_CHAT - обычная разговорная модель
  • DEEPSEEK_REASONER - модель с включенным рассуждением

Класс Fragment, вложенный в DeepSeekMessage представляет из себя датакласс, который содержит информацию о фрагменте сообщения, он имеет следующие поля:

  • type - тип фрагмента (константа типа Type)
  • content - текстовые данные сообщения
  • search_result - список экземпляров класса SearchResultItem (содержит результаты поиска, если тип фрагмента TOOL_SEARCH, иначе - пуст)

Класс Type, вложенный в Fragment представляет из себя класс-перечисление, который кодирует тип фрагмента, имеет значения:

  • REQUEST - пользовательский запрос
  • RESPONSE - ответ нейросети
  • THINK - результат «размышления» нейросети
  • TOOL_SEARCH - результаты поиска в интернете

Класс SearchResultItem, вложенный в Fragment представляет из себя датакласс, который содержит информацию о результате поиска в интернете, имеет следующие поля:

  • url - url-адрес найденной страницы
  • title- заголовок найденной страницы

Так как разработчик не поставляет документации о структуре данных - все сведения о них были получены на основе анализа. Есть вероятность, что не все случаи были изучены. Также разработчики в будущем могут поменять структуру выходных данных, что может сделать утилиту не рабочей. Если нашли несоответствия сообщите об этом на почту.

Итак, вы узнали как устроены исходные данные, давайте напишем класс, который будет работать как представление. В пакете customviews создайте новый модуль, в качестве демонстрации я там уже оставил модуль myview.py, ниже представлен его код

from typing import Iterable
from DeepSeekExtractor.view.datatypes import DeepSeekMessage
from DeepSeekExtractor.view.chat_views import ChatViewBase
from DeepSeekExtractor.view import langconsts as lc
 
 
class MyView(ChatViewBase):
    title = "My View"
    type = ChatViewBase.Type.HTML5
 
    @classmethod
    def message_to_text(cls, message: DeepSeekMessage) -> str:
        text = ""
        dt_format = '%d.%m.%Y %H:%M:%S'
        for fragment in message.fragments:
            match fragment.type:
                case DeepSeekMessage.Fragment.Type.REQUEST:
                    text += f"<p class ='meta'>{lc.REQUEST_FROM} {message.inserted_datetime.strftime(dt_format)}</p>"
                    text += f"<p>{fragment.content}</p>"
                case DeepSeekMessage.Fragment.Type.RESPONSE:
                    text += f"<p class ='meta'>{lc.RESPONSE_FROM} {message.inserted_datetime.strftime(dt_format)}</p>"
                    text += f"<p>{fragment.content}</p>"
                case DeepSeekMessage.Fragment.Type.THINK:
                    text += f"<details>\
                     <summary>{lc.THINKING}</summary>\
                     <p>{fragment.content}</p>\
                     </details>"
                case DeepSeekMessage.Fragment.Type.TOOL_SEARCH:
                    if fragment.search_result:
                        text += f"<details>\
                                 <summary>{lc.SEARCH}</summary>\
                                <ul>"
                        for i in  fragment.search_result:
                            text += f"<li><a href=\"{i.url}\">{i.title}</a></li>"
                        text += "</ul></details>"
                case _:
                    continue
 
        return text
 
    @classmethod
    def chat_to_text(cls, chat: Iterable[DeepSeekMessage]) -> str:
        result: list[str] = []
        result.append(
            """
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8" />
                <style type="text/css">
                    body { font-family: 'Segoe UI', sans-serif; font-size: 14pt; color: #000000; }
                    p { margin: 0; padding: 0; white-space: pre-wrap; }
                    .meta { font-style: italic; color: #828282; }
                </style>
            </head>
            <body>
            """
        )
 
        chat_length = len(chat)
        for n, msg in enumerate(chat):
            result.append(cls.message_to_text(msg))
            if n < chat_length - 1:
                result.append("<p><br/></p>")
 
        result.append("</body></html>")
 
        return "".join(result)

Представление это класс MyView, который наследуется от ChatViewBase, он имеет ряд обязательных атрибутов:

  • title - это заголовок представления, он будет отображаться в списке, может быть строкой или языковой константой
  • type - тип представления (константа типа ChatViewBase.Type)

ChatViewBase.Type - это класс-перечисление, определяет как будет отображаться текст представления, имеет следующие значения:

  • PLAIN_TEXT - обычный текст, никак не форматируется при отображении, для отображения используется виджет QPlainTextEdit
  • HTML4 - язык гипертекста 4й версии, для отображения используется виджет QTextEdit
  • MARKDOWN - простой язык текстовой разметки Markdown, для отображения используется виджет QTextEdit
  • HTML5 - язык гипертекста 5й версии, для отображения используется виджет QWebEngineView

Если вы заметите, что текст отображается не так как ожидается, то изучите официальную документацию виджетов.

Затем нужно переопределить только один метод: chat_to_text(), который преобразует исходные данные в текст в нужном вам формате, в данном примере я использую HTML5.

Итак, начнем по порядку. Все текстовые данные по ходу вычисления отправляются в список в переменной result. Затем в список добавляется строка с объявлением шапки HTML-документа, данный блок завершается открытием блока <body>, для справки: внутри этого тега записываются данные, которые выводятся непосредственно на экран; также не забываем внутри тега <head> объявить метаданные, у меня объявлена кодировка utf-8 (при использовании HTML следует использовать только эту кодировку, так как только в ней сохраняются файлы!) и объявлены стили CSS. HTML5 предлагает обширные возможности для настройки визуала, но эта тема выходит за рамки данной статьи.

Так, документ мы подготовили, теперь, на очереди вывод сообщений. Для удобства я создал метод def message_to_text(cls, message: DeepSeekMessage) → str для преобразования единичного сообщения в текст. В нем производится обход всех фрагментов сообщения, и в зависимости от его типа, контент форматируется определенным образом. Для запросов и ответов формируется 2 абзаца, в первом мы указываем, Запрос от <дата добавления сообщения>или Ответ от <дата добавления сообщения>, заметьте, что данному абзацу присвоен класс стиля meta, он помечает текст серым цветом и курсивом; во втором абзаце выводится содержимое фрагмента. Результаты поиска в интернете и размышлений нейросети выводятся в сворачиваемом блоке (тег <details><summary>…</summary>…</details>).

Вернемся к методу chat_to_text(). В нем мы обходим все сообщения в цикле for, используя метод message_to_text(), далее после каждого сообщения кроме последнего ставим горизонтальную черту, добавляя тег <hr>. В заключении закрываем теги <body> и <html>, и преобразовываем полученный список строк в единую строку. Чтобы проверить работу представления не нужно перезапускать приложение, рядом со списком представлений нажмите кнопку с закругленной стрелкой, чтобы перезагрузить все представления.

На этом всё. Теперь, вы знаете как разрабатывать представления и готовы создавать собственные. Для представлений других типов действуют те же самые принципы, вы просто формируете текст, согласно требуемому синтаксису. Можете заглянуть в файл …DeepSeekExtractor\view\chat_views.py, чтобы ознакомиться со встроенными представлениями.

products/pyrog-plugins/deepseek-extractor/manual/dev.txt · Последнее изменение: ironmesh