mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-08-03 21:11:44 +00:00
Compare commits
1 Commits
fix/hitl-w
...
docs/deplo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7b7a1b3d2 |
57
.github/workflows/vulnerability-scan.yml
vendored
57
.github/workflows/vulnerability-scan.yml
vendored
@@ -47,20 +47,48 @@ jobs:
|
||||
|
||||
- name: Run pip-audit
|
||||
run: |
|
||||
pip_audit_args=(
|
||||
--desc
|
||||
--aliases
|
||||
--skip-editable
|
||||
--format json
|
||||
--output pip-audit-report.json
|
||||
--ignore-vuln CVE-2026-27448 # pyOpenSSL: fixes require 26.0.0, blocked by snowflake-connector-python 3.x.
|
||||
--ignore-vuln CVE-2026-27459 # pyOpenSSL: same constraint as CVE-2026-27448.
|
||||
--ignore-vuln PYSEC-2026-597 # nltk 3.9.4 (CVE-2026-12243): no fix available, transitive through crewai-tools[xml] -> unstructured.
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47 # torch 2.12.0 (CVE-2025-3000): local-only memory corruption in torch.jit.script; no fix available.
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c # chromadb 1.1.1 (CVE-2026-45829): pre-auth RCE in the HTTP server; no fix available.
|
||||
--ignore-vuln GHSA-xf7x-x43h-rpqh # json-repair 0.25.3: the affected schema module is absent, and CrewAI does not pass schemas.
|
||||
)
|
||||
uv run pip-audit "${pip_audit_args[@]}"
|
||||
uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json \
|
||||
--ignore-vuln PYSEC-2024-277 \
|
||||
--ignore-vuln PYSEC-2026-89 \
|
||||
--ignore-vuln PYSEC-2026-97 \
|
||||
--ignore-vuln PYSEC-2025-148 \
|
||||
--ignore-vuln PYSEC-2025-183 \
|
||||
--ignore-vuln PYSEC-2025-189 \
|
||||
--ignore-vuln PYSEC-2025-190 \
|
||||
--ignore-vuln PYSEC-2025-191 \
|
||||
--ignore-vuln PYSEC-2025-192 \
|
||||
--ignore-vuln PYSEC-2025-193 \
|
||||
--ignore-vuln PYSEC-2025-194 \
|
||||
--ignore-vuln PYSEC-2025-195 \
|
||||
--ignore-vuln PYSEC-2025-196 \
|
||||
--ignore-vuln PYSEC-2025-197 \
|
||||
--ignore-vuln PYSEC-2025-210 \
|
||||
--ignore-vuln PYSEC-2026-139 \
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47 \
|
||||
--ignore-vuln PYSEC-2025-211 \
|
||||
--ignore-vuln PYSEC-2025-212 \
|
||||
--ignore-vuln PYSEC-2025-213 \
|
||||
--ignore-vuln PYSEC-2025-214 \
|
||||
--ignore-vuln PYSEC-2025-215 \
|
||||
--ignore-vuln PYSEC-2025-216 \
|
||||
--ignore-vuln PYSEC-2025-217 \
|
||||
--ignore-vuln PYSEC-2025-218 \
|
||||
--ignore-vuln PYSEC-2026-597 \
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c
|
||||
# Ignored CVEs:
|
||||
# PYSEC-2024-277 - joblib 1.5.3: disputed; NumpyArrayWrapper only used with trusted caches
|
||||
# PYSEC-2026-89 - markdown 3.10.2: DoS via malformed HTML; fix 3.8.1 — already past, advisory range is stale
|
||||
# PYSEC-2026-97 - nltk 3.9.4: arbitrary file read in filestring(); no fix available
|
||||
# PYSEC-2026-597 - nltk 3.9.4 (CVE-2026-12243): path traversal via _UNSAFE_NO_PROTOCOL_RE bypass (incomplete fix of nltk#3504); 3.9.4 is the latest release, no fix available
|
||||
# PYSEC-2025-148 - onnx 1.21.0: path traversal in save_external_data; no fix available
|
||||
# PYSEC-2025-183 - pyjwt 2.12.1: disputed weak-encryption claim; key length is application-chosen
|
||||
# PYSEC-2025-189..197 - torch 2.11.0: memory-corruption/DoS in functions only reachable via untrusted models; no fix available
|
||||
# PYSEC-2025-210, PYSEC-2026-139 - torch 2.11.0: profiler/deserialization issues; no fix available
|
||||
# GHSA-rrmf-rvhw-rf47 - torch 2.11.0 (CVE-2025-3000, alias of PYSEC-2025-194): memory corruption in torch.jit.script, CVSS 1.9, local-only; affected <=2.12.0, no fix available. pip-audit reports it under the GHSA id so the PYSEC ignore above does not catch it.
|
||||
# PYSEC-2025-211..218 - transformers 5.5.4: deserialization/code injection via malicious model checkpoints; no fix available
|
||||
# GHSA-f4j7-r4q5-qw2c - chromadb 1.1.1 (CVE-2026-45829): pre-auth RCE via /api/v2/tenants/{tenant}/databases/{db}/collections when trust_remote_code=true.
|
||||
# Advisory: vulnerable >=1.0.0,<=1.5.9, firstPatchedVersion=none. We only use chromadb.PersistentClient (lib/crewai/src/crewai/rag/chromadb/factory.py)
|
||||
# and chromadb.utils.embedding_functions; the chromadb HTTP server is never started, so the vulnerable route is not exposed.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Display results
|
||||
@@ -106,3 +134,4 @@ jobs:
|
||||
~/.local/share/uv
|
||||
.venv
|
||||
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
|
||||
|
||||
|
||||
117
docs/deployment-sizing.mdx
Normal file
117
docs/deployment-sizing.mdx
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: "Deployment Sizing"
|
||||
description: Choose the right deployment size for your crew workloads — and know when to scale up.
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Every CrewAI Enterprise deployment runs on a fixed resource tier called an **instance size**. The size controls how much CPU, memory, and — most importantly — how many crew runs can execute simultaneously. Choosing the wrong size is the most common cause of queue build-up, slow run starts, and OOMKilled pods.
|
||||
|
||||
This page explains what each size provides, how to read the signals that you've outgrown your current tier, and how to right-size for your workload.
|
||||
|
||||
---
|
||||
|
||||
## Instance Sizes
|
||||
|
||||
| # | Name | vCPU | Memory | Max Concurrent Runs | Storage |
|
||||
|---|------|------|--------|---------------------|---------|
|
||||
| 1 | Small | 1 | 2 GiB | 4 | 20 GiB |
|
||||
| 2 | Regular | 2 | 4 GiB | 16 | 20 GiB |
|
||||
| 3 | Large | 4 | 8 GiB | 32 | 20 GiB |
|
||||
| 4 | Extra Large | 8 | 16 GiB | 64 | 100 GiB |
|
||||
| 5 | Extra Extra Large | 16 | 32 GiB | 128 | 100 GiB |
|
||||
| 6 | Insane Large | 32 | 64 GiB | 256 | 100 GiB |
|
||||
|
||||
**vCPU** and **Memory** are the total resources allocated to the deployment (web server + workers + Redis combined).
|
||||
|
||||
**Max Concurrent Runs** is the worker concurrency limit — the number of crew runs that can be actively executing at the same time. Runs submitted beyond this limit are queued and wait for a slot to open.
|
||||
|
||||
<Note>
|
||||
Concurrency is per-deployment, not per-crew. If you have 10 crews deployed on a Small instance, all 10 share the same pool of 4 concurrent run slots.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## What "concurrent runs" actually means
|
||||
|
||||
A **concurrent run** is one active kickoff of a crew — from the moment it starts executing until it completes or errors. It does not mean the number of agents running in parallel inside a single crew (that's controlled by your crew's process type and agent configuration).
|
||||
|
||||
**Example:** A Small deployment (concurrency = 4) with 20 incoming run requests will execute 4 runs simultaneously and queue the remaining 16. Each queued run starts as soon as a slot frees up.
|
||||
|
||||
---
|
||||
|
||||
## Symptoms of an undersized deployment
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---------|-------------|
|
||||
| Runs sit in `queued` state for a long time | Concurrency limit reached — all worker slots are occupied |
|
||||
| Runs complete slowly even for simple tasks | CPU throttling — workers are competing for the same vCPU budget |
|
||||
| Pods restart with `OOMKilled` | Memory limit exceeded — reduce concurrency or upgrade size |
|
||||
| Builds fail or time out | Insufficient CPU/memory for the BuildKit image build step |
|
||||
| High p95/p99 run latency with normal p50 | Bursty traffic hitting the concurrency ceiling |
|
||||
|
||||
---
|
||||
|
||||
## How to choose a size
|
||||
|
||||
### Start with your concurrency requirement
|
||||
|
||||
Estimate the peak number of crew runs you expect to have in-flight simultaneously. Add ~25% headroom for bursts.
|
||||
|
||||
| Peak concurrent runs | Recommended size |
|
||||
|----------------------|-----------------|
|
||||
| 1–3 | Small |
|
||||
| 4–12 | Regular |
|
||||
| 13–25 | Large |
|
||||
| 26–50 | Extra Large |
|
||||
| 51–100 | Extra Extra Large |
|
||||
| 100+ | Insane Large |
|
||||
|
||||
### Factor in run duration
|
||||
|
||||
Long-running crews (minutes to hours) hold concurrency slots for the full duration. If your crews run for 10 minutes on average and you receive 30 runs per hour, you need at least `30 × (10/60) = 5` concurrent slots — Regular or above.
|
||||
|
||||
### Factor in memory per run
|
||||
|
||||
Each concurrent run consumes memory proportional to the number of agents, the size of context windows, and any in-memory data processing. If individual runs are memory-heavy (large document processing, many parallel agents), size up even if your concurrency requirement is low.
|
||||
|
||||
A rough heuristic: assume **~256 MiB per concurrent run** as a baseline, then add overhead for your specific workload. On a Small instance (2 GiB total, shared with web and Redis), you have roughly 1 GiB available for workers — enough for ~4 lightweight runs, which matches the concurrency limit.
|
||||
|
||||
---
|
||||
|
||||
## Changing your deployment size
|
||||
|
||||
Deployment size is configurable from the **Admin Panel → Deployments → [your deployment] → Instance Size**. Changes take effect on the next deployment cycle (a rolling restart of the worker pods).
|
||||
|
||||
<Warning>
|
||||
Downsizing a deployment that is actively processing runs will cause in-flight runs to be interrupted when the old pods are replaced. Schedule size changes during low-traffic windows.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Monitoring utilization
|
||||
|
||||
Use these signals to track whether your current size is appropriate:
|
||||
|
||||
```bash
|
||||
# Check current pod resource usage
|
||||
kubectl top pods
|
||||
|
||||
# Watch for OOMKilled restarts
|
||||
kubectl get pods -o wide
|
||||
kubectl describe pod <worker-pod-name> | grep -A5 "Last State"
|
||||
|
||||
# Check worker queue depth (from a web pod)
|
||||
kubectl exec -it deploy/crewai-web -- bin/rails runner \
|
||||
"puts Sidekiq::Queue.all.map { |q| \"#{q.name}: #{q.size}\" }.join(\"\\n\")"
|
||||
```
|
||||
|
||||
A consistently non-zero queue depth on the default queue is the clearest signal that you need more concurrency (a larger instance size).
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [Troubleshooting](/troubleshooting) — OOMKilled, pod restarts, build failures
|
||||
- [Factory Health & Debug](/factory-health) — health check endpoint and component status
|
||||
- [Aurora Instance Sizing](/deployment-guides/aws-workos-wharf-studio#aurora-instance-sizing) — database sizing to match your deployment tier
|
||||
6365
docs/docs.json
6365
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -4,136 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="20 يوليو 2026">
|
||||
## v1.15.5
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.5)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- مصادقة تنزيلات سجل المهارات
|
||||
|
||||
### الوثائق
|
||||
- تحديث اللقطة وسجل التغييرات للإصدار v1.15.4
|
||||
|
||||
## المساهمون
|
||||
|
||||
@vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="17 يوليو 2026">
|
||||
## v1.15.4
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.4)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- ترقية مستودع المهارات من حالة تجريبية
|
||||
|
||||
### الوثائق
|
||||
- إضافة تدفقات في وثائق الاستوديو
|
||||
|
||||
## المساهمون
|
||||
|
||||
@jessemiller, @joaomdmoura, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 يوليو 2026">
|
||||
## v1.15.3
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إضافة معلمة معرف المنظمة إلى عميل PlusAPI
|
||||
- إضافة نقاط اعتراض الخطوات وإعادة صياغة توثيق نقاط تنفيذ @on
|
||||
- توصيل نقاط اعتراض حدود التنفيذ
|
||||
- إضافة موزع اعتراض عام
|
||||
- تشغيل التدفقات التصريحية على واجهة المستخدم النصية (خيار الطرية بدون واجهة)
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- مزامنة حدث بدء التنفيذ المكتمل مع نتيجة خطاف OUTPUT
|
||||
- إصلاح سمات وكيل المستودع الفارغة
|
||||
- التأكد من أن خطافات after_llm_call لا تكسر تنفيذ الأدوات الأصلية
|
||||
- تجنب الإضافة المزدوجة لرد الدور عندما يقوم المعالج بقص التاريخ
|
||||
- جعل تخزين نتائج الأدوات اختيارياً بدلاً من أن يكون مفعلًا بشكل افتراضي
|
||||
- التوقف عن إعادة كتابة وصف الأداة المؤلف عند الإنشاء
|
||||
- كشف استخدام الرموز تحت كلا الاسمين في نتائج الوكيل والطاقم
|
||||
- الإبلاغ عن مقاييس الاستخدام لكل استدعاء في نتائج بدء التنفيذ
|
||||
- التوقف عن إعادة تشغيل نية الدور السابق عندما تعيد route_turn() قيمة غير صحيحة
|
||||
|
||||
### الوثائق
|
||||
- تحديث تجميع خطافات التنفيذ وتوثيق جميع سياقات الخطاف
|
||||
|
||||
## المساهمون
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 يوليو 2026">
|
||||
## v1.15.3a2
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a2)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح تزامن حدث انتهاء الانطلاق مع نتيجة خطاف OUTPUT
|
||||
|
||||
### الوثائق
|
||||
- تحديث لقطة الشاشة وسجل التغييرات للإصدار v1.15.3a1
|
||||
|
||||
### تحديثات التبعية
|
||||
- رفع setuptools إلى 0.83.0 لمعالجة PYSEC-2026-3447
|
||||
|
||||
## المساهمون
|
||||
|
||||
@lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 يوليو 2026">
|
||||
## v1.15.3a1
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a1)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إضافة معلمة معرف المنظمة إلى عميل PlusAPI.
|
||||
- إضافة نقاط اعتراض الخطوات وإعادة صياغة وثائق نقاط تنفيذ `@on`.
|
||||
- توصيل نقاط اعتراض حدود التنفيذ.
|
||||
- إضافة موصل عام للاعتراضات.
|
||||
- تشغيل التدفقات التصريحية على واجهة المستخدم النصية (نسخة احتياطية من الطرفية بدون واجهة).
|
||||
- تحسين عناوين URL المخصصة لـ OpenAI.
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح سمات وكيل المستودع الفارغة.
|
||||
- إصلاح نقاط `after_llm_call` لمنع كسر تنفيذ الأدوات الأصلية.
|
||||
- إيقاف الإضافة المزدوجة لرد الدور عندما يقوم المعالج بقص التاريخ.
|
||||
- جعل تخزين نتائج الأدوات اختيارياً بدلاً من أن يكون مفعلًا بشكل افتراضي.
|
||||
- إيقاف إعادة كتابة وصف الأداة المؤلف عند الإنشاء.
|
||||
- كشف استخدام الرموز تحت كلا الاسمين في نتائج الوكيل والطاقم.
|
||||
- الإبلاغ عن مقاييس الاستخدام لكل استدعاء في نتائج البداية.
|
||||
- إيقاف إعادة تشغيل نية الدور السابق عندما تعيد `route_turn()` قيمة غير صحيحة.
|
||||
- تصريف الكتابات في الذاكرة قبل أحداث البداية وإكمال التدفق.
|
||||
|
||||
### الوثائق
|
||||
- تجميع نقاط تنفيذ الوثائق وتوثيق جميع سياقات الاعتراض.
|
||||
- تحديث الوثائق لنقاط تنفيذ الاعتراض.
|
||||
|
||||
## المساهمون
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="7 يوليو 2026">
|
||||
## v1.15.2
|
||||
|
||||
|
||||
@@ -24,23 +24,15 @@ mode: "wide"
|
||||
|
||||
## البداية السريعة
|
||||
|
||||
### 1. إنشاء مهارة باستخدام سطر الأوامر (CLI)
|
||||
|
||||
واجهة سطر الأوامر هي الطريقة المدعومة لإنشاء مهارة — فهي تُنشئ لك هيكل المجلد وملف `SKILL.md` صالحًا:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create code-review
|
||||
```
|
||||
|
||||
داخل مشروع طاقم (حيث يوجد `pyproject.toml`) يُنشئ هذا الأمر `./skills/code-review/`؛ وخارج المشروع يُنشئ `./code-review/` في المجلد الحالي (يمكنك فرض هذا السلوك باستخدام `--no-project`):
|
||||
### 1. إنشاء مجلد المهارة
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # Required — instructions (pre-filled template)
|
||||
├── references/ # Optional — reference docs
|
||||
├── scripts/ # Optional — executable scripts
|
||||
└── assets/ # Optional — static files
|
||||
├── SKILL.md # مطلوب — التعليمات
|
||||
├── references/ # اختياري — مستندات مرجعية
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # اختياري — سكربتات قابلة للتنفيذ
|
||||
```
|
||||
|
||||
### 2. كتابة SKILL.md الخاص بك
|
||||
@@ -172,65 +164,6 @@ agent = Agent(
|
||||
|
||||
---
|
||||
|
||||
## إنشاء المهارات ونشرها وتثبيتها
|
||||
|
||||
للمهارات دورة حياة كاملة تُدار عبر واجهة سطر الأوامر: **أنشئها باستخدام `crewai skill create`، وانشرها باستخدام `crewai skill publish`** — إنشاء المجلدات يدويًا يصلح للتجارب المحلية، لكن واجهة سطر الأوامر هي سير العمل المقصود، وهي تحافظ على صحة هيكل المهارة وبياناتها الوصفية.
|
||||
|
||||
### الإنشاء
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create my-skill
|
||||
```
|
||||
|
||||
يُنشئ هذا الأمر المجلد (داخل `./skills/` في مشروع الطاقم) مع قالب `SKILL.md`، بالإضافة إلى مجلدات فارغة `scripts/` و `references/` و `assets/`. عدّل `SKILL.md` لتعريف التعليمات.
|
||||
|
||||
### النشر
|
||||
|
||||
نفّذ الأمر من داخل مجلد المهارة (حيث يوجد `SKILL.md`):
|
||||
|
||||
```shell Terminal
|
||||
cd skills/my-skill
|
||||
crewai skill publish
|
||||
```
|
||||
|
||||
يقرأ النشر الحقول `name` و `description` و `metadata.version` من البيانات الوصفية في مقدمة `SKILL.md` ويدفع المهارة إلى سجل CrewAI. **المهارات المنشورة تكون دائمًا مقيّدة بنطاق مؤسستك** — مثل الأدوات، لا يستطيع رؤيتها وتثبيتها إلا أعضاء المؤسسة الناشرة؛ ولا توجد رؤية عامة. أعلام مفيدة:
|
||||
|
||||
| العلم | التأثير |
|
||||
| :--- | :--- |
|
||||
| `--org <slug>` | النشر تحت مؤسسة محددة (يتجاوز الإعدادات). |
|
||||
| `--force` | تخطي التحقق من حالة git (تغييرات غير مُثبتة، إلخ). |
|
||||
|
||||
### التثبيت
|
||||
|
||||
ثبّت مهارة منشورة عبر مرجعها `@org/name`:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill install @acme/code-review
|
||||
```
|
||||
|
||||
داخل مشروع الطاقم تُثبَّت المهارة في `./skills/{name}/`؛ وخارج المشروع تذهب إلى ذاكرة التخزين المؤقتة المشتركة في `~/.crewai/skills/{org}/{name}/`.
|
||||
|
||||
يمكن للوكلاء أيضًا الإشارة إلى مهارات السجل مباشرة — يتم حلّها من ذاكرة التخزين المؤقتة المحلية (أو من مجلد `skills/` في المشروع) وقت التشغيل:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["@acme/code-review"], # registry ref, resolved locally
|
||||
)
|
||||
```
|
||||
|
||||
### عرض القائمة
|
||||
|
||||
```shell Terminal
|
||||
crewai skill list
|
||||
```
|
||||
|
||||
يعرض المهارات المثبّتة من مجلد المشروع `./skills/` ومن ذاكرة التخزين المؤقتة العامة معًا، مع إصداراتها ومساراتها.
|
||||
|
||||
---
|
||||
|
||||
## المهارات على مستوى الطاقم
|
||||
|
||||
يمكن تعيين المهارات على الطاقم لتُطبّق على **جميع الوكلاء**:
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
title: التدفقات في الاستوديو
|
||||
description: "أنشئ سير عمل يعتمد على الأحداث يجمع بين التحكم الحتمي خطوة بخطوة والذكاء الوكيلي — دون كتابة أي كود."
|
||||
icon: "diagram-project"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**الطرح جارٍ حاليًا**: يجري طرح التدفقات في الاستوديو تدريجيًا خلال أسبوع 20 يوليو 2026. إذا لم يظهر لك خيار Flows في الاستوديو بعد، فهذا يعني أن الميزة لم تصل إلى مؤسستك بعد — عاود التحقق قريبًا.
|
||||
</Info>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يدعم الاستوديو الآن إنشاء **التدفقات (Flows)** إلى جانب فرق Crew. التدفقات هي سير عمل يعتمد على الأحداث تتحكم فيه بدقة في الخطوات التي تُنفَّذ وترتيبها وشروط تنفيذها — بينما تُفوِّض العمل الذكي داخل كل خطوة إلى وكلاء الذكاء الاصطناعي.
|
||||
|
||||
لإنشاء تدفق، افتح الاستوديو وصف الأتمتة التي تريدها، ثم اختر **Flows** من المحدد بجوار مربع الإدخال.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
## لماذا التدفقات؟
|
||||
|
||||
فرق Crew ممتازة عندما تريد أن يتعاون فريق من الوكلاء بشكل مستقل نحو هدف. لكن كثيرًا من الأتمتة الواقعية يحتاج إلى قدر أكبر من القابلية للتنبؤ: اجلب هذه البيانات أولًا، ثم لخصها، ثم انشر النتيجة — في كل مرة وبنفس الترتيب.
|
||||
|
||||
تمنحك التدفقات الأمرين معًا:
|
||||
|
||||
- **الحتمية حيث تهم**: تُنفَّذ الخطوات وفق تسلسل محدد وتفرعات صريحة، فتكون عمليات التشغيل قابلة للتنبؤ والتكرار وسهلة التصحيح.
|
||||
- **الذكاء حيث تحتاجه**: كل خطوة يشغّلها وكيل (أو فريق Crew كامل)، لذا يستفيد العمل داخل الخطوة — التلخيص والتقييم والصياغة واتخاذ القرار — من قدرات الاستدلال الكاملة للنموذج اللغوي.
|
||||
|
||||
هذا المزيج هو ما يجعل التدفقات مناسبة للأتمتة في بيئات الإنتاج: البنية مضمونة، والاستقلالية محصورة في الخطوات التي تحتاجها.
|
||||
|
||||
## إنشاء تدفق
|
||||
|
||||
صف ما تريده بلغة طبيعية وسيصمم مساعد الاستوديو (Studio Assistant) التدفق لك — بإنشاء الخطوات وربطها ببعضها وتهيئة الوكلاء وتكاملات التطبيقات التي تحتاجها كل خطوة. تعرض اللوحة (Canvas) على اليمين سير العمل الناتج كعُقد متصلة، ويمكنك مواصلة التحسين عبر المحادثة أو تعديل أي عقدة مباشرة.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
عندما تكون جاهزًا، استخدم **Run** لاختبار التدفق من البداية إلى النهاية، وافحص النتائج في تبويبي **Output** و**Traces**، ثم نفّذ **Deploy** عندما يستقر. يمكنك أيضًا مشاركة المشروع عبر **Share** أو تنزيل الكود المصدري عبر **Download**.
|
||||
|
||||
## أنواع العُقد
|
||||
|
||||
تتكون التدفقات من ثلاثة أنواع أساسية من العُقد. كل عقدة هي خطوة في سير العمل، ويمكنك المزج بينها بحرية.
|
||||
|
||||
### الوكيل المنفرد (Single Agent)
|
||||
|
||||
تُشغِّل عقدة Single Agent وكيلًا واحدًا لمهمة واحدة مركزة — وهي مثالية للخطوات محددة النطاق مثل جلب البيانات من تكامل، أو تحويل المحتوى، أو نشر رسالة.
|
||||
|
||||
عند النقر على عقدة الوكيل تُفتح تهيئتها الكاملة:
|
||||
|
||||
- **Task**: ما ينبغي أن تنجزه هذه الخطوة والمخرجات التي يجب أن تنتجها
|
||||
- **Profile**: دور الوكيل وهدفه وخلفيته
|
||||
- **Model**: النموذج اللغوي الذي يشغّل الوكيل
|
||||
- **Apps**: التكاملات التي يمكن للوكيل استخدامها (مثل Linear وSlack وHubSpot)
|
||||
- **Runtime Controls**: خيارات التخطيط قبل التنفيذ والتفويض والذاكرة
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
### فرق Crew
|
||||
|
||||
تُضمِّن عقدة Crew فريقًا كاملًا — عدة وكلاء يتعاونون عبر عدة مهام — كخطوة واحدة في تدفقك. استخدمها عندما تكون الخطوة أكبر من أن يتولاها وكيل واحد، مثل تجميع البيانات وتلخيصها حسب الفريق ثم تنسيق النتيجة للتسليم.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
يكشف فتح عقدة Crew عن بنيتها الداخلية: المهام التي تؤديها، والوكلاء المعيّنين لكل مهمة، والتطبيقات التي يستخدمونها. يعمل الفريق بشكل مستقل داخل الخطوة، ثم يسلّم مخرجاته إلى العقدة التالية في التدفق.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
هذا هو نمط الحتمية مع الذكاء الوكيلي عمليًا: يضمن التدفق *متى* يعمل الفريق، بينما يضيف الفريق ذكاءً تعاونيًا إلى *كيفية* إنجاز العمل.
|
||||
|
||||
### الموجِّه (Router)
|
||||
|
||||
تُفرِّع عقدة Router التدفق بناءً على شروط، بحيث تسلك النتائج المختلفة مسارات مختلفة. على سبيل المثال، يمكن لتدفق توجيه العملاء المحتملين تقييم العملاء الواردين ثم توجيه ذوي الجودة العالية إلى خطوة إسناد المبيعات، مع تسجيل البقية للمتابعة والرعاية لاحقًا.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
الموجِّهات هي ما يجعل التدفقات معتمدة على الأحداث فعليًا: يتعامل سير العمل نفسه مع كل الحالات، لكن كل عملية تشغيل تتبع فقط الفرع الذي تستدعيه بياناتها — بلا خطوات مهدرة ولا غموض حول ما سيحدث تاليًا.
|
||||
|
||||
## المزامنة مع مستودع الوكلاء
|
||||
|
||||
لا يلزم أن يبقى الوكلاء الذين تنشئهم في التدفقات حبيسي مشروع واحد. تتضمن كل عقدة وكيل زر **Publish to Agent Repository** الذي يحفظ الوكيل — بدوره وهدفه وخلفيته ونموذجه وتهيئته — في [مستودع الوكلاء](/ar/enterprise/features/agent-repositories) الخاص بمؤسستك.
|
||||
|
||||
يعمل هذا في الاتجاهين:
|
||||
|
||||
- **النشر**: رقِّ وكيلًا حسّنته داخل تدفق إلى المستودع ليتمكن باقي الفرق والمشاريع من إعادة استخدامه.
|
||||
- **السحب**: أدخِل وكيلًا موجودًا من المستودع إلى تدفق جديد بدلًا من إعادة بنائه من الصفر.
|
||||
|
||||
ولأن وكلاء المستودع متزامنون عبر مؤسستك كلها، فإن أي تحسين على وكيل مشترك يعود بالنفع على كل تدفق يستخدمه — مما يحافظ على سلوك وكلاء متسق وخاضع للحوكمة وخالٍ من ازدواجية الجهد.
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
- **اختر التدفق** عندما يكون للأتمتة تسلسل واضح أو منطق تفرّع؛ واختر Crew عندما يكون الطريق إلى الهدف مفتوحًا.
|
||||
- **أبقِ مهام الوكلاء مركزة** — عقدة Single Agent بوصف مهمة محكم أكثر موثوقية من وكيل مطلوب منه ثلاثة أشياء.
|
||||
- **استخدم الموجّهات لمعالجة كل حالة صراحةً**، بما في ذلك مسار "عدم فعل شيء" (مثل تسجيل العملاء المتجاوزين)، حتى تكون كل عمليات التشغيل محسوبة بالكامل.
|
||||
- **انشر الوكلاء المستقرين في مستودع الوكلاء** لتبني مؤسستك مكتبة مشتركة بدلًا من نسخ متوازية لمرة واحدة.
|
||||
- **اختبر عبر Run وافحص Traces** قبل النشر لاكتشاف مشكلات التكامل أو الموجِّهات النصية مبكرًا.
|
||||
|
||||
## ذات صلة
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="استوديو الطاقم" href="/ar/enterprise/features/crew-studio" icon="pencil">
|
||||
أنشئ فرق Crew في الاستوديو.
|
||||
</Card>
|
||||
<Card title="مستودعات الوكلاء" href="/ar/enterprise/features/agent-repositories" icon="people-group">
|
||||
شارك الوكلاء وأعد استخدامهم عبر مؤسستك.
|
||||
</Card>
|
||||
<Card title="مفاهيم التدفقات" href="/ar/concepts/flows" icon="diagram-project">
|
||||
تعرّف على كيفية عمل التدفقات في إطار عمل CrewAI.
|
||||
</Card>
|
||||
<Card title="الأدوات والتكاملات" href="/ar/enterprise/features/tools-and-integrations" icon="plug">
|
||||
اربط التطبيقات التي يستخدمها وكلاؤك.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -4,136 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Jul 20, 2026">
|
||||
## v1.15.5
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.5)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Authenticate skill registry downloads
|
||||
|
||||
### Documentation
|
||||
- Update snapshot and changelog for v1.15.4
|
||||
|
||||
## Contributors
|
||||
|
||||
@vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 17, 2026">
|
||||
## v1.15.4
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.4)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Promote Skills Repository out of experimental status
|
||||
|
||||
### Documentation
|
||||
- Add Flows in Studio documentation
|
||||
|
||||
## Contributors
|
||||
|
||||
@jessemiller, @joaomdmoura, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 16, 2026">
|
||||
## v1.15.3
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Add organization ID parameter to PlusAPI client
|
||||
- Add step interception points and rework execution hooks documentation around @on
|
||||
- Wire execution-boundary interception points
|
||||
- Add generic interception-hook dispatcher
|
||||
- Run declarative flows on the TUI (headless terminal fallback)
|
||||
|
||||
### Bug Fixes
|
||||
- Sync kickoff-completed event with OUTPUT hook result
|
||||
- Fix null repository agent attributes
|
||||
- Ensure after_llm_call hooks do not break native tool execution
|
||||
- Avoid double-append of the turn reply when a handler trims history
|
||||
- Make tool-result caching opt-in instead of on by default
|
||||
- Stop rewriting the authored tool description at construction
|
||||
- Expose token usage under both names on agent and crew results
|
||||
- Report per-call usage metrics on kickoff results
|
||||
- Stop replaying previous turn's intent when route_turn() returns falsy
|
||||
|
||||
### Documentation
|
||||
- Update execution hooks grouping and document all hook contexts
|
||||
|
||||
## Contributors
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 16, 2026">
|
||||
## v1.15.3a2
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a2)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Bug Fixes
|
||||
- Fix synchronization of kickoff-completed event with OUTPUT hook result
|
||||
|
||||
### Documentation
|
||||
- Update snapshot and changelog for v1.15.3a1
|
||||
|
||||
### Dependency Updates
|
||||
- Bump setuptools to 0.83.0 to address PYSEC-2026-3447
|
||||
|
||||
## Contributors
|
||||
|
||||
@lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 16, 2026">
|
||||
## v1.15.3a1
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a1)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Add organization ID parameter to PlusAPI client.
|
||||
- Add step interception points and rework execution hooks documentation around `@on`.
|
||||
- Wire execution-boundary interception points.
|
||||
- Add generic interception-hook dispatcher.
|
||||
- Run declarative flows on the TUI (headless terminal fallback).
|
||||
- Improve custom OpenAI URLs.
|
||||
|
||||
### Bug Fixes
|
||||
- Fix null repository agent attributes.
|
||||
- Fix `after_llm_call` hooks to prevent breaking native tool execution.
|
||||
- Stop double-appending the turn reply when a handler trims history.
|
||||
- Make tool-result caching opt-in instead of on by default.
|
||||
- Stop rewriting the authored tool description at construction.
|
||||
- Expose token usage under both names on agent and crew results.
|
||||
- Report per-call usage metrics on kickoff results.
|
||||
- Stop replaying the previous turn's intent when `route_turn()` returns falsy.
|
||||
- Drain memory writes before kickoff and flow completion events.
|
||||
|
||||
### Documentation
|
||||
- Group execution hooks and document all hook contexts.
|
||||
- Update documentation for execution hooks.
|
||||
|
||||
## Contributors
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 07, 2026">
|
||||
## v1.15.2
|
||||
|
||||
|
||||
@@ -144,18 +144,6 @@ In this section, you'll find detailed examples that help you select, configure,
|
||||
)
|
||||
```
|
||||
|
||||
**Custom OpenAI-Compatible Endpoint:**
|
||||
```python Code
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="anthropic/claude-sonnet-4-6",
|
||||
custom_openai=True,
|
||||
base_url="https://your-gateway.example.com/v1",
|
||||
api_key="your-gateway-api-key",
|
||||
)
|
||||
```
|
||||
|
||||
**Advanced Configuration:**
|
||||
```python Code
|
||||
from crewai import LLM
|
||||
|
||||
@@ -24,23 +24,15 @@ You often need **both**: skills for expertise, tools for action. They are config
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create a Skill with the CLI
|
||||
|
||||
The CLI is the supported way to create a skill — it scaffolds the directory layout and a valid `SKILL.md` for you:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create code-review
|
||||
```
|
||||
|
||||
Inside a crew project (where `pyproject.toml` lives) this creates `./skills/code-review/`; outside a project it creates `./code-review/` in the current directory (you can force that behavior with `--no-project`):
|
||||
### 1. Create a Skill Directory
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # Required — instructions (pre-filled template)
|
||||
├── SKILL.md # Required — instructions
|
||||
├── references/ # Optional — reference docs
|
||||
├── scripts/ # Optional — executable scripts
|
||||
└── assets/ # Optional — static files
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # Optional — executable scripts
|
||||
```
|
||||
|
||||
### 2. Write Your SKILL.md
|
||||
@@ -172,65 +164,6 @@ agent = Agent(
|
||||
|
||||
---
|
||||
|
||||
## Creating, Publishing, and Installing Skills
|
||||
|
||||
Skills have a full lifecycle managed by the CLI: **create them with `crewai skill create`, publish them with `crewai skill publish`** — hand-rolling directories works for local experiments, but the CLI is the intended workflow and keeps your skill layout and frontmatter valid.
|
||||
|
||||
### Create
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create my-skill
|
||||
```
|
||||
|
||||
Scaffolds the directory (into `./skills/` inside a crew project) with a template `SKILL.md`, plus empty `scripts/`, `references/`, and `assets/` directories. Edit `SKILL.md` to define the instructions.
|
||||
|
||||
### Publish
|
||||
|
||||
Run from inside the skill directory (where `SKILL.md` is):
|
||||
|
||||
```shell Terminal
|
||||
cd skills/my-skill
|
||||
crewai skill publish
|
||||
```
|
||||
|
||||
Publishing reads `name`, `description`, and `metadata.version` from the `SKILL.md` frontmatter and pushes the skill to the CrewAI registry. **Published skills are always scoped to your organization** — like tools, only members of the publishing org can see and install them; there is no public visibility. Useful flags:
|
||||
|
||||
| Flag | Effect |
|
||||
| :--- | :--- |
|
||||
| `--org <slug>` | Publish under a specific organization (overrides settings). |
|
||||
| `--force` | Skip git-state validation (uncommitted changes, etc.). |
|
||||
|
||||
### Install
|
||||
|
||||
Install a published skill by its `@org/name` reference:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill install @acme/code-review
|
||||
```
|
||||
|
||||
Inside a crew project the skill lands in `./skills/{name}/`; outside a project it goes to the shared cache at `~/.crewai/skills/{org}/{name}/`.
|
||||
|
||||
Agents can also reference registry skills directly — they resolve from the local cache (or project `skills/` directory) at runtime:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["@acme/code-review"], # registry ref, resolved locally
|
||||
)
|
||||
```
|
||||
|
||||
### List
|
||||
|
||||
```shell Terminal
|
||||
crewai skill list
|
||||
```
|
||||
|
||||
Shows installed skills from both the project `./skills/` directory and the global cache, with their versions and paths.
|
||||
|
||||
---
|
||||
|
||||
## Crew-Level Skills
|
||||
|
||||
Skills can be set on a crew to apply to **all agents**:
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
title: Flows in Studio
|
||||
description: "Build event-driven workflows that combine deterministic, step-by-step control with agentic intelligence — no code required."
|
||||
icon: "diagram-project"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Rolling out now**: Flows in Studio is being rolled out gradually during the week of July 20th, 2026. If you don't see the Flows option in Studio yet, it hasn't reached your organization — check back soon.
|
||||
</Info>
|
||||
|
||||
## Overview
|
||||
|
||||
Studio now supports building **Flows** in addition to Crews. Flows are event-driven workflows where you control exactly which steps run, in what order, and under what conditions — while still delegating the intelligent work within each step to AI agents.
|
||||
|
||||
To build a Flow, open Studio, describe your automation, and select **Flows** from the selector next to the prompt box.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
## Why Flows?
|
||||
|
||||
Crews are great when you want a team of agents to collaborate autonomously toward a goal. But many real-world automations need more predictability: fetch this data first, then summarize it, then post the result — every time, in that order.
|
||||
|
||||
Flows give you both:
|
||||
|
||||
- **Determinism where it matters**: steps execute in a defined sequence with explicit branching, so runs are predictable, repeatable, and easy to debug.
|
||||
- **Intelligence where you need it**: each step is powered by an agent (or an entire crew), so the work inside a step — summarizing, scoring, drafting, deciding — benefits from full LLM reasoning.
|
||||
|
||||
This mix is what makes Flows well suited for production automations: the structure is guaranteed, and the agency is scoped to the steps that need it.
|
||||
|
||||
## Building a Flow
|
||||
|
||||
Describe what you want in natural language and the Studio Assistant designs the Flow for you — creating the steps, wiring them together, and configuring the agents and app integrations each step needs. The canvas on the right shows the resulting workflow as connected nodes, and you can keep iterating conversationally or edit any node directly.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
When you're ready, use **Run** to test the Flow end-to-end, inspect results in the **Output** and **Traces** tabs, and **Deploy** when it's stable. You can also **Share** the project or **Download** the source code.
|
||||
|
||||
## Node Types
|
||||
|
||||
Flows are composed from three core node types. Each node is a step in the workflow, and you can mix them freely.
|
||||
|
||||
### Single Agent
|
||||
|
||||
A Single Agent node runs one agent against one focused task — ideal for well-scoped steps like fetching data from an integration, transforming content, or posting a message.
|
||||
|
||||
Clicking into an agent node opens its full configuration:
|
||||
|
||||
- **Task**: what this step should accomplish and what output it should produce
|
||||
- **Profile**: the agent's role, goal, and backstory
|
||||
- **Model**: which LLM powers the agent
|
||||
- **Apps**: the integrations the agent can use (e.g. Linear, Slack, HubSpot)
|
||||
- **Runtime Controls**: toggles for planning before executing, delegation, and memory
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
### Crews
|
||||
|
||||
A Crew node embeds an entire crew — multiple agents collaborating across multiple tasks — as a single step in your Flow. Use it when a step is too rich for one agent, like grouping and summarizing data by team and then formatting the result for delivery.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Opening a Crew node reveals its internal structure: the tasks it performs, the agents assigned to each, and the apps they use. The crew runs autonomously within the step, then hands its output to the next node in the Flow.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
This is the deterministic-plus-agentic pattern in action: the Flow guarantees *when* the crew runs, and the crew brings collaborative intelligence to *how* the work gets done.
|
||||
|
||||
### Router
|
||||
|
||||
A Router node branches the Flow based on conditions, so different outcomes take different paths. For example, a lead-routing Flow can score incoming leads and then route high-quality leads to a sales-assignment step while logging the rest for future nurturing.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Routers are what make Flows genuinely event-driven: the same workflow handles every case, but each run follows only the branch its data warrants — no wasted steps, no ambiguity about what happens next.
|
||||
|
||||
## Agent Repository Sync
|
||||
|
||||
Agents you build in Flows don't have to stay locked inside a single project. Every agent node includes a **Publish to Agent Repository** button that saves the agent — its role, goal, backstory, model, and configuration — to your organization's [Agent Repository](/en/enterprise/features/agent-repositories).
|
||||
|
||||
This works in both directions:
|
||||
|
||||
- **Publish**: promote an agent you've refined in a Flow to the repository so other teams and projects can reuse it.
|
||||
- **Pull**: bring an existing repository agent into a new Flow instead of rebuilding it from scratch.
|
||||
|
||||
Because repository agents are synced across your organization, an improvement made to a shared agent benefits every Flow that uses it — keeping agent behavior consistent, governed, and free of duplicated effort.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Reach for a Flow** when the automation has a clear sequence or branching logic; reach for a Crew when the path to the goal is open-ended.
|
||||
- **Keep agent tasks focused** — a Single Agent node with a tight task description is more reliable than one asked to do three things.
|
||||
- **Use Routers to handle every case explicitly**, including the "do nothing" path (e.g. logging skipped leads), so runs are fully accounted for.
|
||||
- **Publish stable agents to the Agent Repository** so your organization builds a shared library instead of parallel one-offs.
|
||||
- **Test with Run and inspect Traces** before deploying to catch integration or prompt issues early.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Crew Studio" href="/en/enterprise/features/crew-studio" icon="pencil">
|
||||
Build Crews in Studio.
|
||||
</Card>
|
||||
<Card title="Agent Repositories" href="/en/enterprise/features/agent-repositories" icon="people-group">
|
||||
Share and reuse agents across your organization.
|
||||
</Card>
|
||||
<Card title="Flows Concepts" href="/en/concepts/flows" icon="diagram-project">
|
||||
Learn how Flows work in the CrewAI framework.
|
||||
</Card>
|
||||
<Card title="Tools & Integrations" href="/en/enterprise/features/tools-and-integrations" icon="plug">
|
||||
Connect the apps your agents use.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,187 +0,0 @@
|
||||
---
|
||||
title: Execution Boundary Hooks
|
||||
description: Intercept the start, inputs, output, and end of crew and flow executions with the @on decorator
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
Execution boundary hooks intercept the outermost edges of a run — before any
|
||||
work starts, when inputs are resolved, when the final result is ready, and when
|
||||
the execution finishes. They fire for both crews and flows and are the right
|
||||
place for run-level policy checks, input rewriting, and output sanitization.
|
||||
|
||||
## Overview
|
||||
|
||||
Four interception points cover the boundaries:
|
||||
|
||||
| Point | When | `ctx.payload` |
|
||||
|-------|------|---------------|
|
||||
| `EXECUTION_START` | A crew or flow is about to begin | inputs `dict` |
|
||||
| `INPUT` | Resolved inputs for the execution | inputs `dict` |
|
||||
| `OUTPUT` | The final result is ready | the output object |
|
||||
| `EXECUTION_END` | The execution has finished (success or failure) | the output object, or `None` on failure |
|
||||
|
||||
For a crew, the output payload is a `CrewOutput`. For a flow, it is the final
|
||||
flow-method result.
|
||||
|
||||
## Hook Signature
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def boundary_hook(ctx) -> Any | None:
|
||||
# Mutate ctx.payload in place, or
|
||||
# return a non-None value to replace it, or
|
||||
# raise HookAborted(reason, source) to stop the run
|
||||
return None
|
||||
```
|
||||
|
||||
Boundary hooks follow the standard contract: proceed (`return None`), mutate in
|
||||
place, replace by returning, or abort by raising
|
||||
[`HookAborted`](/edge/en/learn/execution-hooks#aborting-an-operation). An abort at any
|
||||
boundary propagates out of `kickoff()` with its reason.
|
||||
|
||||
## Context Schema
|
||||
|
||||
Each point receives a typed context. All contexts share the base fields:
|
||||
|
||||
```python
|
||||
class InterceptionContext:
|
||||
payload: Any # The interceptable value (see table above)
|
||||
agent: Any = None # Not populated at execution boundaries
|
||||
agent_role: str | None # Not populated at execution boundaries
|
||||
task: Any = None # Not populated at execution boundaries
|
||||
crew: Any = None # The Crew instance (crew runs only)
|
||||
flow: Any = None # The Flow instance (flow runs only)
|
||||
```
|
||||
|
||||
The per-point contexts add a named alias for the payload:
|
||||
|
||||
```python
|
||||
class ExecutionStartContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class InputContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class OutputContext(InterceptionContext):
|
||||
output: Any # The output object
|
||||
|
||||
class ExecutionEndContext(InterceptionContext):
|
||||
output: Any # The output object (None when status == "failed")
|
||||
status: str # "completed" or "failed"
|
||||
error: BaseException | None # The exception when status == "failed"
|
||||
```
|
||||
|
||||
<Note>
|
||||
`ctx.inputs` aliases the **original** inputs dict, so in-place edits through
|
||||
either name are equivalent. If an earlier hook *replaced* the payload by
|
||||
returning a new dict, only `ctx.payload` is rebound — always read and write
|
||||
`ctx.payload` when hooks might chain.
|
||||
</Note>
|
||||
|
||||
## Crew Runs vs. Flow Runs
|
||||
|
||||
Boundary hooks fire on both runtimes, and crew execution internally rides on a
|
||||
flow runtime. During a `crew.kickoff()`, a global boundary hook therefore fires
|
||||
for the crew boundary (`ctx.crew` set, `ctx.flow` `None`) **and** for the
|
||||
internal flow (`ctx.flow` set, `ctx.crew` `None`). Discriminate by runtime:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def crew_output_only(ctx):
|
||||
if ctx.crew is None:
|
||||
return None # Skip the internal flow (or a bare flow)
|
||||
ctx.payload.raw = ctx.payload.raw.strip()
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Policy Check at Start
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def enforce_policy(ctx):
|
||||
if ctx.crew is not None and not ctx.payload.get("authorized"):
|
||||
raise HookAborted(reason="unauthorized execution", source="access-control")
|
||||
```
|
||||
|
||||
### Input Rewriting
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.INPUT)
|
||||
def add_defaults(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.setdefault("locale", "en-US")
|
||||
ctx.payload["topic"] = ctx.payload["topic"].strip().lower()
|
||||
```
|
||||
|
||||
Rewritten inputs flow into task interpolation, so the run behaves as if it was
|
||||
kicked off with the modified dict.
|
||||
|
||||
### Output Sanitization
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def redact_emails(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.raw = re.sub(
|
||||
r"\b[\w.+-]+@[\w-]+\.[\w.]+\b", "[EMAIL-REDACTED]", ctx.payload.raw
|
||||
)
|
||||
```
|
||||
|
||||
`OUTPUT` runs before `EXECUTION_END`, and both see the (possibly replaced)
|
||||
payload from earlier hooks; the final rewritten value is what `kickoff()`
|
||||
returns.
|
||||
|
||||
### Observing Failures
|
||||
|
||||
`EXECUTION_END` fires exactly once per execution, on success and on failure
|
||||
alike. When the run raises — a task error, a flow-method exception, or a
|
||||
`HookAborted` from an earlier point — the hook receives `status="failed"` with
|
||||
the exception in `ctx.error`, and the original exception still propagates out
|
||||
of `kickoff()` unchanged:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_END)
|
||||
def report_outcome(ctx):
|
||||
if ctx.status == "failed":
|
||||
notify_policy_engine(status="failed", error=repr(ctx.error))
|
||||
else:
|
||||
notify_policy_engine(status="completed")
|
||||
```
|
||||
|
||||
Two caveats: `EXECUTION_END` does not fire when `EXECUTION_START` never
|
||||
dispatched (an abort at start counts as the execution never beginning), and
|
||||
raising `HookAborted` from a failure-path `EXECUTION_END` dispatch is ignored —
|
||||
there is nothing left to abort, and the original error wins.
|
||||
|
||||
## Ordering
|
||||
|
||||
For a crew run the boundary order is:
|
||||
|
||||
```
|
||||
EXECUTION_START → before_kickoff callbacks → INPUT → tasks execute → OUTPUT → EXECUTION_END
|
||||
```
|
||||
|
||||
Hooks at the same point run in registration order, global hooks first, then
|
||||
crew-scoped hooks. Telemetry (`HookDispatchedEvent`) is emitted per dispatch.
|
||||
|
||||
## Managing Hooks in Tests
|
||||
|
||||
```python
|
||||
from crewai.hooks import clear_all_hooks
|
||||
|
||||
clear_all_hooks() # Clears every point, including boundaries
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Execution Hooks Overview →](/edge/en/learn/execution-hooks)
|
||||
- [Step Hooks →](/edge/en/learn/step-hooks)
|
||||
- [LLM Call Hooks →](/edge/en/learn/llm-hooks)
|
||||
- [Tool Call Hooks →](/edge/en/learn/tool-hooks)
|
||||
@@ -1,281 +1,525 @@
|
||||
---
|
||||
title: Execution Hooks
|
||||
description: Intercept, modify, and control CrewAI's runtime with the @on decorator - one contract covering every interception point
|
||||
title: Execution Hooks Overview
|
||||
description: Understanding and using execution hooks in CrewAI for fine-grained control over agent operations
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
Execution hooks provide fine-grained control over the runtime behavior of your
|
||||
CrewAI agents. Unlike kickoff hooks that run before and after crew execution,
|
||||
execution hooks intercept specific operations during execution — from the moment
|
||||
a run starts, through every model call, tool call, and task or flow-method step,
|
||||
down to the final output.
|
||||
Execution Hooks provide fine-grained control over the runtime behavior of your CrewAI agents. Unlike kickoff hooks that run before and after crew execution, execution hooks intercept specific operations during agent execution, allowing you to modify behavior, implement safety checks, and add comprehensive monitoring.
|
||||
|
||||
Hooks are written with the `@on` decorator: one registration API and one
|
||||
contract cover every interception point in the framework.
|
||||
## Types of Execution Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
CrewAI provides two main categories of execution hooks:
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["delete_file"])
|
||||
def guard_deletes(ctx):
|
||||
raise HookAborted(reason="file deletion is not allowed", source="policy")
|
||||
```
|
||||
### 1. [LLM Call Hooks](/learn/llm-hooks)
|
||||
|
||||
<Note>
|
||||
The point-specific decorators (`@before_llm_call`, `@after_tool_call`, ...) keep
|
||||
working unchanged — they are adapters over the same engine. See
|
||||
[Point-specific decorators (legacy)](#point-specific-decorators-legacy) at the
|
||||
end of this page.
|
||||
</Note>
|
||||
Control and monitor language model interactions:
|
||||
- **Before LLM Call**: Modify prompts, validate inputs, implement approval gates
|
||||
- **After LLM Call**: Transform responses, sanitize outputs, update conversation history
|
||||
|
||||
## The contract
|
||||
**Use Cases:**
|
||||
- Iteration limiting
|
||||
- Cost tracking and token usage monitoring
|
||||
- Response sanitization and content filtering
|
||||
- Human-in-the-loop approval for LLM calls
|
||||
- Adding safety guidelines or context
|
||||
- Debug logging and request/response inspection
|
||||
|
||||
Every hook is a **synchronous** callable that receives a single typed context:
|
||||
[View LLM Hooks Documentation →](/learn/llm-hooks)
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
### 2. [Tool Call Hooks](/learn/tool-hooks)
|
||||
|
||||
@on(InterceptionPoint.INPUT)
|
||||
def add_defaults(ctx):
|
||||
# 1. Observe: read anything off the context.
|
||||
# 2. Mutate in place: change ctx.payload or nested fields directly.
|
||||
ctx.payload.setdefault("locale", "en-US")
|
||||
# 3. Or replace: return a new value to swap ctx.payload.
|
||||
# 4. Or abort: raise HookAborted(reason, source) to stop the operation.
|
||||
return None
|
||||
```
|
||||
Control and monitor tool execution:
|
||||
- **Before Tool Call**: Modify inputs, validate parameters, block dangerous operations
|
||||
- **After Tool Call**: Transform results, sanitize outputs, log execution details
|
||||
|
||||
A hook may do any of four things:
|
||||
**Use Cases:**
|
||||
- Safety guardrails for destructive operations
|
||||
- Human approval for sensitive actions
|
||||
- Input validation and sanitization
|
||||
- Result caching and rate limiting
|
||||
- Tool usage analytics
|
||||
- Debug logging and monitoring
|
||||
|
||||
| Action | How | Effect |
|
||||
|--------|-----|--------|
|
||||
| **Proceed** | `return None` (or nothing) | Operation continues unchanged |
|
||||
| **Mutate** | Change `ctx.payload` / fields in place | Change is visible downstream |
|
||||
| **Replace** | `return new_payload` | A non-`None` return replaces `ctx.payload` |
|
||||
| **Abort** | `raise HookAborted(reason, source)` | Operation is stopped; the reason propagates |
|
||||
[View Tool Hooks Documentation →](/learn/tool-hooks)
|
||||
|
||||
## Registering hooks
|
||||
## Hook Registration Methods
|
||||
|
||||
Use `@on` for global hooks. It accepts `agents=` / `tools=` filters to scope a
|
||||
hook to specific agent roles or tool names:
|
||||
### 1. Decorator-Based Hooks (Recommended)
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, InterceptionPoint
|
||||
|
||||
@on(InterceptionPoint.POST_TOOL_CALL, agents=["researcher"], tools=["web_search"])
|
||||
def log_search_results(ctx):
|
||||
print(f"search returned: {(ctx.tool_result or '')[:80]}")
|
||||
```
|
||||
|
||||
Applied to a method inside a `@CrewBase` class, `@on` registers a
|
||||
**crew-scoped** hook, active only while that crew runs:
|
||||
|
||||
```python
|
||||
from crewai import CrewBase
|
||||
from crewai.hooks import on, InterceptionPoint
|
||||
|
||||
@CrewBase
|
||||
class MyProjCrew:
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def validate_inputs(self, ctx):
|
||||
# Only applies to this crew
|
||||
return None
|
||||
```
|
||||
|
||||
## Interception point catalog
|
||||
|
||||
Each family has a detailed guide covering its context schema, payload
|
||||
semantics, and examples.
|
||||
|
||||
### [Execution boundaries](/edge/en/learn/execution-boundary-hooks)
|
||||
|
||||
| Point | When | `ctx.payload` |
|
||||
|-------|------|---------------|
|
||||
| `EXECUTION_START` | A crew or flow is about to begin | inputs `dict` |
|
||||
| `INPUT` | Resolved inputs for the execution | inputs `dict` |
|
||||
| `OUTPUT` | Final result is ready | the output object |
|
||||
| `EXECUTION_END` | A crew or flow has finished | the output object |
|
||||
|
||||
### [Model boundaries](/edge/en/learn/llm-hooks) & [tool boundaries](/edge/en/learn/tool-hooks)
|
||||
|
||||
| Point | When | Hook receives |
|
||||
|-------|------|---------------|
|
||||
| `PRE_MODEL_CALL` | Before an LLM call | `LLMCallHookContext` |
|
||||
| `POST_MODEL_CALL` | After an LLM call | `LLMCallHookContext` (with `response` set) |
|
||||
| `PRE_TOOL_CALL` | Before a tool runs | `ToolCallHookContext` |
|
||||
| `POST_TOOL_CALL` | After a tool runs | `ToolCallHookContext` (with results set) |
|
||||
|
||||
At these four points the hook receives the rich legacy context **directly** as
|
||||
its argument — there is no separate `ctx.payload`. Mutate `ctx.messages` /
|
||||
`ctx.tool_input` in place, and return a string from a post hook to replace the
|
||||
response / tool result.
|
||||
|
||||
### [Step points](/edge/en/learn/step-hooks)
|
||||
|
||||
| Point | When | `ctx.payload` |
|
||||
|-------|------|---------------|
|
||||
| `PRE_STEP` | Before a task or flow-method step | step input |
|
||||
| `POST_STEP` | After a task or flow-method step | step output |
|
||||
|
||||
`PRE_STEP` / `POST_STEP` carry `ctx.kind` (`"task"` or `"flow_method"`) and
|
||||
`ctx.step_name`.
|
||||
|
||||
## Aborting an operation
|
||||
|
||||
`HookAborted` carries a `reason` and an optional `source`. The `source` defaults
|
||||
to the aborting hook when omitted, which is useful for telemetry and failure
|
||||
messages:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def enforce_policy(ctx):
|
||||
if not ctx.payload.get("authorized"):
|
||||
raise HookAborted(reason="unauthorized execution", source="access-control")
|
||||
```
|
||||
|
||||
## Composition, ordering, and fail-open
|
||||
|
||||
- Multiple hooks on the same point run in **registration order**, global hooks
|
||||
first, then execution-scoped hooks. Legacy hooks registered for the same point
|
||||
participate in the same chain.
|
||||
- The (possibly mutated) payload flows from one hook to the next.
|
||||
- `HookAborted` **propagates by design** and stops the chain.
|
||||
- Any *other* exception raised by a hook is **swallowed** (fail-open) so a single
|
||||
buggy hook can't crash a run.
|
||||
- When no hook is registered for a point, dispatch is a single dict lookup
|
||||
(no-op fast path), so unused points cost effectively nothing.
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Safety guardrails
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def block_dangerous_tools(ctx):
|
||||
dangerous = {"delete_file", "drop_table", "system_shutdown"}
|
||||
if ctx.tool_name in dangerous:
|
||||
raise HookAborted(reason=f"{ctx.tool_name} is blocked", source="safety-policy")
|
||||
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def iteration_limit(ctx):
|
||||
if ctx.iterations > 15:
|
||||
raise HookAborted(reason="maximum iterations exceeded", source="loop-guard")
|
||||
```
|
||||
|
||||
### Human-in-the-loop approval
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["send_email", "make_payment"])
|
||||
def require_approval(ctx):
|
||||
response = ctx.request_human_input(
|
||||
prompt=f"Approve {ctx.tool_name}?",
|
||||
default_message="Type 'yes' to approve:",
|
||||
)
|
||||
if response.lower() != "yes":
|
||||
raise HookAborted(reason="rejected by operator", source="approval-gate")
|
||||
```
|
||||
|
||||
### Sanitizing outputs
|
||||
|
||||
A non-`None` return value replaces the interceptable value, so transformations
|
||||
are plain return statements:
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.POST_MODEL_CALL)
|
||||
def redact_keys(ctx):
|
||||
return re.sub(
|
||||
r'(api[_-]?key)["\']?\s*[:=]\s*["\']?[\w-]+',
|
||||
r"\1: [REDACTED]",
|
||||
ctx.response,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
```
|
||||
|
||||
### Observing steps
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.POST_STEP)
|
||||
def trace_steps(ctx):
|
||||
print(f"{ctx.kind} '{ctx.step_name}' finished")
|
||||
```
|
||||
|
||||
## Telemetry
|
||||
|
||||
Whenever a point actually dispatches to at least one hook, CrewAI emits a
|
||||
`HookDispatchedEvent` on the event bus with the point, the outcome
|
||||
(`proceeded` / `modified` / `aborted`), the hook count, the duration, and — for
|
||||
aborts — the reason and source. The no-op fast path emits nothing.
|
||||
|
||||
## Managing hooks in tests
|
||||
|
||||
Global hooks persist for the lifetime of the process. Reset them between tests:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from crewai.hooks import clear_all_hooks
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_hooks():
|
||||
clear_all_hooks()
|
||||
yield
|
||||
clear_all_hooks()
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
1. **Keep hooks focused** — one clear responsibility per hook; register several
|
||||
small hooks rather than one that does everything.
|
||||
2. **Keep hooks fast** — hooks run on every dispatch of their point; avoid heavy
|
||||
computation and lazy-import heavy dependencies.
|
||||
3. **Prefer scoping** — use `agents=` / `tools=` filters and crew-scoped
|
||||
registration instead of unconditional global hooks.
|
||||
4. **Abort loudly** — raise `HookAborted` with a meaningful `reason` and
|
||||
`source`; that context surfaces in error messages and telemetry. Remember
|
||||
that any other exception is swallowed (fail-open), so don't rely on raising
|
||||
`ValueError` to stop a run.
|
||||
|
||||
## Point-specific decorators (legacy)
|
||||
|
||||
Before `@on`, LLM and tool calls were hooked with dedicated decorator pairs.
|
||||
These keep working unchanged — they are adapters over the same dispatcher, so
|
||||
they compose with `@on` hooks in the same registration-order chain:
|
||||
The cleanest and most Pythonic way to register hooks:
|
||||
|
||||
```python
|
||||
from crewai.hooks import before_llm_call, after_llm_call, before_tool_call, after_tool_call
|
||||
|
||||
@before_llm_call
|
||||
def limit_iterations(context):
|
||||
"""Prevent infinite loops by limiting iterations."""
|
||||
if context.iterations > 10:
|
||||
return False # Block execution
|
||||
return None
|
||||
|
||||
@after_llm_call
|
||||
def sanitize_response(context):
|
||||
"""Remove sensitive data from LLM responses."""
|
||||
if "API_KEY" in context.response:
|
||||
return context.response.replace("API_KEY", "[REDACTED]")
|
||||
return None
|
||||
|
||||
@before_tool_call
|
||||
def block_dangerous_tools(context):
|
||||
"""Block destructive operations."""
|
||||
if context.tool_name == "delete_database":
|
||||
return False # Block execution
|
||||
return None
|
||||
|
||||
@after_tool_call
|
||||
def log_tool_result(context):
|
||||
"""Log tool execution."""
|
||||
print(f"Tool {context.tool_name} completed")
|
||||
return None
|
||||
```
|
||||
|
||||
Differences from `@on`:
|
||||
### 2. Crew-Scoped Hooks
|
||||
|
||||
- They cover **only** the four model/tool points — no execution boundaries, no
|
||||
steps.
|
||||
- Blocking is `return False`, with no abort reason or source attached.
|
||||
- They receive the same rich contexts — `LLMCallHookContext` (with full
|
||||
executor access) and `ToolCallHookContext` — that `@on` hooks receive at the
|
||||
model/tool points.
|
||||
- Crew-scoping works the same way: apply the decorator to a method inside a
|
||||
`@CrewBase` class.
|
||||
- They support the same `agents=` / `tools=` filters.
|
||||
Apply hooks only to specific crew instances:
|
||||
|
||||
You might still prefer them for existing codebases that already use
|
||||
`return False` semantics, or when you want the point-specific typed signatures.
|
||||
For the detailed guides — context attributes, patterns, and management APIs
|
||||
(`register_*` / `unregister_*` / `clear_*`) — see:
|
||||
```python
|
||||
from crewai import CrewBase
|
||||
from crewai.project import crew
|
||||
from crewai.hooks import before_llm_call_crew, after_tool_call_crew
|
||||
|
||||
- [LLM Call Hooks →](/edge/en/learn/llm-hooks)
|
||||
- [Tool Call Hooks →](/edge/en/learn/tool-hooks)
|
||||
@CrewBase
|
||||
class MyProjCrew:
|
||||
@before_llm_call_crew
|
||||
def validate_inputs(self, context):
|
||||
# Only applies to this crew
|
||||
print(f"LLM call in {self.__class__.__name__}")
|
||||
return None
|
||||
|
||||
## Related documentation
|
||||
@after_tool_call_crew
|
||||
def log_results(self, context):
|
||||
# Crew-specific logging
|
||||
print(f"Tool result: {context.tool_result[:50]}...")
|
||||
return None
|
||||
|
||||
- [Before and After Kickoff Hooks →](/edge/en/learn/before-and-after-kickoff-hooks)
|
||||
- [Human-in-the-Loop →](/edge/en/learn/human-in-the-loop)
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential
|
||||
)
|
||||
```
|
||||
|
||||
## Hook Execution Flow
|
||||
|
||||
### LLM Call Flow
|
||||
|
||||
```
|
||||
Agent needs to call LLM
|
||||
↓
|
||||
[Before LLM Call Hooks Execute]
|
||||
├→ Hook 1: Validate iteration count
|
||||
├→ Hook 2: Add safety context
|
||||
└→ Hook 3: Log request
|
||||
↓
|
||||
If any hook returns False:
|
||||
├→ Block LLM call
|
||||
└→ Raise ValueError
|
||||
↓
|
||||
If all hooks return True/None:
|
||||
├→ LLM call proceeds
|
||||
└→ Response generated
|
||||
↓
|
||||
[After LLM Call Hooks Execute]
|
||||
├→ Hook 1: Sanitize response
|
||||
├→ Hook 2: Log response
|
||||
└→ Hook 3: Update metrics
|
||||
↓
|
||||
Final response returned
|
||||
```
|
||||
|
||||
### Tool Call Flow
|
||||
|
||||
```
|
||||
Agent needs to execute tool
|
||||
↓
|
||||
[Before Tool Call Hooks Execute]
|
||||
├→ Hook 1: Check if tool is allowed
|
||||
├→ Hook 2: Validate inputs
|
||||
└→ Hook 3: Request approval if needed
|
||||
↓
|
||||
If any hook returns False:
|
||||
├→ Block tool execution
|
||||
└→ Return error message
|
||||
↓
|
||||
If all hooks return True/None:
|
||||
├→ Tool execution proceeds
|
||||
└→ Result generated
|
||||
↓
|
||||
[After Tool Call Hooks Execute]
|
||||
├→ Hook 1: Sanitize result
|
||||
├→ Hook 2: Cache result
|
||||
└→ Hook 3: Log metrics
|
||||
↓
|
||||
Final result returned
|
||||
```
|
||||
|
||||
## Hook Context Objects
|
||||
|
||||
### LLMCallHookContext
|
||||
|
||||
Provides access to LLM execution state:
|
||||
|
||||
```python
|
||||
class LLMCallHookContext:
|
||||
executor: CrewAgentExecutor # Full executor access
|
||||
messages: list # Mutable message list
|
||||
agent: Agent # Current agent
|
||||
task: Task # Current task
|
||||
crew: Crew # Crew instance
|
||||
llm: BaseLLM # LLM instance
|
||||
iterations: int # Current iteration
|
||||
response: str | None # LLM response (after hooks)
|
||||
```
|
||||
|
||||
### ToolCallHookContext
|
||||
|
||||
Provides access to tool execution state:
|
||||
|
||||
```python
|
||||
class ToolCallHookContext:
|
||||
tool_name: str # Tool being called
|
||||
tool_input: dict # Mutable input parameters
|
||||
tool: CrewStructuredTool # Tool instance
|
||||
agent: Agent | None # Agent executing
|
||||
task: Task | None # Current task
|
||||
crew: Crew | None # Crew instance
|
||||
tool_result: str | None # Agent-facing result string (after hooks)
|
||||
raw_tool_result: Any | None # Raw Python result (after hooks)
|
||||
```
|
||||
|
||||
For typed tool outputs, `tool_result` is the string the agent sees. By default, this is JSON. If the tool uses custom formatting, it can be Markdown or another string. `raw_tool_result` is the original Python value returned by the tool.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Safety and Validation
|
||||
|
||||
```python
|
||||
@before_tool_call
|
||||
def safety_check(context):
|
||||
"""Block destructive operations."""
|
||||
dangerous = ['delete_file', 'drop_table', 'system_shutdown']
|
||||
if context.tool_name in dangerous:
|
||||
print(f"🛑 Blocked: {context.tool_name}")
|
||||
return False
|
||||
return None
|
||||
|
||||
@before_llm_call
|
||||
def iteration_limit(context):
|
||||
"""Prevent infinite loops."""
|
||||
if context.iterations > 15:
|
||||
print("⛔ Maximum iterations exceeded")
|
||||
return False
|
||||
return None
|
||||
```
|
||||
|
||||
### Human-in-the-Loop
|
||||
|
||||
```python
|
||||
@before_tool_call
|
||||
def require_approval(context):
|
||||
"""Require approval for sensitive operations."""
|
||||
sensitive = ['send_email', 'make_payment', 'post_message']
|
||||
|
||||
if context.tool_name in sensitive:
|
||||
response = context.request_human_input(
|
||||
prompt=f"Approve {context.tool_name}?",
|
||||
default_message="Type 'yes' to approve:"
|
||||
)
|
||||
|
||||
if response.lower() != 'yes':
|
||||
return False
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### Monitoring and Analytics
|
||||
|
||||
```python
|
||||
from collections import defaultdict
|
||||
import time
|
||||
|
||||
metrics = defaultdict(lambda: {'count': 0, 'total_time': 0})
|
||||
|
||||
@before_tool_call
|
||||
def start_timer(context):
|
||||
context.tool_input['_start'] = time.time()
|
||||
return None
|
||||
|
||||
@after_tool_call
|
||||
def track_metrics(context):
|
||||
start = context.tool_input.get('_start', time.time())
|
||||
duration = time.time() - start
|
||||
|
||||
metrics[context.tool_name]['count'] += 1
|
||||
metrics[context.tool_name]['total_time'] += duration
|
||||
|
||||
return None
|
||||
|
||||
# View metrics
|
||||
def print_metrics():
|
||||
for tool, data in metrics.items():
|
||||
avg = data['total_time'] / data['count']
|
||||
print(f"{tool}: {data['count']} calls, {avg:.2f}s avg")
|
||||
```
|
||||
|
||||
### Response Sanitization
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@after_llm_call
|
||||
def sanitize_llm_response(context):
|
||||
"""Remove sensitive data from LLM responses."""
|
||||
if not context.response:
|
||||
return None
|
||||
|
||||
result = context.response
|
||||
result = re.sub(r'(api[_-]?key)["\']?\s*[:=]\s*["\']?[\w-]+',
|
||||
r'\1: [REDACTED]', result, flags=re.IGNORECASE)
|
||||
return result
|
||||
|
||||
@after_tool_call
|
||||
def sanitize_tool_result(context):
|
||||
"""Remove sensitive data from tool results."""
|
||||
if not context.tool_result:
|
||||
return None
|
||||
|
||||
result = context.tool_result
|
||||
result = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
|
||||
'[EMAIL-REDACTED]', result)
|
||||
return result
|
||||
```
|
||||
|
||||
## Hook Management
|
||||
|
||||
### Clearing All Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import clear_all_global_hooks
|
||||
|
||||
# Clear all hooks at once
|
||||
result = clear_all_global_hooks()
|
||||
print(f"Cleared {result['total']} hooks")
|
||||
# Output: {'llm_hooks': (2, 1), 'tool_hooks': (1, 2), 'total': (3, 3)}
|
||||
```
|
||||
|
||||
### Clearing Specific Hook Types
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
clear_before_llm_call_hooks,
|
||||
clear_after_llm_call_hooks,
|
||||
clear_before_tool_call_hooks,
|
||||
clear_after_tool_call_hooks
|
||||
)
|
||||
|
||||
# Clear specific types
|
||||
llm_before_count = clear_before_llm_call_hooks()
|
||||
tool_after_count = clear_after_tool_call_hooks()
|
||||
```
|
||||
|
||||
### Unregistering Individual Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
unregister_before_llm_call_hook,
|
||||
unregister_after_tool_call_hook
|
||||
)
|
||||
|
||||
def my_hook(context):
|
||||
...
|
||||
|
||||
# Register
|
||||
register_before_llm_call_hook(my_hook)
|
||||
|
||||
# Later, unregister
|
||||
success = unregister_before_llm_call_hook(my_hook)
|
||||
print(f"Unregistered: {success}")
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Keep Hooks Focused
|
||||
Each hook should have a single, clear responsibility:
|
||||
|
||||
```python
|
||||
# ✅ Good - focused responsibility
|
||||
@before_tool_call
|
||||
def validate_file_path(context):
|
||||
if context.tool_name == 'read_file':
|
||||
if '..' in context.tool_input.get('path', ''):
|
||||
return False
|
||||
return None
|
||||
|
||||
# ❌ Bad - too many responsibilities
|
||||
@before_tool_call
|
||||
def do_everything(context):
|
||||
# Validation + logging + metrics + approval...
|
||||
...
|
||||
```
|
||||
|
||||
### 2. Handle Errors Gracefully
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def safe_hook(context):
|
||||
try:
|
||||
# Your logic
|
||||
if some_condition:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Hook error: {e}")
|
||||
return None # Allow execution despite error
|
||||
```
|
||||
|
||||
### 3. Modify Context In-Place
|
||||
|
||||
```python
|
||||
# ✅ Correct - modify in-place
|
||||
@before_llm_call
|
||||
def add_context(context):
|
||||
context.messages.append({"role": "system", "content": "Be concise"})
|
||||
|
||||
# ❌ Wrong - replaces reference
|
||||
@before_llm_call
|
||||
def wrong_approach(context):
|
||||
context.messages = [{"role": "system", "content": "Be concise"}]
|
||||
```
|
||||
|
||||
### 4. Use Type Hints
|
||||
|
||||
```python
|
||||
from crewai.hooks import LLMCallHookContext, ToolCallHookContext
|
||||
|
||||
def my_llm_hook(context: LLMCallHookContext) -> bool | None:
|
||||
# IDE autocomplete and type checking
|
||||
return None
|
||||
|
||||
def my_tool_hook(context: ToolCallHookContext) -> str | None:
|
||||
return None
|
||||
```
|
||||
|
||||
### 5. Clean Up in Tests
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from crewai.hooks import clear_all_global_hooks
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clean_hooks():
|
||||
"""Reset hooks before each test."""
|
||||
yield
|
||||
clear_all_global_hooks()
|
||||
```
|
||||
|
||||
## When to Use Which Hook
|
||||
|
||||
### Use LLM Hooks When:
|
||||
- Implementing iteration limits
|
||||
- Adding context or safety guidelines to prompts
|
||||
- Tracking token usage and costs
|
||||
- Sanitizing or transforming responses
|
||||
- Implementing approval gates for LLM calls
|
||||
- Debugging prompt/response interactions
|
||||
|
||||
### Use Tool Hooks When:
|
||||
- Blocking dangerous or destructive operations
|
||||
- Validating tool inputs before execution
|
||||
- Implementing approval gates for sensitive actions
|
||||
- Caching tool results
|
||||
- Tracking tool usage and performance
|
||||
- Sanitizing tool outputs
|
||||
- Rate limiting tool calls
|
||||
|
||||
### Use Both When:
|
||||
Building comprehensive observability, safety, or approval systems that need to monitor all agent operations.
|
||||
|
||||
## Alternative Registration Methods
|
||||
|
||||
### Programmatic Registration (Advanced)
|
||||
|
||||
For dynamic hook registration or when you need to register hooks programmatically:
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
register_before_llm_call_hook,
|
||||
register_after_tool_call_hook
|
||||
)
|
||||
|
||||
def my_hook(context):
|
||||
return None
|
||||
|
||||
# Register programmatically
|
||||
register_before_llm_call_hook(my_hook)
|
||||
|
||||
# Useful for:
|
||||
# - Loading hooks from configuration
|
||||
# - Conditional hook registration
|
||||
# - Plugin systems
|
||||
```
|
||||
|
||||
**Note:** For most use cases, decorators are cleaner and more maintainable.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Keep Hooks Fast**: Hooks execute on every call - avoid heavy computation
|
||||
2. **Cache When Possible**: Store expensive validations or lookups
|
||||
3. **Be Selective**: Use crew-scoped hooks when global hooks aren't needed
|
||||
4. **Monitor Hook Overhead**: Profile hook execution time in production
|
||||
5. **Lazy Import**: Import heavy dependencies only when needed
|
||||
|
||||
## Debugging Hooks
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@before_llm_call
|
||||
def debug_hook(context):
|
||||
logger.debug(f"LLM call: {context.agent.role}, iteration {context.iterations}")
|
||||
return None
|
||||
```
|
||||
|
||||
### Hook Execution Order
|
||||
|
||||
Hooks execute in registration order. If a before hook returns `False`, subsequent hooks don't execute:
|
||||
|
||||
```python
|
||||
# Register order matters!
|
||||
register_before_tool_call_hook(hook1) # Executes first
|
||||
register_before_tool_call_hook(hook2) # Executes second
|
||||
register_before_tool_call_hook(hook3) # Executes third
|
||||
|
||||
# If hook2 returns False:
|
||||
# - hook1 executed
|
||||
# - hook2 executed and returned False
|
||||
# - hook3 NOT executed
|
||||
# - Tool call blocked
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [LLM Call Hooks →](/learn/llm-hooks) - Detailed LLM hook documentation
|
||||
- [Tool Call Hooks →](/learn/tool-hooks) - Detailed tool hook documentation
|
||||
- [Before and After Kickoff Hooks →](/learn/before-and-after-kickoff-hooks) - Crew lifecycle hooks
|
||||
- [Human-in-the-Loop →](/learn/human-in-the-loop) - Human input patterns
|
||||
|
||||
## Conclusion
|
||||
|
||||
Execution hooks provide powerful control over agent runtime behavior. Use them to implement safety guardrails, approval workflows, comprehensive monitoring, and custom business logic. Combined with proper error handling, type safety, and performance considerations, hooks enable production-ready, secure, and observable agent systems.
|
||||
|
||||
@@ -240,15 +240,14 @@ from crewai import LLM
|
||||
|
||||
# After (OpenAI-compatible mode, no LiteLLM needed):
|
||||
llm = LLM(
|
||||
model="llama3",
|
||||
custom_openai=True,
|
||||
model="openai/llama3",
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key="ollama" # Ollama doesn't require a real API key
|
||||
)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Many local inference servers (Ollama, vLLM, LM Studio, llama.cpp) expose an OpenAI-compatible API. You can use `custom_openai=True` with a custom `base_url` to connect to any of them natively while keeping the model ID your gateway expects.
|
||||
Many local inference servers (Ollama, vLLM, LM Studio, llama.cpp) expose an OpenAI-compatible API. You can use the `openai/` prefix with a custom `base_url` to connect to any of them natively.
|
||||
</Tip>
|
||||
|
||||
### Step 4: Update your YAML configs
|
||||
@@ -296,92 +295,6 @@ crewai run
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
## Custom OpenAI-Compatible Endpoints
|
||||
|
||||
Many providers and local servers (Ollama, vLLM, LM Studio, llama.cpp, LiteLLM proxies, and hosted gateways) expose an **OpenAI-compatible** API. Instead of routing these through LiteLLM, you can talk to them directly with CrewAI's native OpenAI integration by setting `custom_openai=True`.
|
||||
|
||||
This is the recommended replacement for any LiteLLM provider that offers an OpenAI-compatible endpoint.
|
||||
|
||||
### How it works
|
||||
|
||||
- `custom_openai=True` forces CrewAI to use the native OpenAI SDK, regardless of the model name.
|
||||
- The model ID is passed to the endpoint without validation against OpenAI's known-model list. This lets you use arbitrary model IDs your gateway expects (for example, `anthropic/claude-sonnet-4-6` served behind an OpenAI-compatible proxy). An optional leading `openai/` routing prefix is stripped.
|
||||
- A base URL is **required**. CrewAI resolves it, in order, from:
|
||||
1. `base_url=...`
|
||||
2. `api_base=...`
|
||||
3. `OPENAI_BASE_URL` environment variable
|
||||
4. `OPENAI_API_BASE` environment variable (legacy)
|
||||
|
||||
If none are set, CrewAI raises a `ValueError` so misconfiguration fails fast instead of silently hitting `api.openai.com`.
|
||||
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="anthropic/claude-sonnet-4-6", # passed through as-is
|
||||
custom_openai=True,
|
||||
base_url="https://your-gateway.example/v1",
|
||||
api_key="your-key",
|
||||
)
|
||||
```
|
||||
|
||||
### Connect to common servers
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Ollama">
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="llama3.2:latest",
|
||||
custom_openai=True,
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key="ollama", # Ollama ignores it, but the client requires a value
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="vLLM">
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
||||
custom_openai=True,
|
||||
base_url="http://localhost:8000/v1",
|
||||
api_key="not-needed",
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="LM Studio">
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="your-loaded-model",
|
||||
custom_openai=True,
|
||||
base_url="http://localhost:1234/v1",
|
||||
api_key="lm-studio",
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Env vars">
|
||||
```bash
|
||||
export OPENAI_BASE_URL="https://your-gateway.example/v1"
|
||||
export OPENAI_API_KEY="your-key"
|
||||
```
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
# base_url is picked up from OPENAI_BASE_URL / OPENAI_API_BASE
|
||||
llm = LLM(model="anthropic/claude-sonnet-4-6", custom_openai=True)
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
If you use the `openai/` prefix with a model that isn't a known OpenAI model and pass `base_url` or `api_base` directly, CrewAI automatically treats it as a custom OpenAI-compatible endpoint. Environment variables alone do not enable automatic routing for unknown models; set `custom_openai=True` when configuring the endpoint through `OPENAI_BASE_URL` or `OPENAI_API_BASE`.
|
||||
</Tip>
|
||||
|
||||
## Quick Reference: Model String Mapping
|
||||
|
||||
Here are common migration paths from LiteLLM-dependent providers to native ones:
|
||||
@@ -408,8 +321,7 @@ llm = LLM(model="anthropic/claude-sonnet-4-20250514") # High quality
|
||||
# Ollama → OpenAI-compatible (keep using local models)
|
||||
# llm = LLM(model="ollama/llama3")
|
||||
llm = LLM(
|
||||
model="llama3",
|
||||
custom_openai=True,
|
||||
model="openai/llama3",
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key="ollama"
|
||||
)
|
||||
@@ -437,9 +349,6 @@ llm = LLM(
|
||||
<Accordion title="What about environment variables like OPENAI_API_KEY?">
|
||||
Native providers use the same environment variables you're already familiar with. No changes needed for `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, etc.
|
||||
</Accordion>
|
||||
<Accordion title="How do I connect to Groq, Together AI, or other OpenAI-compatible providers without LiteLLM?">
|
||||
Most of these providers expose an OpenAI-compatible API. Use `custom_openai=True` with their base URL and API key — see [Custom OpenAI-Compatible Endpoints](#custom-openai-compatible-endpoints). For example, Groq: `LLM(model="llama-3.1-70b-versatile", custom_openai=True, base_url="https://api.groq.com/openai/v1", api_key="...")`. The model ID is passed through untouched, so use whatever ID the provider expects.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related Resources
|
||||
|
||||
@@ -4,51 +4,49 @@ description: Learn how to use LLM call hooks to intercept, modify, and control l
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
LLM Call Hooks provide fine-grained control over language model interactions
|
||||
during agent execution. These hooks allow you to intercept LLM calls, modify
|
||||
prompts, transform responses, implement approval gates, and add custom logging
|
||||
or monitoring.
|
||||
LLM Call Hooks provide fine-grained control over language model interactions during agent execution. These hooks allow you to intercept LLM calls, modify prompts, transform responses, implement approval gates, and add custom logging or monitoring.
|
||||
|
||||
## Overview
|
||||
|
||||
LLM hooks are executed at two interception points:
|
||||
LLM hooks are executed at two critical points:
|
||||
- **Before LLM Call**: Modify messages, validate inputs, or block execution
|
||||
- **After LLM Call**: Transform responses, sanitize outputs, or modify conversation history
|
||||
|
||||
| Point | When | Hook receives |
|
||||
|-------|------|---------------|
|
||||
| `PRE_MODEL_CALL` | Before every LLM call | `LLMCallHookContext` |
|
||||
| `POST_MODEL_CALL` | After every LLM call | `LLMCallHookContext` (with `response` set) |
|
||||
## Hook Types
|
||||
|
||||
Write them with the [`@on` decorator](/edge/en/learn/execution-hooks). The
|
||||
[legacy `@before_llm_call` / `@after_llm_call` decorators](#legacy-decorators)
|
||||
keep working unchanged — both styles register on the same engine and run in one
|
||||
ordered chain.
|
||||
### Before LLM Call Hooks
|
||||
|
||||
## Hook Signature
|
||||
Executed before every LLM call, these hooks can:
|
||||
- Inspect and modify messages sent to the LLM
|
||||
- Block LLM execution based on conditions
|
||||
- Implement rate limiting or approval gates
|
||||
- Add context or system messages
|
||||
- Log request details
|
||||
|
||||
**Signature:**
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint, LLMCallHookContext
|
||||
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def before_hook(ctx: LLMCallHookContext) -> None:
|
||||
# Mutate ctx.messages in place, or
|
||||
# raise HookAborted(reason, source) to block the call
|
||||
...
|
||||
|
||||
@on(InterceptionPoint.POST_MODEL_CALL)
|
||||
def after_hook(ctx: LLMCallHookContext) -> str | None:
|
||||
# Return a string to replace ctx.response
|
||||
# Return None to keep the original response
|
||||
def before_hook(context: LLMCallHookContext) -> bool | None:
|
||||
# Return False to block execution
|
||||
# Return True or None to allow execution
|
||||
...
|
||||
```
|
||||
|
||||
Unlike the boundary and step points, the model-call points pass the rich
|
||||
`LLMCallHookContext` directly as the hook argument (there is no separate
|
||||
`ctx.payload`): mutate `ctx.messages` in place before the call, and return a
|
||||
string to replace the response after it.
|
||||
### After LLM Call Hooks
|
||||
|
||||
Blocking a call raises `ValueError("LLM call blocked by before_llm_call hook")`
|
||||
inside the executor; the `HookAborted` reason and source are recorded in
|
||||
[telemetry](/edge/en/learn/execution-hooks#telemetry).
|
||||
Executed after every LLM call, these hooks can:
|
||||
- Modify or sanitize LLM responses
|
||||
- Add metadata or formatting
|
||||
- Log response details
|
||||
- Update conversation history
|
||||
- Implement content filtering
|
||||
|
||||
**Signature:**
|
||||
```python
|
||||
def after_hook(context: LLMCallHookContext) -> str | None:
|
||||
# Return modified response string
|
||||
# Return None to keep original response
|
||||
...
|
||||
```
|
||||
|
||||
## LLM Hook Context
|
||||
|
||||
@@ -56,171 +54,49 @@ The `LLMCallHookContext` object provides comprehensive access to execution state
|
||||
|
||||
```python
|
||||
class LLMCallHookContext:
|
||||
executor: CrewAgentExecutor | LiteAgent | None # Executor (None for direct LLM calls)
|
||||
executor: CrewAgentExecutor # Full executor reference
|
||||
messages: list # Mutable message list
|
||||
agent: Agent | None # Current agent (None for direct LLM calls)
|
||||
task: Task | None # Current task (None for direct calls or LiteAgent)
|
||||
crew: Crew | None # Crew instance (None for direct calls or LiteAgent)
|
||||
llm: BaseLLM | None # LLM instance
|
||||
iterations: int # Current iteration count (0 for direct calls)
|
||||
response: str | None # LLM response (POST_MODEL_CALL only)
|
||||
agent: Agent # Current agent
|
||||
task: Task # Current task
|
||||
crew: Crew # Crew instance
|
||||
llm: BaseLLM # LLM instance
|
||||
iterations: int # Current iteration count
|
||||
response: str | None # LLM response (after hooks only)
|
||||
```
|
||||
|
||||
The context also exposes `request_human_input(prompt, default_message)`, which
|
||||
pauses live console updates and collects input from the terminal — useful for
|
||||
approval gates.
|
||||
|
||||
### Modifying Messages
|
||||
|
||||
**Important:** Always modify messages in-place:
|
||||
|
||||
```python
|
||||
# ✅ Correct - modify in-place
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def add_context(ctx: LLMCallHookContext) -> None:
|
||||
ctx.messages.append({"role": "system", "content": "Be concise"})
|
||||
def add_context(context: LLMCallHookContext) -> None:
|
||||
context.messages.append({"role": "system", "content": "Be concise"})
|
||||
|
||||
# ❌ Wrong - replaces list reference and breaks the executor
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def wrong_approach(ctx: LLMCallHookContext) -> None:
|
||||
ctx.messages = [{"role": "system", "content": "Be concise"}]
|
||||
# ❌ Wrong - replaces list reference
|
||||
def wrong_approach(context: LLMCallHookContext) -> None:
|
||||
context.messages = [{"role": "system", "content": "Be concise"}]
|
||||
```
|
||||
|
||||
## Registration Methods
|
||||
|
||||
### 1. Global Hooks
|
||||
### 1. Global Hook Registration
|
||||
|
||||
Apply to all LLM calls across all crews. Use the `agents=` filter to scope a
|
||||
hook to specific agent roles:
|
||||
Register hooks that apply to all LLM calls across all crews:
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, InterceptionPoint
|
||||
from crewai.hooks import register_before_llm_call_hook, register_after_llm_call_hook
|
||||
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def log_llm_call(ctx):
|
||||
print(f"LLM call by {ctx.agent.role} at iteration {ctx.iterations}")
|
||||
def log_llm_call(context):
|
||||
print(f"LLM call by {context.agent.role} at iteration {context.iterations}")
|
||||
return None # Allow execution
|
||||
|
||||
@on(InterceptionPoint.POST_MODEL_CALL, agents=["Researcher"])
|
||||
def log_researcher_responses(ctx):
|
||||
print(f"Response length: {len(ctx.response)}")
|
||||
register_before_llm_call_hook(log_llm_call)
|
||||
```
|
||||
|
||||
### 2. Crew-Scoped Hooks
|
||||
### 2. Decorator-Based Registration
|
||||
|
||||
Apply the same decorator to a method inside a `@CrewBase` class to scope the
|
||||
hook to that crew only:
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, InterceptionPoint
|
||||
|
||||
@CrewBase
|
||||
class MyProjCrew:
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def validate_inputs(self, ctx):
|
||||
# Only applies to this crew
|
||||
if ctx.iterations == 0:
|
||||
print(f"Starting task: {ctx.task.description}")
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential)
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### 1. Iteration Limiting
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def limit_iterations(ctx: LLMCallHookContext) -> None:
|
||||
if ctx.iterations > 15:
|
||||
raise HookAborted(reason="exceeded 15 iterations", source="loop-guard")
|
||||
```
|
||||
|
||||
### 2. Human Approval Gate
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def require_approval(ctx: LLMCallHookContext) -> None:
|
||||
if ctx.iterations > 5:
|
||||
response = ctx.request_human_input(
|
||||
prompt=f"Iteration {ctx.iterations}: Approve LLM call?",
|
||||
default_message="Press Enter to approve, or type 'no' to block:",
|
||||
)
|
||||
if response.lower() == "no":
|
||||
raise HookAborted(reason="blocked by user", source="approval-gate")
|
||||
```
|
||||
|
||||
### 3. Adding System Context
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def add_guardrails(ctx: LLMCallHookContext) -> None:
|
||||
ctx.messages.append({
|
||||
"role": "system",
|
||||
"content": "Ensure responses are factual and cite sources when possible."
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Response Sanitization
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.POST_MODEL_CALL)
|
||||
def sanitize_sensitive_data(ctx: LLMCallHookContext) -> str | None:
|
||||
if not ctx.response:
|
||||
return None
|
||||
sanitized = re.sub(r'\b\d{3}-\d{2}-\d{4}\b', '[SSN-REDACTED]', ctx.response)
|
||||
return re.sub(r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b', '[CARD-REDACTED]', sanitized)
|
||||
```
|
||||
|
||||
### 5. Debug Logging
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_MODEL_CALL)
|
||||
def debug_request(ctx: LLMCallHookContext) -> None:
|
||||
print(f"Agent: {ctx.agent.role}, iteration {ctx.iterations}, "
|
||||
f"{len(ctx.messages)} messages")
|
||||
|
||||
@on(InterceptionPoint.POST_MODEL_CALL)
|
||||
def debug_response(ctx: LLMCallHookContext) -> None:
|
||||
if ctx.response:
|
||||
print(f"Response preview: {ctx.response[:100]}...")
|
||||
```
|
||||
|
||||
## Hook Management
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
InterceptionPoint,
|
||||
clear_all_hooks,
|
||||
clear_hooks,
|
||||
get_hooks,
|
||||
unregister_hook,
|
||||
)
|
||||
|
||||
# Unregister a specific hook
|
||||
unregister_hook(InterceptionPoint.PRE_MODEL_CALL, my_hook)
|
||||
|
||||
# Clear one point, or everything (e.g. between tests)
|
||||
clear_hooks(InterceptionPoint.POST_MODEL_CALL)
|
||||
clear_all_hooks()
|
||||
|
||||
# Inspect what's registered
|
||||
print(len(get_hooks(InterceptionPoint.PRE_MODEL_CALL)))
|
||||
```
|
||||
|
||||
The legacy management API (`register_before_llm_call_hook`,
|
||||
`unregister_before_llm_call_hook`, `clear_before_llm_call_hooks`,
|
||||
`clear_all_llm_call_hooks`, `get_before_llm_call_hooks`, and their `after_`
|
||||
counterparts) operates on the same underlying registries, so either API can
|
||||
manage hooks registered by the other.
|
||||
|
||||
## Legacy Decorators
|
||||
|
||||
The original per-point decorators keep working unchanged and run in the same
|
||||
registration-order chain as `@on` hooks:
|
||||
Use decorators for cleaner syntax:
|
||||
|
||||
```python
|
||||
from crewai.hooks import before_llm_call, after_llm_call
|
||||
@@ -228,55 +104,324 @@ from crewai.hooks import before_llm_call, after_llm_call
|
||||
@before_llm_call
|
||||
def validate_iteration_count(context):
|
||||
if context.iterations > 10:
|
||||
print("⚠️ Exceeded maximum iterations")
|
||||
return False # Block execution
|
||||
return None
|
||||
|
||||
@after_llm_call(agents=["Researcher"])
|
||||
@after_llm_call
|
||||
def sanitize_response(context):
|
||||
if context.response and "API_KEY" in context.response:
|
||||
return context.response.replace("API_KEY", "[REDACTED]")
|
||||
return None
|
||||
```
|
||||
|
||||
Differences from `@on`:
|
||||
### 3. Crew-Scoped Hooks
|
||||
|
||||
- **Blocking** is `return False` from a before hook — equivalent to raising
|
||||
`HookAborted`, but without a custom reason or source for telemetry.
|
||||
- **Signatures** are point-specific: before hooks return `bool | None`, after
|
||||
hooks return `str | None`. The context object is the same
|
||||
`LLMCallHookContext`.
|
||||
- **Filters and crew-scoping** work the same way: `@before_llm_call(agents=[...])`,
|
||||
and applying the decorator to a `@CrewBase` method scopes it to that crew.
|
||||
Register hooks for a specific crew instance:
|
||||
|
||||
Prefer `@on` for new code; keep the legacy style where it is already in use —
|
||||
there is no behavioral penalty.
|
||||
```python
|
||||
@CrewBase
|
||||
class MyProjCrew:
|
||||
@before_llm_call_crew
|
||||
def validate_inputs(self, context):
|
||||
# Only applies to this crew
|
||||
if context.iterations == 0:
|
||||
print(f"Starting task: {context.task.description}")
|
||||
return None
|
||||
|
||||
@after_llm_call_crew
|
||||
def log_responses(self, context):
|
||||
# Crew-specific response logging
|
||||
print(f"Response length: {len(context.response)}")
|
||||
return None
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True
|
||||
)
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### 1. Iteration Limiting
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def limit_iterations(context: LLMCallHookContext) -> bool | None:
|
||||
max_iterations = 15
|
||||
if context.iterations > max_iterations:
|
||||
print(f"⛔ Blocked: Exceeded {max_iterations} iterations")
|
||||
return False # Block execution
|
||||
return None
|
||||
```
|
||||
|
||||
### 2. Human Approval Gate
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def require_approval(context: LLMCallHookContext) -> bool | None:
|
||||
if context.iterations > 5:
|
||||
response = context.request_human_input(
|
||||
prompt=f"Iteration {context.iterations}: Approve LLM call?",
|
||||
default_message="Press Enter to approve, or type 'no' to block:"
|
||||
)
|
||||
if response.lower() == "no":
|
||||
print("🚫 LLM call blocked by user")
|
||||
return False
|
||||
return None
|
||||
```
|
||||
|
||||
### 3. Adding System Context
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def add_guardrails(context: LLMCallHookContext) -> None:
|
||||
# Add safety guidelines to every LLM call
|
||||
context.messages.append({
|
||||
"role": "system",
|
||||
"content": "Ensure responses are factual and cite sources when possible."
|
||||
})
|
||||
return None
|
||||
```
|
||||
|
||||
### 4. Response Sanitization
|
||||
|
||||
```python
|
||||
@after_llm_call
|
||||
def sanitize_sensitive_data(context: LLMCallHookContext) -> str | None:
|
||||
if not context.response:
|
||||
return None
|
||||
|
||||
# Remove sensitive patterns
|
||||
import re
|
||||
sanitized = context.response
|
||||
sanitized = re.sub(r'\b\d{3}-\d{2}-\d{4}\b', '[SSN-REDACTED]', sanitized)
|
||||
sanitized = re.sub(r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b', '[CARD-REDACTED]', sanitized)
|
||||
|
||||
return sanitized
|
||||
```
|
||||
|
||||
### 5. Cost Tracking
|
||||
|
||||
```python
|
||||
import tiktoken
|
||||
|
||||
@before_llm_call
|
||||
def track_token_usage(context: LLMCallHookContext) -> None:
|
||||
encoding = tiktoken.get_encoding("cl100k_base")
|
||||
total_tokens = sum(
|
||||
len(encoding.encode(msg.get("content", "")))
|
||||
for msg in context.messages
|
||||
)
|
||||
print(f"📊 Input tokens: ~{total_tokens}")
|
||||
return None
|
||||
|
||||
@after_llm_call
|
||||
def track_response_tokens(context: LLMCallHookContext) -> None:
|
||||
if context.response:
|
||||
encoding = tiktoken.get_encoding("cl100k_base")
|
||||
tokens = len(encoding.encode(context.response))
|
||||
print(f"📊 Response tokens: ~{tokens}")
|
||||
return None
|
||||
```
|
||||
|
||||
### 6. Debug Logging
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def debug_request(context: LLMCallHookContext) -> None:
|
||||
print(f"""
|
||||
🔍 LLM Call Debug:
|
||||
- Agent: {context.agent.role}
|
||||
- Task: {context.task.description[:50]}...
|
||||
- Iteration: {context.iterations}
|
||||
- Message Count: {len(context.messages)}
|
||||
- Last Message: {context.messages[-1] if context.messages else 'None'}
|
||||
""")
|
||||
return None
|
||||
|
||||
@after_llm_call
|
||||
def debug_response(context: LLMCallHookContext) -> None:
|
||||
if context.response:
|
||||
print(f"✅ Response Preview: {context.response[:100]}...")
|
||||
return None
|
||||
```
|
||||
|
||||
## Hook Management
|
||||
|
||||
### Unregistering Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
unregister_before_llm_call_hook,
|
||||
unregister_after_llm_call_hook
|
||||
)
|
||||
|
||||
# Unregister specific hook
|
||||
def my_hook(context):
|
||||
...
|
||||
|
||||
register_before_llm_call_hook(my_hook)
|
||||
# Later...
|
||||
unregister_before_llm_call_hook(my_hook) # Returns True if found
|
||||
```
|
||||
|
||||
### Clearing Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
clear_before_llm_call_hooks,
|
||||
clear_after_llm_call_hooks,
|
||||
clear_all_llm_call_hooks
|
||||
)
|
||||
|
||||
# Clear specific hook type
|
||||
count = clear_before_llm_call_hooks()
|
||||
print(f"Cleared {count} before hooks")
|
||||
|
||||
# Clear all LLM hooks
|
||||
before_count, after_count = clear_all_llm_call_hooks()
|
||||
print(f"Cleared {before_count} before and {after_count} after hooks")
|
||||
```
|
||||
|
||||
### Listing Registered Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
get_before_llm_call_hooks,
|
||||
get_after_llm_call_hooks
|
||||
)
|
||||
|
||||
# Get current hooks
|
||||
before_hooks = get_before_llm_call_hooks()
|
||||
after_hooks = get_after_llm_call_hooks()
|
||||
|
||||
print(f"Registered: {len(before_hooks)} before, {len(after_hooks)} after")
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Conditional Hook Execution
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def conditional_blocking(context: LLMCallHookContext) -> bool | None:
|
||||
# Only block for specific agents
|
||||
if context.agent.role == "researcher" and context.iterations > 10:
|
||||
return False
|
||||
|
||||
# Only block for specific tasks
|
||||
if "sensitive" in context.task.description.lower() and context.iterations > 5:
|
||||
return False
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### Context-Aware Modifications
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def adaptive_prompting(context: LLMCallHookContext) -> None:
|
||||
# Add different context based on iteration
|
||||
if context.iterations == 0:
|
||||
context.messages.append({
|
||||
"role": "system",
|
||||
"content": "Start with a high-level overview."
|
||||
})
|
||||
elif context.iterations > 3:
|
||||
context.messages.append({
|
||||
"role": "system",
|
||||
"content": "Focus on specific details and provide examples."
|
||||
})
|
||||
return None
|
||||
```
|
||||
|
||||
### Chaining Hooks
|
||||
|
||||
```python
|
||||
# Multiple hooks execute in registration order
|
||||
|
||||
@before_llm_call
|
||||
def first_hook(context):
|
||||
print("1. First hook executed")
|
||||
return None
|
||||
|
||||
@before_llm_call
|
||||
def second_hook(context):
|
||||
print("2. Second hook executed")
|
||||
return None
|
||||
|
||||
@before_llm_call
|
||||
def blocking_hook(context):
|
||||
if context.iterations > 10:
|
||||
print("3. Blocking hook - execution stopped")
|
||||
return False # Subsequent hooks won't execute
|
||||
print("3. Blocking hook - execution allowed")
|
||||
return None
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep hooks focused and fast** — they run on every LLM call
|
||||
2. **Modify in-place** — always mutate `ctx.messages`, never replace the list
|
||||
3. **Use type hints** — annotate with `LLMCallHookContext` for IDE support
|
||||
4. **Abort loudly** — raise `HookAborted` with a meaningful reason and source;
|
||||
any other exception is swallowed (fail-open)
|
||||
5. **Clear hooks in tests** — call `clear_all_hooks()` between test runs
|
||||
1. **Keep Hooks Focused**: Each hook should have a single responsibility
|
||||
2. **Avoid Heavy Computation**: Hooks execute on every LLM call
|
||||
3. **Handle Errors Gracefully**: Use try-except to prevent hook failures from breaking execution
|
||||
4. **Use Type Hints**: Leverage `LLMCallHookContext` for better IDE support
|
||||
5. **Document Hook Behavior**: Especially for blocking conditions
|
||||
6. **Test Hooks Independently**: Unit test hooks before using in production
|
||||
7. **Clear Hooks in Tests**: Use `clear_all_llm_call_hooks()` between test runs
|
||||
8. **Modify In-Place**: Always modify `context.messages` in-place, never replace
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
@before_llm_call
|
||||
def safe_hook(context: LLMCallHookContext) -> bool | None:
|
||||
try:
|
||||
# Your hook logic
|
||||
if some_condition:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"⚠️ Hook error: {e}")
|
||||
# Decide: allow or block on error
|
||||
return None # Allow execution despite error
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
|
||||
```python
|
||||
from crewai.hooks import LLMCallHookContext, BeforeLLMCallHookType, AfterLLMCallHookType
|
||||
|
||||
# Explicit type annotations
|
||||
def my_before_hook(context: LLMCallHookContext) -> bool | None:
|
||||
return None
|
||||
|
||||
def my_after_hook(context: LLMCallHookContext) -> str | None:
|
||||
return None
|
||||
|
||||
# Type-safe registration
|
||||
register_before_llm_call_hook(my_before_hook)
|
||||
register_after_llm_call_hook(my_after_hook)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook Not Executing
|
||||
- Verify the hook is registered before crew execution
|
||||
- Check whether an earlier hook aborted (subsequent hooks don't run)
|
||||
- Verify hook is registered before crew execution
|
||||
- Check if previous hook returned `False` (blocks subsequent hooks)
|
||||
- Ensure hook signature matches expected type
|
||||
|
||||
### Message Modifications Not Persisting
|
||||
- Use in-place modifications: `ctx.messages.append(...)`
|
||||
- Don't replace the list: `ctx.messages = []`
|
||||
- Use in-place modifications: `context.messages.append()`
|
||||
- Don't replace the list: `context.messages = []`
|
||||
|
||||
### Response Modifications Not Working
|
||||
- Return the modified string from a `POST_MODEL_CALL` hook
|
||||
- Return the modified string from after hooks
|
||||
- Returning `None` keeps the original response
|
||||
|
||||
## Related Documentation
|
||||
## Conclusion
|
||||
|
||||
- [Execution Hooks Overview →](/edge/en/learn/execution-hooks)
|
||||
- [Tool Call Hooks →](/edge/en/learn/tool-hooks)
|
||||
- [Execution Boundary Hooks →](/edge/en/learn/execution-boundary-hooks)
|
||||
- [Step Hooks →](/edge/en/learn/step-hooks)
|
||||
LLM Call Hooks provide powerful capabilities for controlling and monitoring language model interactions in CrewAI. Use them to implement safety guardrails, approval gates, logging, cost tracking, and response sanitization. Combined with proper error handling and type safety, hooks enable robust and production-ready agent systems.
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
title: Step Hooks
|
||||
description: Intercept task and flow-method steps with PRE_STEP and POST_STEP hooks in CrewAI
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
Step hooks intercept each unit of work inside an execution: every crew **task**
|
||||
and every **flow method**. Use them to inspect or rewrite what goes into a
|
||||
step, transform what comes out, or trace step-by-step progress — without
|
||||
touching the level of individual LLM or tool calls.
|
||||
|
||||
## Overview
|
||||
|
||||
Two interception points cover steps:
|
||||
|
||||
| Point | When | `ctx.payload` |
|
||||
|-------|------|---------------|
|
||||
| `PRE_STEP` | Before a task or flow method runs | step input (see below) |
|
||||
| `POST_STEP` | After a task or flow method runs | step output (see below) |
|
||||
|
||||
What the payload holds depends on `ctx.kind`:
|
||||
|
||||
| `ctx.kind` | `PRE_STEP` payload | `POST_STEP` payload |
|
||||
|------------|--------------------|---------------------|
|
||||
| `"task"` | The context string passed to the agent | The `TaskOutput` object |
|
||||
| `"flow_method"` | The method's parameters as a `dict` | The method's return value |
|
||||
|
||||
For flow methods, positional arguments appear in the params dict under `_0`,
|
||||
`_1`, ... keys and keyword arguments under their own names; edits and
|
||||
replacements are mapped back onto the actual call.
|
||||
|
||||
## Hook Signature
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
|
||||
@on(InterceptionPoint.PRE_STEP)
|
||||
def step_hook(ctx) -> Any | None:
|
||||
# Mutate ctx.payload in place, or
|
||||
# return a non-None value to replace it, or
|
||||
# raise HookAborted(reason, source) to stop the step
|
||||
return None
|
||||
```
|
||||
|
||||
## Context Schema
|
||||
|
||||
Both points receive a `StepContext`:
|
||||
|
||||
```python
|
||||
class StepContext(InterceptionContext):
|
||||
payload: Any # Step input (pre) or step output (post)
|
||||
kind: str | None # "task" or "flow_method"
|
||||
step_name: str | None # Task name/description, or flow method name
|
||||
output: Any # POST_STEP only: same object as payload
|
||||
agent: Any # Task steps: the executing agent (else None)
|
||||
agent_role: str | None # Task steps: the agent's role (else None)
|
||||
task: Any # Task steps: the Task instance (else None)
|
||||
crew: Any # None for step points
|
||||
flow: Any # Flow-method steps: the Flow instance (else None)
|
||||
```
|
||||
|
||||
For task steps, `step_name` is the task's `name` (falling back to its
|
||||
description). For flow-method steps, it is the method name.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Step Tracing
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.POST_STEP)
|
||||
def trace_steps(ctx):
|
||||
print(f"{ctx.kind} '{ctx.step_name}' finished")
|
||||
```
|
||||
|
||||
### Rewriting Task Context
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_STEP)
|
||||
def inject_disclaimer(ctx):
|
||||
if ctx.kind != "task":
|
||||
return None
|
||||
return f"{ctx.payload}\n\nNote: treat all figures as estimates."
|
||||
```
|
||||
|
||||
### Transforming Task Output
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.POST_STEP)
|
||||
def normalize_output(ctx):
|
||||
if ctx.kind != "task":
|
||||
return None
|
||||
ctx.payload.raw = ctx.payload.raw.strip()
|
||||
```
|
||||
|
||||
<Note>
|
||||
`POST_STEP` runs before the task's output is stored, so rewrites propagate
|
||||
everywhere the output is used: downstream task context, callbacks, the final
|
||||
crew output, and the task's `output_file` on disk.
|
||||
</Note>
|
||||
|
||||
### Guarding Flow Methods
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_STEP)
|
||||
def guard_publish(ctx):
|
||||
if ctx.kind == "flow_method" and ctx.step_name == "publish":
|
||||
if not ctx.flow.state.get("reviewed"):
|
||||
raise HookAborted(reason="publish requires review", source="review-gate")
|
||||
```
|
||||
|
||||
### Filtering by Agent
|
||||
|
||||
Step hooks support the same `agents=` filter as the other points (matched
|
||||
against the executing agent's role on task steps):
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.POST_STEP, agents=["Researcher"])
|
||||
def log_research_steps(ctx):
|
||||
print(f"research step done: {ctx.step_name}")
|
||||
```
|
||||
|
||||
## Aborting a Step
|
||||
|
||||
Raising `HookAborted` in `PRE_STEP` stops the step before any agent or method
|
||||
work happens, and the abort propagates out of the execution with its reason —
|
||||
it is not swallowed. Any other exception raised by a step hook is swallowed
|
||||
(fail-open), like at every other point.
|
||||
|
||||
## Managing Hooks in Tests
|
||||
|
||||
```python
|
||||
from crewai.hooks import clear_all_hooks
|
||||
|
||||
clear_all_hooks() # Clears every point, including steps
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Execution Hooks Overview →](/edge/en/learn/execution-hooks)
|
||||
- [Execution Boundary Hooks →](/edge/en/learn/execution-boundary-hooks)
|
||||
- [LLM Call Hooks →](/edge/en/learn/llm-hooks)
|
||||
- [Tool Call Hooks →](/edge/en/learn/tool-hooks)
|
||||
@@ -4,57 +4,53 @@ description: Learn how to use tool call hooks to intercept, modify, and control
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
Tool Call Hooks provide fine-grained control over tool execution during agent
|
||||
operations. These hooks allow you to intercept tool calls, modify inputs,
|
||||
transform outputs, implement safety checks, and add comprehensive logging or
|
||||
monitoring.
|
||||
Tool Call Hooks provide fine-grained control over tool execution during agent operations. These hooks allow you to intercept tool calls, modify inputs, transform outputs, implement safety checks, and add comprehensive logging or monitoring.
|
||||
|
||||
## Overview
|
||||
|
||||
Tool hooks are executed at two interception points:
|
||||
Tool hooks are executed at two critical points:
|
||||
- **Before Tool Call**: Modify inputs, validate parameters, or block execution
|
||||
- **After Tool Call**: Transform results, sanitize outputs, or log execution details
|
||||
|
||||
| Point | When | Hook receives |
|
||||
|-------|------|---------------|
|
||||
| `PRE_TOOL_CALL` | Before every tool execution | `ToolCallHookContext` |
|
||||
| `POST_TOOL_CALL` | After every tool execution | `ToolCallHookContext` (with results set) |
|
||||
## Hook Types
|
||||
|
||||
Write them with the [`@on` decorator](/edge/en/learn/execution-hooks). The
|
||||
[legacy `@before_tool_call` / `@after_tool_call` decorators](#legacy-decorators)
|
||||
keep working unchanged — both styles register on the same engine and run in one
|
||||
ordered chain.
|
||||
### Before Tool Call Hooks
|
||||
|
||||
## Hook Signature
|
||||
Executed before every tool execution, these hooks can:
|
||||
- Inspect and modify tool inputs
|
||||
- Block tool execution based on conditions
|
||||
- Implement approval gates for dangerous operations
|
||||
- Validate parameters
|
||||
- Log tool invocations
|
||||
|
||||
**Signature:**
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint, ToolCallHookContext
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def before_hook(ctx: ToolCallHookContext) -> None:
|
||||
# Mutate ctx.tool_input in place, or
|
||||
# raise HookAborted(reason, source) to block the call
|
||||
...
|
||||
|
||||
@on(InterceptionPoint.POST_TOOL_CALL)
|
||||
def after_hook(ctx: ToolCallHookContext) -> str | None:
|
||||
# Return a string to replace ctx.tool_result
|
||||
# Return None to keep the original result
|
||||
def before_hook(context: ToolCallHookContext) -> bool | None:
|
||||
# Return False to block execution
|
||||
# Return True or None to allow execution
|
||||
...
|
||||
```
|
||||
|
||||
Unlike the boundary and step points, the tool-call points pass the rich
|
||||
`ToolCallHookContext` directly as the hook argument (there is no separate
|
||||
`ctx.payload`): mutate `ctx.tool_input` in place before the call, and return a
|
||||
string to replace the result after it.
|
||||
### After Tool Call Hooks
|
||||
|
||||
When a call is blocked, the tool does not run and the agent receives
|
||||
`"Tool execution blocked by hook. Tool: <name>"` as the result — the run
|
||||
continues. `POST_TOOL_CALL` hooks still fire on blocked calls, so monitoring
|
||||
hooks see every attempt.
|
||||
Executed after every tool execution, these hooks can:
|
||||
- Modify or sanitize tool results
|
||||
- Add metadata or formatting
|
||||
- Log execution results
|
||||
- Implement result validation
|
||||
- Transform output formats
|
||||
|
||||
**Signature:**
|
||||
```python
|
||||
def after_hook(context: ToolCallHookContext) -> str | None:
|
||||
# Return modified result string
|
||||
# Return None to keep original result
|
||||
...
|
||||
```
|
||||
|
||||
## Tool Hook Context
|
||||
|
||||
The `ToolCallHookContext` object provides comprehensive access to tool
|
||||
execution state:
|
||||
The `ToolCallHookContext` object provides comprehensive access to tool execution state:
|
||||
|
||||
```python
|
||||
class ToolCallHookContext:
|
||||
@@ -64,18 +60,11 @@ class ToolCallHookContext:
|
||||
agent: Agent | BaseAgent | None # Agent executing the tool
|
||||
task: Task | None # Current task
|
||||
crew: Crew | None # Crew instance
|
||||
tool_result: str | None # Agent-facing result string (POST_TOOL_CALL only)
|
||||
raw_tool_result: Any | None # Raw Python result (POST_TOOL_CALL only)
|
||||
tool_result: str | None # Agent-facing result string (after hooks only)
|
||||
raw_tool_result: Any | None # Raw Python result (after hooks only)
|
||||
```
|
||||
|
||||
For typed tool outputs, `tool_result` is the string the agent sees. By default,
|
||||
this is JSON. If the tool uses custom formatting, it can be Markdown or another
|
||||
string. Use `raw_tool_result` when your hook needs the typed object or
|
||||
dictionary; it is not affected by result replacement.
|
||||
|
||||
The context also exposes `request_human_input(prompt, default_message)`, which
|
||||
pauses live console updates and collects input from the terminal — useful for
|
||||
approval gates.
|
||||
For typed tool outputs, `tool_result` is the string the agent sees. By default, this is JSON. If the tool uses custom formatting, it can be Markdown or another string. Use `raw_tool_result` when your hook needs the typed object or dictionary.
|
||||
|
||||
### Modifying Tool Inputs
|
||||
|
||||
@@ -83,58 +72,83 @@ approval gates.
|
||||
|
||||
```python
|
||||
# ✅ Correct - modify in-place
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def sanitize_input(ctx: ToolCallHookContext) -> None:
|
||||
ctx.tool_input['query'] = ctx.tool_input['query'].lower()
|
||||
def sanitize_input(context: ToolCallHookContext) -> None:
|
||||
context.tool_input['query'] = context.tool_input['query'].lower()
|
||||
|
||||
# ❌ Wrong - replaces dict reference; the tool never sees it
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def wrong_approach(ctx: ToolCallHookContext) -> None:
|
||||
ctx.tool_input = {'query': 'new query'}
|
||||
# ❌ Wrong - replaces dict reference
|
||||
def wrong_approach(context: ToolCallHookContext) -> None:
|
||||
context.tool_input = {'query': 'new query'}
|
||||
```
|
||||
|
||||
## Registration Methods
|
||||
|
||||
### 1. Global Hooks
|
||||
### 1. Global Hook Registration
|
||||
|
||||
Apply to all tool calls across all crews. Use `tools=` / `agents=` filters to
|
||||
scope a hook:
|
||||
Register hooks that apply to all tool calls across all crews:
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
from crewai.hooks import register_before_tool_call_hook, register_after_tool_call_hook
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def log_tool_call(ctx):
|
||||
print(f"Tool: {ctx.tool_name}, input: {ctx.tool_input}")
|
||||
def log_tool_call(context):
|
||||
print(f"Tool: {context.tool_name}")
|
||||
print(f"Input: {context.tool_input}")
|
||||
return None # Allow execution
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["delete_file", "drop_table"])
|
||||
def block_destructive(ctx):
|
||||
raise HookAborted(reason=f"{ctx.tool_name} is not allowed", source="safety-policy")
|
||||
|
||||
@on(InterceptionPoint.POST_TOOL_CALL, tools=["web_search"], agents=["Researcher"])
|
||||
def log_search_results(ctx):
|
||||
print(f"search returned {len(ctx.tool_result or '')} chars")
|
||||
register_before_tool_call_hook(log_tool_call)
|
||||
```
|
||||
|
||||
### 2. Crew-Scoped Hooks
|
||||
### 2. Decorator-Based Registration
|
||||
|
||||
Apply the same decorator to a method inside a `@CrewBase` class to scope the
|
||||
hook to that crew only:
|
||||
Use decorators for cleaner syntax:
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, InterceptionPoint
|
||||
from crewai.hooks import before_tool_call, after_tool_call
|
||||
|
||||
@before_tool_call
|
||||
def block_dangerous_tools(context):
|
||||
dangerous_tools = ['delete_database', 'drop_table', 'rm_rf']
|
||||
if context.tool_name in dangerous_tools:
|
||||
print(f"⛔ Blocked dangerous tool: {context.tool_name}")
|
||||
return False # Block execution
|
||||
return None
|
||||
|
||||
@after_tool_call
|
||||
def sanitize_results(context):
|
||||
if context.tool_result and "password" in context.tool_result.lower():
|
||||
return context.tool_result.replace("password", "[REDACTED]")
|
||||
return None
|
||||
```
|
||||
|
||||
### 3. Crew-Scoped Hooks
|
||||
|
||||
Register hooks for a specific crew instance:
|
||||
|
||||
```python
|
||||
@CrewBase
|
||||
class MyProjCrew:
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def validate_tool_inputs(self, ctx):
|
||||
@before_tool_call_crew
|
||||
def validate_tool_inputs(self, context):
|
||||
# Only applies to this crew
|
||||
if ctx.tool_name == "web_search" and not ctx.tool_input.get("query"):
|
||||
raise HookAborted(reason="empty search query", source="input-validation")
|
||||
if context.tool_name == "web_search":
|
||||
if not context.tool_input.get('query'):
|
||||
print("❌ Invalid search query")
|
||||
return False
|
||||
return None
|
||||
|
||||
@after_tool_call_crew
|
||||
def log_tool_results(self, context):
|
||||
# Crew-specific tool logging
|
||||
print(f"✅ {context.tool_name} completed")
|
||||
return None
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential)
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True
|
||||
)
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
@@ -142,63 +156,112 @@ class MyProjCrew:
|
||||
### 1. Safety Guardrails
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def safety_check(ctx: ToolCallHookContext) -> None:
|
||||
destructive = {'delete_file', 'drop_table', 'remove_user', 'system_shutdown'}
|
||||
if ctx.tool_name in destructive:
|
||||
raise HookAborted(reason=f"{ctx.tool_name} is destructive", source="safety-policy")
|
||||
@before_tool_call
|
||||
def safety_check(context: ToolCallHookContext) -> bool | None:
|
||||
# Block tools that could cause harm
|
||||
destructive_tools = [
|
||||
'delete_file',
|
||||
'drop_table',
|
||||
'remove_user',
|
||||
'system_shutdown'
|
||||
]
|
||||
|
||||
if context.tool_name in destructive_tools:
|
||||
print(f"🛑 Blocked destructive tool: {context.tool_name}")
|
||||
return False
|
||||
|
||||
# Warn on sensitive operations
|
||||
sensitive_tools = ['send_email', 'post_to_social_media', 'charge_payment']
|
||||
if context.tool_name in sensitive_tools:
|
||||
print(f"⚠️ Executing sensitive tool: {context.tool_name}")
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### 2. Human Approval Gate
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["send_email", "make_purchase", "delete_file"])
|
||||
def require_approval(ctx: ToolCallHookContext) -> None:
|
||||
response = ctx.request_human_input(
|
||||
prompt=f"Approve {ctx.tool_name}?",
|
||||
default_message=f"Input: {ctx.tool_input}\nType 'yes' to approve:",
|
||||
)
|
||||
if response.lower() != 'yes':
|
||||
raise HookAborted(reason="denied by operator", source="approval-gate")
|
||||
@before_tool_call
|
||||
def require_approval_for_actions(context: ToolCallHookContext) -> bool | None:
|
||||
approval_required = [
|
||||
'send_email',
|
||||
'make_purchase',
|
||||
'delete_file',
|
||||
'post_message'
|
||||
]
|
||||
|
||||
if context.tool_name in approval_required:
|
||||
response = context.request_human_input(
|
||||
prompt=f"Approve {context.tool_name}?",
|
||||
default_message=f"Input: {context.tool_input}\nType 'yes' to approve:"
|
||||
)
|
||||
|
||||
if response.lower() != 'yes':
|
||||
print(f"❌ Tool execution denied: {context.tool_name}")
|
||||
return False
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### 3. Input Validation and Sanitization
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["web_search"])
|
||||
def validate_query(ctx: ToolCallHookContext) -> None:
|
||||
query = ctx.tool_input.get('query', '')
|
||||
if len(query) < 3:
|
||||
raise HookAborted(reason="search query too short", source="input-validation")
|
||||
ctx.tool_input['query'] = query.strip().lower()
|
||||
@before_tool_call
|
||||
def validate_and_sanitize_inputs(context: ToolCallHookContext) -> bool | None:
|
||||
# Validate search queries
|
||||
if context.tool_name == 'web_search':
|
||||
query = context.tool_input.get('query', '')
|
||||
if len(query) < 3:
|
||||
print("❌ Search query too short")
|
||||
return False
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL, tools=["read_file"])
|
||||
def validate_path(ctx: ToolCallHookContext) -> None:
|
||||
path = ctx.tool_input.get('path', '')
|
||||
if '..' in path or path.startswith('/'):
|
||||
raise HookAborted(reason="invalid file path", source="input-validation")
|
||||
# Sanitize query
|
||||
context.tool_input['query'] = query.strip().lower()
|
||||
|
||||
# Validate file paths
|
||||
if context.tool_name == 'read_file':
|
||||
path = context.tool_input.get('path', '')
|
||||
if '..' in path or path.startswith('/'):
|
||||
print("❌ Invalid file path")
|
||||
return False
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### 4. Result Sanitization
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.POST_TOOL_CALL)
|
||||
def sanitize_sensitive_data(ctx: ToolCallHookContext) -> str | None:
|
||||
if not ctx.tool_result:
|
||||
@after_tool_call
|
||||
def sanitize_sensitive_data(context: ToolCallHookContext) -> str | None:
|
||||
if not context.tool_result:
|
||||
return None
|
||||
|
||||
import re
|
||||
result = context.tool_result
|
||||
|
||||
# Remove API keys
|
||||
result = re.sub(
|
||||
r'(api[_-]?key|token)["\']?\s*[:=]\s*["\']?[\w-]+',
|
||||
r'\1: [REDACTED]',
|
||||
ctx.tool_result,
|
||||
flags=re.IGNORECASE,
|
||||
result,
|
||||
flags=re.IGNORECASE
|
||||
)
|
||||
return re.sub(
|
||||
|
||||
# Remove email addresses
|
||||
result = re.sub(
|
||||
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
|
||||
'[EMAIL-REDACTED]',
|
||||
result,
|
||||
result
|
||||
)
|
||||
|
||||
# Remove credit card numbers
|
||||
result = re.sub(
|
||||
r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b',
|
||||
'[CARD-REDACTED]',
|
||||
result
|
||||
)
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
### 5. Tool Usage Analytics
|
||||
@@ -207,17 +270,32 @@ def sanitize_sensitive_data(ctx: ToolCallHookContext) -> str | None:
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
tool_stats = defaultdict(lambda: {'count': 0, 'total_time': 0})
|
||||
tool_stats = defaultdict(lambda: {'count': 0, 'total_time': 0, 'failures': 0})
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def start_timer(ctx: ToolCallHookContext) -> None:
|
||||
ctx.tool_input['_start_time'] = time.time()
|
||||
@before_tool_call
|
||||
def start_timer(context: ToolCallHookContext) -> None:
|
||||
context.tool_input['_start_time'] = time.time()
|
||||
return None
|
||||
|
||||
@on(InterceptionPoint.POST_TOOL_CALL)
|
||||
def track_tool_usage(ctx: ToolCallHookContext) -> None:
|
||||
start_time = ctx.tool_input.pop('_start_time', time.time())
|
||||
tool_stats[ctx.tool_name]['count'] += 1
|
||||
tool_stats[ctx.tool_name]['total_time'] += time.time() - start_time
|
||||
@after_tool_call
|
||||
def track_tool_usage(context: ToolCallHookContext) -> None:
|
||||
start_time = context.tool_input.get('_start_time', time.time())
|
||||
duration = time.time() - start_time
|
||||
|
||||
tool_stats[context.tool_name]['count'] += 1
|
||||
tool_stats[context.tool_name]['total_time'] += duration
|
||||
|
||||
if not context.tool_result or 'error' in context.tool_result.lower():
|
||||
tool_stats[context.tool_name]['failures'] += 1
|
||||
|
||||
print(f"""
|
||||
📊 Tool Stats for {context.tool_name}:
|
||||
- Executions: {tool_stats[context.tool_name]['count']}
|
||||
- Avg Time: {tool_stats[context.tool_name]['total_time'] / tool_stats[context.tool_name]['count']:.2f}s
|
||||
- Failures: {tool_stats[context.tool_name]['failures']}
|
||||
""")
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### 6. Rate Limiting
|
||||
@@ -228,113 +306,298 @@ from datetime import datetime, timedelta
|
||||
|
||||
tool_call_history = defaultdict(list)
|
||||
|
||||
@on(InterceptionPoint.PRE_TOOL_CALL)
|
||||
def rate_limit_tools(ctx: ToolCallHookContext) -> None:
|
||||
@before_tool_call
|
||||
def rate_limit_tools(context: ToolCallHookContext) -> bool | None:
|
||||
tool_name = context.tool_name
|
||||
now = datetime.now()
|
||||
history = tool_call_history[ctx.tool_name]
|
||||
history[:] = [t for t in history if now - t < timedelta(minutes=1)]
|
||||
if len(history) >= 10:
|
||||
raise HookAborted(reason=f"rate limit exceeded for {ctx.tool_name}",
|
||||
source="rate-limiter")
|
||||
history.append(now)
|
||||
|
||||
# Clean old entries (older than 1 minute)
|
||||
tool_call_history[tool_name] = [
|
||||
call_time for call_time in tool_call_history[tool_name]
|
||||
if now - call_time < timedelta(minutes=1)
|
||||
]
|
||||
|
||||
# Check rate limit (max 10 calls per minute)
|
||||
if len(tool_call_history[tool_name]) >= 10:
|
||||
print(f"🚫 Rate limit exceeded for {tool_name}")
|
||||
return False
|
||||
|
||||
# Record this call
|
||||
tool_call_history[tool_name].append(now)
|
||||
return None
|
||||
```
|
||||
|
||||
### 7. Caching Tool Results
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
tool_cache = {}
|
||||
|
||||
def cache_key(tool_name: str, tool_input: dict) -> str:
|
||||
"""Generate cache key from tool name and input."""
|
||||
input_str = json.dumps(tool_input, sort_keys=True)
|
||||
return hashlib.md5(f"{tool_name}:{input_str}".encode()).hexdigest()
|
||||
|
||||
@before_tool_call
|
||||
def check_cache(context: ToolCallHookContext) -> bool | None:
|
||||
key = cache_key(context.tool_name, context.tool_input)
|
||||
if key in tool_cache:
|
||||
print(f"💾 Cache hit for {context.tool_name}")
|
||||
# Note: Can't return cached result from before hook
|
||||
# Would need to implement this differently
|
||||
return None
|
||||
|
||||
@after_tool_call
|
||||
def cache_result(context: ToolCallHookContext) -> None:
|
||||
if context.tool_result:
|
||||
key = cache_key(context.tool_name, context.tool_input)
|
||||
tool_cache[key] = context.tool_result
|
||||
print(f"💾 Cached result for {context.tool_name}")
|
||||
return None
|
||||
```
|
||||
|
||||
### 8. Debug Logging
|
||||
|
||||
```python
|
||||
@before_tool_call
|
||||
def debug_tool_call(context: ToolCallHookContext) -> None:
|
||||
print(f"""
|
||||
🔍 Tool Call Debug:
|
||||
- Tool: {context.tool_name}
|
||||
- Agent: {context.agent.role if context.agent else 'Unknown'}
|
||||
- Task: {context.task.description[:50] if context.task else 'Unknown'}...
|
||||
- Input: {context.tool_input}
|
||||
""")
|
||||
return None
|
||||
|
||||
@after_tool_call
|
||||
def debug_tool_result(context: ToolCallHookContext) -> None:
|
||||
if context.tool_result:
|
||||
result_preview = context.tool_result[:200]
|
||||
print(f"✅ Result Preview: {result_preview}...")
|
||||
else:
|
||||
print("⚠️ No result returned")
|
||||
return None
|
||||
```
|
||||
|
||||
## Hook Management
|
||||
|
||||
### Unregistering Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
InterceptionPoint,
|
||||
clear_all_hooks,
|
||||
clear_hooks,
|
||||
get_hooks,
|
||||
unregister_hook,
|
||||
unregister_before_tool_call_hook,
|
||||
unregister_after_tool_call_hook
|
||||
)
|
||||
|
||||
# Unregister a specific hook
|
||||
unregister_hook(InterceptionPoint.PRE_TOOL_CALL, my_hook)
|
||||
# Unregister specific hook
|
||||
def my_hook(context):
|
||||
...
|
||||
|
||||
# Clear one point, or everything (e.g. between tests)
|
||||
clear_hooks(InterceptionPoint.POST_TOOL_CALL)
|
||||
clear_all_hooks()
|
||||
|
||||
# Inspect what's registered
|
||||
print(len(get_hooks(InterceptionPoint.PRE_TOOL_CALL)))
|
||||
register_before_tool_call_hook(my_hook)
|
||||
# Later...
|
||||
success = unregister_before_tool_call_hook(my_hook)
|
||||
print(f"Unregistered: {success}")
|
||||
```
|
||||
|
||||
The legacy management API (`register_before_tool_call_hook`,
|
||||
`unregister_before_tool_call_hook`, `clear_before_tool_call_hooks`,
|
||||
`clear_all_tool_call_hooks`, `get_before_tool_call_hooks`, and their `after_`
|
||||
counterparts) operates on the same underlying registries, so either API can
|
||||
manage hooks registered by the other.
|
||||
|
||||
## Legacy Decorators
|
||||
|
||||
The original per-point decorators keep working unchanged and run in the same
|
||||
registration-order chain as `@on` hooks:
|
||||
### Clearing Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import before_tool_call, after_tool_call
|
||||
from crewai.hooks import (
|
||||
clear_before_tool_call_hooks,
|
||||
clear_after_tool_call_hooks,
|
||||
clear_all_tool_call_hooks
|
||||
)
|
||||
|
||||
# Clear specific hook type
|
||||
count = clear_before_tool_call_hooks()
|
||||
print(f"Cleared {count} before hooks")
|
||||
|
||||
# Clear all tool hooks
|
||||
before_count, after_count = clear_all_tool_call_hooks()
|
||||
print(f"Cleared {before_count} before and {after_count} after hooks")
|
||||
```
|
||||
|
||||
### Listing Registered Hooks
|
||||
|
||||
```python
|
||||
from crewai.hooks import (
|
||||
get_before_tool_call_hooks,
|
||||
get_after_tool_call_hooks
|
||||
)
|
||||
|
||||
# Get current hooks
|
||||
before_hooks = get_before_tool_call_hooks()
|
||||
after_hooks = get_after_tool_call_hooks()
|
||||
|
||||
print(f"Registered: {len(before_hooks)} before, {len(after_hooks)} after")
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Conditional Hook Execution
|
||||
|
||||
```python
|
||||
@before_tool_call
|
||||
def block_dangerous_tools(context):
|
||||
if context.tool_name in ('delete_database', 'drop_table'):
|
||||
return False # Block execution
|
||||
return None
|
||||
def conditional_blocking(context: ToolCallHookContext) -> bool | None:
|
||||
# Only block for specific agents
|
||||
if context.agent and context.agent.role == "junior_agent":
|
||||
if context.tool_name in ['delete_file', 'send_email']:
|
||||
print(f"❌ Junior agents cannot use {context.tool_name}")
|
||||
return False
|
||||
|
||||
# Only block during specific tasks
|
||||
if context.task and "sensitive" in context.task.description.lower():
|
||||
if context.tool_name == 'web_search':
|
||||
print("❌ Web search blocked for sensitive tasks")
|
||||
return False
|
||||
|
||||
@after_tool_call(tools=["web_search"])
|
||||
def sanitize_results(context):
|
||||
if context.tool_result and "password" in context.tool_result.lower():
|
||||
return context.tool_result.replace("password", "[REDACTED]")
|
||||
return None
|
||||
```
|
||||
|
||||
Differences from `@on`:
|
||||
### Context-Aware Input Modification
|
||||
|
||||
- **Blocking** is `return False` from a before hook — equivalent to raising
|
||||
`HookAborted`, but without a custom reason or source for telemetry. The agent
|
||||
sees the same `"Tool execution blocked by hook"` message.
|
||||
- **Signatures** are point-specific: before hooks return `bool | None`, after
|
||||
hooks return `str | None`. The context object is the same
|
||||
`ToolCallHookContext`.
|
||||
- **Filters and crew-scoping** work the same way:
|
||||
`@before_tool_call(tools=[...], agents=[...])`, and applying the decorator to
|
||||
a `@CrewBase` method scopes it to that crew.
|
||||
```python
|
||||
@before_tool_call
|
||||
def enhance_tool_inputs(context: ToolCallHookContext) -> None:
|
||||
# Add context based on agent role
|
||||
if context.agent and context.agent.role == "researcher":
|
||||
if context.tool_name == 'web_search':
|
||||
# Add domain restrictions for researchers
|
||||
context.tool_input['domains'] = ['edu', 'gov', 'org']
|
||||
|
||||
Prefer `@on` for new code; keep the legacy style where it is already in use —
|
||||
there is no behavioral penalty.
|
||||
# Add context based on task
|
||||
if context.task and "urgent" in context.task.description.lower():
|
||||
if context.tool_name == 'send_email':
|
||||
context.tool_input['priority'] = 'high'
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### Tool Chain Monitoring
|
||||
|
||||
```python
|
||||
tool_call_chain = []
|
||||
|
||||
@before_tool_call
|
||||
def track_tool_chain(context: ToolCallHookContext) -> None:
|
||||
tool_call_chain.append({
|
||||
'tool': context.tool_name,
|
||||
'timestamp': time.time(),
|
||||
'agent': context.agent.role if context.agent else 'Unknown'
|
||||
})
|
||||
|
||||
# Detect potential infinite loops
|
||||
recent_calls = tool_call_chain[-5:]
|
||||
if len(recent_calls) == 5 and all(c['tool'] == context.tool_name for c in recent_calls):
|
||||
print(f"⚠️ Warning: {context.tool_name} called 5 times in a row")
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep hooks focused and fast** — they run on every tool call
|
||||
2. **Modify in-place** — always mutate `ctx.tool_input`, never replace the dict
|
||||
3. **Prefer filters over conditionals** — `tools=` / `agents=` keep hook bodies small
|
||||
4. **Abort loudly** — raise `HookAborted` with a meaningful reason and source;
|
||||
any other exception is swallowed (fail-open)
|
||||
5. **Use type hints** — annotate with `ToolCallHookContext` for IDE support
|
||||
6. **Clear hooks in tests** — call `clear_all_hooks()` between test runs
|
||||
1. **Keep Hooks Focused**: Each hook should have a single responsibility
|
||||
2. **Avoid Heavy Computation**: Hooks execute on every tool call
|
||||
3. **Handle Errors Gracefully**: Use try-except to prevent hook failures
|
||||
4. **Use Type Hints**: Leverage `ToolCallHookContext` for better IDE support
|
||||
5. **Document Blocking Conditions**: Make it clear when/why tools are blocked
|
||||
6. **Test Hooks Independently**: Unit test hooks before using in production
|
||||
7. **Clear Hooks in Tests**: Use `clear_all_tool_call_hooks()` between test runs
|
||||
8. **Modify In-Place**: Always modify `context.tool_input` in-place, never replace
|
||||
9. **Log Important Decisions**: Especially when blocking tool execution
|
||||
10. **Consider Performance**: Cache expensive validations when possible
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
@before_tool_call
|
||||
def safe_validation(context: ToolCallHookContext) -> bool | None:
|
||||
try:
|
||||
# Your validation logic
|
||||
if not validate_input(context.tool_input):
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"⚠️ Hook error: {e}")
|
||||
# Decide: allow or block on error
|
||||
return None # Allow execution despite error
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
|
||||
```python
|
||||
from crewai.hooks import ToolCallHookContext, BeforeToolCallHookType, AfterToolCallHookType
|
||||
|
||||
# Explicit type annotations
|
||||
def my_before_hook(context: ToolCallHookContext) -> bool | None:
|
||||
return None
|
||||
|
||||
def my_after_hook(context: ToolCallHookContext) -> str | None:
|
||||
return None
|
||||
|
||||
# Type-safe registration
|
||||
register_before_tool_call_hook(my_before_hook)
|
||||
register_after_tool_call_hook(my_after_hook)
|
||||
```
|
||||
|
||||
## Integration with Existing Tools
|
||||
|
||||
### Wrapping Existing Validation
|
||||
|
||||
```python
|
||||
def existing_validator(tool_name: str, inputs: dict) -> bool:
|
||||
"""Your existing validation function."""
|
||||
# Your validation logic
|
||||
return True
|
||||
|
||||
@before_tool_call
|
||||
def integrate_validator(context: ToolCallHookContext) -> bool | None:
|
||||
if not existing_validator(context.tool_name, context.tool_input):
|
||||
print(f"❌ Validation failed for {context.tool_name}")
|
||||
return False
|
||||
return None
|
||||
```
|
||||
|
||||
### Logging to External Systems
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@before_tool_call
|
||||
def log_to_external_system(context: ToolCallHookContext) -> None:
|
||||
logger.info(f"Tool call: {context.tool_name}", extra={
|
||||
'tool_name': context.tool_name,
|
||||
'tool_input': context.tool_input,
|
||||
'agent': context.agent.role if context.agent else None
|
||||
})
|
||||
return None
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook Not Executing
|
||||
- Verify the hook is registered before crew execution
|
||||
- Check whether an earlier hook blocked the call (subsequent pre hooks don't run)
|
||||
- Check `tools=` / `agents=` filters against the actual tool name and agent role
|
||||
- Verify hook is registered before crew execution
|
||||
- Check if previous hook returned `False` (blocks execution and subsequent hooks)
|
||||
- Ensure hook signature matches expected type
|
||||
|
||||
### Input Modifications Not Working
|
||||
- Use in-place modifications: `ctx.tool_input['key'] = value`
|
||||
- Don't replace the dict: `ctx.tool_input = {}`
|
||||
- Use in-place modifications: `context.tool_input['key'] = value`
|
||||
- Don't replace the dict: `context.tool_input = {}`
|
||||
|
||||
### Result Modifications Not Working
|
||||
- Return the modified string from a `POST_TOOL_CALL` hook
|
||||
- Return the modified string from after hooks
|
||||
- Returning `None` keeps the original result
|
||||
- Ensure the tool actually returned a result
|
||||
|
||||
### Tool Blocked Unexpectedly
|
||||
- Check all pre hooks for `HookAborted` / `return False` conditions
|
||||
- The abort reason and source appear on the `HookDispatchedEvent` telemetry
|
||||
- Check all before hooks for blocking conditions
|
||||
- Verify hook execution order
|
||||
- Add debug logging to identify which hook is blocking
|
||||
|
||||
## Related Documentation
|
||||
## Conclusion
|
||||
|
||||
- [Execution Hooks Overview →](/edge/en/learn/execution-hooks)
|
||||
- [LLM Call Hooks →](/edge/en/learn/llm-hooks)
|
||||
- [Execution Boundary Hooks →](/edge/en/learn/execution-boundary-hooks)
|
||||
- [Step Hooks →](/edge/en/learn/step-hooks)
|
||||
Tool Call Hooks provide powerful capabilities for controlling and monitoring tool execution in CrewAI. Use them to implement safety guardrails, approval gates, input validation, result sanitization, logging, and analytics. Combined with proper error handling and type safety, hooks enable secure and production-ready agent systems with comprehensive observability.
|
||||
|
||||
@@ -4,136 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 7월 20일">
|
||||
## v1.15.5
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.5)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 기술 레지스트리 다운로드 인증
|
||||
|
||||
### 문서
|
||||
- v1.15.4에 대한 스냅샷 및 변경 로그 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 17일">
|
||||
## v1.15.4
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.4)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 기술 저장소를 실험적 상태에서 벗어나도록 승격
|
||||
|
||||
### 문서
|
||||
- Studio 문서에 흐름 추가
|
||||
|
||||
## 기여자
|
||||
|
||||
@jessemiller, @joaomdmoura, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 16일">
|
||||
## v1.15.3
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- PlusAPI 클라이언트에 조직 ID 매개변수 추가
|
||||
- @on을 중심으로 단계 가로채기 포인트 및 실행 훅 문서 재작업
|
||||
- 실행 경계 가로채기 포인트 연결
|
||||
- 일반 가로채기 훅 디스패처 추가
|
||||
- TUI(헤드리스 터미널 대체)에서 선언적 흐름 실행
|
||||
|
||||
### 버그 수정
|
||||
- OUTPUT 훅 결과와 kickoff-completed 이벤트 동기화
|
||||
- null 리포지토리 에이전트 속성 수정
|
||||
- after_llm_call 훅이 네이티브 도구 실행을 방해하지 않도록 보장
|
||||
- 핸들러가 히스토리를 잘라낼 때 턴 응답의 중복 추가 방지
|
||||
- 도구 결과 캐싱을 기본값이 아닌 선택 사항으로 설정
|
||||
- 생성 시 작성된 도구 설명 재작성 중지
|
||||
- 에이전트 및 크루 결과에서 두 이름 아래의 토큰 사용량 노출
|
||||
- kickoff 결과에 대한 호출당 사용 메트릭 보고
|
||||
- route_turn()이 falsy를 반환할 때 이전 턴의 의도를 재생하지 않도록 중지
|
||||
|
||||
### 문서화
|
||||
- 실행 훅 그룹화 업데이트 및 모든 훅 컨텍스트 문서화
|
||||
|
||||
## 기여자
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 16일">
|
||||
## v1.15.3a2
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a2)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 버그 수정
|
||||
- OUTPUT 훅 결과와 kickoff-completed 이벤트의 동기화 수정
|
||||
|
||||
### 문서
|
||||
- v1.15.3a1에 대한 스냅샷 및 변경 로그 업데이트
|
||||
|
||||
### 의존성 업데이트
|
||||
- PYSEC-2026-3447 문제를 해결하기 위해 setuptools를 0.83.0으로 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 16일">
|
||||
## v1.15.3a1
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a1)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- PlusAPI 클라이언트에 조직 ID 매개변수 추가.
|
||||
- `@on` 주위의 실행 후크 문서를 재작업하고 단계 가로채기 포인트 추가.
|
||||
- 실행 경계 가로채기 포인트 연결.
|
||||
- 일반 가로채기 후크 디스패처 추가.
|
||||
- TUI(헤드리스 터미널 대체)에서 선언적 흐름 실행.
|
||||
- 사용자 정의 OpenAI URL 개선.
|
||||
|
||||
### 버그 수정
|
||||
- null 리포지토리 에이전트 속성 수정.
|
||||
- 기본 도구 실행이 중단되지 않도록 `after_llm_call` 후크 수정.
|
||||
- 핸들러가 기록을 잘라낼 때 턴 응답을 이중으로 추가하는 것을 중지.
|
||||
- 도구 결과 캐싱을 기본값이 아닌 선택적으로 설정.
|
||||
- 생성 시 작성된 도구 설명을 재작성하는 것을 중지.
|
||||
- 에이전트 및 크루 결과에서 두 이름으로 토큰 사용량 노출.
|
||||
- 시작 결과에서 호출당 사용 메트릭 보고.
|
||||
- `route_turn()`이 falsy를 반환할 때 이전 턴의 의도를 재생하지 않도록 중지.
|
||||
- 시작 및 흐름 완료 이벤트 전에 메모리 쓰기 배수.
|
||||
|
||||
### 문서
|
||||
- 실행 후크를 그룹화하고 모든 후크 컨텍스트 문서화.
|
||||
- 실행 후크에 대한 문서 업데이트.
|
||||
|
||||
## 기여자
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 7일">
|
||||
## v1.15.2
|
||||
|
||||
|
||||
@@ -24,23 +24,15 @@ mode: "wide"
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 1. CLI로 스킬 생성
|
||||
|
||||
CLI는 스킬을 생성하는 공식 지원 방식입니다 — 디렉터리 레이아웃과 유효한 `SKILL.md`를 자동으로 스캐폴딩해 줍니다:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create code-review
|
||||
```
|
||||
|
||||
크루 프로젝트 내부(`pyproject.toml`이 있는 곳)에서는 `./skills/code-review/`가 생성되고, 프로젝트 외부에서는 현재 디렉터리에 `./code-review/`가 생성됩니다 (`--no-project`로 이 동작을 강제할 수 있습니다):
|
||||
### 1. 스킬 디렉터리 생성
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # 필수 — 지침 (미리 채워진 템플릿)
|
||||
├── SKILL.md # 필수 — 지침
|
||||
├── references/ # 선택 — 참조 문서
|
||||
├── scripts/ # 선택 — 실행 가능한 스크립트
|
||||
└── assets/ # 선택 — 정적 파일
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # 선택 — 실행 가능한 스크립트
|
||||
```
|
||||
|
||||
### 2. SKILL.md 작성
|
||||
@@ -172,65 +164,6 @@ agent = Agent(
|
||||
|
||||
---
|
||||
|
||||
## 스킬 생성, 게시 및 설치
|
||||
|
||||
스킬은 CLI로 관리되는 전체 라이프사이클을 갖습니다: **`crewai skill create`로 생성하고, `crewai skill publish`로 게시하세요** — 디렉터리를 직접 만드는 방식도 로컬 실험에는 사용할 수 있지만, CLI가 의도된 워크플로우이며 스킬 레이아웃과 프론트매터를 유효하게 유지해 줍니다.
|
||||
|
||||
### 생성
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create my-skill
|
||||
```
|
||||
|
||||
디렉터리를 스캐폴딩하고 (크루 프로젝트 내부에서는 `./skills/`에 생성) 템플릿 `SKILL.md`와 함께 빈 `scripts/`, `references/`, `assets/` 디렉터리를 만듭니다. `SKILL.md`를 편집하여 지침을 정의하세요.
|
||||
|
||||
### 게시
|
||||
|
||||
스킬 디렉터리 내부(`SKILL.md`가 있는 곳)에서 실행하세요:
|
||||
|
||||
```shell Terminal
|
||||
cd skills/my-skill
|
||||
crewai skill publish
|
||||
```
|
||||
|
||||
게시 시 `SKILL.md` 프론트매터에서 `name`, `description`, `metadata.version`을 읽어 스킬을 CrewAI 레지스트리로 푸시합니다. **게시된 스킬은 항상 조직 범위로 제한됩니다** — 도구와 마찬가지로 게시한 조직의 멤버만 스킬을 보고 설치할 수 있으며, 공개 가시성은 없습니다. 유용한 플래그:
|
||||
|
||||
| 플래그 | 효과 |
|
||||
| :--- | :--- |
|
||||
| `--org <slug>` | 특정 조직으로 게시합니다 (설정을 재정의). |
|
||||
| `--force` | git 상태 검증을 건너뜁니다 (커밋되지 않은 변경 사항 등). |
|
||||
|
||||
### 설치
|
||||
|
||||
게시된 스킬을 `@org/name` 참조로 설치합니다:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill install @acme/code-review
|
||||
```
|
||||
|
||||
크루 프로젝트 내부에서는 스킬이 `./skills/{name}/`에 설치되고, 프로젝트 외부에서는 공유 캐시인 `~/.crewai/skills/{org}/{name}/`에 저장됩니다.
|
||||
|
||||
에이전트는 레지스트리 스킬을 직접 참조할 수도 있습니다 — 런타임에 로컬 캐시(또는 프로젝트 `skills/` 디렉터리)에서 해석됩니다:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["@acme/code-review"], # registry ref, resolved locally
|
||||
)
|
||||
```
|
||||
|
||||
### 목록 조회
|
||||
|
||||
```shell Terminal
|
||||
crewai skill list
|
||||
```
|
||||
|
||||
프로젝트 `./skills/` 디렉터리와 전역 캐시 양쪽에 설치된 스킬을 버전 및 경로와 함께 보여줍니다.
|
||||
|
||||
---
|
||||
|
||||
## 크루 레벨 스킬
|
||||
|
||||
스킬을 크루에 설정하여 **모든 에이전트**에 적용할 수 있습니다:
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
title: "Studio의 Flows"
|
||||
description: "결정론적인 단계별 제어와 에이전트 지능을 결합한 이벤트 기반 워크플로우를 코드 없이 구축하세요."
|
||||
icon: "diagram-project"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**점진적 출시 중**: Studio의 Flows는 2026년 7월 20일 주간에 걸쳐 순차적으로 출시됩니다. Studio에서 아직 Flows 옵션이 보이지 않는다면 소속 조직에 아직 배포되지 않은 것이니 곧 다시 확인해 주세요.
|
||||
</Info>
|
||||
|
||||
## 개요
|
||||
|
||||
이제 Studio에서 Crew뿐 아니라 **Flows**도 구축할 수 있습니다. Flows는 어떤 단계가 어떤 순서로, 어떤 조건에서 실행될지를 직접 제어하는 이벤트 기반 워크플로우이며, 각 단계 내부의 지능적인 작업은 AI 에이전트에게 맡길 수 있습니다.
|
||||
|
||||
Flow를 만들려면 Studio를 열고 자동화를 설명한 뒤, 프롬프트 입력창 옆의 선택기에서 **Flows**를 선택하세요.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
## 왜 Flows인가?
|
||||
|
||||
Crew는 에이전트 팀이 목표를 향해 자율적으로 협업하도록 할 때 매우 유용합니다. 하지만 실제 자동화 중 상당수는 더 높은 예측 가능성이 필요합니다. 먼저 이 데이터를 가져오고, 그다음 요약하고, 마지막으로 결과를 게시하는 식으로 — 매번 같은 순서로요.
|
||||
|
||||
Flows는 두 가지를 모두 제공합니다:
|
||||
|
||||
- **필요한 곳의 결정론**: 단계가 정의된 순서와 명시적인 분기에 따라 실행되므로, 실행 결과가 예측 가능하고 반복 가능하며 디버깅하기 쉽습니다.
|
||||
- **필요한 곳의 지능**: 각 단계는 에이전트(또는 전체 crew)가 수행하므로, 요약·평가·작성·판단 같은 단계 내부의 작업은 LLM의 추론 능력을 온전히 활용합니다.
|
||||
|
||||
이 조합 덕분에 Flows는 프로덕션 자동화에 적합합니다. 구조는 보장되고, 에이전트의 자율성은 그것이 필요한 단계로만 한정됩니다.
|
||||
|
||||
## Flow 구축하기
|
||||
|
||||
원하는 것을 자연어로 설명하면 Studio Assistant가 Flow를 설계해 줍니다. 단계를 만들고, 서로 연결하고, 각 단계에 필요한 에이전트와 앱 연동을 구성합니다. 오른쪽 캔버스에는 완성된 워크플로우가 연결된 노드로 표시되며, 대화를 이어가며 반복 수정하거나 노드를 직접 편집할 수 있습니다.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
준비가 되면 **Run**으로 Flow를 처음부터 끝까지 테스트하고, **Output** 및 **Traces** 탭에서 결과를 확인한 뒤, 안정화되면 **Deploy**하세요. 프로젝트를 **Share**하거나 소스 코드를 **Download**할 수도 있습니다.
|
||||
|
||||
## 노드 유형
|
||||
|
||||
Flows는 세 가지 핵심 노드 유형으로 구성됩니다. 각 노드는 워크플로우의 한 단계이며 자유롭게 조합할 수 있습니다.
|
||||
|
||||
### Single Agent
|
||||
|
||||
Single Agent 노드는 하나의 에이전트가 하나의 집중된 작업을 수행합니다. 연동 서비스에서 데이터 가져오기, 콘텐츠 변환, 메시지 게시처럼 범위가 명확한 단계에 적합합니다.
|
||||
|
||||
에이전트 노드를 클릭하면 전체 구성이 열립니다:
|
||||
|
||||
- **Task**: 이 단계가 달성해야 할 목표와 생성해야 할 출력
|
||||
- **Profile**: 에이전트의 role, goal, backstory
|
||||
- **Model**: 에이전트를 구동하는 LLM
|
||||
- **Apps**: 에이전트가 사용할 수 있는 연동 서비스(예: Linear, Slack, HubSpot)
|
||||
- **Runtime Controls**: 실행 전 계획 수립, 위임, 메모리에 대한 토글
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
### Crews
|
||||
|
||||
Crew 노드는 여러 에이전트가 여러 작업을 협업으로 수행하는 전체 crew를 Flow의 단일 단계로 포함합니다. 데이터를 팀별로 그룹화·요약한 뒤 전달용으로 포맷을 정리하는 것처럼, 하나의 에이전트로 감당하기 어려운 단계에 사용하세요.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Crew 노드를 열면 내부 구조가 표시됩니다. 수행하는 작업, 각 작업에 배정된 에이전트, 사용하는 앱을 확인할 수 있습니다. crew는 해당 단계 내에서 자율적으로 실행된 뒤 결과를 Flow의 다음 노드로 전달합니다.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
이것이 결정론과 에이전트 지능이 결합된 패턴입니다. Flow는 crew가 *언제* 실행될지를 보장하고, crew는 작업을 *어떻게* 수행할지에 협업 지능을 더합니다.
|
||||
|
||||
### Router
|
||||
|
||||
Router 노드는 조건에 따라 Flow를 분기시켜 결과에 따라 서로 다른 경로를 따르게 합니다. 예를 들어 리드 라우팅 Flow는 유입 리드를 평가한 뒤, 고품질 리드는 영업 배정 단계로 보내고 나머지는 향후 육성을 위해 기록만 남길 수 있습니다.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Router가 있기에 Flows는 진정한 이벤트 기반 워크플로우가 됩니다. 하나의 워크플로우가 모든 경우를 처리하되, 각 실행은 데이터에 해당하는 분기만 따라갑니다 — 불필요한 단계도, 다음에 무슨 일이 일어날지에 대한 모호함도 없습니다.
|
||||
|
||||
## Agent Repository 동기화
|
||||
|
||||
Flows에서 만든 에이전트를 하나의 프로젝트에 가둘 필요가 없습니다. 모든 에이전트 노드에는 **Publish to Agent Repository** 버튼이 있어, 에이전트의 role, goal, backstory, 모델, 구성을 조직의 [Agent Repository](/ko/enterprise/features/agent-repositories)에 저장할 수 있습니다.
|
||||
|
||||
양방향으로 동작합니다:
|
||||
|
||||
- **게시(Publish)**: Flow에서 다듬은 에이전트를 리포지토리로 승격하여 다른 팀과 프로젝트에서 재사용할 수 있게 합니다.
|
||||
- **가져오기(Pull)**: 처음부터 다시 만드는 대신 리포지토리의 기존 에이전트를 새 Flow로 가져옵니다.
|
||||
|
||||
리포지토리 에이전트는 조직 전체에 동기화되므로, 공유 에이전트를 한 번 개선하면 이를 사용하는 모든 Flow가 혜택을 받습니다. 에이전트 동작을 일관되고 관리 가능하게 유지하며 중복 작업을 없앨 수 있습니다.
|
||||
|
||||
## 모범 사례
|
||||
|
||||
- 자동화에 명확한 순서나 분기 로직이 있다면 **Flow를 선택**하고, 목표까지의 경로가 열려 있다면 Crew를 선택하세요.
|
||||
- **에이전트 작업은 집중적으로 유지하세요** — 작업 설명이 명확한 Single Agent 노드가 세 가지 일을 한꺼번에 맡은 에이전트보다 안정적입니다.
|
||||
- **Router로 모든 경우를 명시적으로 처리하세요.** "아무것도 하지 않는" 경로(예: 건너뛴 리드 기록)까지 포함해 모든 실행이 빠짐없이 처리되도록 합니다.
|
||||
- **안정화된 에이전트는 Agent Repository에 게시**하여 조직이 일회성 복사본 대신 공유 라이브러리를 구축하도록 하세요.
|
||||
- **배포 전에 Run으로 테스트하고 Traces를 확인**하여 연동이나 프롬프트 문제를 조기에 발견하세요.
|
||||
|
||||
## 관련 문서
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Crew Studio" href="/ko/enterprise/features/crew-studio" icon="pencil">
|
||||
Studio에서 Crew를 구축하세요.
|
||||
</Card>
|
||||
<Card title="Agent Repositories" href="/ko/enterprise/features/agent-repositories" icon="people-group">
|
||||
조직 전체에서 에이전트를 공유하고 재사용하세요.
|
||||
</Card>
|
||||
<Card title="Flows 개념" href="/ko/concepts/flows" icon="diagram-project">
|
||||
CrewAI 프레임워크에서 Flows가 동작하는 방식을 알아보세요.
|
||||
</Card>
|
||||
<Card title="Tools & Integrations" href="/ko/enterprise/features/tools-and-integrations" icon="plug">
|
||||
에이전트가 사용할 앱을 연결하세요.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -4,136 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="20 jul 2026">
|
||||
## v1.15.5
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.5)
|
||||
|
||||
## O Que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Autenticar downloads do registro de habilidades
|
||||
|
||||
### Documentação
|
||||
- Atualizar snapshot e changelog para v1.15.4
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="17 jul 2026">
|
||||
## v1.15.4
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.4)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Promover o Repositório de Habilidades para fora do status experimental
|
||||
|
||||
### Documentação
|
||||
- Adicionar Fluxos na documentação do Studio
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@jessemiller, @joaomdmoura, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 jul 2026">
|
||||
## v1.15.3
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Adicionar parâmetro de ID da organização ao cliente PlusAPI
|
||||
- Adicionar pontos de interceptação de etapas e reformular a documentação dos hooks de execução em torno de @on
|
||||
- Conectar pontos de interceptação de limite de execução
|
||||
- Adicionar despachador de hook de interceptação genérico
|
||||
- Executar fluxos declarativos na TUI (fallback de terminal sem interface gráfica)
|
||||
|
||||
### Correções de Bugs
|
||||
- Sincronizar evento de kickoff-completed com o resultado do hook OUTPUT
|
||||
- Corrigir atributos de agente de repositório nulos
|
||||
- Garantir que os hooks after_llm_call não quebrem a execução de ferramentas nativas
|
||||
- Evitar duplicação da resposta da rodada quando um manipulador corta o histórico
|
||||
- Tornar o cache de resultados de ferramentas opcional em vez de ativado por padrão
|
||||
- Parar de reescrever a descrição da ferramenta criada na construção
|
||||
- Expor o uso de tokens sob ambos os nomes nos resultados de agente e equipe
|
||||
- Relatar métricas de uso por chamada nos resultados de kickoff
|
||||
- Parar de reproduzir a intenção da rodada anterior quando route_turn() retorna um valor falso
|
||||
|
||||
### Documentação
|
||||
- Atualizar o agrupamento de hooks de execução e documentar todos os contextos de hooks
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 jul 2026">
|
||||
## v1.15.3a2
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a2)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir a sincronização do evento kickoff-completed com o resultado do hook OUTPUT
|
||||
|
||||
### Documentação
|
||||
- Atualizar snapshot e changelog para v1.15.3a1
|
||||
|
||||
### Atualizações de Dependências
|
||||
- Atualizar setuptools para 0.83.0 para resolver PYSEC-2026-3447
|
||||
|
||||
## Contributors
|
||||
|
||||
@lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="16 jul 2026">
|
||||
## v1.15.3a1
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.3a1)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Adicionar parâmetro de ID da organização ao cliente PlusAPI.
|
||||
- Adicionar pontos de interceptação de etapas e reformular a documentação dos hooks de execução em torno de `@on`.
|
||||
- Conectar pontos de interceptação de limites de execução.
|
||||
- Adicionar despachador genérico de hooks de interceptação.
|
||||
- Executar fluxos declarativos na TUI (fallback de terminal sem cabeça).
|
||||
- Melhorar URLs personalizadas do OpenAI.
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir atributos de agente de repositório nulos.
|
||||
- Corrigir hooks `after_llm_call` para evitar quebrar a execução de ferramentas nativas.
|
||||
- Parar de adicionar a resposta da rodada duas vezes quando um manipulador reduz o histórico.
|
||||
- Tornar o cache de resultados de ferramentas opcional em vez de ativado por padrão.
|
||||
- Parar de reescrever a descrição da ferramenta autoral na construção.
|
||||
- Expor o uso de tokens sob ambos os nomes nos resultados de agente e equipe.
|
||||
- Relatar métricas de uso por chamada nos resultados de início.
|
||||
- Parar de reproduzir a intenção da rodada anterior quando `route_turn()` retorna um valor falso.
|
||||
- Esvaziar gravações de memória antes dos eventos de início e conclusão de fluxo.
|
||||
|
||||
### Documentação
|
||||
- Agrupar hooks de execução e documentar todos os contextos de hooks.
|
||||
- Atualizar a documentação para hooks de execução.
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@joaomdmoura, @lorenzejay, @lucasgomide, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="07 jul 2026">
|
||||
## v1.15.2
|
||||
|
||||
|
||||
@@ -24,23 +24,15 @@ Frequentemente você precisa de **ambos**: skills para expertise, ferramentas pa
|
||||
|
||||
## Início Rápido
|
||||
|
||||
### 1. Crie uma Skill com a CLI
|
||||
|
||||
A CLI é a forma suportada de criar uma skill — ela gera a estrutura de diretórios e um `SKILL.md` válido para você:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create code-review
|
||||
```
|
||||
|
||||
Dentro de um projeto de crew (onde o `pyproject.toml` está) isso cria `./skills/code-review/`; fora de um projeto, cria `./code-review/` no diretório atual (você pode forçar esse comportamento com `--no-project`):
|
||||
### 1. Crie um Diretório de Skill
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # Obrigatório — instruções (template pré-preenchido)
|
||||
├── SKILL.md # Obrigatório — instruções
|
||||
├── references/ # Opcional — documentos de referência
|
||||
├── scripts/ # Opcional — scripts executáveis
|
||||
└── assets/ # Opcional — arquivos estáticos
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # Opcional — scripts executáveis
|
||||
```
|
||||
|
||||
### 2. Escreva seu SKILL.md
|
||||
@@ -172,65 +164,6 @@ agent = Agent(
|
||||
|
||||
---
|
||||
|
||||
## Criando, Publicando e Instalando Skills
|
||||
|
||||
Skills têm um ciclo de vida completo gerenciado pela CLI: **crie-as com `crewai skill create`, publique-as com `crewai skill publish`** — criar diretórios à mão funciona para experimentos locais, mas a CLI é o fluxo de trabalho pretendido e mantém a estrutura e o frontmatter da sua skill válidos.
|
||||
|
||||
### Criar
|
||||
|
||||
```shell Terminal
|
||||
crewai skill create my-skill
|
||||
```
|
||||
|
||||
Gera o diretório (em `./skills/` dentro de um projeto de crew) com um `SKILL.md` de template, além dos diretórios vazios `scripts/`, `references/` e `assets/`. Edite o `SKILL.md` para definir as instruções.
|
||||
|
||||
### Publicar
|
||||
|
||||
Execute de dentro do diretório da skill (onde o `SKILL.md` está):
|
||||
|
||||
```shell Terminal
|
||||
cd skills/my-skill
|
||||
crewai skill publish
|
||||
```
|
||||
|
||||
A publicação lê `name`, `description` e `metadata.version` do frontmatter do `SKILL.md` e envia a skill para o registro da CrewAI. **Skills publicadas são sempre escopadas à sua organização** — assim como ferramentas, apenas membros da organização que publicou podem vê-las e instalá-las; não há visibilidade pública. Flags úteis:
|
||||
|
||||
| Flag | Efeito |
|
||||
| :--- | :--- |
|
||||
| `--org <slug>` | Publica sob uma organização específica (sobrepõe as configurações). |
|
||||
| `--force` | Pula a validação de estado do git (alterações não commitadas, etc.). |
|
||||
|
||||
### Instalar
|
||||
|
||||
Instale uma skill publicada pela sua referência `@org/name`:
|
||||
|
||||
```shell Terminal
|
||||
crewai skill install @acme/code-review
|
||||
```
|
||||
|
||||
Dentro de um projeto de crew, a skill é colocada em `./skills/{name}/`; fora de um projeto, vai para o cache compartilhado em `~/.crewai/skills/{org}/{name}/`.
|
||||
|
||||
Agentes também podem referenciar skills do registro diretamente — elas são resolvidas a partir do cache local (ou do diretório `skills/` do projeto) em tempo de execução:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["@acme/code-review"], # registry ref, resolved locally
|
||||
)
|
||||
```
|
||||
|
||||
### Listar
|
||||
|
||||
```shell Terminal
|
||||
crewai skill list
|
||||
```
|
||||
|
||||
Mostra as skills instaladas tanto do diretório `./skills/` do projeto quanto do cache global, com suas versões e caminhos.
|
||||
|
||||
---
|
||||
|
||||
## Skills no Nível do Crew
|
||||
|
||||
Skills podem ser definidas no crew para aplicar a **todos os agentes**:
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
title: Flows no Studio
|
||||
description: "Crie fluxos de trabalho orientados a eventos que combinam controle determinístico passo a passo com inteligência agêntica — sem escrever código."
|
||||
icon: "diagram-project"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Lançamento em andamento**: Flows no Studio está sendo liberado gradualmente durante a semana de 20 de julho de 2026. Se você ainda não vê a opção Flows no Studio, o recurso ainda não chegou à sua organização — volte em breve.
|
||||
</Info>
|
||||
|
||||
## Visão geral
|
||||
|
||||
O Studio agora permite criar **Flows** além de Crews. Flows são fluxos de trabalho orientados a eventos em que você controla exatamente quais etapas são executadas, em que ordem e sob quais condições — enquanto delega o trabalho inteligente dentro de cada etapa a agentes de IA.
|
||||
|
||||
Para criar um Flow, abra o Studio, descreva sua automação e selecione **Flows** no seletor ao lado da caixa de prompt.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
## Por que Flows?
|
||||
|
||||
Crews são ótimos quando você quer que uma equipe de agentes colabore de forma autônoma em direção a um objetivo. Mas muitas automações do mundo real precisam de mais previsibilidade: buscar estes dados primeiro, depois resumi-los, depois publicar o resultado — sempre, nessa ordem.
|
||||
|
||||
Flows oferecem os dois:
|
||||
|
||||
- **Determinismo onde importa**: as etapas são executadas em uma sequência definida com ramificações explícitas, tornando as execuções previsíveis, repetíveis e fáceis de depurar.
|
||||
- **Inteligência onde você precisa**: cada etapa é executada por um agente (ou por um crew inteiro), então o trabalho dentro da etapa — resumir, pontuar, redigir, decidir — se beneficia de todo o raciocínio do LLM.
|
||||
|
||||
Essa combinação é o que torna os Flows adequados para automações em produção: a estrutura é garantida e a agência fica restrita às etapas que precisam dela.
|
||||
|
||||
## Criando um Flow
|
||||
|
||||
Descreva o que você quer em linguagem natural e o Studio Assistant projeta o Flow para você — criando as etapas, conectando-as e configurando os agentes e as integrações de apps de que cada etapa precisa. O canvas à direita mostra o fluxo resultante como nós conectados, e você pode continuar iterando por conversa ou editar qualquer nó diretamente.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Quando estiver pronto, use **Run** para testar o Flow de ponta a ponta, inspecione os resultados nas abas **Output** e **Traces** e faça o **Deploy** quando estiver estável. Você também pode compartilhar o projeto (**Share**) ou baixar o código-fonte (**Download**).
|
||||
|
||||
## Tipos de nós
|
||||
|
||||
Flows são compostos por três tipos principais de nós. Cada nó é uma etapa do fluxo de trabalho, e você pode combiná-los livremente.
|
||||
|
||||
### Single Agent
|
||||
|
||||
Um nó Single Agent executa um agente em uma única tarefa focada — ideal para etapas bem delimitadas, como buscar dados de uma integração, transformar conteúdo ou publicar uma mensagem.
|
||||
|
||||
Ao clicar em um nó de agente, você abre sua configuração completa:
|
||||
|
||||
- **Task**: o que essa etapa deve realizar e qual saída deve produzir
|
||||
- **Profile**: o papel (role), o objetivo (goal) e a história (backstory) do agente
|
||||
- **Model**: qual LLM alimenta o agente
|
||||
- **Apps**: as integrações que o agente pode usar (ex.: Linear, Slack, HubSpot)
|
||||
- **Runtime Controls**: opções para planejar antes de executar, delegação e memória
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
### Crews
|
||||
|
||||
Um nó Crew incorpora um crew inteiro — múltiplos agentes colaborando em múltiplas tarefas — como uma única etapa do seu Flow. Use-o quando uma etapa for rica demais para um único agente, como agrupar e resumir dados por equipe e depois formatar o resultado para entrega.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Ao abrir um nó Crew, você vê sua estrutura interna: as tarefas que ele executa, os agentes atribuídos a cada uma e os apps que eles usam. O crew é executado de forma autônoma dentro da etapa e entrega sua saída ao próximo nó do Flow.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Esse é o padrão determinístico-mais-agêntico em ação: o Flow garante *quando* o crew é executado, e o crew traz inteligência colaborativa para *como* o trabalho é feito.
|
||||
|
||||
### Router
|
||||
|
||||
Um nó Router ramifica o Flow com base em condições, para que resultados diferentes sigam caminhos diferentes. Por exemplo, um Flow de roteamento de leads pode pontuar os leads recebidos e encaminhar os de alta qualidade para uma etapa de atribuição de vendas, enquanto registra os demais para nutrição futura.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Os Routers são o que torna os Flows verdadeiramente orientados a eventos: o mesmo fluxo de trabalho lida com todos os casos, mas cada execução segue apenas o ramo que seus dados justificam — sem etapas desperdiçadas, sem ambiguidade sobre o que acontece a seguir.
|
||||
|
||||
## Sincronização com o Agent Repository
|
||||
|
||||
Os agentes que você cria em Flows não precisam ficar presos a um único projeto. Todo nó de agente inclui um botão **Publish to Agent Repository**, que salva o agente — papel, objetivo, história, modelo e configuração — no [Agent Repository](/pt-BR/enterprise/features/agent-repositories) da sua organização.
|
||||
|
||||
Isso funciona nos dois sentidos:
|
||||
|
||||
- **Publicar**: promova um agente refinado em um Flow para o repositório, para que outras equipes e projetos possam reutilizá-lo.
|
||||
- **Importar**: traga um agente existente do repositório para um novo Flow em vez de recriá-lo do zero.
|
||||
|
||||
Como os agentes do repositório são sincronizados em toda a organização, uma melhoria feita em um agente compartilhado beneficia todos os Flows que o utilizam — mantendo o comportamento dos agentes consistente, governado e sem duplicação de esforço.
|
||||
|
||||
## Boas práticas
|
||||
|
||||
- **Escolha um Flow** quando a automação tiver uma sequência clara ou lógica de ramificação; escolha um Crew quando o caminho até o objetivo for aberto.
|
||||
- **Mantenha as tarefas dos agentes focadas** — um nó Single Agent com uma descrição de tarefa enxuta é mais confiável do que um agente encarregado de três coisas.
|
||||
- **Use Routers para tratar todos os casos explicitamente**, inclusive o caminho "não fazer nada" (ex.: registrar leads ignorados), para que as execuções fiquem totalmente contabilizadas.
|
||||
- **Publique agentes estáveis no Agent Repository** para que sua organização construa uma biblioteca compartilhada em vez de cópias paralelas.
|
||||
- **Teste com Run e inspecione os Traces** antes do deploy para detectar problemas de integração ou de prompt com antecedência.
|
||||
|
||||
## Relacionados
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Crew Studio" href="/pt-BR/enterprise/features/crew-studio" icon="pencil">
|
||||
Crie Crews no Studio.
|
||||
</Card>
|
||||
<Card title="Agent Repositories" href="/pt-BR/enterprise/features/agent-repositories" icon="people-group">
|
||||
Compartilhe e reutilize agentes em toda a sua organização.
|
||||
</Card>
|
||||
<Card title="Conceitos de Flows" href="/pt-BR/concepts/flows" icon="diagram-project">
|
||||
Saiba como os Flows funcionam no framework CrewAI.
|
||||
</Card>
|
||||
<Card title="Tools & Integrations" href="/pt-BR/enterprise/features/tools-and-integrations" icon="plug">
|
||||
Conecte os apps que seus agentes usam.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB |
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ class ContentApprovalFlow(Flow):
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| الترويسة | الوصف |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
| `X-Signature` | توقيع HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | الطابع الزمني Unix عند توقيع الطلب |
|
||||
|
||||
#### التحقق
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ All webhooks receive a JSON payload with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Please review this article for publication.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Each webhook request includes these headers:
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Unix timestamp when the request was signed |
|
||||
| `X-Signature` | HMAC-SHA256 signature: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Unix timestamp when the request was signed |
|
||||
|
||||
#### Verification
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Flow가 인간 피드백을 위해 일시 중지되면, 요청 데이터를 자
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "이 기사의 게시를 검토해 주세요.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Content-Type: application/json
|
||||
|
||||
| 헤더 | 설명 |
|
||||
|------|------|
|
||||
| `X-CrewAI-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
| `X-Signature` | HMAC-SHA256 서명: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | 요청이 서명된 Unix 타임스탬프 |
|
||||
|
||||
#### 검증
|
||||
|
||||
|
||||
@@ -387,26 +387,27 @@ Todos os webhooks recebem um payload JSON com esta estrutura:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "new_request",
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "pending",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"output": "Content to review...",
|
||||
"emit": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
"event": "new_request",
|
||||
"request": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"flow_id": "flow_abc123",
|
||||
"method_name": "review_article",
|
||||
"message": "Por favor, revise este artigo para publicação.",
|
||||
"emit_options": ["approved", "rejected", "request_changes"],
|
||||
"state": {
|
||||
"article_id": 12345,
|
||||
"author": "john@example.com",
|
||||
"category": "technology"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z"
|
||||
},
|
||||
"metadata": {},
|
||||
"created_at": "2026-01-14T12:00:00Z",
|
||||
"callback_url": "https://app.crewai.com/crewai_plus/api/v1/human_feedback_requests/{id}/respond?token={response_token}",
|
||||
"response_token": "secure-token-string",
|
||||
"deployment_id": 456,
|
||||
"deployment_name": "Content Review Flow",
|
||||
"organization_id": 789,
|
||||
"deployment": {
|
||||
"id": 456,
|
||||
"name": "Content Review Flow",
|
||||
"organization_id": 789
|
||||
},
|
||||
"callback_url": "https://api.crewai.com/...",
|
||||
"assigned_to_email": "reviewer@company.com"
|
||||
}
|
||||
```
|
||||
@@ -444,8 +445,8 @@ Cada requisição de webhook inclui estes headers:
|
||||
|
||||
| Header | Descrição |
|
||||
|--------|-----------|
|
||||
| `X-CrewAI-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-CrewAI-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
| `X-Signature` | Assinatura HMAC-SHA256: `sha256=<hex_digest>` |
|
||||
| `X-Timestamp` | Timestamp Unix de quando a requisição foi assinada |
|
||||
|
||||
#### Verificação
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user