diff --git a/backend/.gitignore b/backend/.gitignore index b4d02cc..d21cba4 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -7,6 +7,7 @@ wheels/ *.egg-info .coverage .coverage.* +.ruff_cache agent_history.gif static/browser_history/*.gif diff --git a/backend/.vscode/extensions.json b/backend/.vscode/extensions.json new file mode 100644 index 0000000..37b9f37 --- /dev/null +++ b/backend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["charliermarsh.ruff"] +} diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json new file mode 100644 index 0000000..077c411 --- /dev/null +++ b/backend/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "[python]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff" + } +} diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 97807d1..8df486c 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -15,3 +15,8 @@ dependencies = [ "readabilipy>=0.3.0", "tavily-python>=0.7.17", ] + +[dependency-groups] +dev = [ + "ruff>=0.14.11", +] diff --git a/backend/ruff.toml b/backend/ruff.toml new file mode 100644 index 0000000..6dbc56c --- /dev/null +++ b/backend/ruff.toml @@ -0,0 +1,10 @@ +line-length = 240 +target-version = "py312" + +[lint] +select = ["E", "F", "I", "UP"] +ignore = [] + +[format] +quote-style = "double" +indent-style = "space" diff --git a/backend/src/agents/lead_agent/agent.py b/backend/src/agents/lead_agent/agent.py index 45a8877..7ea415b 100644 --- a/backend/src/agents/lead_agent/agent.py +++ b/backend/src/agents/lead_agent/agent.py @@ -1,4 +1,5 @@ from langchain.agents import create_agent + from src.agents.lead_agent.prompt import apply_prompt_template from src.models import create_chat_model from src.tools import get_available_tools diff --git a/backend/src/agents/lead_agent/prompt.py b/backend/src/agents/lead_agent/prompt.py index 81967dc..93d6e59 100644 --- a/backend/src/agents/lead_agent/prompt.py +++ b/backend/src/agents/lead_agent/prompt.py @@ -71,7 +71,4 @@ All temporary work happens in `{MOUNT_POINT}/user-data/workspace`. Final deliver def apply_prompt_template() -> str: - return ( - SYSTEM_PROMPT - + f"\n{datetime.now().strftime("%Y-%m-%d, %A")}" - ) + return SYSTEM_PROMPT + f"\n{datetime.now().strftime('%Y-%m-%d, %A')}" diff --git a/backend/src/config/app_config.py b/backend/src/config/app_config.py index 2168652..a23d9fa 100644 --- a/backend/src/config/app_config.py +++ b/backend/src/config/app_config.py @@ -13,14 +13,10 @@ from src.config.tool_config import ToolConfig, ToolGroupConfig class AppConfig(BaseModel): """Config for the DeerFlow application""" - models: list[ModelConfig] = Field( - default_factory=list, description="Available models" - ) + models: list[ModelConfig] = Field(default_factory=list, description="Available models") sandbox: SandboxConfig = Field(description="Sandbox configuration") tools: list[ToolConfig] = Field(default_factory=list, description="Available tools") - tool_groups: list[ToolGroupConfig] = Field( - default_factory=list, description="Available tool groups" - ) + tool_groups: list[ToolGroupConfig] = Field(default_factory=list, description="Available tool groups") model_config = ConfigDict(extra="allow", frozen=False) @classmethod @@ -35,16 +31,12 @@ class AppConfig(BaseModel): if config_path: path = Path(config_path) if not Path.exists(path): - raise FileNotFoundError( - f"Config file specified by param `config_path` not found at {path}" - ) + raise FileNotFoundError(f"Config file specified by param `config_path` not found at {path}") return path elif os.getenv("DEER_FLOW_CONFIG_PATH"): path = Path(os.getenv("DEER_FLOW_CONFIG_PATH")) if not Path.exists(path): - raise FileNotFoundError( - f"Config file specified by environment variable `DEER_FLOW_CONFIG_PATH` not found at {path}" - ) + raise FileNotFoundError(f"Config file specified by environment variable `DEER_FLOW_CONFIG_PATH` not found at {path}") return path else: # Check if the config.yaml is in the parent directory of CWD @@ -66,7 +58,7 @@ class AppConfig(BaseModel): AppConfig: The loaded config. """ resolved_path = cls.resolve_config_path(config_path) - with open(resolved_path, "r") as f: + with open(resolved_path) as f: config_data = yaml.safe_load(f) cls.resolve_env_variables(config_data) result = cls.model_validate(config_data) diff --git a/backend/src/reflection/resolvers.py b/backend/src/reflection/resolvers.py index 3b73a10..9f17a7d 100644 --- a/backend/src/reflection/resolvers.py +++ b/backend/src/reflection/resolvers.py @@ -1,12 +1,12 @@ from importlib import import_module -from typing import Type, TypeVar +from typing import TypeVar T = TypeVar("T") def resolve_variable[T]( variable_path: str, - expected_type: Type[T] | tuple[Type, ...] | None = None, + expected_type: type[T] | tuple[type, ...] | None = None, ) -> T: """Resolve a variable from a path. @@ -25,9 +25,7 @@ def resolve_variable[T]( try: module_path, variable_name = variable_path.rsplit(":", 1) except ValueError as err: - raise ImportError( - f"{variable_path} doesn't look like a variable path. Example: parent_package_name.sub_package_name.module_name:variable_name" - ) from err + raise ImportError(f"{variable_path} doesn't look like a variable path. Example: parent_package_name.sub_package_name.module_name:variable_name") from err try: module = import_module(module_path) @@ -37,26 +35,18 @@ def resolve_variable[T]( try: variable = getattr(module, variable_name) except AttributeError as err: - raise ImportError( - f"Module {module_path} does not define a {variable_name} attribute/class" - ) from err + raise ImportError(f"Module {module_path} does not define a {variable_name} attribute/class") from err # Type validation if expected_type is not None: if not isinstance(variable, expected_type): - type_name = ( - expected_type.__name__ - if isinstance(expected_type, type) - else " or ".join(t.__name__ for t in expected_type) - ) - raise ValueError( - f"{variable_path} is not an instance of {type_name}, got {type(variable).__name__}" - ) + type_name = expected_type.__name__ if isinstance(expected_type, type) else " or ".join(t.__name__ for t in expected_type) + raise ValueError(f"{variable_path} is not an instance of {type_name}, got {type(variable).__name__}") return variable -def resolve_class(class_path: str, base_class: Type[T] | None = None) -> Type[T]: +def resolve_class[T](class_path: str, base_class: type[T] | None = None) -> type[T]: """Resolve a class from a module path and class name. Args: diff --git a/backend/src/sandbox/local/local_sandbox.py b/backend/src/sandbox/local/local_sandbox.py index f09c516..dc3fd0b 100644 --- a/backend/src/sandbox/local/local_sandbox.py +++ b/backend/src/sandbox/local/local_sandbox.py @@ -29,7 +29,7 @@ class LocalSandbox(Sandbox): return list_dir(path, max_depth) def read_file(self, path: str) -> str: - with open(path, "r") as f: + with open(path) as f: return f.read() def write_file(self, path: str, content: str, append: bool = False) -> None: diff --git a/backend/uv.lock b/backend/uv.lock index dc083cb..46a950d 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -128,6 +128,11 @@ dependencies = [ { name = "tavily-python" }, ] +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] + [package.metadata] requires-dist = [ { name = "langchain", specifier = ">=1.2.3" }, @@ -141,6 +146,9 @@ requires-dist = [ { name = "tavily-python", specifier = ">=0.7.17" }, ] +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.14.11" }] + [[package]] name = "distro" version = "1.9.0" @@ -897,6 +905,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, ] +[[package]] +name = "ruff" +version = "0.14.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" }, + { url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" }, + { url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" }, + { url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" }, +] + [[package]] name = "six" version = "1.17.0" diff --git a/deer-flow.code-workspace b/deer-flow.code-workspace new file mode 100644 index 0000000..c4ed7ce --- /dev/null +++ b/deer-flow.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "backend" + } + ] +}