新功能(agent/prompt.py):更新高级python程序员的提示信息和要求

🔧 更新(pages/chat_page.py):添加Python代码编辑功能和执行按钮
📝 添加(pages/python_code_edit.py):添加Python代码编辑器类
📝 添加(pages/python_code_render.py):添加Python代码渲染器类📝 添加/更新(agent/prompt.py): 更新关于高级python程序员的提示文本和约束信息
🔧 更新(pages/chat_page.py): 更新代码展示窗口的实现,添加了能够执行保存和执行按钮功能
♻️ 重构(pages/python_code_edit.py): 删除不必要的代码和导入,规范代码缩进,并调整代码结构
 引入(pages/python_code_render.py): 引入用于渲染Python代码的页面文件,并实现基本的文本编辑功能
This commit is contained in:
yuruo
2024-06-01 13:04:15 +08:00
parent 5574955055
commit 035fc4d3ff
4 changed files with 416 additions and 70 deletions

View File

@@ -28,13 +28,15 @@ require_alignment_prompt="""# 上下文 #
"""
programmer_prompt=Template("""# 上下文 #
你是一位高级python程序员根据产品经理的需求编写python代码这个代码会被传递到python的eval()函数直接执行请不要返回markdown格式内容我可以提供封装好的函数你可以直接拿来使用函数如下
```python
$python_code
```
你是一位高级python程序员根据产品经理的需求编写python代码
#############
# 目标 #
我希望你够根据产品经理的自动化需求,返回可执行的python代码内容,注意不要返回其他信息,你返回的内容会传递到python的eval()函数直接执行
我希望你够根据产品经理的自动化需求返回可执行的python代码你返回的所有内容会直接传递到eval()的参数
你可以直接使用以下封装好的【函数】
$python_code
#############
# 风格 #
请你编写python代码时要遵循PEP8规范代码简单易懂每一行代码都要用#编写注释并且在关键地方用#给出修改建议。
@@ -45,12 +47,26 @@ $python_code
# 受众 #
会写python但是不太熟悉
#############
# 约束 #
返回的代码不要被```python```包裹只返回python代码以下是错误的例子
1. ```python
print("abc")
```
2. ```python
c = [i in range(10)]
print(c)
```
代码中的函数一定是python内置函数或第三方库或我提供的【函数】不要假想不存在的函数。
############
# 回复格式 #
[python代码]
#############
# 例子 #
只返回python代码不要返回任何其他信息比如
1. print("abc")
2. c = [i in range(10)]\nprint(c)
#############
""")

View File

