mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-07 07:59:29 +00:00
fix: resolve all CI failures — format, lint, mypy, and review comments
- Format: auto-reformat agent_tui.py, benchmark.py, coworker_tools.py via ruff - Lint: 0 remaining errors after format pass - Mypy: fix _NullPrinter to subclass Printer for type compatibility in executor.py, planning.py, and skill_builder.py; add isinstance(r, Message) guards in spawn_tools.py; annotate return types and fix dict type params and MCPToolResolver logger type in new_agent.py; add missing printer args to get_llm_response calls - cli.py: fix _read_config to use sentinel so falsy values (0, false) are returned correctly instead of being treated as missing keys - create_agent.py: replace regex-based JSONC comment stripper with a token-aware parser that preserves // inside quoted strings (e.g. URLs) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -533,8 +533,13 @@ class AgentTUI(App[None]):
|
||||
yield Static("AGENTS", classes="sidebar-label")
|
||||
yield OptionList(id="agent-list")
|
||||
with TabPane("Memory", id="tab-memory"):
|
||||
yield Static("Click below to open the memory browser.", id="memory-scope-label")
|
||||
yield Button("Open Memory Browser", id="btn-memory", variant="default")
|
||||
yield Static(
|
||||
"Click below to open the memory browser.",
|
||||
id="memory-scope-label",
|
||||
)
|
||||
yield Button(
|
||||
"Open Memory Browser", id="btn-memory", variant="default"
|
||||
)
|
||||
with Vertical(id="chat-area"):
|
||||
yield VerticalScroll(id="chat-scroll")
|
||||
with Horizontal(id="input-row"):
|
||||
@@ -1617,6 +1622,7 @@ class AgentTUI(App[None]):
|
||||
callback=self._on_room_created,
|
||||
)
|
||||
return
|
||||
|
||||
# ── Room creation ──
|
||||
|
||||
def _on_room_created(self, result: dict[str, Any] | None) -> None:
|
||||
|
||||
@@ -111,7 +111,6 @@ def load_benchmark_cases(path: str | Path) -> LoadedCases:
|
||||
return LoadedCases(cases, threshold)
|
||||
|
||||
|
||||
|
||||
def _check_expected(expected: str, actual: str) -> tuple[bool, float]:
|
||||
"""Check if expected output is found in actual (case-insensitive substring match).
|
||||
|
||||
|
||||
@@ -604,10 +604,12 @@ def _read_config(*keys: str) -> Any:
|
||||
"""Read a nested value from config.json (JSONC-safe).
|
||||
|
||||
Example: _read_config("test", "threshold") reads config["test"]["threshold"].
|
||||
Returns None only when the key is missing, not when the value is falsy.
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
_MISSING = object()
|
||||
config_path = Path("config.json")
|
||||
if not config_path.exists():
|
||||
return None
|
||||
@@ -618,8 +620,8 @@ def _read_config(*keys: str) -> Any:
|
||||
for k in keys:
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
data = data.get(k)
|
||||
if data is None:
|
||||
data = data.get(k, _MISSING)
|
||||
if data is _MISSING:
|
||||
return None
|
||||
return data
|
||||
except Exception:
|
||||
|
||||
@@ -812,10 +812,30 @@ def _prompt_agent_name() -> str:
|
||||
)
|
||||
|
||||
|
||||
_JSONC_TOKEN_RE = re.compile(
|
||||
r'"(?:[^"\\]|\\.)*"' # double-quoted string
|
||||
r"|'(?:[^'\\]|\\.)*'" # single-quoted string (not standard JSON, but safe)
|
||||
r"|/\*.*?\*/" # /* block comment */
|
||||
r"|//[^\n]*" # // line comment
|
||||
r"|.", # any other character
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
def _strip_jsonc(text: str) -> str:
|
||||
"""Strip // and /* */ comments from JSONC text, then fix trailing commas."""
|
||||
result = re.sub(r"(?<!:)//.*?$", "", text, flags=re.MULTILINE)
|
||||
result = re.sub(r"/\*.*?\*/", "", result, flags=re.DOTALL)
|
||||
"""Strip // and /* */ comments from JSONC text, then fix trailing commas.
|
||||
|
||||
Only strips comments that appear outside of quoted strings, so double
|
||||
slashes inside string values (e.g. URLs) are preserved correctly.
|
||||
"""
|
||||
|
||||
def _replacer(match: re.Match[str]) -> str:
|
||||
token = match.group(0)
|
||||
if token.startswith(("//", "/*")):
|
||||
return ""
|
||||
return token
|
||||
|
||||
result = _JSONC_TOKEN_RE.sub(_replacer, text)
|
||||
result = re.sub(r",\s*([}\]])", r"\1", result)
|
||||
return result
|
||||
|
||||
|
||||
@@ -244,7 +244,9 @@ class DelegateToCoworkerTool(BaseTool):
|
||||
)
|
||||
raise
|
||||
|
||||
async def _arun(self, message: str, fire_and_forget: bool = False, **kwargs: Any) -> str:
|
||||
async def _arun(
|
||||
self, message: str, fire_and_forget: bool = False, **kwargs: Any
|
||||
) -> str:
|
||||
"""Async delegation — avoids blocking the event loop."""
|
||||
from crewai.new_agent.events import (
|
||||
NewAgentDelegationCompletedEvent,
|
||||
|
||||
@@ -11,6 +11,7 @@ import re
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from crewai_core.printer import Printer
|
||||
from pydantic import BaseModel, Field, PrivateAttr
|
||||
|
||||
from crewai.new_agent.models import (
|
||||
@@ -2419,8 +2420,8 @@ class ConversationalAgentExecutor(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class _NullPrinter:
|
||||
"""Minimal printer that swallows output."""
|
||||
class _NullPrinter(Printer):
|
||||
"""Minimal printer that subclasses Printer but swallows all output."""
|
||||
|
||||
def print(self, *args: Any, **kwargs: Any) -> None:
|
||||
def print(self, *args: Any, **kwargs: Any) -> None: # type: ignore[override]
|
||||
pass
|
||||
|
||||
@@ -36,11 +36,11 @@ def _get_init_chain() -> set[str]:
|
||||
"""Return the thread-local set of agent IDs currently being initialized."""
|
||||
if not hasattr(_init_chain, "agent_ids"):
|
||||
_init_chain.agent_ids = set()
|
||||
return _init_chain.agent_ids
|
||||
return _init_chain.agent_ids # type: ignore[no-any-return]
|
||||
|
||||
|
||||
# ── GAP-63: Process-level AMP definition cache ──────────────────
|
||||
_amp_cache: dict[str, dict] = {}
|
||||
_amp_cache: dict[str, dict[str, Any]] = {}
|
||||
|
||||
|
||||
def clear_amp_cache() -> None:
|
||||
@@ -286,8 +286,10 @@ class NewAgent(BaseModel):
|
||||
if self.mcps:
|
||||
try:
|
||||
from crewai.mcp.tool_resolver import MCPToolResolver
|
||||
from crewai.utilities.logger import Logger as _CrewLogger
|
||||
|
||||
resolver = MCPToolResolver(agent=self, logger=self._logger)
|
||||
_mcp_logger = _CrewLogger(verbose=self.verbose)
|
||||
resolver = MCPToolResolver(agent=self, logger=_mcp_logger)
|
||||
mcp_tools = resolver.resolve(self.mcps)
|
||||
resolved.extend(mcp_tools)
|
||||
except Exception as e:
|
||||
@@ -631,7 +633,7 @@ class NewAgent(BaseModel):
|
||||
if self.on_message:
|
||||
self.on_message(user_msg)
|
||||
|
||||
response = executor.invoke(user_msg)
|
||||
response: Message = executor.invoke(user_msg)
|
||||
|
||||
if self.on_complete:
|
||||
self.on_complete(response)
|
||||
@@ -656,7 +658,7 @@ class NewAgent(BaseModel):
|
||||
if self.on_message:
|
||||
self.on_message(user_msg)
|
||||
|
||||
response = await executor.ainvoke(user_msg)
|
||||
response: Message = await executor.ainvoke(user_msg)
|
||||
|
||||
if self.on_complete:
|
||||
self.on_complete(response)
|
||||
@@ -793,10 +795,13 @@ class NewAgent(BaseModel):
|
||||
messages: list[LLMMessage] = [
|
||||
format_message_for_llm(prompt, role="user")
|
||||
]
|
||||
from crewai.new_agent.executor import _NullPrinter
|
||||
|
||||
reasoning_text = get_llm_response(
|
||||
llm=self._llm_instance,
|
||||
messages=messages,
|
||||
callbacks=[],
|
||||
printer=_NullPrinter(),
|
||||
)
|
||||
if reasoning_text:
|
||||
reasoning_str = str(reasoning_text).strip()
|
||||
@@ -855,7 +860,7 @@ class NewAgent(BaseModel):
|
||||
executor = self._executors.get(conversation_id)
|
||||
if executor is None:
|
||||
return []
|
||||
return executor.conversation_history
|
||||
return list(executor.conversation_history)
|
||||
|
||||
@property
|
||||
def conversation_history(self) -> list[Message]:
|
||||
@@ -863,14 +868,15 @@ class NewAgent(BaseModel):
|
||||
executor = self._executors.get(self._default_conversation_id)
|
||||
if executor is None:
|
||||
return []
|
||||
return executor.conversation_history
|
||||
return list(executor.conversation_history)
|
||||
|
||||
@property
|
||||
def last_prompt_stack(self) -> PromptStack | None:
|
||||
executor = self._executors.get(self._default_conversation_id)
|
||||
if executor is None:
|
||||
return None
|
||||
return executor.prompt_stack
|
||||
result: PromptStack | None = executor.prompt_stack
|
||||
return result
|
||||
|
||||
@property
|
||||
def usage_metrics(self) -> dict[str, int]:
|
||||
@@ -962,6 +968,7 @@ class NewAgent(BaseModel):
|
||||
)
|
||||
|
||||
try:
|
||||
from crewai.new_agent.executor import _NullPrinter
|
||||
from crewai.utilities.agent_utils import (
|
||||
format_message_for_llm,
|
||||
get_llm_response,
|
||||
@@ -973,6 +980,7 @@ class NewAgent(BaseModel):
|
||||
llm=llm,
|
||||
messages=messages,
|
||||
callbacks=[],
|
||||
printer=_NullPrinter(),
|
||||
)
|
||||
resolved = str(result).strip()
|
||||
return resolved if resolved else text
|
||||
|
||||
@@ -14,6 +14,7 @@ from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from crewai.new_agent.models import Message
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
|
||||
|
||||
@@ -248,7 +249,8 @@ class SpawnSubtaskTool(BaseTool):
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
output.append(f"[Subtask {i + 1}] {r.content}")
|
||||
content = r.content if isinstance(r, Message) else str(r)
|
||||
output.append(f"[Subtask {i + 1}] {content}")
|
||||
# GAP-57: Emit spawn completed event
|
||||
try:
|
||||
from crewai.new_agent.events import NewAgentSpawnCompletedEvent
|
||||
@@ -427,7 +429,8 @@ class SpawnSubtaskTool(BaseTool):
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
results.append(f"[Subtask {i + 1}] {r.content}")
|
||||
content = r.content if isinstance(r, Message) else str(r)
|
||||
results.append(f"[Subtask {i + 1}] {content}")
|
||||
try:
|
||||
from crewai.new_agent.events import NewAgentSpawnCompletedEvent
|
||||
|
||||
|
||||
Reference in New Issue
Block a user