Compare commits

..

1 Commits

Author SHA1 Message Date
theCyberTech
0cc43b2720 feat: replace advisory pip-audit with blocking vuln process
New vulnerability scan process:
1. Run pip-audit without ignores on every PR
2. Classify vulns as direct or transitive (checks against all monorepo pyproject.toml files)
3. Direct vulns: auto-fix with pip-audit --fix and commit the bump to the PR branch
4. Transitive vulns: add to ignore list and create a GitHub issue for tracking
5. Re-run pip-audit with transitive ignores — PR passes only if direct vulns are resolved
6. Scheduled runs also validate that previously ignored vulns are still unfixable

Removes continue-on-error: true so the action actually blocks.
2026-06-06 15:48:26 +08:00
15971 changed files with 21300 additions and 3262901 deletions

View File

@@ -29,30 +29,4 @@ jobs:
lib/crewai/src/crewai/cli/templates/**
**/*.json
**/test_durations/**
**/cassettes/**
python-diff-size:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- name: Enforce Python diff size limit
env:
MAX: "1500"
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
# Three-dot base...head == merge-base(base, head)..head: matches GitHub's
# "Files changed" diff and ignores the synthetic merge commit at HEAD.
# Sum added + deleted lines across changed .py files; skip binaries ("-").
total=$(git diff --numstat "$BASE_SHA...$HEAD_SHA" -- '*.py' \
| awk '$1 != "-" && $2 != "-" { sum += $1 + $2 } END { print sum + 0 }')
echo "Python churn: $total lines (limit $MAX)"
if [ "$total" -gt "$MAX" ]; then
echo "::error::Python changes total $total lines, over the $MAX-line limit. Split into smaller PRs."
git diff --numstat "$BASE_SHA...$HEAD_SHA" -- '*.py' | sort -rn
exit 1
fi
**/cassettes/**

View File

@@ -9,7 +9,9 @@ on:
- cron: '0 9 * * 1'
permissions:
contents: read
contents: write
pull-requests: write
issues: write
jobs:
pip-audit:
@@ -18,7 +20,7 @@ jobs:
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
persist-credentials: ${{ github.event_name == 'pull_request' }}
- name: Restore global uv cache
id: cache-restore
@@ -46,48 +48,197 @@ jobs:
run: uv pip install pip-audit
- name: Run pip-audit
id: audit
run: |
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 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-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
uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json || true
# Intentionally ignore exit code — we parse the JSON ourselves below.
- name: Classify vulnerabilities
id: classify
run: |
set -euo pipefail
python3 << 'PYEOF'
import json, sys, glob, re
from pathlib import Path
# Collect direct deps from all pyproject.toml files in the monorepo
try:
import tomllib
except ImportError:
import tomli as tomllib
direct_deps = set()
for toml_path in glob.glob("**/pyproject.toml", recursive=True):
if "templates/" in toml_path or "node_modules/" in toml_path:
continue
try:
with open(toml_path, "rb") as f:
data = tomllib.load(f)
except Exception:
continue
project = data.get("project", {})
for dep_str in project.get("dependencies", []):
name = re.split(r"[><=!~\[]", dep_str)[0].strip().lower()
direct_deps.add(name)
for group_deps in project.get("optional-dependencies", {}).values():
for dep_str in group_deps:
name = re.split(r"[><=!~\[]", dep_str)[0].strip().lower()
direct_deps.add(name)
for group_deps in data.get("dependency-groups", {}).values():
if isinstance(group_deps, list):
for dep_str in group_deps:
if isinstance(dep_str, str):
name = re.split(r"[><=!~\[]", dep_str)[0].strip().lower()
direct_deps.add(name)
# Load pip-audit report
try:
with open("pip-audit-report.json") as f:
report = json.load(f)
except FileNotFoundError:
print("::error::pip-audit report not found")
sys.exit(1)
deps = report.get("dependencies", [])
vulns = [d for d in deps if d.get("vulns")]
if not vulns:
print("No vulnerabilities found")
Path("direct_vulns.txt").write_text("")
Path("transitive_vulns.txt").write_text("")
Path("transitive_ids.txt").write_text("")
sys.exit(0)
direct_vulns = []
transitive_vulns = []
transitive_ids = []
for dep in vulns:
name = dep["name"]
version = dep["version"]
is_direct = name.lower() in direct_deps
for v in dep["vulns"]:
entry = f"{name}=={version} ({v['id']})"
if is_direct:
direct_vulns.append(entry)
else:
transitive_vulns.append(entry)
transitive_ids.append(v['id'])
Path("direct_vulns.txt").write_text("\n".join(direct_vulns) if direct_vulns else "")
Path("transitive_vulns.txt").write_text("\n".join(transitive_vulns) if transitive_vulns else "")
Path("transitive_ids.txt").write_text("\n".join(transitive_ids) if transitive_ids else "")
print(f"Direct: {len(direct_vulns)}, Transitive: {len(transitive_vulns)}")
for v in direct_vulns:
print(f" DIRECT: {v}")
for v in transitive_vulns:
print(f" TRANSITIVE: {v}")
PYEOF
# Set outputs
if [ -s direct_vulns.txt ]; then
echo "has_direct=true" >> "$GITHUB_OUTPUT"
else
echo "has_direct=false" >> "$GITHUB_OUTPUT"
fi
if [ -s transitive_vulns.txt ]; then
echo "has_transitive=true" >> "$GITHUB_OUTPUT"
else
echo "has_transitive=false" >> "$GITHUB_OUTPUT"
fi
- name: Attempt fix for direct vulnerabilities
if: github.event_name == 'pull_request' && steps.classify.outputs.has_direct == 'true'
id: fix
run: |
set -euo pipefail
echo "Attempting to fix direct vulnerabilities..."
cat direct_vulns.txt
# Try pip-audit --fix to bump direct deps
uv run pip-audit --fix --skip-editable 2>&1 || true
# Check if uv.lock changed
if git diff --quiet uv.lock; then
echo "fixed=false" >> "$GITHUB_OUTPUT"
echo "::warning::Could not auto-fix direct vulnerabilities. Manual intervention required."
else
echo "fixed=true" >> "$GITHUB_OUTPUT"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add uv.lock
git commit -m "fix: bump dependencies to resolve security vulnerabilities
Auto-fixed by vulnerability-scan workflow.
Resolved: $(cat direct_vulns.txt | tr '\n' ', ')"
git push
fi
- name: Add transitive vulns to ignore list and create issues
if: steps.classify.outputs.has_transitive == 'true'
id: ignore
run: |
set -euo pipefail
# Build --ignore-vuln flags from transitive vuln IDs
IGNORE_FLAGS=""
while IFS= read -r vuln_id; do
if [ -n "$vuln_id" ]; then
IGNORE_FLAGS="$IGNORE_FLAGS --ignore-vuln $vuln_id"
fi
done < transitive_ids.txt
echo "ignore_flags=$IGNORE_FLAGS" >> "$GITHUB_OUTPUT"
# Create GitHub issues for transitive vulns
while IFS= read -r line; do
if [ -z "$line" ]; then continue; fi
VULN_ID=$(echo "$line" | grep -oE '[A-Z]+-[0-9]+-[0-9]+|GHSA-[a-z0-9-]+' || true)
PKG=$(echo "$line" | cut -d'=' -f1)
# Check if issue already exists
EXISTING=$(gh issue list --label "security,transitive-vuln" --state open --json title \
--jq ".[] | select(.title | contains(\"$VULN_ID\"))" || true)
if [ -z "$EXISTING" ]; then
gh issue create \
--title "🔒 Transitive vulnerability: $VULN_ID in $PKG" \
--label "security,transitive-vuln" \
--body "## Transitive Dependency Vulnerability
**Package:** \`$line\`
**Vulnerability:** $VULN_ID
**Status:** No fix available upstream
This vulnerability is in a transitive dependency and cannot be fixed directly. It has been added to the pip-audit ignore list until an upstream fix is available.
### Action Required
- [ ] Monitor upstream for a fix
- [ ] Remove from ignore list once fixed
- [ ] Close this issue when resolved
_Auto-created by vulnerability-scan workflow._"
fi
done < <(cat transitive_vulns.txt)
- name: Re-run pip-audit with transitive ignores
if: steps.classify.outputs.has_transitive == 'true'
id: audit-final
run: |
IGNORE_FLAGS="${{ steps.ignore.outputs.ignore_flags }}"
eval uv run pip-audit --desc --aliases --skip-editable --format json \
--output pip-audit-report.json \
$IGNORE_FLAGS
- name: Fail if direct vulnerabilities remain unfixed
if: steps.classify.outputs.has_direct == 'true' && steps.fix.outputs.fixed != 'true'
run: |
echo "::error::Direct vulnerabilities found that could not be auto-fixed:"
cat direct_vulns.txt
echo ""
echo "Fix these manually or run: pip-audit --fix"
exit 1
- name: Display results
if: always()
@@ -97,23 +248,8 @@ jobs:
echo '```json' >> $GITHUB_STEP_SUMMARY
cat pip-audit-report.json | python3 -m json.tool >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
# Fail if vulnerabilities found
python3 -c "
import json, sys
with open('pip-audit-report.json') as f:
data = json.load(f)
vulns = [d for d in data.get('dependencies', []) if d.get('vulns')]
if vulns:
print(f'::error::Found vulnerabilities in {len(vulns)} package(s)')
for v in vulns:
for vuln in v['vulns']:
print(f' - {v[\"name\"]}=={v[\"version\"]}: {vuln[\"id\"]}')
sys.exit(1)
print('No known vulnerabilities found')
"
else
echo "::error::pip-audit failed to produce a report. Check the pip-audit step logs."
exit 1
echo "::error::pip-audit failed to produce a report."
fi
- name: Upload pip-audit report
@@ -132,4 +268,3 @@ jobs:
~/.local/share/uv
.venv
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}

