Skip to content

Commit 38566ea

Browse files
authored
Apply stricter linting (#89)
Apply stricter linting across the codebase
1 parent 5b97145 commit 38566ea

File tree

10 files changed

+69
-45
lines changed

10 files changed

+69
-45
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ MANIFEST
3939
dmypy.json
4040

4141
.langgraph_api
42+
.idea

examples/customer_support/src/agent/customer_support.ipynb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
" hotel_id: str,\n",
129129
" config: RunnableConfig,\n",
130130
") -> str:\n",
131-
" \"\"\"Book a hotel\"\"\"\n",
131+
" \"\"\"Book a hotel.\"\"\"\n",
132132
" user_id = config[\"configurable\"].get(\"user_id\")\n",
133133
" hotel = [hotel for hotel in HOTELS if hotel[\"id\"] == hotel_id][0]\n",
134134
" RESERVATIONS[user_id][\"hotel_info\"] = hotel\n",
@@ -239,7 +239,6 @@
239239
"source": [
240240
"def print_stream(stream):\n",
241241
" for ns, update in stream:\n",
242-
" print(f\"Namespace '{ns}'\")\n",
243242
" for node, node_updates in update.items():\n",
244243
" if node_updates is None:\n",
245244
" continue\n",
@@ -252,21 +251,16 @@
252251
" raise ValueError(node_updates)\n",
253252
"\n",
254253
" for node_updates in node_updates_list:\n",
255-
" print(f\"Update from node '{node}'\")\n",
256254
" if isinstance(node_updates, tuple):\n",
257-
" print(node_updates)\n",
258255
" continue\n",
259256
" messages_key = next(\n",
260257
" (k for k in node_updates.keys() if \"messages\" in k), None\n",
261258
" )\n",
262259
" if messages_key is not None:\n",
263260
" node_updates[messages_key][-1].pretty_print()\n",
264261
" else:\n",
265-
" print(node_updates)\n",
266-
"\n",
267-
" print(\"\\n\\n\")\n",
268-
"\n",
269-
" print(\"\\n===\\n\")"
262+
" pass\n",
263+
"\n"
270264
]
271265
},
272266
{

examples/customer_support/src/agent/customer_support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def book_hotel(
7575
hotel_id: str,
7676
config: RunnableConfig,
7777
) -> str:
78-
"""Book a hotel"""
78+
"""Book a hotel."""
7979
user_id = config["configurable"].get("user_id")
8080
hotel = [hotel for hotel in HOTELS if hotel["id"] == hotel_id][0]
8181
RESERVATIONS[user_id]["hotel_info"] = hotel

examples/research/src/agent/utils.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
def print_stream(stream):
88
for ns, update in stream:
9-
print(f"Namespace '{ns}'")
109
for node, node_updates in update.items():
1110
if node_updates is None:
1211
continue
@@ -19,21 +18,17 @@ def print_stream(stream):
1918
raise ValueError(node_updates)
2019

2120
for node_updates in node_updates_list:
22-
print(f"Update from node '{node}'")
2321
if isinstance(node_updates, tuple):
24-
print(node_updates)
2522
continue
2623
messages_key = next(
2724
(k for k in node_updates.keys() if "messages" in k), None
2825
)
2926
if messages_key is not None:
3027
node_updates[messages_key][-1].pretty_print()
3128
else:
32-
print(node_updates)
29+
pass
3330

34-
print("\n\n")
3531

36-
print("\n===\n")
3732

3833

3934
def fetch_doc(url: str) -> str:

langgraph_swarm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from langgraph_swarm.handoff import create_handoff_tool
22
from langgraph_swarm.swarm import SwarmState, add_active_agent_router, create_swarm
33

4-
__all__ = ["create_swarm", "add_active_agent_router", "create_handoff_tool", "SwarmState"]
4+
__all__ = ["SwarmState", "add_active_agent_router", "create_handoff_tool", "create_swarm"]

langgraph_swarm/handoff.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import re
2+
from typing import Annotated
23

34
from langchain_core.messages import ToolMessage
45
from langchain_core.tools import BaseTool, InjectedToolCallId, tool
56
from langgraph.graph.state import CompiledStateGraph
67
from langgraph.prebuilt import InjectedState, ToolNode
78
from langgraph.types import Command
8-
from typing_extensions import Annotated
99

1010
WHITESPACE_RE = re.compile(r"\s+")
1111
METADATA_KEY_HANDOFF_DESTINATION = "__handoff_destination"
@@ -17,7 +17,10 @@ def _normalize_agent_name(agent_name: str) -> str:
1717

1818

1919
def create_handoff_tool(
20-
*, agent_name: str, name: str | None = None, description: str | None = None
20+
*,
21+
agent_name: str,
22+
name: str | None = None,
23+
description: str | None = None,
2124
) -> BaseTool:
2225
"""Create a tool that can handoff control to the requested agent.
2326
@@ -32,6 +35,7 @@ def create_handoff_tool(
3235
If not provided, the tool name will be `transfer_to_<agent_name>`.
3336
description: Optional description for the handoff tool.
3437
If not provided, the tool description will be `Ask agent <agent_name> for help`.
38+
3539
"""
3640
if name is None:
3741
name = f"transfer_to_{_normalize_agent_name(agent_name)}"

langgraph_swarm/swarm.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from typing import Literal, Optional, Union, get_args, get_origin
2+
13
from langgraph.graph import START, MessagesState, StateGraph
24
from langgraph.pregel import Pregel
3-
from typing_extensions import Any, Literal, Optional, Type, TypeVar, Union, get_args, get_origin
5+
from typing_extensions import Any, TypeVar
46

57
from langgraph_swarm.handoff import get_handoff_destinations
68

@@ -11,18 +13,18 @@ class SwarmState(MessagesState):
1113
# NOTE: this state field is optional and is not expected to be provided by the user.
1214
# If a user does provide it, the graph will start from the specified active agent.
1315
# If active agent is typed as a `str`, we turn it into enum of all active agent names.
14-
active_agent: Optional[str]
16+
active_agent: str | None
1517

1618

1719
StateSchema = TypeVar("StateSchema", bound=SwarmState)
18-
StateSchemaType = Type[StateSchema]
20+
StateSchemaType = type[StateSchema]
1921

2022

2123
def _update_state_schema_agent_names(
22-
state_schema: StateSchemaType, agent_names: list[str]
24+
state_schema: StateSchemaType,
25+
agent_names: list[str],
2326
) -> StateSchemaType:
2427
"""Update the state schema to use Literal with agent names for 'active_agent'."""
25-
2628
active_agent_annotation = state_schema.__annotations__["active_agent"]
2729

2830
# Check if the annotation is str or Optional[str]
@@ -120,14 +122,17 @@ def add(a: int, b: int) -> int:
120122
config,
121123
)
122124
```
125+
123126
"""
124127
channels = builder.schemas[builder.state_schema]
125128
if "active_agent" not in channels:
126-
raise ValueError("Missing required key 'active_agent' in in builder's state_schema")
129+
msg = "Missing required key 'active_agent' in in builder's state_schema"
130+
raise ValueError(msg)
127131

128132
if default_active_agent not in route_to:
133+
msg = f"Default active agent '{default_active_agent}' not found in routes {route_to}"
129134
raise ValueError(
130-
f"Default active agent '{default_active_agent}' not found in routes {route_to}"
135+
msg,
131136
)
132137

133138
def route_to_active_agent(state: dict):
@@ -142,7 +147,7 @@ def create_swarm(
142147
*,
143148
default_active_agent: str,
144149
state_schema: StateSchemaType = SwarmState,
145-
config_schema: Type[Any] | None = None,
150+
config_schema: type[Any] | None = None,
146151
) -> StateGraph:
147152
"""Create a multi-agent swarm.
148153
@@ -200,10 +205,12 @@ def add(a: int, b: int) -> int:
200205
config,
201206
)
202207
```
208+
203209
"""
204210
active_agent_annotation = state_schema.__annotations__.get("active_agent")
205211
if active_agent_annotation is None:
206-
raise ValueError("Missing required key 'active_agent' in state_schema")
212+
msg = "Missing required key 'active_agent' in state_schema"
213+
raise ValueError(msg)
207214

208215
agent_names = [agent.name for agent in agents]
209216
state_schema = _update_state_schema_agent_names(state_schema, agent_names)

pyproject.toml

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,17 @@ target-version = "py310"
4242

4343
[tool.ruff.lint]
4444
select = [
45-
"E", # pycodestyle errors
46-
"W", # pycodestyle warnings
47-
"F", # pyflakes
48-
"I", # isort
49-
"B", # flake8-bugbear
45+
"ALL",
5046
]
5147
ignore = [
52-
"E501" # line-length
48+
"E501", # line-length
49+
"D100",
50+
"D104",
51+
"D203",
52+
"D213",
53+
"UP007",
54+
"COM812",
55+
"ANN",
5356
]
5457

5558

@@ -59,3 +62,24 @@ warn_return_any = true
5962
warn_unused_configs = true
6063
disallow_untyped_defs = true
6164
check_untyped_defs = true
65+
66+
67+
[tool.ruff.lint.extend-per-file-ignores]
68+
"tests/**/*.py" = [
69+
# at least this three should be fine in tests:
70+
"S101", # asserts allowed in tests...
71+
"ARG", # Unused function args -> fixtures nevertheless are functionally relevant...
72+
"FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize()
73+
"D104",
74+
# The below are debateable
75+
"PLR2004", # Magic value used in comparison, ...
76+
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
77+
"D100", # Missing docstring in public module
78+
"D101", # Missing docstring in public class
79+
"D102", # Missing docstring in public method
80+
"D103", # Missing docstring in public function
81+
"D104", # Missing docstring in public package
82+
"D105", # Missing docstring in magic method
83+
"D415", # First line should end with a period, question mark, or exclamation point
84+
]
85+

tests/test_import.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
def test_import() -> None:
2-
"""Test that the code can be imported"""
2+
"""Test that the code can be imported."""
33
from langgraph_swarm import ( # noqa: F401
44
add_active_agent_router,
55
create_handoff_tool,

tests/test_swarm.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from typing import Optional
2-
31
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
42
from langchain_core.language_models.chat_models import BaseChatModel
53
from langchain_core.messages import AIMessage, BaseMessage
@@ -21,8 +19,8 @@ def _llm_type(self) -> str:
2119
def _generate(
2220
self,
2321
messages: list[BaseMessage],
24-
stop: Optional[list[str]] = None,
25-
run_manager: Optional[CallbackManagerForLLMRun] = None,
22+
stop: list[str] | None = None,
23+
run_manager: CallbackManagerForLLMRun | None = None,
2624
**kwargs,
2725
) -> ChatResult:
2826
generation = ChatGeneration(message=self.responses[self.idx])
@@ -44,7 +42,7 @@ def test_basic_swarm() -> None:
4442
"name": "transfer_to_bob",
4543
"args": {},
4644
"id": "call_1LlFyjm6iIhDjdn7juWuPYr4",
47-
}
45+
},
4846
],
4947
),
5048
AIMessage(
@@ -59,7 +57,7 @@ def test_basic_swarm() -> None:
5957
"name": "transfer_to_alice",
6058
"args": {},
6159
"id": "call_T6pNmo2jTfZEK3a9avQ14f8Q",
62-
}
60+
},
6361
],
6462
),
6563
AIMessage(
@@ -73,7 +71,7 @@ def test_basic_swarm() -> None:
7371
"b": 7,
7472
},
7573
"id": "call_4kLYO1amR2NfhAxfECkALCr1",
76-
}
74+
},
7775
],
7876
),
7977
AIMessage(
@@ -85,7 +83,7 @@ def test_basic_swarm() -> None:
8583
model = FakeChatModel(responses=recorded_messages)
8684

8785
def add(a: int, b: int) -> int:
88-
"""Add two numbers"""
86+
"""Add two numbers."""
8987
return a + b
9088

9189
alice = create_react_agent(
@@ -99,8 +97,9 @@ def add(a: int, b: int) -> int:
9997
model,
10098
[
10199
create_handoff_tool(
102-
agent_name="Alice", description="Transfer to Alice, she can help with math"
103-
)
100+
agent_name="Alice",
101+
description="Transfer to Alice, she can help with math",
102+
),
104103
],
105104
prompt="You are Bob, you speak like a pirate.",
106105
name="Bob",

0 commit comments

Comments
 (0)