Add skill lanes for MCP tool serialization#2308
Conversation
Signed-off-by: Ritwij Aryan Parmar <ritwij.aryan.parmar@gmail.com>
Greptile SummaryThis PR introduces skill execution lanes — an opt-in
Confidence Score: 5/5Safe to merge — the lane serialization logic is correct, the injectable lock dict cleanly isolates tests from the global state, and the decorator changes are backward-compatible. All five changed files have straightforward, well-tested changes. The lock acquisition and release path through No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant C as MCP Client
participant H as handle_request
participant TC as _handle_tools_call
participant LL as lane_locks dict
participant TP as Thread Pool
C->>H: "tools/call {name:"drive"}"
H->>TC: req_id, params, skills, rpc_calls, lane_locks
TC->>TC: lookup rpc_call in rpc_calls
TC->>TC: "lane = next(s.lane for s in skills if s.func_name=="drive")"
alt lane is None
TC->>TP: run_in_executor(rpc_call)
TP-->>TC: result
else "lane = "motion""
TC->>LL: setdefault("motion", asyncio.Lock())
LL-->>TC: lock
TC->>TC: async with lock (acquire)
TC->>TP: run_in_executor(rpc_call)
Note over TP: second same-lane call waits on lock
TP-->>TC: result
TC->>TC: release lock
end
TC-->>H: JSON-RPC result
H-->>C: response
Reviews (2): Last reviewed commit: "Fix MCP lane lock test isolation" | Re-trigger Greptile |
| logger.warning("MCP tool not found", tool=name) | ||
| return _jsonrpc_result_text(req_id, f"Tool not found: {name}") | ||
|
|
||
| lane = next((s.lane for s in skills if s.func_name == name), None) |
There was a problem hiding this comment.
The O(n) linear scan for the lane duplicates work already done when the
rpc_calls dict was built. A {s.func_name: s.lane for s in skills}.get(name) dict comprehension or a pre-built lane map alongside rpc_calls would make this O(1).
| lane = next((s.lane for s in skills if s.func_name == name), None) | |
| lane = {s.func_name: s.lane for s in skills}.get(name) |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Addressed the test-isolation concern from the Greptile review. What changed:
Validation run locally:
I could not run the full pytest target locally because the repo's |
Problem
Addresses #2204. DimOS skills currently do not expose whether a tool can run concurrently with another tool. That is risky for robot/world-mutating skills such as motion or manipulation: two MCP calls can be dispatched at the same time even when they should share an execution lane.
Closes DIM-XXX
Solution
@skill(lane="motion")form while preserving the existing bare@skilldecorator.SkillInfodiscovery so module skill metadata knows which lane a skill belongs to.tools/listresponses under tool_meta.tools/callrequests that share the same non-empty lane, while leaving unlaned or differently-laned skills concurrent.How to Test
/tmp/dimos/.venv/bin/python -m ruff check dimos/agents/annotation.py dimos/core/module.py dimos/agents/mcp/mcp_server.py dimos/agents/test_skill_result.py dimos/agents/mcp/test_mcp_server.py/tmp/dimos/.venv/bin/python -m pytest dimos/agents/test_skill_result.py dimos/agents/mcp/test_mcp_server.pyNote: I used Python 3.12 through
uv. A fulluv runsync initially hit localpyaudio/PortAudio headers on this Mac, so I provisioned a minimal env for the focused files above.Contributor License Agreement