mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-06-15 21:28:17 +00:00
Compare commits
1 Commits
docs/file-
...
docs/plann
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
472abf89ad |
103
.github/workflows/import-time.yml
vendored
103
.github/workflows/import-time.yml
vendored
@@ -1,103 +0,0 @@
|
||||
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}%')
|
||||
"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,4 +30,3 @@ chromadb-*.lock
|
||||
.crewai/memory
|
||||
blogs/*
|
||||
secrets/*
|
||||
UNKNOWN.egg-info/
|
||||
|
||||
@@ -117,35 +117,23 @@ task = Task(
|
||||
|
||||
### مع التدفقات
|
||||
|
||||
تعمل الحقول من نوع الملف (`File`، `ImageFile`، `PDFFile`) في مخطط حالة التدفق كإشارة لواجهة المنصة. عند النشر، تُعرض هذه الحقول كمناطق سحب وإفلات لرفع الملفات. يمكن أيضًا تمرير الملفات عبر `input_files` في API.
|
||||
مرر الملفات إلى التدفقات، والتي تنتقل تلقائيًا إلى الأطقم:
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile
|
||||
from pydantic import BaseModel
|
||||
from crewai_files import ImageFile
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform UI
|
||||
cover_image: ImageFile # Image-specific dropzone
|
||||
title: str = ""
|
||||
|
||||
class AnalysisFlow(Flow[MyState]):
|
||||
class AnalysisFlow(Flow):
|
||||
@start()
|
||||
def analyze(self):
|
||||
# Files are automatically populated in state
|
||||
content = self.state.document.read()
|
||||
return self.analysis_crew.kickoff()
|
||||
|
||||
flow = AnalysisFlow()
|
||||
result = flow.kickoff(
|
||||
input_files={"document": File(source="report.pdf")}
|
||||
input_files={"image": ImageFile(source="data.png")}
|
||||
)
|
||||
```
|
||||
|
||||
<Note type="info" title="تكامل منصة CrewAI">
|
||||
عند النشر على منصة CrewAI، تحصل الحقول من نوع الملف مثل `ImageFile` و `PDFFile` وغيرها في حالة التدفق تلقائيًا على واجهة رفع ملفات. يمكن للمستخدمين سحب وإفلات الملفات مباشرة في واجهة المنصة. يتم تخزين الملفات بشكل آمن وتمريرها إلى الوكلاء باستخدام تحسينات خاصة بالمزود (base64 مضمّن، أو واجهات برمجة لرفع الملفات، أو مراجع URL حسب المزود). للاطلاع على أمثلة استخدام API، راجع [مدخلات الملفات في التدفقات](/ar/concepts/flows#مدخلات-الملفات).
|
||||
</Note>
|
||||
|
||||
### مع الوكلاء المستقلين
|
||||
|
||||
مرر الملفات مباشرة إلى تشغيل الوكيل:
|
||||
|
||||
@@ -341,90 +341,6 @@ flow.kickoff()
|
||||
|
||||
من خلال توفير خيارات إدارة الحالة غير المهيكلة والمهيكلة، تمكّن تدفقات CrewAI المطورين من بناء سير عمل ذكاء اصطناعي مرن ومتين في آن واحد، ملبيةً مجموعة واسعة من متطلبات التطبيقات.
|
||||
|
||||
### مدخلات الملفات
|
||||
|
||||
عند استخدام الحالة المهيكلة، يمكنك تضمين حقول من نوع الملف باستخدام فئات من `crewai-files`. تعمل الحقول من نوع الملف في حالة التدفق كإشارة للمنصة — فهي تُعرض تلقائيًا كمناطق سحب وإفلات لرفع الملفات في واجهة علامة تبويب Run ويتم تعبئتها عند رفع الملفات عبر المنصة أو تمريرها عبر `input_files` في API.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile, PDFFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform
|
||||
title: str = ""
|
||||
|
||||
class MyFlow(Flow[MyState]):
|
||||
@start()
|
||||
def process(self):
|
||||
# File object is automatically populated in state
|
||||
# when uploaded via Platform UI or passed via API
|
||||
content = self.state.document.read()
|
||||
print(f"Processing {self.state.title}: {len(content)} bytes")
|
||||
return content
|
||||
```
|
||||
|
||||
عند النشر على **منصة CrewAI**، تُعرض الحقول من نوع الملف (`File`، `ImageFile`، `PDFFile` من `crewai-files`) تلقائيًا كمناطق سحب وإفلات لرفع الملفات في واجهة المستخدم. يمكن للمستخدمين سحب وإفلات الملفات، والتي تُملأ بعد ذلك في حالة التدفق الخاص بك.
|
||||
|
||||
**بدء التشغيل مع الملفات عبر API:**
|
||||
|
||||
تكتشف نقطة النهاية `/kickoff` تنسيق الطلب تلقائيًا:
|
||||
- **جسم JSON** ← بدء تشغيل عادي
|
||||
- **multipart/form-data** ← رفع ملف + بدء تشغيل
|
||||
|
||||
يمكن لمستخدمي API أيضًا تمرير سلاسل URL مباشرة إلى الحقول من نوع الملف — يقوم Pydantic بتحويلها تلقائيًا.
|
||||
|
||||
### استخدام API
|
||||
|
||||
#### الخيار 1: بدء تشغيل multipart (موصى به)
|
||||
|
||||
أرسل الملفات مباشرة مع طلب بدء التشغيل:
|
||||
|
||||
```bash
|
||||
# With files (multipart) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"company_name": "Einstein"}' \
|
||||
-F 'cover_image=@/path/to/photo.jpg'
|
||||
```
|
||||
|
||||
يتم تخزين الملفات تلقائيًا وتحويلها إلى كائنات `FileInput`. يتلقى الوكيل الملف مع تحسين خاص بالمزود (base64 مضمّن، أو API لرفع الملفات، أو مرجع URL حسب مزود LLM).
|
||||
|
||||
#### الخيار 2: بدء تشغيل JSON (بدون ملفات)
|
||||
|
||||
```bash
|
||||
# Without files (JSON) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}}'
|
||||
```
|
||||
|
||||
#### الخيار 3: رفع منفصل + بدء تشغيل
|
||||
|
||||
هذا بديل لرفع multipart عندما تحتاج إلى رفع الملفات بشكل منفصل عن طلب بدء التشغيل. ارفع الملفات أولاً، ثم أشِر إليها بواسطة URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/photo.jpg' \
|
||||
-F 'field_name=cover_image'
|
||||
# Returns: {"url": "https://...", "field_name": "cover_image"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}, "input_files": {"cover_image": "https://..."}}'
|
||||
```
|
||||
|
||||
راجع وثائق Platform API للحصول على تفاصيل كاملة حول نقطة النهاية `/files`.
|
||||
|
||||
#### على منصة CrewAI
|
||||
|
||||
عند استخدام واجهة المنصة، تُعرض الحقول من نوع الملف تلقائيًا كمناطق سحب وإفلات للرفع. لا حاجة لاستدعاءات API — فقط أفلِت الملف وانقر على تشغيل.
|
||||
|
||||
## استمرارية التدفق
|
||||
|
||||
يتيح مزخرف @persist الاستمرارية التلقائية للحالة في تدفقات CrewAI، مما يسمح لك بالحفاظ على حالة التدفق عبر عمليات إعادة التشغيل أو تنفيذات سير العمل المختلفة. يمكن تطبيق هذا المزخرف على مستوى الفئة أو مستوى الدالة، مما يوفر مرونة في كيفية إدارة استمرارية الحالة.
|
||||
|
||||
@@ -86,60 +86,6 @@ curl -H "Authorization: Bearer YOUR_CREW_TOKEN" https://your-crew-url.crewai.com
|
||||
|
||||
رمز الحامل متاح في علامة تبويب Status في صفحة تفاصيل طاقمك.
|
||||
|
||||
## رفع الملفات
|
||||
|
||||
عندما يتضمن طاقمك أو تدفقك حقول حالة من نوع الملف (باستخدام `ImageFile` أو `PDFFile` أو `File` من `crewai-files`)، تُعرض هذه الحقول تلقائيًا كمناطق سحب وإفلات لرفع الملفات في واجهة علامة تبويب Run. يمكن للمستخدمين سحب وإفلات الملفات مباشرة، وتتولى المنصة التخزين والتسليم إلى وكلائك.
|
||||
|
||||
### بدء تشغيل Multipart (موصى به)
|
||||
|
||||
أرسل الملفات مباشرة مع طلب بدء التشغيل باستخدام `multipart/form-data`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"title": "Report"}' \
|
||||
-F 'document=@/path/to/file.pdf'
|
||||
```
|
||||
|
||||
يتم تخزين الملفات تلقائيًا وتحويلها إلى كائنات ملفات. يتلقى الوكيل الملف مع تحسين خاص بالمزود (base64 مضمّن، أو API لرفع الملفات، أو مرجع URL حسب مزود LLM).
|
||||
|
||||
### بدء تشغيل JSON مع عناوين URL للملفات
|
||||
|
||||
إذا كانت لديك ملفات مستضافة بالفعل على عناوين URL، مررها عبر `input_files`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"inputs": {"title": "Report"},
|
||||
"input_files": {"document": "https://example.com/file.pdf"}
|
||||
}'
|
||||
```
|
||||
|
||||
### رفع منفصل + بدء تشغيل
|
||||
|
||||
ارفع الملفات أولاً، ثم أشِر إليها بواسطة URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/file.pdf' \
|
||||
-F 'field_name=document'
|
||||
# Returns: {"url": "https://...", "field_name": "document"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"title": "Report"}, "input_files": {"document": "https://..."}}'
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
يعمل رفع الملفات بنفس الطريقة لكل من الطواقم والتدفقات. عرّف حقول من نوع الملف في مخطط حالتك، وستتولى واجهة المنصة وAPI الرفع تلقائيًا.
|
||||
</Note>
|
||||
|
||||
### التحقق من صحة الطاقم
|
||||
|
||||
قبل تنفيذ العمليات، يمكنك التحقق من أن طاقمك يعمل بشكل صحيح:
|
||||
|
||||
@@ -207,6 +207,9 @@ CrewAI AMP مُصمَّم لفرق الإنتاج. إليك ما تحصل علي
|
||||
- **Factory (استضافة ذاتية)** — على بنيتك التحتية لسيطرة كاملة على البيانات
|
||||
- **هجين** — دمج السحابة والاستضافة الذاتية حسب حساسية البيانات
|
||||
</Accordion>
|
||||
<Accordion title="كيف يعمل التسعير؟">
|
||||
سجّل في [app.crewai.com](https://app.crewai.com) لمعرفة الخطط الحالية. تسعير المؤسسات وFactory متاح عند الطلب.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Card title="استكشف CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
|
||||
|
||||
@@ -117,35 +117,23 @@ task = Task(
|
||||
|
||||
### With Flows
|
||||
|
||||
File-typed fields (`File`, `ImageFile`, `PDFFile`) in your flow's state schema serve as the signal to the Platform UI. When deployed, these fields render as file upload dropzones. Files can also be passed via `input_files` in the API.
|
||||
Pass files to flows, which automatically inherit to crews:
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile
|
||||
from pydantic import BaseModel
|
||||
from crewai_files import ImageFile
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform UI
|
||||
cover_image: ImageFile # Image-specific dropzone
|
||||
title: str = ""
|
||||
|
||||
class AnalysisFlow(Flow[MyState]):
|
||||
class AnalysisFlow(Flow):
|
||||
@start()
|
||||
def analyze(self):
|
||||
# Files are automatically populated in state
|
||||
content = self.state.document.read()
|
||||
return self.analysis_crew.kickoff()
|
||||
|
||||
flow = AnalysisFlow()
|
||||
result = flow.kickoff(
|
||||
input_files={"document": File(source="report.pdf")}
|
||||
input_files={"image": ImageFile(source="data.png")}
|
||||
)
|
||||
```
|
||||
|
||||
<Note type="info" title="CrewAI Platform Integration">
|
||||
When deployed on CrewAI Platform, `ImageFile`, `PDFFile`, and other file-typed fields in your flow state automatically get a file upload UI. Users can drag and drop files directly in the Platform interface. Files are stored securely and passed to agents using provider-specific optimizations (inline base64, file upload APIs, or URL references depending on the provider). For API usage examples, see [File Inputs in Flows](/concepts/flows#file-inputs).
|
||||
</Note>
|
||||
|
||||
### With Standalone Agents
|
||||
|
||||
Pass files directly to agent kickoff:
|
||||
|
||||
@@ -341,90 +341,6 @@ flow.kickoff()
|
||||
|
||||
By providing both unstructured and structured state management options, CrewAI Flows empowers developers to build AI workflows that are both flexible and robust, catering to a wide range of application requirements.
|
||||
|
||||
### File Inputs
|
||||
|
||||
When using structured state, you can include file-typed fields using classes from `crewai-files`. File-typed fields in your flow state serve as the signal to the Platform—they automatically render as file upload dropzones in the Run tab UI and get populated when files are uploaded via the Platform or passed via `input_files` in the API.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile, PDFFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform
|
||||
title: str = ""
|
||||
|
||||
class MyFlow(Flow[MyState]):
|
||||
@start()
|
||||
def process(self):
|
||||
# File object is automatically populated in state
|
||||
# when uploaded via Platform UI or passed via API
|
||||
content = self.state.document.read()
|
||||
print(f"Processing {self.state.title}: {len(content)} bytes")
|
||||
return content
|
||||
```
|
||||
|
||||
When deployed on **CrewAI Platform**, file-typed fields (`File`, `ImageFile`, `PDFFile` from `crewai-files`) automatically render as file upload dropzones in the UI. Users can drag and drop files, which are then populated into your flow's state.
|
||||
|
||||
**Kicking off with files via API:**
|
||||
|
||||
The `/kickoff` endpoint auto-detects the request format:
|
||||
- **JSON body** → normal kickoff
|
||||
- **multipart/form-data** → file upload + kickoff
|
||||
|
||||
API users can also pass URL strings directly to file-typed fields—Pydantic coerces them automatically.
|
||||
|
||||
### API Usage
|
||||
|
||||
#### Option 1: Multipart kickoff (recommended)
|
||||
|
||||
Send files directly with the kickoff request:
|
||||
|
||||
```bash
|
||||
# With files (multipart) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"company_name": "Einstein"}' \
|
||||
-F 'cover_image=@/path/to/photo.jpg'
|
||||
```
|
||||
|
||||
Files are automatically stored and converted to `FileInput` objects. The agent receives the file with provider-specific optimization (inline base64, file upload API, or URL reference depending on the LLM provider).
|
||||
|
||||
#### Option 2: JSON kickoff (no files)
|
||||
|
||||
```bash
|
||||
# Without files (JSON) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}}'
|
||||
```
|
||||
|
||||
#### Option 3: Separate upload + kickoff
|
||||
|
||||
This is an alternative to multipart upload when you need to upload files separately from the kickoff request. Upload files first, then reference them by URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/photo.jpg' \
|
||||
-F 'field_name=cover_image'
|
||||
# Returns: {"url": "https://...", "field_name": "cover_image"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}, "input_files": {"cover_image": "https://..."}}'
|
||||
```
|
||||
|
||||
See the Platform API documentation for full `/files` endpoint details.
|
||||
|
||||
#### On CrewAI Platform
|
||||
|
||||
When using the Platform UI, file-typed fields automatically render as drag-and-drop upload zones. No API calls needed—just drop the file and click Run.
|
||||
|
||||
## Flow Persistence
|
||||
|
||||
The @persist decorator enables automatic state persistence in CrewAI Flows, allowing you to maintain flow state across restarts or different workflow executions. This decorator can be applied at either the class level or method level, providing flexibility in how you manage state persistence.
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
---
|
||||
title: Planning
|
||||
description: Learn how to add planning to your CrewAI Crew and improve their performance.
|
||||
description: Learn how to add planning to CrewAI at the crew level (sequential task planning) and the agent level (Plan-and-Act with PlanningConfig).
|
||||
icon: ruler-combined
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The planning feature in CrewAI allows you to add planning capability to your crew. When enabled, before each Crew iteration,
|
||||
all Crew information is sent to an AgentPlanner that will plan the tasks step by step, and this plan will be added to each task description.
|
||||
CrewAI provides two complementary planning systems:
|
||||
|
||||
- **Crew-level planning** — before each crew iteration, an `AgentPlanner` produces a step-by-step plan for every task and injects it into the task description. Useful when you want the crew to think through the *whole pipeline* before any agent starts working.
|
||||
- **Agent-level planning (Plan-and-Act)** — a single agent builds an explicit multi-step plan, executes it step by step, and observes/replans as it goes. Configured per-agent via `PlanningConfig`. Useful when you want one agent to tackle a complex task adaptively.
|
||||
|
||||
The two are independent and can be combined: a crew can have planning enabled, and individual agents in that crew can also use `planning_config`.
|
||||
|
||||
## Crew-Level Planning
|
||||
|
||||
The crew-level planning feature adds planning capability to your crew. When enabled, before each Crew iteration,
|
||||
all Crew information is sent to an `AgentPlanner` that will plan the tasks step by step, and this plan will be added to each task description.
|
||||
|
||||
### Using the Planning Feature
|
||||
|
||||
Getting started with the planning feature is very easy, the only step required is to add `planning=True` to your Crew:
|
||||
Getting started with crew-level planning is very easy, the only step required is to add `planning=True` to your Crew:
|
||||
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
@@ -36,9 +45,9 @@ When planning is enabled, crewAI will use `gpt-4o-mini` as the default LLM for p
|
||||
|
||||
#### Planning LLM
|
||||
|
||||
Now you can define the LLM that will be used to plan the tasks.
|
||||
Now you can define the LLM that will be used to plan the tasks.
|
||||
|
||||
When running the base case example, you will see something like the output below, which represents the output of the `AgentPlanner`
|
||||
When running the base case example, you will see something like the output below, which represents the output of the `AgentPlanner`
|
||||
responsible for creating the step-by-step logic to add to the Agents' tasks.
|
||||
|
||||
<CodeGroup>
|
||||
@@ -152,4 +161,191 @@ A list with 10 bullet points of the most relevant information about AI LLMs.
|
||||
**Expected Output:**
|
||||
A fully fledged report with the main topics, each with a full section of information. Formatted as markdown without '```'.
|
||||
```
|
||||
</CodeGroup>
|
||||
</CodeGroup>
|
||||
|
||||
## Agent-Level Planning (Plan-and-Act)
|
||||
|
||||
Agent-level planning gives a single agent an explicit Plan-and-Act loop: it builds a structured multi-step plan up front, executes each step, observes the result, and can replan or refine when reality diverges from the plan. It's configured per-agent through `PlanningConfig`.
|
||||
|
||||
### Enabling Agent Planning
|
||||
|
||||
Pass a `PlanningConfig` to the agent. The presence of a `PlanningConfig` enables planning — you don't need a separate flag.
|
||||
|
||||
<CodeGroup>
|
||||
```python Defaults
|
||||
from crewai import Agent, PlanningConfig
|
||||
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze datasets and surface insights",
|
||||
backstory="You are an experienced data analyst.",
|
||||
planning_config=PlanningConfig(), # medium effort, defaults
|
||||
)
|
||||
```
|
||||
|
||||
```python Tuned
|
||||
from crewai import Agent, PlanningConfig
|
||||
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze datasets and surface insights",
|
||||
backstory="You are an experienced data analyst.",
|
||||
planning_config=PlanningConfig(
|
||||
reasoning_effort="high",
|
||||
max_steps=10,
|
||||
max_replans=2,
|
||||
max_step_iterations=10,
|
||||
step_timeout=120,
|
||||
llm="gpt-4o-mini",
|
||||
),
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Reasoning Effort
|
||||
|
||||
`reasoning_effort` controls what happens *between steps* — how aggressively the agent observes, replans, and refines as it executes the plan. It is the most important knob for tuning latency vs. adaptiveness.
|
||||
|
||||
<ParamField body="low" type="string">
|
||||
Observe each step for success validation only. Skip the decide/replan/refine pipeline; steps are marked complete and execution continues linearly. **Fastest option** — best when the plan is likely to be correct on the first try and you want minimal overhead per step.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="medium" type="string" default="default">
|
||||
Observe each step. On failure, trigger replanning. On success, skip refinement and continue. **Balanced option (default)** — replans only when something goes wrong, so you get adaptiveness without paying for it on the happy path.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="high" type="string">
|
||||
Full observation pipeline with `decide_next_action` after every step. Can trigger early goal achievement (finish before all steps run), full replanning, or lightweight step refinement. **Most adaptive, highest latency** — best for open-ended or exploratory tasks where the right path can't be predicted up front.
|
||||
</ParamField>
|
||||
|
||||
### PlanningConfig Fields
|
||||
|
||||
<ParamField body="reasoning_effort" type="Literal['low', 'medium', 'high']" default="medium">
|
||||
Post-step observation/replanning behavior. See above.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="max_attempts" type="int | None" default="None">
|
||||
Maximum number of planning refinement attempts during the initial plan creation. If `None`, the agent keeps refining until it indicates readiness.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="max_steps" type="int" default="20">
|
||||
Maximum number of steps in the generated plan. Must be `>= 1`. Lower this when you want concise plans; raise it for complex tasks that legitimately need many steps.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="max_replans" type="int" default="3">
|
||||
Maximum number of full replanning cycles allowed during execution. Must be `>= 0`. Set to `0` to forbid replanning entirely (the agent will stick to the original plan even if steps fail).
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="max_step_iterations" type="int" default="15">
|
||||
Maximum LLM iterations per step inside the `StepExecutor` multi-turn loop. Must be `>= 1`. Lower values make individual steps faster but less thorough — useful when each step is a small, well-scoped action.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="step_timeout" type="int | None" default="None">
|
||||
Wall-clock seconds for a single step. If exceeded, the step is marked failed and observation decides whether to continue or replan. `None` means no per-step timeout.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="system_prompt" type="str | None" default="None">
|
||||
Override the default planning system prompt. Use this to inject domain-specific instructions for how plans should be structured.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="plan_prompt" type="str | None" default="None">
|
||||
Override the prompt used to create the initial plan. Supports template variables like `{description}`.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="refine_prompt" type="str | None" default="None">
|
||||
Override the prompt used to refine the plan during the `max_attempts` refinement loop.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="llm" type="str | BaseLLM | None" default="None">
|
||||
LLM used for planning. Falls back to the agent's own LLM if not provided. Pass either a model string (e.g., `"gpt-4o-mini"`) or a `BaseLLM` instance.
|
||||
</ParamField>
|
||||
|
||||
### How the Plan-and-Act Loop Works
|
||||
|
||||
When `planning_config` is set, the agent executes the task as follows:
|
||||
|
||||
1. **Plan** — build an initial multi-step plan, refining up to `max_attempts` times until ready.
|
||||
2. **Execute step** — run one step through the `StepExecutor` (up to `max_step_iterations` LLM turns, bounded by `step_timeout`).
|
||||
3. **Observe** — validate whether the step succeeded.
|
||||
4. **Decide next action** — depending on `reasoning_effort`:
|
||||
- `low`: continue to the next step.
|
||||
- `medium`: continue on success; replan on failure.
|
||||
- `high`: route through `decide_next_action`, which can finish early, replan, refine the next step, or continue.
|
||||
5. Repeat until the plan completes, the goal is achieved, or `max_replans` is exhausted.
|
||||
|
||||
### Custom Prompts Example
|
||||
|
||||
```python
|
||||
from crewai import Agent, PlanningConfig
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Research topics",
|
||||
backstory="Expert researcher",
|
||||
planning_config=PlanningConfig(
|
||||
reasoning_effort="high",
|
||||
max_attempts=3,
|
||||
max_steps=10,
|
||||
plan_prompt="Create a focused plan for: {description}",
|
||||
refine_prompt="Tighten this plan, removing any step that doesn't materially advance the goal.",
|
||||
llm="gpt-4o-mini",
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### Migration from `reasoning=True`
|
||||
|
||||
The original agent reasoning API used two fields directly on `Agent`:
|
||||
|
||||
- `reasoning: bool = False`
|
||||
- `max_reasoning_attempts: int | None = None`
|
||||
|
||||
Both are **deprecated**. They still work — passing them emits a `DeprecationWarning` and CrewAI auto-migrates them to an equivalent `PlanningConfig` — but new code should use `PlanningConfig` directly.
|
||||
|
||||
<Warning>
|
||||
`Agent(reasoning=True, ...)` and `Agent(max_reasoning_attempts=N, ...)` are deprecated and will be removed in a future release. Migrate to `planning_config=PlanningConfig(...)`.
|
||||
</Warning>
|
||||
|
||||
<CodeGroup>
|
||||
```python Before (deprecated)
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
backstory="Expert data analyst.",
|
||||
reasoning=True,
|
||||
max_reasoning_attempts=3,
|
||||
)
|
||||
```
|
||||
|
||||
```python After
|
||||
from crewai import Agent, PlanningConfig
|
||||
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
backstory="Expert data analyst.",
|
||||
planning_config=PlanningConfig(max_attempts=3),
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The mapping is direct:
|
||||
|
||||
- `reasoning=True` → presence of `planning_config` enables planning.
|
||||
- `max_reasoning_attempts=N` → `PlanningConfig(max_attempts=N)`.
|
||||
|
||||
Everything else (`reasoning_effort`, `max_steps`, `max_replans`, `max_step_iterations`, `step_timeout`, custom prompts, dedicated planning LLM) is new functionality only available through `PlanningConfig`.
|
||||
|
||||
## Choosing Between Crew-Level and Agent-Level Planning
|
||||
|
||||
| Concern | Crew-level (`Crew(planning=True)`) | Agent-level (`PlanningConfig`) |
|
||||
| --- | --- | --- |
|
||||
| Scope | Plans every task in the crew up front | Plans one agent's task adaptively |
|
||||
| When the plan is built | Once per crew iteration, before any task runs | At the start of each agent's task |
|
||||
| Adapts mid-execution | No — the plan is injected as guidance | Yes — observes, replans, and refines per step |
|
||||
| Best for | Multi-task pipelines where ordering and hand-offs matter | Open-ended tasks where the right path emerges as the agent works |
|
||||
| Configuration surface | `planning`, `planning_llm` on `Crew` | `PlanningConfig` on `Agent` |
|
||||
|
||||
The two are complementary — you can enable crew-level planning to coordinate the overall pipeline and use `planning_config` on individual agents that need to think adaptively while executing their step.
|
||||
|
||||
@@ -1,148 +1,59 @@
|
||||
---
|
||||
title: Reasoning
|
||||
description: "Learn how to enable and use agent reasoning to improve task execution."
|
||||
description: "Agent reasoning has been renamed to planning_config. See the Planning page for the current API."
|
||||
icon: brain
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Agent reasoning is a feature that allows agents to reflect on a task and create a plan before execution. This helps agents approach tasks more methodically and ensures they're ready to perform the assigned work.
|
||||
<Warning>
|
||||
The `reasoning=True` and `max_reasoning_attempts=N` arguments on `Agent` are **deprecated**. They still work for now — passing them emits a `DeprecationWarning` and CrewAI auto-migrates the values into a `PlanningConfig` — but they will be removed in a future release.
|
||||
|
||||
## Usage
|
||||
The replacement is **`planning_config`**, documented in full on the [Planning](/en/concepts/planning) page.
|
||||
</Warning>
|
||||
|
||||
To enable reasoning for an agent, simply set `reasoning=True` when creating the agent:
|
||||
## Migration
|
||||
|
||||
```python
|
||||
The new API lives on `Agent.planning_config` and uses the `PlanningConfig` model. The presence of a `PlanningConfig` enables planning — there is no separate boolean flag.
|
||||
|
||||
<CodeGroup>
|
||||
```python Before (deprecated)
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze complex datasets and provide insights",
|
||||
backstory="You are an experienced data analyst with expertise in finding patterns in complex data.",
|
||||
reasoning=True, # Enable reasoning
|
||||
max_reasoning_attempts=3 # Optional: Set a maximum number of reasoning attempts
|
||||
)
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
When reasoning is enabled, before executing a task, the agent will:
|
||||
|
||||
1. Reflect on the task and create a detailed plan
|
||||
2. Evaluate whether it's ready to execute the task
|
||||
3. Refine the plan as necessary until it's ready or max_reasoning_attempts is reached
|
||||
4. Inject the reasoning plan into the task description before execution
|
||||
|
||||
This process helps the agent break down complex tasks into manageable steps and identify potential challenges before starting.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
<ParamField body="reasoning" type="bool" default="False">
|
||||
Enable or disable reasoning
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="max_reasoning_attempts" type="int" default="None">
|
||||
Maximum number of attempts to refine the plan before proceeding with execution. If None (default), the agent will continue refining until it's ready.
|
||||
</ParamField>
|
||||
|
||||
## Example
|
||||
|
||||
Here's a complete example:
|
||||
|
||||
```python
|
||||
from crewai import Agent, Task, Crew
|
||||
|
||||
# Create an agent with reasoning enabled
|
||||
analyst = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
backstory="You are an expert data analyst.",
|
||||
backstory="Expert data analyst.",
|
||||
reasoning=True,
|
||||
max_reasoning_attempts=3 # Optional: Set a limit on reasoning attempts
|
||||
max_reasoning_attempts=3,
|
||||
)
|
||||
|
||||
# Create a task
|
||||
analysis_task = Task(
|
||||
description="Analyze the provided sales data and identify key trends.",
|
||||
expected_output="A report highlighting the top 3 sales trends.",
|
||||
agent=analyst
|
||||
)
|
||||
|
||||
# Create a crew and run the task
|
||||
crew = Crew(agents=[analyst], tasks=[analysis_task])
|
||||
result = crew.kickoff()
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
```python After
|
||||
from crewai import Agent, PlanningConfig
|
||||
|
||||
The reasoning process is designed to be robust, with error handling built in. If an error occurs during reasoning, the agent will proceed with executing the task without the reasoning plan. This ensures that tasks can still be executed even if the reasoning process fails.
|
||||
|
||||
Here's how to handle potential errors in your code:
|
||||
|
||||
```python
|
||||
from crewai import Agent, Task
|
||||
import logging
|
||||
|
||||
# Set up logging to capture any reasoning errors
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# Create an agent with reasoning enabled
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
reasoning=True,
|
||||
max_reasoning_attempts=3
|
||||
backstory="Expert data analyst.",
|
||||
planning_config=PlanningConfig(max_attempts=3),
|
||||
)
|
||||
|
||||
# Create a task
|
||||
task = Task(
|
||||
description="Analyze the provided sales data and identify key trends.",
|
||||
expected_output="A report highlighting the top 3 sales trends.",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Execute the task
|
||||
# If an error occurs during reasoning, it will be logged and execution will continue
|
||||
result = agent.execute_task(task)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Example Reasoning Output
|
||||
Field mapping:
|
||||
|
||||
Here's an example of what a reasoning plan might look like for a data analysis task:
|
||||
- `reasoning=True` → presence of `planning_config` enables planning.
|
||||
- `max_reasoning_attempts=N` → `PlanningConfig(max_attempts=N)`.
|
||||
|
||||
```
|
||||
Task: Analyze the provided sales data and identify key trends.
|
||||
## What's New
|
||||
|
||||
Reasoning Plan:
|
||||
I'll analyze the sales data to identify the top 3 trends.
|
||||
`PlanningConfig` exposes capabilities that the old `reasoning` flag did not, including:
|
||||
|
||||
1. Understanding of the task:
|
||||
I need to analyze sales data to identify key trends that would be valuable for business decision-making.
|
||||
- `reasoning_effort` (`"low"` / `"medium"` / `"high"`) to control post-step observation, replanning, and refinement.
|
||||
- `max_steps`, `max_replans`, `max_step_iterations`, and `step_timeout` to bound plan size and execution.
|
||||
- A dedicated planning `llm` separate from the agent's execution LLM.
|
||||
- Custom `system_prompt`, `plan_prompt`, and `refine_prompt` overrides.
|
||||
|
||||
2. Key steps I'll take:
|
||||
- First, I'll examine the data structure to understand what fields are available
|
||||
- Then I'll perform exploratory data analysis to identify patterns
|
||||
- Next, I'll analyze sales by time periods to identify temporal trends
|
||||
- I'll also analyze sales by product categories and customer segments
|
||||
- Finally, I'll identify the top 3 most significant trends
|
||||
|
||||
3. Approach to challenges:
|
||||
- If the data has missing values, I'll decide whether to fill or filter them
|
||||
- If the data has outliers, I'll investigate whether they're valid data points or errors
|
||||
- If trends aren't immediately obvious, I'll apply statistical methods to uncover patterns
|
||||
|
||||
4. Use of available tools:
|
||||
- I'll use data analysis tools to explore and visualize the data
|
||||
- I'll use statistical tools to identify significant patterns
|
||||
- I'll use knowledge retrieval to access relevant information about sales analysis
|
||||
|
||||
5. Expected outcome:
|
||||
A concise report highlighting the top 3 sales trends with supporting evidence from the data.
|
||||
|
||||
READY: I am ready to execute the task.
|
||||
```
|
||||
|
||||
This reasoning plan helps the agent organize its approach to the task, consider potential challenges, and ensure it delivers the expected output.
|
||||
For the full field reference, the Plan-and-Act loop, and guidance on when to use agent-level planning vs. crew-level planning, see [Planning](/en/concepts/planning).
|
||||
|
||||
@@ -86,60 +86,6 @@ curl -H "Authorization: Bearer YOUR_CREW_TOKEN" https://your-crew-url.crewai.com
|
||||
|
||||
Your bearer token is available on the Status tab of your crew's detail page.
|
||||
|
||||
## File Uploads
|
||||
|
||||
When your crew or flow includes file-typed state fields (using `ImageFile`, `PDFFile`, or `File` from `crewai-files`), these fields automatically render as file upload dropzones in the Run tab UI. Users can drag and drop files directly, and the Platform handles storage and delivery to your agents.
|
||||
|
||||
### Multipart Kickoff (Recommended)
|
||||
|
||||
Send files directly with the kickoff request using `multipart/form-data`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"title": "Report"}' \
|
||||
-F 'document=@/path/to/file.pdf'
|
||||
```
|
||||
|
||||
Files are automatically stored and converted to file objects. The agent receives the file with provider-specific optimization (inline base64, file upload API, or URL reference depending on the LLM provider).
|
||||
|
||||
### JSON Kickoff with File URLs
|
||||
|
||||
If you have files already hosted at URLs, pass them via `input_files`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"inputs": {"title": "Report"},
|
||||
"input_files": {"document": "https://example.com/file.pdf"}
|
||||
}'
|
||||
```
|
||||
|
||||
### Separate Upload + Kickoff
|
||||
|
||||
Upload files first, then reference them by URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/file.pdf' \
|
||||
-F 'field_name=document'
|
||||
# Returns: {"url": "https://...", "field_name": "document"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"title": "Report"}, "input_files": {"document": "https://..."}}'
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
File uploads work the same way for both crews and flows. Define file-typed fields in your state schema, and the Platform UI and API will handle uploads automatically.
|
||||
</Note>
|
||||
|
||||
### Checking Crew Health
|
||||
|
||||
Before executing operations, you can verify that your crew is running properly:
|
||||
|
||||
@@ -207,6 +207,9 @@ CrewAI AMP is built for production teams. Here's what you get beyond deployment.
|
||||
- **Factory (self-hosted)** — run on your own infrastructure for full data control
|
||||
- **Hybrid** — mix cloud and self-hosted based on sensitivity requirements
|
||||
</Accordion>
|
||||
<Accordion title="How does pricing work?">
|
||||
Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Card title="Explore CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
|
||||
|
||||
@@ -117,35 +117,23 @@ task = Task(
|
||||
|
||||
### Flow와 함께
|
||||
|
||||
flow의 상태 스키마에 있는 파일 타입 필드(`File`, `ImageFile`, `PDFFile`)는 플랫폼 UI에 대한 신호 역할을 합니다. 배포 시 이러한 필드는 파일 업로드 드롭존으로 렌더링됩니다. 파일은 API에서 `input_files`를 통해서도 전달할 수 있습니다.
|
||||
flow에 파일을 전달하면 자동으로 crew에 상속됩니다:
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile
|
||||
from pydantic import BaseModel
|
||||
from crewai_files import ImageFile
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform UI
|
||||
cover_image: ImageFile # Image-specific dropzone
|
||||
title: str = ""
|
||||
|
||||
class AnalysisFlow(Flow[MyState]):
|
||||
class AnalysisFlow(Flow):
|
||||
@start()
|
||||
def analyze(self):
|
||||
# Files are automatically populated in state
|
||||
content = self.state.document.read()
|
||||
return self.analysis_crew.kickoff()
|
||||
|
||||
flow = AnalysisFlow()
|
||||
result = flow.kickoff(
|
||||
input_files={"document": File(source="report.pdf")}
|
||||
input_files={"image": ImageFile(source="data.png")}
|
||||
)
|
||||
```
|
||||
|
||||
<Note type="info" title="CrewAI 플랫폼 통합">
|
||||
CrewAI 플랫폼에 배포하면 flow 상태의 `ImageFile`, `PDFFile` 및 기타 파일 타입 필드가 자동으로 파일 업로드 UI를 갖게 됩니다. 사용자는 플랫폼 인터페이스에서 직접 파일을 드래그 앤 드롭할 수 있습니다. 파일은 안전하게 저장되고 프로바이더별 최적화(인라인 base64, 파일 업로드 API 또는 프로바이더에 따른 URL 참조)를 사용하여 에이전트에 전달됩니다. API 사용 예제는 [Flows의 파일 입력](/ko/concepts/flows#파일-입력)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
### 단독 에이전트와 함께
|
||||
|
||||
에이전트 킥오프에 직접 파일을 전달합니다:
|
||||
|
||||
@@ -334,90 +334,6 @@ flow.kickoff()
|
||||
|
||||
CrewAI Flows는 비구조적 및 구조적 상태 관리 옵션을 모두 제공함으로써, 개발자들이 다양한 애플리케이션 요구 사항에 맞춰 유연하면서도 견고한 AI 워크플로를 구축할 수 있도록 지원합니다.
|
||||
|
||||
### 파일 입력
|
||||
|
||||
구조화된 상태를 사용할 때, `crewai-files`의 클래스를 사용하여 파일 타입 필드를 포함할 수 있습니다. flow 상태의 파일 타입 필드는 플랫폼에 대한 신호 역할을 합니다 — Run 탭 UI에서 자동으로 파일 업로드 드롭존으로 렌더링되며, 플랫폼을 통해 파일을 업로드하거나 API에서 `input_files`를 통해 전달할 때 자동으로 채워집니다.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile, PDFFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform
|
||||
title: str = ""
|
||||
|
||||
class MyFlow(Flow[MyState]):
|
||||
@start()
|
||||
def process(self):
|
||||
# File object is automatically populated in state
|
||||
# when uploaded via Platform UI or passed via API
|
||||
content = self.state.document.read()
|
||||
print(f"Processing {self.state.title}: {len(content)} bytes")
|
||||
return content
|
||||
```
|
||||
|
||||
**CrewAI 플랫폼**에 배포하면 파일 타입 필드(`crewai-files`의 `File`, `ImageFile`, `PDFFile`)가 UI에서 자동으로 파일 업로드 드롭존으로 렌더링됩니다. 사용자는 파일을 드래그 앤 드롭할 수 있으며, 해당 파일은 flow의 상태에 자동으로 채워집니다.
|
||||
|
||||
**API를 통한 파일 포함 시작:**
|
||||
|
||||
`/kickoff` 엔드포인트는 요청 형식을 자동으로 감지합니다:
|
||||
- **JSON body** → 일반 kickoff
|
||||
- **multipart/form-data** → 파일 업로드 + kickoff
|
||||
|
||||
API 사용자는 파일 타입 필드에 URL 문자열을 직접 전달할 수도 있습니다 — Pydantic이 자동으로 변환합니다.
|
||||
|
||||
### API 사용법
|
||||
|
||||
#### 옵션 1: Multipart kickoff (권장)
|
||||
|
||||
kickoff 요청과 함께 파일을 직접 전송합니다:
|
||||
|
||||
```bash
|
||||
# With files (multipart) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"company_name": "Einstein"}' \
|
||||
-F 'cover_image=@/path/to/photo.jpg'
|
||||
```
|
||||
|
||||
파일은 자동으로 저장되고 `FileInput` 객체로 변환됩니다. 에이전트는 프로바이더별 최적화(LLM 프로바이더에 따라 인라인 base64, 파일 업로드 API 또는 URL 참조)와 함께 파일을 수신합니다.
|
||||
|
||||
#### 옵션 2: JSON kickoff (파일 없음)
|
||||
|
||||
```bash
|
||||
# Without files (JSON) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}}'
|
||||
```
|
||||
|
||||
#### 옵션 3: 분리된 업로드 + kickoff
|
||||
|
||||
kickoff 요청과 별도로 파일을 업로드해야 할 때 multipart 업로드의 대안입니다. 먼저 파일을 업로드한 다음 URL로 참조합니다:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/photo.jpg' \
|
||||
-F 'field_name=cover_image'
|
||||
# Returns: {"url": "https://...", "field_name": "cover_image"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}, "input_files": {"cover_image": "https://..."}}'
|
||||
```
|
||||
|
||||
`/files` 엔드포인트에 대한 자세한 내용은 플랫폼 API 문서를 참조하세요.
|
||||
|
||||
#### CrewAI 플랫폼에서
|
||||
|
||||
플랫폼 UI를 사용할 때 파일 타입 필드는 자동으로 드래그 앤 드롭 업로드 영역으로 렌더링됩니다. API 호출이 필요 없습니다 — 파일을 드롭하고 실행을 클릭하면 됩니다.
|
||||
|
||||
## 플로우 지속성
|
||||
|
||||
@persist 데코레이터는 CrewAI 플로우에서 자동 상태 지속성을 활성화하여, 플로우 상태를 재시작이나 다른 워크플로우 실행 간에도 유지할 수 있도록 합니다. 이 데코레이터는 클래스 수준이나 메서드 수준 모두에 적용할 수 있어, 상태 지속성을 관리하는 데 유연성을 제공합니다.
|
||||
|
||||
@@ -86,60 +86,6 @@ curl -H "Authorization: Bearer YOUR_CREW_TOKEN" https://your-crew-url.crewai.com
|
||||
|
||||
베어러 토큰은 crew의 상세 페이지의 Status 탭에서 확인할 수 있습니다.
|
||||
|
||||
## 파일 업로드
|
||||
|
||||
crew나 flow에 파일 타입 상태 필드(`crewai-files`의 `ImageFile`, `PDFFile`, 또는 `File` 사용)가 포함되어 있으면, 이러한 필드는 Run 탭 UI에서 자동으로 파일 업로드 드롭존으로 렌더링됩니다. 사용자는 파일을 직접 드래그 앤 드롭할 수 있으며, 플랫폼이 저장 및 에이전트 전달을 처리합니다.
|
||||
|
||||
### Multipart Kickoff (권장)
|
||||
|
||||
`multipart/form-data`를 사용하여 kickoff 요청과 함께 파일을 직접 전송합니다:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"title": "Report"}' \
|
||||
-F 'document=@/path/to/file.pdf'
|
||||
```
|
||||
|
||||
파일은 자동으로 저장되고 파일 객체로 변환됩니다. 에이전트는 프로바이더별 최적화(LLM 프로바이더에 따라 인라인 base64, 파일 업로드 API 또는 URL 참조)와 함께 파일을 수신합니다.
|
||||
|
||||
### 파일 URL을 포함한 JSON Kickoff
|
||||
|
||||
이미 URL에 호스팅된 파일이 있다면 `input_files`를 통해 전달합니다:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"inputs": {"title": "Report"},
|
||||
"input_files": {"document": "https://example.com/file.pdf"}
|
||||
}'
|
||||
```
|
||||
|
||||
### 분리된 업로드 + Kickoff
|
||||
|
||||
먼저 파일을 업로드한 다음 URL로 참조합니다:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/file.pdf' \
|
||||
-F 'field_name=document'
|
||||
# Returns: {"url": "https://...", "field_name": "document"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"title": "Report"}, "input_files": {"document": "https://..."}}'
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
파일 업로드는 crew와 flow 모두에서 동일하게 작동합니다. 상태 스키마에 파일 타입 필드를 정의하면 플랫폼 UI와 API가 자동으로 업로드를 처리합니다.
|
||||
</Note>
|
||||
|
||||
### 크루 상태 확인
|
||||
|
||||
작업을 실행하기 전에 크루가 정상적으로 실행되고 있는지 확인할 수 있습니다:
|
||||
|
||||
@@ -207,6 +207,9 @@ CrewAI AMP는 프로덕션 팀을 위해 만들어졌습니다. 배포 외에
|
||||
- **Factory(셀프 호스팅)** — 데이터 통제를 위해 자체 인프라에서 실행
|
||||
- **하이브리드** — 민감도에 따라 클라우드와 셀프 호스팅을 혼합
|
||||
</Accordion>
|
||||
<Accordion title="가격은 어떻게 되나요?">
|
||||
[app.crewai.com](https://app.crewai.com)에 가입하면 현재 요금제를 확인할 수 있습니다. 엔터프라이즈 및 Factory 가격은 문의 시 안내합니다.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Card title="CrewAI AMP 살펴보기 →" icon="arrow-right" href="https://app.crewai.com">
|
||||
|
||||
@@ -117,35 +117,23 @@ task = Task(
|
||||
|
||||
### Com Flows
|
||||
|
||||
Campos tipados como arquivo (`File`, `ImageFile`, `PDFFile`) no esquema de estado do seu flow servem como sinal para a interface da Plataforma. Quando implantado, esses campos são renderizados como zonas de upload de arquivos. Arquivos também podem ser passados via `input_files` na API.
|
||||
Passe arquivos para flows, que automaticamente herdam para crews:
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile
|
||||
from pydantic import BaseModel
|
||||
from crewai_files import ImageFile
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform UI
|
||||
cover_image: ImageFile # Image-specific dropzone
|
||||
title: str = ""
|
||||
|
||||
class AnalysisFlow(Flow[MyState]):
|
||||
class AnalysisFlow(Flow):
|
||||
@start()
|
||||
def analyze(self):
|
||||
# Files are automatically populated in state
|
||||
content = self.state.document.read()
|
||||
return self.analysis_crew.kickoff()
|
||||
|
||||
flow = AnalysisFlow()
|
||||
result = flow.kickoff(
|
||||
input_files={"document": File(source="report.pdf")}
|
||||
input_files={"image": ImageFile(source="data.png")}
|
||||
)
|
||||
```
|
||||
|
||||
<Note type="info" title="Integração com a Plataforma CrewAI">
|
||||
Quando implantado na Plataforma CrewAI, campos tipados como arquivo como `ImageFile`, `PDFFile` e outros no estado do seu flow recebem automaticamente uma interface de upload de arquivos. Os usuários podem arrastar e soltar arquivos diretamente na interface da Plataforma. Os arquivos são armazenados de forma segura e passados para os agentes usando otimizações específicas do provedor (base64 inline, APIs de upload de arquivo ou referências por URL dependendo do provedor). Para exemplos de uso da API, consulte [Entradas de Arquivos em Flows](/pt-BR/concepts/flows#entradas-de-arquivos).
|
||||
</Note>
|
||||
|
||||
### Com Agentes Standalone
|
||||
|
||||
Passe arquivos diretamente no kickoff do agente:
|
||||
|
||||
@@ -173,90 +173,6 @@ Cada estado nos flows do CrewAI recebe automaticamente um identificador único (
|
||||
|
||||
Ao oferecer as duas opções de gerenciamento de estado, o CrewAI Flows permite que desenvolvedores criem fluxos de IA que sejam ao mesmo tempo flexíveis e robustos, atendendo a uma ampla variedade de requisitos de aplicação.
|
||||
|
||||
### Entradas de Arquivos
|
||||
|
||||
Ao usar estado estruturado, você pode incluir campos tipados como arquivo usando classes do `crewai-files`. Campos tipados como arquivo no estado do seu flow servem como sinal para a Plataforma — eles são renderizados automaticamente como zonas de upload de arquivos na aba Run da interface e são preenchidos quando arquivos são enviados via Plataforma ou passados via `input_files` na API.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from crewai_files import File, ImageFile, PDFFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MyState(BaseModel):
|
||||
document: File # Renders as file dropzone in Platform
|
||||
title: str = ""
|
||||
|
||||
class MyFlow(Flow[MyState]):
|
||||
@start()
|
||||
def process(self):
|
||||
# File object is automatically populated in state
|
||||
# when uploaded via Platform UI or passed via API
|
||||
content = self.state.document.read()
|
||||
print(f"Processing {self.state.title}: {len(content)} bytes")
|
||||
return content
|
||||
```
|
||||
|
||||
Quando implantado na **Plataforma CrewAI**, campos tipados como arquivo (`File`, `ImageFile`, `PDFFile` do `crewai-files`) são renderizados automaticamente como zonas de upload de arquivos na interface. Os usuários podem arrastar e soltar arquivos, que são então preenchidos no estado do seu flow.
|
||||
|
||||
**Iniciando com arquivos via API:**
|
||||
|
||||
O endpoint `/kickoff` detecta automaticamente o formato da requisição:
|
||||
- **Corpo JSON** → kickoff normal
|
||||
- **multipart/form-data** → upload de arquivo + kickoff
|
||||
|
||||
Usuários da API também podem passar strings de URL diretamente para campos tipados como arquivo — o Pydantic as converte automaticamente.
|
||||
|
||||
### Uso da API
|
||||
|
||||
#### Opção 1: Kickoff multipart (recomendado)
|
||||
|
||||
Envie arquivos diretamente com a requisição de kickoff:
|
||||
|
||||
```bash
|
||||
# With files (multipart) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"company_name": "Einstein"}' \
|
||||
-F 'cover_image=@/path/to/photo.jpg'
|
||||
```
|
||||
|
||||
Os arquivos são armazenados automaticamente e convertidos em objetos `FileInput`. O agente recebe o arquivo com otimização específica do provedor (base64 inline, API de upload de arquivo ou referência por URL dependendo do provedor LLM).
|
||||
|
||||
#### Opção 2: Kickoff JSON (sem arquivos)
|
||||
|
||||
```bash
|
||||
# Without files (JSON) — same endpoint
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}}'
|
||||
```
|
||||
|
||||
#### Opção 3: Upload separado + kickoff
|
||||
|
||||
Esta é uma alternativa ao upload multipart quando você precisa fazer upload dos arquivos separadamente da requisição de kickoff. Faça o upload dos arquivos primeiro e depois referencie-os por URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/photo.jpg' \
|
||||
-F 'field_name=cover_image'
|
||||
# Returns: {"url": "https://...", "field_name": "cover_image"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"company_name": "Einstein"}, "input_files": {"cover_image": "https://..."}}'
|
||||
```
|
||||
|
||||
Consulte a documentação da API da Plataforma para detalhes completos do endpoint `/files`.
|
||||
|
||||
#### Na Plataforma CrewAI
|
||||
|
||||
Ao usar a interface da Plataforma, campos tipados como arquivo são renderizados automaticamente como zonas de arrastar e soltar para upload. Nenhuma chamada de API é necessária — basta soltar o arquivo e clicar em Executar.
|
||||
|
||||
## Persistência de Flow
|
||||
|
||||
O decorador @persist permite a persistência automática do estado nos flows do CrewAI, garantindo que você mantenha o estado do flow entre reinicializações ou execuções diferentes do workflow. Esse decorador pode ser aplicado tanto ao nível de classe, quanto ao nível de método, oferecendo flexibilidade sobre como gerenciar a persistência do estado.
|
||||
|
||||
@@ -86,60 +86,6 @@ curl -H "Authorization: Bearer YOUR_CREW_TOKEN" https://your-crew-url.crewai.com
|
||||
|
||||
Seu bearer token está disponível na aba Status na página de detalhes do seu crew.
|
||||
|
||||
## Upload de Arquivos
|
||||
|
||||
Quando seu crew ou flow inclui campos de estado tipados como arquivo (usando `ImageFile`, `PDFFile` ou `File` do `crewai-files`), esses campos são renderizados automaticamente como zonas de upload de arquivos na aba Run da interface. Os usuários podem arrastar e soltar arquivos diretamente, e a Plataforma gerencia o armazenamento e entrega para seus agentes.
|
||||
|
||||
### Kickoff Multipart (Recomendado)
|
||||
|
||||
Envie arquivos diretamente com a requisição de kickoff usando `multipart/form-data`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'inputs={"title": "Report"}' \
|
||||
-F 'document=@/path/to/file.pdf'
|
||||
```
|
||||
|
||||
Os arquivos são armazenados automaticamente e convertidos em objetos de arquivo. O agente recebe o arquivo com otimização específica do provedor (base64 inline, API de upload de arquivo ou referência por URL dependendo do provedor LLM).
|
||||
|
||||
### Kickoff JSON com URLs de Arquivos
|
||||
|
||||
Se você já tem arquivos hospedados em URLs, passe-os via `input_files`:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"inputs": {"title": "Report"},
|
||||
"input_files": {"document": "https://example.com/file.pdf"}
|
||||
}'
|
||||
```
|
||||
|
||||
### Upload Separado + Kickoff
|
||||
|
||||
Faça upload dos arquivos primeiro e depois referencie-os por URL:
|
||||
|
||||
```bash
|
||||
# Step 1: Upload
|
||||
curl -X POST https://your-deployment.crewai.com/files \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-F 'file=@/path/to/file.pdf' \
|
||||
-F 'field_name=document'
|
||||
# Returns: {"url": "https://...", "field_name": "document"}
|
||||
|
||||
# Step 2: Kickoff with URL
|
||||
curl -X POST https://your-deployment.crewai.com/kickoff \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"inputs": {"title": "Report"}, "input_files": {"document": "https://..."}}'
|
||||
```
|
||||
|
||||
<Note type="info">
|
||||
O upload de arquivos funciona da mesma forma tanto para crews quanto para flows. Defina campos tipados como arquivo no seu esquema de estado, e a interface da Plataforma e a API tratarão os uploads automaticamente.
|
||||
</Note>
|
||||
|
||||
### Verificando o Status do Crew
|
||||
|
||||
Antes de executar operações, você pode verificar se seu crew está funcionando corretamente:
|
||||
|
||||
@@ -207,6 +207,9 @@ O CrewAI AMP foi feito para equipes em produção. Além da implantação, você
|
||||
- **Factory (self-hosted)** — na sua infraestrutura para controle total dos dados
|
||||
- **Híbrido** — combine nuvem e self-hosted conforme a sensibilidade dos dados
|
||||
</Accordion>
|
||||
<Accordion title="Como funciona o preço?">
|
||||
Cadastre-se em [app.crewai.com](https://app.crewai.com) para ver os planos atuais. Preços enterprise e Factory sob consulta.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Card title="Conheça o CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
|
||||
|
||||
@@ -112,7 +112,7 @@ github = [
|
||||
]
|
||||
rag = [
|
||||
"python-docx>=1.1.0",
|
||||
"lxml>=6.1.0,<7", # 6.1.0+ required for GHSA-vfmq-68hx-4jfw (XXE in iterparse)
|
||||
"lxml>=5.3.0,<5.4.0", # Pin to avoid etree import issues in 5.4.0
|
||||
]
|
||||
xml = [
|
||||
"unstructured[local-inference, all-docs]>=0.17.2"
|
||||
|
||||
@@ -78,7 +78,8 @@ from crewai.knowledge.knowledge import Knowledge
|
||||
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.config import MCPServerConfig
|
||||
from crewai.mcp import MCPServerConfig
|
||||
from crewai.mcp.tool_resolver import MCPToolResolver
|
||||
from crewai.rag.embeddings.types import EmbedderConfig
|
||||
from crewai.security.fingerprint import Fingerprint
|
||||
from crewai.skills.loader import activate_skill, discover_skills
|
||||
@@ -118,7 +119,6 @@ if TYPE_CHECKING:
|
||||
|
||||
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
|
||||
@@ -1120,8 +1120,6 @@ class Agent(BaseAgent):
|
||||
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)
|
||||
|
||||
|
||||
@@ -6,20 +6,112 @@ This module provides the event infrastructure that allows users to:
|
||||
- Build custom logging and analytics
|
||||
- Extend CrewAI with custom event handlers
|
||||
- Declare handler dependencies for ordered execution
|
||||
|
||||
Event type classes are lazy-loaded on first access to avoid importing
|
||||
~12 Pydantic model modules (and their transitive deps) at package init time.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from crewai.events.base_event_listener import BaseEventListener
|
||||
from crewai.events.depends import Depends
|
||||
from crewai.events.event_bus import crewai_event_bus
|
||||
from crewai.events.handler_graph import CircularDependencyError
|
||||
from crewai.events.types.crew_events import (
|
||||
CrewKickoffCompletedEvent,
|
||||
CrewKickoffFailedEvent,
|
||||
CrewKickoffStartedEvent,
|
||||
CrewTestCompletedEvent,
|
||||
CrewTestFailedEvent,
|
||||
CrewTestResultEvent,
|
||||
CrewTestStartedEvent,
|
||||
CrewTrainCompletedEvent,
|
||||
CrewTrainFailedEvent,
|
||||
CrewTrainStartedEvent,
|
||||
)
|
||||
from crewai.events.types.flow_events import (
|
||||
FlowCreatedEvent,
|
||||
FlowEvent,
|
||||
FlowFinishedEvent,
|
||||
FlowPlotEvent,
|
||||
FlowStartedEvent,
|
||||
HumanFeedbackReceivedEvent,
|
||||
HumanFeedbackRequestedEvent,
|
||||
MethodExecutionFailedEvent,
|
||||
MethodExecutionFinishedEvent,
|
||||
MethodExecutionStartedEvent,
|
||||
)
|
||||
from crewai.events.types.knowledge_events import (
|
||||
KnowledgeQueryCompletedEvent,
|
||||
KnowledgeQueryFailedEvent,
|
||||
KnowledgeQueryStartedEvent,
|
||||
KnowledgeRetrievalCompletedEvent,
|
||||
KnowledgeRetrievalStartedEvent,
|
||||
KnowledgeSearchQueryFailedEvent,
|
||||
)
|
||||
from crewai.events.types.llm_events import (
|
||||
LLMCallCompletedEvent,
|
||||
LLMCallFailedEvent,
|
||||
LLMCallStartedEvent,
|
||||
LLMStreamChunkEvent,
|
||||
)
|
||||
from crewai.events.types.llm_guardrail_events import (
|
||||
LLMGuardrailCompletedEvent,
|
||||
LLMGuardrailStartedEvent,
|
||||
)
|
||||
from crewai.events.types.logging_events import (
|
||||
AgentLogsExecutionEvent,
|
||||
AgentLogsStartedEvent,
|
||||
)
|
||||
from crewai.events.types.mcp_events import (
|
||||
MCPConfigFetchFailedEvent,
|
||||
MCPConnectionCompletedEvent,
|
||||
MCPConnectionFailedEvent,
|
||||
MCPConnectionStartedEvent,
|
||||
MCPToolExecutionCompletedEvent,
|
||||
MCPToolExecutionFailedEvent,
|
||||
MCPToolExecutionStartedEvent,
|
||||
)
|
||||
from crewai.events.types.memory_events import (
|
||||
MemoryQueryCompletedEvent,
|
||||
MemoryQueryFailedEvent,
|
||||
MemoryQueryStartedEvent,
|
||||
MemoryRetrievalCompletedEvent,
|
||||
MemoryRetrievalFailedEvent,
|
||||
MemoryRetrievalStartedEvent,
|
||||
MemorySaveCompletedEvent,
|
||||
MemorySaveFailedEvent,
|
||||
MemorySaveStartedEvent,
|
||||
)
|
||||
from crewai.events.types.reasoning_events import (
|
||||
AgentReasoningCompletedEvent,
|
||||
AgentReasoningFailedEvent,
|
||||
AgentReasoningStartedEvent,
|
||||
ReasoningEvent,
|
||||
)
|
||||
from crewai.events.types.skill_events import (
|
||||
SkillActivatedEvent,
|
||||
SkillDiscoveryCompletedEvent,
|
||||
SkillDiscoveryStartedEvent,
|
||||
SkillEvent,
|
||||
SkillLoadFailedEvent,
|
||||
SkillLoadedEvent,
|
||||
)
|
||||
from crewai.events.types.task_events import (
|
||||
TaskCompletedEvent,
|
||||
TaskEvaluationEvent,
|
||||
TaskFailedEvent,
|
||||
TaskStartedEvent,
|
||||
)
|
||||
from crewai.events.types.tool_usage_events import (
|
||||
ToolExecutionErrorEvent,
|
||||
ToolSelectionErrorEvent,
|
||||
ToolUsageErrorEvent,
|
||||
ToolUsageEvent,
|
||||
ToolUsageFinishedEvent,
|
||||
ToolUsageStartedEvent,
|
||||
ToolValidateInputErrorEvent,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from crewai.events.types.agent_events import (
|
||||
@@ -33,223 +125,6 @@ if TYPE_CHECKING:
|
||||
LiteAgentExecutionErrorEvent,
|
||||
LiteAgentExecutionStartedEvent,
|
||||
)
|
||||
from crewai.events.types.crew_events import (
|
||||
CrewKickoffCompletedEvent,
|
||||
CrewKickoffFailedEvent,
|
||||
CrewKickoffStartedEvent,
|
||||
CrewTestCompletedEvent,
|
||||
CrewTestFailedEvent,
|
||||
CrewTestResultEvent,
|
||||
CrewTestStartedEvent,
|
||||
CrewTrainCompletedEvent,
|
||||
CrewTrainFailedEvent,
|
||||
CrewTrainStartedEvent,
|
||||
)
|
||||
from crewai.events.types.flow_events import (
|
||||
FlowCreatedEvent,
|
||||
FlowEvent,
|
||||
FlowFinishedEvent,
|
||||
FlowPlotEvent,
|
||||
FlowStartedEvent,
|
||||
HumanFeedbackReceivedEvent,
|
||||
HumanFeedbackRequestedEvent,
|
||||
MethodExecutionFailedEvent,
|
||||
MethodExecutionFinishedEvent,
|
||||
MethodExecutionStartedEvent,
|
||||
)
|
||||
from crewai.events.types.knowledge_events import (
|
||||
KnowledgeQueryCompletedEvent,
|
||||
KnowledgeQueryFailedEvent,
|
||||
KnowledgeQueryStartedEvent,
|
||||
KnowledgeRetrievalCompletedEvent,
|
||||
KnowledgeRetrievalStartedEvent,
|
||||
KnowledgeSearchQueryFailedEvent,
|
||||
)
|
||||
from crewai.events.types.llm_events import (
|
||||
LLMCallCompletedEvent,
|
||||
LLMCallFailedEvent,
|
||||
LLMCallStartedEvent,
|
||||
LLMStreamChunkEvent,
|
||||
)
|
||||
from crewai.events.types.llm_guardrail_events import (
|
||||
LLMGuardrailCompletedEvent,
|
||||
LLMGuardrailStartedEvent,
|
||||
)
|
||||
from crewai.events.types.logging_events import (
|
||||
AgentLogsExecutionEvent,
|
||||
AgentLogsStartedEvent,
|
||||
)
|
||||
from crewai.events.types.mcp_events import (
|
||||
MCPConfigFetchFailedEvent,
|
||||
MCPConnectionCompletedEvent,
|
||||
MCPConnectionFailedEvent,
|
||||
MCPConnectionStartedEvent,
|
||||
MCPToolExecutionCompletedEvent,
|
||||
MCPToolExecutionFailedEvent,
|
||||
MCPToolExecutionStartedEvent,
|
||||
)
|
||||
from crewai.events.types.memory_events import (
|
||||
MemoryQueryCompletedEvent,
|
||||
MemoryQueryFailedEvent,
|
||||
MemoryQueryStartedEvent,
|
||||
MemoryRetrievalCompletedEvent,
|
||||
MemoryRetrievalFailedEvent,
|
||||
MemoryRetrievalStartedEvent,
|
||||
MemorySaveCompletedEvent,
|
||||
MemorySaveFailedEvent,
|
||||
MemorySaveStartedEvent,
|
||||
)
|
||||
from crewai.events.types.reasoning_events import (
|
||||
AgentReasoningCompletedEvent,
|
||||
AgentReasoningFailedEvent,
|
||||
AgentReasoningStartedEvent,
|
||||
ReasoningEvent,
|
||||
)
|
||||
from crewai.events.types.skill_events import (
|
||||
SkillActivatedEvent,
|
||||
SkillDiscoveryCompletedEvent,
|
||||
SkillDiscoveryStartedEvent,
|
||||
SkillEvent,
|
||||
SkillLoadFailedEvent,
|
||||
SkillLoadedEvent,
|
||||
)
|
||||
from crewai.events.types.task_events import (
|
||||
TaskCompletedEvent,
|
||||
TaskEvaluationEvent,
|
||||
TaskFailedEvent,
|
||||
TaskStartedEvent,
|
||||
)
|
||||
from crewai.events.types.tool_usage_events import (
|
||||
ToolExecutionErrorEvent,
|
||||
ToolSelectionErrorEvent,
|
||||
ToolUsageErrorEvent,
|
||||
ToolUsageEvent,
|
||||
ToolUsageFinishedEvent,
|
||||
ToolUsageStartedEvent,
|
||||
ToolValidateInputErrorEvent,
|
||||
)
|
||||
|
||||
# Map every event class name → its module path for lazy loading
|
||||
_LAZY_EVENT_MAPPING: dict[str, str] = {
|
||||
# agent_events
|
||||
"AgentEvaluationCompletedEvent": "crewai.events.types.agent_events",
|
||||
"AgentEvaluationFailedEvent": "crewai.events.types.agent_events",
|
||||
"AgentEvaluationStartedEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionCompletedEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionErrorEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionStartedEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionCompletedEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionErrorEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionStartedEvent": "crewai.events.types.agent_events",
|
||||
# crew_events
|
||||
"CrewKickoffCompletedEvent": "crewai.events.types.crew_events",
|
||||
"CrewKickoffFailedEvent": "crewai.events.types.crew_events",
|
||||
"CrewKickoffStartedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTestCompletedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTestFailedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTestResultEvent": "crewai.events.types.crew_events",
|
||||
"CrewTestStartedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTrainCompletedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTrainFailedEvent": "crewai.events.types.crew_events",
|
||||
"CrewTrainStartedEvent": "crewai.events.types.crew_events",
|
||||
# flow_events
|
||||
"FlowCreatedEvent": "crewai.events.types.flow_events",
|
||||
"FlowEvent": "crewai.events.types.flow_events",
|
||||
"FlowFinishedEvent": "crewai.events.types.flow_events",
|
||||
"FlowPlotEvent": "crewai.events.types.flow_events",
|
||||
"FlowStartedEvent": "crewai.events.types.flow_events",
|
||||
"HumanFeedbackReceivedEvent": "crewai.events.types.flow_events",
|
||||
"HumanFeedbackRequestedEvent": "crewai.events.types.flow_events",
|
||||
"MethodExecutionFailedEvent": "crewai.events.types.flow_events",
|
||||
"MethodExecutionFinishedEvent": "crewai.events.types.flow_events",
|
||||
"MethodExecutionStartedEvent": "crewai.events.types.flow_events",
|
||||
# knowledge_events
|
||||
"KnowledgeQueryCompletedEvent": "crewai.events.types.knowledge_events",
|
||||
"KnowledgeQueryFailedEvent": "crewai.events.types.knowledge_events",
|
||||
"KnowledgeQueryStartedEvent": "crewai.events.types.knowledge_events",
|
||||
"KnowledgeRetrievalCompletedEvent": "crewai.events.types.knowledge_events",
|
||||
"KnowledgeRetrievalStartedEvent": "crewai.events.types.knowledge_events",
|
||||
"KnowledgeSearchQueryFailedEvent": "crewai.events.types.knowledge_events",
|
||||
# llm_events
|
||||
"LLMCallCompletedEvent": "crewai.events.types.llm_events",
|
||||
"LLMCallFailedEvent": "crewai.events.types.llm_events",
|
||||
"LLMCallStartedEvent": "crewai.events.types.llm_events",
|
||||
"LLMStreamChunkEvent": "crewai.events.types.llm_events",
|
||||
# llm_guardrail_events
|
||||
"LLMGuardrailCompletedEvent": "crewai.events.types.llm_guardrail_events",
|
||||
"LLMGuardrailStartedEvent": "crewai.events.types.llm_guardrail_events",
|
||||
# logging_events
|
||||
"AgentLogsExecutionEvent": "crewai.events.types.logging_events",
|
||||
"AgentLogsStartedEvent": "crewai.events.types.logging_events",
|
||||
# mcp_events
|
||||
"MCPConfigFetchFailedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPConnectionCompletedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPConnectionFailedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPConnectionStartedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPToolExecutionCompletedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPToolExecutionFailedEvent": "crewai.events.types.mcp_events",
|
||||
"MCPToolExecutionStartedEvent": "crewai.events.types.mcp_events",
|
||||
# memory_events
|
||||
"MemoryQueryCompletedEvent": "crewai.events.types.memory_events",
|
||||
"MemoryQueryFailedEvent": "crewai.events.types.memory_events",
|
||||
"MemoryQueryStartedEvent": "crewai.events.types.memory_events",
|
||||
"MemoryRetrievalCompletedEvent": "crewai.events.types.memory_events",
|
||||
"MemoryRetrievalFailedEvent": "crewai.events.types.memory_events",
|
||||
"MemoryRetrievalStartedEvent": "crewai.events.types.memory_events",
|
||||
"MemorySaveCompletedEvent": "crewai.events.types.memory_events",
|
||||
"MemorySaveFailedEvent": "crewai.events.types.memory_events",
|
||||
"MemorySaveStartedEvent": "crewai.events.types.memory_events",
|
||||
# reasoning_events
|
||||
"AgentReasoningCompletedEvent": "crewai.events.types.reasoning_events",
|
||||
"AgentReasoningFailedEvent": "crewai.events.types.reasoning_events",
|
||||
"AgentReasoningStartedEvent": "crewai.events.types.reasoning_events",
|
||||
"ReasoningEvent": "crewai.events.types.reasoning_events",
|
||||
# skill_events
|
||||
"SkillActivatedEvent": "crewai.events.types.skill_events",
|
||||
"SkillDiscoveryCompletedEvent": "crewai.events.types.skill_events",
|
||||
"SkillDiscoveryStartedEvent": "crewai.events.types.skill_events",
|
||||
"SkillEvent": "crewai.events.types.skill_events",
|
||||
"SkillLoadFailedEvent": "crewai.events.types.skill_events",
|
||||
"SkillLoadedEvent": "crewai.events.types.skill_events",
|
||||
# task_events
|
||||
"TaskCompletedEvent": "crewai.events.types.task_events",
|
||||
"TaskEvaluationEvent": "crewai.events.types.task_events",
|
||||
"TaskFailedEvent": "crewai.events.types.task_events",
|
||||
"TaskStartedEvent": "crewai.events.types.task_events",
|
||||
# tool_usage_events
|
||||
"ToolExecutionErrorEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolSelectionErrorEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolUsageErrorEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolUsageEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolUsageFinishedEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolUsageStartedEvent": "crewai.events.types.tool_usage_events",
|
||||
"ToolValidateInputErrorEvent": "crewai.events.types.tool_usage_events",
|
||||
}
|
||||
|
||||
_extension_exports: dict[str, Any] = {}
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
"""Lazy import for event types and registered extensions."""
|
||||
if name in _LAZY_EVENT_MAPPING:
|
||||
module_path = _LAZY_EVENT_MAPPING[name]
|
||||
module = importlib.import_module(module_path)
|
||||
val = getattr(module, name)
|
||||
globals()[name] = val # cache for subsequent access
|
||||
return val
|
||||
|
||||
if name in _extension_exports:
|
||||
value = _extension_exports[name]
|
||||
if isinstance(value, str):
|
||||
module_path, _, attr_name = value.rpartition(".")
|
||||
if module_path:
|
||||
module = importlib.import_module(module_path)
|
||||
return getattr(module, attr_name)
|
||||
return importlib.import_module(value)
|
||||
return value
|
||||
|
||||
msg = f"module {__name__!r} has no attribute {name!r}"
|
||||
raise AttributeError(msg)
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -339,3 +214,42 @@ __all__ = [
|
||||
"_extension_exports",
|
||||
"crewai_event_bus",
|
||||
]
|
||||
|
||||
_AGENT_EVENT_MAPPING = {
|
||||
"AgentEvaluationCompletedEvent": "crewai.events.types.agent_events",
|
||||
"AgentEvaluationFailedEvent": "crewai.events.types.agent_events",
|
||||
"AgentEvaluationStartedEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionCompletedEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionErrorEvent": "crewai.events.types.agent_events",
|
||||
"AgentExecutionStartedEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionCompletedEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionErrorEvent": "crewai.events.types.agent_events",
|
||||
"LiteAgentExecutionStartedEvent": "crewai.events.types.agent_events",
|
||||
}
|
||||
|
||||
_extension_exports: dict[str, Any] = {}
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
"""Lazy import for agent events and registered extensions."""
|
||||
if name in _AGENT_EVENT_MAPPING:
|
||||
import importlib
|
||||
|
||||
module_path = _AGENT_EVENT_MAPPING[name]
|
||||
module = importlib.import_module(module_path)
|
||||
return getattr(module, name)
|
||||
|
||||
if name in _extension_exports:
|
||||
import importlib
|
||||
|
||||
value = _extension_exports[name]
|
||||
if isinstance(value, str):
|
||||
module_path, _, attr_name = value.rpartition(".")
|
||||
if module_path:
|
||||
module = importlib.import_module(module_path)
|
||||
return getattr(module, attr_name)
|
||||
return importlib.import_module(value)
|
||||
return value
|
||||
|
||||
msg = f"module {__name__!r} has no attribute {name!r}"
|
||||
raise AttributeError(msg)
|
||||
|
||||
@@ -2,17 +2,9 @@
|
||||
|
||||
This module provides native MCP client functionality, allowing CrewAI agents
|
||||
to connect to any MCP-compliant server using various transport types.
|
||||
|
||||
Heavy imports (MCPClient, MCPToolResolver, BaseTransport, TransportType) are
|
||||
lazy-loaded on first access to avoid pulling in the ``mcp`` SDK (~400ms)
|
||||
when only lightweight config/filter types are needed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from crewai.mcp.client import MCPClient
|
||||
from crewai.mcp.config import (
|
||||
MCPServerConfig,
|
||||
MCPServerHTTP,
|
||||
@@ -26,28 +18,8 @@ from crewai.mcp.filters import (
|
||||
create_dynamic_tool_filter,
|
||||
create_static_tool_filter,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from crewai.mcp.client import MCPClient
|
||||
from crewai.mcp.tool_resolver import MCPToolResolver
|
||||
from crewai.mcp.transports.base import BaseTransport, TransportType
|
||||
|
||||
_LAZY: dict[str, tuple[str, str]] = {
|
||||
"MCPClient": ("crewai.mcp.client", "MCPClient"),
|
||||
"MCPToolResolver": ("crewai.mcp.tool_resolver", "MCPToolResolver"),
|
||||
"BaseTransport": ("crewai.mcp.transports.base", "BaseTransport"),
|
||||
"TransportType": ("crewai.mcp.transports.base", "TransportType"),
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if name in _LAZY:
|
||||
mod_path, attr = _LAZY[name]
|
||||
mod = importlib.import_module(mod_path)
|
||||
val = getattr(mod, attr)
|
||||
globals()[name] = val # cache for subsequent access
|
||||
return val
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
from crewai.mcp.tool_resolver import MCPToolResolver
|
||||
from crewai.mcp.transports.base import BaseTransport, TransportType
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -164,7 +164,7 @@ info = "Commits must follow Conventional Commits 1.0.0."
|
||||
[tool.uv]
|
||||
# Pinned to include the security patch releases (authlib 1.6.11,
|
||||
# langchain-text-splitters 1.1.2) uploaded on 2026-04-16.
|
||||
exclude-newer = "2026-04-22"
|
||||
exclude-newer = "2026-04-17"
|
||||
|
||||
# composio-core pins rich<14 but textual requires rich>=14.
|
||||
# onnxruntime 1.24+ dropped Python 3.10 wheels; cap it so qdrant[fastembed] resolves on 3.10.
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Benchmark `import crewai` cold start time.
|
||||
|
||||
Usage:
|
||||
python scripts/benchmark_import_time.py [--runs N] [--json]
|
||||
|
||||
Spawns a fresh Python subprocess for each run to ensure cold imports.
|
||||
Prints median, mean, min, max across all runs.
|
||||
With --json, outputs machine-readable results for CI.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
IMPORT_SCRIPT = "import time; t0 = time.perf_counter(); import crewai; print(time.perf_counter() - t0)"
|
||||
|
||||
|
||||
def measure_import(python: str = sys.executable) -> float:
|
||||
"""Run a single cold-import measurement in a subprocess."""
|
||||
result = subprocess.run(
|
||||
[python, "-c", IMPORT_SCRIPT],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env={"PATH": "", "VIRTUAL_ENV": "", "PYTHONPATH": ""},
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Import failed: {result.stderr.strip()}")
|
||||
return float(result.stdout.strip())
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Benchmark crewai import time")
|
||||
parser.add_argument("--runs", type=int, default=5, help="Number of runs (default: 5)")
|
||||
parser.add_argument("--json", action="store_true", help="Output JSON for CI")
|
||||
parser.add_argument("--threshold", type=float, default=None,
|
||||
help="Fail if median exceeds this value (seconds)")
|
||||
args = parser.parse_args()
|
||||
|
||||
times = []
|
||||
for i in range(args.runs):
|
||||
t = measure_import()
|
||||
times.append(t)
|
||||
if not args.json:
|
||||
print(f" Run {i + 1}: {t:.3f}s")
|
||||
|
||||
median = statistics.median(times)
|
||||
mean = statistics.mean(times)
|
||||
stdev = statistics.stdev(times) if len(times) > 1 else 0.0
|
||||
|
||||
result = {
|
||||
"runs": args.runs,
|
||||
"median_s": round(median, 3),
|
||||
"mean_s": round(mean, 3),
|
||||
"stdev_s": round(stdev, 3),
|
||||
"min_s": round(min(times), 3),
|
||||
"max_s": round(max(times), 3),
|
||||
}
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(result))
|
||||
else:
|
||||
print(f"\n Median: {median:.3f}s")
|
||||
print(f" Mean: {mean:.3f}s ± {stdev:.3f}s")
|
||||
print(f" Range: {min(times):.3f}s – {max(times):.3f}s")
|
||||
|
||||
if args.threshold and median > args.threshold:
|
||||
print(f"\n ❌ FAILED: median {median:.3f}s exceeds threshold {args.threshold:.3f}s")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user