docs: updated docstrings using ruff's autofix feature (#2923)

* Updated documentation using ruff's autofix feature

* Updated pyproject.toml to include docstring validations

* Updated documentation using ruff's autofix feature

* Updated pyproject.toml to include docstring validations

* Updated docstrings using ruff's autfix feature

* Deleted opendevin/runtime/utils/soource.py, Keeping in sync with main

---------

Co-authored-by: Graham Neubig <neubig@gmail.com>
This commit is contained in:
Anush Kumar V 2024-07-15 19:35:33 -06:00 committed by GitHub
parent 149dac8e5b
commit 8f76587e5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 224 additions and 472 deletions

View File

@ -99,8 +99,7 @@ class BrowsingAgent(Agent):
self,
llm: LLM,
) -> None:
"""
Initializes a new instance of the BrowsingAgent class.
"""Initializes a new instance of the BrowsingAgent class.
Parameters:
- llm (LLM): The llm to be used by this agent
@ -120,16 +119,13 @@ class BrowsingAgent(Agent):
self.reset()
def reset(self) -> None:
"""
Resets the Browsing Agent.
"""
"""Resets the Browsing Agent."""
super().reset()
self.cost_accumulator = 0
self.error_accumulator = 0
def step(self, state: State) -> Action:
"""
Performs one step using the Browsing Agent.
"""Performs one step using the Browsing Agent.
This includes gathering information on previous steps and prompting the model to make a browsing command to execute.
Parameters:

View File