@@ -1,20 +1,17 @@
import json
import traceback
from PyQt6 import QtWidgets, QtCore
from PyQt6.QtCore import QThread, pyqtSignal, QEvent, Qt
from PyQt6.QtCore import QThread, pyqtSignal, Qt
from PyQt6.QtGui import QPixmap, QIcon
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMainWindow, QLabel, QTextEdit, QListWidgetItem, QSpacerItem, QSizePolicy, QAbstractItemView, QListWidget, QMenu
from pygments import highlight
from PyQt6.QtWidgets import QPushButton, QApplication, QSystemTrayIcon, QMainWindow, QLabel, QTextEdit, QListWidgetItem, QSpacerItem, QSizePolicy, QAbstractItemView, QListWidget, QMenu, QPushButton
from actions.action_util import ActionUtil
from agent.require_alignment_agent import RequireAlignmentAgent
from agent.programmer_agent import ProgrammerAgent
from pages.config_page import ConfigPage
from pages.plugin_page import PluginPage
from pages.python_code_edit import PythonHighlighter, QCodeEditor
from utils.global_keyboard_listen import GlobalKeyboardListen
from utils.qt_util import QtUtil
from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter
from utils.qt_util import QtUtil
class ActionItems(QListWidgetItem):
def __init__(self, action, chat_page):
@@ -219,8 +216,15 @@ class ChatPage(QMainWindow, interface_ui):
"<b>你好我叫智子你的智能Agent助手</b><br><br>你可以输入“/”搜索行为,或者可有什么要求可以随时吩咐!",
"system"
)
self.new_conversation(
"<b>你好我叫智子你的智能Agent助手</b><br><br>你可以输入“/”搜索行为,或者可有什么要求可以随时吩咐!",
"system",
type="code"
)
def setup_up(self):
self.chat_input = ChatInput(parent=self.centralwidget, chat_page=self)
self.chat_list = ChatList(parent=self.centralwidget, chat_page=self)
self.action_list = ActionList(parent=self.centralwidget, chat_page=self)
@@ -303,33 +307,60 @@ class ChatPage(QMainWindow, interface_ui):
h_box.setStretch(0, 1)
h_box.setStretch(1, 1)
h_box.setStretch(2, 10)
# 创建 QTextEdit 对象并设置其文本
text_edit = QTextEdit(parent=widget)
text_edit.setReadOnly(True)
v_box.addLayout(h_box)
# 设置 QTextEdit 的背景为白色,边角为椭圆
item = QListWidgetItem()
# 创建 QTextEdit 对象并设置其文本
if type == "code":
text_edit = QCodeEditor(display_line_numbers=True,
highlight_current_line=True,
syntax_high_lighter=PythonHighlighter,
)
text = text.strip('```python').rstrip('```')
text_edit.setPlainText(text)
# 设置 widget、v_box 和 item 的大小
v_box.addWidget(text_edit)
item.setSizeHint(widget.size())
run_button_h_box= QtWidgets.QHBoxLayout()
spacer = QSpacerItem(20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
run_button_h_box.addItem(spacer)
run_button_h_box.setStretch(0, 6)
save_button = QPushButton("保存")
save_button.setStyleSheet("background-color: grey; color: white;")
run_button_h_box.addWidget(save_button)
run_button_h_box.setStretch(1, 2)
run_button = QPushButton("执行")
run_button.setStyleSheet("background-color: green; color: white;")
run_button_h_box.addWidget(run_button)
run_button_h_box.setStretch(2, 2)
v_box.addLayout(run_button_h_box)
# # 获取 QTextEdit 的文档的大小
doc_size = text_edit.document().size().toSize()
widget.setFixedHeight(doc_size.height()*50 + 100)
else:
text_edit = QTextEdit()
text_edit.setReadOnly(True)
text_edit.setHtml(f"<p style='font-size:10pt;'>{text}</p>")
def update_size(widget, item, text_edit):
# 获取 QTextEdit 的文档的大小
doc_size = text_edit.document().size().toSize()
# 设置 widget、v_box 和 item 的大小
widget.setFixedHeight(doc_size.height() + 55)
item.setSizeHint(widget.size())
text_edit.document().documentLayout().documentSizeChanged.connect(lambda: update_size(widget, item, text_edit))
v_box.addWidget(text_edit)
# # 设置 QTextEdit 的背景为白色,边角为椭圆
text_edit.setStyleSheet("""
background-color: white;
border-radius: 10px;
""")
if type == "code":
text = highlight(text, PythonLexer(), HtmlFormatter())
text_edit.setHtml(text)
else:
text_edit.setHtml(text)
v_box.addWidget(text_edit)
item = QListWidgetItem()
# 连接文档大小改变的信号
text_edit.document().documentLayout().documentSizeChanged.connect(
lambda: self.update_size(widget, item, text_edit))
# 将 item 添加到 QListWidget
text_edit.document().setDocumentMargin(10) # 将 item 添加到 QListWidget
self.chat_list.insertItem(self.chat_list.count(), item)
self.chat_list.setItemWidget(item, widget)
self.chat_list.scrollToBottom()
@staticmethod
def update_size(widget, item, text_edit):
# 获取 QTextEdit 的文档的大小
doc_size = text_edit.document().size().toSize()
# 设置 widget、v_box 和 item 的大小
widget.setFixedHeight(doc_size.height() + 60)
item.setSizeHint(widget.size())

View File

@@ -1,39 +1,9 @@
import uuid
from pydantic import BaseModel, Field
from actions.action_base import ActionBase
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTextEdit
from PyQt6.QtCore import QRect
from PyQt6.QtCore import QRect, pyqtSignal
from PyQt6.QtGui import QFont, QColor, QPainter, QTextFormat
from PyQt6.QtWidgets import QWidget, QPlainTextEdit, QTextEdit
from PyQt6 import QtGui, QtCore
class PythonExecutorInput(BaseModel):
code: str = Field(description="python代码", title="pytho代码", default="")
class PythonExecutorActoin(ActionBase):
name: str = "执行python代码"
description: str = "执行python代码"
args: PythonExecutorInput
output_save_name:str = "python_output"
def config_page_ui(self):
code_editor = QCodeEditor(display_line_numbers=True,
highlight_current_line=True,
syntax_high_lighter=PythonHighlighter)
self._config_ui.config_list.addWidget(code_editor)
self._ui_name_and_line_edit["code"] = code_editor
code_editor.setPlainText('# 保存输出结果\n' + self.output_save_name + " = ''")
# 执行python代码
def run(self, code):
environent = {}
exec(code, environent)
return environent[self.output_save_name]
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, editor)
@@ -111,6 +81,7 @@ class QCodeEditor(QPlainTextEdit):
syntax_high_lighter=None, *args):
"""
Parameters
----------
display_line_numbers : bool
switch on/off the presence of the lines number bar
@@ -120,11 +91,9 @@ class QCodeEditor(QPlainTextEdit):
should be inherited from QSyntaxHighlighter
"""
super(QCodeEditor, self).__init__()
self.setFont(QFont("Microsoft YaHei UI Light", 11))
super(QCodeEditor, self).__init__(*args)
self.setFont(QFont("Microsoft YaHei UI Light", 12))
self.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
self.DISPLAY_LINE_NUMBERS = display_line_numbers
if display_line_numbers:
@@ -150,7 +119,7 @@ class QCodeEditor(QPlainTextEdit):
cr = self.contentsRect()
rec = QRect(cr.left(), cr.top(), self.number_bar.get_width(), cr.height())
self.number_bar.setGeometry(rec)
QPlainTextEdit.resizeEvent(self, *e)
# 获取代码

