Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/agent/src/argus_agent/collectors/process_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,12 @@ async def _check_remote(self) -> dict[str, Any]:

def get_process_list(
sort_by: str = "cpu_percent",
limit: int = 50,
) -> list[dict[str, Any]]:
"""Get current process list snapshot (synchronous, for tools).

Uses `ps` for a complete listing (no AccessDenied gaps),
falls back to psutil if `ps` is unavailable.
Returns ALL processes — no limit.
"""
processes = _get_process_list_ps()
if processes is None:
Expand All @@ -259,7 +259,7 @@ def get_process_list(
else:
processes.sort(key=lambda p: p.get("cpu_percent", 0), reverse=True)

return processes[:limit]
return processes


def _get_process_list_ps() -> list[dict[str, Any]] | None:
Expand Down
37 changes: 4 additions & 33 deletions packages/agent/src/argus_agent/tools/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def name(self) -> str:
@property
def description(self) -> str:
return (
"List running processes with CPU and memory usage. "
"Sort by cpu_percent, memory_percent, or pid."
"List ALL running processes with CPU and memory usage. "
"Returns every process — no filtering, no limits."
)

@property
Expand All @@ -39,42 +39,13 @@ def parameters_schema(self) -> dict[str, Any]:
"description": "Sort: cpu_percent, memory_percent, pid",
"default": "cpu_percent",
},
"limit": {
"type": "integer",
"description": "Max processes to return (default: 25)",
"default": 25,
},
"filter_name": {
"type": "string",
"description": "Filter by process name (substring match)",
},
"filter_user": {
"type": "string",
"description": "Filter by username",
},
},
}

async def execute(self, **kwargs: Any) -> dict[str, Any]:
sort_by = kwargs.get("sort_by", "cpu_percent")
limit = min(kwargs.get("limit", 25), 100)
filter_name = kwargs.get("filter_name", "")
filter_user = kwargs.get("filter_user", "")

processes = get_process_list(sort_by=sort_by, limit=200)

# Apply filters
if filter_name:
fn = filter_name.lower()
processes = [
p
for p in processes
if fn in p.get("name", "").lower() or fn in p.get("cmdline", "").lower()
]
if filter_user:
processes = [p for p in processes if p.get("username") == filter_user]

processes = processes[:limit]

processes = get_process_list(sort_by=sort_by)

return {
"total_processes": len(processes),
Expand Down
6 changes: 3 additions & 3 deletions packages/agent/tests/test_collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,20 @@ async def test_format_snapshot_with_data(self):

class TestProcessMonitor:
def test_get_process_list(self):
processes = get_process_list(limit=10)
processes = get_process_list()
assert len(processes) > 0
assert "pid" in processes[0]
assert "name" in processes[0]
assert "cpu_percent" in processes[0]
assert "memory_percent" in processes[0]

def test_sort_by_memory(self):
processes = get_process_list(sort_by="memory_percent", limit=5)
processes = get_process_list(sort_by="memory_percent")
if len(processes) >= 2:
assert processes[0]["memory_percent"] >= processes[1]["memory_percent"]

def test_sort_by_pid(self):
processes = get_process_list(sort_by="pid", limit=5)
processes = get_process_list(sort_by="pid")
if len(processes) >= 2:
assert processes[0]["pid"] <= processes[1]["pid"]

Expand Down
16 changes: 5 additions & 11 deletions packages/agent/tests/test_new_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,13 @@ async def test_list_processes(self):
assert result["display_type"] == "process_table"

@pytest.mark.asyncio
async def test_limit(self):
async def test_returns_all_processes(self):
tool = ProcessListTool()
result = await tool.execute(limit=3)

assert len(result["processes"]) <= 3

@pytest.mark.asyncio
async def test_filter_name(self):
tool = ProcessListTool()
result = await tool.execute(filter_name="python", limit=100)
result = await tool.execute()

for p in result["processes"]:
assert "python" in p["name"].lower() or "python" in p.get("cmdline", "").lower()
# Should return all processes, no artificial limit
assert result["total_processes"] == len(result["processes"])
assert result["total_processes"] > 10

def test_tool_properties(self):
tool = ProcessListTool()
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "argus-ai-sdk"
version = "0.2.1"
version = "0.2.2"
description = "Argus Python SDK - Instrumentation for AI-native observability"
readme = "README.md"
requires-python = ">=3.9"
Expand Down
7 changes: 3 additions & 4 deletions packages/sdk-python/src/argus/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,21 @@ def _tool_system_metrics(**_kwargs: Any) -> dict[str, Any]:


def _tool_process_list(**kwargs: Any) -> dict[str, Any]:
"""List running processes.
"""List ALL running processes.

Uses `ps` for a complete listing (no AccessDenied gaps),
falls back to psutil if `ps` is unavailable.
Returns every process — no filtering, no limits.
"""
limit = int(kwargs.get("limit", 20))
sort_by = kwargs.get("sort_by", "cpu")
key = "cpu_percent" if sort_by == "cpu" else "memory_percent"

# Try ps first — shows all processes regardless of permissions
procs = _ps_process_list()
if procs is None:
procs = _psutil_process_list()

procs.sort(key=lambda x: x.get(key) or 0, reverse=True)
return {"processes": procs[:limit], "total": len(procs)}
return {"processes": procs, "total": len(procs)}


def _ps_process_list() -> list[dict[str, Any]] | None:
Expand Down
Loading