Compare commits

..

2 Commits

Author SHA1 Message Date
Iris Clawd
71c7293197 docs: remove --public flag references (public tools no longer supported)
Remove all references to --public visibility flag since public tools
have been removed from the platform (crewai-plus#2380, ENG-1453).
Tools are now organization-scoped (private) by default.

Co-authored-by: Diego Nogues <diego@crewai.com>
2026-05-14 09:52:10 -03:00
Iris Clawd
288568110f docs: add Platform Tools CLI guide for crewai tool create/publish/install
Add documentation for the CLI commands referenced in the Create Tool
modal on the platform (crewai tool create, crewai tool publish,
crewai tool install). These commands manage tools on the CrewAI
platform registry — distinct from PyPI publishing.

Changes:
- New guide: docs/en/guides/tools/platform-tools-cli.mdx
  Full lifecycle: create → implement → publish → install
  Covers visibility flags (--public/--private/--force)
  Includes platform vs PyPI comparison
- Updated create-custom-tools.mdx tip to cross-reference both guides
- Added new page to docs.json navigation (all versions)

Resolves EPD-76

Co-authored-by: Diego Nogues <diego@crewai.com>
2026-05-14 09:52:10 -03:00
50 changed files with 1565 additions and 3946 deletions

View File

@@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: ${{ matrix.python-version }}

View File

@@ -22,10 +22,10 @@ jobs:
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: tibdex/github-app-token@v2
with:
app-id: ${{ secrets.CREWAI_TOOL_SPECS_APP_ID }}
private-key: ${{ secrets.CREWAI_TOOL_SPECS_PRIVATE_KEY }}
app_id: ${{ secrets.CREWAI_TOOL_SPECS_APP_ID }}
private_key: ${{ secrets.CREWAI_TOOL_SPECS_PRIVATE_KEY }}
- name: Checkout code
uses: actions/checkout@v4
@@ -34,7 +34,7 @@ jobs:
token: ${{ steps.app-token.outputs.token }}
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.12"

View File

@@ -13,7 +13,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
@@ -41,7 +41,7 @@ jobs:
uv-main-py3.11-
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.11"

View File

@@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.12"
@@ -103,7 +103,7 @@ jobs:
contents: read
steps:
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.12"

View File

@@ -10,7 +10,7 @@ jobs:
permissions:
pull-requests: write
steps:
- uses: codelytv/pr-size-labeler@095a41fca88b8764fd9e008ad269bcdb82bb38b9 # v1
- uses: codelytv/pr-size-labeler@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
xs_label: "size/XS"

View File

@@ -12,7 +12,7 @@ jobs:
pr-title:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
- uses: amannn/action-semantic-pull-request@v5
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -34,7 +34,7 @@ jobs:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
uses: astral-sh/setup-uv@v4
- name: Build packages
run: |
@@ -63,7 +63,7 @@ jobs:
ref: ${{ inputs.release_tag || github.ref }}
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.12"
@@ -159,7 +159,7 @@ jobs:
- name: Notify Slack
if: success()
uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
uses: slackapi/slack-github-action@v2.1.0
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook

View File

@@ -13,7 +13,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
@@ -51,7 +51,7 @@ jobs:
uv-main-py${{ matrix.python-version }}-
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: ${{ matrix.python-version }}

View File

@@ -13,7 +13,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
@@ -48,7 +48,7 @@ jobs:
uv-main-py${{ matrix.python-version }}-
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: ${{ matrix.python-version }}

View File

@@ -38,7 +38,7 @@ jobs:
uv-main-py${{ matrix.python-version }}-
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: ${{ matrix.python-version }}

View File

@@ -31,7 +31,7 @@ jobs:
uv-main-py3.11-
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.11"
@@ -46,39 +46,11 @@ jobs:
- name: Run pip-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 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 CVE-2026-3219 \
--ignore-vuln GHSA-r374-rxx8-8654
# 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
# PYSEC-2025-211..218 - transformers 5.5.4: deserialization/code injection via malicious model checkpoints; no fix available
# CVE-2026-3219 - pip 26.0.1 (GHSA-58qw-9mgm-455v): no fix available, archive handling issue
# GHSA-r374-rxx8-8654 - paramiko 4.0.0 (SHA-1 in rsakey.py): no fix available; transitive via composio-core
continue-on-error: true
- name: Display results

View File

@@ -4,86 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
icon: "clock"
mode: "wide"
---
<Update label="19 مايو 2026">
## v1.14.5
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
## ما الذي تغير
### الميزات
- إلغاء استخدام `CrewAgentExecutor`، وتعيين وكلاء الطاقم الافتراضيين إلى `AgentExecutor`
- تحسين أدوات صندوق الرمل Daytona
- إضافة معلمة بدء `restore_from_state_id`
- إضافة تسليط الضوء على `ExaSearchTool`، وإعادة تسميته من `EXASearchTool`
### إصلاحات الأخطاء
- إصلاح تسرب الذاكرة في `git.py` باستخدام `cached_property`
- عرض استدعاءات الأدوات المتدفقة عندما تكون `available_functions` غائبة
- ضمان تحميل أحداث `skills` للتتبع
- تصحيح مسار نقطة النهاية للحالة من `/{kickoff_id}/status` إلى `/status/{kickoff_id}`
- استعادة كتلة الشيفرة المفقودة في دليل التدفق الأول للغة البرتغالية (pt-BR)
- منع `result_as_answer` من إرجاع رسائل الخطأ أو الكتل المرتبطة كإجابة نهائية
- الحفاظ على مخرجات المهام عبر تفريغ الدفعات غير المتزامنة
- دائمًا استعادة `task.output_pydantic` في كتلة finally
- التعامل مع إدخال `BaseModel` في `convert_to_model`
### الوثائق
- تحديث سجل التغييرات والإصدار لـ v1.14.5
- إضافة دليل ترقية OSS و انتقال الطاقم إلى التدفق
- توثيق متغيرات البيئة الإضافية لأدوات المطور
- إضافة وثائق لـ `TavilyGetResearch`
### إعادة الهيكلة
- استخراج واجهة سطر الأوامر إلى حزمة مستقلة `crewai-cli`
## المساهمون
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
</Update>
<Update label="18 مايو 2026">
## v1.14.5a7
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
## ما الذي تغير
### الوثائق
- تحديث سجل التغييرات والإصدار لـ v1.14.5a6
### تغييرات كسرية
- إلغاء حقل function_calling_llm
## المساهمون
@greysonlalonde, @heitorado
</Update>
<Update label="15 مايو 2026">
## v1.14.5a6
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
## ما الذي تغير
### إصلاحات الأخطاء
- إصلاح استدعاءات الأدوات المتدفقة عندما تكون available_functions غائبة
- رفع اعتماد langsmith إلى الإصدار >=0.8.0 لمعالجة GHSA-3644-q5cj-c5c7
- حل مشاكل الأماكن الشاغرة لكتل التعليمات البرمجية غير المترجمة في وثائق البرتغالية البرازيلية
### الوثائق
- إضافة وثائق لـ TavilyGetResearch
- تحديث سجل التغييرات والإصدار لـ v1.14.5a5
## المساهمون
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
</Update>
<Update label="13 مايو 2026">
## v1.14.5a5

View File

@@ -146,6 +146,7 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
</Step>
{" "}
<Step title="الإجابة على الأسئلة">
أجب على أسئلة التوضيح من مساعد الطاقم لتنقيح
متطلباتك.
@@ -160,10 +161,12 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
</Step>
{" "}
<Step title="الموافقة أو التعديل">
وافق على الخطة أو اطلب تغييرات إذا لزم الأمر.
</Step>
{" "}
<Step title="التنزيل أو النشر">
نزّل الكود للتخصيص أو انشر مباشرة على المنصة.
</Step>

View File

@@ -802,6 +802,7 @@ The tables below show a representative sample of current top-performing models a
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
</Step>
{" "}
<Step title="Identify Specialized Needs">
Determine if your crew has specific requirements (coding, reasoning, speed)
that would benefit from specialized models like **Claude 4 Sonnet** for
@@ -809,6 +810,7 @@ The tables below show a representative sample of current top-performing models a
consider fast inference providers like **Groq** alongside model selection.
</Step>
{" "}
<Step title="Implement Multi-Model Strategy">
Use different models for different agents based on their roles.
High-capability models for managers and complex tasks, efficient models for

File diff suppressed because it is too large Load Diff

View File

@@ -4,86 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="May 19, 2026">
## v1.14.5
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
## What's Changed
### Features
- Deprecate `CrewAgentExecutor`, default Crew agents to `AgentExecutor`
- Improve Daytona sandbox tools
- Add `restore_from_state_id` kickoff parameter
- Add highlights to `ExaSearchTool`, rename from `EXASearchTool`
### Bug Fixes
- Fix memory leak in `git.py` by using `cached_property`
- Surface streamed tool calls when `available_functions` is absent
- Ensure `skills` loading events for traces
- Correct status endpoint path from `/{kickoff_id}/status` to `/status/{kickoff_id}`
- Restore missing code block in pt-BR first-flow guide
- Prevent `result_as_answer` from returning hook-block or error messages as final answer
- Preserve task outputs across async batch flush
- Always restore `task.output_pydantic` in finally block
- Handle `BaseModel` input in `convert_to_model`
### Documentation
- Update changelog and version for v1.14.5
- Add OSS upgrade & crew-to-flow migration guide
- Document additional env vars for devtools
- Add docs for `TavilyGetResearch`
### Refactoring
- Extract CLI into standalone `crewai-cli` package
## Contributors
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
</Update>
<Update label="May 18, 2026">
## v1.14.5a7
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
## What's Changed
### Documentation
- Update changelog and version for v1.14.5a6
### Breaking Changes
- Deprecate function_calling_llm field
## Contributors
@greysonlalonde, @heitorado
</Update>
<Update label="May 15, 2026">
## v1.14.5a6
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
## What's Changed
### Bug Fixes
- Fix streamed tool calls when available_functions is absent
- Bump langsmith dependency to version >=0.8.0 to address GHSA-3644-q5cj-c5c7
- Resolve untranslated code block placeholders in Brazilian Portuguese documentation
### Documentation
- Add documentation for TavilyGetResearch
- Update changelog and version for v1.14.5a5
## Contributors
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
</Update>
<Update label="May 13, 2026">
## v1.14.5a5

View File

@@ -146,6 +146,7 @@ Here's a typical workflow for creating a crew with Crew Studio:
</Step>
{" "}
<Step title="Answer Questions">
Respond to clarifying questions from the Crew Assistant to refine your
requirements.
@@ -160,10 +161,12 @@ Here's a typical workflow for creating a crew with Crew Studio:
</Step>
{" "}
<Step title="Approve or Modify">
Approve the plan or request changes if necessary.
</Step>
{" "}
<Step title="Download or Deploy">
Download the code for customization or deploy directly to the platform.
</Step>

View File

@@ -0,0 +1,131 @@
---
title: Platform Tools CLI
description: Create, publish, and install custom tools on the CrewAI platform using the CLI.
icon: terminal
mode: "wide"
---
## Overview
The CrewAI CLI provides commands to manage custom tools on the **CrewAI platform** — a hosted tool registry that lets you share tools within your organization without publishing to PyPI.
| Command | Purpose |
|---------|---------|
| `crewai tool create <handle>` | Scaffold a new tool project |
| `crewai tool publish` | Publish the tool to the CrewAI platform |
| `crewai tool install <handle>` | Install a platform tool into your crew project |
<Note type="info" title="Platform vs PyPI">
These commands manage tools on the **CrewAI platform registry**. If you want to publish a standalone Python package to PyPI instead, see the [Publish Custom Tools to PyPI](/en/guides/tools/publish-custom-tools) guide.
</Note>
## Prerequisites
- **CrewAI CLI** installed (`pip install crewai`)
- **Authenticated** with the platform — run `crewai login` first
---
## Step 1: Create a Tool Project
Scaffold a new tool project:
```bash
crewai tool create my_custom_tool
```
This generates a project structure with the boilerplate you need to start building your tool.
<Tip>
The `handle` is the unique identifier for your tool on the platform. Choose something descriptive and specific to what the tool does.
</Tip>
### Implement Your Tool
Edit the generated tool file to add your logic. The tool follows the standard CrewAI tools contract — you can subclass `BaseTool` or use the `@tool` decorator:
```python
from crewai.tools import BaseTool
class MyCustomTool(BaseTool):
name: str = "My Custom Tool"
description: str = "Description of what this tool does — be specific so agents know when to use it."
def _run(self, argument: str) -> str:
# Your tool logic here
return "result"
```
For the full tools API reference (input schemas, caching, async support, error handling), see the [Create Custom Tools](/en/learn/create-custom-tools) guide.
---
## Step 2: Publish to the Platform
From your tool project directory, publish it to the CrewAI platform:
```bash
crewai tool publish
```
### Options
| Flag | Description |
|------|-------------|
| `--force` | Bypass Git remote validations |
Tools are published privately to your organization by default.
---
## Step 3: Install a Platform Tool
To install a tool that's been published to the platform:
```bash
crewai tool install my_custom_tool
```
Once installed, you can use the tool in your crew like any other tool — assign it to an agent via the `tools` parameter.
---
## Full Lifecycle Example
```bash
# 1. Authenticate with the platform
crewai login
# 2. Scaffold a new tool
crewai tool create weather_lookup
# 3. Implement your logic in the generated project
cd weather_lookup
# ... edit the tool file ...
# 4. Publish to the platform
crewai tool publish
# 5. In another project, install and use it
crewai tool install weather_lookup
```
---
## Platform Tools vs PyPI Packages
| | Platform Tools | PyPI Packages |
|---|---|---|
| **Publish** | `crewai tool publish` | `uv build` + `uv publish` |
| **Registry** | CrewAI platform | PyPI |
| **Install** | `crewai tool install <handle>` | `pip install <package>` |
| **Auth** | `crewai login` | PyPI account + token |
| **Visibility** | Organization-scoped (private) | Always public |
| **Guide** | This page | [Publish Custom Tools](/en/guides/tools/publish-custom-tools) |
---
## Related
- [Create Custom Tools](/en/learn/create-custom-tools) — Python API reference for building tools (BaseTool, @tool decorator)
- [Publish Custom Tools to PyPI](/en/guides/tools/publish-custom-tools) — package and distribute tools as standalone Python libraries

View File

@@ -12,7 +12,9 @@ incorporating the latest functionalities such as tool delegation, error handling
enabling agents to perform a wide range of actions.
<Tip>
**Want to publish your tool for the community?** If you're building a tool that others could benefit from, check out the [Publish Custom Tools](/en/guides/tools/publish-custom-tools) guide to learn how to package and distribute your tool on PyPI.
**Want to publish your tool to the CrewAI platform?** Use the CLI to scaffold, publish, and share tools directly on the platform — see the [Platform Tools CLI](/en/guides/tools/platform-tools-cli) guide.
**Prefer publishing to PyPI?** Check out the [Publish Custom Tools](/en/guides/tools/publish-custom-tools) guide to package and distribute your tool as a standalone Python library.
</Tip>
### Subclassing `BaseTool`

View File

@@ -805,6 +805,7 @@ The tables below show a representative sample of current top-performing models a
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
</Step>
{" "}
<Step title="Identify Specialized Needs">
Determine if your crew has specific requirements (coding, reasoning, speed)
that would benefit from specialized models like **Claude 4 Sonnet** for
@@ -812,6 +813,7 @@ The tables below show a representative sample of current top-performing models a
consider fast inference providers like **Groq** alongside model selection.
</Step>
{" "}
<Step title="Implement Multi-Model Strategy">
Use different models for different agents based on their roles.
High-capability models for managers and complex tasks, efficient models for

View File

@@ -4,86 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
icon: "clock"
mode: "wide"
---
<Update label="2026년 5월 19일">
## v1.14.5
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
## 변경 사항
### 기능
- `CrewAgentExecutor` 사용 중단, 기본 Crew 에이전트를 `AgentExecutor`로 설정
- Daytona 샌드박스 도구 개선
- `restore_from_state_id` 시작 매개변수 추가
- `ExaSearchTool`에 하이라이트 추가, 이름을 `EXASearchTool`에서 변경
### 버그 수정
- `git.py`에서 `cached_property`를 사용하여 메모리 누수 수정
- `available_functions`가 없을 때 스트리밍 도구 호출 표시
- 추적을 위한 `skills` 로딩 이벤트 보장
- 상태 엔드포인트 경로를 `/{kickoff_id}/status`에서 `/status/{kickoff_id}`로 수정
- pt-BR 첫 흐름 가이드에서 누락된 코드 블록 복원
- `result_as_answer`가 후크 블록이나 오류 메시지를 최종 답변으로 반환하지 않도록 방지
- 비동기 배치 플러시 간 작업 출력 보존
- 항상 finally 블록에서 `task.output_pydantic` 복원
- `convert_to_model`에서 `BaseModel` 입력 처리
### 문서화
- v1.14.5에 대한 변경 로그 및 버전 업데이트
- OSS 업그레이드 및 Crew-투-흐름 마이그레이션 가이드 추가
- 개발 도구를 위한 추가 환경 변수 문서화
- `TavilyGetResearch`에 대한 문서 추가
### 리팩토링
- CLI를 독립형 `crewai-cli` 패키지로 추출
## 기여자
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
</Update>
<Update label="2026년 5월 18일">
## v1.14.5a7
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
## 변경 사항
### 문서
- v1.14.5a6의 변경 로그 및 버전 업데이트
### 주요 변경 사항
- function_calling_llm 필드 사용 중단
## 기여자
@greysonlalonde, @heitorado
</Update>
<Update label="2026년 5월 15일">
## v1.14.5a6
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
## 변경 사항
### 버그 수정
- available_functions가 없을 때 스트리밍 도구 호출 수정
- GHSA-3644-q5cj-c5c7 문제를 해결하기 위해 langsmith 의존성을 버전 >=0.8.0으로 업데이트
- 브라질 포르투갈어 문서에서 번역되지 않은 코드 블록 자리 표시자 해결
### 문서
- TavilyGetResearch에 대한 문서 추가
- v1.14.5a5에 대한 변경 로그 및 버전 업데이트
## 기여자
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
</Update>
<Update label="2026년 5월 13일">
## v1.14.5a5

View File

@@ -145,6 +145,7 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
</Step>
{" "}
<Step title="질문에 답하기">
crew assistant가 요구 사항을 구체화할 수 있도록 하는 추가 질문에 답변하세요.
</Step>
@@ -158,10 +159,12 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
</Step>
{" "}
<Step title="승인 또는 수정">
계획을 승인하거나 필요하다면 변경을 요청하세요.
</Step>
{" "}
<Step title="다운로드 또는 배포">
사용자화를 위해 코드를 다운로드하거나 플랫폼에 직접 배포하세요.
</Step>

View File

@@ -797,6 +797,7 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
여러 차원에서 우수한 성능을 제공하며 실제 환경에서 광범위하게 검증된 **GPT-4.1**, **Claude 3.7 Sonnet**, **Gemini 2.0 Flash**와 같은 잘 알려진 모델부터 시작하십시오.
</Step>
{" "}
<Step title="특화된 요구 사항 식별">
crew에 코드 작성, reasoning, 속도 등 특정 요구가 있는지 확인하고, 이러한
요구에 부합하는 **Claude 4 Sonnet**(개발용) 또는 **o3**(복잡한 분석용)과 같은
@@ -804,6 +805,7 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
더불어 **Groq**와 같은 빠른 추론 제공자를 고려할 수 있습니다.
</Step>
{" "}
<Step title="다중 모델 전략 구현">
각 에이전트의 역할에 따라 다양한 모델을 사용하세요. 관리자와 복잡한 작업에는
고성능 모델을, 일상적 운영에는 효율적인 모델을 적용합니다.

View File

@@ -4,86 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="19 mai 2026">
## v1.14.5
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
## O que Mudou
### Recursos
- Deprecar `CrewAgentExecutor`, definir agentes Crew como `AgentExecutor`
- Melhorar ferramentas do sandbox Daytona
- Adicionar parâmetro de início `restore_from_state_id`
- Adicionar destaques ao `ExaSearchTool`, renomeando de `EXASearchTool`
### Correções de Bugs
- Corrigir vazamento de memória em `git.py` usando `cached_property`
- Exibir chamadas de ferramentas transmitidas quando `available_functions` está ausente
- Garantir eventos de carregamento de `skills` para rastros
- Corrigir caminho do endpoint de status de `/{kickoff_id}/status` para `/status/{kickoff_id}`
- Restaurar bloco de código ausente no guia de primeiro fluxo em pt-BR
- Impedir que `result_as_answer` retorne mensagens de bloqueio de hook ou de erro como resposta final
- Preservar saídas de tarefas durante o descarregamento assíncrono em lote
- Sempre restaurar `task.output_pydantic` no bloco finally
- Lidar com entrada de `BaseModel` em `convert_to_model`
### Documentação
- Atualizar changelog e versão para v1.14.5
- Adicionar guia de migração de atualização OSS & crew-to-flow
- Documentar variáveis de ambiente adicionais para devtools
- Adicionar documentação para `TavilyGetResearch`
### Refatoração
- Extrair CLI para o pacote autônomo `crewai-cli`
## Contribuidores
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
</Update>
<Update label="18 mai 2026">
## v1.14.5a7
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
## O que Mudou
### Documentação
- Atualizar changelog e versão para v1.14.5a6
### Mudanças Quebradoras
- Depreciar o campo function_calling_llm
## Contributors
@greysonlalonde, @heitorado
</Update>
<Update label="15 mai 2026">
## v1.14.5a6
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
## O que mudou
### Correções de Bugs
- Corrigir chamadas de ferramentas transmitidas quando available_functions está ausente
- Atualizar a dependência langsmith para a versão >=0.8.0 para resolver GHSA-3644-q5cj-c5c7
- Resolver espaços reservados de blocos de código não traduzidos na documentação em português brasileiro
### Documentação
- Adicionar documentação para TavilyGetResearch
- Atualizar changelog e versão para v1.14.5a5
## Contributors
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
</Update>
<Update label="13 mai 2026">
## v1.14.5a5

View File

@@ -146,6 +146,7 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
</Step>
{" "}
<Step title="Responder Perguntas">
Responda às perguntas de esclarecimento do Crew Assistant para refinar seus
requisitos.
@@ -160,10 +161,12 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
</Step>
{" "}
<Step title="Aprovar ou Modificar">
Aprove o plano ou solicite alterações, se necessário.
</Step>
{" "}
<Step title="Baixar ou Fazer Deploy">
Baixe o código para personalização ou faça o deploy diretamente na plataforma.
</Step>

View File

@@ -797,6 +797,7 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
Inicie com opções consagradas como **GPT-4.1**, **Claude 3.7 Sonnet** ou **Gemini 2.0 Flash**, que oferecem bom desempenho e ampla validação.
</Step>
{" "}
<Step title="Identifique Demandas Especializadas">
Descubra se sua crew possui requisitos específicos (código, raciocínio,
velocidade) que justifiquem modelos como **Claude 4 Sonnet** para
@@ -804,6 +805,7 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
velocidade, considere Groq aliado à seleção do modelo.
</Step>
{" "}
<Step title="Implemente Estratégia Multi-Modelo">
Use modelos diferentes para agentes distintos conforme o papel. Modelos de
alta capacidade para managers e tarefas complexas, eficientes para rotinas.

View File

@@ -8,7 +8,7 @@ authors = [
]
requires-python = ">=3.10, <3.14"
dependencies = [
"crewai-core==1.14.5",
"crewai-core==1.14.5a5",
"click~=8.1.7",
"pydantic>=2.11.9,<2.13",
"pydantic-settings~=2.10.1",

View File

@@ -1 +1 @@
__version__ = "1.14.5"
__version__ = "1.14.5a5"

View File

@@ -1,6 +1,5 @@
from typing import Any
from crewai_core.plus_api import CreateCrewPayload
from rich.console import Console
from crewai_cli import git
@@ -162,7 +161,7 @@ class DeployCommand(BaseCommand, PlusAPIMixin):
self,
env_vars: dict[str, str],
remote_repo_url: str,
) -> CreateCrewPayload:
) -> dict[str, Any]:
"""
Create the payload for crew creation.
@@ -173,8 +172,6 @@ class DeployCommand(BaseCommand, PlusAPIMixin):
Returns:
Dict[str, Any]: The payload for crew creation.
"""
if not self.project_name:
raise ValueError("project_name is required to create a deployment payload")
return {
"deploy": {
"name": self.project_name,

View File

@@ -1,4 +1,4 @@
from functools import cached_property
from functools import lru_cache
import subprocess
@@ -9,7 +9,7 @@ class Repository:
if not self.is_git_installed():
raise ValueError("Git is not installed or not found in your PATH.")
if not self.is_git_repo:
if not self.is_git_repo():
raise ValueError(f"{self.path} is not a Git repository.")
self.fetch()
@@ -40,9 +40,13 @@ class Repository:
encoding="utf-8",
).strip()
@cached_property # noqa: B019
@lru_cache(maxsize=None) # noqa: B019
def is_git_repo(self) -> bool:
"""Check if the current directory is a git repository."""
"""Check if the current directory is a git repository.
Notes:
- TODO: This method is cached to avoid redundant checks, but using lru_cache on methods can lead to memory leaks
"""
try:
subprocess.check_output(
["git", "rev-parse", "--is-inside-work-tree"], # noqa: S607

View File

@@ -1 +1 @@
__version__ = "1.14.5"
__version__ = "1.14.5a5"

View File

@@ -3,161 +3,36 @@
from __future__ import annotations
import os
from typing import Any, Final, Literal, TypedDict, cast
from typing import Any
from urllib.parse import urljoin
import httpx
from typing_extensions import NotRequired
from crewai_core.constants import DEFAULT_CREWAI_ENTERPRISE_URL
from crewai_core.settings import Settings
from crewai_core.version import get_crewai_version
HttpMethod = Literal["GET", "POST", "PATCH", "DELETE"]
class AvailableExport(TypedDict):
name: str
class EnvVarEntry(TypedDict):
name: str
description: str
required: bool
default: str | None
class ToolMetadata(TypedDict):
name: str
module: str
humanized_name: str
description: str
run_params_schema: dict[str, Any]
init_params_schema: dict[str, Any]
env_vars: list[EnvVarEntry]
class ToolsMetadataPayload(TypedDict):
package: str
tools: list[ToolMetadata] | None
class PublishToolPayload(TypedDict):
handle: str
public: bool
version: str
file: str
description: str | None
available_exports: list[AvailableExport] | None
tools_metadata: ToolsMetadataPayload | None
class CrewDeploymentSpec(TypedDict):
name: str
repo_clone_url: str
env: dict[str, str]
class CreateCrewPayload(TypedDict):
deploy: CrewDeploymentSpec
class _WithUserIdentifier(TypedDict):
user_identifier: NotRequired[str]
class LoginPayload(_WithUserIdentifier):
pass
class TraceExecutionContext(TypedDict):
crew_fingerprint: str | None
crew_name: str | None
flow_name: str | None
crewai_version: str
privacy_level: str
class TraceExecutionMetadata(TypedDict):
expected_duration_estimate: int
agent_count: int
task_count: int
flow_method_count: int
execution_started_at: str
class TraceBatchInitPayload(_WithUserIdentifier):
trace_id: str
execution_type: str
execution_context: TraceExecutionContext
execution_metadata: TraceExecutionMetadata
ephemeral_trace_id: NotRequired[str]
class TraceBatchMetadata(TypedDict):
events_count: int
batch_sequence: int
is_final_batch: bool
class TraceEventsPayload(TypedDict):
events: list[dict[str, Any]]
batch_metadata: TraceBatchMetadata
class TraceFinalizePayload(TypedDict):
status: Literal["completed"]
duration_ms: float | None
final_event_count: int
class TraceFailedPayload(TypedDict):
status: Literal["failed"]
failure_reason: str
Headers = TypedDict(
"Headers",
{
"Content-Type": str,
"User-Agent": str,
"X-Crewai-Version": str,
"Authorization": NotRequired[str],
"X-Crewai-Organization-Id": NotRequired[str],
},
)
class RequestKwargs(TypedDict):
headers: dict[str, str]
json: NotRequired[Any]
params: NotRequired[dict[str, str]]
timeout: NotRequired[float]
class PlusAPI:
"""Client for working with the CrewAI+ API."""
TOOLS_RESOURCE: Final = "/crewai_plus/api/v1/tools"
ORGANIZATIONS_RESOURCE: Final = "/crewai_plus/api/v1/me/organizations"
CREWS_RESOURCE: Final = "/crewai_plus/api/v1/crews"
AGENTS_RESOURCE: Final = "/crewai_plus/api/v1/agents"
TRACING_RESOURCE: Final = "/crewai_plus/api/v1/tracing"
EPHEMERAL_TRACING_RESOURCE: Final = "/crewai_plus/api/v1/tracing/ephemeral"
INTEGRATIONS_RESOURCE: Final = "/crewai_plus/api/v1/integrations"
TOOLS_RESOURCE = "/crewai_plus/api/v1/tools"
ORGANIZATIONS_RESOURCE = "/crewai_plus/api/v1/me/organizations"
CREWS_RESOURCE = "/crewai_plus/api/v1/crews"
AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
TRACING_RESOURCE = "/crewai_plus/api/v1/tracing"
EPHEMERAL_TRACING_RESOURCE = "/crewai_plus/api/v1/tracing/ephemeral"
INTEGRATIONS_RESOURCE = "/crewai_plus/api/v1/integrations"
def __init__(self, api_key: str | None = None) -> None:
version = get_crewai_version()
self.api_key = api_key
self.headers: Headers = {
self.headers = {
"Content-Type": "application/json",
"User-Agent": f"CrewAI-CLI/{version}",
"X-Crewai-Version": version,
"User-Agent": f"CrewAI-CLI/{get_crewai_version()}",
"X-Crewai-Version": get_crewai_version(),
}
if api_key:
self.headers["Authorization"] = f"Bearer {api_key}"
settings = Settings()
if settings.org_uuid:
self.headers["X-Crewai-Organization-Id"] = settings.org_uuid
@@ -169,30 +44,17 @@ class PlusAPI:
)
def _make_request(
self,
method: HttpMethod,
endpoint: str,
*,
json: Any = None,
params: dict[str, str] | None = None,
timeout: float | None = None,
verify: bool = True,
self, method: str, endpoint: str, **kwargs: Any
) -> httpx.Response:
url = urljoin(self.base_url, endpoint)
request_kwargs: RequestKwargs = {"headers": cast(dict[str, str], self.headers)}
if json is not None:
request_kwargs["json"] = json
if params is not None:
request_kwargs["params"] = params
if timeout is not None:
request_kwargs["timeout"] = timeout
verify = kwargs.pop("verify", True)
with httpx.Client(trust_env=False, verify=verify) as client:
return client.request(method, url, **request_kwargs)
return client.request(method, url, headers=self.headers, **kwargs)
def login_to_tool_repository(
self, user_identifier: str | None = None
) -> httpx.Response:
payload: LoginPayload = {}
payload = {}
if user_identifier:
payload["user_identifier"] = user_identifier
return self._make_request("POST", f"{self.TOOLS_RESOURCE}/login", json=payload)
@@ -203,7 +65,7 @@ class PlusAPI:
async def get_agent(self, handle: str) -> httpx.Response:
url = urljoin(self.base_url, f"{self.AGENTS_RESOURCE}/{handle}")
async with httpx.AsyncClient() as client:
return await client.get(url, headers=cast(dict[str, str], self.headers))
return await client.get(url, headers=self.headers)
def publish_tool(
self,
@@ -212,10 +74,10 @@ class PlusAPI:
version: str,
description: str | None,
encoded_file: str,
available_exports: list[AvailableExport] | None = None,
tools_metadata: list[ToolMetadata] | None = None,
available_exports: list[dict[str, Any]] | None = None,
tools_metadata: list[dict[str, Any]] | None = None,
) -> httpx.Response:
params: PublishToolPayload = {
params = {
"handle": handle,
"public": is_public,
"version": version,
@@ -267,13 +129,13 @@ class PlusAPI:
def list_crews(self) -> httpx.Response:
return self._make_request("GET", self.CREWS_RESOURCE)
def create_crew(self, payload: CreateCrewPayload) -> httpx.Response:
def create_crew(self, payload: dict[str, Any]) -> httpx.Response:
return self._make_request("POST", self.CREWS_RESOURCE, json=payload)
def get_organizations(self) -> httpx.Response:
return self._make_request("GET", self.ORGANIZATIONS_RESOURCE)
def initialize_trace_batch(self, payload: TraceBatchInitPayload) -> httpx.Response:
def initialize_trace_batch(self, payload: dict[str, Any]) -> httpx.Response:
return self._make_request(
"POST",
f"{self.TRACING_RESOURCE}/batches",
@@ -282,7 +144,7 @@ class PlusAPI:
)
def initialize_ephemeral_trace_batch(
self, payload: TraceBatchInitPayload
self, payload: dict[str, Any]
) -> httpx.Response:
return self._make_request(
"POST",
@@ -291,7 +153,7 @@ class PlusAPI:
)
def send_trace_events(
self, trace_batch_id: str, payload: TraceEventsPayload
self, trace_batch_id: str, payload: dict[str, Any]
) -> httpx.Response:
return self._make_request(
"POST",
@@ -301,7 +163,7 @@ class PlusAPI:
)
def send_ephemeral_trace_events(
self, trace_batch_id: str, payload: TraceEventsPayload
self, trace_batch_id: str, payload: dict[str, Any]
) -> httpx.Response:
return self._make_request(
"POST",
@@ -311,7 +173,7 @@ class PlusAPI:
)
def finalize_trace_batch(
self, trace_batch_id: str, payload: TraceFinalizePayload
self, trace_batch_id: str, payload: dict[str, Any]
) -> httpx.Response:
return self._make_request(
"PATCH",
@@ -321,7 +183,7 @@ class PlusAPI:
)
def finalize_ephemeral_trace_batch(
self, trace_batch_id: str, payload: TraceFinalizePayload
self, trace_batch_id: str, payload: dict[str, Any]
) -> httpx.Response:
return self._make_request(
"PATCH",
@@ -333,28 +195,20 @@ class PlusAPI:
def mark_trace_batch_as_failed(
self, trace_batch_id: str, error_message: str
) -> httpx.Response:
payload: TraceFailedPayload = {
"status": "failed",
"failure_reason": error_message,
}
return self._make_request(
"PATCH",
f"{self.TRACING_RESOURCE}/batches/{trace_batch_id}",
json=payload,
json={"status": "failed", "failure_reason": error_message},
timeout=30,
)
def mark_ephemeral_trace_batch_as_failed(
self, trace_batch_id: str, error_message: str
) -> httpx.Response:
payload: TraceFailedPayload = {
"status": "failed",
"failure_reason": error_message,
}
return self._make_request(
"PATCH",
f"{self.EPHEMERAL_TRACING_RESOURCE}/batches/{trace_batch_id}",
json=payload,
json={"status": "failed", "failure_reason": error_message},
timeout=30,
)

View File

@@ -152,4 +152,4 @@ __all__ = [
"wrap_file_source",
]
__version__ = "1.14.5"
__version__ = "1.14.5a5"

View File

@@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14"
dependencies = [
"pytube~=15.0.0",
"requests>=2.33.0,<3",
"crewai==1.14.5",
"crewai==1.14.5a5",
"tiktoken>=0.8.0,<0.13",
"beautifulsoup4~=4.13.4",
"python-docx~=1.2.0",

View File

@@ -330,4 +330,4 @@ __all__ = [
"ZapierActionTools",
]
__version__ = "1.14.5"
__version__ = "1.14.5a5"

View File

@@ -8,8 +8,8 @@ authors = [
]
requires-python = ">=3.10, <3.14"
dependencies = [
"crewai-core==1.14.5",
"crewai-cli==1.14.5",
"crewai-core==1.14.5a5",
"crewai-cli==1.14.5a5",
# Core Dependencies
"pydantic>=2.11.9,<2.13",
"openai>=2.30.0,<3",
@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
[project.optional-dependencies]
tools = [
"crewai-tools==1.14.5",
"crewai-tools==1.14.5a5",
]
embeddings = [
"tiktoken>=0.8.0,<0.13"
@@ -105,7 +105,7 @@ a2a = [
"aiocache[redis,memcached]~=0.12.3",
]
file-processing = [
"crewai-files",
"crewai-files==1.14.5a5",
]
qdrant-edge = [
"qdrant-edge-py>=0.6.0",

View File

@@ -48,7 +48,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
_suppress_pydantic_deprecation_warnings()
__version__ = "1.14.5"
__version__ = "1.14.5a5"
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
"Memory": ("crewai.memory.unified_memory", "Memory"),

View File

@@ -220,11 +220,7 @@ class Agent(BaseAgent):
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
] = Field(
description="Language model that will run the agent.",
default=None,
deprecated="function_calling_llm is deprecated and will be removed in a future release.",
)
] = Field(description="Language model that will run the agent.", default=None)
system_template: str | None = Field(
default=None, description="System format for the agent."
)

View File

@@ -51,10 +51,7 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
_graph: Any = PrivateAttr(default=None)
_memory: Any = PrivateAttr(default=None)
_max_iterations: int = PrivateAttr(default=10)
function_calling_llm: Any = Field(
default=None,
deprecated="function_calling_llm is deprecated and will be removed in a future release.",
)
function_calling_llm: Any = Field(default=None)
step_callback: SerializableCallable | None = Field(default=None)
model: str = Field(default="gpt-4o")

View File

@@ -60,10 +60,7 @@ class OpenAIAgentAdapter(BaseAgentAdapter):
_openai_agent: OpenAIAgentProtocol = PrivateAttr()
_logger: Logger = PrivateAttr(default_factory=Logger)
_active_thread: str | None = PrivateAttr(default=None)
function_calling_llm: Any = Field(
default=None,
deprecated="function_calling_llm is deprecated and will be removed in a future release.",
)
function_calling_llm: Any = Field(default=None)
step_callback: Any = Field(default=None)
_tool_adapter: OpenAIAgentToolAdapter = PrivateAttr()
_converter_adapter: OpenAIConverterAdapter = PrivateAttr()

View File

@@ -251,11 +251,7 @@ class Crew(FlowTrackable, BaseModel):
str | LLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
] = Field(
description="Language model that will run the agent.",
default=None,
deprecated="function_calling_llm is deprecated and will be removed in a future release.",
)
] = Field(description="Language model that will run the agent.", default=None)
config: Json[dict[str, Any]] | dict[str, Any] | None = Field(default=None)
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
share_crew: bool | None = Field(default=False)

View File

@@ -6,14 +6,6 @@ import time
from typing import Any
import uuid
from crewai_core.plus_api import (
TraceBatchInitPayload,
TraceBatchMetadata,
TraceEventsPayload,
TraceExecutionContext,
TraceExecutionMetadata,
TraceFinalizePayload,
)
from crewai_core.settings import Settings
from rich.console import Console
from rich.panel import Panel
@@ -131,27 +123,25 @@ class TraceBatchManager:
return None
try:
execution_context: TraceExecutionContext = {
"crew_fingerprint": execution_metadata.get("crew_fingerprint"),
"crew_name": execution_metadata.get("crew_name", None),
"flow_name": execution_metadata.get("flow_name", None),
"crewai_version": self.current_batch.version,
"privacy_level": user_context.get("privacy_level", "standard"),
}
execution_metadata_payload: TraceExecutionMetadata = {
"expected_duration_estimate": execution_metadata.get(
"expected_duration_estimate", 300
),
"agent_count": execution_metadata.get("agent_count", 0),
"task_count": execution_metadata.get("task_count", 0),
"flow_method_count": execution_metadata.get("flow_method_count", 0),
"execution_started_at": datetime.now(timezone.utc).isoformat(),
}
payload: TraceBatchInitPayload = {
payload = {
"trace_id": self.current_batch.batch_id,
"execution_type": execution_metadata.get("execution_type", "crew"),
"execution_context": execution_context,
"execution_metadata": execution_metadata_payload,
"execution_context": {
"crew_fingerprint": execution_metadata.get("crew_fingerprint"),
"crew_name": execution_metadata.get("crew_name", None),
"flow_name": execution_metadata.get("flow_name", None),
"crewai_version": self.current_batch.version,
"privacy_level": user_context.get("privacy_level", "standard"),
},
"execution_metadata": {
"expected_duration_estimate": execution_metadata.get(
"expected_duration_estimate", 300
),
"agent_count": execution_metadata.get("agent_count", 0),
"task_count": execution_metadata.get("task_count", 0),
"flow_method_count": execution_metadata.get("flow_method_count", 0),
"execution_started_at": datetime.now(timezone.utc).isoformat(),
},
}
if use_ephemeral:
payload["ephemeral_trace_id"] = self.current_batch.batch_id
@@ -274,14 +264,13 @@ class TraceBatchManager:
if not self.plus_api or not self.trace_batch_id or not self.event_buffer:
return 500
try:
batch_metadata: TraceBatchMetadata = {
"events_count": len(self.event_buffer),
"batch_sequence": 1,
"is_final_batch": False,
}
payload: TraceEventsPayload = {
payload = {
"events": [event.to_dict() for event in self.event_buffer],
"batch_metadata": batch_metadata,
"batch_metadata": {
"events_count": len(self.event_buffer),
"batch_sequence": 1,
"is_final_batch": False,
},
}
response = (
@@ -375,7 +364,7 @@ class TraceBatchManager:
return
try:
payload: TraceFinalizePayload = {
payload = {
"status": "completed",
"duration_ms": self.calculate_duration("execution"),
"final_event_count": events_count,

View File

@@ -940,21 +940,6 @@ class LLM(BaseLLM):
self._track_token_usage_internal(usage_info)
self._handle_streaming_callbacks(callbacks, usage_info, last_chunk)
if accumulated_tool_args and not available_functions:
tool_calls_list: list[ChatCompletionDeltaToolCall] = [
ChatCompletionDeltaToolCall(
index=idx,
function=Function(
name=tool_arg.function.name,
arguments=tool_arg.function.arguments,
),
)
for idx, tool_arg in sorted(accumulated_tool_args.items())
if tool_arg.function.name
]
if tool_calls_list:
return tool_calls_list
if not tool_calls or not available_functions:
if response_model and self.is_litellm:
instructor_instance = InternalInstructor(
@@ -1550,7 +1535,8 @@ class LLM(BaseLLM):
if usage_info:
self._track_token_usage_internal(usage_info)
if accumulated_tool_args:
if accumulated_tool_args and available_functions:
# Convert accumulated tool args to ChatCompletionDeltaToolCall objects
tool_calls_list: list[ChatCompletionDeltaToolCall] = [
ChatCompletionDeltaToolCall(
index=idx,
@@ -1559,24 +1545,21 @@ class LLM(BaseLLM):
arguments=tool_arg.function.arguments,
),
)
for idx, tool_arg in sorted(accumulated_tool_args.items())
for idx, tool_arg in accumulated_tool_args.items()
if tool_arg.function.name
]
if tool_calls_list:
if available_functions:
result = self._handle_streaming_tool_calls(
tool_calls=tool_calls_list,
accumulated_tool_args=accumulated_tool_args,
available_functions=available_functions,
from_task=from_task,
from_agent=from_agent,
response_id=response_id,
)
if result is not None:
return result
else:
return tool_calls_list
result = self._handle_streaming_tool_calls(
tool_calls=tool_calls_list,
accumulated_tool_args=accumulated_tool_args,
available_functions=available_functions,
from_task=from_task,
from_agent=from_agent,
response_id=response_id,
)
if result is not None:
return result
usage_dict = self._usage_to_dict(usage_info)
self._handle_emit_call_events(

View File

@@ -13,7 +13,6 @@ import sys
import types
from typing import Any, cast, get_type_hints
from crewai_core.plus_api import AvailableExport, EnvVarEntry, ToolMetadata
from crewai_core.project import (
get_project_description as get_project_description,
get_project_name as get_project_name,
@@ -280,7 +279,7 @@ def is_valid_tool(obj: Any) -> bool:
return isinstance(obj, Tool)
def extract_available_exports(dir_path: str = "src") -> list[AvailableExport]:
def extract_available_exports(dir_path: str = "src") -> list[dict[str, Any]]:
"""Extract available tool classes from the project's __init__.py files.
Only includes classes that inherit from BaseTool or functions decorated with @tool.
@@ -339,7 +338,7 @@ def _load_module_from_file(
sys.modules.pop(module_name, None)
def _load_tools_from_init(init_file: Path) -> list[AvailableExport]:
def _load_tools_from_init(init_file: Path) -> list[dict[str, Any]]:
"""Load and validate tools from a given __init__.py file."""
try:
with _load_module_from_file(init_file) as module:
@@ -393,7 +392,7 @@ def _print_no_tools_warning() -> None:
)
def extract_tools_metadata(dir_path: str = "src") -> list[ToolMetadata]:
def extract_tools_metadata(dir_path: str = "src") -> list[dict[str, Any]]:
"""
Extract rich metadata from tool classes in the project.
@@ -405,7 +404,7 @@ def extract_tools_metadata(dir_path: str = "src") -> list[ToolMetadata]:
- init_params_schema: JSON Schema for __init__ params (filtered)
- env_vars: List of environment variable dicts
"""
tools_metadata: list[ToolMetadata] = []
tools_metadata: list[dict[str, Any]] = []
for init_file in Path(dir_path).glob("**/__init__.py"):
tools = _extract_tool_metadata_from_init(init_file)
@@ -414,7 +413,7 @@ def extract_tools_metadata(dir_path: str = "src") -> list[ToolMetadata]:
return tools_metadata
def _extract_tool_metadata_from_init(init_file: Path) -> list[ToolMetadata]:
def _extract_tool_metadata_from_init(init_file: Path) -> list[dict[str, Any]]:
"""
Load module from init file and extract metadata from valid tool classes.
"""
@@ -429,7 +428,7 @@ def _extract_tool_metadata_from_init(init_file: Path) -> list[ToolMetadata]:
if not exported_names:
return []
tools_metadata: list[ToolMetadata] = []
tools_metadata = []
for name in exported_names:
obj = getattr(module, name, None)
if obj is None or not (
@@ -447,7 +446,7 @@ def _extract_tool_metadata_from_init(init_file: Path) -> list[ToolMetadata]:
return []
def _extract_single_tool_metadata(tool_class: type) -> ToolMetadata | None:
def _extract_single_tool_metadata(tool_class: type) -> dict[str, Any] | None:
"""
Extract metadata from a single tool class.
"""
@@ -471,17 +470,19 @@ def _extract_single_tool_metadata(tool_class: type) -> ToolMetadata | None:
except (TypeError, ValueError):
module = tool_class.__module__
return ToolMetadata(
name=tool_class.__name__,
module=module,
humanized_name=str(
_extract_field_default(fields.get("name"), fallback=tool_class.__name__)
return {
"name": tool_class.__name__,
"module": module,
"humanized_name": _extract_field_default(
fields.get("name"), fallback=tool_class.__name__
),
description=str(_extract_field_default(fields.get("description"))).strip(),
run_params_schema=_extract_run_params_schema(fields.get("args_schema")),
init_params_schema=_extract_init_params_schema(tool_class),
env_vars=_extract_env_vars(fields.get("env_vars")),
)
"description": str(
_extract_field_default(fields.get("description"))
).strip(),
"run_params_schema": _extract_run_params_schema(fields.get("args_schema")),
"init_params_schema": _extract_init_params_schema(tool_class),
"env_vars": _extract_env_vars(fields.get("env_vars")),
}
except Exception:
return None
@@ -596,7 +597,7 @@ def _extract_init_params_schema(tool_class: type) -> dict[str, Any]:
return {}
def _extract_env_vars(env_vars_field: dict[str, Any] | None) -> list[EnvVarEntry]:
def _extract_env_vars(env_vars_field: dict[str, Any] | None) -> list[dict[str, Any]]:
"""
Extract environment variable definitions from env_vars field.
"""

View File

@@ -624,15 +624,12 @@ def test_handle_streaming_tool_calls_no_available_functions(
],
tools=[get_weather_tool_schema],
)
assert isinstance(response, list)
assert len(response) == 1
assert response[0].function.name == "get_weather"
assert response[0].function.arguments == '{"location":"New York, NY"}'
assert response == ""
assert_event_count(
mock_emit=mock_emit,
expected_stream_chunk=9,
expected_completed_llm_call=0,
expected_completed_llm_call=1,
expected_final_chunk_result='{"location":"New York, NY"}',
)

View File

@@ -1,3 +1,3 @@
"""CrewAI development tools."""
__version__ = "1.14.5"
__version__ = "1.14.5a5"

View File

@@ -3,7 +3,6 @@
from collections.abc import Mapping
import os
from pathlib import Path
import re
import subprocess
import sys
import tempfile
@@ -356,19 +355,8 @@ def update_pyproject_dependencies(
workspace_packages = _DEFAULT_WORKSPACE_PACKAGES + (extra_packages or [])
current_extra: str | None = None
extra_header = re.compile(r"^\s*([A-Za-z0-9_-]+)\s*=\s*\[")
for i, line in enumerate(lines):
match = extra_header.match(line)
if match:
current_extra = match.group(1)
elif line.strip().startswith("]"):
current_extra = None
for pkg in workspace_packages:
if pkg == "crewai-files" and current_extra == "file-processing":
continue
if f"{pkg}==" in line:
stripped = line.lstrip()
indent = line[: len(line) - len(stripped)]
@@ -744,23 +732,18 @@ def _is_prerelease(version: str) -> bool:
return any(indicator in v for indicator in _PRERELEASE_INDICATORS)
def get_commits_from_last_tag(
tag_name: str, version: str, cwd: Path | None = None
) -> tuple[str, str]:
def get_commits_from_last_tag(tag_name: str, version: str) -> tuple[str, str]:
"""Get commits from the last tag, excluding current version.
Args:
tag_name: Current tag name (e.g., "v1.0.0").
version: Current version (e.g., "1.0.0").
cwd: Directory to run git commands in (defaults to current).
Returns:
Tuple of (commit_range, commits) where commits is newline-separated.
"""
try:
all_tags = run_command(
["git", "tag", "--sort=-version:refname"], cwd=cwd
).split("\n")
all_tags = run_command(["git", "tag", "--sort=-version:refname"]).split("\n")
prev_tags = [t for t in all_tags if t and t != tag_name and t != f"v{version}"]
if not _is_prerelease(version):
@@ -769,30 +752,22 @@ def get_commits_from_last_tag(
if prev_tags:
last_tag = prev_tags[0]
commit_range = f"{last_tag}..HEAD"
commits = run_command(
["git", "log", commit_range, "--pretty=format:%s"], cwd=cwd
)
commits = run_command(["git", "log", commit_range, "--pretty=format:%s"])
else:
commit_range = "HEAD"
commits = run_command(["git", "log", "--pretty=format:%s"], cwd=cwd)
commits = run_command(["git", "log", "--pretty=format:%s"])
except subprocess.CalledProcessError:
commit_range = "HEAD"
commits = run_command(["git", "log", "--pretty=format:%s"], cwd=cwd)
commits = run_command(["git", "log", "--pretty=format:%s"])
return commit_range, commits
def get_github_contributors(
commit_range: str,
repo: str = "crewAIInc/crewAI",
cwd: Path | None = None,
) -> list[str]:
def get_github_contributors(commit_range: str) -> list[str]:
"""Get GitHub usernames from commit range using GitHub API.
Args:
commit_range: Git commit range (e.g., "abc123..HEAD").
repo: GitHub repo in ``owner/name`` form to resolve commits against.
cwd: Directory to run git commands in (defaults to current).
Returns:
List of GitHub usernames sorted alphabetically.
@@ -804,10 +779,10 @@ def get_github_contributors(
gh_token = None
g = Github(login_or_token=gh_token) if gh_token else Github()
github_repo = g.get_repo(repo)
github_repo = g.get_repo("crewAIInc/crewAI")
commit_shas = run_command(
["git", "log", commit_range, "--pretty=format:%H"], cwd=cwd
["git", "log", commit_range, "--pretty=format:%H"]
).split("\n")
contributors = set()
@@ -947,26 +922,9 @@ def _generate_release_notes(
version: str,
tag_name: str,
no_edit: bool,
cwd: Path | None = None,
gh_repo: str = "crewAIInc/crewAI",
openai_client: OpenAI | None = None,
bump_already_done: bool = True,
) -> tuple[str, OpenAI, bool]:
"""Generate, display, and optionally edit release notes.
Args:
version: Version being released.
tag_name: Tag name for the release.
no_edit: Skip the interactive edit prompt.
cwd: Directory to run git commands in (defaults to current).
gh_repo: GitHub repo (``owner/name``) for resolving contributors.
openai_client: Reuse an existing OpenAI client if provided.
bump_already_done: True when the ``feat: bump versions to <version>``
commit for the current release is already in history (the real
release path). False in previews where no bump exists yet — the
most recent bump commit is the *previous* version and must be
used as the range start.
Returns:
Tuple of (release_notes, openai_client, is_prerelease).
"""
@@ -981,8 +939,7 @@ def _generate_release_notes(
"log",
"--grep=^feat: bump versions to",
"--format=%H %s",
],
cwd=cwd,
]
)
bump_entries = [
line for line in prev_bump_output.strip().split("\n") if line.strip()
@@ -990,8 +947,7 @@ def _generate_release_notes(
is_stable = not _is_prerelease(version)
prev_commit = None
scan_entries = bump_entries[1:] if bump_already_done else bump_entries
for entry in scan_entries:
for entry in bump_entries[1:]:
bump_ver = entry.split("feat: bump versions to", 1)[-1].strip()
if is_stable and _is_prerelease(bump_ver):
continue
@@ -1001,7 +957,7 @@ def _generate_release_notes(
if prev_commit:
commit_range = f"{prev_commit}..HEAD"
commits = run_command(
["git", "log", commit_range, "--pretty=format:%s"], cwd=cwd
["git", "log", commit_range, "--pretty=format:%s"]
)
commit_lines = [
@@ -1011,21 +967,14 @@ def _generate_release_notes(
]
commits = "\n".join(commit_lines)
else:
commit_range, commits = get_commits_from_last_tag(
tag_name, version, cwd=cwd
)
commit_range, commits = get_commits_from_last_tag(tag_name, version)
except subprocess.CalledProcessError:
commit_range, commits = get_commits_from_last_tag(
tag_name, version, cwd=cwd
)
commit_range, commits = get_commits_from_last_tag(tag_name, version)
github_contributors = get_github_contributors(
commit_range, repo=gh_repo, cwd=cwd
)
github_contributors = get_github_contributors(commit_range)
if openai_client is None:
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
if commits.strip():
contributors_section = ""
@@ -1583,13 +1532,7 @@ def _wait_for_pr_merged(branch: str, cwd: Path) -> None:
time.sleep(_PR_MERGE_POLL_INTERVAL)
def _release_enterprise(
version: str,
is_prerelease: bool,
dry_run: bool,
no_edit: bool = False,
openai_client: OpenAI | None = None,
) -> None:
def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> None:
"""Clone the enterprise repo, bump versions, and create a release PR.
Expects ENTERPRISE_REPO, ENTERPRISE_VERSION_DIRS, and
@@ -1599,8 +1542,6 @@ def _release_enterprise(
version: New version string.
is_prerelease: Whether this is a pre-release version.
dry_run: Show what would be done without making changes.
no_edit: Skip the interactive release-notes edit prompt.
openai_client: Reuse OpenAI client from earlier phases if available.
"""
if (
not _ENTERPRISE_REPO
@@ -1618,6 +1559,7 @@ def _release_enterprise(
)
if dry_run:
console.print(f"[dim][DRY RUN][/dim] Would clone {enterprise_repo}")
for d in _ENTERPRISE_VERSION_DIRS:
console.print(f"[dim][DRY RUN][/dim] Would update versions in {d}")
console.print(
@@ -1628,26 +1570,6 @@ def _release_enterprise(
"[dim][DRY RUN][/dim] Would create bump PR, wait for merge, "
"then tag and release"
)
with tempfile.TemporaryDirectory() as tmp:
repo_dir = Path(tmp) / enterprise_repo.split("/")[-1]
console.print(f"\nCloning {enterprise_repo} (read-only preview)...")
run_command(["gh", "repo", "clone", enterprise_repo, str(repo_dir)])
console.print(f"[green]✓[/green] Cloned {enterprise_repo}")
_generate_release_notes(
version,
version,
no_edit,
cwd=repo_dir,
gh_repo=enterprise_repo,
openai_client=openai_client,
bump_already_done=False,
)
console.print(
"[dim][DRY RUN][/dim] Would tag and create GitHub release "
"with the notes above"
)
return
with tempfile.TemporaryDirectory() as tmp:
@@ -1760,18 +1682,8 @@ def _release_enterprise(
run_command(["git", "pull"], cwd=repo_dir)
tag_name = version
release_notes, _, _ = _generate_release_notes(
version,
tag_name,
no_edit,
cwd=repo_dir,
gh_repo=enterprise_repo,
openai_client=openai_client,
)
run_command(
["git", "tag", "-a", tag_name, "-m", release_notes],
["git", "tag", "-a", tag_name, "-m", f"Release {version}"],
cwd=repo_dir,
)
run_command(["git", "push", "origin", tag_name], cwd=repo_dir)
@@ -1787,7 +1699,7 @@ def _release_enterprise(
"--title",
tag_name,
"--notes",
release_notes,
f"Release {version}",
]
if is_prerelease:
gh_cmd.append("--prerelease")
@@ -2086,7 +1998,7 @@ def tag(dry_run: bool, no_edit: bool) -> None:
console.print("[green]✓[/green] main branch up to date")
release_notes, openai_client, is_prerelease = _generate_release_notes(
version, tag_name, no_edit, bump_already_done=True
version, tag_name, no_edit
)
docs_branch = _update_docs_and_create_pr(
@@ -2197,7 +2109,7 @@ def release(
if skip_to_enterprise:
try:
_release_enterprise(version, is_prerelease, dry_run, no_edit=no_edit)
_release_enterprise(version, is_prerelease, dry_run)
except BaseException as e:
_print_release_error(e)
_resume_hint(
@@ -2293,7 +2205,7 @@ def release(
console.print("[green]✓[/green] main branch up to date")
release_notes, openai_client, is_prerelease = _generate_release_notes(
version, tag_name, no_edit, bump_already_done=not dry_run
version, tag_name, no_edit
)
docs_branch = _update_docs_and_create_pr(
@@ -2347,13 +2259,7 @@ def release(
if not skip_enterprise:
try:
_release_enterprise(
version,
is_prerelease,
dry_run,
no_edit=no_edit,
openai_client=openai_client,
)
_release_enterprise(version, is_prerelease, dry_run)
except BaseException as e:
_print_release_error(e)
_resume_hint(

View File

@@ -282,25 +282,6 @@ class TestUpdatePyprojectDependencies:
assert '"crewai-files==2.0.0"' in result
assert '"requests>=2.0"' in result
def test_skips_crewai_files_in_file_processing_extra(self, tmp_path: Path) -> None:
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
dedent("""\
[project.optional-dependencies]
file-processing = [
"crewai-files==1.0.0",
]
other = [
"crewai-files==1.0.0",
]
""")
)
update_pyproject_dependencies(pyproject, "2.0.0")
result = pyproject.read_text()
assert '"crewai-files==1.0.0"' in result
assert '"crewai-files==2.0.0"' in result
def test_leaves_bare_crewai_pin_alone(self, tmp_path: Path) -> None:
"""`crewai==` must not collide with `crewai-core==` etc."""
pyproject = tmp_path / "pyproject.toml"

View File

@@ -185,10 +185,8 @@ exclude-newer = "3 days"
# python-multipart <0.0.27 has GHSA-pp6c-gr5w-3c5g (DoS via unbounded multipart headers).
# gitpython <3.1.50 has GHSA-mv93-w799-cj2w (config_writer newline injection bypassing the 3.1.49 patch -> RCE via core.hooksPath).
# urllib3 <2.7.0 has GHSA-qccp-gfcp-xxvc (ProxyManager cross-origin redirect leaks Authorization/Cookie) and GHSA-mf9v-mfxr-j63j (streaming decompression-bomb bypass); force 2.7.0+.
# langsmith <0.8.0 has GHSA-3644-q5cj-c5c7 (public prompt manifest deserialization, SSRF/secret disclosure); force 0.8.0+.
# langsmith <0.7.31 has GHSA-rr7j-v2q5-chgv (streaming token redaction bypass); force 0.7.31+.
# authlib <1.6.11 has GHSA-jj8c-mmj3-mmgv (CSRF bypass in cache-based state storage).
# pip <26.1.1 has GHSA-58qw-9mgm-455v (archive handling); OSV considers 26.1.1 unaffected.
# paramiko <5.0.0 has GHSA-r374-rxx8-8654 (SHA-1 in rsakey.py); OSV considers 5.0.0 unaffected. Transitive via composio-core.
# litellm 1.83.8+ hard-pins openai==2.24.0, missing openai.types.responses used by crewai;
# override to >=2.30.0 (the version litellm 1.83.7 used) until upstream relaxes the pin.
override-dependencies = [
@@ -205,10 +203,8 @@ override-dependencies = [
"uv>=0.11.6,<1",
"python-multipart>=0.0.27,<1",
"gitpython>=3.1.50,<4",
"langsmith>=0.8.0,<1",
"langsmith>=0.7.31,<0.8",
"authlib>=1.6.11",
"pip>=26.1.1",
"paramiko>=5.0.0",
]
[tool.uv.workspace]

36
uv.lock generated
View File

@@ -13,7 +13,7 @@ resolution-markers = [
]
[options]
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
exclude-newer = "2026-05-08T16:33:02.834109Z"
exclude-newer-span = "P3D"
[manifest]
@@ -31,12 +31,10 @@ overrides = [
{ name = "gitpython", specifier = ">=3.1.50,<4" },
{ name = "langchain-core", specifier = ">=1.3.3,<2" },
{ name = "langchain-text-splitters", specifier = ">=1.1.2,<2" },
{ name = "langsmith", specifier = ">=0.8.0,<1" },
{ name = "langsmith", specifier = ">=0.7.31,<0.8" },
{ name = "onnxruntime", marker = "python_full_version < '3.11'", specifier = "<1.24" },
{ name = "openai", specifier = ">=2.30.0,<3" },
{ name = "paramiko", specifier = ">=5.0.0" },
{ name = "pillow", specifier = ">=12.1.1" },
{ name = "pip", specifier = ">=26.1.1" },
{ name = "pypdf", specifier = ">=6.10.2,<7" },
{ name = "python-multipart", specifier = ">=0.0.27,<1" },
{ name = "rich", specifier = ">=13.7.1" },
@@ -443,14 +441,14 @@ wheels = [
[[package]]
name = "authlib"
version = "1.6.12"
version = "1.6.11"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d3/30/6691fdc63b35f54a5a65e04fa1e59d827f4d4e8f4a39678ba7d3088ce0c8/authlib-1.6.12.tar.gz", hash = "sha256:0656d8482f28fc8221929d5f35b2bde5d13e10555ebc06b4561b0d622e83b1bd", size = 165368, upload-time = "2026-05-04T08:11:31.826Z" }
sdist = { url = "https://files.pythonhosted.org/packages/28/10/b325d58ffe86815b399334a101e63bc6fa4e1953921cb23703b48a0a0220/authlib-1.6.11.tar.gz", hash = "sha256:64db35b9b01aeccb4715a6c9a6613a06f2bd7be2ab9d2eb89edd1dfc7580a38f", size = 165359, upload-time = "2026-04-16T07:22:50.279Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cd/51/9b0b5cd4cf683a02db937a6f9bbebcdc9c56558a7bb3763ce7d3512103c3/authlib-1.6.12-py2.py3-none-any.whl", hash = "sha256:e9229ad7fde610b139dd12f5edbe97eab9ee78bfb85691247e767727850b99ab", size = 244473, upload-time = "2026-05-04T08:11:30.354Z" },
{ url = "https://files.pythonhosted.org/packages/57/2f/55fca558f925a51db046e5b929deb317ddb05afed74b22d89f4eca578980/authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3", size = 244469, upload-time = "2026-04-16T07:22:48.413Z" },
]
[[package]]
@@ -3270,11 +3268,11 @@ wheels = [
[[package]]
name = "idna"
version = "3.15"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
@@ -3890,7 +3888,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2
[[package]]
name = "langsmith"
version = "0.8.3"
version = "0.7.32"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx" },
@@ -3903,9 +3901,9 @@ dependencies = [
{ name = "xxhash" },
{ name = "zstandard" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/8a/1e8ea5e8bab2a65fa95bd36229ef38e8723ec46e430e20ca2d953487a7f1/langsmith-0.8.3.tar.gz", hash = "sha256:767ff7a8d136ed42926bf99059ac631dc6883542d6e3104b32e71c7625e1fa05", size = 4460330, upload-time = "2026-05-07T19:56:56.18Z" }
sdist = { url = "https://files.pythonhosted.org/packages/2f/b4/a0b4a501bee6b8a741ce29f8c48155b132118483cddc6f9247735ddb38fa/langsmith-0.7.32.tar.gz", hash = "sha256:b59b8e106d0e4c4842e158229296086e2aa7c561e3f602acda73d3ad0062e915", size = 1184518, upload-time = "2026-04-15T23:42:41.885Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/a9/51e644c1f1dbc3dd7d22dfd6412eab206d538c81e024e4f287373544bdcb/langsmith-0.8.3-py3-none-any.whl", hash = "sha256:b2e40e308222fa0beb2dccee3b4b30bfee9062d7a4f20a3e3e93df3c51a08ab4", size = 399048, upload-time = "2026-05-07T19:56:53.994Z" },
{ url = "https://files.pythonhosted.org/packages/62/bc/148f98ac7dad73ac5e1b1c985290079cfeeb9ba13d760a24f25002beb2c9/langsmith-0.7.32-py3-none-any.whl", hash = "sha256:e1fde928990c4c52f47dc5132708cec674355d9101723d564183e965f383bf5f", size = 378272, upload-time = "2026-04-15T23:42:39.905Z" },
]
[[package]]
@@ -5790,7 +5788,7 @@ wheels = [
[[package]]
name = "paramiko"
version = "5.0.0"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "bcrypt" },
@@ -5798,9 +5796,9 @@ dependencies = [
{ name = "invoke" },
{ name = "pynacl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/93/dcc25d52f49022ae6175d15e6bd751f1acc99b98bc61fc55e5155a7be2e7/paramiko-5.0.0.tar.gz", hash = "sha256:36763b5b95c2a0dcfdf1abc48e48156ee425b21efe2f0e787c2dd5a95c0e5e79", size = 1548586, upload-time = "2026-05-09T18:28:52.256Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1f/e7/81fdcbc7f190cdb058cffc9431587eb289833bdd633e2002455ca9bb13d4/paramiko-4.0.0.tar.gz", hash = "sha256:6a25f07b380cc9c9a88d2b920ad37167ac4667f8d9886ccebd8f90f654b5d69f", size = 1630743, upload-time = "2025-08-04T01:02:03.711Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/82/5b/eadf6d45de38d30ab603f49393b6cd2cbe7e233af8cf90197e32782b68a9/paramiko-5.0.0-py3-none-any.whl", hash = "sha256:b7044611c30140d9a75261653210e2002977b71a0497ff3ba0d98d7edbf62f7c", size = 208919, upload-time = "2026-05-09T18:28:50.295Z" },
{ url = "https://files.pythonhosted.org/packages/a9/90/a744336f5af32c433bd09af7854599682a383b37cfd78f7de263de6ad6cb/paramiko-4.0.0-py3-none-any.whl", hash = "sha256:0e20e00ac666503bf0b4eda3b6d833465a2b7aff2e2b3d79a8bba5ef144ee3b9", size = 223932, upload-time = "2025-08-04T01:02:02.029Z" },
]
[[package]]
@@ -6062,11 +6060,11 @@ wheels = [
[[package]]
name = "pip"
version = "26.1.1"
version = "26.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b6/48/cb9b7a682f6fe01a4221e1728941dd4ac3cd9090a17db3779d6ff490b602/pip-26.1.1.tar.gz", hash = "sha256:d36762751d156a4ee895de8af39aa0abeeeb577f93a2eca6ab62467bbf0f8a78", size = 1840400, upload-time = "2026-05-04T19:02:21.248Z" }
sdist = { url = "https://files.pythonhosted.org/packages/73/7e/d2b04004e1068ad4fdfa2f227b839b5d03e602e47cdbbf49de71137c9546/pip-26.1.tar.gz", hash = "sha256:81e13ebcca3ffa8cc85e4deff5c27e1ee26dea0aa7fc2f294a073ac208806ff3", size = 1840316, upload-time = "2026-04-26T21:00:05.406Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/eb/fea4d1d51c49832120f7f285d07306db3960f423a2612c6057caf3e8196f/pip-26.1.1-py3-none-any.whl", hash = "sha256:99cb1c2899893b075ff56e4ed0af55669a955b49ad7fb8d8603ecdaf4ed653fb", size = 1812777, upload-time = "2026-05-04T19:02:18.9Z" },
{ url = "https://files.pythonhosted.org/packages/70/7a/be4bd8bcbb24ea475856dd68159d78b03b2bb53dae369f69c9606b8888f5/pip-26.1-py3-none-any.whl", hash = "sha256:4e8486d821d814b77319acb7b9e8bf5a4ee7590a643e7cb21029f209be8573c1", size = 1812804, upload-time = "2026-04-26T21:00:03.194Z" },
]
[[package]]