330
pages/python_code_render.py Normal file
View File

@@ -0,0 +1,330 @@
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTextEdit
from PyQt6.QtCore import QRect
from PyQt6.QtGui import QFont, QColor, QPainter, QTextFormat
from PyQt6.QtWidgets import QWidget, QPlainTextEdit, QTextEdit
from PyQt6 import QtGui, QtCore
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, editor)
self.editor = editor
self.editor.blockCountChanged.connect(self.update_width)
self.editor.updateRequest.connect(self.update_contents)
self.font = QFont()
self.numberBarColor = QColor("#e8e8e8")
def paintEvent(self, event):
# Override paintEvent to draw the line numbers
painter = QPainter(self)
painter.fillRect(event.rect(), self.numberBarColor)
block = self.editor.firstVisibleBlock()
# Iterate over all visible text blocks in the document.
while block.isValid():
block_number = block.blockNumber()
block_top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
# Check if the position of the block is outside the visible area.
if not block.isVisible() or block_top >= event.rect().bottom():
break
# We want the line number for the selected line to be bold.
if block_number == self.editor.textCursor().blockNumber():
self.font.setBold(True)
painter.setPen(QColor("#000000"))
else:
self.font.setBold(False)
painter.setPen(QColor("#717171"))
painter.setFont(self.font)
# Draw the line number right justified at the position of the line.
paint_rect = QRect(0, int(block_top), self.width(), self.editor.fontMetrics().height())
painter.drawText(paint_rect, Qt.AlignmentFlag.AlignRight, str(block_number + 1))
block = block.next()
painter.end()
QWidget.paintEvent(self, event)
# 根据文档的总行数来计算宽度
def get_width(self):
count = self.editor.blockCount()
# width = self.fontMetrics().width(str(count)) + 10
width = self.fontMetrics().horizontalAdvance(str(count)) + 5
return width
# 设置宽度
def update_width(self):
width = self.get_width()
if self.width() != width:
self.setFixedWidth(width)
self.editor.setViewportMargins(width, 0, 0, 0);
# 更行内容
def update_contents(self, rect, scroll):
if scroll:
self.scroll(0, scroll)
else:
self.update(0, rect.y(), self.width(), rect.height())
if rect.contains(self.editor.viewport().rect()):
font_size = self.editor.currentCharFormat().font().pointSize()
self.font.setPointSize(font_size)
self.font.setStyle(QFont.Style.StyleNormal)
self.update_width()
class QCodeEditor(QPlainTextEdit):
def __init__(self, display_line_numbers=True, highlight_current_line=True,
syntax_high_lighter=None, *args):
"""
Parameters
----------
display_line_numbers : bool
switch on/off the presence of the lines number bar
highlight_current_line : bool
switch on/off the current line highlighting
syntax_high_lighter : QSyntaxHighlighter
should be inherited from QSyntaxHighlighter
"""
super(QCodeEditor, self).__init__()
self.setFont(QFont("Microsoft YaHei UI Light", 11))
self.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
self.DISPLAY_LINE_NUMBERS = display_line_numbers
if display_line_numbers:
self.number_bar = LineNumberArea(self)
if highlight_current_line:
self.currentLineNumber = None
self.currentLineColor = self.palette().alternateBase()
self.cursorPositionChanged.connect(self.highlight_current_line)
if syntax_high_lighter is not None: # add highlighter to text document
self.highlighter = syntax_high_lighter(self.document())
# 默认选中第一行
cursor = self.textCursor()
cursor.movePosition(QtGui.QTextCursor.MoveOperation.Start)
self.setTextCursor(cursor)
self.highlight_current_line() # 确保第一行高亮
def resizeEvent(self, *e):
"""overload resizeEvent handler"""
if self.DISPLAY_LINE_NUMBERS: # resize LineNumberArea widget
cr = self.contentsRect()
rec = QRect(cr.left(), cr.top(), self.number_bar.get_width(), cr.height())
self.number_bar.setGeometry(rec)
QPlainTextEdit.resizeEvent(self, *e)
# 获取代码
def text(self):
return self.toPlainText()
def highlight_current_line(self):
new_current_line_number = self.textCursor().blockNumber()
if new_current_line_number != self.currentLineNumber:
self.currentLineNumber = new_current_line_number
hi_selection = QTextEdit.ExtraSelection()
hi_selection.format.setBackground(self.currentLineColor)
hi_selection.format.setProperty(QTextFormat.Property.FullWidthSelection, True)
hi_selection.cursor = self.textCursor()
hi_selection.cursor.clearSelection()
self.setExtraSelections([hi_selection])
def format_syn(color, style=''):
"""Return a QTextCharFormat with the given attributes.
"""
_color = QtGui.QColor()
_color.setNamedColor(color)
_format = QtGui.QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
# QtGui.QFont.setBold(True)
_format.setFontWeight(QtGui.QFont.bold.__sizeof__())
if 'italic' in style:
_format.setFontItalic(True)
return _format
# Syntax styles that can be shared by all languages
STYLES = {
'keyword': format_syn('blue'),
'operator': format_syn('red'),
'brace': format_syn('darkGray'),
'defclass': format_syn('black', 'bold'),
'string': format_syn('magenta'),
'string2': format_syn('darkMagenta'),
'comment': format_syn('darkGreen', 'italic'),
'self': format_syn('black', 'italic'),
'numbers': format_syn('brown'),
}
class PythonHighlighter(QtGui.QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
# Python keywords
keywords = [
'and', 'assert', 'break', 'class', 'continue', 'def',
'del', 'elif', 'else', 'except', 'exec', 'finally',
'for', 'from', 'global', 'if', 'import', 'in',
'is', 'lambda', 'not', 'or', 'pass', 'print',
'raise', 'return', 'try', 'while', 'yield',
'None', 'True', 'False',
]
# Python operators
operators = [
r'=',
# Comparison
r'==', r'!=', r'<', r'<=', r'>', r'>=',
# Arithmetic
r'\+', r'-', r'\*', r'/', r'//', r'\%', r'\*\*',
# In-place
r'\+=', r'-=', r'\*=', r'/=', r'\%=',
# Bitwise
r'\^', r'\|', r'\&', r'\~', r'>>', r'<<',
]
# Python braces
braces = [
r'\{', r'\}', r'\(', r'\)', r'\[', r'\]',
]
def __init__(self, parent: QtGui.QTextDocument) -> None:
super().__init__(parent)
# Multi-line strings (expression, flag, style)
self.tri_single = (QtCore.QRegularExpression("'''"), 1, STYLES['string2'])
self.tri_double = (QtCore.QRegularExpression('"""'), 2, STYLES['string2'])
rules = []
# Keyword, operator, and brace rules
rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
for w in PythonHighlighter.keywords]
rules += [(r'%s' % o, 0, STYLES['operator'])
for o in PythonHighlighter.operators]
rules += [(r'%s' % b, 0, STYLES['brace'])
for b in PythonHighlighter.braces]
# All other rules
rules += [
# 'self'
(r'\bself\b', 0, STYLES['self']),
# 'def' followed by an identifier
(r"\bdef\b\s*(\w+)", 1, STYLES['defclass']),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']),
# From '#' until a newline
(r'#[^\n]*', 0, STYLES['comment']),
]
# Build a QRegExp for each pattern
self.rules = [(QtCore.QRegularExpression(pat), index, fmt)
for (pat, index, fmt) in rules]
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
self.tripleQuoutesWithinStrings = []
# Do other syntax formatting
for expression, nth, format in self.rules:
index = expression.match(text, 0).capturedStart(0)
if index >= 0:
# if there is a string we check
# if there are some triple quotes within the string
# they will be ignored if they are matched again
if expression.pattern() in [r'"[^"\\]*(\\.[^"\\]*)*"', r"'[^'\\]*(\\.[^'\\]*)*'"]:
# 匹配到三个引号
innerIndex = self.tri_single[0].match(text, index + 1).capturedStart(0)
if innerIndex == -1:
innerIndex = self.tri_double[0].match(text, index + 1).capturedStart(0)
if innerIndex != -1:
tripleQuoteIndexes = range(innerIndex, innerIndex + 3)
self.tripleQuoutesWithinStrings.extend(tripleQuoteIndexes)
while index >= 0:
# 跳过三引号
if index in self.tripleQuoutesWithinStrings:
index += 1
continue
# We actually want the index of the nth match
index = expression.match(text, index).capturedStart(nth)
length = expression.match(text, index).capturedLength(nth)
self.setFormat(index, length, format)
index = expression.match(text, index + length).capturedStart(0)
self.setCurrentBlockState(0)
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlight of multi-line strings. ``delimiter`` should be a
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
start = delimiter.match(text).capturedStart(0)
# skipping triple quotes within strings
if start in self.tripleQuoutesWithinStrings:
return False
# Move past this match
add = delimiter.match(text).capturedLength()
# As long as there's a delimiter match on this line...
while start >= 0: # Look for the ending delimiter
end = delimiter.match(text, start + add).capturedStart(0)
# Ending delimiter on this line?
if end >= add:
length = end - start + add + delimiter.match(text).capturedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, style)
# Look for the next match
start = delimiter.match(text, start + length).capturedStart()
# Return True if still inside a multi-line string, False otherwise
if self.currentBlockState() == in_state:
return True
else:
return False