Integrate Multimodal tools to agentskills. (#2016)

* suport reading multimodal files

* move file

* update dependency

* remove useless pip install

* add comments

* update the comment

* Apply suggestions from code review

* Add unit test for TXTReader

* pre-commit hook corrupted utf16 test txt

* Revert unnecessary dependency upgrades

* feat: import some readers for agentskill

* add dependencies

* Integrate some multimodal tools

* add shell pip dependency

* update dependencies

* update dependencies

* update print window

* remove __main__

* locally import cv2

* add c library for opencv

* update lock file

* update prompt

* remove unuseful file

* add some unittest

* add unittest & remove excel-related parser

* rollback poetry lock

* remove markdown

* remove requests

* optimize parse_video output

* Fix integration tests for CodeActAgent

* remove test_parse_image unittest

* Add a TODO to containers/sandbox/Dockerfile

* update dependencies

* remove pyproject.toml useless package

* change document via openai key

* Fix prompts after removing some actions

---------

Co-authored-by: Mingchen Zhuge <mczhuge@gmail.com>
Co-authored-by: yufansong <yufan@risingwave-labs.com>
Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
Co-authored-by: Mingchen Zhuge <64179323+mczhuge@users.noreply.github.com>
Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
This commit is contained in:
Shimada666
2024-05-25 18:58:49 +08:00
committed by GitHub
parent 91f313c914
commit b31f7701eb
17 changed files with 707 additions and 8 deletions

View File

@@ -22,6 +22,7 @@ RUN apt-get update && apt-get install -y \
g++ \
make \
iproute2 \
libgl1-mesa-glx \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p -m0755 /var/run/sshd
@@ -32,3 +33,5 @@ RUN ln -s /usr/bin/python3 /usr/bin/python
# install basic dependencies for CodeActAgent
RUN pip3 install --upgrade pip
RUN pip3 install jupyterlab notebook jupyter_kernel_gateway flake8
# TODO: those dependencies are needed for agentskills, we should pack them in a new sandbox image
RUN pip3 install python-docx PyPDF2 python-pptx pylatexenc openai opencv-python

View File

@@ -20,12 +20,29 @@ import subprocess
from inspect import signature
from typing import Optional
import base64
import PyPDF2
import docx
from pptx import Presentation
from pylatexenc.latex2text import LatexNodes2Text
from openai import OpenAI
CURRENT_FILE = None
CURRENT_LINE = 1
WINDOW = 100
ENABLE_AUTO_LINT = os.getenv('ENABLE_AUTO_LINT', 'false').lower() == 'true'
# OPENAI
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', '')
OPENAI_BASE_URL = os.getenv('OPENAI_BASE_URL', 'https://api.openai.com/v1')
OPENAI_MODEL = os.getenv('OPENAI_MODEL', 'gpt-4o-2024-05-13')
MAX_TOKEN = os.getenv('MAX_TOKEN', 500)
OPENAI_PROXY = f'{OPENAI_BASE_URL}/chat/completions'
client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)
def _lint_file(file_path: str) -> Optional[str]:
"""
@@ -103,9 +120,9 @@ def open_file(path: str, line_number: Optional[int] = None) -> None:
if line_number is not None:
if (
not isinstance(line_number, int)
or line_number < 1
or line_number > total_lines
not isinstance(line_number, int)
or line_number < 1
or line_number > total_lines
):
raise ValueError(f'Line number must be between 1 and {total_lines}')
CURRENT_LINE = line_number
@@ -261,7 +278,7 @@ def edit_file(start: int, end: int, content: str) -> None:
# recover the original file
with open(original_file_backup_path, 'r') as fin, open(
CURRENT_FILE, 'w'
CURRENT_FILE, 'w'
) as fout:
fout.write(fin.read())
os.remove(original_file_backup_path)
@@ -379,7 +396,203 @@ def find_file(file_name: str, dir_path: str = './') -> None:
print(f'[No matches found for "{file_name}" in {dir_path}]')
def parse_pdf(file_path: str) -> None:
"""Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
"""
print(f'[Reading PDF file from {file_path}]')
content = PyPDF2.PdfReader(file_path)
text = ''
for page_idx in range(len(content.pages)):
text += f'@@ Page {page_idx + 1} @@\n' + content.pages[page_idx].extract_text() + f'\n\n'
print(text.strip())
def parse_docx(file_path: str) -> None:
"""
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
"""
print(f'[Reading DOCX file from {file_path}]')
content = docx.Document(file_path)
text = ''
for i, para in enumerate(content.paragraphs):
text += f'@@ Page {i + 1} @@\n' + para.text + f'\n\n'
print(text)
def parse_latex(file_path: str) -> None:
"""
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
"""
print(f'[Reading LaTex file from {file_path}]')
with open(file_path, 'r') as f:
data = f.read()
text = LatexNodes2Text().latex_to_text(data)
print(text.strip())
def _base64_img(file_path: str) -> str:
with open(file_path, 'rb') as image_file:
encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
return encoded_image
def _base64_video(file_path: str, frame_interval: int = 10) -> list[str]:
import cv2
video = cv2.VideoCapture(file_path)
base64_frames = []
frame_count = 0
while video.isOpened():
success, frame = video.read()
if not success:
break
if frame_count % frame_interval == 0:
_, buffer = cv2.imencode('.jpg', frame)
base64_frames.append(base64.b64encode(buffer).decode('utf-8'))
frame_count += 1
video.release()
return base64_frames
def _prepare_image_messages(task: str, base64_image: str):
return [
{
'role': 'user',
'content': [
{'type': 'text', 'text': task},
{
'type': 'image_url',
'image_url': {
'url': f'data:image/jpeg;base64,{base64_image}'
},
},
],
}
]
def parse_audio(file_path: str, model: str = 'whisper-1') -> None:
"""
Parses the content of an audio file and prints it.
Args:
file_path: str: The path to the audio file to transcribe.
model: Optional[str]: The audio model to use for transcription. Defaults to 'whisper-1'.
"""
print(f'[Transcribing audio file from {file_path}]')
try:
# TODO: record the COST of the API call
with open(file_path, 'rb') as audio_file:
transcript = client.audio.translations.create(
model=model, file=audio_file
)
print(transcript.text)
except Exception as e:
print(f'Error transcribing audio file: {e}')
def parse_image(file_path: str, task: str = 'Describe this image as detail as possible.') -> None:
"""
Parses the content of an image file and prints the description.
Args:
file_path: str: The path to the file to open.
task: Optional[str]: The task description for the API call. Defaults to 'Describe this image as detail as possible.'.
"""
print(f'[Reading image file from {file_path}]')
# TODO: record the COST of the API call
try:
base64_image = _base64_img(file_path)
response = client.chat.completions.create(
model=OPENAI_MODEL,
messages=_prepare_image_messages(task, base64_image),
max_tokens=MAX_TOKEN
)
content = response.choices[0].message.content
print(content)
except Exception as error:
print(f'Error with the request: {error}')
def parse_video(file_path: str,
task: str = 'Describe this image as detail as possible.',
frame_interval: int = 30) -> None:
"""
Parses the content of an image file and prints the description.
Args:
file_path: str: The path to the video file to open.
task: Optional[str]: The task description for the API call. Defaults to 'Describe this image as detail as possible.'.
frame_interval: Optional[int]: The interval between frames to analyze. Defaults to 30.
"""
print(f'[Processing video file from {file_path} with frame interval {frame_interval}]')
video_summary = ''
task = task or 'This is one frame from a video, please summarize this frame.'
base64_frames = _base64_video(file_path)
selected_frames = base64_frames[::frame_interval]
if len(selected_frames) > 30:
new_interval = len(base64_frames) // 30
selected_frames = base64_frames[::new_interval]
print(f'Totally {len(selected_frames)} would be analyze...\n')
idx = 0
for base64_frame in selected_frames:
idx += 1
print(f'Process the {file_path}, current No. {idx * frame_interval} frame...')
# TODO: record the COST of the API call
try:
response = client.chat.completions.create(
model=OPENAI_MODEL,
messages=_prepare_image_messages(task, base64_frame),
max_tokens=MAX_TOKEN
)
content = response.choices[0].message.content
current_frame_content = f"Frame {idx}'s content: {content}\n"
print(current_frame_content)
except Exception as error:
print(f'Error with the request: {error}')
def parse_pptx(file_path: str) -> None:
"""
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
"""
print(f'[Reading PowerPoint file from {file_path}]')
try:
pres = Presentation(str(file_path))
text = []
for slide_idx, slide in enumerate(pres.slides):
text.append(f'@@ Slide {slide_idx + 1} @@')
for shape in slide.shapes:
if hasattr(shape, 'text'):
text.append(shape.text)
print('\n'.join(text))
except Exception as e:
print(f'Error reading PowerPoint file: {e}')
__all__ = [
# file operation
'open_file',
'goto_line',
'scroll_down',
@@ -389,8 +602,15 @@ __all__ = [
'search_dir',
'search_file',
'find_file',
# readers
'parse_pdf',
'parse_docx',
'parse_latex',
'parse_pptx'
]
if OPENAI_API_KEY and OPENAI_BASE_URL:
__all__ += ['parse_audio', 'parse_video', 'parse_image']
DOCUMENTATION = ''
for func_name in __all__:

View File

@@ -10,4 +10,4 @@ export PATH=/opendevin/plugins/agent_skills:$PATH
echo 'export PYTHONPATH=/opendevin/plugins/agent_skills:$PYTHONPATH' >> ~/.bashrc
export PYTHONPATH=/opendevin/plugins/agent_skills:$PYTHONPATH
pip install flake8
pip install flake8 python-docx PyPDF2 python-pptx pylatexenc openai opencv-python

129
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aenum"
@@ -674,6 +674,17 @@ files = [
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]]
name = "chardet"
version = "5.2.0"
description = "Universal encoding detector for Python 3"
optional = false
python-versions = ">=3.7"
files = [
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
@@ -4053,6 +4064,28 @@ typing-extensions = ">=4.7,<5"
[package.extras]
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
[[package]]
name = "opencv-python"
version = "4.9.0.80"
description = "Wrapper package for OpenCV python bindings."
optional = false
python-versions = ">=3.6"
files = [
{file = "opencv-python-4.9.0.80.tar.gz", hash = "sha256:1a9f0e6267de3a1a1db0c54213d022c7c8b5b9ca4b580e80bdc58516c922c9e1"},
{file = "opencv_python-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:7e5f7aa4486651a6ebfa8ed4b594b65bd2d2f41beeb4241a3e4b1b85acbbbadb"},
{file = "opencv_python-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71dfb9555ccccdd77305fc3dcca5897fbf0cf28b297c51ee55e079c065d812a3"},
{file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b34a52e9da36dda8c151c6394aed602e4b17fa041df0b9f5b93ae10b0fcca2a"},
{file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4088cab82b66a3b37ffc452976b14a3c599269c247895ae9ceb4066d8188a57"},
{file = "opencv_python-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:dcf000c36dd1651118a2462257e3a9e76db789a78432e1f303c7bac54f63ef6c"},
{file = "opencv_python-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:3f16f08e02b2a2da44259c7cc712e779eff1dd8b55fdb0323e8cab09548086c0"},
]
[package.dependencies]
numpy = [
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
{version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
]
[[package]]
name = "opentelemetry-api"
version = "1.24.0"
@@ -4332,8 +4365,8 @@ files = [
[package.dependencies]
numpy = [
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
]
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
@@ -4972,6 +5005,16 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pylatexenc"
version = "2.10"
description = "Simple LaTeX parser providing latex-to-unicode and unicode-to-latex conversion"
optional = false
python-versions = "*"
files = [
{file = "pylatexenc-2.10.tar.gz", hash = "sha256:3dd8fd84eb46dc30bee1e23eaab8d8fb5a7f507347b23e5f38ad9675c84f40d3"},
]
[[package]]
name = "pyparsing"
version = "3.1.2"
@@ -5004,6 +5047,24 @@ docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"]
full = ["Pillow (>=8.0.0)", "PyCryptodome", "cryptography"]
image = ["Pillow (>=8.0.0)"]
[[package]]
name = "pypdf2"
version = "3.0.1"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
optional = false
python-versions = ">=3.6"
files = [
{file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"},
{file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"},
]
[package.extras]
crypto = ["PyCryptodome"]
dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"]
docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"]
full = ["Pillow", "PyCryptodome"]
image = ["Pillow"]
[[package]]
name = "pypika"
version = "0.48.9"
@@ -5121,6 +5182,21 @@ files = [
[package.dependencies]
six = ">=1.5"
[[package]]
name = "python-docx"
version = "1.1.2"
description = "Create, read, and update Microsoft Word .docx files."
optional = false
python-versions = ">=3.7"
files = [
{file = "python_docx-1.1.2-py3-none-any.whl", hash = "sha256:08c20d6058916fb19853fcf080f7f42b6270d89eac9fa5f8c15f691c0017fabe"},
{file = "python_docx-1.1.2.tar.gz", hash = "sha256:0cf1f22e95b9002addca7948e16f2cd7acdfd498047f1941ca5d293db7762efd"},
]
[package.dependencies]
lxml = ">=3.1.0"
typing-extensions = ">=4.9.0"
[[package]]
name = "python-dotenv"
version = "1.0.1"
@@ -5149,6 +5225,22 @@ files = [
[package.extras]
dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
[[package]]
name = "python-pptx"
version = "0.6.23"
description = "Generate and manipulate Open XML PowerPoint (.pptx) files"
optional = false
python-versions = "*"
files = [
{file = "python-pptx-0.6.23.tar.gz", hash = "sha256:587497ff28e779ab18dbb074f6d4052893c85dedc95ed75df319364f331fedee"},
{file = "python_pptx-0.6.23-py3-none-any.whl", hash = "sha256:dd0527194627a2b7cc05f3ba23ecaa2d9a0d5ac9b6193a28ed1b7a716f4217d4"},
]
[package.dependencies]
lxml = ">=3.1.0"
Pillow = ">=3.3.2"
XlsxWriter = ">=0.5.7"
[[package]]
name = "pytz"
version = "2024.1"
@@ -5346,6 +5438,26 @@ files = [
{file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"},
]
[[package]]
name = "reportlab"
version = "4.2.0"
description = "The Reportlab Toolkit"
optional = false
python-versions = "<4,>=3.7"
files = [
{file = "reportlab-4.2.0-py3-none-any.whl", hash = "sha256:53630f9d25a7938def3e6a93d723b72a7a5921d34d23cf7a0930adeb2cb0e6c1"},
{file = "reportlab-4.2.0.tar.gz", hash = "sha256:474fb28d63431a5d47d75c90d580393050df7d491a09c7877df3291a2e9f6d0a"},
]
[package.dependencies]
chardet = "*"
pillow = ">=9.0.0"
[package.extras]
accel = ["rl-accel (>=0.9.0,<1.1)"]
pycairo = ["freetype-py (>=2.3.0,<2.4)", "rlPyCairo (>=0.2.0,<1)"]
renderpm = ["rl-renderPM (>=4.0.3,<4.1)"]
[[package]]
name = "requests"
version = "2.31.0"
@@ -7102,6 +7214,17 @@ files = [
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
]
[[package]]
name = "xlsxwriter"
version = "3.2.0"
description = "A Python module for creating Excel XLSX files."
optional = false
python-versions = ">=3.6"
files = [
{file = "XlsxWriter-3.2.0-py3-none-any.whl", hash = "sha256:ecfd5405b3e0e228219bcaf24c2ca0915e012ca9464a14048021d21a995d490e"},
{file = "XlsxWriter-3.2.0.tar.gz", hash = "sha256:9977d0c661a72866a61f9f7a809e25ebbb0fb7036baa3b9fe74afcfca6b3cb8c"},
]
[[package]]
name = "xxhash"
version = "3.4.1"
@@ -7403,4 +7526,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "5c32421d9ba3c24d624b55092816059d5fe2bdbee0bc53e5158e7b4368780391"
content-hash = "141771396f59fc23d52623ada07e4b89272ca781e5a2072f98ebccdf3f18a43b"

View File

@@ -53,6 +53,14 @@ pytest-cov = "*"
pytest-asyncio = "*"
pytest-forked = "*"
flake8 = "*"
openai = "*"
python-docx = "*"
PyPDF2 = "*"
pylatexenc = "*"
python-pptx = "*"
opencv-python = "*"
pandas = "*"
reportlab = "*"
[tool.coverage.run]
concurrency = ["gevent"]

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -72,6 +72,26 @@ find_file(file_name: str, dir_path: str = './') -> None:
file_name: str: The name of the file to find.
dir_path: Optional[str]: The path to the directory to search.
parse_pdf(file_path: str) -> None:
Parses the content of a PDF file and prints it.
Args:
file_path: str: The path to the file to open.
parse_docx(file_path: str) -> None:
Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
parse_latex(file_path: str) -> None:
Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
parse_pptx(file_path: str) -> None:
Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.
Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
The assistant's response should be concise.

View File

@@ -1,7 +1,9 @@
import contextlib
import io
import docx
import pytest
import sys
from opendevin.runtime.plugins.agent_skills.agentskills import (
create_file,
@@ -13,6 +15,11 @@ from opendevin.runtime.plugins.agent_skills.agentskills import (
scroll_up,
search_dir,
search_file,
parse_docx,
parse_latex,
parse_pdf,
parse_pptx,
parse_image
)
@@ -588,3 +595,121 @@ def test_lint_file_disabled_undefined_name(tmp_path, monkeypatch, capsys):
'[File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]\n'
)
assert result.split('\n') == expected.split('\n')
def test_parse_docx(tmp_path):
# Create a DOCX file with some content
test_docx_path = tmp_path / "test.docx"
doc = docx.Document()
doc.add_paragraph('Hello, this is a test document.')
doc.add_paragraph('This is the second paragraph.')
doc.save(str(test_docx_path))
old_stdout = sys.stdout
sys.stdout = io.StringIO()
# Call the parse_docx function
parse_docx(str(test_docx_path))
# Capture the output
output = sys.stdout.getvalue()
sys.stdout = old_stdout
# Check if the output is correct
expected_output = (
f'[Reading DOCX file from {test_docx_path}]\n'
'@@ Page 1 @@\nHello, this is a test document.\n\n'
'@@ Page 2 @@\nThis is the second paragraph.\n\n\n'
)
assert output == expected_output, f"Expected output does not match. Got: {output}"
def test_parse_latex(tmp_path):
# Create a LaTeX file with some content
test_latex_path = tmp_path / "test.tex"
with open(test_latex_path, 'w') as f:
f.write(r'''
\documentclass{article}
\begin{document}
Hello, this is a test LaTeX document.
\end{document}
''')
old_stdout = sys.stdout
sys.stdout = io.StringIO()
# Call the parse_latex function
parse_latex(str(test_latex_path))
# Capture the output
output = sys.stdout.getvalue()
sys.stdout = old_stdout
# Check if the output is correct
expected_output = (
f'[Reading LaTex file from {test_latex_path}]\n'
'Hello, this is a test LaTeX document.\n'
)
assert output == expected_output, f"Expected output does not match. Got: {output}"
def test_parse_pdf(tmp_path):
# Create a PDF file with some content
test_pdf_path = tmp_path / "test.pdf"
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
c = canvas.Canvas(str(test_pdf_path), pagesize=letter)
c.drawString(100, 750, "Hello, this is a test PDF document.")
c.save()
old_stdout = sys.stdout
sys.stdout = io.StringIO()
# Call the parse_pdf function
parse_pdf(str(test_pdf_path))
# Capture the output
output = sys.stdout.getvalue()
sys.stdout = old_stdout
# Check if the output is correct
expected_output = (
f'[Reading PDF file from {test_pdf_path}]\n'
'@@ Page 1 @@\n'
'Hello, this is a test PDF document.\n'
)
assert output == expected_output, f"Expected output does not match. Got: {output}"
def test_parse_pptx(tmp_path):
test_pptx_path = tmp_path / "test.pptx"
from pptx import Presentation
pres = Presentation()
slide1 = pres.slides.add_slide(pres.slide_layouts[0])
title1 = slide1.shapes.title
title1.text = "Hello, this is the first test PPTX slide."
slide2 = pres.slides.add_slide(pres.slide_layouts[0])
title2 = slide2.shapes.title
title2.text = "Hello, this is the second test PPTX slide."
pres.save(str(test_pptx_path))
old_stdout = sys.stdout
sys.stdout = io.StringIO()
parse_pptx(str(test_pptx_path))
output = sys.stdout.getvalue()
sys.stdout = old_stdout
expected_output = (
f'[Reading PowerPoint file from {test_pptx_path}]\n'
'@@ Slide 1 @@\n'
'Hello, this is the first test PPTX slide.\n\n'
'@@ Slide 2 @@\n'
'Hello, this is the second test PPTX slide.\n\n'
)
assert output == expected_output, f"Expected output does not match. Got: {output}"