2
.gitignore vendored
View File

@@ -31,5 +31,3 @@ chromadb-*.lock
blogs/*
secrets/*
UNKNOWN.egg-info/
demos/*
.crewai/*

View File

@@ -47,7 +47,6 @@ repos:
--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

142
AGENTS.md
View File

@@ -1,142 +0,0 @@
# Docs contributor guide
The `docs/` directory is published at [docs.crewai.com](https://docs.crewai.com)
by [Mintlify](https://www.mintlify.com/). Mintlify watches `docs/docs.json`
and the MDX files referenced from it.
## TL;DR for editing docs
- Edit MDX under `docs/edge/<lang>/...` (e.g. `docs/edge/en/concepts/agents.mdx`).
- Your change ships under the **Edge** version selector the moment it merges
to `main`. Edge follows `main` and is the channel for unreleased work.
- On release cut, the current Edge state is frozen into `docs/v<X.Y.Z>/` and
that snapshot becomes the new default version in the selector (tag:
`Latest`). Canonical URLs (`/<lang>/...`) auto-redirect to the new default.
- Never modify files under `docs/v*/`. Those are frozen release snapshots
and the `docs-snapshots` CI guard rejects writes. The only exception is a
release-cut PR (auto-generated by `devtools release` or the manual
`scripts/docs/freeze_current_edge.py` wrapper), which uses a
`[docs-freeze]` title prefix to opt out.
- Never delete or rename files under `docs/images/`. Images are append-only.
See [Images](#images) below.
## The version model
The site has one rolling channel (Edge) plus one frozen snapshot per
release.
```
docs/
edge/ <-- Edge sources (you edit here)
en/...
pt-BR/ ko/ ar/
enterprise-api.*.yaml
v1.14.7/ <-- frozen snapshot of v1.14.7
en/...
pt-BR/ ko/ ar/
enterprise-api.*.yaml
v1.14.6/...
...
images/ <-- shared, append-only
docs.json <-- Mintlify config: navigation + redirects
```
`docs/docs.json` lists one navigation block per version per language. Edge
points at `docs/edge/<lang>/...`; every other version points at its own
`docs/v<X.Y.Z>/<lang>/...` subtree. Mintlify scopes both the sidebar and the
in-site search to whichever version the reader selects, so picking
`v1.10.0` genuinely shows the v1.10.0 docs (and only those).
### URLs and canonical redirects
Each Mintlify version corresponds to its own URL prefix:
- Edge: `/edge/<lang>/<page>` (e.g. `/edge/en/concepts/agents`)
- Frozen: `/v<X.Y.Z>/<lang>/<page>` (e.g. `/v1.14.7/en/concepts/agents`)
External links to the old, unversioned `/<lang>/<page>` URLs would 404 under
this layout. To keep them working, `docs.json` ships wildcard redirects:
```jsonc
{ "source": "/en/:slug*", "destination": "/v1.14.7/en/:slug*", "permanent": false }
```
The release-cut step rewrites the destination on every release so canonical
`/<lang>/...` URLs always resolve to the latest stable docs.
## Lifecycle
1. **During development.** You add or edit pages under
`docs/edge/<lang>/...` in normal PRs. They land in Edge as soon as the PR
merges. Both `/edge/<lang>/<page>` and the version selector's `Edge` entry
reflect the change immediately.
2. **Release cut.** The release engineer runs `devtools release X.Y.Z`. As
part of that flow the CLI opens a `[docs-freeze]` PR that copies Edge into
`docs/v<X.Y.Z>/`, rewrites internal OpenAPI references, updates
`docs/docs.json` to make `v<X.Y.Z>` the new default + `Latest`, and rewires
the canonical-URL redirects to the new default. The PR must merge before
the tag and PyPI publish run.
3. **After release.** Edge keeps rolling. Patch fixes to the just-released
docs go into Edge and ship with the next release. We do not back-edit
frozen snapshots.
See [`RELEASING.md`](RELEASING.md) for the full release runbook.
## Images
Snapshots share a single `docs/images/` directory. If an image is deleted
or renamed, every frozen snapshot that referenced it breaks. So the rule
is:
- Adding new images is always fine.
- Deleting or renaming an existing image fails CI unless the PR is a
`[docs-freeze]` release-cut PR.
- If an asset is wrong, add a new file with a new name and reference the
new name in the Edge MDX (`docs/edge/<lang>/...`). Leave the old file
alone.
## Local preview
Install the Mintlify CLI and run from `docs/`:
```bash
npm i -g mintlify
mintlify dev
```
Use the version selector at the top of the rendered page to switch between
Edge and frozen versions.
To check links across every version:
```bash
mintlify broken-links
```
CI runs the broken-links check on every PR that touches `docs/**` via
[`.github/workflows/docs-broken-links.yml`](.github/workflows/docs-broken-links.yml).
## Scripts
- `scripts/docs/freeze_historical_versions.py` — one-time migration that
reconstructed `docs/v1.10.0/` through `docs/v1.14.7/` from git tags. You
should not need to run this again.
- `scripts/docs/prefix_version_paths.py` — one-time migration that switched
`docs/docs.json` to directory-based versioning, inserted Edge, and added
the canonical-URL redirects. You should not need to run this again.
- `scripts/docs/freeze_current_edge.py` — thin CLI wrapper around
`crewai_devtools.docs_versioning.freeze`. `devtools release` calls the
same module during its docs PR step; this script is the manual escape
hatch (e.g. retroactively freezing a forgotten release).
## CI guards
- [`.github/workflows/docs-snapshots.yml`](.github/workflows/docs-snapshots.yml)
enforces the two rules above (frozen snapshots immutable, images
append-only). Both checks accept the `[docs-freeze]` PR-title escape
hatch.
- [`.github/workflows/docs-broken-links.yml`](.github/workflows/docs-broken-links.yml)
runs `mintlify broken-links` against the whole site, so adding a new
page or moving a snapshot file that breaks a link will fail CI.

View File

@@ -601,19 +601,6 @@ CrewAI is open-source and we welcome contributions. If you're looking to contrib
- Send a pull request.
- We appreciate your input!
### Contributing to the docs
The site at [docs.crewai.com](https://docs.crewai.com) is published from
`docs/` by [Mintlify](https://www.mintlify.com/). The docs use directory-based
versioning: edits to `docs/edge/<lang>/...` (e.g.
`docs/edge/en/concepts/agents.mdx`) land under the **Edge** version selector
immediately and are frozen into a new versioned snapshot under
`docs/v<X.Y.Z>/` at the next release cut. Frozen snapshots are immutable — CI
rejects PRs that modify them without a `[docs-freeze]` title prefix. The
release CLI (`devtools release`) handles the freeze automatically; see
[`AGENTS.md`](AGENTS.md) for the full contributor guide and
[`RELEASING.md`](RELEASING.md) for the release-cut runbook.
### Installing Dependencies
```bash

View File

@@ -11,99 +11,7 @@ from typing import Any
from dotenv import load_dotenv
import pytest
def _patch_vcrpy_aiohttp_compat() -> None:
"""Keep vcrpy's aiohttp stub working under aiohttp 3.14.0.
aiohttp 3.14.0 (pulled in to fix GHSA-jg22-mg44-37j8 and GHSA-hg6j-4rv6-33pg):
* removed ``aiohttp.streams.AsyncStreamReaderMixin`` (folded into ``StreamReader``),
which vcrpy's ``MockStream`` still subclasses -- vcr's patch machinery then raises
``AttributeError`` at collection time; and
* added a required ``stream_writer`` keyword-only arg to ``ClientResponse.__init__``,
which vcrpy's ``MockClientResponse`` does not pass -- raising ``TypeError`` at
cassette playback.
Restore the mixin, then rebuild ``MockClientResponse``'s ``super().__init__`` call from
the live ``ClientResponse`` signature (defaulting every required keyword-only arg to
``None``, mirroring vcrpy's original call) so it also survives future aiohttp additions.
"""
import asyncio
import inspect
from aiohttp import streams
from aiohttp.client_reqrep import ClientResponse
if not hasattr(streams, "AsyncStreamReaderMixin"):
class AsyncStreamReaderMixin:
__slots__ = ()
def __aiter__(self) -> streams.AsyncStreamIterator[bytes]:
return streams.AsyncStreamIterator(self.readline) # type: ignore[attr-defined]
def iter_chunked(self, n: int) -> streams.AsyncStreamIterator[bytes]:
return streams.AsyncStreamIterator(lambda: self.read(n)) # type: ignore[attr-defined]
def iter_any(self) -> streams.AsyncStreamIterator[bytes]:
return streams.AsyncStreamIterator(self.readany) # type: ignore[attr-defined]
def iter_chunks(self) -> streams.ChunkTupleAsyncStreamIterator:
return streams.ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type]
streams.AsyncStreamReaderMixin = AsyncStreamReaderMixin # type: ignore[attr-defined]
# Importing the stub builds MockStream/MockClientResponse, so it must run after the
# mixin is restored above.
import vcr.stubs.aiohttp_stubs as aiohttp_stubs # type: ignore[import-untyped]
if getattr(aiohttp_stubs.MockClientResponse, "_crewai_aiohttp_patched", False):
return
keyword_only = [
name
for name, param in inspect.signature(ClientResponse.__init__).parameters.items()
if param.kind is inspect.Parameter.KEYWORD_ONLY
]
class _NullStreamWriter:
# aiohttp 3.14.0 reads stream_writer.output_size in the "request already
# sent" branch (writer is None), so None is not enough -- supply a stub.
output_size = 0
fallback_loop: list[asyncio.AbstractEventLoop] = []
def _resolve_loop() -> asyncio.AbstractEventLoop:
# MockClientResponse is normally built inside aiohttp's running loop, so
# prefer that. In a sync context there is no running loop; avoid
# asyncio.get_event_loop(), which on 3.12+ emits a DeprecationWarning
# (and can RuntimeError) when no current loop is set. Use one cached
# loop instead -- the mock only stores it and calls loop.get_debug().
try:
return asyncio.get_running_loop()
except RuntimeError:
if not fallback_loop:
fallback_loop.append(asyncio.new_event_loop())
return fallback_loop[0]
def _mock_client_response_init(
self: Any, method: str, url: Any, request_info: Any = None
) -> None:
kwargs: dict[str, Any] = dict.fromkeys(keyword_only)
kwargs["request_info"] = request_info
if "loop" in kwargs:
kwargs["loop"] = _resolve_loop()
if "stream_writer" in kwargs:
kwargs["stream_writer"] = _NullStreamWriter()
ClientResponse.__init__(self, method, url, **kwargs)
aiohttp_stubs.MockClientResponse.__init__ = _mock_client_response_init
aiohttp_stubs.MockClientResponse._crewai_aiohttp_patched = True
_patch_vcrpy_aiohttp_compat()
from vcr.request import Request # type: ignore[import-untyped] # noqa: E402
from vcr.request import Request # type: ignore[import-untyped]
try:

View File

@@ -4,6 +4,67 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
icon: "clock"
mode: "wide"
---
<Update label="5 يونيو 2026">
## v1.14.7a2
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a2)
## ما الذي تغير
### الميزات
- إضافة دعم تتبع تدفقات المحادثة.
- تحديث وثائق تدفق المحادثة لاستخدام `handle_turn`.
- عرض السبب الحقيقي لإنهاء المحادثة، ومعلمات العينة، و`response.id` في أحداث LLM.
- تصنيف مشغلات DSL كزخارف واعية بالمسار.
- تنفيذ واجهة برمجة التطبيقات للدردشة لتدفقات المحادثة.
- جعل قفل الخلفية قابلاً للتجاوز في متجر القفل.
- تقسيم أحادي تدفق DSL إلى وحدات زخرفية مركزة.
- تسطيح استخدام ذاكرة التخزين المؤقت LiteLLM/أعداد الأسباب الفرعية في `_usage_to_dict`.
- بناء `FlowDefinition` من بيانات التعريف الخاصة بتدفق DSL.
### الوثائق
- إضافة دليل NVIDIA Nemotron LLM.
- توثيق عمليات نشر المونوريبو.
- تحديث سجل التغييرات والإصدار لـ v1.14.7a1.
## المساهمون
@alex-clawd, @gvieira, @lorenzejay, @lucasgomide, @mattatcha, @vinibrsl
</Update>
<Update label="3 يونيو 2026">
## v1.14.7a1
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a1)
## ما الذي تغير
### الميزات
- إضافة دعم ملفات الوكلاء المدربين
- إضافة مزود LLM الأصلي لـ Snowflake Cortex
- إضافة دليل تكامل Databricks
- إضافة دليل تكامل Snowflake
### إصلاحات الأخطاء
- إصلاح CLI عن طريق استعادة `[project.scripts]` في حزمة crewai لتثبيت أداة UV
- حل مشكلات موثوقية إدخال الملفات
- إصلاح تاريخ نتائج الأدوات غير المكتملة في Snowflake Claude
- التعامل مع استدعاءات الأدوات الممثلة كسلاسل لـ Snowflake Claude
- إعادة تفعيل مستمعي `or_` متعدد المصادر عبر دورات مدفوعة بالموجه
### الأداء
- تحسين سرعة استيراد crewai عن طريق تحميل استيرادات docling بشكل كسول
### إعادة هيكلة
- تقسيم `flow.py` إلى DSL، تعريف، وتشغيل
## المساهمون
@Luzk, @alex-clawd, @devin-ai-integration[bot], @greysonlalonde, @jessemiller, @lorenzejay, @vinibrsl
</Update>
<Update label="28 مايو 2026">
## v1.14.6

Some files were not shown because too many files have changed in this diff Show More