@ -75,7 +75,8 @@ class PromptElement:
Prompt elements are used to build the prompt. Use flags to control which
prompt elements are visible. We use class attributes as a convenient way
to implement static prompts, but feel free to override them with instance
attributes or @property decorator."""
attributes or @property decorator.
"""
_prompt = ''
_abstract_ex = ''
@ -200,11 +201,10 @@ def fit_tokens(
model_name : str, optional
The name of the model used when tokenizing.
Returns
Returns:
-------
str : the prompt after shrinking.
"""
if max_prompt_chars is None:
return shrinkable.prompt
@ -579,8 +579,8 @@ the form is not visible yet or some fields are disabled. I need to replan.
def diff(previous, new):
"""Return a string showing the difference between original and new.
If the difference is above diff_threshold, return the diff string."""
If the difference is above diff_threshold, return the diff string.
"""
if previous == new:
return 'Identical', []

View File

@ -37,9 +37,8 @@ class BrowsingResponseParser(ResponseParser):
class BrowsingActionParserMessage(ActionParser):
"""
Parser action:
- BrowseInteractiveAction(browser_actions) - unexpected response format, message back to user
"""Parser action:
- BrowseInteractiveAction(browser_actions) - unexpected response format, message back to user
"""
def __init__(
@ -60,9 +59,8 @@ class BrowsingActionParserMessage(ActionParser):
class BrowsingActionParserBrowseInteractive(ActionParser):
"""
Parser action:
- BrowseInteractiveAction(browser_actions) - handle send message to user function call in BrowserGym
"""Parser action:
- BrowseInteractiveAction(browser_actions) - handle send message to user function call in BrowserGym
"""
def __init__(

View File

@ -7,7 +7,6 @@ import yaml
def yaml_parser(message):
"""Parse a yaml message for the retry function."""
# saves gpt-3.5 from some yaml parsing errors
message = re.sub(r':\s*\n(?=\S|\n)', ': ', message)
@ -47,7 +46,6 @@ def _compress_chunks(text, identifier, skip_list, split_regex='\n\n+'):
def compress_string(text):
"""Compress a string by replacing redundant paragraphs and lines with identifiers."""
# Perform paragraph-level compression
def_dict, compressed_text = _compress_chunks(
text, identifier='§', skip_list=[], split_regex='\n\n+'
@ -79,12 +77,12 @@ def extract_html_tags(text, keys):
keys : list of str
The HTML tags to extract the content from.
Returns
Returns:
-------
dict
A dictionary mapping each key to a list of subset in `text` that match the key.
Notes
Notes:
-----
All text and keys will be converted to lowercase before matching.
@ -126,7 +124,7 @@ def parse_html_tags(text, keys=(), optional_keys=(), merge_multiple=False):
optional_keys : list of str
The HTML tags to extract the content from, but are optional.
Returns
Returns:
-------
dict
A dictionary mapping each key to subset of `text` that match the key.

View File

@ -12,13 +12,12 @@ from opendevin.events.action import (
class CodeActResponseParser(ResponseParser):
"""
Parser action:
- CmdRunAction(command) - bash command to run
- IPythonRunCellAction(code) - IPython code to run
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""Parser action:
- CmdRunAction(command) - bash command to run
- IPythonRunCellAction(code) - IPython code to run
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""
def __init__(self):
@ -53,9 +52,8 @@ class CodeActResponseParser(ResponseParser):
class CodeActActionParserFinish(ActionParser):
"""
Parser action:
- AgentFinishAction() - end the interaction
"""Parser action:
- AgentFinishAction() - end the interaction
"""
def __init__(
@ -76,10 +74,9 @@ class CodeActActionParserFinish(ActionParser):
class CodeActActionParserCmdRun(ActionParser):
"""
Parser action:
- CmdRunAction(command) - bash command to run
- AgentFinishAction() - end the interaction
"""Parser action:
- CmdRunAction(command) - bash command to run
- AgentFinishAction() - end the interaction
"""
def __init__(
@ -106,9 +103,8 @@ class CodeActActionParserCmdRun(ActionParser):
class CodeActActionParserIPythonRunCell(ActionParser):
"""
Parser action:
- IPythonRunCellAction(code) - IPython code to run
"""Parser action:
- IPythonRunCellAction(code) - IPython code to run
"""
def __init__(
@ -137,9 +133,8 @@ class CodeActActionParserIPythonRunCell(ActionParser):
class CodeActActionParserAgentDelegate(ActionParser):
"""
Parser action:
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
"""Parser action:
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
"""
def __init__(
@ -164,9 +159,8 @@ class CodeActActionParserAgentDelegate(ActionParser):
class CodeActActionParserMessage(ActionParser):
"""
Parser action:
- MessageAction(content) - Message action to run (e.g. ask for clarification)
"""Parser action:
- MessageAction(content) - Message action to run (e.g. ask for clarification)
"""
def __init__(

View File

@ -158,8 +158,7 @@ class CodeActAgent(Agent):
self,
llm: LLM,
) -> None:
"""
Initializes a new instance of the CodeActAgent class.
"""Initializes a new instance of the CodeActAgent class.
Parameters:
- llm (LLM): The llm to be used by this agent
@ -168,14 +167,11 @@ class CodeActAgent(Agent):
self.reset()
def reset(self) -> None:
"""
Resets the CodeAct Agent.
"""
"""Resets the CodeAct Agent."""
super().reset()
def step(self, state: State) -> Action:
"""
Performs one step using the CodeAct Agent.
"""Performs one step using the CodeAct Agent.
This includes gathering info on previous steps and prompting the model to make a command to execute.
Parameters:
@ -188,7 +184,6 @@ class CodeActAgent(Agent):
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""
# if we're done, go back
latest_user_message = state.history.get_last_user_message()
if latest_user_message and latest_user_message.strip() == '/exit':

View File

@ -11,9 +11,8 @@ from opendevin.events.action import (
class CodeActSWEActionParserFinish(ActionParser):
"""
Parser action:
- AgentFinishAction() - end the interaction
"""Parser action:
- AgentFinishAction() - end the interaction
"""
def __init__(
@ -34,10 +33,9 @@ class CodeActSWEActionParserFinish(ActionParser):
class CodeActSWEActionParserCmdRun(ActionParser):
"""
Parser action:
- CmdRunAction(command) - bash command to run
- AgentFinishAction() - end the interaction
"""Parser action:
- CmdRunAction(command) - bash command to run
- AgentFinishAction() - end the interaction
"""
def __init__(
@ -64,9 +62,8 @@ class CodeActSWEActionParserCmdRun(ActionParser):
class CodeActSWEActionParserIPythonRunCell(ActionParser):
"""
Parser action:
- IPythonRunCellAction(code) - IPython code to run
"""Parser action:
- IPythonRunCellAction(code) - IPython code to run
"""
def __init__(
@ -95,9 +92,8 @@ class CodeActSWEActionParserIPythonRunCell(ActionParser):
class CodeActSWEActionParserMessage(ActionParser):
"""
Parser action:
- MessageAction(content) - Message action to run (e.g. ask for clarification)
"""Parser action:
- MessageAction(content) - Message action to run (e.g. ask for clarification)
"""
def __init__(

View File

@ -113,8 +113,7 @@ class CodeActSWEAgent(Agent):
self,
llm: LLM,
) -> None:
"""
Initializes a new instance of the CodeActAgent class.
"""Initializes a new instance of the CodeActAgent class.
Parameters:
- llm (LLM): The llm to be used by this agent
@ -123,14 +122,11 @@ class CodeActSWEAgent(Agent):
self.reset()
def reset(self) -> None:
"""
Resets the CodeAct Agent.
"""
"""Resets the CodeAct Agent."""
super().reset()
def step(self, state: State) -> Action:
"""
Performs one step using the CodeAct Agent.
"""Performs one step using the CodeAct Agent.
This includes gathering info on previous steps and prompting the model to make a command to execute.
Parameters:
@ -142,7 +138,6 @@ class CodeActSWEAgent(Agent):
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""
# if we're done, go back
latest_user_message = state.history.get_last_user_message()
if latest_user_message and latest_user_message.strip() == '/exit':

View File

@ -9,12 +9,11 @@ from opendevin.events.action import Action
class CodeActSWEResponseParser(ResponseParser):
"""
Parser action:
- CmdRunAction(command) - bash command to run
- IPythonRunCellAction(code) - IPython code to run
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""Parser action:
- CmdRunAction(command) - bash command to run
- IPythonRunCellAction(code) - IPython code to run
- MessageAction(content) - Message action to run (e.g. ask for clarification)
- AgentFinishAction() - end the interaction
"""
def __init__(self):

View File

@ -14,8 +14,7 @@ class DelegatorAgent(Agent):
current_delegate: str = ''
def __init__(self, llm: LLM):
"""
Initialize the Delegator Agent with an LLM
"""Initialize the Delegator Agent with an LLM
Parameters:
- llm (LLM): The llm to be used by this agent
@ -23,8 +22,7 @@ class DelegatorAgent(Agent):
super().__init__(llm)
def step(self, state: State) -> Action:
"""
Checks to see if current step is completed, returns AgentFinishAction if True.
"""Checks to see if current step is completed, returns AgentFinishAction if True.
Otherwise, delegates the task to the next agent in the pipeline.
Parameters:

View File

@ -23,16 +23,12 @@ def parse_response(orig_response: str) -> Action:
def to_json(obj, **kwargs):
"""
Serialize an object to str format
"""
"""Serialize an object to str format"""
return json.dumps(obj, **kwargs)
def history_to_json(history: ShortTermHistory, max_events=20, **kwargs):
"""
Serialize and simplify history to str format
"""
"""Serialize and simplify history to str format"""
# TODO: get agent specific llm config
llm_config = config.get_llm_config()
max_message_chars = llm_config.max_message_chars

View File

@ -47,8 +47,7 @@ class MonologueAgent(Agent):
response_parser = MonologueResponseParser()
def __init__(self, llm: LLM):
"""
Initializes the Monologue Agent with an llm.
"""Initializes the Monologue Agent with an llm.
Parameters:
- llm (LLM): The llm to be used by this agent
@ -56,8 +55,7 @@ class MonologueAgent(Agent):
super().__init__(llm)
def _initialize(self, task: str):
"""
Utilizes the INITIAL_THOUGHTS list to give the agent a context for its capabilities
"""Utilizes the INITIAL_THOUGHTS list to give the agent a context for its capabilities
and how to navigate the WORKSPACE_MOUNT_PATH_IN_SANDBOX in `config` (e.g., /workspace by default).
Short circuited to return when already initialized.
Will execute again when called after reset.
@ -68,7 +66,6 @@ class MonologueAgent(Agent):
Raises:
- AgentNoInstructionError: If task is not provided
"""
if self._initialized:
return
@ -133,8 +130,7 @@ class MonologueAgent(Agent):
self.initial_thoughts.append(event_to_memory(action, max_message_chars))
def step(self, state: State) -> Action:
"""
Modifies the current state by adding the most recent actions and observations, then prompts the model to think about it's next action to take using monologue, memory, and hint.
"""Modifies the current state by adding the most recent actions and observations, then prompts the model to think about it's next action to take using monologue, memory, and hint.
Parameters:
- state (State): The current state based on previous steps taken

View File

@ -19,8 +19,7 @@ class MonologueResponseParser(ResponseParser):
return response['choices'][0]['message']['content']
def parse_action(self, action_str: str) -> Action:
"""
Parses a string to find an action within it
"""Parses a string to find an action within it
Parameters:
- response (str): The string to be parsed

View File

@ -120,8 +120,7 @@ INITIAL_THOUGHTS = [
def get_summarize_monologue_prompt(thoughts: list[dict]):
"""
Gets the prompt for summarizing the monologue
"""Gets the prompt for summarizing the monologue
Returns:
- str: A formatted string with the current monologue within the prompt
@ -136,8 +135,7 @@ def get_request_action_prompt(
thoughts: list[dict],
recent_events: list[dict],
):
"""
Gets the action prompt formatted with appropriate values.
"""Gets the action prompt formatted with appropriate values.
Parameters:
- task (str): The current task the agent is trying to accomplish
@ -146,7 +144,6 @@ def get_request_action_prompt(
Returns:
- str: Formatted prompt string with hint, task, monologue, and background commands included
"""
hint = ''
if len(recent_events) > 0:
latest_event = recent_events[-1]
@ -179,8 +176,7 @@ def get_request_action_prompt(
def parse_action_response(orig_response: str) -> Action:
"""
Parses a string to find an action within it
"""Parses a string to find an action within it
Parameters:
- response (str): The string to be parsed
@ -199,8 +195,7 @@ def parse_action_response(orig_response: str) -> Action:
def parse_summary_response(response: str) -> list[dict]:
"""
Parses a summary of the monologue
"""Parses a summary of the monologue
Parameters:
- response (str): The response string to be parsed

View File

@ -18,8 +18,7 @@ class PlannerAgent(Agent):
response_parser = MonologueResponseParser()
def __init__(self, llm: LLM):
"""
Initialize the Planner Agent with an LLM
"""Initialize the Planner Agent with an LLM
Parameters:
- llm (LLM): The llm to be used by this agent
@ -27,8 +26,7 @@ class PlannerAgent(Agent):
super().__init__(llm)
def step(self, state: State) -> Action:
"""
Checks to see if current step is completed, returns AgentFinishAction if True.
"""Checks to see if current step is completed, returns AgentFinishAction if True.
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
Parameters:
@ -38,7 +36,6 @@ class PlannerAgent(Agent):
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
- Action: The next action to take based on llm response
"""
if state.root_task.state in [
'completed',
'verified',

View File

@ -101,7 +101,6 @@ What is your next thought or action? Again, you must reply with JSON, and only w
def get_hint(latest_action_id: str) -> str:
"""Returns action type hint based on given action_id"""
hints = {
'': "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with.",
ActionType.RUN: 'You should think about the command you just ran, what output it gave, and how that affects your plan.',
@ -118,8 +117,7 @@ def get_hint(latest_action_id: str) -> str:
def get_prompt(state: State) -> str:
"""
Gets the prompt for the planner agent.
"""Gets the prompt for the planner agent.
Formatted with the most recent action-observation pairs, current task, and hint based on last action
Parameters:
@ -180,10 +178,10 @@ def get_prompt(state: State) -> str:
def parse_response(response: str) -> Action:
"""
Parses the model output to find a valid action to take
"""Parses the model output to find a valid action to take
Parameters:
- response (str): A response from the model that potentially contains an Action.
Returns:
- Action: A valid next action to perform from model output
"""

View File

@ -66,9 +66,7 @@ AGENT_CLS_TO_INST_SUFFIX = {
def execute_sql(db_path, gen_sql, gold_sql):
"""
Execute the generated SQL and the ground truth SQL and compare the results.
"""
"""Execute the generated SQL and the ground truth SQL and compare the results."""
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
cursor.execute(gen_sql)
@ -255,18 +253,14 @@ def process_instance(
def load_bird():
"""
Main function to handle the flow of downloading, processing, and loading the bird dataset.
"""
"""Main function to handle the flow of downloading, processing, and loading the bird dataset."""
raw_dataset_path = download_bird()
bird_dataset = process_bird(raw_dataset_path)
return bird_dataset
def download_bird():
"""
Downloads and extracts the bird dataset from a specified URL into a local directory.
"""
"""Downloads and extracts the bird dataset from a specified URL into a local directory."""
dataset_path = os.path.join(config.workspace_base, 'evaluation_bird')
devset_path = os.path.join(dataset_path, 'dev')
if not os.path.exists(dataset_path):
@ -292,9 +286,7 @@ def download_bird():
def process_bird(dataset_path):
"""
Processes the raw bird dataset into a structured format and saves it as JSON.
"""
"""Processes the raw bird dataset into a structured format and saves it as JSON."""
processed_path = os.path.join(dataset_path, 'processed_dev.json')
if not os.path.exists(processed_path):
logger.info(f'{processed_path} folder does not exist, starting processing...')
@ -325,9 +317,7 @@ def process_bird(dataset_path):
def extract_create_table_prompt(db_path, limit_value=0):
"""
Generates a SQL prompt with CREATE TABLE statements and sample data from the database.
"""
"""Generates a SQL prompt with CREATE TABLE statements and sample data from the database."""
table_query = "SELECT * FROM sqlite_master WHERE type='table';"
tables = sqlite3.connect(db_path).cursor().execute(table_query).fetchall()
prompt = ''
@ -367,9 +357,7 @@ def extract_create_table_prompt(db_path, limit_value=0):
def create_prompt(e, database_path):
"""
Create a prompt for the given example
"""
"""Create a prompt for the given example"""
db_id = e['db_id']
db_path = pathlib.Path(database_path) / db_id / f'{db_id}.sqlite'

View File

@ -80,14 +80,14 @@ def question_scorer(
def normalize_str(input_str, remove_punct=True) -> str:
"""
Normalize a string by:
"""Normalize a string by:
- Removing all white spaces
- Optionally removing punctuation (if remove_punct is True)
- Converting to lowercase
Parameters:
- input_str: str, the string to normalize
- remove_punct: bool, whether to remove punctuation (default: True)
Returns:
- str, the normalized string
"""

View File

@ -10,7 +10,6 @@ from ast_eval_th import ast_eval_th
# This function is modified from Gorilla's APIBench implementations (https://github.com/ShishirPatil/gorilla/blob/main/eval/get_llm_responses.py).
def encode_question(question, api_name):
"""Encode multiple prompt instructions into a single string."""
prompts = []
if api_name == 'torch':
api_name = 'torchhub'

View File

@ -1,5 +1,4 @@
"""
Overview:
"""Overview:
This code implements the evaluation of agents on the GPQA Benchmark with Open Book setting.
- The benchmark consists of 448 high-quality and extremely difficult multiple-choice questions in the domains of biology, physics, and chemistry. The questions are intentionally designed to be "Google-proof," meaning that even highly skilled non-expert validators achieve only 34% accuracy despite unrestricted access to the web.
- Even experts in the corresponding domains achieve only 65% accuracy.
@ -54,8 +53,7 @@ AGENT_CLS_TO_INST_SUFFIX = {
def parse_final_answer(final_answer: str) -> str:
"""
Parse the final answer from the final message generated by the agent
"""Parse the final answer from the final message generated by the agent
to extract the final answer. The final answer is usually enclosed in the format:
<<FINAL_ANSWER||
<insert correct answer here>
@ -71,15 +69,12 @@ def parse_final_answer(final_answer: str) -> str:
def compare_answers(predicted_answer, ground_truth):
"""
Compare the predicted answer with the ground truth answer
"""
"""Compare the predicted answer with the ground truth answer"""
return predicted_answer == ground_truth
def get_test_result(model_output, ground_truth):
"""
Implements the evaluation logic for GPQA
"""Implements the evaluation logic for GPQA
Checks if the output of a given instance is correct (as per the ground truth)
"""
# parse the final answer from model output
@ -92,8 +87,7 @@ def get_test_result(model_output, ground_truth):
def convert_instance_dict(instance):
"""
Used for preprocessing the hf dataset into a format that can be used by the agent.
"""Used for preprocessing the hf dataset into a format that can be used by the agent.
Reads and extracts relevant information from the dataset instance.
"""
out_instance_dict = {}

View File

@ -1,5 +1,4 @@
"""
Implements evaluation of agents on HumanEvalFix from the HumanEvalPack benchmark introduced in
"""Implements evaluation of agents on HumanEvalFix from the HumanEvalPack benchmark introduced in
"OctoPack: Instruction Tuning Code Large Language Models" (https://arxiv.org/abs/2308.07124).
Please see https://github.com/bigcode-project/bigcode-evaluation-harness/blob/main/bigcode_eval/tasks/humanevalpack.py
for the reference implementation used in the paper.

View File

@ -74,7 +74,6 @@ class HumanEvalTask(CodeGenTask):
Modified from:
https://github.com/bigcode-project/bigcode-evaluation-harness/blob/d61afde130005ecc65cf800ad8eca790a9bc2115/lm_eval/tasks/humaneval.py#L56
"""
# STOP_WORDS = ["\nclass", "\ndef", "\n#", "\n@", "\nprint", "\nif"]
# # Remove the last block of the code containing stop_words for HumanEval
# string_list = re.split("(%s)" % "|".join(STOP_WORDS), solution)

View File

@ -79,14 +79,12 @@ def check_correctness(
timeout: float = 10,
completion_id: Optional[int] = None,
) -> Dict:
"""
Evaluates the functional correctness of a completion by running the test
"""Evaluates the functional correctness of a completion by running the test
suite provided in the problem.
:param completion_id: an optional completion ID so we can match
the results later even if execution finishes asynchronously.
"""
manager = multiprocessing.Manager()
result = manager.list()
@ -181,18 +179,16 @@ def chdir(root):
def reliability_guard(maximum_memory_bytes: Optional[int] = None):
"""
This disables various destructive functions and prevents the generated code
"""This disables various destructive functions and prevents the generated code
from interfering with the test (e.g. fork bomb, killing other processes,
removing filesystem files, etc.)
WARNING
Warning:
This function is NOT a security sandbox. Untrusted code, including, model-
generated code, should not be blindly executed outside of one. See the
Codex paper for more information about OpenAI's code sandbox, and proceed
with caution.
"""
if maximum_memory_bytes is not None:
import resource

View File

@ -1,5 +1,4 @@
"""
Implements evaluation of agents on ML-Bench, a benchmark for assessing the effectiveness of
"""Implements evaluation of agents on ML-Bench, a benchmark for assessing the effectiveness of
Large Language Models (LLMs) in leveraging existing functions in open-source libraries for
machine learning tasks. The benchmark is introduced in the paper "ML-Bench: Evaluating Large
Language Models for Code Generation in Repository-Level Machine Learning Tasks"

View File

@ -6,9 +6,7 @@ from conftest import agents
@pytest.mark.parametrize('agent', agents())
def test_hello_world(task_file, run_test_case, agent):
"""
Test case for the "Hello, World!" Bash script using different agents.
"""
"""Test case for the "Hello, World!" Bash script using different agents."""
# Run the test case for the specified agent
workspace_dir = run_test_case(agent, 'hello-world')
@ -16,7 +14,7 @@ def test_hello_world(task_file, run_test_case, agent):
assert os.path.exists(workspace_dir)
assert os.path.isfile(os.path.join(workspace_dir, 'hello_world.sh'))
# Execute the hello_world.sh script
# Execute the hello_world.sh script
os.chdir(workspace_dir)
output = os.popen('bash hello_world.sh').read()
assert output == 'Hello, World!\n'

View File

@ -1,5 +1,4 @@
"""
This script compares gold patches with OpenDevin-generated patches and check whether
"""This script compares gold patches with OpenDevin-generated patches and check whether
OpenDevin found the right (set of) files to modify.
"""

View File

@ -4,8 +4,7 @@ from opendevin.events.action import Action
class ResponseParser(ABC):
"""
This abstract base class is a general interface for an response parser dedicated to
"""This abstract base class is a general interface for an response parser dedicated to
parsing the action from the response from the LLM.
"""
@ -17,8 +16,7 @@ class ResponseParser(ABC):
@abstractmethod
def parse(self, response: str) -> Action:
"""
Parses the action from the response from the LLM.
"""Parses the action from the response from the LLM.
Parameters:
- response (str): The response from the LLM.
@ -30,8 +28,7 @@ class ResponseParser(ABC):
@abstractmethod
def parse_response(self, response) -> str:
"""
Parses the action from the response from the LLM.
"""Parses the action from the response from the LLM.
Parameters:
- response (str): The response from the LLM.
@ -43,8 +40,7 @@ class ResponseParser(ABC):
@abstractmethod
def parse_action(self, action_str: str) -> Action:
"""
Parses the action from the response from the LLM.
"""Parses the action from the response from the LLM.
Parameters:
- action_str (str): The response from the LLM.
@ -56,21 +52,16 @@ class ResponseParser(ABC):
class ActionParser(ABC):
"""
This abstract base class is a general interface for an action parser dedicated to
"""This abstract base class is a general interface for an action parser dedicated to
parsing the action from the action str from the LLM.
"""
@abstractmethod
def check_condition(self, action_str: str) -> bool:
"""
Check if the action string can be parsed by this parser.
"""
"""Check if the action string can be parsed by this parser."""
pass
@abstractmethod
def parse(self, action_str: str) -> Action:
"""
Parses the action from the action string from the LLM response.
"""
"""Parses the action from the action string from the LLM response."""
pass

View File

@ -35,8 +35,7 @@ class Agent(ABC):
@property
def complete(self) -> bool:
"""
Indicates whether the current instruction execution is complete.
"""Indicates whether the current instruction execution is complete.
Returns:
- complete (bool): True if execution is complete; False otherwise.
@ -45,15 +44,13 @@ class Agent(ABC):
@abstractmethod
def step(self, state: 'State') -> 'Action':
"""
Starts the execution of the assigned instruction. This method should
"""Starts the execution of the assigned instruction. This method should
be implemented by subclasses to define the specific execution logic.
"""
pass
def reset(self) -> None:
"""
Resets the agent's execution status and clears the history. This method can be used
"""Resets the agent's execution status and clears the history. This method can be used
to prepare the agent for restarting the instruction or cleaning up before destruction.
"""
@ -66,8 +63,7 @@ class Agent(ABC):
@classmethod
def register(cls, name: str, agent_cls: Type['Agent']):
"""
Registers an agent class in the registry.
"""Registers an agent class in the registry.
Parameters:
- name (str): The name to register the class under.
@ -82,8 +78,7 @@ class Agent(ABC):
@classmethod
def get_cls(cls, name: str) -> Type['Agent']:
"""
Retrieves an agent class from the registry.
"""Retrieves an agent class from the registry.
Parameters:
- name (str): The name of the class to retrieve
@ -100,8 +95,7 @@ class Agent(ABC):
@classmethod
def list_agents(cls) -> list[str]:
"""
Retrieves the list of all agent names from the registry.
"""Retrieves the list of all agent names from the registry.
Raises:
- AgentNotRegisteredError: If no agent is registered

View File

@ -122,8 +122,7 @@ class AgentController:
self.state.metrics = self.agent.llm.metrics
async def report_error(self, message: str, exception: Exception | None = None):
"""
This error will be reported to the user and sent to the LLM next step, in the hope it can self-correct.
"""This error will be reported to the user and sent to the LLM next step, in the hope it can self-correct.
This method should be called for a particular type of errors, which have:
- a user-friendly message, which will be shown in the chat box. This should not be a raw exception message.

View File

@ -110,9 +110,7 @@ class State:
# remove the restored data from the state if any
def get_current_user_intent(self):
"""
Returns the latest user message that appears after a FinishAction, or the first (the task) if nothing was finished yet.
"""
"""Returns the latest user message that appears after a FinishAction, or the first (the task) if nothing was finished yet."""
last_user_message = None
for event in self.history.get_events(reverse=True):
if isinstance(event, MessageAction) and event.source == 'user':

View File

@ -21,8 +21,7 @@ load_dotenv()
@dataclass
class LLMConfig:
"""
Configuration for the LLM model.
"""Configuration for the LLM model.
Attributes:
model: The model to use.
@ -75,9 +74,7 @@ class LLMConfig:
ollama_base_url: str | None = None
def defaults_to_dict(self) -> dict:
"""
Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
"""
"""Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional."""
result = {}
for f in fields(self):
result[f.name] = get_field_info(f)
@ -102,8 +99,7 @@ class LLMConfig:
@dataclass
class AgentConfig:
"""
Configuration for the agent.
"""Configuration for the agent.
Attributes:
memory_enabled: Whether long-term memory (embeddings) is enabled.
@ -116,9 +112,7 @@ class AgentConfig:
llm_config: str | None = None
def defaults_to_dict(self) -> dict:
"""
Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
"""
"""Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional."""
result = {}
for f in fields(self):
result[f.name] = get_field_info(f)
@ -127,8 +121,7 @@ class AgentConfig:
@dataclass
class SandboxConfig(metaclass=Singleton):
"""
Configuration for the sandbox.
"""Configuration for the sandbox.
Attributes:
box_type: The type of sandbox to use. Options are: ssh, e2b, local.
@ -148,9 +141,7 @@ class SandboxConfig(metaclass=Singleton):
timeout: int = 120
def defaults_to_dict(self) -> dict:
"""
Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
"""
"""Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional."""
dict = {}
for f in fields(self):
dict[f.name] = get_field_info(f)
@ -176,8 +167,7 @@ class UndefinedString(str, Enum):
@dataclass
class AppConfig(metaclass=Singleton):
"""
Configuration for the app.
"""Configuration for the app.
Attributes:
llms: A dictionary of name -> LLM configuration. Default config is under 'llm' key.
@ -247,9 +237,7 @@ class AppConfig(metaclass=Singleton):
defaults_dict: ClassVar[dict] = {}
def get_llm_config(self, name='llm') -> LLMConfig:
"""
llm is the name for default config (for backward compatibility prior to 0.8)
"""
"""Llm is the name for default config (for backward compatibility prior to 0.8)"""
if name in self.llms:
return self.llms[name]
if name is not None and name != 'llm':
@ -262,9 +250,7 @@ class AppConfig(metaclass=Singleton):
self.llms[name] = value
def get_agent_config(self, name='agent') -> AgentConfig:
"""
agent is the name for default config (for backward compability prior to 0.8)
"""
"""Agent is the name for default config (for backward compability prior to 0.8)"""
if name in self.agents:
return self.agents[name]
if 'agent' not in self.agents:
@ -280,15 +266,11 @@ class AppConfig(metaclass=Singleton):
return self.get_llm_config(llm_config_name)
def __post_init__(self):
"""
Post-initialization hook, called when the instance is created with only default values.
"""
"""Post-initialization hook, called when the instance is created with only default values."""
AppConfig.defaults_dict = self.defaults_to_dict()
def defaults_to_dict(self) -> dict:
"""
Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
"""
"""Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional."""
result = {}
for f in fields(self):
field_value = getattr(self, f.name)
@ -323,8 +305,7 @@ class AppConfig(metaclass=Singleton):
def get_field_info(f):
"""
Extract information about a dataclass field: type, optional, and default.
"""Extract information about a dataclass field: type, optional, and default.
Args:
f: The field to extract information from.
@ -423,7 +404,6 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
cfg: The AppConfig object to update attributes of.
toml_file: The path to the toml file. Defaults to 'config.toml'.
"""
# try to read the config.toml file into the config object
try:
with open(toml_file, 'r', encoding='utf-8') as toml_contents:
@ -514,10 +494,7 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
def finalize_config(cfg: AppConfig):
"""
More tweaks to the config after it's been loaded.
"""
"""More tweaks to the config after it's been loaded."""
# Set workspace_mount_path if not set by the user
if cfg.workspace_mount_path is UndefinedString.UNDEFINED:
cfg.workspace_mount_path = os.path.abspath(cfg.workspace_base)
@ -558,8 +535,7 @@ finalize_config(config)
def get_llm_config_arg(
llm_config_arg: str, toml_file: str = 'config.toml'
) -> LLMConfig | None:
"""
Get a group of llm settings from the config file.
"""Get a group of llm settings from the config file.
A group in config.toml can look like this:
@ -583,7 +559,6 @@ def get_llm_config_arg(
Returns:
LLMConfig: The LLMConfig object with the settings from the config file.
"""
# keep only the name, just in case
llm_config_arg = llm_config_arg.strip('[]')
@ -613,9 +588,7 @@ def get_llm_config_arg(
# Command line arguments
def get_parser() -> argparse.ArgumentParser:
"""
Get the parser for the command line arguments.
"""
"""Get the parser for the command line arguments."""
parser = argparse.ArgumentParser(description='Run an agent with a specific task')
parser.add_argument(
'-d',
@ -689,9 +662,7 @@ def get_parser() -> argparse.ArgumentParser:
def parse_arguments() -> argparse.Namespace:
"""
Parse the command line arguments.
"""
"""Parse the command line arguments."""
parser = get_parser()
parsed_args, _ = parser.parse_known_args()
if parsed_args.directory:

View File

@ -115,9 +115,7 @@ class SensitiveDataFilter(logging.Filter):
def get_console_handler():
"""
Returns a console handler for logging.
"""
"""Returns a console handler for logging."""
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
if config.debug:
@ -127,9 +125,7 @@ def get_console_handler():
def get_file_handler(log_dir=None):
"""
Returns a file handler for logging.
"""
"""Returns a file handler for logging."""
log_dir = os.path.join(os.getcwd(), 'logs') if log_dir is None else log_dir
os.makedirs(log_dir, exist_ok=True)
timestamp = datetime.now().strftime('%Y-%m-%d')
@ -146,8 +142,7 @@ logging.basicConfig(level=logging.ERROR)
def log_uncaught_exceptions(ex_cls, ex, tb):
"""
Logs uncaught exceptions along with the traceback.
"""Logs uncaught exceptions along with the traceback.
Args:
ex_cls (type): The type of the exception.
@ -183,13 +178,10 @@ logging.getLogger('LiteLLM Proxy').disabled = True
class LlmFileHandler(logging.FileHandler):
"""
# LLM prompt and response logging
"""
"""# LLM prompt and response logging"""
def __init__(self, filename, mode='a', encoding='utf-8', delay=False):
"""
Initializes an instance of LlmFileHandler.
"""Initializes an instance of LlmFileHandler.
Args:
filename (str): The name of the log file.
@ -220,8 +212,7 @@ class LlmFileHandler(logging.FileHandler):
super().__init__(self.baseFilename, mode, encoding, delay)
def emit(self, record):
"""
Emits a log record.
"""Emits a log record.
Args:
record (logging.LogRecord): The log record to emit.

View File

@ -50,7 +50,6 @@ async def run_agent_controller(
fake_user_response_fn: An optional function that receives the current state (could be None) and returns a fake user response.
sandbox: An optional sandbox to run the agent in.
"""
# Logging
logger.info(
f'Running agent {agent.name}, model {agent.llm.model_name}, with task: "{task_str}"'

View File

@ -1,6 +1,5 @@
class Metrics:
"""
Metrics class can record various metrics during running and evaluation.
"""Metrics class can record various metrics during running and evaluation.
Currently, we define the following metrics:
accumulated_cost: the total cost (USD $) of the current LLM.
"""
@ -30,15 +29,11 @@ class Metrics:
self._costs.append(value)
def get(self):
"""
Return the metrics in a dictionary.
"""
"""Return the metrics in a dictionary."""
return {'accumulated_cost': self._accumulated_cost, 'costs': self._costs}
def log(self):
"""
Log the metrics.
"""
"""Log the metrics."""
metrics = self.get()
logs = ''
for key, value in metrics.items():

View File

@ -9,9 +9,7 @@ from opendevin.events.serialization import event_to_dict
def my_default_encoder(obj):
"""
Custom JSON encoder that handles datetime and event objects
"""
"""Custom JSON encoder that handles datetime and event objects"""
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, Event):
@ -20,17 +18,12 @@ def my_default_encoder(obj):
def dumps(obj, **kwargs):
"""
Serialize an object to str format
"""
"""Serialize an object to str format"""
return json.dumps(obj, default=my_default_encoder, **kwargs)
def loads(json_str, **kwargs):
"""
Create a JSON object from str
"""
"""Create a JSON object from str"""
try:
return json.loads(json_str, **kwargs)
except json.JSONDecodeError:

View File

@ -8,8 +8,7 @@ from .action import Action
@dataclass
class FileReadAction(Action):
"""
Reads a file from a given path.
"""Reads a file from a given path.
Can be set to read specific lines using start and end
Default lines 0:-1 (whole file)
"""

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class AgentStateChangedObservation(Observation):
"""
This data class represents the result from delegating to another agent
"""
"""This data class represents the result from delegating to another agent"""
agent_state: str
observation: str = ObservationType.AGENT_STATE_CHANGED

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class BrowserOutputObservation(Observation):
"""
This data class represents the output of a browser.
"""
"""This data class represents the output of a browser."""
url: str
screenshot: str = field(repr=False) # don't show in repr

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class CmdOutputObservation(Observation):
"""
This data class represents the output of a command.
"""
"""This data class represents the output of a command."""
command_id: int
command: str
@ -30,9 +28,7 @@ class CmdOutputObservation(Observation):
@dataclass
class IPythonRunCellObservation(Observation):
"""
This data class represents the output of a IPythonRunCellAction.
"""
"""This data class represents the output of a IPythonRunCellAction."""
code: str
observation: str = ObservationType.RUN_IPYTHON

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class AgentDelegateObservation(Observation):
"""
This data class represents the result from delegating to another agent
"""
"""This data class represents the result from delegating to another agent"""
outputs: dict
observation: str = ObservationType.DELEGATE

View File

@ -7,8 +7,7 @@ from .observation import Observation
@dataclass
class NullObservation(Observation):
"""
This data class represents a null observation.
"""This data class represents a null observation.
This is used when the produced action is NOT executable.
"""

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class ErrorObservation(Observation):
"""
This data class represents an error encountered by the agent.
"""
"""This data class represents an error encountered by the agent."""
observation: str = ObservationType.ERROR

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class FileReadObservation(Observation):
"""
This data class represents the content of a file.
"""
"""This data class represents the content of a file."""
path: str
observation: str = ObservationType.READ
@ -21,9 +19,7 @@ class FileReadObservation(Observation):
@dataclass
class FileWriteObservation(Observation):
"""
This data class represents a file write operation
"""
"""This data class represents a file write operation"""
path: str
observation: str = ObservationType.WRITE

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class RejectObservation(Observation):
"""
This data class represents the result of a successful action.
"""
"""This data class represents the result of a successful action."""
observation: str = ObservationType.USER_REJECTED

View File

@ -7,9 +7,7 @@ from .observation import Observation
@dataclass
class SuccessObservation(Observation):
"""
This data class represents the result of a successful action.
"""
"""This data class represents the result of a successful action."""
observation: str = ObservationType.SUCCESS

View File

@ -83,9 +83,7 @@ def event_to_memory(event: 'Event', max_message_chars: int) -> dict:
def truncate_content(content: str, max_chars: int) -> str:
"""
Truncate the middle of the observation content if it is too long.
"""
"""Truncate the middle of the observation content if it is too long."""
if len(content) <= max_chars:
return content

View File

@ -1,6 +1,5 @@
def remove_fields(obj, fields: set[str]):
"""
Remove fields from an object.
"""Remove fields from an object.
Parameters:
- obj: The dictionary, or list of dictionaries to remove fields from

View File

@ -32,8 +32,7 @@ message_separator = '\n\n----------\n\n'
class LLM:
"""
The LLM class represents a Language Model instance.
"""The LLM class represents a Language Model instance.
Attributes:
model_name (str): The name of the language model.
@ -67,8 +66,7 @@ class LLM:
input_cost_per_token=None,
output_cost_per_token=None,
):
"""
Initializes the LLM. If LLMConfig is passed, its values will be the fallback.
"""Initializes the LLM. If LLMConfig is passed, its values will be the fallback.
Passing simple parameters always overrides config.
@ -213,10 +211,7 @@ class LLM:
after=attempt_on_error,
)
def wrapper(*args, **kwargs):
"""
Wrapper for the litellm completion function. Logs the input and output of the completion function.
"""
"""Wrapper for the litellm completion function. Logs the input and output of the completion function."""
# some callers might just send the messages directly
if 'messages' in kwargs:
messages = kwargs['messages']
@ -244,17 +239,14 @@ class LLM:
@property
def completion(self):
"""
Decorator for the litellm completion function.
"""Decorator for the litellm completion function.
Check the complete documentation at https://litellm.vercel.app/docs/completion
"""
return self._completion
def _post_completion(self, response: str) -> None:
"""
Post-process the completion response.
"""
"""Post-process the completion response."""
try:
cur_cost = self.completion_cost(response)
except Exception:
@ -267,8 +259,7 @@ class LLM:
)
def get_token_count(self, messages):
"""
Get the number of tokens in a list of messages.
"""Get the number of tokens in a list of messages.
Args:
messages (list): A list of messages.
@ -279,8 +270,7 @@ class LLM:
return litellm.token_counter(model=self.model_name, messages=messages)
def is_local(self):
"""
Determines if the system is using a locally running LLM.
"""Determines if the system is using a locally running LLM.
Returns:
boolean: True if executing a local model.
@ -295,8 +285,7 @@ class LLM:
return False
def completion_cost(self, response):
"""
Calculate the cost of a completion response based on the model. Local models are treated as free.
"""Calculate the cost of a completion response based on the model. Local models are treated as free.
Add the current cost into total cost in metrics.
Args:

View File

@ -4,8 +4,7 @@ from opendevin.llm.llm import LLM
class MemoryCondenser:
def condense(self, summarize_prompt: str, llm: LLM):
"""
Attempts to condense the monologue by using the llm
"""Attempts to condense the monologue by using the llm
Parameters:
- llm (LLM): llm to be used for summarization
@ -13,7 +12,6 @@ class MemoryCondenser:
Raises:
- Exception: the same exception as it got from the llm or processing the response
"""
try:
messages = [{'content': summarize_prompt, 'role': 'user'}]
resp = llm.completion(messages=messages)

View File

@ -19,8 +19,7 @@ from opendevin.events.stream import EventStream
class ShortTermHistory(list[Event]):
"""
A list of events that represents the short-term memory of the agent.
"""A list of events that represents the short-term memory of the agent.
This class provides methods to retrieve and filter the events in the history of the running agent from the event stream.
"""
@ -46,15 +45,11 @@ class ShortTermHistory(list[Event]):
self._event_stream = event_stream
def get_events_as_list(self) -> list[Event]:
"""
Return the history as a list of Event objects.
"""
"""Return the history as a list of Event objects."""
return list(self.get_events())
def get_events(self, reverse: bool = False) -> Iterable[Event]:
"""
Return the events as a stream of Event objects.
"""
"""Return the events as a stream of Event objects."""
# TODO handle AgentRejectAction, if it's not part of a chunk ending with an AgentDelegateObservation
# or even if it is, because currently we don't add it to the summary
@ -86,9 +81,7 @@ class ShortTermHistory(list[Event]):
yield event
def get_last_action(self, end_id: int = -1) -> Action | None:
"""
Return the last action from the event stream, filtered to exclude unwanted events.
"""
"""Return the last action from the event stream, filtered to exclude unwanted events."""
# from end_id in reverse, find the first action
end_id = self._event_stream.get_latest_event_id() if end_id == -1 else end_id
@ -106,9 +99,7 @@ class ShortTermHistory(list[Event]):
return last_action
def get_last_observation(self, end_id: int = -1) -> Observation | None:
"""
Return the last observation from the event stream, filtered to exclude unwanted events.
"""
"""Return the last observation from the event stream, filtered to exclude unwanted events."""
# from end_id in reverse, find the first observation
end_id = self._event_stream.get_latest_event_id() if end_id == -1 else end_id
@ -126,10 +117,7 @@ class ShortTermHistory(list[Event]):
return last_observation
def get_last_user_message(self) -> str:
"""
Return the content of the last user message from the event stream.
"""
"""Return the content of the last user message from the event stream."""
last_user_message = next(
(
event.content
@ -142,10 +130,7 @@ class ShortTermHistory(list[Event]):
return last_user_message if last_user_message is not None else ''
def get_last_agent_message(self) -> str:
"""
Return the content of the last agent message from the event stream.
"""
"""Return the content of the last agent message from the event stream."""
last_agent_message = next(
(
event.content
@ -159,9 +144,7 @@ class ShortTermHistory(list[Event]):
return last_agent_message if last_agent_message is not None else ''
def get_last_events(self, n: int) -> list[Event]:
"""
Return the last n events from the event stream.
"""
"""Return the last n events from the event stream."""
# dummy agent is using this
# it should work, but it's not great to store temporary lists now just for a test
end_id = self._event_stream.get_latest_event_id()
@ -224,9 +207,7 @@ class ShortTermHistory(list[Event]):
return history_pairs
def get_pairs(self) -> list[tuple[Action, Observation]]:
"""
Return the history as a list of tuples (action, observation).
"""
"""Return the history as a list of tuples (action, observation)."""
tuples: list[tuple[Action, Observation]] = []
action_map: dict[int, Action] = {}
observation_map: dict[int, Observation] = {}

View File

@ -108,14 +108,10 @@ class EmbeddingsLoader:
class LongTermMemory:
"""
Handles storing information for the agent to access later, using chromadb.
"""
"""Handles storing information for the agent to access later, using chromadb."""
def __init__(self, agent_config_name='agent'):
"""
Initialize the chromadb and set up ChromaVectorStore for later use.
"""
"""Initialize the chromadb and set up ChromaVectorStore for later use."""
db = chromadb.Client(chromadb.Settings(anonymized_telemetry=False))
self.collection = db.get_or_create_collection(name='memories')
vector_store = ChromaVectorStore(chroma_collection=self.collection)
@ -131,8 +127,7 @@ class LongTermMemory:
self._add_threads = []
def add_event(self, event: dict):
"""
Adds a new event to the long term memory with a unique id.
"""Adds a new event to the long term memory with a unique id.
Parameters:
- event (dict): The new event to be added to memory
@ -165,8 +160,7 @@ class LongTermMemory:
self.index.insert(doc)
def search(self, query: str, k: int = 10):
"""
Searches through the current memory using VectorIndexRetriever
"""Searches through the current memory using VectorIndexRetriever
Parameters:
- query (str): A query to match search results to

View File

@ -177,7 +177,6 @@ class BrowserEnv:
image: np.ndarray | Image.Image, add_data_prefix: bool = False
):
"""Convert a numpy array to a base64 encoded png image url."""
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
if image.mode in ('RGBA', 'LA'):
@ -197,7 +196,6 @@ class BrowserEnv:
image: np.ndarray | Image.Image, add_data_prefix: bool = False
):
"""Convert a numpy array to a base64 encoded jpeg image url."""
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
if image.mode in ('RGBA', 'LA'):

View File

@ -1,5 +1,4 @@
"""
agentskills.py
"""agentskills.py
This module provides various file manipulation skills for the OpenDevin agent.
@ -119,14 +118,12 @@ def _clamp(value, min_value, max_value):
def _lint_file(file_path: str) -> tuple[Optional[str], Optional[int]]:
"""
Lint the file at the given path and return a tuple with a boolean indicating if there are errors,
"""Lint the file at the given path and return a tuple with a boolean indicating if there are errors,
and the line number of the first error, if any.
Returns:
tuple[str, Optional[int]]: (lint_error, first_error_line_number)
"""
if file_path.endswith('.py'):
# Define the flake8 command with selected error codes
def _command_fn(executable):
@ -237,8 +234,7 @@ def _cur_file_header(current_file, total_lines) -> str:
def open_file(
path: str, line_number: int | None = 1, context_lines: int | None = WINDOW
) -> None:
"""
Opens the file at the given path in the editor. If line_number is provided, the window will be moved to include that line.
"""Opens the file at the given path in the editor. If line_number is provided, the window will be moved to include that line.
It only shows the first 100 lines by default! Max `context_lines` supported is 2000, use `scroll up/down`
to view the file if you want to see more.
@ -273,8 +269,7 @@ def open_file(
@update_pwd_decorator
def goto_line(line_number: int) -> None:
"""
Moves the window to show the specified line number.
"""Moves the window to show the specified line number.
Args:
line_number: int: The line number to move to.
@ -365,7 +360,6 @@ def _append_impl(lines, content):
content: str: The new content of the file.
n_added_lines: int: The number of lines added to the file.
"""
content_lines = content.splitlines(keepends=True)
n_added_lines = len(content_lines)
if lines and not (len(lines) == 1 and lines[0].strip() == ''):
@ -774,6 +768,7 @@ def insert_content_at_line(file_name: str, line_number: int, content: str) -> No
def append_file(file_name: str, content: str) -> None:
"""Append content to the given file.
It appends text `content` to the end of the specified file.
Args:
file_name: str: The name of the file to edit.
line_number: int: The line number (starting from 1) to insert the content after.
@ -910,8 +905,7 @@ def parse_pdf(file_path: str) -> None:
@update_pwd_decorator
def parse_docx(file_path: str) -> None:
"""
Parses the content of a DOCX file and prints it.
"""Parses the content of a DOCX file and prints it.
Args:
file_path: str: The path to the file to open.
@ -926,8 +920,7 @@ def parse_docx(file_path: str) -> None:
@update_pwd_decorator
def parse_latex(file_path: str) -> None:
"""
Parses the content of a LaTex file and prints it.
"""Parses the content of a LaTex file and prints it.
Args:
file_path: str: The path to the file to open.
@ -980,8 +973,7 @@ def _prepare_image_messages(task: str, base64_image: str):
@update_pwd_decorator
def parse_audio(file_path: str, model: str = 'whisper-1') -> None:
"""
Parses the content of an audio file and prints it.
"""Parses the content of an audio file and prints it.
Args:
file_path: str: The path to the audio file to transcribe.
@ -1002,8 +994,7 @@ def parse_audio(file_path: str, model: str = 'whisper-1') -> None:
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.
"""Parses the content of an image file and prints the description.
Args:
file_path: str: The path to the file to open.
@ -1031,8 +1022,7 @@ def parse_video(
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.
"""Parses the content of an image file and prints the description.
Args:
file_path: str: The path to the video file to open.
@ -1076,8 +1066,7 @@ def parse_video(
@update_pwd_decorator
def parse_pptx(file_path: str) -> None:
"""
Parses the content of a pptx file and prints it.
"""Parses the content of a pptx file and prints it.
Args:
file_path: str: The path to the file to open.

View File

@ -16,8 +16,7 @@ logging.basicConfig(level=logging.INFO)
def strip_ansi(o: str) -> str:
"""
Removes ANSI escape sequences from `o`, as defined by ECMA-048 in
"""Removes ANSI escape sequences from `o`, as defined by ECMA-048 in
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
# https://github.com/ewen-lbh/python-strip-ansi/blob/master/strip_ansi/__init__.py
@ -43,7 +42,6 @@ def strip_ansi(o: str) -> str:
>>> strip_ansi('\\x1b[1m\\x1b[46m\\x1b[31mLorem dolor sit ipsum\\x1b[0m')
'Lorem dolor sit ipsum'
"""
# pattern = re.compile(r'/(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]/')
pattern = re.compile(r'\x1B\[\d+(;\d+){0,2}m')
stripped = pattern.sub('', o)

View File

@ -35,7 +35,6 @@ class PluginMixin:
def init_plugins(self: SandboxProtocol, requirements: list[PluginRequirement]):
"""Load a plugin into the sandbox."""
if hasattr(self, 'plugin_initialized') and self.plugin_initialized:
return

View File

@ -28,8 +28,7 @@ from opendevin.storage import FileStore
class Runtime:
"""
The runtime is how the agent interacts with the external environment.
"""The runtime is how the agent interacts with the external environment.
This includes a bash sandbox, a browser, and filesystem interactions.
sid is the session id, which is used to identify the current user session.
@ -45,8 +44,7 @@ class Runtime:
atexit.register(self.close_sync)
async def ainit(self) -> None:
"""
Initialize the runtime (asynchronously).
"""Initialize the runtime (asynchronously).
This method should be called after the runtime's constructor.
"""
pass
@ -93,8 +91,7 @@ class Runtime:
self.event_stream.add_event(observation, event.source) # type: ignore[arg-type]
async def run_action(self, action: Action) -> Observation:
"""
Run an action and return the resulting observation.
"""Run an action and return the resulting observation.
If the action is not runnable in any runtime, a NullObservation is returned.
If the action is not supported by the current runtime, an ErrorObservation is returned.
"""

View File

@ -77,9 +77,7 @@ async def read_file(path, workdir, start=0, end=-1) -> Observation:
def insert_lines(
to_insert: list[str], original: list[str], start: int = 0, end: int = -1
):
"""
Insert the new content to the original content based on start and end
"""
"""Insert the new content to the original content based on start and end"""
new_lines = [''] if start == 0 else original[:start]
new_lines += [i + '\n' for i in to_insert]
new_lines += [''] if end == -1 else original[end:]

View File

@ -1,5 +1,4 @@
"""
This module contains functions for building and managing the agnostic sandbox image.
"""This module contains functions for building and managing the agnostic sandbox image.
This WILL BE DEPRECATED when EventStreamRuntime is fully implemented and adopted.
"""
@ -12,8 +11,7 @@ from opendevin.core.logger import opendevin_logger as logger
def generate_dockerfile(base_image: str) -> str:
"""
Generate the Dockerfile content for the agnostic sandbox image based on user-provided base image.
"""Generate the Dockerfile content for the agnostic sandbox image based on user-provided base image.
NOTE: This is only tested on debian yet.
"""

View File

@ -13,7 +13,6 @@ from opendevin.core.logger import opendevin_logger as logger
def _create_project_source_dist():
"""Create a source distribution of the project. Return the path to the tarball."""
# Copy the project directory to the container
# get the location of "opendevin" package
project_root = os.path.dirname(os.path.dirname(os.path.abspath(opendevin.__file__)))
@ -54,8 +53,7 @@ def _put_source_code_to_dir(temp_dir: str) -> str:
def _generate_dockerfile(
base_image: str, source_code_dirname: str, skip_init: bool = False
) -> str:
"""
Generate the Dockerfile content for the eventstream runtime image based on user-provided base image.
"""Generate the Dockerfile content for the eventstream runtime image based on user-provided base image.
NOTE: This is only tested on debian yet.
"""

View File

@ -6,8 +6,7 @@ from opendevin.core.logger import opendevin_logger as logger
def get_sid_from_token(token: str) -> str:
"""
Retrieves the session id from a JWT token.
"""Retrieves the session id from a JWT token.
Parameters:
token (str): The JWT token from which the session id is to be extracted.

View File

@ -55,8 +55,7 @@ security_scheme = HTTPBearer()
def load_file_upload_config() -> tuple[int, bool, list[str]]:
"""
Load file upload configuration from the config object.
"""Load file upload configuration from the config object.
This function retrieves the file upload settings from the global config object.
It handles the following settings:
@ -115,8 +114,7 @@ MAX_FILE_SIZE_MB, RESTRICT_FILE_TYPES, ALLOWED_EXTENSIONS = load_file_upload_con
def is_extension_allowed(filename):
"""
Check if the file extension is allowed based on the current configuration.
"""Check if the file extension is allowed based on the current configuration.
This function supports wildcards and files without extensions.
The check is case-insensitive for extensions.
@ -140,8 +138,7 @@ def is_extension_allowed(filename):
@app.middleware('http')
async def attach_session(request: Request, call_next):
"""
Middleware to attach session information to the request.
"""Middleware to attach session information to the request.
This middleware checks for the Authorization header, validates the token,
and attaches the corresponding session to the request state.
@ -189,13 +186,13 @@ async def attach_session(request: Request, call_next):
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
"""
WebSocket endpoint for receiving events from the client (i.e., the browser).
"""WebSocket endpoint for receiving events from the client (i.e., the browser).
Once connected, the client can send various actions:
- Initialize the agent:
session management, and event streaming.
```json
{"action": "initialize", "args": {"LLM_MODEL": "ollama/llama3", "AGENT": "CodeActAgent", "LANGUAGE": "en", "LLM_API_KEY": "ollama"}}
Args:
```
websocket (WebSocket): The WebSocket connection object.
@ -284,8 +281,7 @@ async def websocket_endpoint(websocket: WebSocket):
@app.get('/api/options/models')
async def get_litellm_models():
"""
Get all models supported by LiteLLM.
"""Get all models supported by LiteLLM.
This function combines models from litellm and Bedrock, removing any
error-prone Bedrock models.
@ -326,8 +322,7 @@ async def get_litellm_models():
@app.get('/api/options/agents')
async def get_agents():
"""
Get all agents supported by LiteLLM.
"""Get all agents supported by LiteLLM.
To get the agents:
```sh
@ -343,8 +338,7 @@ async def get_agents():
@app.get('/api/list-files')
def list_files(request: Request, path: str = '/'):
"""
List files in the specified path.
"""List files in the specified path.
This function retrieves a list of files from the agent's runtime file store,
excluding certain system and hidden files/directories.
@ -453,8 +447,7 @@ def list_files(request: Request, path: str = '/'):
@app.get('/api/select-file')
def select_file(file: str, request: Request):
"""
Retrieve the content of a specified file.
"""Retrieve the content of a specified file.
To select a file:
```sh
@ -484,9 +477,7 @@ def select_file(file: str, request: Request):
def sanitize_filename(filename):
"""
Sanitize the filename to prevent directory traversal
"""
"""Sanitize the filename to prevent directory traversal"""
# Remove any directory components
filename = os.path.basename(filename)
# Remove any non-alphanumeric characters except for .-_
@ -501,8 +492,7 @@ def sanitize_filename(filename):
@app.post('/api/upload-files')
async def upload_file(request: Request, files: list[UploadFile]):
"""
Upload a list of files to the workspace.
"""Upload a list of files to the workspace.
To upload a files:
```sh
@ -580,8 +570,7 @@ async def upload_file(request: Request, files: list[UploadFile]):
@app.post('/api/submit-feedback')
async def submit_feedback(request: Request, feedback: FeedbackDataModel):
"""
Submit user feedback.
"""Submit user feedback.
This function stores the provided feedback data.
@ -614,8 +603,7 @@ async def submit_feedback(request: Request, feedback: FeedbackDataModel):
@app.get('/api/root_task')
def get_root_task(request: Request):
"""
Retrieve the root task of the current agent session.
"""Retrieve the root task of the current agent session.
To get the root_task:
```sh
@ -644,8 +632,7 @@ def get_root_task(request: Request):
@app.get('/api/defaults')
async def appconfig_defaults():
"""
Retrieve the default configuration settings.
"""Retrieve the default configuration settings.
To get the default configurations:
```sh
@ -660,8 +647,7 @@ async def appconfig_defaults():
@app.post('/api/save-file')
async def save_file(request: Request):
"""
Save a file to the agent's runtime file store.
"""Save a file to the agent's runtime file store.
This endpoint allows saving a file when the agent is in a paused, finished,
or awaiting user input state. It checks the agent's state before proceeding

View File

@ -64,14 +64,6 @@ reportlab = "*"
concurrency = ["gevent"]
[tool.poetry.group.evaluation.dependencies]
streamlit = "*"
whatthepatch = "*"
retry = "*"
evaluate = "*"
swebench = { git = "https://github.com/OpenDevin/SWE-bench.git" }
[tool.poetry.group.runtime.dependencies]
jupyterlab = "*"
notebook = "*"
@ -96,3 +88,19 @@ ignore = [ "E501" ]
[tool.black]
# prevent black (if installed) from changing single quotes to double quotes
skip-string-normalization = true
[tool.ruff.lint]
select = ["D"]
# ignore warnings for missing docstrings
ignore = ["D1"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.poetry.group.evaluation.dependencies]
streamlit = "*"
whatthepatch = "*"
retry = "*"
evaluate = "*"
swebench = { git = "https://github.com/OpenDevin/SWE-bench.git" }

View File

@ -42,8 +42,7 @@ def get_log_id(prompt_log_name):
def apply_prompt_and_get_mock_response(test_name: str, messages: str, id: int) -> str:
"""
Apply the mock prompt, and find mock response based on id.
"""Apply the mock prompt, and find mock response based on id.
If there is no matching response file, return None.
Note: this function blindly replaces existing prompt file with the given
@ -67,8 +66,7 @@ def apply_prompt_and_get_mock_response(test_name: str, messages: str, id: int) -
def get_mock_response(test_name: str, messages: str, id: int) -> str:
"""
Find mock response based on prompt. Prompts are stored under nested
"""Find mock response based on prompt. Prompts are stored under nested
folders under mock folder. If prompt_{id}.log matches,
then the mock response we're looking for is at response_{id}.log.

View File

@ -41,9 +41,7 @@ def test_all_agents_are_loaded():
def test_coder_agent_with_summary(event_stream: EventStream):
"""
Coder agent should render code summary as part of prompt
"""
"""Coder agent should render code summary as part of prompt"""
mock_llm = MagicMock()
content = json.dumps({'action': 'finish', 'args': {}})
mock_llm.completion.return_value = {'choices': [{'message': {'content': content}}]}
@ -69,8 +67,7 @@ def test_coder_agent_with_summary(event_stream: EventStream):
def test_coder_agent_without_summary(event_stream: EventStream):
"""
When there's no codebase_summary available, there shouldn't be any prompt
"""When there's no codebase_summary available, there shouldn't be any prompt
about 'code summary'
"""
mock_llm = MagicMock()