====== Расширенное руководство ====== Данное руководство нацелено на тех, кому не хватает встроенных представлений для отображения ветки сообщений. Для разработки собственных представлений необходимо иметь базовые знания языка [[https://www.python.org|Python]]. ===== Разработка представления ветки сообщений ===== Представление выполняет преобразование исходных данных сообщений в требуемый текстовый формат. Для начала нужно познакомиться со структурой данных, с которыми предстоит работать. Представлению на вход подается список из экземпляров датакласса **DeepSeekMessage**. Класс **DeepSeekMessage** это датакласс, который содержит в себе информацию о сообщении Deepseek. Он состоит из следующих полей: * //model// - модель нейросети (константа типа **Model**) * //inserted_datetime// - время добавления сообщения (объект класса **[[https://docs.python.org/3/library/datetime.html#datetime-objects|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//- заголовок найденной страницы Так как разработчик не поставляет документации о структуре данных - все сведения о них были получены на основе анализа. Есть вероятность, что не все случаи были изучены. Также разработчики в будущем могут поменять структуру выходных данных, что может сделать утилиту не рабочей. Если нашли несоответствия сообщите об этом [[feedback@ironmesh.ru|на почту]]. Итак, вы узнали как устроены исходные данные, давайте напишем класс, который будет работать как представление. В пакете //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"

{lc.REQUEST_FROM} {message.inserted_datetime.strftime(dt_format)}

" text += f"

{fragment.content}

" case DeepSeekMessage.Fragment.Type.RESPONSE: text += f"

{lc.RESPONSE_FROM} {message.inserted_datetime.strftime(dt_format)}

" text += f"

{fragment.content}

" case DeepSeekMessage.Fragment.Type.THINK: text += f"
\ {lc.THINKING}\

{fragment.content}

\
" case DeepSeekMessage.Fragment.Type.TOOL_SEARCH: if fragment.search_result: text += f"
\ {lc.SEARCH}\
" case _: continue return text @classmethod def chat_to_text(cls, chat: Iterable[DeepSeekMessage]) -> str: result: list[str] = [] result.append( """ """ ) chat_length = len(chat) for n, msg in enumerate(chat): result.append(cls.message_to_text(msg)) if n < chat_length - 1: result.append("


") result.append("") return "".join(result)
Представление это класс **MyView**, который наследуется от **ChatViewBase**, он имеет ряд обязательных атрибутов: * //title// - это заголовок представления, он будет отображаться в списке, может быть строкой или [[products:pyrog:api:latest:types:lang_constant|языковой константой]] * //type// - тип представления (константа типа **ChatViewBase.Type**) **ChatViewBase.Type** - это класс-перечисление, определяет как будет отображаться текст представления, имеет следующие значения: * ''PLAIN_TEXT'' - обычный текст, никак не форматируется при отображении, для отображения используется виджет [[https://doc.qt.io/qtforpython-6.8/PySide6/QtWidgets/QPlainTextEdit.html|QPlainTextEdit]] * ''HTML4'' - язык гипертекста 4й версии, для отображения используется виджет [[https://doc.qt.io/qtforpython-6.8/PySide6/QtWidgets/QTextEdit.html|QTextEdit]] * ''MARKDOWN'' - простой язык текстовой разметки Markdown, для отображения используется виджет [[https://doc.qt.io/qtforpython-6.8/PySide6/QtWidgets/QTextEdit.html|QTextEdit]] * ''HTML5'' - язык гипертекста 5й версии, для отображения используется виджет [[https://doc.qt.io/qtforpython-6.8/PySide6/QtWebEngineWidgets/QWebEngineView.html|QWebEngineView]] Если вы заметите, что текст отображается не так как ожидается, то изучите официальную документацию виджетов. Затем нужно переопределить только один метод: //chat_to_text()//, который преобразует исходные данные в текст в нужном вам формате, в данном примере я использую ''HTML5''. Итак, начнем по порядку. Все текстовые данные по ходу вычисления отправляются в список в переменной //result//. Затем в список добавляется строка с объявлением шапки HTML-документа, данный блок завершается открытием блока '''', для справки: внутри этого тега записываются данные, которые выводятся непосредственно на экран; также не забываем внутри тега '''' объявить метаданные, у меня объявлена кодировка //utf-8// (**при использовании HTML следует использовать только эту кодировку, так как только в ней сохраняются файлы!**) и объявлены стили CSS. HTML5 предлагает обширные возможности для настройки визуала, но эта тема выходит за рамки данной статьи. Так, документ мы подготовили, теперь, на очереди вывод сообщений. Для удобства я создал метод ''def message_to_text(cls, message: DeepSeekMessage) -> str'' для преобразования единичного сообщения в текст. В нем производится обход всех фрагментов сообщения, и в зависимости от его типа, контент форматируется определенным образом. Для запросов и ответов формируется 2 абзаца, в первом мы указываем, //Запрос от <дата добавления сообщения>//или //Ответ от <дата добавления сообщения>//, заметьте, что данному абзацу присвоен класс стиля ''meta'', он помечает текст серым цветом и курсивом; во втором абзаце выводится содержимое фрагмента. Результаты поиска в интернете и размышлений нейросети выводятся в сворачиваемом блоке (тег ''
......
''). Вернемся к методу //chat_to_text()//. В нем мы обходим все сообщения в цикле ''for'', используя метод //message_to_text()//, далее после каждого сообщения кроме последнего ставим горизонтальную черту, добавляя тег ''
''. В заключении закрываем теги '''' и '''', и преобразовываем полученный список строк в единую строку. Чтобы проверить работу представления не нужно перезапускать приложение, рядом со списком представлений нажмите кнопку с закругленной стрелкой, чтобы перезагрузить все представления. На этом всё. Теперь, вы знаете как разрабатывать представления и готовы создавать собственные. Для представлений других типов действуют те же самые принципы, вы просто формируете текст, согласно требуемому синтаксису. Можете заглянуть в файл **...DeepSeekExtractor\view\chat_views.py**, чтобы ознакомиться со встроенными представлениями. {{section>products:pyrog-plugins:includes#footer&noheader}}