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
103 changes: 103 additions & 0 deletions .github/workflows/import-time.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Import Time Guard

on:
pull_request:
paths:
- "lib/crewai/src/**"
- "lib/crewai/pyproject.toml"
- "pyproject.toml"

permissions:
contents: read

jobs:
import-time:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
enable-cache: true

- name: Install the project
run: uv sync --all-extras --no-dev
env:
UV_PYTHON: ${{ matrix.python-version }}

- name: Benchmark PR branch
id: pr
run: |
result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json)
echo "result=$result" >> "$GITHUB_OUTPUT"
echo "pr_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT"
echo "### PR Branch Import Time" >> "$GITHUB_STEP_SUMMARY"
echo "$result" | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(f'- Median: {d[\"median_s\"]}s')
print(f'- Mean: {d[\"mean_s\"]}s ± {d[\"stdev_s\"]}s')
print(f'- Range: {d[\"min_s\"]}s – {d[\"max_s\"]}s')
" >> "$GITHUB_STEP_SUMMARY"
env:
UV_PYTHON: ${{ matrix.python-version }}

- name: Checkout base branch
run: git checkout ${{ github.event.pull_request.base.sha }}

- name: Install base branch
run: uv sync --all-extras --no-dev
env:
UV_PYTHON: ${{ matrix.python-version }}

- name: Benchmark base branch
id: base
run: |
result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json 2>/dev/null || echo '{"median_s": 0}')
echo "result=$result" >> "$GITHUB_OUTPUT"
echo "base_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT"
echo "### Base Branch Import Time" >> "$GITHUB_STEP_SUMMARY"
echo "$result" | python3 -c "
import sys, json
d = json.load(sys.stdin)
if d.get('median_s', 0) > 0:
print(f'- Median: {d[\"median_s\"]}s')
else:
print('- Benchmark script not present on base branch (skip comparison)')
" >> "$GITHUB_STEP_SUMMARY"
env:
UV_PYTHON: ${{ matrix.python-version }}

- name: Compare and gate
run: |
pr_median=${{ steps.pr.outputs.pr_median }}
base_median=${{ steps.base.outputs.base_median }}

python3 -c "
pr = float('$pr_median')
base = float('$base_median')

if base <= 0:
print('⏭️ No base benchmark available — skipping comparison.')
exit(0)

change_pct = ((pr - base) / base) * 100
print(f'Base: {base:.3f}s')
print(f'PR: {pr:.3f}s')
print(f'Change: {change_pct:+.1f}%')
print()

if change_pct > 5:
print(f'❌ BLOCKED: Import time regressed by {change_pct:.1f}% (threshold: 5%)')
exit(1)
elif change_pct > 0:
print(f'⚠️ Slight regression ({change_pct:.1f}%) but within 5% threshold.')
else:
print(f'✅ Import time improved by {abs(change_pct):.1f}%')
"
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ chromadb-*.lock
.crewai/memory
blogs/*
secrets/*
UNKNOWN.egg-info/
2 changes: 1 addition & 1 deletion lib/crewai-tools/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ github = [
]
rag = [
"python-docx>=1.1.0",
"lxml>=5.3.0,<5.4.0", # Pin to avoid etree import issues in 5.4.0
"lxml>=6.1.0,<7", # 6.1.0+ required for GHSA-vfmq-68hx-4jfw (XXE in iterparse)
]
xml = [
"unstructured[local-inference, all-docs]>=0.17.2"
Expand Down
6 changes: 4 additions & 2 deletions lib/crewai/src/crewai/agent/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
from crewai.lite_agent_output import LiteAgentOutput
from crewai.llms.base_llm import BaseLLM
from crewai.mcp import MCPServerConfig
from crewai.mcp.tool_resolver import MCPToolResolver
from crewai.mcp.config import MCPServerConfig
from crewai.rag.embeddings.types import EmbedderConfig
from crewai.security.fingerprint import Fingerprint
from crewai.skills.loader import activate_skill, discover_skills
Expand Down Expand Up @@ -119,6 +118,7 @@

from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai.agents.agent_builder.base_agent import PlatformAppOrAction
from crewai.mcp.tool_resolver import MCPToolResolver
from crewai.task import Task
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
Expand Down Expand Up @@ -1120,6 +1120,8 @@ def get_mcp_tools(self, mcps: list[str | MCPServerConfig]) -> list[BaseTool]:
Delegates to :class:`~crewai.mcp.tool_resolver.MCPToolResolver`.
"""
self._cleanup_mcp_clients()
from crewai.mcp.tool_resolver import MCPToolResolver

self._mcp_resolver = MCPToolResolver(agent=self, logger=self._logger)
return self._mcp_resolver.resolve(mcps)

Expand Down
Loading
Loading