From 2f48937ce41ef2fe91e40f17085418c5428a9393 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:57:00 -0700 Subject: [PATCH 01/32] docs(crews): document missing params and add Checkpointing section (OSS-32) (#5409) - Add 8 missing parameters to the Crew Attributes table: chat_llm, before_kickoff_callbacks, after_kickoff_callbacks, tracing, skills, security_config, checkpoint - Add new "## Checkpointing" section before "## Memory Utilization" with: - Quick-start checkpoint=True example - Full CheckpointConfig usage example - Crew.from_checkpoint() resume pattern - CheckpointConfig attributes table (location, on_events, provider, max_checkpoints) - Note on auto-restored checkpoint fields Closes OSS-32 --- docs/en/concepts/crews.mdx | 75 +++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/docs/en/concepts/crews.mdx b/docs/en/concepts/crews.mdx index 07fcfd59d..aacf02e1e 100644 --- a/docs/en/concepts/crews.mdx +++ b/docs/en/concepts/crews.mdx @@ -33,7 +33,14 @@ A crew in crewAI represents a collaborative group of agents working together to | **Planning** *(optional)* | `planning` | Adds planning ability to the Crew. When activated before each Crew iteration, all Crew data is sent to an AgentPlanner that will plan the tasks and this plan will be added to each task description. | | **Planning LLM** *(optional)* | `planning_llm` | The language model used by the AgentPlanner in a planning process. | | **Knowledge Sources** _(optional)_ | `knowledge_sources` | Knowledge sources available at the crew level, accessible to all the agents. | -| **Stream** _(optional)_ | `stream` | Enable streaming output to receive real-time updates during crew execution. Returns a `CrewStreamingOutput` object that can be iterated for chunks. Defaults to `False`. | +| **Stream** _(optional)_ | `stream` | Enable streaming output to receive real-time updates during crew execution. Returns a `CrewStreamingOutput` object that can be iterated for chunks. Defaults to `False`. | +| **Chat LLM** _(optional)_ | `chat_llm` | The language model used to orchestrate `crewai chat` CLI interactions with the crew. Accepts a model name string or `LLM` instance. Defaults to `None`. | +| **Before Kickoff Callbacks** _(optional)_ | `before_kickoff_callbacks` | A list of callable functions executed **before** the crew starts. Each callback receives and can modify the inputs dict. Distinct from the `@before_kickoff` decorator. Defaults to `[]`. | +| **After Kickoff Callbacks** _(optional)_ | `after_kickoff_callbacks` | A list of callable functions executed **after** the crew finishes. Each callback receives and can modify the `CrewOutput`. Distinct from the `@after_kickoff` decorator. Defaults to `[]`. | +| **Tracing** _(optional)_ | `tracing` | Controls OpenTelemetry tracing for the crew. `True` = always enable, `False` = always disable, `None` = inherit from environment / user settings. Defaults to `None`. | +| **Skills** _(optional)_ | `skills` | A list of `Path` objects (skill search directories) or pre-loaded `Skill` objects applied to all agents in the crew. Defaults to `None`. | +| **Security Config** _(optional)_ | `security_config` | A `SecurityConfig` instance managing crew fingerprinting and identity. Defaults to `SecurityConfig()`. | +| **Checkpoint** _(optional)_ | `checkpoint` | Enables automatic checkpointing. Pass `True` for sensible defaults, a `CheckpointConfig` for full control, `False` to opt out, or `None` to inherit. See the [Checkpointing](#checkpointing) section below. Defaults to `None`. | **Crew Max RPM**: The `max_rpm` attribute sets the maximum number of requests per minute the crew can perform to avoid rate limits and will override individual agents' `max_rpm` settings if you set it. @@ -271,6 +278,72 @@ crew = Crew(output_log_file = file_name.json) # Logs will be saved as file_name +## Checkpointing + +Checkpointing lets a crew automatically save its state after key events (e.g. task completion) so that long-running or interrupted runs can be resumed exactly where they left off without re-executing completed tasks. + +### Quick Start + +Pass `checkpoint=True` to enable checkpointing with sensible defaults (saves to `.checkpoints/` after every task): + +```python Code +from crewai import Crew, Process + +crew = Crew( + agents=[researcher, writer], + tasks=[research_task, write_task], + process=Process.sequential, + checkpoint=True, # saves to .checkpoints/ after every task +) + +crew.kickoff(inputs={"topic": "AI trends"}) +``` + +### Full Control with `CheckpointConfig` + +Use `CheckpointConfig` for fine-grained control over location, trigger events, storage backend, and retention: + +```python Code +from crewai import Crew, Process +from crewai.state.checkpoint_config import CheckpointConfig + +crew = Crew( + agents=[researcher, writer], + tasks=[research_task, write_task], + process=Process.sequential, + checkpoint=CheckpointConfig( + location="./.checkpoints", # directory for JSON files (default) + on_events=["task_completed"], # trigger after each task (default) + max_checkpoints=5, # keep only the 5 most recent checkpoints + ), +) + +crew.kickoff(inputs={"topic": "AI trends"}) +``` + +### Resuming from a Checkpoint + +Use `Crew.from_checkpoint()` to restore a crew from a saved checkpoint file, then call `kickoff()` to resume: + +```python Code +# Resume from the most recent checkpoint +crew = Crew.from_checkpoint(".checkpoints/latest.json") +crew.kickoff() +``` + + +When restoring from a checkpoint, `checkpoint_inputs`, `checkpoint_train`, and `checkpoint_kickoff_event_id` are automatically reconstructed — you do not need to set these manually. + + +### `CheckpointConfig` Attributes + +| Attribute | Type | Default | Description | +| :----------------- | :------------------------------------- | :------------------- | :-------------------------------------------------------------------------------------------- | +| `location` | `str` | `"./.checkpoints"` | Storage destination. For `JsonProvider` this is a directory path; for `SqliteProvider` a database file path. | +| `on_events` | `list[str]` | `["task_completed"]` | Event types that trigger a checkpoint write. Use `["*"]` to checkpoint on every event. | +| `provider` | `JsonProvider \| SqliteProvider` | `JsonProvider()` | Storage backend. Defaults to `JsonProvider` (plain JSON files). | +| `max_checkpoints` | `int \| None` | `None` | Maximum checkpoints to keep. Oldest are pruned after each write. `None` keeps all. | + ## Memory Utilization Crews can utilize memory (short-term, long-term, and entity memory) to enhance their execution and learning over time. This feature allows crews to store and recall execution memories, aiding in decision-making and task execution strategies. From 19ac7d2f64e199931fe77bdb763473cf825aed7e Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 17 Apr 2026 21:25:47 +0800 Subject: [PATCH 02/32] fix: patch authlib, langchain-text-splitters, and pypdf vulnerabilities - authlib 1.6.9 -> 1.6.11 (GHSA-jj8c-mmj3-mmgv) - langchain-text-splitters 1.1.1 -> 1.1.2 (GHSA-fv5p-p927-qmxr) - langchain-core 1.2.28 -> 1.2.31 (required by text-splitters 1.1.2) - pypdf 6.10.1 -> 6.10.2 (GHSA-4pxv-j86v-mhcw, GHSA-7gw9-cf7v-778f, GHSA-x284-j5p8-9c5p) Pinned tool.uv.exclude-newer to 2026-04-17 so the 2026-04-16 patch releases fall inside the resolution window. --- pyproject.toml | 16 +++++++++++----- uv.lock | 33 +++++++++++++++++---------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f3d5f7f8f..1b8aea627 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,30 +162,36 @@ info = "Commits must follow Conventional Commits 1.0.0." [tool.uv] -exclude-newer = "1 day" +# Pinned to include the security patch releases (authlib 1.6.11, +# langchain-text-splitters 1.1.2) uploaded on 2026-04-16. +exclude-newer = "2026-04-17" # composio-core pins rich<14 but textual requires rich>=14. # onnxruntime 1.24+ dropped Python 3.10 wheels; cap it so qdrant[fastembed] resolves on 3.10. # fastembed 0.7.x and docling 2.63 cap pillow<12; the removed APIs don't affect them. -# langchain-core <1.2.28 has GHSA-926x-3r5x-gfhw (incomplete f-string validation). +# langchain-core <1.2.31 has GHSA-926x-3r5x-gfhw and is required by langchain-text-splitters 1.1.2+. +# langchain-text-splitters <1.1.2 has GHSA-fv5p-p927-qmxr (SSRF bypass in split_text_from_url). # transformers 4.57.6 has CVE-2026-1839; force 5.4+ (docling 2.84 allows huggingface-hub>=1). # cryptography 46.0.6 has CVE-2026-39892; force 46.0.7+. -# pypdf <6.10.1 has CVE-2026-40260 and GHSA-jj6c-8h6c-hppx; force 6.10.1+. +# pypdf <6.10.2 has GHSA-4pxv-j86v-mhcw, GHSA-7gw9-cf7v-778f, GHSA-x284-j5p8-9c5p; force 6.10.2+. # uv <0.11.6 has GHSA-pjjw-68hj-v9mw; force 0.11.6+. # python-multipart <0.0.26 has GHSA-mj87-hwqh-73pj; force 0.0.26+. # 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). override-dependencies = [ "rich>=13.7.1", "onnxruntime<1.24; python_version < '3.11'", "pillow>=12.1.1", - "langchain-core>=1.2.28,<2", + "langchain-core>=1.2.31,<2", + "langchain-text-splitters>=1.1.2,<2", "urllib3>=2.6.3", "transformers>=5.4.0; python_version >= '3.10'", "cryptography>=46.0.7", - "pypdf>=6.10.1,<7", + "pypdf>=6.10.2,<7", "uv>=0.11.6,<1", "python-multipart>=0.0.26,<1", "langsmith>=0.7.31,<0.8", + "authlib>=1.6.11", ] [tool.uv.workspace] diff --git a/uv.lock b/uv.lock index a89b53544..ad1bf7276 100644 --- a/uv.lock +++ b/uv.lock @@ -13,8 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-15T15:14:38.695171Z" -exclude-newer-span = "P1D" +exclude-newer = "2026-04-17T16:00:00Z" [manifest] members = [ @@ -24,12 +23,14 @@ members = [ "crewai-tools", ] overrides = [ + { name = "authlib", specifier = ">=1.6.11" }, { name = "cryptography", specifier = ">=46.0.7" }, - { name = "langchain-core", specifier = ">=1.2.28,<2" }, + { name = "langchain-core", specifier = ">=1.2.31,<2" }, + { name = "langchain-text-splitters", specifier = ">=1.1.2,<2" }, { name = "langsmith", specifier = ">=0.7.31,<0.8" }, { name = "onnxruntime", marker = "python_full_version < '3.11'", specifier = "<1.24" }, { name = "pillow", specifier = ">=12.1.1" }, - { name = "pypdf", specifier = ">=6.10.1,<7" }, + { name = "pypdf", specifier = ">=6.10.2,<7" }, { name = "python-multipart", specifier = ">=0.0.26,<1" }, { name = "rich", specifier = ">=13.7.1" }, { name = "transformers", marker = "python_full_version >= '3.10'", specifier = ">=5.4.0" }, @@ -422,14 +423,14 @@ wheels = [ [[package]] name = "authlib" -version = "1.6.9" +version = "1.6.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } +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/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, + { 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]] @@ -3557,7 +3558,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.2.28" +version = "1.2.31" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -3569,21 +3570,21 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/317a1a3ac1df33a64adb3670bf88bbe3b3d5baa274db6863a979db472897/langchain_core-1.2.28.tar.gz", hash = "sha256:271a3d8bd618f795fdeba112b0753980457fc90537c46a0c11998516a74dc2cb", size = 846119, upload-time = "2026-04-08T18:19:34.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/5a/7523ff55668a233beef7e909e8e2074a1cc3b620e0bbf0a4ec5f38549b3b/langchain_core-1.2.31.tar.gz", hash = "sha256:aad3ecc9e4dce2dd2bb79526c81b92e5322fd81db7834a031cb80359f2e3ebaa", size = 850756, upload-time = "2026-04-16T13:26:29.241Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/92/32f785f077c7e898da97064f113c73fbd9ad55d1e2169cf3a391b183dedb/langchain_core-1.2.28-py3-none-any.whl", hash = "sha256:80764232581eaf8057bcefa71dbf8adc1f6a28d257ebd8b95ba9b8b452e8c6ac", size = 508727, upload-time = "2026-04-08T18:19:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/52/02/668ddf4f1cf963ad691bdbea672a85244e6271eb0a4acfaf662bbd94a3b1/langchain_core-1.2.31-py3-none-any.whl", hash = "sha256:c407193edb99311cc36ec3e4d3667a065bbc4d7d72fbb6e368538b9b134d4033", size = 513264, upload-time = "2026-04-16T13:26:27.566Z" }, ] [[package]] name = "langchain-text-splitters" -version = "1.1.1" +version = "1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/38/14121ead61e0e75f79c3a35e5148ac7c2fe754a55f76eab3eed573269524/langchain_text_splitters-1.1.1.tar.gz", hash = "sha256:34861abe7c07d9e49d4dc852d0129e26b32738b60a74486853ec9b6d6a8e01d2", size = 279352, upload-time = "2026-02-18T23:02:42.798Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/9f/6c545900fefb7b00ddfa3f16b80d61338a0ec68c31c5451eeeab99082760/langchain_text_splitters-1.1.2.tar.gz", hash = "sha256:782a723db0a4746ac91e251c7c1d57fd23636e4f38ed733074e28d7a86f41627", size = 293580, upload-time = "2026-04-16T14:20:39.162Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/66/d9e0c3b83b0ad75ee746c51ba347cacecb8d656b96e1d513f3e334d1ccab/langchain_text_splitters-1.1.1-py3-none-any.whl", hash = "sha256:5ed0d7bf314ba925041e7d7d17cd8b10f688300d5415fb26c29442f061e329dc", size = 35734, upload-time = "2026-02-18T23:02:41.913Z" }, + { url = "https://files.pythonhosted.org/packages/d3/26/1ef06f56198d631296d646a6223de35bcc6cf9795ceb2442816bc963b84c/langchain_text_splitters-1.1.2-py3-none-any.whl", hash = "sha256:a2de0d799ff31886429fd6e2e0032df275b60ec817c19059a7b46181cc1c2f10", size = 35903, upload-time = "2026-04-16T14:20:38.243Z" }, ] [[package]] @@ -6729,14 +6730,14 @@ wheels = [ [[package]] name = "pypdf" -version = "6.10.1" +version = "6.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/79/f2730c42ec7891a75a2fcea2eb4f356872bcbc671b711418060424796612/pypdf-6.10.1.tar.gz", hash = "sha256:62e6ca7f65aaa28b3d192addb44f97296e4be1748f57ed0f4efb2d4915841880", size = 5315704, upload-time = "2026-04-14T12:55:20.996Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/3f/9f2167401c2e94833ca3b69535bad89e533b5de75fefe4197a2c224baec2/pypdf-6.10.2.tar.gz", hash = "sha256:7d09ce108eff6bf67465d461b6ef352dcb8d84f7a91befc02f904455c6eea11d", size = 5315679, upload-time = "2026-04-15T16:37:36.978Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/04/e3aa7f1f14dbc53429cae34666261eb935d99bd61d24756ab94d7e0309da/pypdf-6.10.1-py3-none-any.whl", hash = "sha256:6331940d3bfe75b7e6601d35db7adabab5fc1d716efaeb384e3c0c3957d033de", size = 335606, upload-time = "2026-04-14T12:55:18.941Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d6/1d5c60cc17bbdf37c1552d9c03862fc6d32c5836732a0415b2d637edc2d0/pypdf-6.10.2-py3-none-any.whl", hash = "sha256:aa53be9826655b51c96741e5d7983ca224d898ac0a77896e64636810517624aa", size = 336308, upload-time = "2026-04-15T16:37:34.851Z" }, ] [[package]] From 11989da4b12b03ad15f303af11a8d8c26fec947f Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 17 Apr 2026 21:55:48 +0800 Subject: [PATCH 03/32] fix: prompt on stale branch conflicts in devtools release --- lib/devtools/src/crewai_devtools/cli.py | 126 ++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 9 deletions(-) diff --git a/lib/devtools/src/crewai_devtools/cli.py b/lib/devtools/src/crewai_devtools/cli.py index eca54063b..b9aee6e1b 100644 --- a/lib/devtools/src/crewai_devtools/cli.py +++ b/lib/devtools/src/crewai_devtools/cli.py @@ -154,6 +154,109 @@ def check_git_clean() -> None: sys.exit(1) +def _branch_exists_local(branch: str, cwd: Path | None = None) -> bool: + try: + subprocess.run( # noqa: S603 + ["git", "show-ref", "--verify", "--quiet", f"refs/heads/{branch}"], # noqa: S607 + cwd=cwd, + check=True, + capture_output=True, + ) + return True + except subprocess.CalledProcessError: + return False + + +def _branch_exists_remote(branch: str, cwd: Path | None = None) -> bool: + try: + output = run_command(["git", "ls-remote", "--heads", "origin", branch], cwd=cwd) + return bool(output.strip()) + except subprocess.CalledProcessError: + return False + + +def _open_pr_url_for_branch(branch: str, cwd: Path | None = None) -> str | None: + """Return URL of open PR for branch, or None if no open PR exists.""" + try: + url = run_command( + [ + "gh", + "pr", + "list", + "--head", + branch, + "--state", + "open", + "--json", + "url", + "--jq", + ".[0].url // empty", + ], + cwd=cwd, + ) + return url or None + except subprocess.CalledProcessError: + return None + + +def create_or_reset_branch(branch: str, cwd: Path | None = None) -> None: + """Create ``branch`` from current HEAD, resetting any stale copy. + + If the branch exists locally or on origin, prompts the user to + choose between resetting it or aborting. If an open PR exists on + the branch, the prompt surfaces the PR URL and includes a + close-and-reset option so in-flight work isn't silently clobbered. + + Raises: + SystemExit: If the user declines to reset. + """ + local_exists = _branch_exists_local(branch, cwd=cwd) + remote_exists = _branch_exists_remote(branch, cwd=cwd) + open_pr = _open_pr_url_for_branch(branch, cwd=cwd) if remote_exists else None + + if local_exists or remote_exists: + if open_pr: + console.print( + f"\n[yellow]![/yellow] Branch [bold]{branch}[/bold] already has an open PR: {open_pr}" + ) + prompt = "Close the PR, reset the branch, and continue?" + else: + where = [] + if local_exists: + where.append("local") + if remote_exists: + where.append("remote") + console.print( + f"\n[yellow]![/yellow] Branch [bold]{branch}[/bold] already exists ({', '.join(where)}) with no open PR" + ) + prompt = "Delete it and recreate?" + + if not Confirm.ask(prompt, default=False): + console.print("[red]Aborted.[/red]") + sys.exit(1) + + if open_pr: + console.print(f"Closing PR {open_pr}...") + run_command( + ["gh", "pr", "close", branch, "--delete-branch"], + cwd=cwd, + ) + # `gh pr close --delete-branch` removes the remote branch + # and, when checked out, the local branch too. + local_exists = _branch_exists_local(branch, cwd=cwd) + remote_exists = False + + if local_exists: + console.print(f"[yellow]![/yellow] Deleting local branch {branch}") + run_command(["git", "branch", "-D", branch], cwd=cwd) + + if remote_exists: + console.print(f"[yellow]![/yellow] Deleting remote branch {branch}") + run_command(["git", "push", "origin", "--delete", branch], cwd=cwd) + + run_command(["git", "checkout", "-b", branch], cwd=cwd) + + def update_version_in_file(file_path: Path, new_version: str) -> bool: """Update __version__ attribute in a Python file. @@ -980,7 +1083,7 @@ def _update_docs_and_create_pr( if docs_files_staged: docs_branch = f"docs/changelog-v{version}" - run_command(["git", "checkout", "-b", docs_branch]) + create_or_reset_branch(docs_branch) for f in docs_files_staged: run_command(["git", "add", f]) run_command( @@ -1418,7 +1521,7 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non console.print("[green]✓[/green] Workspace synced") branch_name = f"feat/bump-version-{version}" - run_command(["git", "checkout", "-b", branch_name], cwd=repo_dir) + create_or_reset_branch(branch_name, cwd=repo_dir) run_command(["git", "add", "."], cwd=repo_dir) run_command( ["git", "commit", "-m", f"feat: bump versions to {version}"], @@ -1616,18 +1719,20 @@ def bump(version: str, dry_run: bool, no_push: bool, no_commit: bool) -> None: for pkg in packages: console.print(f" - {pkg.name}") - console.print(f"\nUpdating version to {version}...") - _update_all_versions(cwd, lib_dir, version, packages, dry_run) - if no_commit: + console.print(f"\nUpdating version to {version}...") + _update_all_versions(cwd, lib_dir, version, packages, dry_run) console.print("\nSkipping git operations (--no-commit flag set)") else: branch_name = f"feat/bump-version-{version}" if not dry_run: console.print(f"\nCreating branch {branch_name}...") - run_command(["git", "checkout", "-b", branch_name]) + create_or_reset_branch(branch_name) console.print("[green]✓[/green] Branch created") + console.print(f"\nUpdating version to {version}...") + _update_all_versions(cwd, lib_dir, version, packages, dry_run) + console.print("\nCommitting changes...") run_command(["git", "add", "."]) run_command( @@ -1643,6 +1748,8 @@ def bump(version: str, dry_run: bool, no_push: bool, no_commit: bool) -> None: console.print( f"[dim][DRY RUN][/dim] Would create branch: {branch_name}" ) + console.print(f"\nUpdating version to {version}...") + _update_all_versions(cwd, lib_dir, version, packages, dry_run) console.print( f"[dim][DRY RUN][/dim] Would commit: feat: bump versions to {version}" ) @@ -1906,14 +2013,15 @@ def release( console.print(f"\n[bold cyan]Phase 1: Bumping versions to {version}[/bold cyan]") try: - _update_all_versions(cwd, lib_dir, version, packages, dry_run) - branch_name = f"feat/bump-version-{version}" if not dry_run: console.print(f"\nCreating branch {branch_name}...") - run_command(["git", "checkout", "-b", branch_name]) + create_or_reset_branch(branch_name) console.print("[green]✓[/green] Branch created") + _update_all_versions(cwd, lib_dir, version, packages, dry_run) + + if not dry_run: console.print("\nCommitting changes...") run_command(["git", "add", "."]) run_command(["git", "commit", "-m", f"feat: bump versions to {version}"]) From 5ca62c20f216f3ade6c2d42a73f25374718d0788 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 17 Apr 2026 22:01:27 +0800 Subject: [PATCH 04/32] feat: bump versions to 1.14.2 --- lib/crewai-files/src/crewai_files/__init__.py | 2 +- lib/crewai-tools/pyproject.toml | 2 +- lib/crewai-tools/src/crewai_tools/__init__.py | 2 +- lib/crewai/pyproject.toml | 2 +- lib/crewai/src/crewai/__init__.py | 2 +- lib/crewai/src/crewai/cli/templates/crew/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/flow/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/tool/pyproject.toml | 2 +- lib/devtools/src/crewai_devtools/__init__.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/crewai-files/src/crewai_files/__init__.py b/lib/crewai-files/src/crewai_files/__init__.py index 9cfa885c2..1b954d886 100644 --- a/lib/crewai-files/src/crewai_files/__init__.py +++ b/lib/crewai-files/src/crewai_files/__init__.py @@ -152,4 +152,4 @@ __all__ = [ "wrap_file_source", ] -__version__ = "1.14.2rc1" +__version__ = "1.14.2" diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index 5547d96ef..c75e406dd 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14" dependencies = [ "pytube~=15.0.0", "requests>=2.33.0,<3", - "crewai==1.14.2rc1", + "crewai==1.14.2", "tiktoken~=0.8.0", "beautifulsoup4~=4.13.4", "python-docx~=1.2.0", diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index d9ce4e9c2..56b30bf9b 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -305,4 +305,4 @@ __all__ = [ "ZapierActionTools", ] -__version__ = "1.14.2rc1" +__version__ = "1.14.2" diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index 350e1b65f..e6ab04399 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -55,7 +55,7 @@ Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] tools = [ - "crewai-tools==1.14.2rc1", + "crewai-tools==1.14.2", ] embeddings = [ "tiktoken~=0.8.0" diff --git a/lib/crewai/src/crewai/__init__.py b/lib/crewai/src/crewai/__init__.py index 14ef26727..b6766741f 100644 --- a/lib/crewai/src/crewai/__init__.py +++ b/lib/crewai/src/crewai/__init__.py @@ -46,7 +46,7 @@ def _suppress_pydantic_deprecation_warnings() -> None: _suppress_pydantic_deprecation_warnings() -__version__ = "1.14.2rc1" +__version__ = "1.14.2" _telemetry_submitted = False diff --git a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml index 01ce3f12b..c9bddf975 100644 --- a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2rc1" + "crewai[tools]==1.14.2" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml index b91fcbaf1..ddb2750e9 100644 --- a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2rc1" + "crewai[tools]==1.14.2" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml index 1089ad5c0..a84300df4 100644 --- a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2rc1" + "crewai[tools]==1.14.2" ] [tool.crewai] diff --git a/lib/devtools/src/crewai_devtools/__init__.py b/lib/devtools/src/crewai_devtools/__init__.py index db857f660..9c2031491 100644 --- a/lib/devtools/src/crewai_devtools/__init__.py +++ b/lib/devtools/src/crewai_devtools/__init__.py @@ -1,3 +1,3 @@ """CrewAI development tools.""" -__version__ = "1.14.2rc1" +__version__ = "1.14.2" From a8994347b0c74023ab9011aa5d9f2f01cb62aeac Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 17 Apr 2026 22:08:25 +0800 Subject: [PATCH 05/32] docs: update changelog and version for v1.14.2 --- docs/ar/changelog.mdx | 39 + docs/docs.json | 1880 +++++++++++++++++++++++++++++++++++++- docs/en/changelog.mdx | 39 + docs/ko/changelog.mdx | 39 + docs/pt-BR/changelog.mdx | 39 + 5 files changed, 2032 insertions(+), 4 deletions(-) diff --git a/docs/ar/changelog.mdx b/docs/ar/changelog.mdx index 7b4ad9f17..09a397719 100644 --- a/docs/ar/changelog.mdx +++ b/docs/ar/changelog.mdx @@ -4,6 +4,45 @@ description: "تحديثات المنتج والتحسينات وإصلاحات icon: "clock" mode: "wide" --- + + ## v1.14.2 + + [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) + + ## ما الذي تغير + + ### الميزات + - إضافة أوامر استئناف النقاط التفتيش، والاختلاف، والتنظيف مع تحسين إمكانية الاكتشاف. + - إضافة معلمة `from_checkpoint` إلى `Agent.kickoff` والطرق ذات الصلة. + - إضافة أوامر إدارة القوالب لقوالب المشاريع. + - إضافة تلميحات استئناف إلى إصدار أدوات المطور عند الفشل. + - إضافة واجهة سطر الأوامر للتحقق من النشر وتعزيز سهولة استخدام تهيئة LLM. + - إضافة تقسيم النقاط التفتيشية مع تتبع النسب. + - إثراء تتبع رموز LLM مع رموز الاستدلال ورموز إنشاء التخزين المؤقت. + + ### إصلاحات الأخطاء + - إصلاح المطالبة بشأن تعارضات الفروع القديمة في إصدار أدوات المطور. + - تصحيح الثغرات في `authlib` و `langchain-text-splitters` و `pypdf`. + - تحديد نطاق معالجات البث لمنع تلوث أجزاء التشغيل المتقاطعة. + - إرسال نقاط التفتيش عبر واجهات Flow في TUI. + - استخدام نمط البحث المتكرر لاكتشاف نقاط التفتيش بتنسيق JSON. + - التعامل مع مخططات JSON الدائرية في أداة حل MCP. + - الحفاظ على معلمات استدعاء أداة Bedrock من خلال إزالة القيمة الافتراضية الصحيحة. + - إصدار حدث flow_finished بعد استئناف HITL. + - إصلاح ثغرات متنوعة من خلال تحديث التبعيات، بما في ذلك `requests` و `cryptography` و `pytest`. + - إصلاح لإيقاف تمرير وضع صارم إلى واجهة برمجة التطبيقات Bedrock Converse. + + ### الوثائق + - توثيق المعلمات المفقودة وإضافة قسم النقاط التفتيشية. + - تحديث سجل التغييرات والإصدار للإصدار v1.14.2 ومرشحي الإصدار السابقين. + - إضافة توثيق ميزة A2A الخاصة بالشركات وتحديث وثائق A2A المفتوحة المصدر. + + ## المساهمون + + @Yanhu007، @alex-clawd، @github-actions[bot]، @greysonlalonde، @iris-clawd، @lorenzejay، @lucasgomide + + + ## v1.14.2rc1 diff --git a/docs/docs.json b/docs/docs.json index 3f37157df..f727c3e84 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -56,7 +56,7 @@ }, "versions": [ { - "version": "v1.14.1", + "version": "v1.14.2", "default": true, "tabs": [ { @@ -530,6 +530,480 @@ } ] }, + { + "version": "v1.14.1", + "tabs": [ + { + "tab": "Home", + "icon": "house", + "groups": [ + { + "group": "Welcome", + "pages": [ + "index" + ] + } + ] + }, + { + "tab": "Documentation", + "icon": "book-open", + "groups": [ + { + "group": "Get Started", + "pages": [ + "en/introduction", + "en/skills", + "en/installation", + "en/quickstart" + ] + }, + { + "group": "Guides", + "pages": [ + { + "group": "Strategy", + "icon": "compass", + "pages": [ + "en/guides/concepts/evaluating-use-cases" + ] + }, + { + "group": "Agents", + "icon": "user", + "pages": [ + "en/guides/agents/crafting-effective-agents" + ] + }, + { + "group": "Crews", + "icon": "users", + "pages": [ + "en/guides/crews/first-crew" + ] + }, + { + "group": "Flows", + "icon": "code-branch", + "pages": [ + "en/guides/flows/first-flow", + "en/guides/flows/mastering-flow-state" + ] + }, + { + "group": "Tools", + "icon": "wrench", + "pages": [ + "en/guides/tools/publish-custom-tools" + ] + }, + { + "group": "Coding Tools", + "icon": "terminal", + "pages": [ + "en/guides/coding-tools/agents-md" + ] + }, + { + "group": "Advanced", + "icon": "gear", + "pages": [ + "en/guides/advanced/customizing-prompts", + "en/guides/advanced/fingerprinting" + ] + }, + { + "group": "Migration", + "icon": "shuffle", + "pages": [ + "en/guides/migration/migrating-from-langgraph" + ] + } + ] + }, + { + "group": "Core Concepts", + "pages": [ + "en/concepts/agents", + "en/concepts/agent-capabilities", + "en/concepts/tasks", + "en/concepts/crews", + "en/concepts/flows", + "en/concepts/production-architecture", + "en/concepts/knowledge", + "en/concepts/skills", + "en/concepts/llms", + "en/concepts/files", + "en/concepts/processes", + "en/concepts/collaboration", + "en/concepts/training", + "en/concepts/memory", + "en/concepts/reasoning", + "en/concepts/planning", + "en/concepts/testing", + "en/concepts/cli", + "en/concepts/tools", + "en/concepts/event-listener", + "en/concepts/checkpointing" + ] + }, + { + "group": "MCP Integration", + "pages": [ + "en/mcp/overview", + "en/mcp/dsl-integration", + "en/mcp/stdio", + "en/mcp/sse", + "en/mcp/streamable-http", + "en/mcp/multiple-servers", + "en/mcp/security" + ] + }, + { + "group": "Tools", + "pages": [ + "en/tools/overview", + { + "group": "File & Document", + "icon": "folder-open", + "pages": [ + "en/tools/file-document/overview", + "en/tools/file-document/filereadtool", + "en/tools/file-document/filewritetool", + "en/tools/file-document/pdfsearchtool", + "en/tools/file-document/docxsearchtool", + "en/tools/file-document/mdxsearchtool", + "en/tools/file-document/xmlsearchtool", + "en/tools/file-document/txtsearchtool", + "en/tools/file-document/jsonsearchtool", + "en/tools/file-document/csvsearchtool", + "en/tools/file-document/directorysearchtool", + "en/tools/file-document/directoryreadtool", + "en/tools/file-document/ocrtool", + "en/tools/file-document/pdf-text-writing-tool" + ] + }, + { + "group": "Web Scraping & Browsing", + "icon": "globe", + "pages": [ + "en/tools/web-scraping/overview", + "en/tools/web-scraping/scrapewebsitetool", + "en/tools/web-scraping/scrapeelementfromwebsitetool", + "en/tools/web-scraping/scrapflyscrapetool", + "en/tools/web-scraping/seleniumscrapingtool", + "en/tools/web-scraping/scrapegraphscrapetool", + "en/tools/web-scraping/spidertool", + "en/tools/web-scraping/browserbaseloadtool", + "en/tools/web-scraping/hyperbrowserloadtool", + "en/tools/web-scraping/stagehandtool", + "en/tools/web-scraping/firecrawlcrawlwebsitetool", + "en/tools/web-scraping/firecrawlscrapewebsitetool", + "en/tools/web-scraping/oxylabsscraperstool", + "en/tools/web-scraping/brightdata-tools" + ] + }, + { + "group": "Search & Research", + "icon": "magnifying-glass", + "pages": [ + "en/tools/search-research/overview", + "en/tools/search-research/serperdevtool", + "en/tools/search-research/bravesearchtool", + "en/tools/search-research/exasearchtool", + "en/tools/search-research/linkupsearchtool", + "en/tools/search-research/githubsearchtool", + "en/tools/search-research/websitesearchtool", + "en/tools/search-research/codedocssearchtool", + "en/tools/search-research/youtubechannelsearchtool", + "en/tools/search-research/youtubevideosearchtool", + "en/tools/search-research/tavilysearchtool", + "en/tools/search-research/tavilyextractortool", + "en/tools/search-research/arxivpapertool", + "en/tools/search-research/serpapi-googlesearchtool", + "en/tools/search-research/serpapi-googleshoppingtool", + "en/tools/search-research/databricks-query-tool" + ] + }, + { + "group": "Database & Data", + "icon": "database", + "pages": [ + "en/tools/database-data/overview", + "en/tools/database-data/mysqltool", + "en/tools/database-data/pgsearchtool", + "en/tools/database-data/snowflakesearchtool", + "en/tools/database-data/nl2sqltool", + "en/tools/database-data/qdrantvectorsearchtool", + "en/tools/database-data/weaviatevectorsearchtool", + "en/tools/database-data/mongodbvectorsearchtool", + "en/tools/database-data/singlestoresearchtool" + ] + }, + { + "group": "AI & Machine Learning", + "icon": "brain", + "pages": [ + "en/tools/ai-ml/overview", + "en/tools/ai-ml/dalletool", + "en/tools/ai-ml/visiontool", + "en/tools/ai-ml/aimindtool", + "en/tools/ai-ml/llamaindextool", + "en/tools/ai-ml/langchaintool", + "en/tools/ai-ml/ragtool", + "en/tools/ai-ml/codeinterpretertool" + ] + }, + { + "group": "Cloud & Storage", + "icon": "cloud", + "pages": [ + "en/tools/cloud-storage/overview", + "en/tools/cloud-storage/s3readertool", + "en/tools/cloud-storage/s3writertool", + "en/tools/cloud-storage/bedrockkbretriever" + ] + }, + { + "group": "Integrations", + "icon": "plug", + "pages": [ + "en/tools/integration/overview", + "en/tools/integration/bedrockinvokeagenttool", + "en/tools/integration/crewaiautomationtool", + "en/tools/integration/mergeagenthandlertool" + ] + }, + { + "group": "Automation", + "icon": "bolt", + "pages": [ + "en/tools/automation/overview", + "en/tools/automation/apifyactorstool", + "en/tools/automation/composiotool", + "en/tools/automation/multiontool", + "en/tools/automation/zapieractionstool" + ] + } + ] + }, + { + "group": "Observability", + "pages": [ + "en/observability/tracing", + "en/observability/overview", + "en/observability/arize-phoenix", + "en/observability/braintrust", + "en/observability/datadog", + "en/observability/galileo", + "en/observability/langdb", + "en/observability/langfuse", + "en/observability/langtrace", + "en/observability/maxim", + "en/observability/mlflow", + "en/observability/neatlogs", + "en/observability/openlit", + "en/observability/opik", + "en/observability/patronus-evaluation", + "en/observability/portkey", + "en/observability/weave", + "en/observability/truefoundry" + ] + }, + { + "group": "Learn", + "pages": [ + "en/learn/overview", + "en/learn/llm-selection-guide", + "en/learn/conditional-tasks", + "en/learn/coding-agents", + "en/learn/create-custom-tools", + "en/learn/custom-llm", + "en/learn/custom-manager-agent", + "en/learn/customizing-agents", + "en/learn/dalle-image-generation", + "en/learn/force-tool-output-as-result", + "en/learn/hierarchical-process", + "en/learn/human-input-on-execution", + "en/learn/human-in-the-loop", + "en/learn/human-feedback-in-flows", + "en/learn/kickoff-async", + "en/learn/kickoff-for-each", + "en/learn/llm-connections", + "en/learn/litellm-removal-guide", + "en/learn/multimodal-agents", + "en/learn/replay-tasks-from-latest-crew-kickoff", + "en/learn/sequential-process", + "en/learn/using-annotations", + "en/learn/execution-hooks", + "en/learn/llm-hooks", + "en/learn/tool-hooks" + ] + }, + { + "group": "Telemetry", + "pages": [ + "en/telemetry" + ] + } + ] + }, + { + "tab": "AMP", + "icon": "briefcase", + "groups": [ + { + "group": "Getting Started", + "pages": [ + "en/enterprise/introduction" + ] + }, + { + "group": "Build", + "pages": [ + "en/enterprise/features/automations", + "en/enterprise/features/crew-studio", + "en/enterprise/features/marketplace", + "en/enterprise/features/agent-repositories", + "en/enterprise/features/tools-and-integrations", + "en/enterprise/features/pii-trace-redactions", + "en/enterprise/features/a2a" + ] + }, + { + "group": "Operate", + "pages": [ + "en/enterprise/features/traces", + "en/enterprise/features/webhook-streaming", + "en/enterprise/features/hallucination-guardrail", + "en/enterprise/features/flow-hitl-management" + ] + }, + { + "group": "Manage", + "pages": [ + "en/enterprise/features/sso", + "en/enterprise/features/rbac" + ] + }, + { + "group": "Integration Docs", + "pages": [ + "en/enterprise/integrations/asana", + "en/enterprise/integrations/box", + "en/enterprise/integrations/clickup", + "en/enterprise/integrations/github", + "en/enterprise/integrations/gmail", + "en/enterprise/integrations/google_calendar", + "en/enterprise/integrations/google_contacts", + "en/enterprise/integrations/google_docs", + "en/enterprise/integrations/google_drive", + "en/enterprise/integrations/google_sheets", + "en/enterprise/integrations/google_slides", + "en/enterprise/integrations/hubspot", + "en/enterprise/integrations/jira", + "en/enterprise/integrations/linear", + "en/enterprise/integrations/microsoft_excel", + "en/enterprise/integrations/microsoft_onedrive", + "en/enterprise/integrations/microsoft_outlook", + "en/enterprise/integrations/microsoft_sharepoint", + "en/enterprise/integrations/microsoft_teams", + "en/enterprise/integrations/microsoft_word", + "en/enterprise/integrations/notion", + "en/enterprise/integrations/salesforce", + "en/enterprise/integrations/shopify", + "en/enterprise/integrations/slack", + "en/enterprise/integrations/stripe", + "en/enterprise/integrations/zendesk" + ] + }, + { + "group": "Triggers", + "pages": [ + "en/enterprise/guides/automation-triggers", + "en/enterprise/guides/gmail-trigger", + "en/enterprise/guides/google-calendar-trigger", + "en/enterprise/guides/google-drive-trigger", + "en/enterprise/guides/outlook-trigger", + "en/enterprise/guides/onedrive-trigger", + "en/enterprise/guides/microsoft-teams-trigger", + "en/enterprise/guides/slack-trigger", + "en/enterprise/guides/hubspot-trigger", + "en/enterprise/guides/salesforce-trigger", + "en/enterprise/guides/zapier-trigger" + ] + }, + { + "group": "How-To Guides", + "pages": [ + "en/enterprise/guides/build-crew", + "en/enterprise/guides/prepare-for-deployment", + "en/enterprise/guides/deploy-to-amp", + "en/enterprise/guides/private-package-registry", + "en/enterprise/guides/kickoff-crew", + "en/enterprise/guides/update-crew", + "en/enterprise/guides/enable-crew-studio", + "en/enterprise/guides/capture_telemetry_logs", + "en/enterprise/guides/azure-openai-setup", + "en/enterprise/guides/tool-repository", + "en/enterprise/guides/custom-mcp-server", + "en/enterprise/guides/react-component-export", + "en/enterprise/guides/team-management", + "en/enterprise/guides/human-in-the-loop", + "en/enterprise/guides/webhook-automation" + ] + }, + { + "group": "Resources", + "pages": [ + "en/enterprise/resources/frequently-asked-questions" + ] + } + ] + }, + { + "tab": "API Reference", + "icon": "magnifying-glass", + "groups": [ + { + "group": "Getting Started", + "pages": [ + "en/api-reference/introduction", + "en/api-reference/inputs", + "en/api-reference/kickoff", + "en/api-reference/resume", + "en/api-reference/status" + ] + } + ] + }, + { + "tab": "Examples", + "icon": "code", + "groups": [ + { + "group": "Examples", + "pages": [ + "en/examples/example", + "en/examples/cookbooks" + ] + } + ] + }, + { + "tab": "Changelog", + "icon": "clock", + "groups": [ + { + "group": "Release Notes", + "pages": [ + "en/changelog" + ] + } + ] + } + ] + }, { "version": "v1.14.0", "tabs": [ @@ -4820,7 +5294,7 @@ }, "versions": [ { - "version": "v1.14.1", + "version": "v1.14.2", "default": true, "tabs": [ { @@ -5278,6 +5752,464 @@ } ] }, + { + "version": "v1.14.1", + "tabs": [ + { + "tab": "Início", + "icon": "house", + "groups": [ + { + "group": "Bem-vindo", + "pages": [ + "pt-BR/index" + ] + } + ] + }, + { + "tab": "Documentação", + "icon": "book-open", + "groups": [ + { + "group": "Começando", + "pages": [ + "pt-BR/introduction", + "pt-BR/skills", + "pt-BR/installation", + "pt-BR/quickstart" + ] + }, + { + "group": "Guias", + "pages": [ + { + "group": "Estratégia", + "icon": "compass", + "pages": [ + "pt-BR/guides/concepts/evaluating-use-cases" + ] + }, + { + "group": "Agentes", + "icon": "user", + "pages": [ + "pt-BR/guides/agents/crafting-effective-agents" + ] + }, + { + "group": "Crews", + "icon": "users", + "pages": [ + "pt-BR/guides/crews/first-crew" + ] + }, + { + "group": "Flows", + "icon": "code-branch", + "pages": [ + "pt-BR/guides/flows/first-flow", + "pt-BR/guides/flows/mastering-flow-state" + ] + }, + { + "group": "Ferramentas", + "icon": "wrench", + "pages": [ + "pt-BR/guides/tools/publish-custom-tools" + ] + }, + { + "group": "Ferramentas de Codificação", + "icon": "terminal", + "pages": [ + "pt-BR/guides/coding-tools/agents-md" + ] + }, + { + "group": "Avançado", + "icon": "gear", + "pages": [ + "pt-BR/guides/advanced/customizing-prompts", + "pt-BR/guides/advanced/fingerprinting" + ] + }, + { + "group": "Migração", + "icon": "shuffle", + "pages": [ + "pt-BR/guides/migration/migrating-from-langgraph" + ] + } + ] + }, + { + "group": "Conceitos-Chave", + "pages": [ + "pt-BR/concepts/agents", + "pt-BR/concepts/agent-capabilities", + "pt-BR/concepts/tasks", + "pt-BR/concepts/crews", + "pt-BR/concepts/flows", + "pt-BR/concepts/production-architecture", + "pt-BR/concepts/knowledge", + "pt-BR/concepts/skills", + "pt-BR/concepts/llms", + "pt-BR/concepts/files", + "pt-BR/concepts/processes", + "pt-BR/concepts/collaboration", + "pt-BR/concepts/training", + "pt-BR/concepts/memory", + "pt-BR/concepts/reasoning", + "pt-BR/concepts/planning", + "pt-BR/concepts/testing", + "pt-BR/concepts/cli", + "pt-BR/concepts/tools", + "pt-BR/concepts/event-listener", + "pt-BR/concepts/checkpointing" + ] + }, + { + "group": "Integração MCP", + "pages": [ + "pt-BR/mcp/overview", + "pt-BR/mcp/dsl-integration", + "pt-BR/mcp/stdio", + "pt-BR/mcp/sse", + "pt-BR/mcp/streamable-http", + "pt-BR/mcp/multiple-servers", + "pt-BR/mcp/security" + ] + }, + { + "group": "Ferramentas", + "pages": [ + "pt-BR/tools/overview", + { + "group": "Arquivo & Documento", + "icon": "folder-open", + "pages": [ + "pt-BR/tools/file-document/overview", + "pt-BR/tools/file-document/filereadtool", + "pt-BR/tools/file-document/filewritetool", + "pt-BR/tools/file-document/pdfsearchtool", + "pt-BR/tools/file-document/docxsearchtool", + "pt-BR/tools/file-document/mdxsearchtool", + "pt-BR/tools/file-document/xmlsearchtool", + "pt-BR/tools/file-document/txtsearchtool", + "pt-BR/tools/file-document/jsonsearchtool", + "pt-BR/tools/file-document/csvsearchtool", + "pt-BR/tools/file-document/directorysearchtool", + "pt-BR/tools/file-document/directoryreadtool" + ] + }, + { + "group": "Web Scraping & Navegação", + "icon": "globe", + "pages": [ + "pt-BR/tools/web-scraping/overview", + "pt-BR/tools/web-scraping/scrapewebsitetool", + "pt-BR/tools/web-scraping/scrapeelementfromwebsitetool", + "pt-BR/tools/web-scraping/scrapflyscrapetool", + "pt-BR/tools/web-scraping/seleniumscrapingtool", + "pt-BR/tools/web-scraping/scrapegraphscrapetool", + "pt-BR/tools/web-scraping/spidertool", + "pt-BR/tools/web-scraping/browserbaseloadtool", + "pt-BR/tools/web-scraping/hyperbrowserloadtool", + "pt-BR/tools/web-scraping/stagehandtool", + "pt-BR/tools/web-scraping/firecrawlcrawlwebsitetool", + "pt-BR/tools/web-scraping/firecrawlscrapewebsitetool", + "pt-BR/tools/web-scraping/oxylabsscraperstool" + ] + }, + { + "group": "Pesquisa", + "icon": "magnifying-glass", + "pages": [ + "pt-BR/tools/search-research/overview", + "pt-BR/tools/search-research/serperdevtool", + "pt-BR/tools/search-research/bravesearchtool", + "pt-BR/tools/search-research/exasearchtool", + "pt-BR/tools/search-research/linkupsearchtool", + "pt-BR/tools/search-research/githubsearchtool", + "pt-BR/tools/search-research/websitesearchtool", + "pt-BR/tools/search-research/codedocssearchtool", + "pt-BR/tools/search-research/youtubechannelsearchtool", + "pt-BR/tools/search-research/youtubevideosearchtool" + ] + }, + { + "group": "Dados", + "icon": "database", + "pages": [ + "pt-BR/tools/database-data/overview", + "pt-BR/tools/database-data/mysqltool", + "pt-BR/tools/database-data/pgsearchtool", + "pt-BR/tools/database-data/snowflakesearchtool", + "pt-BR/tools/database-data/nl2sqltool", + "pt-BR/tools/database-data/qdrantvectorsearchtool", + "pt-BR/tools/database-data/weaviatevectorsearchtool" + ] + }, + { + "group": "IA & Machine Learning", + "icon": "brain", + "pages": [ + "pt-BR/tools/ai-ml/overview", + "pt-BR/tools/ai-ml/dalletool", + "pt-BR/tools/ai-ml/visiontool", + "pt-BR/tools/ai-ml/aimindtool", + "pt-BR/tools/ai-ml/llamaindextool", + "pt-BR/tools/ai-ml/langchaintool", + "pt-BR/tools/ai-ml/ragtool", + "pt-BR/tools/ai-ml/codeinterpretertool" + ] + }, + { + "group": "Cloud & Armazenamento", + "icon": "cloud", + "pages": [ + "pt-BR/tools/cloud-storage/overview", + "pt-BR/tools/cloud-storage/s3readertool", + "pt-BR/tools/cloud-storage/s3writertool", + "pt-BR/tools/cloud-storage/bedrockkbretriever" + ] + }, + { + "group": "Integrations", + "icon": "plug", + "pages": [ + "pt-BR/tools/integration/overview", + "pt-BR/tools/integration/bedrockinvokeagenttool", + "pt-BR/tools/integration/crewaiautomationtool" + ] + }, + { + "group": "Automação", + "icon": "bolt", + "pages": [ + "pt-BR/tools/automation/overview", + "pt-BR/tools/automation/apifyactorstool", + "pt-BR/tools/automation/composiotool", + "pt-BR/tools/automation/multiontool" + ] + } + ] + }, + { + "group": "Observabilidade", + "pages": [ + "pt-BR/observability/tracing", + "pt-BR/observability/overview", + "pt-BR/observability/arize-phoenix", + "pt-BR/observability/braintrust", + "pt-BR/observability/datadog", + "pt-BR/observability/galileo", + "pt-BR/observability/langdb", + "pt-BR/observability/langfuse", + "pt-BR/observability/langtrace", + "pt-BR/observability/maxim", + "pt-BR/observability/mlflow", + "pt-BR/observability/openlit", + "pt-BR/observability/opik", + "pt-BR/observability/patronus-evaluation", + "pt-BR/observability/portkey", + "pt-BR/observability/weave", + "pt-BR/observability/truefoundry" + ] + }, + { + "group": "Aprenda", + "pages": [ + "pt-BR/learn/overview", + "pt-BR/learn/llm-selection-guide", + "pt-BR/learn/conditional-tasks", + "pt-BR/learn/coding-agents", + "pt-BR/learn/create-custom-tools", + "pt-BR/learn/custom-llm", + "pt-BR/learn/custom-manager-agent", + "pt-BR/learn/customizing-agents", + "pt-BR/learn/dalle-image-generation", + "pt-BR/learn/force-tool-output-as-result", + "pt-BR/learn/hierarchical-process", + "pt-BR/learn/human-input-on-execution", + "pt-BR/learn/human-in-the-loop", + "pt-BR/learn/human-feedback-in-flows", + "pt-BR/learn/kickoff-async", + "pt-BR/learn/kickoff-for-each", + "pt-BR/learn/llm-connections", + "pt-BR/learn/multimodal-agents", + "pt-BR/learn/replay-tasks-from-latest-crew-kickoff", + "pt-BR/learn/sequential-process", + "pt-BR/learn/using-annotations", + "pt-BR/learn/execution-hooks", + "pt-BR/learn/llm-hooks", + "pt-BR/learn/tool-hooks" + ] + }, + { + "group": "Telemetria", + "pages": [ + "pt-BR/telemetry" + ] + } + ] + }, + { + "tab": "AMP", + "icon": "briefcase", + "groups": [ + { + "group": "Começando", + "pages": [ + "pt-BR/enterprise/introduction" + ] + }, + { + "group": "Construir", + "pages": [ + "pt-BR/enterprise/features/automations", + "pt-BR/enterprise/features/crew-studio", + "pt-BR/enterprise/features/marketplace", + "pt-BR/enterprise/features/agent-repositories", + "pt-BR/enterprise/features/tools-and-integrations", + "pt-BR/enterprise/features/pii-trace-redactions" + ] + }, + { + "group": "Operar", + "pages": [ + "pt-BR/enterprise/features/traces", + "pt-BR/enterprise/features/webhook-streaming", + "pt-BR/enterprise/features/hallucination-guardrail", + "pt-BR/enterprise/features/flow-hitl-management" + ] + }, + { + "group": "Gerenciar", + "pages": [ + "pt-BR/enterprise/features/rbac" + ] + }, + { + "group": "Documentação de Integração", + "pages": [ + "pt-BR/enterprise/integrations/asana", + "pt-BR/enterprise/integrations/box", + "pt-BR/enterprise/integrations/clickup", + "pt-BR/enterprise/integrations/github", + "pt-BR/enterprise/integrations/gmail", + "pt-BR/enterprise/integrations/google_calendar", + "pt-BR/enterprise/integrations/google_contacts", + "pt-BR/enterprise/integrations/google_docs", + "pt-BR/enterprise/integrations/google_drive", + "pt-BR/enterprise/integrations/google_sheets", + "pt-BR/enterprise/integrations/google_slides", + "pt-BR/enterprise/integrations/hubspot", + "pt-BR/enterprise/integrations/jira", + "pt-BR/enterprise/integrations/linear", + "pt-BR/enterprise/integrations/microsoft_excel", + "pt-BR/enterprise/integrations/microsoft_onedrive", + "pt-BR/enterprise/integrations/microsoft_outlook", + "pt-BR/enterprise/integrations/microsoft_sharepoint", + "pt-BR/enterprise/integrations/microsoft_teams", + "pt-BR/enterprise/integrations/microsoft_word", + "pt-BR/enterprise/integrations/notion", + "pt-BR/enterprise/integrations/salesforce", + "pt-BR/enterprise/integrations/shopify", + "pt-BR/enterprise/integrations/slack", + "pt-BR/enterprise/integrations/stripe", + "pt-BR/enterprise/integrations/zendesk" + ] + }, + { + "group": "Guias", + "pages": [ + "pt-BR/enterprise/guides/build-crew", + "pt-BR/enterprise/guides/prepare-for-deployment", + "pt-BR/enterprise/guides/deploy-to-amp", + "pt-BR/enterprise/guides/private-package-registry", + "pt-BR/enterprise/guides/kickoff-crew", + "pt-BR/enterprise/guides/training-crews", + "pt-BR/enterprise/guides/update-crew", + "pt-BR/enterprise/guides/enable-crew-studio", + "pt-BR/enterprise/guides/capture_telemetry_logs", + "pt-BR/enterprise/guides/azure-openai-setup", + "pt-BR/enterprise/guides/tool-repository", + "pt-BR/enterprise/guides/custom-mcp-server", + "pt-BR/enterprise/guides/react-component-export", + "pt-BR/enterprise/guides/team-management", + "pt-BR/enterprise/guides/human-in-the-loop", + "pt-BR/enterprise/guides/webhook-automation" + ] + }, + { + "group": "Triggers", + "pages": [ + "pt-BR/enterprise/guides/automation-triggers", + "pt-BR/enterprise/guides/gmail-trigger", + "pt-BR/enterprise/guides/google-calendar-trigger", + "pt-BR/enterprise/guides/google-drive-trigger", + "pt-BR/enterprise/guides/outlook-trigger", + "pt-BR/enterprise/guides/onedrive-trigger", + "pt-BR/enterprise/guides/microsoft-teams-trigger", + "pt-BR/enterprise/guides/slack-trigger", + "pt-BR/enterprise/guides/hubspot-trigger", + "pt-BR/enterprise/guides/salesforce-trigger", + "pt-BR/enterprise/guides/zapier-trigger" + ] + }, + { + "group": "Recursos", + "pages": [ + "pt-BR/enterprise/resources/frequently-asked-questions" + ] + } + ] + }, + { + "tab": "Referência da API", + "icon": "magnifying-glass", + "groups": [ + { + "group": "Começando", + "pages": [ + "pt-BR/api-reference/introduction", + "pt-BR/api-reference/inputs", + "pt-BR/api-reference/kickoff", + "pt-BR/api-reference/resume", + "pt-BR/api-reference/status" + ] + } + ] + }, + { + "tab": "Exemplos", + "icon": "code", + "groups": [ + { + "group": "Exemplos", + "pages": [ + "pt-BR/examples/example", + "pt-BR/examples/cookbooks" + ] + } + ] + }, + { + "tab": "Notas de Versão", + "icon": "clock", + "groups": [ + { + "group": "Notas de Versão", + "pages": [ + "pt-BR/changelog" + ] + } + ] + } + ] + }, { "version": "v1.14.0", "tabs": [ @@ -9422,7 +10354,7 @@ }, "versions": [ { - "version": "v1.14.1", + "version": "v1.14.2", "default": true, "tabs": [ { @@ -9892,6 +10824,476 @@ } ] }, + { + "version": "v1.14.1", + "tabs": [ + { + "tab": "홈", + "icon": "house", + "groups": [ + { + "group": "환영합니다", + "pages": [ + "ko/index" + ] + } + ] + }, + { + "tab": "기술 문서", + "icon": "book-open", + "groups": [ + { + "group": "시작 안내", + "pages": [ + "ko/introduction", + "ko/skills", + "ko/installation", + "ko/quickstart" + ] + }, + { + "group": "가이드", + "pages": [ + { + "group": "전략", + "icon": "compass", + "pages": [ + "ko/guides/concepts/evaluating-use-cases" + ] + }, + { + "group": "에이전트 (Agents)", + "icon": "user", + "pages": [ + "ko/guides/agents/crafting-effective-agents" + ] + }, + { + "group": "크루 (Crews)", + "icon": "users", + "pages": [ + "ko/guides/crews/first-crew" + ] + }, + { + "group": "플로우 (Flows)", + "icon": "code-branch", + "pages": [ + "ko/guides/flows/first-flow", + "ko/guides/flows/mastering-flow-state" + ] + }, + { + "group": "도구", + "icon": "wrench", + "pages": [ + "ko/guides/tools/publish-custom-tools" + ] + }, + { + "group": "코딩 도구", + "icon": "terminal", + "pages": [ + "ko/guides/coding-tools/agents-md" + ] + }, + { + "group": "고급", + "icon": "gear", + "pages": [ + "ko/guides/advanced/customizing-prompts", + "ko/guides/advanced/fingerprinting" + ] + }, + { + "group": "마이그레이션", + "icon": "shuffle", + "pages": [ + "ko/guides/migration/migrating-from-langgraph" + ] + } + ] + }, + { + "group": "핵심 개념", + "pages": [ + "ko/concepts/agents", + "ko/concepts/tasks", + "ko/concepts/agent-capabilities", + "ko/concepts/crews", + "ko/concepts/flows", + "ko/concepts/production-architecture", + "ko/concepts/knowledge", + "ko/concepts/skills", + "ko/concepts/llms", + "ko/concepts/files", + "ko/concepts/processes", + "ko/concepts/collaboration", + "ko/concepts/training", + "ko/concepts/memory", + "ko/concepts/reasoning", + "ko/concepts/planning", + "ko/concepts/testing", + "ko/concepts/cli", + "ko/concepts/tools", + "ko/concepts/event-listener", + "ko/concepts/checkpointing" + ] + }, + { + "group": "MCP 통합", + "pages": [ + "ko/mcp/overview", + "ko/mcp/dsl-integration", + "ko/mcp/stdio", + "ko/mcp/sse", + "ko/mcp/streamable-http", + "ko/mcp/multiple-servers", + "ko/mcp/security" + ] + }, + { + "group": "도구 (Tools)", + "pages": [ + "ko/tools/overview", + { + "group": "파일 & 문서", + "icon": "folder-open", + "pages": [ + "ko/tools/file-document/overview", + "ko/tools/file-document/filereadtool", + "ko/tools/file-document/filewritetool", + "ko/tools/file-document/pdfsearchtool", + "ko/tools/file-document/docxsearchtool", + "ko/tools/file-document/mdxsearchtool", + "ko/tools/file-document/xmlsearchtool", + "ko/tools/file-document/txtsearchtool", + "ko/tools/file-document/jsonsearchtool", + "ko/tools/file-document/csvsearchtool", + "ko/tools/file-document/directorysearchtool", + "ko/tools/file-document/directoryreadtool", + "ko/tools/file-document/ocrtool", + "ko/tools/file-document/pdf-text-writing-tool" + ] + }, + { + "group": "웹 스크래핑 & 브라우징", + "icon": "globe", + "pages": [ + "ko/tools/web-scraping/overview", + "ko/tools/web-scraping/scrapewebsitetool", + "ko/tools/web-scraping/scrapeelementfromwebsitetool", + "ko/tools/web-scraping/scrapflyscrapetool", + "ko/tools/web-scraping/seleniumscrapingtool", + "ko/tools/web-scraping/scrapegraphscrapetool", + "ko/tools/web-scraping/spidertool", + "ko/tools/web-scraping/browserbaseloadtool", + "ko/tools/web-scraping/hyperbrowserloadtool", + "ko/tools/web-scraping/stagehandtool", + "ko/tools/web-scraping/firecrawlcrawlwebsitetool", + "ko/tools/web-scraping/firecrawlscrapewebsitetool", + "ko/tools/web-scraping/oxylabsscraperstool", + "ko/tools/web-scraping/brightdata-tools" + ] + }, + { + "group": "검색 및 연구", + "icon": "magnifying-glass", + "pages": [ + "ko/tools/search-research/overview", + "ko/tools/search-research/serperdevtool", + "ko/tools/search-research/bravesearchtool", + "ko/tools/search-research/exasearchtool", + "ko/tools/search-research/linkupsearchtool", + "ko/tools/search-research/githubsearchtool", + "ko/tools/search-research/websitesearchtool", + "ko/tools/search-research/codedocssearchtool", + "ko/tools/search-research/youtubechannelsearchtool", + "ko/tools/search-research/youtubevideosearchtool", + "ko/tools/search-research/tavilysearchtool", + "ko/tools/search-research/tavilyextractortool", + "ko/tools/search-research/arxivpapertool", + "ko/tools/search-research/serpapi-googlesearchtool", + "ko/tools/search-research/serpapi-googleshoppingtool", + "ko/tools/search-research/databricks-query-tool" + ] + }, + { + "group": "데이터베이스 & 데이터", + "icon": "database", + "pages": [ + "ko/tools/database-data/overview", + "ko/tools/database-data/mysqltool", + "ko/tools/database-data/pgsearchtool", + "ko/tools/database-data/snowflakesearchtool", + "ko/tools/database-data/nl2sqltool", + "ko/tools/database-data/qdrantvectorsearchtool", + "ko/tools/database-data/weaviatevectorsearchtool", + "ko/tools/database-data/mongodbvectorsearchtool", + "ko/tools/database-data/singlestoresearchtool" + ] + }, + { + "group": "인공지능 & 머신러닝", + "icon": "brain", + "pages": [ + "ko/tools/ai-ml/overview", + "ko/tools/ai-ml/dalletool", + "ko/tools/ai-ml/visiontool", + "ko/tools/ai-ml/aimindtool", + "ko/tools/ai-ml/llamaindextool", + "ko/tools/ai-ml/langchaintool", + "ko/tools/ai-ml/ragtool", + "ko/tools/ai-ml/codeinterpretertool" + ] + }, + { + "group": "클라우드 & 스토리지", + "icon": "cloud", + "pages": [ + "ko/tools/cloud-storage/overview", + "ko/tools/cloud-storage/s3readertool", + "ko/tools/cloud-storage/s3writertool", + "ko/tools/cloud-storage/bedrockkbretriever" + ] + }, + { + "group": "Integrations", + "icon": "plug", + "pages": [ + "ko/tools/integration/overview", + "ko/tools/integration/bedrockinvokeagenttool", + "ko/tools/integration/crewaiautomationtool" + ] + }, + { + "group": "자동화", + "icon": "bolt", + "pages": [ + "ko/tools/automation/overview", + "ko/tools/automation/apifyactorstool", + "ko/tools/automation/composiotool", + "ko/tools/automation/multiontool", + "ko/tools/automation/zapieractionstool" + ] + } + ] + }, + { + "group": "Observability", + "pages": [ + "ko/observability/tracing", + "ko/observability/overview", + "ko/observability/arize-phoenix", + "ko/observability/braintrust", + "ko/observability/datadog", + "ko/observability/galileo", + "ko/observability/langdb", + "ko/observability/langfuse", + "ko/observability/langtrace", + "ko/observability/maxim", + "ko/observability/mlflow", + "ko/observability/neatlogs", + "ko/observability/openlit", + "ko/observability/opik", + "ko/observability/patronus-evaluation", + "ko/observability/portkey", + "ko/observability/weave" + ] + }, + { + "group": "학습", + "pages": [ + "ko/learn/overview", + "ko/learn/llm-selection-guide", + "ko/learn/conditional-tasks", + "ko/learn/coding-agents", + "ko/learn/create-custom-tools", + "ko/learn/custom-llm", + "ko/learn/custom-manager-agent", + "ko/learn/customizing-agents", + "ko/learn/dalle-image-generation", + "ko/learn/force-tool-output-as-result", + "ko/learn/hierarchical-process", + "ko/learn/human-input-on-execution", + "ko/learn/human-in-the-loop", + "ko/learn/human-feedback-in-flows", + "ko/learn/kickoff-async", + "ko/learn/kickoff-for-each", + "ko/learn/llm-connections", + "ko/learn/multimodal-agents", + "ko/learn/replay-tasks-from-latest-crew-kickoff", + "ko/learn/sequential-process", + "ko/learn/using-annotations", + "ko/learn/execution-hooks", + "ko/learn/llm-hooks", + "ko/learn/tool-hooks" + ] + }, + { + "group": "Telemetry", + "pages": [ + "ko/telemetry" + ] + } + ] + }, + { + "tab": "엔터프라이즈", + "icon": "briefcase", + "groups": [ + { + "group": "시작 안내", + "pages": [ + "ko/enterprise/introduction" + ] + }, + { + "group": "빌드", + "pages": [ + "ko/enterprise/features/automations", + "ko/enterprise/features/crew-studio", + "ko/enterprise/features/marketplace", + "ko/enterprise/features/agent-repositories", + "ko/enterprise/features/tools-and-integrations", + "ko/enterprise/features/pii-trace-redactions" + ] + }, + { + "group": "운영", + "pages": [ + "ko/enterprise/features/traces", + "ko/enterprise/features/webhook-streaming", + "ko/enterprise/features/hallucination-guardrail", + "ko/enterprise/features/flow-hitl-management" + ] + }, + { + "group": "관리", + "pages": [ + "ko/enterprise/features/rbac" + ] + }, + { + "group": "통합 문서", + "pages": [ + "ko/enterprise/integrations/asana", + "ko/enterprise/integrations/box", + "ko/enterprise/integrations/clickup", + "ko/enterprise/integrations/github", + "ko/enterprise/integrations/gmail", + "ko/enterprise/integrations/google_calendar", + "ko/enterprise/integrations/google_contacts", + "ko/enterprise/integrations/google_docs", + "ko/enterprise/integrations/google_drive", + "ko/enterprise/integrations/google_sheets", + "ko/enterprise/integrations/google_slides", + "ko/enterprise/integrations/hubspot", + "ko/enterprise/integrations/jira", + "ko/enterprise/integrations/linear", + "ko/enterprise/integrations/microsoft_excel", + "ko/enterprise/integrations/microsoft_onedrive", + "ko/enterprise/integrations/microsoft_outlook", + "ko/enterprise/integrations/microsoft_sharepoint", + "ko/enterprise/integrations/microsoft_teams", + "ko/enterprise/integrations/microsoft_word", + "ko/enterprise/integrations/notion", + "ko/enterprise/integrations/salesforce", + "ko/enterprise/integrations/shopify", + "ko/enterprise/integrations/slack", + "ko/enterprise/integrations/stripe", + "ko/enterprise/integrations/zendesk" + ] + }, + { + "group": "How-To Guides", + "pages": [ + "ko/enterprise/guides/build-crew", + "ko/enterprise/guides/prepare-for-deployment", + "ko/enterprise/guides/deploy-to-amp", + "ko/enterprise/guides/private-package-registry", + "ko/enterprise/guides/kickoff-crew", + "ko/enterprise/guides/training-crews", + "ko/enterprise/guides/update-crew", + "ko/enterprise/guides/enable-crew-studio", + "ko/enterprise/guides/capture_telemetry_logs", + "ko/enterprise/guides/azure-openai-setup", + "ko/enterprise/guides/tool-repository", + "ko/enterprise/guides/custom-mcp-server", + "ko/enterprise/guides/react-component-export", + "ko/enterprise/guides/team-management", + "ko/enterprise/guides/human-in-the-loop", + "ko/enterprise/guides/webhook-automation" + ] + }, + { + "group": "트리거", + "pages": [ + "ko/enterprise/guides/automation-triggers", + "ko/enterprise/guides/gmail-trigger", + "ko/enterprise/guides/google-calendar-trigger", + "ko/enterprise/guides/google-drive-trigger", + "ko/enterprise/guides/outlook-trigger", + "ko/enterprise/guides/onedrive-trigger", + "ko/enterprise/guides/microsoft-teams-trigger", + "ko/enterprise/guides/slack-trigger", + "ko/enterprise/guides/hubspot-trigger", + "ko/enterprise/guides/salesforce-trigger", + "ko/enterprise/guides/zapier-trigger" + ] + }, + { + "group": "학습 자원", + "pages": [ + "ko/enterprise/resources/frequently-asked-questions" + ] + } + ] + }, + { + "tab": "API 레퍼런스", + "icon": "magnifying-glass", + "groups": [ + { + "group": "시작 안내", + "pages": [ + "ko/api-reference/introduction", + "ko/api-reference/inputs", + "ko/api-reference/kickoff", + "ko/api-reference/resume", + "ko/api-reference/status" + ] + } + ] + }, + { + "tab": "예시", + "icon": "code", + "groups": [ + { + "group": "예시", + "pages": [ + "ko/examples/example", + "ko/examples/cookbooks" + ] + } + ] + }, + { + "tab": "변경 로그", + "icon": "clock", + "groups": [ + { + "group": "릴리스 노트", + "pages": [ + "ko/changelog" + ] + } + ] + } + ] + }, { "version": "v1.14.0", "tabs": [ @@ -14144,7 +15546,7 @@ }, "versions": [ { - "version": "v1.14.1", + "version": "v1.14.2", "default": true, "tabs": [ { @@ -14614,6 +16016,476 @@ } ] }, + { + "version": "v1.14.1", + "tabs": [ + { + "tab": "الرئيسية", + "icon": "house", + "groups": [ + { + "group": "مرحباً", + "pages": [ + "ar/index" + ] + } + ] + }, + { + "tab": "التقنية التوثيق", + "icon": "book-open", + "groups": [ + { + "group": "البدء", + "pages": [ + "ar/introduction", + "ar/skills", + "ar/installation", + "ar/quickstart" + ] + }, + { + "group": "الأدلّة", + "pages": [ + { + "group": "الاستراتيجية", + "icon": "compass", + "pages": [ + "ar/guides/concepts/evaluating-use-cases" + ] + }, + { + "group": "الوكلاء", + "icon": "user", + "pages": [ + "ar/guides/agents/crafting-effective-agents" + ] + }, + { + "group": "الطواقم", + "icon": "users", + "pages": [ + "ar/guides/crews/first-crew" + ] + }, + { + "group": "التدفقات", + "icon": "code-branch", + "pages": [ + "ar/guides/flows/first-flow", + "ar/guides/flows/mastering-flow-state" + ] + }, + { + "group": "الأدوات", + "icon": "wrench", + "pages": [ + "ar/guides/tools/publish-custom-tools" + ] + }, + { + "group": "أدوات البرمجة", + "icon": "terminal", + "pages": [ + "ar/guides/coding-tools/agents-md" + ] + }, + { + "group": "متقدّم", + "icon": "gear", + "pages": [ + "ar/guides/advanced/customizing-prompts", + "ar/guides/advanced/fingerprinting" + ] + }, + { + "group": "الترحيل", + "icon": "shuffle", + "pages": [ + "ar/guides/migration/migrating-from-langgraph" + ] + } + ] + }, + { + "group": "المفاهيم الأساسية", + "pages": [ + "ar/concepts/agents", + "ar/concepts/agent-capabilities", + "ar/concepts/tasks", + "ar/concepts/crews", + "ar/concepts/flows", + "ar/concepts/production-architecture", + "ar/concepts/knowledge", + "ar/concepts/skills", + "ar/concepts/llms", + "ar/concepts/files", + "ar/concepts/processes", + "ar/concepts/collaboration", + "ar/concepts/training", + "ar/concepts/memory", + "ar/concepts/reasoning", + "ar/concepts/planning", + "ar/concepts/testing", + "ar/concepts/cli", + "ar/concepts/tools", + "ar/concepts/event-listener", + "ar/concepts/checkpointing" + ] + }, + { + "group": "تكامل MCP", + "pages": [ + "ar/mcp/overview", + "ar/mcp/dsl-integration", + "ar/mcp/stdio", + "ar/mcp/sse", + "ar/mcp/streamable-http", + "ar/mcp/multiple-servers", + "ar/mcp/security" + ] + }, + { + "group": "الأدوات", + "pages": [ + "ar/tools/overview", + { + "group": "الملفات والمستندات", + "icon": "folder-open", + "pages": [ + "ar/tools/file-document/overview", + "ar/tools/file-document/filereadtool", + "ar/tools/file-document/filewritetool", + "ar/tools/file-document/pdfsearchtool", + "ar/tools/file-document/docxsearchtool", + "ar/tools/file-document/mdxsearchtool", + "ar/tools/file-document/xmlsearchtool", + "ar/tools/file-document/txtsearchtool", + "ar/tools/file-document/jsonsearchtool", + "ar/tools/file-document/csvsearchtool", + "ar/tools/file-document/directorysearchtool", + "ar/tools/file-document/directoryreadtool", + "ar/tools/file-document/ocrtool", + "ar/tools/file-document/pdf-text-writing-tool" + ] + }, + { + "group": "استخراج بيانات الويب", + "icon": "globe", + "pages": [ + "ar/tools/web-scraping/overview", + "ar/tools/web-scraping/scrapewebsitetool", + "ar/tools/web-scraping/scrapeelementfromwebsitetool", + "ar/tools/web-scraping/scrapflyscrapetool", + "ar/tools/web-scraping/seleniumscrapingtool", + "ar/tools/web-scraping/scrapegraphscrapetool", + "ar/tools/web-scraping/spidertool", + "ar/tools/web-scraping/browserbaseloadtool", + "ar/tools/web-scraping/hyperbrowserloadtool", + "ar/tools/web-scraping/stagehandtool", + "ar/tools/web-scraping/firecrawlcrawlwebsitetool", + "ar/tools/web-scraping/firecrawlscrapewebsitetool", + "ar/tools/web-scraping/oxylabsscraperstool", + "ar/tools/web-scraping/brightdata-tools" + ] + }, + { + "group": "البحث والاستكشاف", + "icon": "magnifying-glass", + "pages": [ + "ar/tools/search-research/overview", + "ar/tools/search-research/serperdevtool", + "ar/tools/search-research/bravesearchtool", + "ar/tools/search-research/exasearchtool", + "ar/tools/search-research/linkupsearchtool", + "ar/tools/search-research/githubsearchtool", + "ar/tools/search-research/websitesearchtool", + "ar/tools/search-research/codedocssearchtool", + "ar/tools/search-research/youtubechannelsearchtool", + "ar/tools/search-research/youtubevideosearchtool", + "ar/tools/search-research/tavilysearchtool", + "ar/tools/search-research/tavilyextractortool", + "ar/tools/search-research/arxivpapertool", + "ar/tools/search-research/serpapi-googlesearchtool", + "ar/tools/search-research/serpapi-googleshoppingtool", + "ar/tools/search-research/databricks-query-tool" + ] + }, + { + "group": "قواعد البيانات", + "icon": "database", + "pages": [ + "ar/tools/database-data/overview", + "ar/tools/database-data/mysqltool", + "ar/tools/database-data/pgsearchtool", + "ar/tools/database-data/snowflakesearchtool", + "ar/tools/database-data/nl2sqltool", + "ar/tools/database-data/qdrantvectorsearchtool", + "ar/tools/database-data/weaviatevectorsearchtool", + "ar/tools/database-data/mongodbvectorsearchtool", + "ar/tools/database-data/singlestoresearchtool" + ] + }, + { + "group": "الذكاء الاصطناعي والتعلّم الآلي", + "icon": "brain", + "pages": [ + "ar/tools/ai-ml/overview", + "ar/tools/ai-ml/dalletool", + "ar/tools/ai-ml/visiontool", + "ar/tools/ai-ml/aimindtool", + "ar/tools/ai-ml/llamaindextool", + "ar/tools/ai-ml/langchaintool", + "ar/tools/ai-ml/ragtool", + "ar/tools/ai-ml/codeinterpretertool" + ] + }, + { + "group": "التخزين السحابي", + "icon": "cloud", + "pages": [ + "ar/tools/cloud-storage/overview", + "ar/tools/cloud-storage/s3readertool", + "ar/tools/cloud-storage/s3writertool", + "ar/tools/cloud-storage/bedrockkbretriever" + ] + }, + { + "group": "Integrations", + "icon": "plug", + "pages": [ + "ar/tools/integration/overview", + "ar/tools/integration/bedrockinvokeagenttool", + "ar/tools/integration/crewaiautomationtool" + ] + }, + { + "group": "الأتمتة", + "icon": "bolt", + "pages": [ + "ar/tools/automation/overview", + "ar/tools/automation/apifyactorstool", + "ar/tools/automation/composiotool", + "ar/tools/automation/multiontool", + "ar/tools/automation/zapieractionstool" + ] + } + ] + }, + { + "group": "Observability", + "pages": [ + "ar/observability/tracing", + "ar/observability/overview", + "ar/observability/arize-phoenix", + "ar/observability/braintrust", + "ar/observability/datadog", + "ar/observability/galileo", + "ar/observability/langdb", + "ar/observability/langfuse", + "ar/observability/langtrace", + "ar/observability/maxim", + "ar/observability/mlflow", + "ar/observability/neatlogs", + "ar/observability/openlit", + "ar/observability/opik", + "ar/observability/patronus-evaluation", + "ar/observability/portkey", + "ar/observability/weave" + ] + }, + { + "group": "التعلّم", + "pages": [ + "ar/learn/overview", + "ar/learn/llm-selection-guide", + "ar/learn/conditional-tasks", + "ar/learn/coding-agents", + "ar/learn/create-custom-tools", + "ar/learn/custom-llm", + "ar/learn/custom-manager-agent", + "ar/learn/customizing-agents", + "ar/learn/dalle-image-generation", + "ar/learn/force-tool-output-as-result", + "ar/learn/hierarchical-process", + "ar/learn/human-input-on-execution", + "ar/learn/human-in-the-loop", + "ar/learn/human-feedback-in-flows", + "ar/learn/kickoff-async", + "ar/learn/kickoff-for-each", + "ar/learn/llm-connections", + "ar/learn/multimodal-agents", + "ar/learn/replay-tasks-from-latest-crew-kickoff", + "ar/learn/sequential-process", + "ar/learn/using-annotations", + "ar/learn/execution-hooks", + "ar/learn/llm-hooks", + "ar/learn/tool-hooks" + ] + }, + { + "group": "Telemetry", + "pages": [ + "ar/telemetry" + ] + } + ] + }, + { + "tab": "المؤسسات", + "icon": "briefcase", + "groups": [ + { + "group": "البدء", + "pages": [ + "ar/enterprise/introduction" + ] + }, + { + "group": "البناء", + "pages": [ + "ar/enterprise/features/automations", + "ar/enterprise/features/crew-studio", + "ar/enterprise/features/marketplace", + "ar/enterprise/features/agent-repositories", + "ar/enterprise/features/tools-and-integrations", + "ar/enterprise/features/pii-trace-redactions" + ] + }, + { + "group": "العمليات", + "pages": [ + "ar/enterprise/features/traces", + "ar/enterprise/features/webhook-streaming", + "ar/enterprise/features/hallucination-guardrail", + "ar/enterprise/features/flow-hitl-management" + ] + }, + { + "group": "الإدارة", + "pages": [ + "ar/enterprise/features/rbac" + ] + }, + { + "group": "التكاملات", + "pages": [ + "ar/enterprise/integrations/asana", + "ar/enterprise/integrations/box", + "ar/enterprise/integrations/clickup", + "ar/enterprise/integrations/github", + "ar/enterprise/integrations/gmail", + "ar/enterprise/integrations/google_calendar", + "ar/enterprise/integrations/google_contacts", + "ar/enterprise/integrations/google_docs", + "ar/enterprise/integrations/google_drive", + "ar/enterprise/integrations/google_sheets", + "ar/enterprise/integrations/google_slides", + "ar/enterprise/integrations/hubspot", + "ar/enterprise/integrations/jira", + "ar/enterprise/integrations/linear", + "ar/enterprise/integrations/microsoft_excel", + "ar/enterprise/integrations/microsoft_onedrive", + "ar/enterprise/integrations/microsoft_outlook", + "ar/enterprise/integrations/microsoft_sharepoint", + "ar/enterprise/integrations/microsoft_teams", + "ar/enterprise/integrations/microsoft_word", + "ar/enterprise/integrations/notion", + "ar/enterprise/integrations/salesforce", + "ar/enterprise/integrations/shopify", + "ar/enterprise/integrations/slack", + "ar/enterprise/integrations/stripe", + "ar/enterprise/integrations/zendesk" + ] + }, + { + "group": "How-To Guides", + "pages": [ + "ar/enterprise/guides/build-crew", + "ar/enterprise/guides/prepare-for-deployment", + "ar/enterprise/guides/deploy-to-amp", + "ar/enterprise/guides/private-package-registry", + "ar/enterprise/guides/kickoff-crew", + "ar/enterprise/guides/training-crews", + "ar/enterprise/guides/update-crew", + "ar/enterprise/guides/enable-crew-studio", + "ar/enterprise/guides/capture_telemetry_logs", + "ar/enterprise/guides/azure-openai-setup", + "ar/enterprise/guides/tool-repository", + "ar/enterprise/guides/custom-mcp-server", + "ar/enterprise/guides/react-component-export", + "ar/enterprise/guides/team-management", + "ar/enterprise/guides/human-in-the-loop", + "ar/enterprise/guides/webhook-automation" + ] + }, + { + "group": "المشغّلات", + "pages": [ + "ar/enterprise/guides/automation-triggers", + "ar/enterprise/guides/gmail-trigger", + "ar/enterprise/guides/google-calendar-trigger", + "ar/enterprise/guides/google-drive-trigger", + "ar/enterprise/guides/outlook-trigger", + "ar/enterprise/guides/onedrive-trigger", + "ar/enterprise/guides/microsoft-teams-trigger", + "ar/enterprise/guides/slack-trigger", + "ar/enterprise/guides/hubspot-trigger", + "ar/enterprise/guides/salesforce-trigger", + "ar/enterprise/guides/zapier-trigger" + ] + }, + { + "group": "موارد التعلّم", + "pages": [ + "ar/enterprise/resources/frequently-asked-questions" + ] + } + ] + }, + { + "tab": "API المرجع", + "icon": "magnifying-glass", + "groups": [ + { + "group": "البدء", + "pages": [ + "ar/api-reference/introduction", + "ar/api-reference/inputs", + "ar/api-reference/kickoff", + "ar/api-reference/resume", + "ar/api-reference/status" + ] + } + ] + }, + { + "tab": "أمثلة", + "icon": "code", + "groups": [ + { + "group": "أمثلة", + "pages": [ + "ar/examples/example", + "ar/examples/cookbooks" + ] + } + ] + }, + { + "tab": "التغييرات السجلات", + "icon": "clock", + "groups": [ + { + "group": "سجل التغييرات", + "pages": [ + "ar/changelog" + ] + } + ] + } + ] + }, { "version": "v1.14.0", "tabs": [ diff --git a/docs/en/changelog.mdx b/docs/en/changelog.mdx index d1a72c2ca..aabff6e24 100644 --- a/docs/en/changelog.mdx +++ b/docs/en/changelog.mdx @@ -4,6 +4,45 @@ description: "Product updates, improvements, and bug fixes for CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.2 + + [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) + + ## What's Changed + + ### Features + - Add checkpoint resume, diff, and prune commands with improved discoverability. + - Add `from_checkpoint` parameter to `Agent.kickoff` and related methods. + - Add template management commands for project templates. + - Add resume hints to devtools release on failure. + - Add deploy validation CLI and enhance LLM initialization ergonomics. + - Add checkpoint forking with lineage tracking. + - Enrich LLM token tracking with reasoning tokens and cache creation tokens. + + ### Bug Fixes + - Fix prompt on stale branch conflicts in devtools release. + - Patch vulnerabilities in `authlib`, `langchain-text-splitters`, and `pypdf`. + - Scope streaming handlers to prevent cross-run chunk contamination. + - Dispatch Flow checkpoints through Flow APIs in TUI. + - Use recursive glob for JSON checkpoint discovery. + - Handle cyclic JSON schemas in MCP tool resolution. + - Preserve Bedrock tool call arguments by removing truthy default. + - Emit flow_finished event after HITL resume. + - Fix various vulnerabilities by updating dependencies, including `requests`, `cryptography`, and `pytest`. + - Fix to stop forwarding strict mode to Bedrock Converse API. + + ### Documentation + - Document missing parameters and add Checkpointing section. + - Update changelog and version for v1.14.2 and previous release candidates. + - Add enterprise A2A feature documentation and update OSS A2A docs. + + ## Contributors + + @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide + + + ## v1.14.2rc1 diff --git a/docs/ko/changelog.mdx b/docs/ko/changelog.mdx index 5cb685f3c..0901afab6 100644 --- a/docs/ko/changelog.mdx +++ b/docs/ko/changelog.mdx @@ -4,6 +4,45 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정" icon: "clock" mode: "wide" --- + + ## v1.14.2 + + [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) + + ## 변경 사항 + + ### 기능 + - 체크포인트 재개, 차이(diff), 및 가지치기(prune) 명령을 추가하여 가시성을 개선했습니다. + - `Agent.kickoff` 및 관련 메서드에 `from_checkpoint` 매개변수를 추가했습니다. + - 프로젝트 템플릿을 위한 템플릿 관리 명령을 추가했습니다. + - 실패 시 개발 도구 릴리스에 재개 힌트를 추가했습니다. + - 배포 검증 CLI를 추가하고 LLM 초기화의 사용 편의성을 향상시켰습니다. + - 계보 추적이 가능한 체크포인트 포킹을 추가했습니다. + - 추론 토큰 및 캐시 생성 토큰으로 LLM 토큰 추적을 풍부하게 했습니다. + + ### 버그 수정 + - 개발 도구 릴리스에서 오래된 브랜치 충돌에 대한 프롬프트를 수정했습니다. + - `authlib`, `langchain-text-splitters`, 및 `pypdf`의 취약점을 패치했습니다. + - 스트리밍 핸들러의 범위를 설정하여 교차 실행 청크 오염을 방지했습니다. + - TUI에서 Flow API를 통해 Flow 체크포인트를 전송했습니다. + - JSON 체크포인트 발견을 위해 재귀적 글로브를 사용했습니다. + - MCP 도구 해상도에서 순환 JSON 스키마를 처리했습니다. + - 진리값이 있는 기본값을 제거하여 Bedrock 도구 호출 인수를 보존했습니다. + - HITL 재개 후 flow_finished 이벤트를 발생시켰습니다. + - `requests`, `cryptography`, 및 `pytest`를 포함한 종속성을 업데이트하여 다양한 취약점을 수정했습니다. + - Bedrock Converse API에 엄격 모드를 전달하지 않도록 수정했습니다. + + ### 문서 + - 누락된 매개변수를 문서화하고 체크포인팅 섹션을 추가했습니다. + - v1.14.2 및 이전 릴리스 후보에 대한 변경 로그 및 버전을 업데이트했습니다. + - 기업 A2A 기능 문서를 추가하고 OSS A2A 문서를 업데이트했습니다. + + ## 기여자 + + @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide + + + ## v1.14.2rc1 diff --git a/docs/pt-BR/changelog.mdx b/docs/pt-BR/changelog.mdx index 02ab715fb..cf1eef1a9 100644 --- a/docs/pt-BR/changelog.mdx +++ b/docs/pt-BR/changelog.mdx @@ -4,6 +4,45 @@ description: "Atualizações de produto, melhorias e correções do CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.2 + + [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) + + ## O que Mudou + + ### Recursos + - Adicionar comandos de retomar, diferenciar e podar checkpoints com melhor descobribilidade. + - Adicionar o parâmetro `from_checkpoint` ao `Agent.kickoff` e métodos relacionados. + - Adicionar comandos de gerenciamento de templates para templates de projeto. + - Adicionar dicas de retomar na liberação de devtools em caso de falha. + - Adicionar CLI de validação de implantação e melhorar a ergonomia da inicialização do LLM. + - Adicionar bifurcação de checkpoints com rastreamento de linhagem. + - Enriquecer o rastreamento de tokens do LLM com tokens de raciocínio e tokens de criação de cache. + + ### Correções de Bugs + - Corrigir prompt em conflitos de branch obsoletos na liberação de devtools. + - Corrigir vulnerabilidades em `authlib`, `langchain-text-splitters` e `pypdf`. + - Restringir manipuladores de streaming para evitar contaminação de chunks entre execuções. + - Despachar checkpoints de Flow através das APIs de Flow na TUI. + - Usar glob recursivo para descoberta de checkpoints JSON. + - Lidar com esquemas JSON cíclicos na resolução de ferramentas MCP. + - Preservar os argumentos de chamada da ferramenta Bedrock removendo o padrão truthy. + - Emitir evento flow_finished após retomar HITL. + - Corrigir várias vulnerabilidades atualizando dependências, incluindo `requests`, `cryptography` e `pytest`. + - Corrigir para parar de encaminhar o modo estrito para a API Bedrock Converse. + + ### Documentação + - Documentar parâmetros ausentes e adicionar seção de Checkpointing. + - Atualizar changelog e versão para v1.14.2 e candidatos a liberação anteriores. + - Adicionar documentação da funcionalidade A2A empresarial e atualizar a documentação A2A OSS. + + ## Contribuidores + + @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide + + + ## v1.14.2rc1 From c9b0004d0ee2e95e8d4d627e7639309d9052d4eb Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 17 Apr 2026 23:26:52 +0800 Subject: [PATCH 06/32] fix: correct dry-run order and handle checked-out stale branch in devtools release - Move _update_all_versions inside each dry-run branch so output order matches actual execution - Switch to main before deleting the stale local branch in create_or_reset_branch --- lib/devtools/src/crewai_devtools/cli.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/devtools/src/crewai_devtools/cli.py b/lib/devtools/src/crewai_devtools/cli.py index b9aee6e1b..35cebf979 100644 --- a/lib/devtools/src/crewai_devtools/cli.py +++ b/lib/devtools/src/crewai_devtools/cli.py @@ -247,6 +247,14 @@ def create_or_reset_branch(branch: str, cwd: Path | None = None) -> None: remote_exists = False if local_exists: + current = run_command( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd + ).strip() + if current == branch: + console.print( + f"[yellow]![/yellow] Currently on {branch}, switching to main before delete" + ) + run_command(["git", "checkout", "main"], cwd=cwd) console.print(f"[yellow]![/yellow] Deleting local branch {branch}") run_command(["git", "branch", "-D", branch], cwd=cwd) @@ -2019,9 +2027,8 @@ def release( create_or_reset_branch(branch_name) console.print("[green]✓[/green] Branch created") - _update_all_versions(cwd, lib_dir, version, packages, dry_run) + _update_all_versions(cwd, lib_dir, version, packages, dry_run) - if not dry_run: console.print("\nCommitting changes...") run_command(["git", "add", "."]) run_command(["git", "commit", "-m", f"feat: bump versions to {version}"]) @@ -2051,6 +2058,7 @@ def release( _poll_pr_until_merged(branch_name, "bump PR") else: console.print(f"[dim][DRY RUN][/dim] Would create branch: {branch_name}") + _update_all_versions(cwd, lib_dir, version, packages, dry_run) console.print( f"[dim][DRY RUN][/dim] Would commit: feat: bump versions to {version}" ) From f8799095268faeb41df7fde0f3bfb12ec2216a79 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Sat, 18 Apr 2026 04:19:31 +0800 Subject: [PATCH 07/32] fix: emit task_started on fork resume, redesign checkpoint TUI Redesign checkpoint TUI with tabbed detail panel, collapsible agent rosters, keybinding actions, and human-readable timestamps. --- lib/crewai/src/crewai/cli/checkpoint_cli.py | 41 +- lib/crewai/src/crewai/cli/checkpoint_tui.py | 604 +++++++++++++------- lib/crewai/src/crewai/crew.py | 24 +- 3 files changed, 448 insertions(+), 221 deletions(-) diff --git a/lib/crewai/src/crewai/cli/checkpoint_cli.py b/lib/crewai/src/crewai/cli/checkpoint_cli.py index 9db783e0e..5e8572c62 100644 --- a/lib/crewai/src/crewai/cli/checkpoint_cli.py +++ b/lib/crewai/src/crewai/cli/checkpoint_cli.py @@ -106,17 +106,50 @@ def _parse_checkpoint_json(raw: str, source: str) -> dict[str, Any]: "name": entity.get("name"), "id": entity.get("id"), } + + raw_agents = entity.get("agents", []) + agents_by_id: dict[str, dict[str, Any]] = {} + parsed_agents: list[dict[str, Any]] = [] + for ag in raw_agents: + agent_info: dict[str, Any] = { + "id": ag.get("id", ""), + "role": ag.get("role", ""), + "goal": ag.get("goal", ""), + } + parsed_agents.append(agent_info) + if ag.get("id"): + agents_by_id[str(ag["id"])] = agent_info + if parsed_agents: + info["agents"] = parsed_agents + if tasks: info["tasks_completed"] = completed info["tasks_total"] = len(tasks) - info["tasks"] = [ - { + parsed_tasks: list[dict[str, Any]] = [] + for t in tasks: + task_info: dict[str, Any] = { "description": t.get("description", ""), "completed": t.get("output") is not None, "output": (t.get("output") or {}).get("raw", ""), } - for t in tasks - ] + task_agent = t.get("agent") + if isinstance(task_agent, dict): + task_info["agent_role"] = task_agent.get("role", "") + task_info["agent_id"] = task_agent.get("id", "") + elif isinstance(task_agent, str) and task_agent in agents_by_id: + task_info["agent_role"] = agents_by_id[task_agent].get("role", "") + task_info["agent_id"] = task_agent + parsed_tasks.append(task_info) + info["tasks"] = parsed_tasks + + if entity.get("entity_type") == "flow": + completed_methods = entity.get("checkpoint_completed_methods") + if completed_methods: + info["completed_methods"] = sorted(completed_methods) + state = entity.get("checkpoint_state") + if isinstance(state, dict): + info["flow_state"] = state + parsed_entities.append(info) inputs: dict[str, Any] = {} diff --git a/lib/crewai/src/crewai/cli/checkpoint_tui.py b/lib/crewai/src/crewai/cli/checkpoint_tui.py index 26791af23..0f945dd31 100644 --- a/lib/crewai/src/crewai/cli/checkpoint_tui.py +++ b/lib/crewai/src/crewai/cli/checkpoint_tui.py @@ -3,17 +3,20 @@ from __future__ import annotations from collections import defaultdict +from datetime import datetime from typing import Any, ClassVar, Literal from textual.app import App, ComposeResult from textual.binding import Binding from textual.containers import Horizontal, Vertical, VerticalScroll from textual.widgets import ( - Button, + Collapsible, Footer, Header, Input, Static, + TabPane, + TabbedContent, TextArea, Tree, ) @@ -32,6 +35,22 @@ _TERTIARY = "#ffffff" _DIM = "#888888" _BG_DARK = "#0d1117" _BG_PANEL = "#161b22" +_ACCENT = "#c9a227" +_SUCCESS = "#3fb950" +_PENDING = "#e3b341" + +_ENTITY_ICONS: dict[str, str] = { + "flow": "◆", + "crew": "●", + "agent": "◈", + "unknown": "○", +} +_ENTITY_COLORS: dict[str, str] = { + "flow": _ACCENT, + "crew": _SECONDARY, + "agent": _PRIMARY, + "unknown": _DIM, +} def _load_entries(location: str) -> list[dict[str, Any]]: @@ -40,8 +59,27 @@ def _load_entries(location: str) -> list[dict[str, Any]]: return _list_json(location) +def _human_ts(ts: str) -> str: + """Turn '2026-04-17 17:05:00' into a short relative label.""" + try: + dt = datetime.strptime(ts, "%Y-%m-%d %H:%M:%S") + except ValueError: + return ts + now = datetime.now() + delta = now.date() - dt.date() + hour = dt.hour % 12 or 12 + ampm = "am" if dt.hour < 12 else "pm" + time_str = f"{hour}:{dt.minute:02d}{ampm}" + if delta.days == 0: + return time_str + if delta.days == 1: + return f"yest {time_str}" + if delta.days < 7: + return f"{dt.strftime('%a').lower()} {time_str}" + return f"{dt.strftime('%b')} {dt.day}" + + def _short_id(name: str) -> str: - """Shorten a checkpoint name for tree display.""" if len(name) > 30: return name[:27] + "..." return name @@ -63,22 +101,22 @@ def _entry_id(entry: dict[str, Any]) -> str: return name -def _build_entity_header(ent: dict[str, Any]) -> str: - """Build rich text header for an entity (progress bar only).""" - lines: list[str] = [] - tasks = ent.get("tasks") - if isinstance(tasks, list): - completed = ent.get("tasks_completed", 0) - total = ent.get("tasks_total", 0) - pct = int(completed / total * 100) if total else 0 - bar_len = 20 - filled = int(bar_len * completed / total) if total else 0 - bar = f"[{_PRIMARY}]{'█' * filled}[/][{_DIM}]{'░' * (bar_len - filled)}[/]" - lines.append(f"{bar} {completed}/{total} tasks ({pct}%)") - return "\n".join(lines) +def _build_progress_bar(completed: int, total: int, width: int = 20) -> str: + if total == 0: + return f"[{_DIM}]{'░' * width}[/] 0/0" + pct = int(completed / total * 100) + filled = int(width * completed / total) + color = _SUCCESS if completed == total else _PRIMARY + bar = f"[{color}]{'█' * filled}[/][{_DIM}]{'░' * (width - filled)}[/]" + return f"{bar} {completed}/{total} ({pct}%)" + + +def _entity_icon(etype: str) -> str: + icon = _ENTITY_ICONS.get(etype, _ENTITY_ICONS["unknown"]) + color = _ENTITY_COLORS.get(etype, _DIM) + return f"[{color}]{icon}[/]" -# Return type: (location, action, inputs, task_output_overrides, entity_type) _TuiResult = ( tuple[ str, @@ -122,7 +160,7 @@ class CheckpointTUI(App[_TuiResult]): height: 1fr; }} #tree-panel {{ - width: 45%; + width: 40%; background: {_BG_PANEL}; border: round {_SECONDARY}; padding: 0 1; @@ -132,41 +170,81 @@ class CheckpointTUI(App[_TuiResult]): border: round {_PRIMARY}; }} #detail-container {{ - width: 55%; + width: 60%; height: 1fr; }} - #detail-scroll {{ - height: 1fr; - background: {_BG_PANEL}; - border: round {_SECONDARY}; - padding: 1 2; - scrollbar-color: {_PRIMARY}; - }} - #detail-scroll:focus-within {{ - border: round {_PRIMARY}; - }} - #detail-header {{ - margin-bottom: 1; - }} #status {{ height: 1; padding: 0 2; color: {_DIM}; }} - #inputs-section {{ - display: none; - height: auto; - max-height: 8; - padding: 0 1; + #detail-tabs {{ + height: 1fr; }} - #inputs-section.visible {{ - display: block; + TabbedContent > ContentSwitcher {{ + background: {_BG_PANEL}; + height: 1fr; }} - #inputs-label {{ - height: 1; + TabPane {{ + padding: 0; + }} + Tabs {{ + background: {_BG_DARK}; + }} + Tab {{ + background: {_BG_DARK}; color: {_DIM}; + padding: 0 2; + }} + Tab.-active {{ + background: {_BG_PANEL}; + color: {_PRIMARY}; + }} + Tab:hover {{ + color: {_TERTIARY}; + }} + Underline > .underline--bar {{ + color: {_SECONDARY}; + background: {_BG_DARK}; + }} + .tab-scroll {{ + background: {_BG_PANEL}; + height: 1fr; + padding: 1 2; + scrollbar-color: {_PRIMARY}; + }} + .section-header {{ + padding: 0 0 0 1; + margin: 1 0 0 0; + }} + .detail-line {{ + padding: 0 0 0 1; + }} + .task-label {{ padding: 0 1; }} + .task-output-editor {{ + height: auto; + max-height: 10; + margin: 0 1 1 3; + border: round {_DIM}; + }} + .task-output-editor:focus {{ + border: round {_PRIMARY}; + }} + Collapsible {{ + background: {_BG_PANEL}; + padding: 0; + margin: 0 0 1 1; + }} + CollapsibleTitle {{ + background: {_BG_DARK}; + color: {_TERTIARY}; + padding: 0 1; + }} + CollapsibleTitle:hover {{ + background: {_SECONDARY}; + }} .input-row {{ height: 3; padding: 0 1; @@ -180,55 +258,9 @@ class CheckpointTUI(App[_TuiResult]): .input-row Input {{ width: 1fr; }} - #no-inputs-label {{ - height: 1; + .empty-state {{ color: {_DIM}; - padding: 0 1; - }} - #action-buttons {{ - height: 3; - align: right middle; - padding: 0 1; - display: none; - }} - #action-buttons.visible {{ - display: block; - }} - #action-buttons Button {{ - margin: 0 0 0 1; - min-width: 10; - }} - #btn-resume {{ - background: {_SECONDARY}; - color: {_TERTIARY}; - }} - #btn-resume:hover {{ - background: {_PRIMARY}; - }} - #btn-fork {{ - background: {_PRIMARY}; - color: {_TERTIARY}; - }} - #btn-fork:hover {{ - background: {_SECONDARY}; - }} - .entity-title {{ - padding: 1 1 0 1; - }} - .entity-detail {{ - padding: 0 1; - }} - .task-output-editor {{ - height: auto; - max-height: 10; - margin: 0 1 1 1; - border: round {_DIM}; - }} - .task-output-editor:focus {{ - border: round {_PRIMARY}; - }} - .task-label {{ - padding: 0 1; + padding: 1; }} Tree {{ background: {_BG_PANEL}; @@ -242,6 +274,8 @@ class CheckpointTUI(App[_TuiResult]): BINDINGS: ClassVar[list[Binding | tuple[str, str] | tuple[str, str, str]]] = [ ("q", "quit", "Quit"), ("r", "refresh", "Refresh"), + ("e", "resume", "Resume"), + ("f", "fork", "Fork"), ] def __init__(self, location: str = "./.checkpoints") -> None: @@ -256,27 +290,49 @@ class CheckpointTUI(App[_TuiResult]): yield Header(show_clock=False) with Horizontal(id="main-layout"): tree: Tree[dict[str, Any]] = Tree("Checkpoints", id="tree-panel") - tree.show_root = True + tree.show_root = False tree.guide_depth = 3 yield tree with Vertical(id="detail-container"): yield Static("", id="status") - with VerticalScroll(id="detail-scroll"): - yield Static( - f"[{_DIM}]Select a checkpoint from the tree[/]", # noqa: S608 - id="detail-header", - ) - with Vertical(id="inputs-section"): - yield Static("Inputs", id="inputs-label") - with Horizontal(id="action-buttons"): - yield Button("Resume", id="btn-resume") - yield Button("Fork", id="btn-fork") + with TabbedContent(id="detail-tabs"): + with TabPane("Overview", id="tab-overview"): + with VerticalScroll(classes="tab-scroll"): + yield Static( + f"[{_DIM}]Select a checkpoint from the tree[/]", # noqa: S608 + id="overview-empty", + ) + with TabPane("Tasks", id="tab-tasks"): + with VerticalScroll(classes="tab-scroll"): + yield Static( + f"[{_DIM}]Select a checkpoint to view tasks[/]", + id="tasks-empty", + ) + with TabPane("Inputs", id="tab-inputs"): + with VerticalScroll(classes="tab-scroll"): + yield Static( + f"[{_DIM}]Select a checkpoint to view inputs[/]", + id="inputs-empty", + ) yield Footer() async def on_mount(self) -> None: self._refresh_tree() self.query_one("#tree-panel", Tree).root.expand() + # ── Tree building ────────────────────────────────────────────── + + @staticmethod + def _top_level_entity(entry: dict[str, Any]) -> tuple[str, str]: + etype, ename = "unknown", "" + for ent in entry.get("entities", []): + t = ent.get("type", "unknown") + if t == "flow": + return "flow", ent.get("name") or "" + if t == "crew" and etype != "crew": + etype, ename = "crew", ent.get("name") or "" + return etype, ename + def _refresh_tree(self) -> None: self._entries = _load_entries(self._location) self._selected_entry = None @@ -285,45 +341,57 @@ class CheckpointTUI(App[_TuiResult]): tree.clear() if not self._entries: - self.query_one("#detail-header", Static).update( - f"[{_DIM}]No checkpoints in {self._location}[/]" - ) - self.query_one("#status", Static).update("") self.sub_title = self._location + self.query_one("#status", Static).update("") return - # Group by branch - branches: dict[str, list[dict[str, Any]]] = defaultdict(list) + grouped: dict[tuple[str, str], dict[str, list[dict[str, Any]]]] = defaultdict( + lambda: defaultdict(list) + ) for entry in self._entries: + key = self._top_level_entity(entry) branch = entry.get("branch", "main") - branches[branch].append(entry) - - # Index checkpoint names to tree nodes so forks can attach - node_by_name: dict[str, Any] = {} + grouped[key][branch].append(entry) def _make_label(e: dict[str, Any]) -> str: - name = e.get("name", "") ts = e.get("ts") or "" trigger = e.get("trigger") or "" - parts = [f"[bold]{_short_id(name)}[/]"] - if ts: - time_part = ts.split(" ")[-1] if " " in ts else ts + time_part = ts.split(" ")[-1] if " " in ts else ts + + total_c, total_t = 0, 0 + for ent in e.get("entities", []): + c = ent.get("tasks_completed") + t = ent.get("tasks_total") + if c is not None and t is not None: + total_c += c + total_t += t + + parts: list[str] = [] + if time_part: parts.append(f"[{_DIM}]{time_part}[/]") if trigger: parts.append(f"[{_PRIMARY}]{trigger}[/]") - return " ".join(parts) + if total_t: + display_c = total_c + if trigger == "task_started" and total_c < total_t: + display_c = total_c + 1 + color = _SUCCESS if total_c == total_t else _DIM + parts.append(f"[{color}]{display_c}/{total_t}[/]") + return " ".join(parts) if parts else _short_id(e.get("name", "")) fork_parents: set[str] = set() - for branch_name, entries in branches.items(): - if branch_name == "main" or not entries: - continue - oldest = min(entries, key=lambda e: str(e.get("name", ""))) - first_parent = oldest.get("parent_id") - if first_parent: - fork_parents.add(str(first_parent)) + for branches in grouped.values(): + for branch_name, entries in branches.items(): + if branch_name == "main" or not entries: + continue + oldest = min(entries, key=lambda e: str(e.get("name", ""))) + first_parent = oldest.get("parent_id") + if first_parent: + fork_parents.add(str(first_parent)) + + node_by_name: dict[str, Any] = {} def _add_checkpoint(parent_node: Any, e: dict[str, Any]) -> None: - """Add a checkpoint node — expandable only if a fork attaches to it.""" cp_id = _entry_id(e) if cp_id in fork_parents: node = parent_node.add( @@ -333,67 +401,97 @@ class CheckpointTUI(App[_TuiResult]): node = parent_node.add_leaf(_make_label(e), data=e) node_by_name[cp_id] = node - if "main" in branches: - for entry in reversed(branches["main"]): - _add_checkpoint(tree.root, entry) + type_order = {"flow": 0, "crew": 1} + sorted_keys = sorted( + grouped.keys(), key=lambda k: (type_order.get(k[0], 9), k[1]) + ) + + for etype, ename in sorted_keys: + branches = grouped[(etype, ename)] + icon = _entity_icon(etype) + color = _ENTITY_COLORS.get(etype, _DIM) + total = sum(len(v) for v in branches.values()) + + label_parts = [f"{icon} [bold {color}]{etype.upper()}[/]"] + if ename: + label_parts.append(f"[bold]{ename}[/]") + label_parts.append(f"[{_DIM}]({total})[/]") + all_entries = [e for bl in branches.values() for e in bl] + timestamps = [str(e.get("ts", "")) for e in all_entries if e.get("ts")] + if timestamps: + latest = max(timestamps) + label_parts.append(f"[{_DIM}]{_human_ts(latest)}[/]") + entity_label = " ".join(label_parts) + entity_node = tree.root.add(entity_label, expand=True) + + if "main" in branches: + for entry in reversed(branches["main"]): + _add_checkpoint(entity_node, entry) + + fork_branches = [ + (name, sorted(entries, key=lambda e: str(e.get("name", "")))) + for name, entries in branches.items() + if name != "main" + ] + remaining = fork_branches + max_passes = len(remaining) + 1 + while remaining and max_passes > 0: + max_passes -= 1 + deferred = [] + made_progress = False + for branch_name, entries in remaining: + first_parent = entries[0].get("parent_id") if entries else None + if first_parent and str(first_parent) not in node_by_name: + deferred.append((branch_name, entries)) + continue + attach_to: Any = entity_node + if first_parent: + attach_to = node_by_name.get(str(first_parent), entity_node) + branch_label = ( + f"[bold {_SECONDARY}]{branch_name}[/] " + f"[{_DIM}]({len(entries)})[/]" + ) + branch_node = attach_to.add(branch_label, expand=False) + for entry in entries: + _add_checkpoint(branch_node, entry) + made_progress = True + remaining = deferred + if not made_progress: + break - fork_branches = [ - (name, sorted(entries, key=lambda e: str(e.get("name", "")))) - for name, entries in branches.items() - if name != "main" - ] - remaining = fork_branches - max_passes = len(remaining) + 1 - while remaining and max_passes > 0: - max_passes -= 1 - deferred = [] - made_progress = False for branch_name, entries in remaining: - first_parent = entries[0].get("parent_id") if entries else None - if first_parent and str(first_parent) not in node_by_name: - deferred.append((branch_name, entries)) - continue - attach_to: Any = tree.root - if first_parent: - attach_to = node_by_name.get(str(first_parent), tree.root) branch_label = ( - f"[bold {_SECONDARY}]{branch_name}[/] [{_DIM}]({len(entries)})[/]" + f"[bold {_SECONDARY}]{branch_name}[/] " + f"[{_DIM}]({len(entries)})[/] [{_DIM}](orphaned)[/]" ) - branch_node = attach_to.add(branch_label, expand=False) + branch_node = entity_node.add(branch_label, expand=False) for entry in entries: _add_checkpoint(branch_node, entry) - made_progress = True - remaining = deferred - if not made_progress: - break - - for branch_name, entries in remaining: - branch_label = ( - f"[bold {_SECONDARY}]{branch_name}[/] " - f"[{_DIM}]({len(entries)})[/] [{_DIM}](orphaned)[/]" - ) - branch_node = tree.root.add(branch_label, expand=False) - for entry in entries: - _add_checkpoint(branch_node, entry) count = len(self._entries) storage = "SQLite" if _is_sqlite(self._location) else "JSON" self.sub_title = self._location self.query_one("#status", Static).update(f" {count} checkpoint(s) | {storage}") - async def _show_detail(self, entry: dict[str, Any]) -> None: - """Update the detail panel for a checkpoint entry.""" - self._selected_entry = entry - self.query_one("#action-buttons").add_class("visible") + # ── Detail panel ─────────────────────────────────────────────── - detail_scroll = self.query_one("#detail-scroll", VerticalScroll) - - # Remove all dynamic children except the header — await so IDs are freed - to_remove = [c for c in detail_scroll.children if c.id != "detail-header"] - for child in to_remove: + async def _clear_scroll(self, tab_id: str) -> VerticalScroll: + tab = self.query_one(f"#{tab_id}", TabPane) + scroll = tab.query_one(VerticalScroll) + for child in list(scroll.children): await child.remove() + return scroll + + async def _show_detail(self, entry: dict[str, Any]) -> None: + self._selected_entry = entry + + await self._render_overview(entry) + await self._render_tasks(entry) + await self._render_inputs(entry.get("inputs", {})) + + async def _render_overview(self, entry: dict[str, Any]) -> None: + scroll = await self._clear_scroll("tab-overview") - # Header name = entry.get("name", "") ts = entry.get("ts") or "unknown" trigger = entry.get("trigger") or "" @@ -414,42 +512,115 @@ class CheckpointTUI(App[_TuiResult]): header_lines.append(f" [bold]Branch[/] [{_SECONDARY}]{branch}[/]") if parent_id: header_lines.append(f" [bold]Parent[/] [{_DIM}]{parent_id}[/]") - if "path" in entry: - header_lines.append(f" [bold]Path[/] [{_DIM}]{entry['path']}[/]") - if "db" in entry: - header_lines.append(f" [bold]Database[/] [{_DIM}]{entry['db']}[/]") - self.query_one("#detail-header", Static).update("\n".join(header_lines)) + await scroll.mount(Static("\n".join(header_lines))) + + for ent in entry.get("entities", []): + etype = ent.get("type", "unknown") + ename = ent.get("name", "unnamed") + icon = _entity_icon(etype) + color = _ENTITY_COLORS.get(etype, _DIM) + + eid = str(ent.get("id", ""))[:8] + entity_title = ( + f"\n{icon} [bold {color}]{etype.upper()}[/] [bold]{ename}[/]" + ) + if eid: + entity_title += f" [{_DIM}]{eid}…[/]" + await scroll.mount(Static(entity_title, classes="section-header")) + await scroll.mount(Static(f"[{_DIM}]{'─' * 46}[/]", classes="detail-line")) + + if etype == "flow": + methods = ent.get("completed_methods", []) + if methods: + method_list = ", ".join(f"[{_SUCCESS}]{m}[/]" for m in methods) + await scroll.mount( + Static( + f" [bold]Methods[/] {method_list}", + classes="detail-line", + ) + ) + flow_state = ent.get("flow_state") + if isinstance(flow_state, dict) and flow_state: + state_parts: list[str] = [] + for k, v in list(flow_state.items())[:5]: + sv = str(v) + if len(sv) > 40: + sv = sv[:37] + "..." + state_parts.append(f"[{_DIM}]{k}[/]={sv}") + await scroll.mount( + Static( + f" [bold]State[/] {', '.join(state_parts)}", + classes="detail-line", + ) + ) + + agents = ent.get("agents", []) + if agents: + agent_lines: list[Static] = [] + for ag in agents: + role = ag.get("role", "unnamed") + goal = ag.get("goal", "") + if len(goal) > 60: + goal = goal[:57] + "..." + agent_line = f" {_entity_icon('agent')} [bold]{role}[/]" + if goal: + agent_line += f"\n [{_DIM}]{goal}[/]" + agent_lines.append(Static(agent_line)) + + collapsible = Collapsible( + *agent_lines, + title=f"Agents ({len(agents)})", + collapsed=len(agents) > 3, + ) + await scroll.mount(collapsible) + + async def _render_tasks(self, entry: dict[str, Any]) -> None: + scroll = await self._clear_scroll("tab-tasks") - # Entity details and editable task outputs — mounted flat for scrolling self._task_output_ids = [] flat_task_idx = 0 + has_tasks = False + for ent_idx, ent in enumerate(entry.get("entities", [])): etype = ent.get("type", "unknown") ename = ent.get("name", "unnamed") - completed = ent.get("tasks_completed") - total = ent.get("tasks_total") - entity_title = f"[bold {_SECONDARY}]{etype}: {ename}[/]" - if completed is not None and total is not None: - entity_title += f" [{_DIM}]{completed}/{total} tasks[/]" - await detail_scroll.mount(Static(entity_title, classes="entity-title")) - await detail_scroll.mount( - Static(_build_entity_header(ent), classes="entity-detail") - ) + icon = _entity_icon(etype) + color = _ENTITY_COLORS.get(etype, _DIM) tasks = ent.get("tasks", []) + if not tasks: + continue + has_tasks = True + + completed = ent.get("tasks_completed", 0) + total = ent.get("tasks_total", 0) + + await scroll.mount( + Static( + f"{icon} [bold {color}]{ename}[/] " + f"{_build_progress_bar(completed, total, width=16)}", + classes="section-header", + ) + ) + for i, task in enumerate(tasks): desc = str(task.get("description", "")) - if len(desc) > 55: - desc = desc[:52] + "..." + if len(desc) > 50: + desc = desc[:47] + "..." + agent_role = task.get("agent_role", "") + if task.get("completed"): - icon = "[green]✓[/]" - await detail_scroll.mount( - Static(f" {icon} {i + 1}. {desc}", classes="task-label") - ) + status_icon = f"[{_SUCCESS}]✓[/]" + task_line = f" {status_icon} {i + 1}. {desc}" + if agent_role: + task_line += ( + f" [{_DIM}]→ {_entity_icon('agent')} {agent_role}[/]" + ) + await scroll.mount(Static(task_line, classes="task-label")) output_text = task.get("output", "") editor_id = f"task-output-{ent_idx}-{i}" - await detail_scroll.mount( + await scroll.mount( TextArea( str(output_text), classes="task-output-editor", @@ -460,28 +631,25 @@ class CheckpointTUI(App[_TuiResult]): (flat_task_idx, editor_id, str(output_text)) ) else: - icon = "[yellow]○[/]" - await detail_scroll.mount( - Static(f" {icon} {i + 1}. {desc}", classes="task-label") - ) + status_icon = f"[{_PENDING}]○[/]" + task_line = f" {status_icon} {i + 1}. {desc}" + if agent_role: + task_line += ( + f" [{_DIM}]→ {_entity_icon('agent')} {agent_role}[/]" + ) + await scroll.mount(Static(task_line, classes="task-label")) flat_task_idx += 1 - # Build input fields - await self._build_input_fields(entry.get("inputs", {})) + if not has_tasks: + await scroll.mount(Static(f"[{_DIM}]No tasks[/]", classes="empty-state")) - async def _build_input_fields(self, inputs: dict[str, Any]) -> None: - """Rebuild the inputs section with one field per input key.""" - section = self.query_one("#inputs-section") - - # Remove old dynamic children — await so IDs are freed - for widget in list(section.query(".input-row, .no-inputs")): - await widget.remove() + async def _render_inputs(self, inputs: dict[str, Any]) -> None: + scroll = await self._clear_scroll("tab-inputs") self._input_keys = [] if not inputs: - await section.mount(Static(f"[{_DIM}]No inputs[/]", classes="no-inputs")) - section.add_class("visible") + await scroll.mount(Static(f"[{_DIM}]No inputs[/]", classes="empty-state")) return for key, value in inputs.items(): @@ -491,12 +659,11 @@ class CheckpointTUI(App[_TuiResult]): row.compose_add_child( Input(value=str(value), placeholder=key, id=f"input-{key}") ) - await section.mount(row) + await scroll.mount(row) - section.add_class("visible") + # ── Data collection ──────────────────────────────────────────── def _collect_inputs(self) -> dict[str, Any] | None: - """Collect current values from input fields.""" if not self._input_keys: return None result: dict[str, Any] = {} @@ -506,7 +673,6 @@ class CheckpointTUI(App[_TuiResult]): return result def _collect_task_overrides(self) -> dict[int, str] | None: - """Collect edited task outputs. Returns only changed values.""" if not self._task_output_ids or self._selected_entry is None: return None overrides: dict[int, str] = {} @@ -517,37 +683,43 @@ class CheckpointTUI(App[_TuiResult]): return overrides or None def _detect_entity_type(self, entry: dict[str, Any]) -> Literal["crew", "flow"]: - """Infer the top-level entity type from checkpoint entities.""" for ent in entry.get("entities", []): if ent.get("type") == "flow": return "flow" return "crew" def _resolve_location(self, entry: dict[str, Any]) -> str: - """Get the restore location string for a checkpoint entry.""" if "path" in entry: return str(entry["path"]) if _is_sqlite(self._location): return f"{self._location}#{entry['name']}" return str(entry.get("name", "")) + # ── Events ───────────────────────────────────────────────────── + async def on_tree_node_highlighted( self, event: Tree.NodeHighlighted[dict[str, Any]] ) -> None: if event.node.data is not None: await self._show_detail(event.node.data) - def on_button_pressed(self, event: Button.Pressed) -> None: + def _exit_with_action(self, action: str) -> None: if self._selected_entry is None: + self.notify("No checkpoint selected", severity="warning") return inputs = self._collect_inputs() overrides = self._collect_task_overrides() loc = self._resolve_location(self._selected_entry) etype = self._detect_entity_type(self._selected_entry) - if event.button.id == "btn-resume": - self.exit((loc, "resume", inputs, overrides, etype)) - elif event.button.id == "btn-fork": - self.exit((loc, "fork", inputs, overrides, etype)) + name = self._selected_entry.get("name", "")[:30] + self.notify(f"{action.title()}: {name}") + self.exit((loc, action, inputs, overrides, etype)) + + def action_resume(self) -> None: + self._exit_with_action("resume") + + def action_fork(self) -> None: + self._exit_with_action("fork") def action_refresh(self) -> None: self._refresh_tree() diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index de9a8f73d..7631a4c2b 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -419,10 +419,32 @@ class Crew(FlowTrackable, BaseModel): def _restore_runtime(self) -> None: """Re-create runtime objects after restoring from a checkpoint.""" + from crewai.events.event_bus import crewai_event_bus + + started_task_ids: set[str] = set() + state = crewai_event_bus._runtime_state + if state is not None: + for node in state.event_record.nodes.values(): + if node.event.type == "task_started" and node.event.task_id: + started_task_ids.add(node.event.task_id) + + resuming_task_agent_roles: set[str] = set() + for task in self.tasks: + if ( + task.output is None + and task.agent is not None + and str(task.id) in started_task_ids + ): + resuming_task_agent_roles.add(task.agent.role) + for agent in self.agents: agent.crew = self executor = agent.agent_executor - if executor and executor.messages: + if ( + executor + and executor.messages + and agent.role in resuming_task_agent_roles + ): executor.crew = self executor.agent = agent executor._resuming = True From 0b120fac902363670976a036aa72693ba0018aa7 Mon Sep 17 00:00:00 2001 From: alex-clawd Date: Sun, 19 Apr 2026 21:27:12 -0700 Subject: [PATCH 08/32] fix: use future dates in checkpoint prune tests to prevent time-dependent failures (#5543) The test_older_than tests in both JSON and SQLite prune suites used hardcoded 2026-04-17 timestamps for the 'new' checkpoint. Once that date passes, the checkpoint is older than 1 day and gets pruned along with the 'old' one, causing assert count >= 1 to fail (count=0). Use 2099-01-01 for the 'new' checkpoint so tests remain stable. Co-authored-by: Joao Moura --- lib/crewai/tests/test_checkpoint_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crewai/tests/test_checkpoint_cli.py b/lib/crewai/tests/test_checkpoint_cli.py index 38e105cce..aa1188336 100644 --- a/lib/crewai/tests/test_checkpoint_cli.py +++ b/lib/crewai/tests/test_checkpoint_cli.py @@ -292,7 +292,7 @@ class TestPruneJson: d, name="20250101T000000_old01111_p-none.json" ) os.utime(old_path, (0, 0)) - _write_json_checkpoint(d, name="20260417T000000_new01111_p-none.json") + _write_json_checkpoint(d, name="20990101T000000_new01111_p-none.json") deleted = _prune_json(d, keep=None, older_than=timedelta(days=1)) assert deleted == 1 @@ -330,7 +330,7 @@ class TestPruneSqlite: with tempfile.TemporaryDirectory() as d: db_path = os.path.join(d, "test.db") _create_sqlite_checkpoint(db_path, "20200101T000000_old01111") - _create_sqlite_checkpoint(db_path, "20260417T000000_new01111") + _create_sqlite_checkpoint(db_path, "20990101T000000_new01111") deleted = _prune_sqlite(db_path, keep=None, older_than=timedelta(days=1)) assert deleted >= 1 with sqlite3.connect(db_path) as conn: From ae242c507d53560bd4ba780869fe3c5655a01661 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Mon, 20 Apr 2026 22:47:37 +0800 Subject: [PATCH 09/32] feat: add checkpoint and fork support to standalone agents Add fork classmethod, _restore_runtime, and _restore_event_scope to BaseAgent. Fix from_checkpoint to set runtime state on the event bus and restore event scopes. Store kickoff event ID across checkpoints to skip re-emission on resume. Handle agent entity type in checkpoint CLI and TUI. --- lib/crewai/src/crewai/agent/core.py | 89 +++++++++++++------ .../crewai/agents/agent_builder/base_agent.py | 75 ++++++++++++++-- lib/crewai/src/crewai/cli/checkpoint_cli.py | 7 ++ lib/crewai/src/crewai/cli/checkpoint_tui.py | 23 ++++- lib/crewai/src/crewai/state/runtime.py | 3 + lib/crewai/tests/test_checkpoint.py | 72 +++++++++++++++ 6 files changed, 233 insertions(+), 36 deletions(-) diff --git a/lib/crewai/src/crewai/agent/core.py b/lib/crewai/src/crewai/agent/core.py index b83752344..74a3e85de 100644 --- a/lib/crewai/src/crewai/agent/core.py +++ b/lib/crewai/src/crewai/agent/core.py @@ -29,7 +29,7 @@ from pydantic import ( model_validator, ) from pydantic.functional_serializers import PlainSerializer -from typing_extensions import Self +from typing_extensions import Self, TypeIs from crewai.agent.planning_config import PlanningConfig from crewai.agent.utils import ( @@ -133,6 +133,13 @@ _EXECUTOR_CLASS_MAP: dict[str, type] = { } +def _is_resuming_agent_executor( + executor: CrewAgentExecutor | AgentExecutor | None, +) -> TypeIs[AgentExecutor]: + """Type guard: True when the executor is resuming from a checkpoint.""" + return isinstance(executor, AgentExecutor) and executor._resuming + + def _validate_executor_class(value: Any) -> Any: if isinstance(value, str): cls = _EXECUTOR_CLASS_MAP.get(value) @@ -1366,24 +1373,42 @@ class Agent(BaseAgent): prompt, stop_words, rpm_limit_fn = self._build_execution_prompt(raw_tools) - executor = AgentExecutor( - llm=cast(BaseLLM, self.llm), - agent=self, - prompt=prompt, - max_iter=self.max_iter, - tools=parsed_tools, - tools_names=get_tool_names(parsed_tools), - stop_words=stop_words, - tools_description=render_text_description_and_args(parsed_tools), - tools_handler=self.tools_handler, - original_tools=raw_tools, - step_callback=self.step_callback, - function_calling_llm=self.function_calling_llm, - respect_context_window=self.respect_context_window, - request_within_rpm_limit=rpm_limit_fn, - callbacks=[TokenCalcHandler(self._token_process)], - response_model=response_format, - ) + if _is_resuming_agent_executor(self.agent_executor): + executor = self.agent_executor + executor.tools = parsed_tools + executor.tools_names = get_tool_names(parsed_tools) + executor.tools_description = render_text_description_and_args(parsed_tools) + executor.original_tools = raw_tools + executor.prompt = prompt + executor.response_model = response_format + executor.stop_words = stop_words + executor.tools_handler = self.tools_handler + executor.step_callback = self.step_callback + executor.function_calling_llm = cast( + BaseLLM | None, self.function_calling_llm + ) + executor.respect_context_window = self.respect_context_window + executor.request_within_rpm_limit = rpm_limit_fn + executor.callbacks = [TokenCalcHandler(self._token_process)] + else: + executor = AgentExecutor( + llm=cast(BaseLLM, self.llm), + agent=self, + prompt=prompt, + max_iter=self.max_iter, + tools=parsed_tools, + tools_names=get_tool_names(parsed_tools), + stop_words=stop_words, + tools_description=render_text_description_and_args(parsed_tools), + tools_handler=self.tools_handler, + original_tools=raw_tools, + step_callback=self.step_callback, + function_calling_llm=self.function_calling_llm, + respect_context_window=self.respect_context_window, + request_within_rpm_limit=rpm_limit_fn, + callbacks=[TokenCalcHandler(self._token_process)], + response_model=response_format, + ) all_files: dict[str, Any] = {} if isinstance(messages, str): @@ -1504,14 +1529,17 @@ class Agent(BaseAgent): ) try: - crewai_event_bus.emit( - self, - event=LiteAgentExecutionStartedEvent( + if self.checkpoint_kickoff_event_id is not None: + self._kickoff_event_id = self.checkpoint_kickoff_event_id + self.checkpoint_kickoff_event_id = None + else: + started_event = LiteAgentExecutionStartedEvent( agent_info=agent_info, tools=parsed_tools, messages=messages, - ), - ) + ) + crewai_event_bus.emit(self, event=started_event) + self._kickoff_event_id = started_event.event_id output = self._execute_and_build_output(executor, inputs, response_format) return self._finalize_kickoff( @@ -1808,14 +1836,17 @@ class Agent(BaseAgent): ) try: - crewai_event_bus.emit( - self, - event=LiteAgentExecutionStartedEvent( + if self.checkpoint_kickoff_event_id is not None: + self._kickoff_event_id = self.checkpoint_kickoff_event_id + self.checkpoint_kickoff_event_id = None + else: + started_event = LiteAgentExecutionStartedEvent( agent_info=agent_info, tools=parsed_tools, messages=messages, - ), - ) + ) + crewai_event_bus.emit(self, event=started_event) + self._kickoff_event_id = started_event.event_id output = await self._execute_and_build_output_async( executor, inputs, response_format diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py index a00f9b49f..74d30e0b2 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py @@ -28,6 +28,9 @@ from crewai.agents.agent_builder.base_agent_executor import BaseAgentExecutor from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.tools_handler import ToolsHandler +from crewai.events.base_events import set_emission_counter +from crewai.events.event_bus import crewai_event_bus +from crewai.events.event_context import restore_event_scope, set_last_event_id from crewai.knowledge.knowledge import Knowledge from crewai.knowledge.knowledge_config import KnowledgeConfig from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource @@ -51,6 +54,7 @@ from crewai.utilities.string_utils import interpolate_only if TYPE_CHECKING: from crewai.context import ExecutionContext from crewai.crew import Crew + from crewai.state.runtime import RuntimeState def _validate_crew_ref(value: Any) -> Any: @@ -219,6 +223,7 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta): _original_goal: str | None = PrivateAttr(default=None) _original_backstory: str | None = PrivateAttr(default=None) _token_process: TokenProcess = PrivateAttr(default_factory=TokenProcess) + _kickoff_event_id: str | None = PrivateAttr(default=None) id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True) role: str = Field(description="Role of the agent") goal: str = Field(description="Objective of the agent") @@ -335,30 +340,90 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta): min_length=1, ) execution_context: ExecutionContext | None = Field(default=None) + checkpoint_kickoff_event_id: str | None = Field(default=None) @classmethod def from_checkpoint(cls, config: CheckpointConfig) -> Self: - """Restore an Agent from a checkpoint. + """Restore an Agent from a checkpoint, ready to resume via kickoff(). Args: - config: Checkpoint configuration with ``restore_from`` set. + config: Checkpoint configuration with ``restore_from`` set to + the path of the checkpoint to load. + + Returns: + An Agent instance. Call kickoff() to resume execution. """ from crewai.context import apply_execution_context from crewai.state.runtime import RuntimeState state = RuntimeState.from_checkpoint(config, context={"from_checkpoint": True}) + crewai_event_bus.set_runtime_state(state) for entity in state.root: if isinstance(entity, cls): if entity.execution_context is not None: apply_execution_context(entity.execution_context) - if entity.agent_executor is not None: - entity.agent_executor.agent = entity - entity.agent_executor._resuming = True + entity._restore_runtime(state) return entity raise ValueError( f"No {cls.__name__} found in checkpoint: {config.restore_from}" ) + @classmethod + def fork(cls, config: CheckpointConfig, branch: str | None = None) -> Self: + """Fork an Agent from a checkpoint, creating a new execution branch. + + Args: + config: Checkpoint configuration with ``restore_from`` set. + branch: Branch label for the fork. Auto-generated if not provided. + + Returns: + An Agent instance on the new branch. Call kickoff() to run. + """ + agent = cls.from_checkpoint(config) + state = crewai_event_bus._runtime_state + if state is None: + raise RuntimeError("Cannot fork: no runtime state on the event bus.") + state.fork(branch) + return agent + + def _restore_runtime(self, state: RuntimeState) -> None: + """Re-create runtime objects after restoring from a checkpoint. + + Args: + state: The RuntimeState containing the event record. + """ + if self.agent_executor is not None: + self.agent_executor.agent = self + self.agent_executor._resuming = True + if self.checkpoint_kickoff_event_id is not None: + self._kickoff_event_id = self.checkpoint_kickoff_event_id + self._restore_event_scope(state) + + def _restore_event_scope(self, state: RuntimeState) -> None: + """Rebuild the event scope stack from the checkpoint's event record. + + Args: + state: The RuntimeState containing the event record. + """ + stack: list[tuple[str, str]] = [] + kickoff_id = self._kickoff_event_id + if kickoff_id: + stack.append((kickoff_id, "lite_agent_execution_started")) + + restore_event_scope(tuple(stack)) + + last_event_id: str | None = None + max_seq = 0 + for node in state.event_record.nodes.values(): + seq = node.event.emission_sequence or 0 + if seq > max_seq: + max_seq = seq + last_event_id = node.event.event_id + if last_event_id is not None: + set_last_event_id(last_event_id) + if max_seq > 0: + set_emission_counter(max_seq) + @model_validator(mode="before") @classmethod def process_model_config(cls, values: Any) -> dict[str, Any]: diff --git a/lib/crewai/src/crewai/cli/checkpoint_cli.py b/lib/crewai/src/crewai/cli/checkpoint_cli.py index 5e8572c62..0b3139d7d 100644 --- a/lib/crewai/src/crewai/cli/checkpoint_cli.py +++ b/lib/crewai/src/crewai/cli/checkpoint_cli.py @@ -472,6 +472,8 @@ def _entity_type_from_meta(meta: dict[str, Any]) -> str: for ent in meta.get("entities", []): if ent.get("type") == "flow": return "flow" + if ent.get("type") == "agent": + return "agent" return "crew" @@ -505,6 +507,11 @@ def resume_checkpoint(location: str, checkpoint_id: str | None) -> None: flow = Flow.from_checkpoint(config) result = asyncio.run(flow.kickoff_async(inputs=inputs)) + elif entity_type == "agent": + from crewai.agent import Agent + + agent = Agent.from_checkpoint(config) + result = asyncio.run(agent.akickoff(messages="Resume execution.")) else: from crewai.crew import Crew diff --git a/lib/crewai/src/crewai/cli/checkpoint_tui.py b/lib/crewai/src/crewai/cli/checkpoint_tui.py index 0f945dd31..7cc1d6867 100644 --- a/lib/crewai/src/crewai/cli/checkpoint_tui.py +++ b/lib/crewai/src/crewai/cli/checkpoint_tui.py @@ -123,7 +123,7 @@ _TuiResult = ( str, dict[str, Any] | None, dict[int, str] | None, - Literal["crew", "flow"], + Literal["crew", "flow", "agent"], ] | None ) @@ -682,10 +682,14 @@ class CheckpointTUI(App[_TuiResult]): overrides[task_idx] = editor.text return overrides or None - def _detect_entity_type(self, entry: dict[str, Any]) -> Literal["crew", "flow"]: + def _detect_entity_type( + self, entry: dict[str, Any] + ) -> Literal["crew", "flow", "agent"]: for ent in entry.get("entities", []): if ent.get("type") == "flow": return "flow" + if ent.get("type") == "agent": + return "agent" return "crew" def _resolve_location(self, entry: dict[str, Any]) -> str: @@ -829,6 +833,21 @@ async def _run_checkpoint_tui_async(location: str) -> None: click.echo(f"\nResult: {getattr(result, 'raw', result)}") return + if entity_type == "agent": + from crewai.agent import Agent + + if action == "fork": + click.echo(f"\nForking agent from: {selected}\n") + agent = Agent.fork(config) + else: + click.echo(f"\nResuming agent from: {selected}\n") + agent = Agent.from_checkpoint(config) + + click.echo() + result = await agent.akickoff(messages="Resume execution.") + click.echo(f"\nResult: {getattr(result, 'raw', result)}") + return + from crewai.crew import Crew if action == "fork": diff --git a/lib/crewai/src/crewai/state/runtime.py b/lib/crewai/src/crewai/state/runtime.py index daae0620e..3243d4c19 100644 --- a/lib/crewai/src/crewai/state/runtime.py +++ b/lib/crewai/src/crewai/state/runtime.py @@ -44,9 +44,12 @@ def _sync_checkpoint_fields(entity: object) -> None: entity: The entity whose private runtime attributes will be copied into its public checkpoint fields. """ + from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.crew import Crew from crewai.flow.flow import Flow + if isinstance(entity, BaseAgent): + entity.checkpoint_kickoff_event_id = entity._kickoff_event_id if isinstance(entity, Flow): entity.checkpoint_completed_methods = ( set(entity._completed_methods) if entity._completed_methods else None diff --git a/lib/crewai/tests/test_checkpoint.py b/lib/crewai/tests/test_checkpoint.py index b1ad9e2df..d92a24803 100644 --- a/lib/crewai/tests/test_checkpoint.py +++ b/lib/crewai/tests/test_checkpoint.py @@ -562,3 +562,75 @@ class TestKickoffFromCheckpoint: ) assert mock_restored.checkpoint.restore_from is None assert result == "flow_result" + + +# ---------- Agent checkpoint/fork ---------- + + +class TestAgentCheckpoint: + def _make_agent_state(self) -> RuntimeState: + agent = Agent(role="r", goal="g", backstory="b", llm="gpt-4o-mini") + return RuntimeState(root=[agent]) + + def test_agent_from_checkpoint_sets_runtime_state(self) -> None: + state = self._make_agent_state() + state._provider = JsonProvider() + with tempfile.TemporaryDirectory() as d: + loc = state.checkpoint(d) + cfg = CheckpointConfig(restore_from=loc) + + from crewai.events.event_bus import crewai_event_bus + + crewai_event_bus._runtime_state = None + Agent.from_checkpoint(cfg) + assert crewai_event_bus._runtime_state is not None + + def test_agent_fork_sets_branch(self) -> None: + state = self._make_agent_state() + state._provider = JsonProvider() + with tempfile.TemporaryDirectory() as d: + loc = state.checkpoint(d) + cfg = CheckpointConfig(restore_from=loc) + + from crewai.events.event_bus import crewai_event_bus + + Agent.fork(cfg, branch="agent-experiment") + rt = crewai_event_bus._runtime_state + assert rt is not None + assert rt._branch == "agent-experiment" + + def test_agent_fork_auto_branch(self) -> None: + state = self._make_agent_state() + state._provider = JsonProvider() + with tempfile.TemporaryDirectory() as d: + loc = state.checkpoint(d) + cfg = CheckpointConfig(restore_from=loc) + + from crewai.events.event_bus import crewai_event_bus + + Agent.fork(cfg) + rt = crewai_event_bus._runtime_state + assert rt is not None + assert rt._branch.startswith("fork/") + + def test_sync_checkpoint_fields_agent(self) -> None: + from crewai.state.runtime import _sync_checkpoint_fields + + agent = Agent(role="r", goal="g", backstory="b", llm="gpt-4o-mini") + agent._kickoff_event_id = "evt-123" + _sync_checkpoint_fields(agent) + assert agent.checkpoint_kickoff_event_id == "evt-123" + + def test_agent_restore_kickoff_event_id(self) -> None: + agent = Agent(role="r", goal="g", backstory="b", llm="gpt-4o-mini") + agent._kickoff_event_id = "evt-456" + state = RuntimeState(root=[agent]) + state._provider = JsonProvider() + with tempfile.TemporaryDirectory() as d: + from crewai.state.runtime import _prepare_entities + + _prepare_entities(state.root) + loc = state.checkpoint(d) + cfg = CheckpointConfig(restore_from=loc) + restored = Agent.from_checkpoint(cfg) + assert restored._kickoff_event_id == "evt-456" From 48f391092c753ee056ece89c307f4ffe0bf9dbf6 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 00:01:55 +0800 Subject: [PATCH 10/32] fix: preserve thought_signature in Gemini streaming tool calls Gemini thinking models (2.5+, 3.x) require thought_signature on functionCall parts when sent back in conversation history. The streaming path was extracting only name/args into plain dicts, losing the signature. Return raw Part objects (matching the non-streaming path) so the executor preserves them via raw_tool_call_parts. --- .../crewai/llms/providers/gemini/completion.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/lib/crewai/src/crewai/llms/providers/gemini/completion.py b/lib/crewai/src/crewai/llms/providers/gemini/completion.py index 1b2fb26cb..f7fd0f61e 100644 --- a/lib/crewai/src/crewai/llms/providers/gemini/completion.py +++ b/lib/crewai/src/crewai/llms/providers/gemini/completion.py @@ -976,6 +976,7 @@ class GeminiCompletion(BaseLLM): "id": call_id, "name": part.function_call.name, "args": args_dict, + "raw_part": part, } self._emit_stream_chunk_event( @@ -1060,29 +1061,20 @@ class GeminiCompletion(BaseLLM): if call_data.get("name") != STRUCTURED_OUTPUT_TOOL_NAME } - # If there are function calls but no available_functions, - # return them for the executor to handle if non_structured_output_calls and not available_functions: - formatted_function_calls = [ - { - "id": call_data["id"], - "function": { - "name": call_data["name"], - "arguments": json.dumps(call_data["args"]), - }, - "type": "function", - } + raw_parts = [ + call_data["raw_part"] for call_data in non_structured_output_calls.values() ] self._emit_call_completed_event( - response=formatted_function_calls, + response=raw_parts, call_type=LLMCallType.TOOL_CALL, from_task=from_task, from_agent=from_agent, messages=self._convert_contents_to_dict(contents), usage=usage_data, ) - return formatted_function_calls + return raw_parts # Handle completed function calls (excluding structured_output) if non_structured_output_calls and available_functions: From 0b408534ab9c46bca273e2f557ef49099c29a1b1 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 00:53:50 +0800 Subject: [PATCH 11/32] feat: bump versions to 1.14.3a1 --- lib/crewai-files/src/crewai_files/__init__.py | 2 +- lib/crewai-tools/pyproject.toml | 2 +- lib/crewai-tools/src/crewai_tools/__init__.py | 2 +- lib/crewai/pyproject.toml | 2 +- lib/crewai/src/crewai/__init__.py | 2 +- lib/crewai/src/crewai/cli/templates/crew/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/flow/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/tool/pyproject.toml | 2 +- lib/devtools/src/crewai_devtools/__init__.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/crewai-files/src/crewai_files/__init__.py b/lib/crewai-files/src/crewai_files/__init__.py index 1b954d886..9fba24b55 100644 --- a/lib/crewai-files/src/crewai_files/__init__.py +++ b/lib/crewai-files/src/crewai_files/__init__.py @@ -152,4 +152,4 @@ __all__ = [ "wrap_file_source", ] -__version__ = "1.14.2" +__version__ = "1.14.3a1" diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index c75e406dd..429dc31ef 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14" dependencies = [ "pytube~=15.0.0", "requests>=2.33.0,<3", - "crewai==1.14.2", + "crewai==1.14.3a1", "tiktoken~=0.8.0", "beautifulsoup4~=4.13.4", "python-docx~=1.2.0", diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 56b30bf9b..3a4adda97 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -305,4 +305,4 @@ __all__ = [ "ZapierActionTools", ] -__version__ = "1.14.2" +__version__ = "1.14.3a1" diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index e6ab04399..4956c81f0 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -55,7 +55,7 @@ Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] tools = [ - "crewai-tools==1.14.2", + "crewai-tools==1.14.3a1", ] embeddings = [ "tiktoken~=0.8.0" diff --git a/lib/crewai/src/crewai/__init__.py b/lib/crewai/src/crewai/__init__.py index b6766741f..444cb5d07 100644 --- a/lib/crewai/src/crewai/__init__.py +++ b/lib/crewai/src/crewai/__init__.py @@ -46,7 +46,7 @@ def _suppress_pydantic_deprecation_warnings() -> None: _suppress_pydantic_deprecation_warnings() -__version__ = "1.14.2" +__version__ = "1.14.3a1" _telemetry_submitted = False diff --git a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml index c9bddf975..bf87e3131 100644 --- a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2" + "crewai[tools]==1.14.3a1" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml index ddb2750e9..bdd4fd2a4 100644 --- a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2" + "crewai[tools]==1.14.3a1" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml index a84300df4..fcd42c81f 100644 --- a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.2" + "crewai[tools]==1.14.3a1" ] [tool.crewai] diff --git a/lib/devtools/src/crewai_devtools/__init__.py b/lib/devtools/src/crewai_devtools/__init__.py index 9c2031491..9df2b583d 100644 --- a/lib/devtools/src/crewai_devtools/__init__.py +++ b/lib/devtools/src/crewai_devtools/__init__.py @@ -1,3 +1,3 @@ """CrewAI development tools.""" -__version__ = "1.14.2" +__version__ = "1.14.3a1" From bc1f1b85a48846920ba00c687f4bc993990224ad Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 00:59:07 +0800 Subject: [PATCH 12/32] docs: update changelog and version for v1.14.3a1 --- docs/ar/changelog.mdx | 25 +++++++++++++++++++++++++ docs/en/changelog.mdx | 25 +++++++++++++++++++++++++ docs/ko/changelog.mdx | 25 +++++++++++++++++++++++++ docs/pt-BR/changelog.mdx | 25 +++++++++++++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/docs/ar/changelog.mdx b/docs/ar/changelog.mdx index 09a397719..c99dba7d7 100644 --- a/docs/ar/changelog.mdx +++ b/docs/ar/changelog.mdx @@ -4,6 +4,31 @@ description: "تحديثات المنتج والتحسينات وإصلاحات icon: "clock" mode: "wide" --- + + ## v1.14.3a1 + + [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) + + ## ما الذي تغير + + ### الميزات + - إضافة دعم نقاط التحقق والفروع لوكلاء مستقلين + + ### إصلاحات الأخطاء + - الحفاظ على thought_signature في استدعاءات أداة البث Gemini + - إصدار task_started عند استئناف الفرع وإعادة تصميم واجهة المستخدم النصية لنقاط التحقق + - تصحيح ترتيب التشغيل الجاف ومعالجة الفرع القديم الذي تم التحقق منه في إصدار أدوات التطوير + - استخدام تواريخ مستقبلية في اختبارات تقليم نقاط التحقق لمنع الفشل المعتمد على الوقت (#5543) + + ### الوثائق + - تحديث سجل التغييرات والإصدار لـ v1.14.2 + + ## المساهمون + + @alex-clawd, @greysonlalonde + + + ## v1.14.2 diff --git a/docs/en/changelog.mdx b/docs/en/changelog.mdx index aabff6e24..26ac50285 100644 --- a/docs/en/changelog.mdx +++ b/docs/en/changelog.mdx @@ -4,6 +4,31 @@ description: "Product updates, improvements, and bug fixes for CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a1 + + [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) + + ## What's Changed + + ### Features + - Add checkpoint and fork support to standalone agents + + ### Bug Fixes + - Preserve thought_signature in Gemini streaming tool calls + - Emit task_started on fork resume and redesign checkpoint TUI + - Correct dry-run order and handle checked-out stale branch in devtools release + - Use future dates in checkpoint prune tests to prevent time-dependent failures (#5543) + + ### Documentation + - Update changelog and version for v1.14.2 + + ## Contributors + + @alex-clawd, @greysonlalonde + + + ## v1.14.2 diff --git a/docs/ko/changelog.mdx b/docs/ko/changelog.mdx index 0901afab6..ab9715714 100644 --- a/docs/ko/changelog.mdx +++ b/docs/ko/changelog.mdx @@ -4,6 +4,31 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정" icon: "clock" mode: "wide" --- + + ## v1.14.3a1 + + [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) + + ## 변경 사항 + + ### 기능 + - 독립형 에이전트에 체크포인트 및 포크 지원 추가 + + ### 버그 수정 + - Gemini 스트리밍 도구 호출에서 thought_signature 보존 + - 포크 재개 시 task_started 방출 및 체크포인트 TUI 재설계 + - dry-run 순서 수정 및 devtools 릴리스에서 체크아웃된 오래된 브랜치 처리 + - 체크포인트 가지치기 테스트에서 미래 날짜 사용하여 시간 의존성 실패 방지 (#5543) + + ### 문서 + - v1.14.2에 대한 변경 로그 및 버전 업데이트 + + ## 기여자 + + @alex-clawd, @greysonlalonde + + + ## v1.14.2 diff --git a/docs/pt-BR/changelog.mdx b/docs/pt-BR/changelog.mdx index cf1eef1a9..a32cc6ff5 100644 --- a/docs/pt-BR/changelog.mdx +++ b/docs/pt-BR/changelog.mdx @@ -4,6 +4,31 @@ description: "Atualizações de produto, melhorias e correções do CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a1 + + [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) + + ## O que Mudou + + ### Funcionalidades + - Adicionar suporte a checkpoint e fork para agentes autônomos + + ### Correções de Bugs + - Preservar thought_signature nas chamadas da ferramenta de streaming Gemini + - Emitir task_started na retomada do fork e redesenhar a TUI de checkpoint + - Corrigir a ordem do dry-run e lidar com branch desatualizada em release do devtools + - Usar datas futuras nos testes de poda de checkpoint para evitar falhas dependentes do tempo (#5543) + + ### Documentação + - Atualizar changelog e versão para v1.14.2 + + ## Contribuidores + + @alex-clawd, @greysonlalonde + + + ## v1.14.2 From 2c08f543414ba804d446bf111c171a133027d807 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:17:11 -0700 Subject: [PATCH 13/32] feat: add Daytona sandbox tools for enhanced functionality (#5530) * feat: add Daytona sandbox tools for enhanced functionality - Introduced DaytonaBaseTool as a shared base for tools interacting with Daytona sandboxes. - Added DaytonaExecTool for executing shell commands within a sandbox. - Implemented DaytonaFileTool for managing files (read, write, delete, etc.) in a sandbox. - Created DaytonaPythonTool for running Python code in a sandbox environment. - Updated pyproject.toml to include Daytona as a dependency. * chore: update tool specifications * refactor: enhance error handling and logging in Daytona tools - Added logging for best-effort cleanup failures in DaytonaBaseTool and DaytonaFileTool to aid in debugging. - Improved error message for ImportError in DaytonaPythonTool to provide clearer guidance on SDK compatibility issues. * linted * addressing comment * pinning version * supporting append * chore: update tool specifications --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/crewai-tools/pyproject.toml | 3 + lib/crewai-tools/src/crewai_tools/__init__.py | 8 + .../src/crewai_tools/tools/__init__.py | 8 + .../tools/daytona_sandbox_tool/README.md | 107 +++ .../tools/daytona_sandbox_tool/__init__.py | 13 + .../daytona_sandbox_tool/daytona_base_tool.py | 198 ++++++ .../daytona_sandbox_tool/daytona_exec_tool.py | 59 ++ .../daytona_sandbox_tool/daytona_file_tool.py | 205 ++++++ .../daytona_python_tool.py | 82 +++ lib/crewai-tools/tool.specs.json | 628 ++++++++++++++++++ uv.lock | 206 +++++- 11 files changed, 1515 insertions(+), 2 deletions(-) create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/README.md create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/__init__.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_base_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_exec_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_python_tool.py diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index 429dc31ef..473f2e411 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -139,6 +139,9 @@ contextual = [ "contextual-client>=0.1.0", "nest-asyncio>=1.6.0", ] +daytona = [ + "daytona~=0.140.0", +] [tool.uv] diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 3a4adda97..87e53b756 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -59,6 +59,11 @@ from crewai_tools.tools.dalle_tool.dalle_tool import DallETool from crewai_tools.tools.databricks_query_tool.databricks_query_tool import ( DatabricksQueryTool, ) +from crewai_tools.tools.daytona_sandbox_tool import ( + DaytonaExecTool, + DaytonaFileTool, + DaytonaPythonTool, +) from crewai_tools.tools.directory_read_tool.directory_read_tool import ( DirectoryReadTool, ) @@ -232,6 +237,9 @@ __all__ = [ "DOCXSearchTool", "DallETool", "DatabricksQueryTool", + "DaytonaExecTool", + "DaytonaFileTool", + "DaytonaPythonTool", "DirectoryReadTool", "DirectorySearchTool", "EXASearchTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/__init__.py index d3c1da664..40fdb74eb 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/tools/__init__.py @@ -48,6 +48,11 @@ from crewai_tools.tools.dalle_tool.dalle_tool import DallETool from crewai_tools.tools.databricks_query_tool.databricks_query_tool import ( DatabricksQueryTool, ) +from crewai_tools.tools.daytona_sandbox_tool import ( + DaytonaExecTool, + DaytonaFileTool, + DaytonaPythonTool, +) from crewai_tools.tools.directory_read_tool.directory_read_tool import ( DirectoryReadTool, ) @@ -217,6 +222,9 @@ __all__ = [ "DOCXSearchTool", "DallETool", "DatabricksQueryTool", + "DaytonaExecTool", + "DaytonaFileTool", + "DaytonaPythonTool", "DirectoryReadTool", "DirectorySearchTool", "EXASearchTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/README.md b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/README.md new file mode 100644 index 000000000..a2365049e --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/README.md @@ -0,0 +1,107 @@ +# Daytona Sandbox Tools + +Run shell commands, execute Python, and manage files inside a [Daytona](https://www.daytona.io/) sandbox. Daytona provides isolated, ephemeral compute environments suitable for agent-driven code execution. + +Three tools are provided so you can pick what the agent actually needs: + +- **`DaytonaExecTool`** — run a shell command (`sandbox.process.exec`). +- **`DaytonaPythonTool`** — run a Python script (`sandbox.process.code_run`). +- **`DaytonaFileTool`** — read / write / list / delete files (`sandbox.fs.*`). + +## Installation + +```shell +uv add "crewai-tools[daytona]" +# or +pip install "crewai-tools[daytona]" +``` + +Set the API key: + +```shell +export DAYTONA_API_KEY="..." +``` + +`DAYTONA_API_URL` and `DAYTONA_TARGET` are also respected if set. + +## Sandbox lifecycle + +All three tools share the same lifecycle controls from `DaytonaBaseTool`: + +| Mode | When the sandbox is created | When it is deleted | +| --- | --- | --- | +| **Ephemeral** (default, `persistent=False`) | On every `_run` call | At the end of that same call | +| **Persistent** (`persistent=True`) | Lazily on first use | At process exit (via `atexit`), or manually via `tool.close()` | +| **Attach** (`sandbox_id="…"`) | Never — the tool attaches to an existing sandbox | Never — the tool will not delete a sandbox it did not create | + +Ephemeral mode is the safe default: nothing leaks if the agent forgets to clean up. Use persistent mode when you want filesystem state or installed packages to carry across steps — this is typical when pairing `DaytonaFileTool` with `DaytonaExecTool`. + +## Examples + +### One-shot Python execution (ephemeral) + +```python +from crewai_tools import DaytonaPythonTool + +tool = DaytonaPythonTool() +result = tool.run(code="print(sum(range(10)))") +``` + +### Multi-step shell session (persistent) + +```python +from crewai_tools import DaytonaExecTool, DaytonaFileTool + +exec_tool = DaytonaExecTool(persistent=True) +file_tool = DaytonaFileTool(persistent=True) + +# Agent writes a script, then runs it — both share the same sandbox instance +# because they each keep their own persistent sandbox. If you need the *same* +# sandbox across two tools, create one tool, grab the sandbox id via +# `tool._persistent_sandbox.id`, and pass it to the other via `sandbox_id=...`. +``` + +### Attach to an existing sandbox + +```python +from crewai_tools import DaytonaExecTool + +tool = DaytonaExecTool(sandbox_id="my-long-lived-sandbox") +``` + +### Custom create params + +Pass Daytona's `CreateSandboxFromSnapshotParams` kwargs via `create_params`: + +```python +tool = DaytonaExecTool( + persistent=True, + create_params={ + "language": "python", + "env_vars": {"MY_FLAG": "1"}, + "labels": {"owner": "crewai-agent"}, + }, +) +``` + +## Tool arguments + +### `DaytonaExecTool` +- `command: str` — shell command to run. +- `cwd: str | None` — working directory. +- `env: dict[str, str] | None` — extra env vars for this command. +- `timeout: int | None` — seconds. + +### `DaytonaPythonTool` +- `code: str` — Python source to execute. +- `argv: list[str] | None` — argv forwarded via `CodeRunParams`. +- `env: dict[str, str] | None` — env vars forwarded via `CodeRunParams`. +- `timeout: int | None` — seconds. + +### `DaytonaFileTool` +- `action: "read" | "write" | "list" | "delete" | "mkdir" | "info"` +- `path: str` — absolute path inside the sandbox. +- `content: str | None` — required for `write`. +- `binary: bool` — if `True`, `content` is base64 on write / returned as base64 on read. +- `recursive: bool` — for `delete`, removes directories recursively. +- `mode: str` — for `mkdir`, octal permission string (default `"0755"`). diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/__init__.py new file mode 100644 index 000000000..e04396bfb --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/__init__.py @@ -0,0 +1,13 @@ +from crewai_tools.tools.daytona_sandbox_tool.daytona_base_tool import DaytonaBaseTool +from crewai_tools.tools.daytona_sandbox_tool.daytona_exec_tool import DaytonaExecTool +from crewai_tools.tools.daytona_sandbox_tool.daytona_file_tool import DaytonaFileTool +from crewai_tools.tools.daytona_sandbox_tool.daytona_python_tool import ( + DaytonaPythonTool, +) + +__all__ = [ + "DaytonaBaseTool", + "DaytonaExecTool", + "DaytonaFileTool", + "DaytonaPythonTool", +] diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_base_tool.py b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_base_tool.py new file mode 100644 index 000000000..b601e4309 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_base_tool.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +import atexit +import logging +import os +import threading +from typing import Any, ClassVar + +from crewai.tools import BaseTool, EnvVar +from pydantic import ConfigDict, Field, PrivateAttr + + +logger = logging.getLogger(__name__) + + +class DaytonaBaseTool(BaseTool): + """Shared base for tools that act on a Daytona sandbox. + + Lifecycle modes: + - persistent=False (default): create a fresh sandbox per `_run` call and + delete it when the call returns. Safer and stateless — nothing leaks if + the agent forgets cleanup. + - persistent=True: lazily create a single sandbox on first use, cache it + on the instance, and register an atexit hook to delete it at process + exit. Cheaper across many calls and lets files/state carry over. + - sandbox_id=: attach to a sandbox the caller already owns. + Never deleted by the tool. + """ + + model_config = ConfigDict(arbitrary_types_allowed=True) + + package_dependencies: list[str] = Field(default_factory=lambda: ["daytona"]) + + api_key: str | None = Field( + default_factory=lambda: os.getenv("DAYTONA_API_KEY"), + description="Daytona API key. Falls back to DAYTONA_API_KEY env var.", + json_schema_extra={"required": False}, + ) + api_url: str | None = Field( + default_factory=lambda: os.getenv("DAYTONA_API_URL"), + description="Daytona API URL override. Falls back to DAYTONA_API_URL env var.", + json_schema_extra={"required": False}, + ) + target: str | None = Field( + default_factory=lambda: os.getenv("DAYTONA_TARGET"), + description="Daytona target region. Falls back to DAYTONA_TARGET env var.", + json_schema_extra={"required": False}, + ) + + persistent: bool = Field( + default=False, + description=( + "If True, reuse one sandbox across all calls to this tool instance " + "and delete it at process exit. Default False creates and deletes a " + "fresh sandbox per call." + ), + ) + sandbox_id: str | None = Field( + default=None, + description=( + "Attach to an existing sandbox by id or name instead of creating a " + "new one. The tool will never delete a sandbox it did not create." + ), + ) + create_params: dict[str, Any] | None = Field( + default=None, + description=( + "Optional kwargs forwarded to CreateSandboxFromSnapshotParams when " + "creating a sandbox (e.g. language, snapshot, env_vars, labels)." + ), + ) + sandbox_timeout: float = Field( + default=60.0, + description="Timeout in seconds for sandbox create/delete operations.", + ) + + env_vars: list[EnvVar] = Field( + default_factory=lambda: [ + EnvVar( + name="DAYTONA_API_KEY", + description="API key for Daytona sandbox service", + required=False, + ), + EnvVar( + name="DAYTONA_API_URL", + description="Daytona API base URL (optional)", + required=False, + ), + EnvVar( + name="DAYTONA_TARGET", + description="Daytona target region (optional)", + required=False, + ), + ] + ) + + _client: Any | None = PrivateAttr(default=None) + _persistent_sandbox: Any | None = PrivateAttr(default=None) + _lock: threading.Lock = PrivateAttr(default_factory=threading.Lock) + _cleanup_registered: bool = PrivateAttr(default=False) + + _sdk_cache: ClassVar[dict[str, Any]] = {} + + @classmethod + def _import_sdk(cls) -> dict[str, Any]: + if cls._sdk_cache: + return cls._sdk_cache + try: + from daytona import ( + CreateSandboxFromSnapshotParams, + Daytona, + DaytonaConfig, + ) + except ImportError as exc: + raise ImportError( + "The 'daytona' package is required for Daytona sandbox tools. " + "Install it with: uv add daytona (or) pip install daytona" + ) from exc + cls._sdk_cache = { + "Daytona": Daytona, + "DaytonaConfig": DaytonaConfig, + "CreateSandboxFromSnapshotParams": CreateSandboxFromSnapshotParams, + } + return cls._sdk_cache + + def _get_client(self) -> Any: + if self._client is not None: + return self._client + sdk = self._import_sdk() + config_kwargs: dict[str, Any] = {} + if self.api_key: + config_kwargs["api_key"] = self.api_key + if self.api_url: + config_kwargs["api_url"] = self.api_url + if self.target: + config_kwargs["target"] = self.target + config = sdk["DaytonaConfig"](**config_kwargs) if config_kwargs else None + self._client = sdk["Daytona"](config) if config else sdk["Daytona"]() + return self._client + + def _build_create_params(self) -> Any | None: + if not self.create_params: + return None + sdk = self._import_sdk() + return sdk["CreateSandboxFromSnapshotParams"](**self.create_params) + + def _acquire_sandbox(self) -> tuple[Any, bool]: + """Return (sandbox, should_delete_after_use).""" + client = self._get_client() + + if self.sandbox_id: + return client.get(self.sandbox_id), False + + if self.persistent: + with self._lock: + if self._persistent_sandbox is None: + self._persistent_sandbox = client.create( + self._build_create_params(), + timeout=self.sandbox_timeout, + ) + if not self._cleanup_registered: + atexit.register(self.close) + self._cleanup_registered = True + return self._persistent_sandbox, False + + sandbox = client.create( + self._build_create_params(), + timeout=self.sandbox_timeout, + ) + return sandbox, True + + def _release_sandbox(self, sandbox: Any, should_delete: bool) -> None: + if not should_delete: + return + try: + sandbox.delete(timeout=self.sandbox_timeout) + except Exception: + logger.debug( + "Best-effort sandbox cleanup failed after ephemeral use; " + "the sandbox may need manual deletion.", + exc_info=True, + ) + + def close(self) -> None: + """Delete the cached persistent sandbox if one exists.""" + with self._lock: + sandbox = self._persistent_sandbox + self._persistent_sandbox = None + if sandbox is None: + return + try: + sandbox.delete(timeout=self.sandbox_timeout) + except Exception: + logger.debug( + "Best-effort persistent sandbox cleanup failed at close(); " + "the sandbox may need manual deletion.", + exc_info=True, + ) diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_exec_tool.py b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_exec_tool.py new file mode 100644 index 000000000..cffcab220 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_exec_tool.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from builtins import type as type_ +from typing import Any + +from pydantic import BaseModel, Field + +from crewai_tools.tools.daytona_sandbox_tool.daytona_base_tool import DaytonaBaseTool + + +class DaytonaExecToolSchema(BaseModel): + command: str = Field(..., description="Shell command to execute in the sandbox.") + cwd: str | None = Field( + default=None, + description="Working directory to run the command in. Defaults to the sandbox work dir.", + ) + env: dict[str, str] | None = Field( + default=None, + description="Optional environment variables to set for this command.", + ) + timeout: int | None = Field( + default=None, + description="Maximum seconds to wait for the command to finish.", + ) + + +class DaytonaExecTool(DaytonaBaseTool): + """Run a shell command inside a Daytona sandbox.""" + + name: str = "Daytona Sandbox Exec" + description: str = ( + "Execute a shell command inside a Daytona sandbox and return the exit " + "code and combined output. Use this to run builds, package installs, " + "git operations, or any one-off shell command." + ) + args_schema: type_[BaseModel] = DaytonaExecToolSchema + + def _run( + self, + command: str, + cwd: str | None = None, + env: dict[str, str] | None = None, + timeout: int | None = None, + ) -> Any: + sandbox, should_delete = self._acquire_sandbox() + try: + response = sandbox.process.exec( + command, + cwd=cwd, + env=env, + timeout=timeout, + ) + return { + "exit_code": getattr(response, "exit_code", None), + "result": getattr(response, "result", None), + "artifacts": getattr(response, "artifacts", None), + } + finally: + self._release_sandbox(sandbox, should_delete) diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py new file mode 100644 index 000000000..e019419b3 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py @@ -0,0 +1,205 @@ +from __future__ import annotations + +import base64 +from builtins import type as type_ +import logging +import posixpath +from typing import Any, Literal + +from pydantic import BaseModel, Field, model_validator + +from crewai_tools.tools.daytona_sandbox_tool.daytona_base_tool import DaytonaBaseTool + + +logger = logging.getLogger(__name__) + + +FileAction = Literal["read", "write", "append", "list", "delete", "mkdir", "info"] + + +class DaytonaFileToolSchema(BaseModel): + action: FileAction = Field( + ..., + description=( + "The filesystem action to perform: 'read' (returns file contents), " + "'write' (create or replace a file with content), 'append' (append " + "content to an existing file — use this for writing large files in " + "chunks to avoid hitting tool-call size limits), 'list' (lists a " + "directory), 'delete' (removes a file/dir), 'mkdir' (creates a " + "directory), 'info' (returns file metadata)." + ), + ) + path: str = Field(..., description="Absolute path inside the sandbox.") + content: str | None = Field( + default=None, + description=( + "Content to write or append. If omitted for 'write', an empty file " + "is created. For files larger than a few KB, prefer one 'write' " + "with empty content followed by multiple 'append' calls of ~4KB " + "each to stay within tool-call payload limits." + ), + ) + binary: bool = Field( + default=False, + description=( + "For 'write': treat content as base64 and upload raw bytes. " + "For 'read': return contents as base64 instead of decoded utf-8." + ), + ) + recursive: bool = Field( + default=False, + description="For action='delete': remove directories recursively.", + ) + mode: str = Field( + default="0755", + description="For action='mkdir': octal permission string (default 0755).", + ) + + @model_validator(mode="after") + def _validate_action_args(self) -> DaytonaFileToolSchema: + if self.action == "append" and self.content is None: + raise ValueError( + "action='append' requires 'content'. Pass the chunk to append " + "in the 'content' field." + ) + return self + + +class DaytonaFileTool(DaytonaBaseTool): + """Read, write, and manage files inside a Daytona sandbox. + + Notes: + - Most useful with `persistent=True` or an explicit `sandbox_id`. With the + default ephemeral mode, files disappear when this tool call finishes. + """ + + name: str = "Daytona Sandbox Files" + description: str = ( + "Perform filesystem operations inside a Daytona sandbox: read a file, " + "write content to a path, append content to an existing file, list a " + "directory, delete a path, make a directory, or fetch file metadata. " + "For files larger than a few KB, create the file with action='write' " + "and empty content, then send the body via multiple 'append' calls of " + "~4KB each to stay within tool-call payload limits." + ) + args_schema: type_[BaseModel] = DaytonaFileToolSchema + + def _run( + self, + action: FileAction, + path: str, + content: str | None = None, + binary: bool = False, + recursive: bool = False, + mode: str = "0755", + ) -> Any: + sandbox, should_delete = self._acquire_sandbox() + try: + if action == "read": + return self._read(sandbox, path, binary=binary) + if action == "write": + return self._write(sandbox, path, content or "", binary=binary) + if action == "append": + return self._append(sandbox, path, content or "", binary=binary) + if action == "list": + return self._list(sandbox, path) + if action == "delete": + sandbox.fs.delete_file(path, recursive=recursive) + return {"status": "deleted", "path": path} + if action == "mkdir": + sandbox.fs.create_folder(path, mode) + return {"status": "created", "path": path, "mode": mode} + if action == "info": + return self._info(sandbox, path) + raise ValueError(f"Unknown action: {action}") + finally: + self._release_sandbox(sandbox, should_delete) + + def _read(self, sandbox: Any, path: str, *, binary: bool) -> dict[str, Any]: + data: bytes = sandbox.fs.download_file(path) + if binary: + return { + "path": path, + "encoding": "base64", + "content": base64.b64encode(data).decode("ascii"), + } + try: + return {"path": path, "encoding": "utf-8", "content": data.decode("utf-8")} + except UnicodeDecodeError: + return { + "path": path, + "encoding": "base64", + "content": base64.b64encode(data).decode("ascii"), + "note": "File was not valid utf-8; returned as base64.", + } + + def _write( + self, sandbox: Any, path: str, content: str, *, binary: bool + ) -> dict[str, Any]: + payload = base64.b64decode(content) if binary else content.encode("utf-8") + self._ensure_parent_dir(sandbox, path) + sandbox.fs.upload_file(payload, path) + return {"status": "written", "path": path, "bytes": len(payload)} + + def _append( + self, sandbox: Any, path: str, content: str, *, binary: bool + ) -> dict[str, Any]: + chunk = base64.b64decode(content) if binary else content.encode("utf-8") + self._ensure_parent_dir(sandbox, path) + try: + existing: bytes = sandbox.fs.download_file(path) + except Exception: + existing = b"" + payload = existing + chunk + sandbox.fs.upload_file(payload, path) + return { + "status": "appended", + "path": path, + "appended_bytes": len(chunk), + "total_bytes": len(payload), + } + + @staticmethod + def _ensure_parent_dir(sandbox: Any, path: str) -> None: + """Make sure the parent directory of `path` exists. + + Daytona's upload returns 400 if the parent directory is missing. We + best-effort mkdir the parent; any error (e.g. already exists) is + swallowed because `create_folder` is not idempotent on the server. + """ + parent = posixpath.dirname(path) + if not parent or parent in ("/", "."): + return + try: + sandbox.fs.create_folder(parent, "0755") + except Exception: + logger.debug( + "Best-effort parent-directory create failed for %s; " + "assuming it already exists and proceeding with the write.", + parent, + exc_info=True, + ) + + def _list(self, sandbox: Any, path: str) -> dict[str, Any]: + entries = sandbox.fs.list_files(path) + return { + "path": path, + "entries": [self._file_info_to_dict(entry) for entry in entries], + } + + def _info(self, sandbox: Any, path: str) -> dict[str, Any]: + return self._file_info_to_dict(sandbox.fs.get_file_info(path)) + + @staticmethod + def _file_info_to_dict(info: Any) -> dict[str, Any]: + fields = ( + "name", + "size", + "mode", + "permissions", + "is_dir", + "mod_time", + "owner", + "group", + ) + return {field: getattr(info, field, None) for field in fields} diff --git a/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_python_tool.py b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_python_tool.py new file mode 100644 index 000000000..c0bc9d405 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_python_tool.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from builtins import type as type_ +from typing import Any + +from pydantic import BaseModel, Field + +from crewai_tools.tools.daytona_sandbox_tool.daytona_base_tool import DaytonaBaseTool + + +class DaytonaPythonToolSchema(BaseModel): + code: str = Field( + ..., + description="Python source to execute inside the sandbox.", + ) + argv: list[str] | None = Field( + default=None, + description="Optional argv passed to the script (forwarded as params.argv).", + ) + env: dict[str, str] | None = Field( + default=None, + description="Optional environment variables for the run (forwarded as params.env).", + ) + timeout: int | None = Field( + default=None, + description="Maximum seconds to wait for the code to finish.", + ) + + +class DaytonaPythonTool(DaytonaBaseTool): + """Run Python source inside a Daytona sandbox.""" + + name: str = "Daytona Sandbox Python" + description: str = ( + "Execute a block of Python code inside a Daytona sandbox and return the " + "exit code, captured stdout, and any produced artifacts. Use this for " + "data processing, quick scripts, or analysis that should run in an " + "isolated environment." + ) + args_schema: type_[BaseModel] = DaytonaPythonToolSchema + + def _run( + self, + code: str, + argv: list[str] | None = None, + env: dict[str, str] | None = None, + timeout: int | None = None, + ) -> Any: + sandbox, should_delete = self._acquire_sandbox() + try: + params = self._build_code_run_params(argv=argv, env=env) + response = sandbox.process.code_run(code, params=params, timeout=timeout) + return { + "exit_code": getattr(response, "exit_code", None), + "result": getattr(response, "result", None), + "artifacts": getattr(response, "artifacts", None), + } + finally: + self._release_sandbox(sandbox, should_delete) + + def _build_code_run_params( + self, + argv: list[str] | None, + env: dict[str, str] | None, + ) -> Any | None: + if argv is None and env is None: + return None + try: + from daytona import CodeRunParams + except ImportError as exc: + raise ImportError( + "Could not import daytona.CodeRunParams while building " + "argv/env for sandbox.process.code_run. This usually means the " + "installed 'daytona' SDK is too old or incompatible. Upgrade " + "with: pip install -U 'crewai-tools[daytona]'" + ) from exc + kwargs: dict[str, Any] = {} + if argv is not None: + kwargs["argv"] = argv + if env is not None: + kwargs["env"] = env + return CodeRunParams(**kwargs) diff --git a/lib/crewai-tools/tool.specs.json b/lib/crewai-tools/tool.specs.json index a00501503..6bd374749 100644 --- a/lib/crewai-tools/tool.specs.json +++ b/lib/crewai-tools/tool.specs.json @@ -6976,6 +6976,634 @@ "type": "object" } }, + { + "description": "Execute a shell command inside a Daytona sandbox and return the exit code and combined output. Use this to run builds, package installs, git operations, or any one-off shell command.", + "env_vars": [ + { + "default": null, + "description": "API key for Daytona sandbox service", + "name": "DAYTONA_API_KEY", + "required": false + }, + { + "default": null, + "description": "Daytona API base URL (optional)", + "name": "DAYTONA_API_URL", + "required": false + }, + { + "default": null, + "description": "Daytona target region (optional)", + "name": "DAYTONA_TARGET", + "required": false + } + ], + "humanized_name": "Daytona Sandbox Exec", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Run a shell command inside a Daytona sandbox.", + "properties": { + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API key. Falls back to DAYTONA_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "api_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API URL override. Falls back to DAYTONA_API_URL env var.", + "required": false, + "title": "Api Url" + }, + "create_params": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional kwargs forwarded to CreateSandboxFromSnapshotParams when creating a sandbox (e.g. language, snapshot, env_vars, labels).", + "title": "Create Params" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and delete it at process exit. Default False creates and deletes a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id or name instead of creating a new one. The tool will never delete a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 60.0, + "description": "Timeout in seconds for sandbox create/delete operations.", + "title": "Sandbox Timeout", + "type": "number" + }, + "target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona target region. Falls back to DAYTONA_TARGET env var.", + "required": false, + "title": "Target" + } + }, + "required": [], + "title": "DaytonaExecTool", + "type": "object" + }, + "name": "DaytonaExecTool", + "package_dependencies": [ + "daytona" + ], + "run_params_schema": { + "properties": { + "command": { + "description": "Shell command to execute in the sandbox.", + "title": "Command", + "type": "string" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Working directory to run the command in. Defaults to the sandbox work dir.", + "title": "Cwd" + }, + "env": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional environment variables to set for this command.", + "title": "Env" + }, + "timeout": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum seconds to wait for the command to finish.", + "title": "Timeout" + } + }, + "required": [ + "command" + ], + "title": "DaytonaExecToolSchema", + "type": "object" + } + }, + { + "description": "Perform filesystem operations inside a Daytona sandbox: read a file, write content to a path, append content to an existing file, list a directory, delete a path, make a directory, or fetch file metadata. For files larger than a few KB, create the file with action='write' and empty content, then send the body via multiple 'append' calls of ~4KB each to stay within tool-call payload limits.", + "env_vars": [ + { + "default": null, + "description": "API key for Daytona sandbox service", + "name": "DAYTONA_API_KEY", + "required": false + }, + { + "default": null, + "description": "Daytona API base URL (optional)", + "name": "DAYTONA_API_URL", + "required": false + }, + { + "default": null, + "description": "Daytona target region (optional)", + "name": "DAYTONA_TARGET", + "required": false + } + ], + "humanized_name": "Daytona Sandbox Files", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Read, write, and manage files inside a Daytona sandbox.\n\nNotes:\n - Most useful with `persistent=True` or an explicit `sandbox_id`. With the\n default ephemeral mode, files disappear when this tool call finishes.", + "properties": { + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API key. Falls back to DAYTONA_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "api_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API URL override. Falls back to DAYTONA_API_URL env var.", + "required": false, + "title": "Api Url" + }, + "create_params": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional kwargs forwarded to CreateSandboxFromSnapshotParams when creating a sandbox (e.g. language, snapshot, env_vars, labels).", + "title": "Create Params" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and delete it at process exit. Default False creates and deletes a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id or name instead of creating a new one. The tool will never delete a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 60.0, + "description": "Timeout in seconds for sandbox create/delete operations.", + "title": "Sandbox Timeout", + "type": "number" + }, + "target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona target region. Falls back to DAYTONA_TARGET env var.", + "required": false, + "title": "Target" + } + }, + "required": [], + "title": "DaytonaFileTool", + "type": "object" + }, + "name": "DaytonaFileTool", + "package_dependencies": [ + "daytona" + ], + "run_params_schema": { + "properties": { + "action": { + "description": "The filesystem action to perform: 'read' (returns file contents), 'write' (create or replace a file with content), 'append' (append content to an existing file \u2014 use this for writing large files in chunks to avoid hitting tool-call size limits), 'list' (lists a directory), 'delete' (removes a file/dir), 'mkdir' (creates a directory), 'info' (returns file metadata).", + "enum": [ + "read", + "write", + "append", + "list", + "delete", + "mkdir", + "info" + ], + "title": "Action", + "type": "string" + }, + "binary": { + "default": false, + "description": "For 'write': treat content as base64 and upload raw bytes. For 'read': return contents as base64 instead of decoded utf-8.", + "title": "Binary", + "type": "boolean" + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Content to write or append. If omitted for 'write', an empty file is created. For files larger than a few KB, prefer one 'write' with empty content followed by multiple 'append' calls of ~4KB each to stay within tool-call payload limits.", + "title": "Content" + }, + "mode": { + "default": "0755", + "description": "For action='mkdir': octal permission string (default 0755).", + "title": "Mode", + "type": "string" + }, + "path": { + "description": "Absolute path inside the sandbox.", + "title": "Path", + "type": "string" + }, + "recursive": { + "default": false, + "description": "For action='delete': remove directories recursively.", + "title": "Recursive", + "type": "boolean" + } + }, + "required": [ + "action", + "path" + ], + "title": "DaytonaFileToolSchema", + "type": "object" + } + }, + { + "description": "Execute a block of Python code inside a Daytona sandbox and return the exit code, captured stdout, and any produced artifacts. Use this for data processing, quick scripts, or analysis that should run in an isolated environment.", + "env_vars": [ + { + "default": null, + "description": "API key for Daytona sandbox service", + "name": "DAYTONA_API_KEY", + "required": false + }, + { + "default": null, + "description": "Daytona API base URL (optional)", + "name": "DAYTONA_API_URL", + "required": false + }, + { + "default": null, + "description": "Daytona target region (optional)", + "name": "DAYTONA_TARGET", + "required": false + } + ], + "humanized_name": "Daytona Sandbox Python", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Run Python source inside a Daytona sandbox.", + "properties": { + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API key. Falls back to DAYTONA_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "api_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona API URL override. Falls back to DAYTONA_API_URL env var.", + "required": false, + "title": "Api Url" + }, + "create_params": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional kwargs forwarded to CreateSandboxFromSnapshotParams when creating a sandbox (e.g. language, snapshot, env_vars, labels).", + "title": "Create Params" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and delete it at process exit. Default False creates and deletes a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id or name instead of creating a new one. The tool will never delete a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 60.0, + "description": "Timeout in seconds for sandbox create/delete operations.", + "title": "Sandbox Timeout", + "type": "number" + }, + "target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Daytona target region. Falls back to DAYTONA_TARGET env var.", + "required": false, + "title": "Target" + } + }, + "required": [], + "title": "DaytonaPythonTool", + "type": "object" + }, + "name": "DaytonaPythonTool", + "package_dependencies": [ + "daytona" + ], + "run_params_schema": { + "properties": { + "argv": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional argv passed to the script (forwarded as params.argv).", + "title": "Argv" + }, + "code": { + "description": "Python source to execute inside the sandbox.", + "title": "Code", + "type": "string" + }, + "env": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional environment variables for the run (forwarded as params.env).", + "title": "Env" + }, + "timeout": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum seconds to wait for the code to finish.", + "title": "Timeout" + } + }, + "required": [ + "code" + ], + "title": "DaytonaPythonToolSchema", + "type": "object" + } + }, { "description": "A tool that can be used to recursively list a directory's content.", "env_vars": [], diff --git a/uv.lock b/uv.lock index ad1bf7276..7451cfa17 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-17T16:00:00Z" +exclude-newer = "2026-04-18T07:00:00Z" [manifest] members = [ @@ -240,6 +240,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, ] +[[package]] +name = "aiohttp-retry" +version = "2.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/61/ebda4d8e3d8cfa1fd3db0fb428db2dd7461d5742cea35178277ad180b033/aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1", size = 13608, upload-time = "2024-11-06T10:44:54.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/99/84ba7273339d0f3dfa57901b846489d2e5c2cd731470167757f1935fffbd/aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54", size = 9981, upload-time = "2024-11-06T10:44:52.917Z" }, +] + [[package]] name = "aioitertools" version = "0.13.0" @@ -1474,6 +1486,9 @@ couchbase = [ databricks-sdk = [ { name = "databricks-sdk" }, ] +daytona = [ + { name = "daytona" }, +] exa-py = [ { name = "exa-py" }, ] @@ -1574,6 +1589,7 @@ requires-dist = [ { name = "crewai", editable = "lib/crewai" }, { name = "cryptography", marker = "extra == 'snowflake'", specifier = ">=43.0.3" }, { name = "databricks-sdk", marker = "extra == 'databricks-sdk'", specifier = ">=0.46.0" }, + { name = "daytona", marker = "extra == 'daytona'", specifier = "~=0.140.0" }, { name = "exa-py", marker = "extra == 'exa-py'", specifier = ">=1.8.7" }, { name = "firecrawl-py", marker = "extra == 'firecrawl-py'", specifier = ">=1.8.0" }, { name = "gitpython", marker = "extra == 'github'", specifier = ">=3.1.41,<4" }, @@ -1616,7 +1632,7 @@ requires-dist = [ { name = "weaviate-client", marker = "extra == 'weaviate-client'", specifier = ">=4.10.2" }, { name = "youtube-transcript-api", specifier = "~=1.2.2" }, ] -provides-extras = ["apify", "beautifulsoup4", "bedrock", "browserbase", "composio-core", "contextual", "couchbase", "databricks-sdk", "exa-py", "firecrawl-py", "github", "hyperbrowser", "linkup-sdk", "mcp", "mongodb", "multion", "mysql", "oxylabs", "patronus", "postgresql", "qdrant-client", "rag", "scrapegraph-py", "scrapfly-sdk", "selenium", "serpapi", "singlestore", "snowflake", "spider-client", "sqlalchemy", "stagehand", "tavily-python", "weaviate-client", "xml"] +provides-extras = ["apify", "beautifulsoup4", "bedrock", "browserbase", "composio-core", "contextual", "couchbase", "databricks-sdk", "daytona", "exa-py", "firecrawl-py", "github", "hyperbrowser", "linkup-sdk", "mcp", "mongodb", "multion", "mysql", "oxylabs", "patronus", "postgresql", "qdrant-client", "rag", "scrapegraph-py", "scrapfly-sdk", "selenium", "serpapi", "singlestore", "snowflake", "spider-client", "sqlalchemy", "stagehand", "tavily-python", "weaviate-client", "xml"] [[package]] name = "cryptography" @@ -1784,6 +1800,94 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, ] +[[package]] +name = "daytona" +version = "0.140.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "daytona-api-client" }, + { name = "daytona-api-client-async" }, + { name = "daytona-toolbox-api-client" }, + { name = "daytona-toolbox-api-client-async" }, + { name = "deprecated" }, + { name = "environs" }, + { name = "httpx" }, + { name = "multipart" }, + { name = "obstore" }, + { name = "pydantic" }, + { name = "toml" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/d4/4362b885f461ca2849f873c98e08594acb89d80ab82644ac88cdb4b7f8e9/daytona-0.140.0.tar.gz", hash = "sha256:8fa6dcc28ec735a9255d02cd98350b819fcf83daab866e688f659760c22bbfbf", size = 121616, upload-time = "2026-02-10T12:20:34.299Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/18/531ec599ff19adc9561ebfc5bdc5e5483fbb47e00d392376e69a259ed384/daytona-0.140.0-py3-none-any.whl", hash = "sha256:93a85d2c76e7e3dccbd708784026a61cd977ebfde37ed0777966c2e702918662", size = 150607, upload-time = "2026-02-10T12:20:32.889Z" }, +] + +[[package]] +name = "daytona-api-client" +version = "0.140.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/7e/64676a69f357be5a32154240c89d145090d76c6706652e50137997f2fcab/daytona_api_client-0.140.0.tar.gz", hash = "sha256:ed28b3337189393d2766697c98d1b764dea4fda82359040e6f8d111f5d073aef", size = 134360, upload-time = "2026-02-10T12:19:35.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/79/17fd48a00c5aea1386f46a232f8af03014ec827c7c6ea46a2e192cddedbd/daytona_api_client-0.140.0-py3-none-any.whl", hash = "sha256:6a0ba0b4483da23f6557e18350de292b727a663874fd82aac3ae21a444d55215", size = 375797, upload-time = "2026-02-10T12:19:33.987Z" }, +] + +[[package]] +name = "daytona-api-client-async" +version = "0.140.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiohttp-retry" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/e3/f5dcfa17f02988899427d1b898f6176922787b8cb361e0a42d962ca319b2/daytona_api_client_async-0.140.0.tar.gz", hash = "sha256:dc6c7126649162bbe31e3da665b421165f52407d34598f8ec89617650456949e", size = 134486, upload-time = "2026-02-10T12:19:50.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/74/0a13a70d19756da1987369820d6bac0c704cffdc684b0e237ccbabf8ffb0/daytona_api_client_async-0.140.0-py3-none-any.whl", hash = "sha256:404ea5492714f6f82d2afbaaa722b87e5f2f9d419dfd28ec37c0a1edad408fb1", size = 378645, upload-time = "2026-02-10T12:19:48.434Z" }, +] + +[[package]] +name = "daytona-toolbox-api-client" +version = "0.140.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/f1/b04957487ef7b6de4a45ba5348123f6b8ed18325fa6e5bf3eea71c0a387d/daytona_toolbox_api_client-0.140.0.tar.gz", hash = "sha256:b7421327fd5f45168ab5d1579cfdceae55356fb3da5939d13d9087ae49f79945", size = 64094, upload-time = "2026-02-10T12:19:40.882Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/d5/08136d47cfec7199081f6a6ccf8e19992425bff091a9c97fdf6872de8a40/daytona_toolbox_api_client-0.140.0-py3-none-any.whl", hash = "sha256:4d71842b461e2a3123e563475964ddda78884d012286d950c9d947a0d2779d07", size = 171059, upload-time = "2026-02-10T12:19:39.107Z" }, +] + +[[package]] +name = "daytona-toolbox-api-client-async" +version = "0.140.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiohttp-retry" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/52/2a1b5fe303f4ea116ade0fe09dd85eba349a67318b83c74f7d2808a42905/daytona_toolbox_api_client_async-0.140.0.tar.gz", hash = "sha256:62a4b51404db28e95e18da836c8de0d2b67192d42027bc3c9273937d3066612b", size = 61090, upload-time = "2026-02-10T12:20:02.273Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/ca/0113aba439cad635a1ecaf4ac50c9a8248002d529b2c44d02f80ec08f503/daytona_toolbox_api_client_async-0.140.0-py3-none-any.whl", hash = "sha256:dddf18320449234ed62ce8d051f470ecaac0f56bf23e800c0bf51b11b5251d17", size = 172380, upload-time = "2026-02-10T12:20:01.005Z" }, +] + [[package]] name = "decli" version = "0.6.3" @@ -2046,6 +2150,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/5e/4b5aaaabddfacfe36ba7768817bd1f71a7a810a43705e531f3ae4c690767/emoji-2.15.0-py3-none-any.whl", hash = "sha256:205296793d66a89d88af4688fa57fd6496732eb48917a87175a023c8138995eb", size = 608433, upload-time = "2025-09-21T12:13:01.197Z" }, ] +[[package]] +name = "environs" +version = "14.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "python-dotenv" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/c7/94f97e6e74482a50b5fc798856b6cc06e8d072ab05a0b74cb5d87bd0d065/environs-14.6.0.tar.gz", hash = "sha256:ed2767588deb503209ffe4dd9bb2b39311c2e4e7e27ce2c64bf62ca83328d068", size = 35563, upload-time = "2026-02-20T04:02:08.869Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/a8/c070e1340636acb38d4e6a7e45c46d168a462b48b9b3257e14ca0e5af79b/environs-14.6.0-py3-none-any.whl", hash = "sha256:f8fb3d6c6a55872b0c6db077a28f5a8c7b8984b7c32029613d44cef95cfc0812", size = 17205, upload-time = "2026-02-20T04:02:07.299Z" }, +] + [[package]] name = "et-xmlfile" version = "2.0.0" @@ -4480,6 +4598,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/9e/b7f6b33222978688afc613e25e73776076e996cb5e545e37af8e373d3b3c/multion-1.1.0-py3-none-any.whl", hash = "sha256:6a4ffa2d71c5667e41492993e7136fa71eb4b52f0c11914f3a737ffd543195ca", size = 39968, upload-time = "2024-04-25T03:43:12.22Z" }, ] +[[package]] +name = "multipart" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/d6/9c4f366d6f9bb8f8fb5eae3acac471335c39510c42b537fd515213d7d8c3/multipart-1.3.1.tar.gz", hash = "sha256:211d7cfc1a7a43e75c4d24ee0e8e0f4f61d522f1a21575303ae85333dea687bf", size = 38929, upload-time = "2026-02-27T10:17:13.7Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/ed/e1f03200ee1f0bf4a2b9b72709afefbf5319b68df654e0b84b35c65613ee/multipart-1.3.1-py3-none-any.whl", hash = "sha256:a82b59e1befe74d3d30b3d3f70efd5a2eba4d938f845dcff9faace968888ff29", size = 15061, upload-time = "2026-02-27T10:17:11.943Z" }, +] + [[package]] name = "multiprocess" version = "0.70.19" @@ -4945,6 +5072,81 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, ] +[[package]] +name = "obstore" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/8c/9ec984edd0f3b72226adfaa19b1c61b15823b35b52f311ca4af36d009d15/obstore-0.8.2.tar.gz", hash = "sha256:a467bc4e97169e2ba749981b4fd0936015428d9b8f3fb83a5528536b1b6f377f", size = 168852, upload-time = "2025-09-16T15:34:55.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e9/0a1e340ef262f225ad71f556ccba257896f85ca197f02cd228fe5e20b45a/obstore-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:49104c0d72688c180af015b02c691fbb6cf6a45b03a9d71b84059ed92dbec704", size = 3622821, upload-time = "2025-09-16T15:32:53.79Z" }, + { url = "https://files.pythonhosted.org/packages/24/86/2b53e8b0a838dbbf89ef5dfddde888770bc1a993c691698dae411a407228/obstore-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c49776abd416e4d80d003213522d82ad48ed3517bee27a6cf8ce0f0cf4e6337e", size = 3356349, upload-time = "2025-09-16T15:32:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/e8/79/1ba6dc854d7de7704a2c474d723ffeb01b6884f72eea7cbe128efc472f4a/obstore-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1636372b5e171a98369612d122ea20b955661daafa6519ed8322f4f0cb43ff74", size = 3454842, upload-time = "2025-09-16T15:32:57.072Z" }, + { url = "https://files.pythonhosted.org/packages/ca/03/ca67ccc9b9e63cfc0cd069b84437807fed4ef880be1e445b3f29d11518e0/obstore-0.8.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2efed0d86ad4ebffcbe3d0c4d84f26c2c6b20287484a0a748499c169a8e1f2c4", size = 3688363, upload-time = "2025-09-16T15:32:58.164Z" }, + { url = "https://files.pythonhosted.org/packages/a7/2f/c78eb4352d8be64a072934fe3ff2af79a1d06f4571af7c70d96f9741766b/obstore-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00c5542616dc5608de82ab6f6820633c9dbab6ff048e770fb8a5fcd1d30cd656", size = 3960133, upload-time = "2025-09-16T15:32:59.614Z" }, + { url = "https://files.pythonhosted.org/packages/4f/34/9e828d19194e227fd9f1d2dd70710da99c2bd2cd728686d59ea80be10b7c/obstore-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9df46aaf25ce80fff48c53382572adc67b6410611660b798024450281a3129", size = 3925493, upload-time = "2025-09-16T15:33:00.923Z" }, + { url = "https://files.pythonhosted.org/packages/5f/7d/9ec5967f3e2915fbc441f72c3892a7f0fb3618e3ae5c8a44181ce4aa641c/obstore-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccf0f03a7fe453fb8640611c922bce19f021c6aaeee6ee44d6d8fb57db6be48", size = 3769401, upload-time = "2025-09-16T15:33:02.373Z" }, + { url = "https://files.pythonhosted.org/packages/85/bf/00b65013068bde630a7369610a2dae4579315cd6ce82d30e3d23315cf308/obstore-0.8.2-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:ddfbfadc88c5e9740b687ef0833384329a56cea07b34f44e1c4b00a0e97d94a9", size = 3534383, upload-time = "2025-09-16T15:33:03.903Z" }, + { url = "https://files.pythonhosted.org/packages/52/39/1b684fd96c9a33974fc52f417c52b42c1d50df40b44e588853c4a14d9ab1/obstore-0.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:53ad53bb16e64102f39559ec470efd78a5272b5e3b84c53aa0423993ac5575c1", size = 3697939, upload-time = "2025-09-16T15:33:05.355Z" }, + { url = "https://files.pythonhosted.org/packages/85/58/93a2c78935f17fde7e22842598a6373e46a9c32d0243ec3b26b5da92df27/obstore-0.8.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b0b905b46354db0961ab818cad762b9c1ac154333ae5d341934c90635a6bd7ab", size = 3681746, upload-time = "2025-09-16T15:33:09.344Z" }, + { url = "https://files.pythonhosted.org/packages/38/90/225c2972338d18f92e7a56f71e34df6935b0b1bd7458bb6a0d2bd4d48f92/obstore-0.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fee235694406ebb2dc4178752cf5587f471d6662659b082e9786c716a0a9465c", size = 3765156, upload-time = "2025-09-16T15:33:10.457Z" }, + { url = "https://files.pythonhosted.org/packages/79/eb/aca27e895bfcbbcd2bf05ea6a2538a94b718e6f6d72986e16ab158b753ec/obstore-0.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c36faf7ace17dd0832aa454118a63ea21862e3d34f71b9297d0c788d00f4985", size = 3941190, upload-time = "2025-09-16T15:33:11.59Z" }, + { url = "https://files.pythonhosted.org/packages/33/ce/c8251a397e7507521768f05bc355b132a0daaff3739e861e51fa6abd821e/obstore-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:948a1db1d34f88cfc7ab7e0cccdcfd84cf3977365634599c95ba03b4ef80d1c4", size = 3970041, upload-time = "2025-09-16T15:33:13.035Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c4/018f90701f1e5ea3fbd57f61463f42e1ef5218e548d3adcf12b6be021c34/obstore-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2edaa97687c191c5324bb939d72f6fe86a7aa8191c410f1648c14e8296d05c1c", size = 3622568, upload-time = "2025-09-16T15:33:14.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/62/72dd1e7d52fc554bb1fdb1a9499bda219cf3facea5865a1d97fdc00b3a1b/obstore-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c4fb7ef8108f08d14edc8bec9e9a6a2e5c4d14eddb8819f5d0da498aff6e8888", size = 3356109, upload-time = "2025-09-16T15:33:15.315Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ae/089fe5b9207091252fe5ce352551214f04560f85eb8f2cc4f716a6a1a57e/obstore-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fda8f658c0edf799ab1e264f9b12c7c184cd09a5272dc645d42e987810ff2772", size = 3454588, upload-time = "2025-09-16T15:33:16.421Z" }, + { url = "https://files.pythonhosted.org/packages/ea/10/1865ae2d1ba45e8ae85fb0c1aada2dc9533baf60c4dfe74dab905348d74a/obstore-0.8.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87fe2bc15ce4051ecb56abd484feca323c2416628beb62c1c7b6712114564d6e", size = 3688627, upload-time = "2025-09-16T15:33:17.604Z" }, + { url = "https://files.pythonhosted.org/packages/a6/09/5d7ba6d0aeac563ea5f5586401c677bace4f782af83522b1fdf15430e152/obstore-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2482aa2562ab6a4ca40250b26bea33f8375b59898a9b5615fd412cab81098123", size = 3959896, upload-time = "2025-09-16T15:33:18.789Z" }, + { url = "https://files.pythonhosted.org/packages/16/15/2b3eda59914761a9ff4d840e2daec5697fd29b293bd18d3dc11c593aed06/obstore-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4153b928f5d2e9c6cb645e83668a53e0b42253d1e8bcb4e16571fc0a1434599a", size = 3933162, upload-time = "2025-09-16T15:33:19.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/7a/5fc63b41526587067537fb1498c59a210884664c65ccf0d1f8f823b0875a/obstore-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbfa9c38620cc191be98c8b5558c62071e495dc6b1cc724f38293ee439aa9f92", size = 3769605, upload-time = "2025-09-16T15:33:21.389Z" }, + { url = "https://files.pythonhosted.org/packages/77/4e/2208ab6e1fc021bf8b7e117249a10ab75d0ed24e0f2de1a8d7cd67d885b5/obstore-0.8.2-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:0822836eae8d52499f10daef17f26855b4c123119c6eb984aa4f2d525ec2678d", size = 3534396, upload-time = "2025-09-16T15:33:22.574Z" }, + { url = "https://files.pythonhosted.org/packages/1d/8f/a0e2882edd6bd285c82b8a5851c4ecf386c93fe75b6e340d5d9d30e809fc/obstore-0.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ef6435dfd586d83b4f778e7927a5d5b0d8b771e9ba914bc809a13d7805410e6", size = 3697777, upload-time = "2025-09-16T15:33:23.723Z" }, + { url = "https://files.pythonhosted.org/packages/94/78/ebf0c33bed5c9a8eed3b00eefafbcc0a687eeb1e05451c76fcf199d29ff8/obstore-0.8.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0f2cba91f4271ca95a932a51aa8dda1537160342b33f7836c75e1eb9d40621a2", size = 3681546, upload-time = "2025-09-16T15:33:24.935Z" }, + { url = "https://files.pythonhosted.org/packages/af/21/9bf4fb9e53fd5f01af580b6538de2eae857e31d24b0ebfc4d916c306a1e4/obstore-0.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:23c876d603af0627627808d19a58d43eb5d8bfd02eecd29460bc9a58030fed55", size = 3765336, upload-time = "2025-09-16T15:33:26.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3c/7f6895c23719482d231b2d6ed328e3223fdf99785f6850fba8d2fc5a86ee/obstore-0.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ff3c4b5d07629b70b9dee494cd6b94fff8465c3864752181a1cb81a77190fe42", size = 3941142, upload-time = "2025-09-16T15:33:27.275Z" }, + { url = "https://files.pythonhosted.org/packages/93/a4/56ccdb756161595680a28f4b0def2c04f7048ffacf128029be8394367b26/obstore-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:aadb2cb72de7227d07f4570f82729625ffc77522fadca5cf13c3a37fbe8c8de9", size = 3970172, upload-time = "2025-09-16T15:33:28.393Z" }, + { url = "https://files.pythonhosted.org/packages/2b/dc/60fefbb5736e69eab56657bca04ca64dc07fdeccb3814164a31b62ad066b/obstore-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bb70ce297a47392b1d9a3e310f18d59cd5ebbb9453428210fef02ed60e4d75d1", size = 3612955, upload-time = "2025-09-16T15:33:29.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8b/844e8f382e5a12b8a3796a05d76a03e12c7aedc13d6900419e39207d7868/obstore-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1619bf618428abf1f607e0b219b2e230a966dcf697b717deccfa0983dd91f646", size = 3346564, upload-time = "2025-09-16T15:33:30.698Z" }, + { url = "https://files.pythonhosted.org/packages/89/73/8537f99e09a38a54a6a15ede907aa25d4da089f767a808f0b2edd9c03cec/obstore-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4605c3ed7c9515aeb4c619b5f7f2c9986ed4a79fe6045e536b5e59b804b1476", size = 3460809, upload-time = "2025-09-16T15:33:31.837Z" }, + { url = "https://files.pythonhosted.org/packages/b4/99/7714dec721e43f521d6325a82303a002cddad089437640f92542b84e9cc8/obstore-0.8.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce42670417876dd8668cbb8659e860e9725e5f26bbc86449fd259970e2dd9d18", size = 3692081, upload-time = "2025-09-16T15:33:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/4ac4175fe95a24c220a96021c25c432bcc0c0212f618be0737184eebbaad/obstore-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a3e893b2a06585f651c541c1972fe1e3bf999ae2a5fda052ee55eb7e6516f5", size = 3957466, upload-time = "2025-09-16T15:33:34.528Z" }, + { url = "https://files.pythonhosted.org/packages/4e/04/caa288fb735484fc5cb019bdf3d896eaccfae0ac4622e520d05692c46790/obstore-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08462b32f95a9948ed56ed63e88406e2e5a4cae1fde198f9682e0fb8487100ed", size = 3951293, upload-time = "2025-09-16T15:33:35.733Z" }, + { url = "https://files.pythonhosted.org/packages/44/2f/d380239da2d6a1fda82e17df5dae600a404e8a93a065784518ff8325d5f6/obstore-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0bf7763292a8fc47d01cd66e6f19002c5c6ad4b3ed4e6b2729f5e190fa8a0d", size = 3766199, upload-time = "2025-09-16T15:33:36.904Z" }, + { url = "https://files.pythonhosted.org/packages/28/41/d391be069d3da82969b54266948b2582aeca5dd735abeda4d63dba36e07b/obstore-0.8.2-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:bcd47f8126cb192cbe86942b8f73b1c45a651ce7e14c9a82c5641dfbf8be7603", size = 3529678, upload-time = "2025-09-16T15:33:38.221Z" }, + { url = "https://files.pythonhosted.org/packages/b9/4c/4862fdd1a3abde459ee8eea699b1797df638a460af235b18ca82c8fffb72/obstore-0.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57eda9fd8c757c3b4fe36cf3918d7e589cc1286591295cc10b34122fa36dd3fd", size = 3698079, upload-time = "2025-09-16T15:33:39.696Z" }, + { url = "https://files.pythonhosted.org/packages/68/ca/014e747bc53b570059c27e3565b2316fbe5c107d4134551f4cd3e24aa667/obstore-0.8.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ea44442aad8992166baa69f5069750979e4c5d9ffce772e61565945eea5774b9", size = 3687154, upload-time = "2025-09-16T15:33:40.92Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/6db5f8edd93028e5b8bfbeee15e6bd3e56f72106107d31cb208b57659de4/obstore-0.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:41496a3ab8527402db4142aaaf0d42df9d7d354b13ba10d9c33e0e48dd49dd96", size = 3773444, upload-time = "2025-09-16T15:33:42.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/e5/c9e2cc540689c873beb61246e1615d6e38301e6a34dec424f5a5c63c1afd/obstore-0.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43da209803f052df96c7c3cbec512d310982efd2407e4a435632841a51143170", size = 3939315, upload-time = "2025-09-16T15:33:43.252Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c9/bb53280ca50103c1ffda373cdc9b0f835431060039c2897cbc87ddd92e42/obstore-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:1836f5dcd49f9f2950c75889ab5c51fb290d3ea93cdc39a514541e0be3af016e", size = 3978234, upload-time = "2025-09-16T15:33:44.393Z" }, + { url = "https://files.pythonhosted.org/packages/f0/5d/8c3316cc958d386d5e6ab03e9db9ddc27f8e2141cee4a6777ae5b92f3aac/obstore-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:212f033e53fe6e53d64957923c5c88949a400e9027f7038c705ec2e9038be563", size = 3612027, upload-time = "2025-09-16T15:33:45.6Z" }, + { url = "https://files.pythonhosted.org/packages/ea/4d/699359774ce6330130536d008bfc32827fab0c25a00238d015a5974a3d1d/obstore-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bee21fa4ba148d08fa90e47a96df11161661ed31e09c056a373cb2154b0f2852", size = 3344686, upload-time = "2025-09-16T15:33:47.185Z" }, + { url = "https://files.pythonhosted.org/packages/82/37/55437341f10512906e02fd9fa69a8a95ad3f2f6a916d3233fda01763d110/obstore-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c66594b59832ff1ced4c72575d9beb8b5f9b4e404ac1150a42bfb226617fd50", size = 3459860, upload-time = "2025-09-16T15:33:48.382Z" }, + { url = "https://files.pythonhosted.org/packages/7a/51/4245a616c94ee4851965e33f7a563ab4090cc81f52cc73227ff9ceca2e46/obstore-0.8.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:089f33af5c2fe132d00214a0c1f40601b28f23a38e24ef9f79fb0576f2730b74", size = 3691648, upload-time = "2025-09-16T15:33:49.524Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/4e2fb24171e3ca3641a4653f006be826e7e17634b11688a5190553b00b83/obstore-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d87f658dfd340d5d9ea2d86a7c90d44da77a0db9e00c034367dca335735110cf", size = 3956867, upload-time = "2025-09-16T15:33:51.082Z" }, + { url = "https://files.pythonhosted.org/packages/42/f5/b703115361c798c9c1744e1e700d5908d904a8c2e2bd38bec759c9ffb469/obstore-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2e4fa92828c4fbc2d487f3da2d3588701a1b67d9f6ca3c97cc2afc912e9c63", size = 3950599, upload-time = "2025-09-16T15:33:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/53/20/08c6dc0f20c1394e2324b9344838e4e7af770cdcb52c30757a475f50daeb/obstore-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab440e89c5c37a8ec230857dd65147d4b923e0cada33297135d05e0f937d696a", size = 3765865, upload-time = "2025-09-16T15:33:53.291Z" }, + { url = "https://files.pythonhosted.org/packages/77/20/77907765e29b2eba6bd8821872284d91170d7084f670855b2dfcb249ea14/obstore-0.8.2-cp313-cp313-manylinux_2_24_aarch64.whl", hash = "sha256:b9beed107c5c9cd995d4a73263861fcfbc414d58773ed65c14f80eb18258a932", size = 3529807, upload-time = "2025-09-16T15:33:54.535Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f5/f629d39cc30d050f52b1bf927e4d65c1cc7d7ffbb8a635cd546b5c5219a0/obstore-0.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b75b4e7746292c785e31edcd5aadc8b758238372a19d4c5e394db5c305d7d175", size = 3693629, upload-time = "2025-09-16T15:33:56.016Z" }, + { url = "https://files.pythonhosted.org/packages/30/ff/106763fd10f2a1cb47f2ef1162293c78ad52f4e73223d8d43fc6b755445d/obstore-0.8.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f33e6c366869d05ab0b7f12efe63269e631c5450d95d6b4ba4c5faf63f69de70", size = 3686176, upload-time = "2025-09-16T15:33:57.247Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0c/d2ccb6f32feeca906d5a7c4255340df5262af8838441ca06c9e4e37b67d5/obstore-0.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:12c885a9ce5ceb09d13cc186586c0c10b62597eff21b985f6ce8ff9dab963ad3", size = 3773081, upload-time = "2025-09-16T15:33:58.475Z" }, + { url = "https://files.pythonhosted.org/packages/fa/79/40d1cc504cefc89c9b3dd8874287f3fddc7d963a8748d6dffc5880222013/obstore-0.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4accc883b93349a81c9931e15dd318cc703b02bbef2805d964724c73d006d00e", size = 3938589, upload-time = "2025-09-16T15:33:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/14/dd/916c6777222db3271e9fb3cf9a97ed92b3a9b3e465bdeec96de9ab809d53/obstore-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ec850adf9980e5788a826ccfd5819989724e2a2f712bfa3258e85966c8d9981e", size = 3977768, upload-time = "2025-09-16T15:34:01.25Z" }, + { url = "https://files.pythonhosted.org/packages/c3/37/14bae1f5bf4369027abc5315cdba2428ad4c16e2fd3bd5d35b7ee584aa0c/obstore-0.8.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6ea04118980a9c22fc8581225ff4507b6a161baf8949d728d96e68326ebaab59", size = 3624857, upload-time = "2025-09-16T15:34:35.601Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c4/8cba91629aa20479ba86a57c2c2b3bc0a54fc6a31a4594014213603efae6/obstore-0.8.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5f33a7570b6001b54252260fbec18c3f6d21e25d3ec57e9b6c5e7330e8290eb2", size = 3355999, upload-time = "2025-09-16T15:34:36.954Z" }, + { url = "https://files.pythonhosted.org/packages/f2/10/3e40557d6d9c38c5a0f7bac1508209b9dbb8c4da918ddfa9326ba9a1de3f/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11fa78dfb749edcf5a041cd6db20eae95b3e8b09dfdd9b38d14939da40e7c115", size = 3457322, upload-time = "2025-09-16T15:34:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/dcf7988350c286683698cbdd8c15498aec43cbca72eaabad06fd77f0f34a/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872bc0921ff88305884546ba05e258ccd95672a03d77db123f0d0563fd3c000b", size = 3689452, upload-time = "2025-09-16T15:34:39.638Z" }, + { url = "https://files.pythonhosted.org/packages/97/02/643eb2ede58933e47bdbc92786058c83d9aa569826d5bf6e83362d24a27a/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72556a2fbf018edd921286283e5c7eec9f69a21c6d12516d8a44108eceaa526a", size = 3961171, upload-time = "2025-09-16T15:34:41.232Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5d/c0b515df6089d0f54109de8031a6f6ed31271361948bee90ab8271d22f79/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75fa1abf21499dfcfb0328941a175f89a9aa58245bf00e3318fe928e4b10d297", size = 3935988, upload-time = "2025-09-16T15:34:42.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/97/114d7bc172bb846472181d6fa3e950172ee1b1ccd11291777303c499dbdd/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f54f72f30cd608c4399679781c884bf8a0e816c1977a2fac993bf5e1fb30609f", size = 3771781, upload-time = "2025-09-16T15:34:44.405Z" }, + { url = "https://files.pythonhosted.org/packages/c3/43/4aa6de6dc406ef5e109b21a5614c34999575de638254deb456703fae24aa/obstore-0.8.2-pp310-pypy310_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:b044ebf1bf7b8f7b0ca309375c1cd9e140be79e072ae8c70bbd5d9b2ad1f7678", size = 3536689, upload-time = "2025-09-16T15:34:45.649Z" }, + { url = "https://files.pythonhosted.org/packages/06/a5/870ce541aa1a9ee1d9c3e99c2187049bf5a4d278ee9678cc449aae0a4e68/obstore-0.8.2-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:b1326cd2288b64d6fe8857cc22d3a8003b802585fc0741eff2640a8dc35e8449", size = 3700560, upload-time = "2025-09-16T15:34:47.252Z" }, + { url = "https://files.pythonhosted.org/packages/7d/93/76a5fc3833aaa833b4152950d9cdfd328493a48316c24e32ddefe9b8870f/obstore-0.8.2-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:ba6863230648a9b0e11502d2745d881cf74262720238bc0093c3eabd22a3b24c", size = 3683450, upload-time = "2025-09-16T15:34:49.589Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/4c389362c187630c42f61ef9214e67fc336e44b8aafc47cf49ba9ab8007d/obstore-0.8.2-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:887615da9eeefeb2df849d87c380e04877487aa29dbeb367efc3f17f667470d3", size = 3766628, upload-time = "2025-09-16T15:34:51.937Z" }, + { url = "https://files.pythonhosted.org/packages/03/12/08547e63edf2239ec6660af434602208ab6f394955ef660a6edda13a0bee/obstore-0.8.2-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4eec1fb32ffa4fb9fe9ad584611ff031927a5c22732b56075ee7204f0e35ebdf", size = 3944069, upload-time = "2025-09-16T15:34:54.108Z" }, +] + [[package]] name = "ocrmac" version = "1.0.1" From 01b84379409574e4bd8f292bd693bfd50375d77b Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 01:59:42 +0800 Subject: [PATCH 14/32] fix: handle BaseModel result in guardrail retry loop The guardrail retry path passed a Pydantic object directly to TaskOutput.raw (which expects a string), causing a ValidationError when output_pydantic is set and a guardrail fails. Mirror the BaseModel check from the initial execution path into both sync and async retry loops. Closes #5544 (part 1) --- lib/crewai/src/crewai/task.py | 36 +++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/crewai/src/crewai/task.py b/lib/crewai/src/crewai/task.py index e12caa2af..1cac87cb8 100644 --- a/lib/crewai/src/crewai/task.py +++ b/lib/crewai/src/crewai/task.py @@ -1241,12 +1241,26 @@ Follow these guidelines: tools=tools, ) - pydantic_output, json_output = self._export_output(result) + if isinstance(result, BaseModel): + raw = result.model_dump_json() + if self.output_pydantic: + pydantic_output = result + json_output = None + elif self.output_json: + pydantic_output = None + json_output = result.model_dump() + else: + pydantic_output = None + json_output = None + else: + raw = result + pydantic_output, json_output = self._export_output(result) + task_output = TaskOutput( name=self.name or self.description, description=self.description, expected_output=self.expected_output, - raw=result, + raw=raw, pydantic=pydantic_output, json_dict=json_output, agent=agent.role, @@ -1337,12 +1351,26 @@ Follow these guidelines: tools=tools, ) - pydantic_output, json_output = self._export_output(result) + if isinstance(result, BaseModel): + raw = result.model_dump_json() + if self.output_pydantic: + pydantic_output = result + json_output = None + elif self.output_json: + pydantic_output = None + json_output = result.model_dump() + else: + pydantic_output = None + json_output = None + else: + raw = result + pydantic_output, json_output = self._export_output(result) + task_output = TaskOutput( name=self.name or self.description, description=self.description, expected_output=self.expected_output, - raw=result, + raw=raw, pydantic=pydantic_output, json_dict=json_output, agent=agent.role, From d6d04717c264c6ee7895beccbe30353501bd2f4c Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 03:15:06 +0800 Subject: [PATCH 15/32] fix: serialize Task class-reference fields for checkpointing Task fields that store class references (output_pydantic, output_json, response_model, converter_cls) caused PydanticSerializationError when RuntimeState serialized Crew entities during checkpointing. Serialize to model_json_schema() and hydrate back via create_model_from_schema. --- lib/crewai/src/crewai/task.py | 49 ++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/crewai/src/crewai/task.py b/lib/crewai/src/crewai/task.py index 1cac87cb8..04bbf3718 100644 --- a/lib/crewai/src/crewai/task.py +++ b/lib/crewai/src/crewai/task.py @@ -32,6 +32,7 @@ from pydantic import ( field_validator, model_validator, ) +from pydantic.functional_serializers import PlainSerializer from pydantic_core import PydanticCustomError from typing_extensions import Self @@ -86,6 +87,22 @@ from crewai.utilities.printer import PRINTER from crewai.utilities.string_utils import interpolate_only +def _serialize_model_class(v: type[BaseModel] | None) -> dict[str, Any] | None: + """Serialize a Pydantic model class reference to its JSON schema.""" + return v.model_json_schema() if v else None + + +def _deserialize_model_class(v: Any) -> type[BaseModel] | None: + """Hydrate a model class reference from checkpoint data.""" + if v is None or isinstance(v, type): + return v + if isinstance(v, dict): + from crewai.utilities.pydantic_schema_utils import create_model_from_schema + + return create_model_from_schema(v) + return None + + class Task(BaseModel): """Class that represents a task to be executed. @@ -141,15 +158,33 @@ class Task(BaseModel): description="Whether the task should be executed asynchronously or not.", default=False, ) - output_json: type[BaseModel] | None = Field( + output_json: Annotated[ + type[BaseModel] | None, + BeforeValidator(_deserialize_model_class), + PlainSerializer( + _serialize_model_class, return_type=dict | None, when_used="json" + ), + ] = Field( description="A Pydantic model to be used to create a JSON output.", default=None, ) - output_pydantic: type[BaseModel] | None = Field( + output_pydantic: Annotated[ + type[BaseModel] | None, + BeforeValidator(_deserialize_model_class), + PlainSerializer( + _serialize_model_class, return_type=dict | None, when_used="json" + ), + ] = Field( description="A Pydantic model to be used to create a Pydantic output.", default=None, ) - response_model: type[BaseModel] | None = Field( + response_model: Annotated[ + type[BaseModel] | None, + BeforeValidator(_deserialize_model_class), + PlainSerializer( + _serialize_model_class, return_type=dict | None, when_used="json" + ), + ] = Field( description="A Pydantic model for structured LLM outputs using native provider features.", default=None, ) @@ -189,7 +224,13 @@ class Task(BaseModel): description="Whether the task should instruct the agent to return the final answer formatted in Markdown", default=False, ) - converter_cls: type[Converter] | None = Field( + converter_cls: Annotated[ + type[Converter] | None, + BeforeValidator(lambda v: v if v is None or isinstance(v, type) else None), + PlainSerializer( + _serialize_model_class, return_type=dict | None, when_used="json" + ), + ] = Field( description="A converter class used to export structured output", default=None, ) From 874405b825845cbc1f23b5de646ecf65e4b02529 Mon Sep 17 00:00:00 2001 From: iris-clawd Date: Mon, 20 Apr 2026 20:09:37 -0300 Subject: [PATCH 16/32] =?UTF-8?q?docs:=20Add=20'Build=20with=20AI'=20page?= =?UTF-8?q?=20=E2=80=94=20AI-native=20docs=20for=20coding=20agents=20(#555?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add Build with AI page for coding agents and AI assistants * docs: add Build with AI section to README * docs: trim README Build with AI section to skills install only * docs: add skills.sh reference link for npx install * docs: add coding agent logos to Build with AI page --------- Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> --- README.md | 17 ++ docs/docs.json | 33 ++- docs/en/guides/coding-tools/build-with-ai.mdx | 206 ++++++++++++++++++ 3 files changed, 245 insertions(+), 11 deletions(-) create mode 100644 docs/en/guides/coding-tools/build-with-ai.mdx diff --git a/README.md b/README.md index c832d0025..bcb6634aa 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ intelligent automations. ## Table of contents +- [Build with AI](#build-with-ai) - [Why CrewAI?](#why-crewai) - [Getting Started](#getting-started) - [Key Features](#key-features) @@ -101,6 +102,22 @@ intelligent automations. - [Telemetry](#telemetry) - [License](#license) +## Build with AI + +Using an AI coding agent? Teach it CrewAI best practices in one command: + +**Claude Code:** +```shell +/plugin marketplace add crewAIInc/skills +``` + +**Cursor, Codex, Windsurf, and others ([skills.sh](https://skills.sh/crewaiinc/skills)):** +```shell +npx skills add crewaiinc/skills +``` + +This installs the official [CrewAI Skills](https://github.com/crewAIInc/skills) — structured instructions that teach coding agents how to scaffold Flows, configure Crews, design agents and tasks, and follow CrewAI patterns. + ## Why CrewAI?
diff --git a/docs/docs.json b/docs/docs.json index f727c3e84..af0854be1 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -127,7 +127,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -601,7 +602,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -1075,7 +1077,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -1549,7 +1552,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -2023,7 +2027,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -2497,7 +2502,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -2969,7 +2975,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -3441,7 +3448,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -3914,7 +3922,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -4388,7 +4397,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { @@ -4860,7 +4870,8 @@ "group": "Coding Tools", "icon": "terminal", "pages": [ - "en/guides/coding-tools/agents-md" + "en/guides/coding-tools/agents-md", + "en/guides/coding-tools/build-with-ai" ] }, { diff --git a/docs/en/guides/coding-tools/build-with-ai.mdx b/docs/en/guides/coding-tools/build-with-ai.mdx new file mode 100644 index 000000000..744ad1bf4 --- /dev/null +++ b/docs/en/guides/coding-tools/build-with-ai.mdx @@ -0,0 +1,206 @@ +--- +title: "Build with AI" +description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +icon: robot +mode: "wide" +--- + +# Build with AI + +CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. + +### Supported Coding Agents + + + + + + + + + + + This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + + +--- + +## 1. Skills — Teach Your Agent CrewAI + +**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. + + + + Anthropic + CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: + ``` + /plugin marketplace add crewAIInc/skills + ``` + + + Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + ```shell + npx skills add crewaiinc/skills + ``` + Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + + + + + + Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + + The skill pack teaches your agent: + - **Flows** — stateful apps, steps, and crew kickoffs + - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation + - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools + - **Project layout** — CLI scaffolds and repo conventions + - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + + Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + + + + + How skills work in CrewAI agents — injection, activation, and patterns. + + + Overview of the crewAIInc/skills pack and what it includes. + + + Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + + Official listing — skills, install stats, and audits. + + + +--- + +## 2. llms.txt — Machine-Readable Docs + +CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. + +``` +https://docs.crewai.com/llms.txt +``` + + + + [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + CrewAI's `llms.txt` is **already live** — your agent can use it right now. + + + Point your coding agent at the URL when it needs CrewAI reference docs: + + ``` + Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. + ``` + + Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + + + - **No scraping required** — clean, structured content in one request + - **Always up-to-date** — served directly from docs.crewai.com + - **Optimized for LLMs** — formatted for context windows, not browsers + - **Complements skills** — skills teach patterns, llms.txt provides reference + + + +--- + +## 3. Deploy to Enterprise + +Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. + + + + Scaffold and test your crew or flow: + ```bash + crewai create crew my_crew + cd my_crew + crewai run + ``` + + + Ensure your project structure is ready: + ```bash + crewai deploy --prepare + ``` + See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + + + Push to the CrewAI AMP platform: + ```bash + crewai deploy + ``` + You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + + + Your deployed crew gets a REST API endpoint. Integrate it into any application: + ```bash + curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ + -H "Authorization: Bearer $CREWAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"inputs": {"topic": "AI agents"}}' + ``` + + + + + + Full deployment guide — CLI, GitHub, and Crew Studio methods. + + + Platform overview — what AMP provides for production crews. + + + +--- + +## 4. Enterprise Features + +CrewAI AMP is built for production teams. Here's what you get beyond deployment. + + + + Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + + No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + + + Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + + SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + + Publish and share custom tools across your organization. Install community tools from the registry. + + + Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + + + + + AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + + - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production + - **Factory (self-hosted)** — run on your own infrastructure for full data control + - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + + Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + + + + Sign up and deploy your first crew to production. + From 3b01da9ad90113c248608014e6a90f3a5562f1d1 Mon Sep 17 00:00:00 2001 From: iris-clawd Date: Mon, 20 Apr 2026 23:43:37 -0300 Subject: [PATCH 17/32] docs: add Build with AI to Get Started nav + page files for all languages (en, ko, pt-BR, ar) (#5567) --- docs/ar/guides/coding-tools/build-with-ai.mdx | 206 ++++++++++++++++++ docs/docs.json | 44 ++++ docs/ko/guides/coding-tools/build-with-ai.mdx | 206 ++++++++++++++++++ .../guides/coding-tools/build-with-ai.mdx | 206 ++++++++++++++++++ 4 files changed, 662 insertions(+) create mode 100644 docs/ar/guides/coding-tools/build-with-ai.mdx create mode 100644 docs/ko/guides/coding-tools/build-with-ai.mdx create mode 100644 docs/pt-BR/guides/coding-tools/build-with-ai.mdx diff --git a/docs/ar/guides/coding-tools/build-with-ai.mdx b/docs/ar/guides/coding-tools/build-with-ai.mdx new file mode 100644 index 000000000..744ad1bf4 --- /dev/null +++ b/docs/ar/guides/coding-tools/build-with-ai.mdx @@ -0,0 +1,206 @@ +--- +title: "Build with AI" +description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +icon: robot +mode: "wide" +--- + +# Build with AI + +CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. + +### Supported Coding Agents + + + + + + + + + + + This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + + +--- + +## 1. Skills — Teach Your Agent CrewAI + +**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. + + + + Anthropic + CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: + ``` + /plugin marketplace add crewAIInc/skills + ``` + + + Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + ```shell + npx skills add crewaiinc/skills + ``` + Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + + + + + + Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + + The skill pack teaches your agent: + - **Flows** — stateful apps, steps, and crew kickoffs + - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation + - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools + - **Project layout** — CLI scaffolds and repo conventions + - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + + Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + + + + + How skills work in CrewAI agents — injection, activation, and patterns. + + + Overview of the crewAIInc/skills pack and what it includes. + + + Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + + Official listing — skills, install stats, and audits. + + + +--- + +## 2. llms.txt — Machine-Readable Docs + +CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. + +``` +https://docs.crewai.com/llms.txt +``` + + + + [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + CrewAI's `llms.txt` is **already live** — your agent can use it right now. + + + Point your coding agent at the URL when it needs CrewAI reference docs: + + ``` + Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. + ``` + + Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + + + - **No scraping required** — clean, structured content in one request + - **Always up-to-date** — served directly from docs.crewai.com + - **Optimized for LLMs** — formatted for context windows, not browsers + - **Complements skills** — skills teach patterns, llms.txt provides reference + + + +--- + +## 3. Deploy to Enterprise + +Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. + + + + Scaffold and test your crew or flow: + ```bash + crewai create crew my_crew + cd my_crew + crewai run + ``` + + + Ensure your project structure is ready: + ```bash + crewai deploy --prepare + ``` + See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + + + Push to the CrewAI AMP platform: + ```bash + crewai deploy + ``` + You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + + + Your deployed crew gets a REST API endpoint. Integrate it into any application: + ```bash + curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ + -H "Authorization: Bearer $CREWAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"inputs": {"topic": "AI agents"}}' + ``` + + + + + + Full deployment guide — CLI, GitHub, and Crew Studio methods. + + + Platform overview — what AMP provides for production crews. + + + +--- + +## 4. Enterprise Features + +CrewAI AMP is built for production teams. Here's what you get beyond deployment. + + + + Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + + No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + + + Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + + SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + + Publish and share custom tools across your organization. Install community tools from the registry. + + + Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + + + + + AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + + - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production + - **Factory (self-hosted)** — run on your own infrastructure for full data control + - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + + Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + + + + Sign up and deploy your first crew to production. + diff --git a/docs/docs.json b/docs/docs.json index af0854be1..e2fe48ff0 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -79,6 +79,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -554,6 +555,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -1029,6 +1031,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -1504,6 +1507,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -1979,6 +1983,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -2454,6 +2459,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -2927,6 +2933,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -3400,6 +3407,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -3874,6 +3882,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -4349,6 +4358,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -4822,6 +4832,7 @@ "group": "Get Started", "pages": [ "en/introduction", + "en/guides/coding-tools/build-with-ai", "en/skills", "en/installation", "en/quickstart" @@ -5328,6 +5339,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -5786,6 +5798,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -6244,6 +6257,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -6702,6 +6716,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -7160,6 +7175,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -7618,6 +7634,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -8075,6 +8092,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -8532,6 +8550,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -8989,6 +9008,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -9445,6 +9465,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -9901,6 +9922,7 @@ "group": "Começando", "pages": [ "pt-BR/introduction", + "pt-BR/guides/coding-tools/build-with-ai", "pt-BR/skills", "pt-BR/installation", "pt-BR/quickstart" @@ -10388,6 +10410,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -10858,6 +10881,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -11328,6 +11352,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -11798,6 +11823,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -12268,6 +12294,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -12738,6 +12765,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -13207,6 +13235,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -13676,6 +13705,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -14145,6 +14175,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -14613,6 +14644,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -15081,6 +15113,7 @@ "group": "시작 안내", "pages": [ "ko/introduction", + "ko/guides/coding-tools/build-with-ai", "ko/skills", "ko/installation", "ko/quickstart" @@ -15580,6 +15613,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -16050,6 +16084,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -16520,6 +16555,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -16990,6 +17026,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -17460,6 +17497,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -17930,6 +17968,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -18399,6 +18438,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -18868,6 +18908,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -19337,6 +19378,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -19805,6 +19847,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" @@ -20273,6 +20316,7 @@ "group": "البدء", "pages": [ "ar/introduction", + "ar/guides/coding-tools/build-with-ai", "ar/skills", "ar/installation", "ar/quickstart" diff --git a/docs/ko/guides/coding-tools/build-with-ai.mdx b/docs/ko/guides/coding-tools/build-with-ai.mdx new file mode 100644 index 000000000..744ad1bf4 --- /dev/null +++ b/docs/ko/guides/coding-tools/build-with-ai.mdx @@ -0,0 +1,206 @@ +--- +title: "Build with AI" +description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +icon: robot +mode: "wide" +--- + +# Build with AI + +CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. + +### Supported Coding Agents + + + + + + + + + + + This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + + +--- + +## 1. Skills — Teach Your Agent CrewAI + +**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. + + + + Anthropic + CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: + ``` + /plugin marketplace add crewAIInc/skills + ``` + + + Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + ```shell + npx skills add crewaiinc/skills + ``` + Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + + + + + + Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + + The skill pack teaches your agent: + - **Flows** — stateful apps, steps, and crew kickoffs + - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation + - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools + - **Project layout** — CLI scaffolds and repo conventions + - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + + Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + + + + + How skills work in CrewAI agents — injection, activation, and patterns. + + + Overview of the crewAIInc/skills pack and what it includes. + + + Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + + Official listing — skills, install stats, and audits. + + + +--- + +## 2. llms.txt — Machine-Readable Docs + +CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. + +``` +https://docs.crewai.com/llms.txt +``` + + + + [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + CrewAI's `llms.txt` is **already live** — your agent can use it right now. + + + Point your coding agent at the URL when it needs CrewAI reference docs: + + ``` + Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. + ``` + + Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + + + - **No scraping required** — clean, structured content in one request + - **Always up-to-date** — served directly from docs.crewai.com + - **Optimized for LLMs** — formatted for context windows, not browsers + - **Complements skills** — skills teach patterns, llms.txt provides reference + + + +--- + +## 3. Deploy to Enterprise + +Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. + + + + Scaffold and test your crew or flow: + ```bash + crewai create crew my_crew + cd my_crew + crewai run + ``` + + + Ensure your project structure is ready: + ```bash + crewai deploy --prepare + ``` + See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + + + Push to the CrewAI AMP platform: + ```bash + crewai deploy + ``` + You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + + + Your deployed crew gets a REST API endpoint. Integrate it into any application: + ```bash + curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ + -H "Authorization: Bearer $CREWAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"inputs": {"topic": "AI agents"}}' + ``` + + + + + + Full deployment guide — CLI, GitHub, and Crew Studio methods. + + + Platform overview — what AMP provides for production crews. + + + +--- + +## 4. Enterprise Features + +CrewAI AMP is built for production teams. Here's what you get beyond deployment. + + + + Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + + No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + + + Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + + SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + + Publish and share custom tools across your organization. Install community tools from the registry. + + + Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + + + + + AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + + - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production + - **Factory (self-hosted)** — run on your own infrastructure for full data control + - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + + Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + + + + Sign up and deploy your first crew to production. + diff --git a/docs/pt-BR/guides/coding-tools/build-with-ai.mdx b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx new file mode 100644 index 000000000..744ad1bf4 --- /dev/null +++ b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx @@ -0,0 +1,206 @@ +--- +title: "Build with AI" +description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +icon: robot +mode: "wide" +--- + +# Build with AI + +CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. + +### Supported Coding Agents + + + + + + + + + + + This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + + +--- + +## 1. Skills — Teach Your Agent CrewAI + +**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. + + + + Anthropic + CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: + ``` + /plugin marketplace add crewAIInc/skills + ``` + + + Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + ```shell + npx skills add crewaiinc/skills + ``` + Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + + + + + + Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + + The skill pack teaches your agent: + - **Flows** — stateful apps, steps, and crew kickoffs + - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation + - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools + - **Project layout** — CLI scaffolds and repo conventions + - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + + Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + + + + + How skills work in CrewAI agents — injection, activation, and patterns. + + + Overview of the crewAIInc/skills pack and what it includes. + + + Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + + Official listing — skills, install stats, and audits. + + + +--- + +## 2. llms.txt — Machine-Readable Docs + +CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. + +``` +https://docs.crewai.com/llms.txt +``` + + + + [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + CrewAI's `llms.txt` is **already live** — your agent can use it right now. + + + Point your coding agent at the URL when it needs CrewAI reference docs: + + ``` + Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. + ``` + + Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + + + - **No scraping required** — clean, structured content in one request + - **Always up-to-date** — served directly from docs.crewai.com + - **Optimized for LLMs** — formatted for context windows, not browsers + - **Complements skills** — skills teach patterns, llms.txt provides reference + + + +--- + +## 3. Deploy to Enterprise + +Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. + + + + Scaffold and test your crew or flow: + ```bash + crewai create crew my_crew + cd my_crew + crewai run + ``` + + + Ensure your project structure is ready: + ```bash + crewai deploy --prepare + ``` + See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + + + Push to the CrewAI AMP platform: + ```bash + crewai deploy + ``` + You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + + + Your deployed crew gets a REST API endpoint. Integrate it into any application: + ```bash + curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ + -H "Authorization: Bearer $CREWAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"inputs": {"topic": "AI agents"}}' + ``` + + + + + + Full deployment guide — CLI, GitHub, and Crew Studio methods. + + + Platform overview — what AMP provides for production crews. + + + +--- + +## 4. Enterprise Features + +CrewAI AMP is built for production teams. Here's what you get beyond deployment. + + + + Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + + No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + + + Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + + SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + + Publish and share custom tools across your organization. Install community tools from the registry. + + + Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + + + + + AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + + - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production + - **Factory (self-hosted)** — run on your own infrastructure for full data control + - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + + Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + + + + Sign up and deploy your first crew to production. + From d45ed61db588803a37650d0db07bf415fd993cad Mon Sep 17 00:00:00 2001 From: MatthiasHowellYopp <106839070+MatthiasHowellYopp@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:09:13 -0400 Subject: [PATCH 18/32] feat: added bedrock V4 support --- lib/crewai/src/crewai/llm.py | 39 ++ lib/crewai/src/crewai/llms/constants.py | 56 +++ .../llms/providers/bedrock/completion.py | 3 + uv.lock | 374 +++++++++--------- 4 files changed, 293 insertions(+), 179 deletions(-) diff --git a/lib/crewai/src/crewai/llm.py b/lib/crewai/src/crewai/llm.py index db126954e..fb8461c04 100644 --- a/lib/crewai/src/crewai/llm.py +++ b/lib/crewai/src/crewai/llm.py @@ -175,6 +175,16 @@ LLM_CONTEXT_WINDOW_SIZES: Final[dict[str, int]] = { "us.amazon.nova-pro-v1:0": 300000, "us.amazon.nova-micro-v1:0": 128000, "us.amazon.nova-lite-v1:0": 300000, + # Claude 4 models + "us.anthropic.claude-opus-4-7": 1000000, + "us.anthropic.claude-sonnet-4-6": 1000000, + "us.anthropic.claude-opus-4-6-v1": 1000000, + "us.anthropic.claude-opus-4-5-20251101-v1:0": 200000, + "us.anthropic.claude-haiku-4-5-20251001-v1:0": 200000, + "us.anthropic.claude-sonnet-4-5-20250929-v1:0": 200000, + "us.anthropic.claude-opus-4-1-20250805-v1:0": 200000, + "us.anthropic.claude-opus-4-20250514-v1:0": 200000, + "us.anthropic.claude-sonnet-4-20250514-v1:0": 200000, "us.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, "us.anthropic.claude-3-5-haiku-20241022-v1:0": 200000, "us.anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, @@ -193,15 +203,44 @@ LLM_CONTEXT_WINDOW_SIZES: Final[dict[str, int]] = { "eu.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, "eu.anthropic.claude-3-sonnet-20240229-v1:0": 200000, "eu.anthropic.claude-3-haiku-20240307-v1:0": 200000, + # Claude 4 EU + "eu.anthropic.claude-opus-4-7": 1000000, + "eu.anthropic.claude-sonnet-4-6": 1000000, + "eu.anthropic.claude-opus-4-6-v1": 1000000, + "eu.anthropic.claude-opus-4-5-20251101-v1:0": 200000, + "eu.anthropic.claude-haiku-4-5-20251001-v1:0": 200000, + "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": 200000, + "eu.anthropic.claude-opus-4-1-20250805-v1:0": 200000, + "eu.anthropic.claude-opus-4-20250514-v1:0": 200000, + "eu.anthropic.claude-sonnet-4-20250514-v1:0": 200000, "eu.meta.llama3-2-3b-instruct-v1:0": 131000, "eu.meta.llama3-2-1b-instruct-v1:0": 131000, "apac.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, "apac.anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, "apac.anthropic.claude-3-sonnet-20240229-v1:0": 200000, "apac.anthropic.claude-3-haiku-20240307-v1:0": 200000, + # Claude 4 APAC + "apac.anthropic.claude-opus-4-7": 1000000, + "apac.anthropic.claude-sonnet-4-6": 1000000, + "apac.anthropic.claude-opus-4-6-v1": 1000000, + "apac.anthropic.claude-opus-4-5-20251101-v1:0": 200000, + "apac.anthropic.claude-haiku-4-5-20251001-v1:0": 200000, + "apac.anthropic.claude-sonnet-4-5-20250929-v1:0": 200000, + "apac.anthropic.claude-opus-4-1-20250805-v1:0": 200000, + "apac.anthropic.claude-opus-4-20250514-v1:0": 200000, + "apac.anthropic.claude-sonnet-4-20250514-v1:0": 200000, "amazon.nova-pro-v1:0": 300000, "amazon.nova-micro-v1:0": 128000, "amazon.nova-lite-v1:0": 300000, + "anthropic.claude-opus-4-7": 1000000, + "anthropic.claude-sonnet-4-6": 1000000, + "anthropic.claude-opus-4-6-v1": 1000000, + "anthropic.claude-opus-4-5-20251101-v1:0": 200000, + "anthropic.claude-haiku-4-5-20251001-v1:0": 200000, + "anthropic.claude-sonnet-4-5-20250929-v1:0": 200000, + "anthropic.claude-opus-4-1-20250805-v1:0": 200000, + "anthropic.claude-opus-4-20250514-v1:0": 200000, + "anthropic.claude-sonnet-4-20250514-v1:0": 200000, "anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, "anthropic.claude-3-5-haiku-20241022-v1:0": 200000, "anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, diff --git a/lib/crewai/src/crewai/llms/constants.py b/lib/crewai/src/crewai/llms/constants.py index 595a0a30d..260c23daf 100644 --- a/lib/crewai/src/crewai/llms/constants.py +++ b/lib/crewai/src/crewai/llms/constants.py @@ -423,6 +423,34 @@ AZURE_MODELS: list[AzureModels] = [ BedrockModels: TypeAlias = Literal[ + # Inference profiles (regional) - Claude 4 + "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "us.anthropic.claude-opus-4-5-20251101-v1:0", + "us.anthropic.claude-opus-4-20250514-v1:0", + "us.anthropic.claude-opus-4-1-20250805-v1:0", + "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "us.anthropic.claude-sonnet-4-6", + "us.anthropic.claude-opus-4-6-v1", + # Inference profiles - shorter versions + "us.anthropic.claude-sonnet-4-5-v1:0", + "us.anthropic.claude-opus-4-5-v1:0", + "us.anthropic.claude-opus-4-6-v1:0", + "us.anthropic.claude-haiku-4-5-v1:0", + "eu.anthropic.claude-sonnet-4-5-v1:0", + "eu.anthropic.claude-opus-4-5-v1:0", + "eu.anthropic.claude-haiku-4-5-v1:0", + "apac.anthropic.claude-sonnet-4-5-v1:0", + "apac.anthropic.claude-opus-4-5-v1:0", + "apac.anthropic.claude-haiku-4-5-v1:0", + # Global inference profiles + "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "global.anthropic.claude-sonnet-4-20250514-v1:0", + "global.anthropic.claude-opus-4-5-20251101-v1:0", + "global.anthropic.claude-opus-4-6-v1", + "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "global.anthropic.claude-sonnet-4-6", + # Direct model IDs "ai21.jamba-1-5-large-v1:0", "ai21.jamba-1-5-mini-v1:0", "amazon.nova-lite-v1:0", @@ -496,6 +524,34 @@ BedrockModels: TypeAlias = Literal[ "twelvelabs.pegasus-1-2-v1:0", ] BEDROCK_MODELS: list[BedrockModels] = [ + # Inference profiles (regional) - Claude 4 + "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "us.anthropic.claude-opus-4-5-20251101-v1:0", + "us.anthropic.claude-opus-4-20250514-v1:0", + "us.anthropic.claude-opus-4-1-20250805-v1:0", + "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "us.anthropic.claude-sonnet-4-6", + "us.anthropic.claude-opus-4-6-v1", + # Inference profiles - shorter versions + "us.anthropic.claude-sonnet-4-5-v1:0", + "us.anthropic.claude-opus-4-5-v1:0", + "us.anthropic.claude-opus-4-6-v1:0", + "us.anthropic.claude-haiku-4-5-v1:0", + "eu.anthropic.claude-sonnet-4-5-v1:0", + "eu.anthropic.claude-opus-4-5-v1:0", + "eu.anthropic.claude-haiku-4-5-v1:0", + "apac.anthropic.claude-sonnet-4-5-v1:0", + "apac.anthropic.claude-opus-4-5-v1:0", + "apac.anthropic.claude-haiku-4-5-v1:0", + # Global inference profiles + "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "global.anthropic.claude-sonnet-4-20250514-v1:0", + "global.anthropic.claude-opus-4-5-20251101-v1:0", + "global.anthropic.claude-opus-4-6-v1", + "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "global.anthropic.claude-sonnet-4-6", + # Direct model IDs "ai21.jamba-1-5-large-v1:0", "ai21.jamba-1-5-mini-v1:0", "amazon.nova-lite-v1:0", diff --git a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py index 54c222c85..cd323eac0 100644 --- a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py +++ b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py @@ -2075,6 +2075,9 @@ class BedrockCompletion(BaseLLM): # Context window sizes for common Bedrock models context_windows = { + "anthropic.claude-sonnet-4": 200000, + "anthropic.claude-opus-4": 200000, + "anthropic.claude-haiku-4": 200000, "anthropic.claude-3-5-sonnet": 200000, "anthropic.claude-3-5-haiku": 200000, "anthropic.claude-3-opus": 200000, diff --git a/uv.lock b/uv.lock index 7451cfa17..7062d317d 100644 --- a/uv.lock +++ b/uv.lock @@ -615,7 +615,7 @@ wheels = [ [[package]] name = "bedrock-agentcore" -version = "1.3.2" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -627,9 +627,9 @@ dependencies = [ { name = "uvicorn" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/90/a11e5a3208b7f607a3eabc8567b7c36767c6e094ec8128fba7ed2f5b3020/bedrock_agentcore-1.3.2.tar.gz", hash = "sha256:1dfae10fd315e078c002e49fd9d9686c41aee71ec8495f21e898a1ef3f782fa3", size = 421197, upload-time = "2026-02-23T20:52:56.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/f6/2884c954343e794e3419348f5ffb0276a26f57b30af11f9fe63c4ca535c0/bedrock_agentcore-1.6.0.tar.gz", hash = "sha256:7ea176c3226dc6af8c399a8f9abb58629948cd8ed8675ece9f2f32b94e861b92", size = 512010, upload-time = "2026-03-31T23:10:06.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/b7/a5cc566901af27314408b95701f8e1d9c286b0aecfa50fc76c53d73efa6f/bedrock_agentcore-1.3.2-py3-none-any.whl", hash = "sha256:3a4e7122f777916f8bd74b42f29eb881415e37fda784a5ff8fab3c813b921706", size = 121703, upload-time = "2026-02-23T20:52:55.038Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f8/bcf158979324f4f4d171588afffadb2154fa8499701290bfc7bdaf82bd3a/bedrock_agentcore-1.6.0-py3-none-any.whl", hash = "sha256:a4cd02f2bfb80fcc7a8c8835be8d55c778339f8286b071ac3aae579460dd2eb2", size = 164034, upload-time = "2026-03-31T23:10:04.902Z" }, ] [[package]] @@ -719,7 +719,7 @@ wheels = [ [[package]] name = "build" -version = "1.4.2" +version = "1.4.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "os_name == 'nt'" }, @@ -728,9 +728,9 @@ dependencies = [ { name = "pyproject-hooks" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6c/1d/ab15c8ac57f4ee8778d7633bc6685f808ab414437b8644f555389cdc875e/build-1.4.2.tar.gz", hash = "sha256:35b14e1ee329c186d3f08466003521ed7685ec15ecffc07e68d706090bf161d1", size = 83433, upload-time = "2026-03-25T14:20:27.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/16/4b272700dea44c1d2e8ca963ebb3c684efe22b3eba8cfa31c5fdb60de707/build-1.4.3.tar.gz", hash = "sha256:5aa4231ae0e807efdf1fd0623e07366eca2ab215921345a2e38acdd5d0fa0a74", size = 89314, upload-time = "2026-04-10T21:25:40.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl", hash = "sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88", size = 24643, upload-time = "2026-03-25T14:20:26.568Z" }, + { url = "https://files.pythonhosted.org/packages/b2/30/f169e1d8b2071beaf8b97088787e30662b1d8fb82f8c0941d14678c0cbf1/build-1.4.3-py3-none-any.whl", hash = "sha256:1bc22b19b383303de8f2c8554c9a32894a58d3f185fe3756b0b20d255bee9a38", size = 26171, upload-time = "2026-04-10T21:25:39.671Z" }, ] [[package]] @@ -981,7 +981,7 @@ name = "coloredlogs" version = "15.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "humanfriendly", marker = "python_full_version < '3.11'" }, + { name = "humanfriendly" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ @@ -1002,7 +1002,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.13.9" +version = "4.13.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -1019,9 +1019,9 @@ dependencies = [ { name = "tomlkit" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/44/10f95e8178ab5a584298726a4a94ceb83a7f77e00741fec4680df05fedd5/commitizen-4.13.9.tar.gz", hash = "sha256:2b4567ed50555e10920e5bd804a6a4e2c42ec70bb74f14a83f2680fe9eaf9727", size = 64145, upload-time = "2026-02-25T02:40:05.326Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/95/da2c71ed6a1c06836cdd4eb60a8b9e1bf05f4ce7029ab508081745171be9/commitizen-4.13.10.tar.gz", hash = "sha256:402b5bcd466be69ba79a3f380be6ba5b55ac658c7d2a93e82fc99668a6eb2673", size = 64106, upload-time = "2026-04-11T06:49:12.907Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/22/9b14ee0f17f0aad219a2fb37a293a57b8324d9d195c6ef6807bcd0bf2055/commitizen-4.13.9-py3-none-any.whl", hash = "sha256:d2af3d6a83cacec9d5200e17768942c5de6266f93d932c955986c60c4285f2db", size = 85373, upload-time = "2026-02-25T02:40:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/11/3a/ad70b3c7dc3da1255668a9396429b1d820c15b74a501668158e4574c1edd/commitizen-4.13.10-py3-none-any.whl", hash = "sha256:95a281317990ac613501fdfe65745cec1fa4042bc5d003a72d332a74926e3039", size = 85746, upload-time = "2026-04-11T06:49:11.167Z" }, ] [[package]] @@ -1700,10 +1700,10 @@ wheels = [ [[package]] name = "cuda-pathfinder" -version = "1.5.2" +version = "1.5.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/f9/1b9b60a30fc463c14cdea7a77228131a0ccc89572e8df9cb86c9648271ab/cuda_pathfinder-1.5.2-py3-none-any.whl", hash = "sha256:0c5f160a7756c5b072723cbbd6d861e38917ef956c68150b02f0b6e9271c71fa", size = 49988, upload-time = "2026-04-06T23:01:05.17Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d6/ac63065d33dd700fee7ebd7d287332401b54e31b9346e142f871e1f0b116/cuda_pathfinder-1.5.3-py3-none-any.whl", hash = "sha256:dff021123aedbb4117cc7ec81717bbfe198fb4e8b5f1ee57e0e084fec5c8577d", size = 49991, upload-time = "2026-04-14T20:09:27.037Z" }, ] [[package]] @@ -2020,7 +2020,7 @@ wheels = [ [[package]] name = "docling-core" -version = "2.73.0" +version = "2.74.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, @@ -2035,9 +2035,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/e3/b9c3b1a1ea62e5e03d9e844a5cff2f89b7a3e960725a862f009e8553ca3d/docling_core-2.73.0.tar.gz", hash = "sha256:33ffc2b2bf736ed0e079bba296081a26885f6cb08081c828d630ca85a51e22e0", size = 308895, upload-time = "2026-04-09T08:08:51.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/d1/147ec84a59217d63620885e5103f9f40101972e70aae9e1c3b501e5637b8/docling_core-2.74.0.tar.gz", hash = "sha256:e8beb0b84a033c814386b1d990e73cb1c68c6485906c78c841b901577c705dc0", size = 316214, upload-time = "2026-04-17T06:50:28.344Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/c3/08143b7e8fe1b9230ce15e54926859f8c40ec2622fb612f0b2ff13169696/docling_core-2.73.0-py3-none-any.whl", hash = "sha256:4366fab8f4422fbde090ed87d9b091bd25b3b37cdd284dc0b02c9a5e24caaa22", size = 271518, upload-time = "2026-04-09T08:08:49.838Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9e/a7a5a71db047f5f50f5e4a4a43a918f346f97752539f1e5d99c785487497/docling_core-2.74.0-py3-none-any.whl", hash = "sha256:359f101a261cdcfa592bcb0e82dd508bd431f8d9ed49c6938ee271db1d420039", size = 275860, upload-time = "2026-04-17T06:50:26.779Z" }, ] [package.optional-dependencies] @@ -2078,7 +2078,7 @@ wheels = [ [[package]] name = "docling-parse" -version = "5.8.0" +version = "5.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-core" }, @@ -2087,33 +2087,33 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/57/7b98e3ccf1ed40977bf832f028c68c248b0df1c25a5a33a50c2b2943ea72/docling_parse-5.8.0.tar.gz", hash = "sha256:cbb1d591dd94edab4ab3b81e9e42a3e4c7fe9ab3c3e690dccd498602aae63c5a", size = 65990181, upload-time = "2026-04-08T09:41:39.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/10/69dc586f0ef54cc4e21e50debcb6bc52a77571482c88b7664aa725a7f150/docling_parse-5.9.0.tar.gz", hash = "sha256:c6812a143225490096cc2491a200b8731670c1dadff9aaf928c481bd5feba410", size = 66685491, upload-time = "2026-04-15T14:53:45.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/38/02a686660fe89a6f6775618ae43f9d4b76f615edc7374a1e8e1bf648fb73/docling_parse-5.8.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:241d09a904d8e4b70a2c040252a75a088e971a7926a46973389cb3235a5cab74", size = 8539476, upload-time = "2026-04-08T09:40:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/f1/38/ebd2fd850eef60d9c201cfb28b24bc3c8a27efeb34e817c12f544453a3c2/docling_parse-5.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e81da134baff612ea38ff0af3bf17deef196195d2415bfcf4f531bc7d0dd84", size = 9311993, upload-time = "2026-04-08T09:40:55.362Z" }, - { url = "https://files.pythonhosted.org/packages/c5/ba/c05c35a75b358ddaafdf0cd1e3f3737091722c6547b692cd66a99071159a/docling_parse-5.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b149bd7eeb91a5c6bdbc4a9bd87055a2a06d9ea959bf34d309580c1722d2e2b9", size = 9553650, upload-time = "2026-04-08T09:40:57.636Z" }, - { url = "https://files.pythonhosted.org/packages/63/7a/3670258908f6e5cf04251b9547967ebbf28211e29ede30eb5da41e0b509a/docling_parse-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac2c03347de9a0f02cdd46385ee4ae05f91eefc72aeac4749389d17f661dd7d5", size = 10357004, upload-time = "2026-04-08T09:40:59.921Z" }, - { url = "https://files.pythonhosted.org/packages/fc/09/57e47cc861f4e98201d6b881c6a7683e84f8ad20e2c1d619fe94c39ab7f2/docling_parse-5.8.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:fd1ae1cc22a96ccef76f82756ff7958d2a1eb38804e7cd9eed6ae951e2480c30", size = 8540650, upload-time = "2026-04-08T09:41:01.933Z" }, - { url = "https://files.pythonhosted.org/packages/5b/55/0265703d03377ad7ad3c4d482b00265275061ac15470dc815815944637cf/docling_parse-5.8.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3908496e6949d2e56e361fc743a8f9248cb0f76807a1860027dde02be14f854", size = 9269550, upload-time = "2026-04-08T09:41:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/96/03/962449ed1b6692e16c3cae0cf00fd60145d620dd1886aedacd1636727dec/docling_parse-5.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:860fbd5f2d30774d1c739d373aec14b7e074fdace191e5ac16750e7b14f136f4", size = 9601965, upload-time = "2026-04-08T09:41:06.807Z" }, - { url = "https://files.pythonhosted.org/packages/eb/18/5bee07b6ef6451b71904e0d21d7721af964fd92f3465305ef791d7a3cf56/docling_parse-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:854630f6ef7889d1757611194330d88fbbe53c0b202b5a010a467bf059f715da", size = 10358059, upload-time = "2026-04-08T09:41:09.049Z" }, - { url = "https://files.pythonhosted.org/packages/f9/61/3038e3a759df3aff0f02628eaeb71f6068b428ddd62981e639c5acf1eca8/docling_parse-5.8.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a37c8c0aab730a9857c726420925cccc304a16abd91f054b25726394ee1ac836", size = 8541739, upload-time = "2026-04-08T09:41:11.525Z" }, - { url = "https://files.pythonhosted.org/packages/d1/98/b9307f84a7753cc369bbdd81f0183f308e8be1efeb2998193a494f8a8f44/docling_parse-5.8.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b2c7455b058525cdd46d4c6b7c429871f096aa7718ce1b8481dae426358cf29", size = 9269677, upload-time = "2026-04-08T09:41:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a6/686adf6ed39d9de9912b233b8d0bd4f5e8113023aef47630ffde12ff0ba4/docling_parse-5.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:987d8eacb0f515f53a860329acc5c826487a9d2ff4430f08bd37498854cdab42", size = 9604016, upload-time = "2026-04-08T09:41:15.762Z" }, - { url = "https://files.pythonhosted.org/packages/f0/1b/90c5447a00a652a81e2b4fea86b33a694b1e0fec3b9fb1862f9b6f48f54a/docling_parse-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6f72b0fdd370e825777f7a9989c390c630774870390c7277b7f016bfae395d6a", size = 10360133, upload-time = "2026-04-08T09:41:18.085Z" }, - { url = "https://files.pythonhosted.org/packages/33/c9/799cc497b71537bafb6b8bf66fcccf303f8a84684503e8783d489db03aab/docling_parse-5.8.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:292b82a9773c66a76e5ee376cfdde4a4d6a8edae6a4493aba4013d939e7a213f", size = 8541804, upload-time = "2026-04-08T09:41:20.358Z" }, - { url = "https://files.pythonhosted.org/packages/93/29/1030c13b257be7a4317bc7837c22366eff6d961ca6d6604b426dc8a9adcd/docling_parse-5.8.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:85c896983aaa7b95f409ed52014da59a945f2b914291c0782740e6a5b6d39028", size = 9269366, upload-time = "2026-04-08T09:41:22.437Z" }, - { url = "https://files.pythonhosted.org/packages/54/22/40990653103c2eb83b073d2aca47aa95b767f1360214fca4c6339df105c3/docling_parse-5.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d9139f8da5e6553a36afb40dba614011ebd1bf97e5d17896ace07191a289c4b", size = 9604422, upload-time = "2026-04-08T09:41:24.619Z" }, - { url = "https://files.pythonhosted.org/packages/7e/9e/4ab1b16f6ba17f9695df79faa08a332b09a2d333d609036a7d0106538d57/docling_parse-5.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7343ee48b0480593ed08b04ed0b09421724a6dec63d82c23fac436129b32c66a", size = 10360242, upload-time = "2026-04-08T09:41:27.132Z" }, + { url = "https://files.pythonhosted.org/packages/58/a0/f04284a3e620d93d496ecfcf3e88bff46661c1bf0b2e90fe8c515ca6b6a4/docling_parse-5.9.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:e7794b173e4d9ae0ea061106aedc98093951394efc7305c7adffe4c43918369a", size = 8618285, upload-time = "2026-04-15T14:52:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/bf/49/ed3b83457b4aef027ceff9d24348fb4397101497721d9449da8292eeb246/docling_parse-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21d1b0fdcb6965d3b1c1a224d87ce6cddc3c52649125ddec951d6b99dcda57da", size = 9335733, upload-time = "2026-04-15T14:52:47.188Z" }, + { url = "https://files.pythonhosted.org/packages/7c/45/cf9bfd6515d8e34181befa9a7567680fee7e109be5902138e665b3021179/docling_parse-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690f10074ec05c69fb76050c282965ed9072c16f8eb020bc2483e228f0dfe39e", size = 9578860, upload-time = "2026-04-15T14:52:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/9a/94/873be136532196e7224c94810826c9517ae6b0065c620c288799c4f9d48b/docling_parse-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:7b54b2272af1a4b6812f30d3b77c7774b021f34b65f2ee7032c561da2cc2c0a8", size = 10385131, upload-time = "2026-04-15T14:52:52.732Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6c/3d6a840a208835b18235dc39a55a49ffbe36b739dffcd23edb43d56f977e/docling_parse-5.9.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5880485aaf7d16cb398c67fcb804abc52f3797364338354fcc13240dac0e829e", size = 8619332, upload-time = "2026-04-15T14:52:56.362Z" }, + { url = "https://files.pythonhosted.org/packages/a6/91/eb49ee414b97190303047abd888478fe9596ae9af7c631668bca37ce0b93/docling_parse-5.9.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:322152aa19c74547a145b1563c6a1d3a1773ad39fcf4c0a7554ef333701101de", size = 9294677, upload-time = "2026-04-15T14:52:59.318Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/8954e384e3e94b745279d5c213b5096a8bedce92ea69acea3377110835a6/docling_parse-5.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:afd7cd326ebe5de545e327f45b14be3e9b683efee0714d1b784f1314b1e22275", size = 9632461, upload-time = "2026-04-15T14:53:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/9e/44/a786427fb8f77578639da41937f51284cff0b756d1507eeae5aee34c60ca/docling_parse-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:17dea2d9e467feb5b7fe53c58ed7493fffb9482563e8f065d426c87fe1078beb", size = 10386431, upload-time = "2026-04-15T14:53:04.538Z" }, + { url = "https://files.pythonhosted.org/packages/a5/c2/c98e01230920c151c679e4526fd655a8f10fe0ce9e34a4d49b3f456ee200/docling_parse-5.9.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f9bb08e9e26cdd30d102d1a81420aca4a4b4136af2070d179147529ed991a64f", size = 8620298, upload-time = "2026-04-15T14:53:07.311Z" }, + { url = "https://files.pythonhosted.org/packages/84/54/fc38b47d77d2ef97fdfb9a67e92daecaa68e29b3c54d6409f725b5901686/docling_parse-5.9.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e141b536ccd954b612f2d7a091bf31e4684af07866ad6fa8b92b83fd60972e4", size = 9295434, upload-time = "2026-04-15T14:53:10.189Z" }, + { url = "https://files.pythonhosted.org/packages/20/68/f5ba9c8bb743e65b79448089bf27d73189aca9ba781bd97d8712ff51595e/docling_parse-5.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27eb3358564998f5f85264b093efc6e09d967113211448438911c646baa8c9b8", size = 9633448, upload-time = "2026-04-15T14:53:12.767Z" }, + { url = "https://files.pythonhosted.org/packages/5e/22/986312f5d7ec860e83fed6b3a604a736700510cb04e0fd8b8ab52a3bfedc/docling_parse-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fcbea80304e7a1549e8cf049c0b3ff8b27e8d99150fc86e65fa1839506c7c002", size = 10388840, upload-time = "2026-04-15T14:53:15.495Z" }, + { url = "https://files.pythonhosted.org/packages/41/28/7284bc189214e5c2a9ed15d0849a51f44d40dd9df9238d03c6db664bfc9e/docling_parse-5.9.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:0ff97842fd48bcc0ffae3dc8dfd1c96cca45b024395bdabea1ff2706bd23b44e", size = 8620340, upload-time = "2026-04-15T14:53:17.994Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5a/5716684a43e6ff0199be57f3b2177b36c2f69449d63a1a5b4db5b5419800/docling_parse-5.9.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:292f54cceba3847d94a34c9110deb932df475185e0773a0297c17d646a0ec641", size = 9296689, upload-time = "2026-04-15T14:53:20.926Z" }, + { url = "https://files.pythonhosted.org/packages/91/36/0a7001fa865a7023b3b26b97eb16a0ad0dfa472836e4042a8053be39ce37/docling_parse-5.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ae90c0444034b1252881c99cec3a02779108df71ccf5a8eafaec7d4c5b4a8e0", size = 9633550, upload-time = "2026-04-15T14:53:23.831Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ae/7880fd8b64b59f5d132426ec2cbe4db7595494254dbb3ffb5b9517ddb768/docling_parse-5.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:25a65bf93b826f733c3169623df720933294a89357c3dfef335e454b57507804", size = 10388600, upload-time = "2026-04-15T14:53:26.711Z" }, ] [[package]] name = "docstring-parser" -version = "0.17.0" +version = "0.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, ] [[package]] @@ -2184,7 +2184,7 @@ wheels = [ [[package]] name = "exa-py" -version = "2.11.0" +version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpcore" }, @@ -2195,9 +2195,9 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/08/af21dace845b5cd67d728e9d7747e4d1024ec90bd83e007d78f969dc6e19/exa_py-2.11.0.tar.gz", hash = "sha256:989103cbd83aae6dbe88cb70e11522a4bb06026fdb54b8659e3a7922da41fc93", size = 54905, upload-time = "2026-04-04T00:04:32.455Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/d2/22f8e5b83fb7ff1a5b19528b21bb908504c8b6a716309b169801881e64ff/exa_py-2.12.0.tar.gz", hash = "sha256:2cd5fe2d47d8e0221f87dcb2be0f007cc0a1f0a643b16dfc586ab1421998f4fc", size = 58731, upload-time = "2026-04-15T12:55:17.616Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/c9/129dd486505e3c0dadda0d6c83c560060f76d4cf14ef4b7b93053846598a/exa_py-2.11.0-py3-none-any.whl", hash = "sha256:3b0070a6ce98e02895755f0f81752dff64e2e121cf9d9a82facf715a4b9a5238", size = 73424, upload-time = "2026-04-04T00:04:33.699Z" }, + { url = "https://files.pythonhosted.org/packages/ea/87/e5c458741a34c945d6b612ec54f00088a6869ffc4f3f8a7b06ae080ec6af/exa_py-2.12.0-py3-none-any.whl", hash = "sha256:78b954ca99151228e4b853bd25e58829048a9a601d6187001befa512e0143f8f", size = 73896, upload-time = "2026-04-15T12:55:16.03Z" }, ] [[package]] @@ -2205,7 +2205,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2223,19 +2223,19 @@ wheels = [ [[package]] name = "faker" -version = "40.13.0" +version = "40.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/95/4822ffe94723553789aef783104f4f18fc20d7c4c68e1bbd633e11d09758/faker-40.13.0.tar.gz", hash = "sha256:a0751c84c3abac17327d7bb4c98e8afe70ebf7821e01dd7d0b15cd8856415525", size = 1962043, upload-time = "2026-04-06T16:44:55.68Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/13/6741787bd91c4109c7bed047d68273965cd52ce8a5f773c471b949334b6d/faker-40.15.0.tar.gz", hash = "sha256:20f3a6ec8c266b74d4c554e34118b21c3c2056c0b4a519d15c8decb3a4e6e795", size = 1967447, upload-time = "2026-04-17T20:05:27.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/8a/708103325edff16a0b0e004de0d37db8ba216a32713948c64d71f6d4a4c2/faker-40.13.0-py3-none-any.whl", hash = "sha256:c1298fd0d819b3688fb5fd358c4ba8f56c7c8c740b411fd3dbd8e30bf2c05019", size = 1994597, upload-time = "2026-04-06T16:44:53.698Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" }, ] [[package]] name = "fastapi" -version = "0.135.3" +version = "0.136.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -2244,9 +2244,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/e6/7adb4c5fa231e82c35b8f5741a9f2d055f520c29af5546fd70d3e8e1cd2e/fastapi-0.135.3.tar.gz", hash = "sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654", size = 396524, upload-time = "2026-04-01T16:23:58.188Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/d9/e66315807e41e69e7f6a1b42a162dada2f249c5f06ad3f1a95f84ab336ef/fastapi-0.136.0.tar.gz", hash = "sha256:cf08e067cc66e106e102d9ba659463abfac245200752f8a5b7b1e813de4ff73e", size = 396607, upload-time = "2026-04-16T11:47:13.623Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/a4/5caa2de7f917a04ada20018eccf60d6cc6145b0199d55ca3711b0fc08312/fastapi-0.135.3-py3-none-any.whl", hash = "sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98", size = 117734, upload-time = "2026-04-01T16:23:59.328Z" }, + { url = "https://files.pythonhosted.org/packages/26/a3/0bd5f0cdb0bbc92650e8dc457e9250358411ee5d1b65e42b6632387daf81/fastapi-0.136.0-py3-none-any.whl", hash = "sha256:8793d44ec7378e2be07f8a013cf7f7aa47d6327d0dfe9804862688ec4541a6b4", size = 117556, upload-time = "2026-04-16T11:47:11.922Z" }, ] [[package]] @@ -2337,11 +2337,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.25.2" +version = "3.28.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/17/6e8890271880903e3538660a21d63a6c1fea969ac71d0d6b608b78727fa9/filelock-3.28.0.tar.gz", hash = "sha256:4ed1010aae813c4ee8d9c660e4792475ee60c4a0ba76073ceaf862bd317e3ca6", size = 56474, upload-time = "2026-04-14T22:54:33.625Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, + { url = "https://files.pythonhosted.org/packages/3b/21/2f728888c45033d34a417bfcd248ea2564c9e08ab1bfd301377cf05d5586/filelock-3.28.0-py3-none-any.whl", hash = "sha256:de9af6712788e7171df1b28b15eba2446c69721433fa427a9bee07b17820a9db", size = 39189, upload-time = "2026-04-14T22:54:32.037Z" }, ] [[package]] @@ -2355,7 +2355,7 @@ wheels = [ [[package]] name = "firecrawl-py" -version = "4.22.1" +version = "4.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -2366,9 +2366,9 @@ dependencies = [ { name = "requests" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/87/08cd440a3b942be5983c1a2db921d55697bdb91f7ead9a925b75715039a0/firecrawl_py-4.22.1.tar.gz", hash = "sha256:fb44d4c63ba91c076ae2f0b688f1556327c971baea45e7fb67d6ed5d393542a2", size = 174394, upload-time = "2026-04-07T01:54:19.682Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/03/fc714c52f156add4c58665ff3ede3ff2b07d96e32742507ed94769a94227/firecrawl_py-4.22.2.tar.gz", hash = "sha256:c1bf17f6faf3b9599291e56d4b1b1d367777dbcf35b28568dd07084f1b0c9149", size = 174536, upload-time = "2026-04-15T21:34:42.124Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/a7/54199470a5bf8e09bdf9511f80e766a11b20daafc3b0e1e638ec04e24fc9/firecrawl_py-4.22.1-py3-none-any.whl", hash = "sha256:3df92a7888f9d5907a6fbbe50ade330d2925f5bf51f8efa507c2ab9891df9a0a", size = 217741, upload-time = "2026-04-07T01:54:18.403Z" }, + { url = "https://files.pythonhosted.org/packages/1a/35/adc7ff46b0f06261ce70b43ab0861c895d12bde7a7ceea95e45d45cb0a82/firecrawl_py-4.22.2-py3-none-any.whl", hash = "sha256:9f13f55ec7e8eb61a7fe91a2af09d5dd5c7539ec3f64f66280a7ceaa8b1bad10", size = 217823, upload-time = "2026-04-15T21:34:40.496Z" }, ] [[package]] @@ -2991,7 +2991,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.10.1" +version = "1.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -3004,9 +3004,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/28/baf5d745559503ce8d28cf5bc9551f5ac59158eafd7b6a6afff0bcdb0f50/huggingface_hub-1.10.1.tar.gz", hash = "sha256:696c53cf9c2ac9befbfb5dd41d05392a031c69fc6930d1ed9671debd405b6fff", size = 758094, upload-time = "2026-04-09T15:01:18.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/89/e7aa12d8a6b9259bed10671abb25ae6fa437c0f88a86ecbf59617bae7759/huggingface_hub-1.11.0.tar.gz", hash = "sha256:15fb3713c7f9cdff7b808a94fd91664f661ab142796bb48c9cd9493e8d166278", size = 761749, upload-time = "2026-04-16T13:07:39.73Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/8c/c7a33f3efaa8d6a5bc40e012e5ecc2d72c2e6124550ca9085fe0ceed9993/huggingface_hub-1.10.1-py3-none-any.whl", hash = "sha256:6b981107a62fbe68c74374418983399c632e35786dcd14642a9f2972633c8b5a", size = 642630, upload-time = "2026-04-09T15:01:17.35Z" }, + { url = "https://files.pythonhosted.org/packages/37/02/4f3f8997d1ea7fe0146b343e5e14bd065fa87af790d07e5576d31b31cc18/huggingface_hub-1.11.0-py3-none-any.whl", hash = "sha256:42a6de0afbfeb5e022222d36398f029679db4eb4778801aafda32257ae9131ab", size = 645499, upload-time = "2026-04-16T13:07:37.716Z" }, ] [[package]] @@ -3014,7 +3014,7 @@ name = "humanfriendly" version = "10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyreadline3", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } wheels = [ @@ -3023,7 +3023,7 @@ wheels = [ [[package]] name = "hyperbrowser" -version = "0.90.1" +version = "0.90.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3031,9 +3031,9 @@ dependencies = [ { name = "pydantic" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/60/b651865b7154feb571980c7f3341c75275a7330d3980c6a328bd875eb1dc/hyperbrowser-0.90.1.tar.gz", hash = "sha256:987259a99a8fe740274bc87b9cd64430476588fb5467313537d746881703fe4c", size = 65524, upload-time = "2026-04-07T23:56:44.951Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/47/2709a71c27e3614147b8bd9df378474bf450da18fb4c16a03b25ebb641de/hyperbrowser-0.90.4.tar.gz", hash = "sha256:14272b7ad78b7a16ecdb0f992c830b3dc3099fcf99bf0c417e78b1f22f1cb946", size = 67090, upload-time = "2026-04-16T18:51:49.957Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/49/cca92edcbace09135bf6c13a15c1856357c1cf68185d09088937b0bfe1f2/hyperbrowser-0.90.1-py3-none-any.whl", hash = "sha256:831c4e9b3143d713b64dd69034936763c5d92dfbf18f2936bc33d72c066b6551", size = 110792, upload-time = "2026-04-07T23:56:43.626Z" }, + { url = "https://files.pythonhosted.org/packages/68/af/b781aa3ad78c85cb8fc10b13ef005ec1e75b691b1af4314e81e5a8318755/hyperbrowser-0.90.4-py3-none-any.whl", hash = "sha256:b0e19e67f80a32a59838ecd12427fd5f7a23279f3987f3d74da336b390af6f8b", size = 113577, upload-time = "2026-04-16T18:51:48.631Z" }, ] [[package]] @@ -3100,11 +3100,11 @@ wheels = [ [[package]] name = "identify" -version = "2.6.18" +version = "2.6.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, ] [[package]] @@ -3241,11 +3241,11 @@ wheels = [ [[package]] name = "importlib-resources" -version = "6.5.2" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, + { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" }, ] [[package]] @@ -3676,7 +3676,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.2.31" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -3688,9 +3688,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/5a/7523ff55668a233beef7e909e8e2074a1cc3b620e0bbf0a4ec5f38549b3b/langchain_core-1.2.31.tar.gz", hash = "sha256:aad3ecc9e4dce2dd2bb79526c81b92e5322fd81db7834a031cb80359f2e3ebaa", size = 850756, upload-time = "2026-04-16T13:26:29.241Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/fe/20190232d9b513242899dbb0c2bb77e31b4d61e343743adbe90ebc2603d2/langchain_core-1.3.0.tar.gz", hash = "sha256:14a39f528bf459aa3aa40d0a7f7f1bae7520d435ef991ae14a4ceb74d8c49046", size = 860755, upload-time = "2026-04-17T14:51:38.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/02/668ddf4f1cf963ad691bdbea672a85244e6271eb0a4acfaf662bbd94a3b1/langchain_core-1.2.31-py3-none-any.whl", hash = "sha256:c407193edb99311cc36ec3e4d3667a065bbc4d7d72fbb6e368538b9b134d4033", size = 513264, upload-time = "2026-04-16T13:26:27.566Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e2/dbfa347aa072a6dc4cd38d6f9ebfc730b4c14c258c47f480f4c5c546f177/langchain_core-1.3.0-py3-none-any.whl", hash = "sha256:baf16ee028475df177b9ab8869a751c79406d64a6f12125b93802991b566cced", size = 515140, upload-time = "2026-04-17T14:51:36.274Z" }, ] [[package]] @@ -3716,7 +3716,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2 [[package]] name = "langsmith" -version = "0.7.31" +version = "0.7.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3729,18 +3729,18 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e6/11/696019490992db5c87774dc20515529ef42a01e1d770fb754ed6d9b12fb0/langsmith-0.7.31.tar.gz", hash = "sha256:331ee4f7c26bb5be4022b9859b7d7b122cbf8c9d01d9f530114c1914b0349ffb", size = 1178480, upload-time = "2026-04-14T17:55:41.242Z" } +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/1d/a1/a013cf458c301cda86a213dd153ce0a01c93f1ab5833f951e6a44c9763ce/langsmith-0.7.31-py3-none-any.whl", hash = "sha256:0291d49203f6e80dda011af1afda61eb0595a4d697adb684590a8805e1d61fb6", size = 373276, upload-time = "2026-04-14T17:55:39.677Z" }, + { 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]] name = "latex2mathml" -version = "3.79.0" +version = "3.81.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/8d/2161f46485d9c36c0fa0e1c997faf08bb7843027e59b549598e49f55f8bf/latex2mathml-3.79.0.tar.gz", hash = "sha256:11bde318c2d2d6fcdd105a07509d867cee2208f653278eb80243dec7ea77a0ce", size = 151103, upload-time = "2026-03-12T23:25:08.028Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/62/35bb816c5c19d4d0cde5bdfb82ebb996306243d5f94e03f201658c629960/latex2mathml-3.81.0.tar.gz", hash = "sha256:4b959cdc3cac8686bc0e3e5aece8127dfb1b81ca1241bed8e00ef31b82bb4022", size = 77584, upload-time = "2026-04-15T00:55:27.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/92/56a954dd59637dd2ee013581fa3beea0821f17f2c07f818fc51dcc11fd10/latex2mathml-3.79.0-py3-none-any.whl", hash = "sha256:9f10720d4fcf6b22d1b81f6628237832419a7a29783c13aa92fa8d680165e63d", size = 73945, upload-time = "2026-03-12T23:25:09.466Z" }, + { url = "https://files.pythonhosted.org/packages/e8/b1/c488b530994c4f68e46efa99a4d6ca6741aaf158e35779fe6c4d8a9a427d/latex2mathml-3.81.0-py3-none-any.whl", hash = "sha256:d317710393fe20579aea39cfe8928fa2ad9b8780896e585326c75e89c1d1d1a4", size = 79185, upload-time = "2026-04-15T00:55:29.301Z" }, ] [[package]] @@ -5225,12 +5225,13 @@ name = "onnxruntime" version = "1.23.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coloredlogs", marker = "python_full_version < '3.11'" }, - { name = "flatbuffers", marker = "python_full_version < '3.11'" }, + { name = "coloredlogs" }, + { name = "flatbuffers" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "protobuf", marker = "python_full_version < '3.11'" }, - { name = "sympy", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "sympy" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/35/d6/311b1afea060015b56c742f3531168c1644650767f27ef40062569960587/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", size = 17195934, upload-time = "2025-10-27T23:06:14.143Z" }, @@ -5259,7 +5260,7 @@ wheels = [ [[package]] name = "openai" -version = "2.31.0" +version = "2.32.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -5271,9 +5272,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/fe/64b3d035780b3188f86c4f6f1bc202e7bb74757ef028802112273b9dcacf/openai-2.31.0.tar.gz", hash = "sha256:43ca59a88fc973ad1848d86b98d7fac207e265ebbd1828b5e4bdfc85f79427a5", size = 684772, upload-time = "2026-04-08T21:01:41.797Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/59/bdcc6b759b8c42dd73afaf5bf8f902c04b37987a5514dbc1c64dba390fef/openai-2.32.0.tar.gz", hash = "sha256:c54b27a9e4cb8d51f0dd94972ffd1a04437efeb259a9e60d8922b8bd26fe55e0", size = 693286, upload-time = "2026-04-15T22:28:19.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/bc/a8f7c3aa03452fedbb9af8be83e959adba96a6b4a35e416faffcc959c568/openai-2.31.0-py3-none-any.whl", hash = "sha256:44e1344d87e56a493d649b17e2fac519d1368cbb0745f59f1957c4c26de50a0a", size = 1153479, upload-time = "2026-04-08T21:01:39.217Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c1/d6e64ccd0536bf616556f0cad2b6d94a8125f508d25cfd814b1d2db4e2f1/openai-2.32.0-py3-none-any.whl", hash = "sha256:4dcc9badeb4bf54ad0d187453742f290226d30150890b7890711bda4f32f192f", size = 1162570, upload-time = "2026-04-15T22:28:17.714Z" }, ] [[package]] @@ -5532,11 +5533,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] @@ -7440,7 +7441,7 @@ wheels = [ [[package]] name = "rapidocr" -version = "3.8.0" +version = "3.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorlog" }, @@ -7457,7 +7458,7 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/49/1f/5f815e17c0b02b8f937b5b680b85d0ec5f34b195314dfa8f11ed14a6de03/rapidocr-3.8.0-py3-none-any.whl", hash = "sha256:54abb10883d588120a3390bc447566f1590aea641e127f63a4ca44415fecd18a", size = 15082360, upload-time = "2026-04-08T13:42:15.89Z" }, + { url = "https://files.pythonhosted.org/packages/ea/4a/fa521d947f0fc7bb304bf11bec4cb66266bd81494588b4cb48dc01001719/rapidocr-3.8.1-py3-none-any.whl", hash = "sha256:650044b1fbce9e6bae5cae462dcf8be754cde11e2f23fc51f65dcc08deae2c46", size = 15080319, upload-time = "2026-04-11T07:13:22.56Z" }, ] [[package]] @@ -7617,15 +7618,15 @@ wheels = [ [[package]] name = "rich" -version = "14.3.3" +version = "15.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] [[package]] @@ -7946,7 +7947,7 @@ wheels = [ [[package]] name = "scrapfly-sdk" -version = "0.8.28" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backoff" }, @@ -7956,14 +7957,14 @@ dependencies = [ { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/3e/a881968b866ed77cb8a5013aeb100a5a3dd2b502e9a9f955615e15157ad0/scrapfly_sdk-0.8.28.tar.gz", hash = "sha256:051f734ae10fd9b136527f3dc3344abb68ed64822c108b1caff6dc8399c197e0", size = 104208, upload-time = "2026-04-09T16:18:51.793Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/7e/3dd57ac5b80c997fd9ee54a67b9a035eb2170a7fa8f5afa8486179401702/scrapfly_sdk-0.10.0.tar.gz", hash = "sha256:4b14a1a448b723771cbc9dba8bc07394c330028cfa77f656e9c182e7b8ab46ea", size = 105048, upload-time = "2026-04-15T17:31:10.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/c6/97a5fbc9ff952c45783303add4c4e431b7a34a020f6dc3adb8f878af0c2a/scrapfly_sdk-0.8.28-py3-none-any.whl", hash = "sha256:116198df90cdbea224d6b0c92d4d74c9ee585fa63c1c5ec9f021b5fc9638fe3f", size = 117920, upload-time = "2026-04-09T16:18:50.356Z" }, + { url = "https://files.pythonhosted.org/packages/73/9e/d6ebd1b3343bb966dabfe0191578db060417ce6d038c4a24ab96bf2a239f/scrapfly_sdk-0.10.0-py3-none-any.whl", hash = "sha256:26599ee9526196f531aa7e07d03bd6dfdd4172c470caf7ee0b56ce3d001d1768", size = 118828, upload-time = "2026-04-15T17:31:08.905Z" }, ] [[package]] name = "selenium" -version = "4.42.0" +version = "4.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -7973,9 +7974,9 @@ dependencies = [ { name = "urllib3" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/46/fb93d37749ecf13853739c31c70bd95704310a7defbc57e7101dc4ab2513/selenium-4.42.0.tar.gz", hash = "sha256:4c8ebd84ff96505db4277223648f12e2799e92e13169bc69633a6b24eb066c72", size = 956304, upload-time = "2026-04-09T08:31:20.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/6a/fe950b498a3c570ab538ad1c2b60f18863eecf077a865eea4459f3fa78a9/selenium-4.43.0.tar.gz", hash = "sha256:bada5c08a989f812728a4b5bea884d8e91894e939a441cc3a025201ce718581e", size = 967747, upload-time = "2026-04-10T06:47:03.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/47/9f094f1cffdb54b01da75b45cc29673869458a504b30002797c0c47ac985/selenium-4.42.0-py3-none-any.whl", hash = "sha256:bb29eababf54fa479c95d5fa3fba73889db5d532f3a76addc5b526bbff14fca7", size = 9559171, upload-time = "2026-04-09T08:31:17.38Z" }, + { url = "https://files.pythonhosted.org/packages/82/c7/0c55fbb0275fc368676ea50514ce7d7839d799a8b3ff8425f380186c7626/selenium-4.43.0-py3-none-any.whl", hash = "sha256:4f97639055dcfa9eadf8ccf549ba7b0e49c655d4e2bde19b9a44e916b754e769", size = 9573091, upload-time = "2026-04-10T06:47:01.134Z" }, ] [[package]] @@ -8002,15 +8003,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.57.0" +version = "2.58.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/87/46c0406d8b5ddd026f73adaf5ab75ce144219c41a4830b52df4b9ab55f7f/sentry_sdk-2.57.0.tar.gz", hash = "sha256:4be8d1e71c32fb27f79c577a337ac8912137bba4bcbc64a4ec1da4d6d8dc5199", size = 435288, upload-time = "2026-03-31T09:39:29.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/b3/fb8291170d0e844173164709fc0fa0c221ed75a5da740c8746f2a83b4eb1/sentry_sdk-2.58.0.tar.gz", hash = "sha256:c1144d947352d54e5b7daa63596d9f848adf684989c06c4f5a659f0c85a18f6f", size = 438764, upload-time = "2026-04-13T17:23:26.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/64/982e07b93219cb52e1cca5d272cb579e2f3eb001956c9e7a9a6d106c9473/sentry_sdk-2.57.0-py2.py3-none-any.whl", hash = "sha256:812c8bf5ff3d2f0e89c82f5ce80ab3a6423e102729c4706af7413fd1eb480585", size = 456489, upload-time = "2026-03-31T09:39:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/fa/eb/d875669993b762556ae8b2efd86219943b4c0864d22204d622a9aee3052b/sentry_sdk-2.58.0-py2.py3-none-any.whl", hash = "sha256:688d1c704ddecf382ea3326f21a67453d4caa95592d722b7c780a36a9d23109e", size = 460919, upload-time = "2026-04-13T17:23:24.675Z" }, ] [[package]] @@ -8097,7 +8098,7 @@ wheels = [ [[package]] name = "singlestoredb" -version = "1.16.9" +version = "1.16.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parsimonious" }, @@ -8107,14 +8108,14 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/15/4ae4f961f939574f328db4a9d0de8698bdf8b174579274a47625f9f1002e/singlestoredb-1.16.9.tar.gz", hash = "sha256:92e72112268ec362c19b1923eeff7a8da31d756b9ae1060e0eaf8eb03db3596d", size = 376737, upload-time = "2026-02-05T19:28:50.234Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/1e/95b27d8a856f174dd33822db476340a207c75be6232b792d669881f2da18/singlestoredb-1.16.10.tar.gz", hash = "sha256:650f952d22e10552b71fac72b08f6df2c72ab71b353950bc43df05a2bdcb2f32", size = 382404, upload-time = "2026-04-13T22:01:51.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a8/95612fb8d3fbf0dd7e624ff06e436920bea44365d5e525f388d0740c6c74/singlestoredb-1.16.9-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d36d8daa58ad0bce924b479535a20c05a063627fdc5f48d680e1787ddf168802", size = 481162, upload-time = "2026-02-05T19:28:39.251Z" }, - { url = "https://files.pythonhosted.org/packages/80/74/014fa784fb27bed36d69bd4dd64b3c776c06c71c7b1b4a6a349d34aa05cf/singlestoredb-1.16.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e958dec4387a4f86c14a73167c120f6637281362e281c4329e3d5bdee55dc43", size = 938771, upload-time = "2026-02-05T19:28:40.899Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6a/eb0893d555798582fb594d4dd0f722f4118d845e2f47ffa71866e908c9fd/singlestoredb-1.16.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab89d9b3b3c774e44fecb0a1fb179960150a0e56589f6305470c1db3b6404c2b", size = 939633, upload-time = "2026-02-05T19:28:42.988Z" }, - { url = "https://files.pythonhosted.org/packages/d9/80/d02c37233c6dbb7038ac44b1d6a26339e2425667ac813ea562303b23bac6/singlestoredb-1.16.9-cp38-abi3-win32.whl", hash = "sha256:c5141337497856e9c743cdfbf8501416e8dfffd5dbc3d3cc7578f00be0e6a7b9", size = 457977, upload-time = "2026-02-05T19:28:45.33Z" }, - { url = "https://files.pythonhosted.org/packages/00/0b/de8fcacc8e4dff819501401395aeccdb09138e7a2ba6947a7eac1b6f1823/singlestoredb-1.16.9-cp38-abi3-win_amd64.whl", hash = "sha256:7277e82f5900e261742b7476712953a214940ce52b623a7879c6589932be2f55", size = 456492, upload-time = "2026-02-05T19:28:47.146Z" }, - { url = "https://files.pythonhosted.org/packages/24/4b/dbfe36798b1349a231ee28c0791bc04f786701d49fdf77f22f8d265647df/singlestoredb-1.16.9-py3-none-any.whl", hash = "sha256:e632ce2fb3df19aa66f265110224372f5511e1aa995c1b661c8a46ef0bb7099d", size = 424420, upload-time = "2026-02-05T19:28:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/45/85/c0d2aa1f117c0e5abacee1cae2c25d889e77bc9251833663836c90bb2226/singlestoredb-1.16.10-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:026a12f3eff764b0a98a88694f2513d6d7f0b41106fa59b2d037cf70566538d0", size = 484460, upload-time = "2026-04-13T22:01:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/ed/01/ab6a4f9904ce80c9864fa9b9b419b56f62edf0df91fbecea6459a6ab8290/singlestoredb-1.16.10-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bfe81d02e96720895d8b7cf92afd07e420244e72a6ed508d072dca41038b7141", size = 942625, upload-time = "2026-04-13T22:01:42.577Z" }, + { url = "https://files.pythonhosted.org/packages/87/ca/4d64f355ef06dfdebb06afb5cbb29cfb62f1c6762822ff4f26d99c37347c/singlestoredb-1.16.10-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c548e80fd729a728b01496ef6bb474d41632b5940204ad068da3272518676094", size = 943492, upload-time = "2026-04-13T22:01:44.625Z" }, + { url = "https://files.pythonhosted.org/packages/e6/41/535f3986b37f72cabd3f82789ad8912c8e92f368e2c77b6dff538b1c82e6/singlestoredb-1.16.10-cp38-abi3-win32.whl", hash = "sha256:33fe4be668c99be4a6e7b26723b87732d75656a400598981b5bb684fa79677cf", size = 461298, upload-time = "2026-04-13T22:01:46.312Z" }, + { url = "https://files.pythonhosted.org/packages/96/2f/4fee1cd75b8ba6c803d410722951b7a95b6f46529de99abab01e0a57df60/singlestoredb-1.16.10-cp38-abi3-win_amd64.whl", hash = "sha256:92afcd147e3cb8d8476f546cfa5f513df57416f314bdca9f99bb94cb32d24137", size = 459811, upload-time = "2026-04-13T22:01:47.814Z" }, + { url = "https://files.pythonhosted.org/packages/c2/33/c55c9218dd900be4c1692a5024fa3ba3f920e461b701598dc7e82ce2754c/singlestoredb-1.16.10-py3-none-any.whl", hash = "sha256:5df7222bebc34e73673d350f8aae293004fa4139e24bd890f546e7b9cf7af81c", size = 427717, upload-time = "2026-04-13T22:01:49.736Z" }, ] [[package]] @@ -8645,7 +8646,7 @@ wheels = [ [[package]] name = "transformers" -version = "5.5.3" +version = "5.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -8659,9 +8660,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/35/cd5b0d1288e65d2c12db4ce84c1ec1074f7ee9bced040de6c9d69e70d620/transformers-5.5.3.tar.gz", hash = "sha256:3f60128e840b40d352655903552e1eed4f94ed49369a4d43e1bc067bd32d3f50", size = 8226047, upload-time = "2026-04-09T15:52:56.231Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/1e/1e244ab2ab50a863e6b52cc55761910567fa532b69a6740f6e99c5fdbd98/transformers-5.5.4.tar.gz", hash = "sha256:2e67cadba81fc7608cc07c4dd54f524820bc3d95b1cabd0ef3db7733c4f8b82e", size = 8227649, upload-time = "2026-04-13T16:55:55.181Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0b/f8524551ab2d896dfaca74ddb70a4453d515bbf4ab5451c100c7788ae155/transformers-5.5.3-py3-none-any.whl", hash = "sha256:e48f3ec31dd96505e96e66b63a1e43e1ad7a65749e108d9227caaf51051cdb02", size = 10236257, upload-time = "2026-04-09T15:52:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/29/fb/162a66789c65e5afa3b051309240c26bf37fbc8fea285b4546ae747995a2/transformers-5.5.4-py3-none-any.whl", hash = "sha256:0bd6281b82966fe5a7a16f553ea517a9db1dee6284d7cb224dfd88fc0dd1c167", size = 10236696, upload-time = "2026-04-13T16:55:51.497Z" }, ] [[package]] @@ -9184,28 +9185,28 @@ wheels = [ [[package]] name = "uv" -version = "0.11.6" +version = "0.11.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/f3/8aceeab67ea69805293ab290e7ca8cc1b61a064d28b8a35c76d8eba063dd/uv-0.11.6.tar.gz", hash = "sha256:e3b21b7e80024c95ff339fcd147ac6fc3dd98d3613c9d45d3a1f4fd1057f127b", size = 4073298, upload-time = "2026-04-09T12:09:01.738Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/7d/17750123a8c8e324627534fe1ae2e7a46689db8492f1a834ab4fd229a7d8/uv-0.11.7.tar.gz", hash = "sha256:46d971489b00bdb27e0aa715e4a5cd4ef2c28ea5b6ef78f2b67bf861eb44b405", size = 4083385, upload-time = "2026-04-15T21:42:55.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/fe/4b61a3d5ad9d02e8a4405026ccd43593d7044598e0fa47d892d4dafe44c9/uv-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:ada04dcf89ddea5b69d27ac9cdc5ef575a82f90a209a1392e930de504b2321d6", size = 23780079, upload-time = "2026-04-09T12:08:56.609Z" }, - { url = "https://files.pythonhosted.org/packages/52/db/d27519a9e1a5ffee9d71af1a811ad0e19ce7ab9ae815453bef39dd479389/uv-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5be013888420f96879c6e0d3081e7bcf51b539b034a01777041934457dfbedf3", size = 23214721, upload-time = "2026-04-09T12:09:32.228Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8f/4399fa8b882bd7e0efffc829f73ab24d117d490a93e6bc7104a50282b854/uv-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ffa5dc1cbb52bdce3b8447e83d1601a57ad4da6b523d77d4b47366db8b1ceb18", size = 21750109, upload-time = "2026-04-09T12:09:24.357Z" }, - { url = "https://files.pythonhosted.org/packages/32/07/5a12944c31c3dda253632da7a363edddb869ed47839d4d92a2dc5f546c93/uv-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bfb107b4dade1d2c9e572992b06992d51dd5f2136eb8ceee9e62dd124289e825", size = 23551146, upload-time = "2026-04-09T12:09:10.439Z" }, - { url = "https://files.pythonhosted.org/packages/79/5b/2ec8b0af80acd1016ed596baf205ddc77b19ece288473b01926c4a9cf6db/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:9e2fe7ce12161d8016b7deb1eaad7905a76ff7afec13383333ca75e0c4b5425d", size = 23331192, upload-time = "2026-04-09T12:09:34.792Z" }, - { url = "https://files.pythonhosted.org/packages/62/7d/eea35935f2112b21c296a3e42645f3e4b1aa8bcd34dcf13345fbd55134b7/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ed9c6f70c25e8dfeedddf4eddaf14d353f5e6b0eb43da9a14d3a1033d51d915", size = 23337686, upload-time = "2026-04-09T12:09:18.522Z" }, - { url = "https://files.pythonhosted.org/packages/21/47/2584f5ab618f6ebe9bdefb2f765f2ca8540e9d739667606a916b35449eec/uv-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d68a013e609cebf82077cbeeb0809ed5e205257814273bfd31e02fc0353bbfc2", size = 25008139, upload-time = "2026-04-09T12:09:03.983Z" }, - { url = "https://files.pythonhosted.org/packages/95/81/497ae5c1d36355b56b97dc59f550c7e89d0291c163a3f203c6f341dff195/uv-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93f736dddca03dae732c6fdea177328d3bc4bf137c75248f3d433c57416a4311", size = 25712458, upload-time = "2026-04-09T12:09:07.598Z" }, - { url = "https://files.pythonhosted.org/packages/3c/1c/74083238e4fab2672b63575b9008f1ea418b02a714bcfcf017f4f6a309b6/uv-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96a66abe53fced0e3389008b8d2eff8278cfa8bb545d75631ae8ceb9c929aba", size = 24915507, upload-time = "2026-04-09T12:08:50.892Z" }, - { url = "https://files.pythonhosted.org/packages/5a/ee/e14fe10ba455a823ed18233f12de6699a601890905420b5c504abf115116/uv-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b096311b2743b228df911a19532b3f18fa420bf9530547aecd6a8e04bbfaccd", size = 24971011, upload-time = "2026-04-09T12:08:54.016Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/7b9c83eaadf98e343317ff6384a7227a4855afd02cdaf9696bcc71ee6155/uv-0.11.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:904d537b4a6e798015b4a64ff5622023bd4601b43b6cd1e5f423d63471f5e948", size = 23640234, upload-time = "2026-04-09T12:09:15.735Z" }, - { url = "https://files.pythonhosted.org/packages/d6/51/75ccdd23e76ff1703b70eb82881cd5b4d2a954c9679f8ef7e0136ef2cfab/uv-0.11.6-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:4ed8150c26b5e319381d75ae2ce6aba1e9c65888f4850f4e3b3fa839953c90a5", size = 24452664, upload-time = "2026-04-09T12:09:26.875Z" }, - { url = "https://files.pythonhosted.org/packages/4d/86/ace80fe47d8d48b5e3b5aee0b6eb1a49deaacc2313782870250b3faa36f5/uv-0.11.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c9218c8d4ac35ca6e617fb0951cc0ab2d907c91a6aea2617de0a5494cf162c0", size = 24494599, upload-time = "2026-04-09T12:09:37.368Z" }, - { url = "https://files.pythonhosted.org/packages/05/2d/4b642669b56648194f026de79bc992cbfc3ac2318b0a8d435f3c284934e8/uv-0.11.6-py3-none-musllinux_1_1_i686.whl", hash = "sha256:9e211c83cc890c569b86a4183fcf5f8b6f0c7adc33a839b699a98d30f1310d3a", size = 24159150, upload-time = "2026-04-09T12:09:13.17Z" }, - { url = "https://files.pythonhosted.org/packages/ae/24/7eecd76fe983a74fed1fc700a14882e70c4e857f1d562a9f2303d4286c12/uv-0.11.6-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1d2089afdf117ad19a4c1dd36b8189c00ae1ad4135d3bfbfced82342595cf", size = 25164324, upload-time = "2026-04-09T12:08:59.56Z" }, - { url = "https://files.pythonhosted.org/packages/27/e0/bbd4ba7c2e5067bbba617d87d306ec146889edaeeaa2081d3e122178ca08/uv-0.11.6-py3-none-win32.whl", hash = "sha256:6e8344f38fa29f85dcfd3e62dc35a700d2448f8e90381077ef393438dcd5012e", size = 22865693, upload-time = "2026-04-09T12:09:21.415Z" }, - { url = "https://files.pythonhosted.org/packages/a5/33/1983ce113c538a856f2d620d16e39691962ecceef091a84086c5785e32e5/uv-0.11.6-py3-none-win_amd64.whl", hash = "sha256:a28bea69c1186303d1200f155c7a28c449f8a4431e458fcf89360cc7ef546e40", size = 25371258, upload-time = "2026-04-09T12:09:40.52Z" }, - { url = "https://files.pythonhosted.org/packages/35/01/be0873f44b9c9bc250fcbf263367fcfc1f59feab996355bcb6b52fff080d/uv-0.11.6-py3-none-win_arm64.whl", hash = "sha256:a78f6d64b9950e24061bc7ec7f15ff8089ad7f5a976e7b65fcadce58fe02f613", size = 23869585, upload-time = "2026-04-09T12:09:29.425Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5b/2bb2ab6fe6c78c2be10852482ef0cae5f3171460a6e5e24c32c9a0843163/uv-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:f422d39530516b1dfb28bb6e90c32bb7dacd50f6a383cd6e40c1a859419fbc8c", size = 23757265, upload-time = "2026-04-15T21:43:14.494Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f5/36ff27b01e60a88712628c8a5a6003b8e418883c24e084e506095844a797/uv-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8b2fe1ec6775dad10183e3fdce430a5b37b7857d49763c884f3a67eaa8ca6f8a", size = 23184529, upload-time = "2026-04-15T21:42:30.225Z" }, + { url = "https://files.pythonhosted.org/packages/8a/fa/f379be661316698f877e78f4c51e5044be0b6f390803387237ad92c4057f/uv-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:162fa961a9a081dcea6e889c79f738a5ae56507047e4672964972e33c301bea9", size = 21780167, upload-time = "2026-04-15T21:42:44.942Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/fbed29775b0612f4f5679d3226268f1a347161abc1727b4080fb41d9f46f/uv-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:5985a15a92bd9a170fc1947abb1fbc3e9828c5a430ad85b5bed8356c20b67a71", size = 23609640, upload-time = "2026-04-15T21:42:22.57Z" }, + { url = "https://files.pythonhosted.org/packages/ad/de/989a69634a869a22322770120557c2d8cbba5b77ec7cfad326b4ec0f0547/uv-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:fab0bb43fbbc0ee5b5fee212078d2300c371b725faff7cf72eeaafa0bff0606b", size = 23322484, upload-time = "2026-04-15T21:43:26.52Z" }, + { url = "https://files.pythonhosted.org/packages/24/08/c1af05ea602eb4eb75d86badb6b0594cc104c3ca83ccf06d9ed4dd2186ad/uv-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23d457d6731ebdb83f1bffebe4894edab2ef43c1ec5488433c74300db4958924", size = 23326385, upload-time = "2026-04-15T21:42:41.32Z" }, + { url = "https://files.pythonhosted.org/packages/68/99/e246962da06383e992ecab55000c62a50fb36efef855ea7264fad4816bf4/uv-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d6a17507b8139b8803f445a03fd097f732ce8356b1b7b13cdb4dd8ef7f4b2e0", size = 24985751, upload-time = "2026-04-15T21:42:37.777Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/b0b68083859579ce811996c1480765ec6a2442b44c451eaef53e6218fbae/uv-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd48823ca4b505124389f49ae50626ba9f57212b9047738efc95126ed5f3844d", size = 25724160, upload-time = "2026-04-15T21:43:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/4e/19/5970e89d9e458fd3c4966bbc586a685a1c0ab0a8bf334503f63fa20b925b/uv-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb91f52ee67e10d5290f2c2897e2171357f1a10966de38d83eefa93d96843b0c", size = 25028512, upload-time = "2026-04-15T21:43:02.721Z" }, + { url = "https://files.pythonhosted.org/packages/83/eb/4e1557daf6693cb446ed28185664ad6682fd98c6dbac9e433cbc35df450a/uv-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e4d5e31bea86e1b6e0f5a0f95e14e80018e6f6c0129256d2915a4b3d793644d", size = 24933975, upload-time = "2026-04-15T21:42:18.828Z" }, + { url = "https://files.pythonhosted.org/packages/68/55/3b517ec8297f110d6981f525cccf26f86e30883fbb9c282769cffbcdcfca/uv-0.11.7-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ceae53b202ea92bc954759bc7c7570cdcd5c3512fce15701198c19fd2dfb8605", size = 23706403, upload-time = "2026-04-15T21:43:10.664Z" }, + { url = "https://files.pythonhosted.org/packages/dc/30/7d93a0312d60e147722967036dc8ea37baab4802784bddc22464cb707deb/uv-0.11.7-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:f97e9f4e4d44fb5c4dfaa05e858ef3414a96416a2e4af270ecd88a3e5fb049a9", size = 24495797, upload-time = "2026-04-15T21:42:26.538Z" }, + { url = "https://files.pythonhosted.org/packages/8c/89/d49480bdab7725d36982793857e461d471bde8e1b7f438ffccee677a7bf8/uv-0.11.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:750ee5b96959b807cf442b73dd8b55111862d63f258f896787ea5f06b68aaca9", size = 24580471, upload-time = "2026-04-15T21:42:52.871Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9f/c57dc03b48be17b564e304eb9ff982890c12dfb888b1ce370788733329ab/uv-0.11.7-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f394331f0507e80ee732cb3df737589de53bed999dd02a6d24682f08c2f8ac4f", size = 24113637, upload-time = "2026-04-15T21:42:34.094Z" }, + { url = "https://files.pythonhosted.org/packages/13/ba/b87e358b629a68258527e3490e73b7b148770f4d2257842dea3b7981d4e8/uv-0.11.7-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:0df59ab0c6a4b14a763e8445e1c303af9abeb53cdfa4428daf9ff9642c0a3cce", size = 25119850, upload-time = "2026-04-15T21:43:22.529Z" }, + { url = "https://files.pythonhosted.org/packages/4b/74/16d229e1d8574bcbafa6dc643ac20b70c3e581f42ac31a6f4fd53035ffe3/uv-0.11.7-py3-none-win32.whl", hash = "sha256:553e67cc766d013ce24353fecd4ea5533d2aedcfd35f9fac430e07b1d1f23ed4", size = 22918454, upload-time = "2026-04-15T21:42:58.702Z" }, + { url = "https://files.pythonhosted.org/packages/a6/1d/b73e473da616ac758b8918fb218febcc46ddf64cba9e03894dfa226b28bd/uv-0.11.7-py3-none-win_amd64.whl", hash = "sha256:5674dfb5944513f4b3735b05c2deba6b1b01151f46729d533d413a9a905f8c5d", size = 25447744, upload-time = "2026-04-15T21:42:48.813Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bb/e6bfdea92ed270f3445a5a3c17599d041b3f2dbc5026c09e02830a03bbaf/uv-0.11.7-py3-none-win_arm64.whl", hash = "sha256:6158b7e39464f1aa1e040daa0186cae4749a78b5cd80ac769f32ca711b8976b1", size = 23941816, upload-time = "2026-04-15T21:43:06.732Z" }, ] [[package]] @@ -9291,7 +9292,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.2.1" +version = "21.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -9300,9 +9301,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" }, + { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, ] [[package]] @@ -9552,51 +9553,66 @@ wheels = [ [[package]] name = "wrapt" -version = "1.17.3" +version = "2.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" }, - { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676, upload-time = "2025-08-12T05:51:32.636Z" }, - { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957, upload-time = "2025-08-12T05:51:54.655Z" }, - { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975, upload-time = "2025-08-12T05:52:30.109Z" }, - { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149, upload-time = "2025-08-12T05:52:09.316Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209, upload-time = "2025-08-12T05:52:10.331Z" }, - { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551, upload-time = "2025-08-12T05:52:31.137Z" }, - { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464, upload-time = "2025-08-12T05:53:01.204Z" }, - { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748, upload-time = "2025-08-12T05:53:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810, upload-time = "2025-08-12T05:52:51.906Z" }, - { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482, upload-time = "2025-08-12T05:51:45.79Z" }, - { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674, upload-time = "2025-08-12T05:51:34.629Z" }, - { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959, upload-time = "2025-08-12T05:51:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376, upload-time = "2025-08-12T05:52:32.134Z" }, - { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604, upload-time = "2025-08-12T05:52:11.663Z" }, - { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782, upload-time = "2025-08-12T05:52:12.626Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076, upload-time = "2025-08-12T05:52:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457, upload-time = "2025-08-12T05:53:03.936Z" }, - { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745, upload-time = "2025-08-12T05:53:02.885Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806, upload-time = "2025-08-12T05:52:53.368Z" }, - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, + { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" }, + { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" }, + { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" }, + { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" }, + { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" }, + { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" }, + { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" }, + { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" }, + { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" }, + { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" }, + { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" }, + { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" }, + { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" }, + { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, + { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, + { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, + { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, + { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, + { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, + { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, + { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, + { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, + { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, + { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, + { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, + { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, + { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] [[package]] @@ -9836,11 +9852,11 @@ wheels = [ [[package]] name = "zipp" -version = "3.23.0" +version = "3.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ] [[package]] From 9caed61f36b9b7383a870bc6d0564ea63e7570b5 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Tue, 21 Apr 2026 21:52:17 +0800 Subject: [PATCH 19/32] chore: remove scarf install tracking --- lib/crewai/src/crewai/__init__.py | 53 +++++-------------------------- 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/lib/crewai/src/crewai/__init__.py b/lib/crewai/src/crewai/__init__.py index 444cb5d07..b59fc893d 100644 --- a/lib/crewai/src/crewai/__init__.py +++ b/lib/crewai/src/crewai/__init__.py @@ -1,10 +1,9 @@ -import contextvars -import threading -from typing import Any -import urllib.request +import importlib +import sys +from typing import TYPE_CHECKING, Annotated, Any import warnings -from pydantic import PydanticUserError +from pydantic import Field, PydanticUserError from crewai.agent.core import Agent from crewai.agent.planning_config import PlanningConfig @@ -20,7 +19,10 @@ from crewai.state.checkpoint_config import CheckpointConfig # noqa: F401 from crewai.task import Task from crewai.tasks.llm_guardrail import LLMGuardrail from crewai.tasks.task_output import TaskOutput -from crewai.telemetry.telemetry import Telemetry + + +if TYPE_CHECKING: + from crewai.memory.unified_memory import Memory def _suppress_pydantic_deprecation_warnings() -> None: @@ -47,37 +49,6 @@ def _suppress_pydantic_deprecation_warnings() -> None: _suppress_pydantic_deprecation_warnings() __version__ = "1.14.3a1" -_telemetry_submitted = False - - -def _track_install() -> None: - """Track package installation/first-use via Scarf analytics.""" - global _telemetry_submitted - - if _telemetry_submitted or Telemetry._is_telemetry_disabled(): - return - - try: - pixel_url = "https://api.scarf.sh/v2/packages/CrewAI/crewai/docs/00f2dad1-8334-4a39-934e-003b2e1146db" - - req = urllib.request.Request(pixel_url) # noqa: S310 - req.add_header("User-Agent", f"CrewAI-Python/{__version__}") - - with urllib.request.urlopen(req, timeout=2): # noqa: S310 - _telemetry_submitted = True - except Exception: # noqa: S110 - pass - - -def _track_install_async() -> None: - """Track installation in background thread to avoid blocking imports.""" - if not Telemetry._is_telemetry_disabled(): - ctx = contextvars.copy_context() - thread = threading.Thread(target=ctx.run, args=(_track_install,), daemon=True) - thread.start() - - -_track_install_async() _LAZY_IMPORTS: dict[str, tuple[str, str]] = { "Memory": ("crewai.memory.unified_memory", "Memory"), @@ -88,8 +59,6 @@ def __getattr__(name: str) -> Any: """Lazily import heavy modules (e.g. Memory → lancedb) on first access.""" if name in _LAZY_IMPORTS: module_path, attr = _LAZY_IMPORTS[name] - import importlib - mod = importlib.import_module(module_path) val = getattr(mod, attr) globals()[name] = val @@ -147,8 +116,6 @@ try: except ImportError: pass - import sys - _full_namespace = { **_base_namespace, "ToolsHandler": _ToolsHandler, @@ -191,10 +158,6 @@ try: Flow.model_rebuild(force=True, _types_namespace=_full_namespace) _AgentExecutor.model_rebuild(force=True, _types_namespace=_full_namespace) - from typing import Annotated - - from pydantic import Field - from crewai.state.runtime import RuntimeState Entity = Annotated[ From 84a4d47aa7496843ad59d11f0ee5c60207d92af1 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:55:39 -0700 Subject: [PATCH 20/32] updated descriptions and applied the actual translations (#5572) --- README.md | 10 + docs/ar/guides/coding-tools/build-with-ai.mdx | 181 ++++++++++-------- docs/en/guides/coding-tools/build-with-ai.mdx | 13 +- docs/ko/guides/coding-tools/build-with-ai.mdx | 181 ++++++++++-------- .../guides/coding-tools/build-with-ai.mdx | 179 +++++++++-------- 5 files changed, 309 insertions(+), 255 deletions(-) diff --git a/README.md b/README.md index bcb6634aa..817f5155f 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,17 @@ Using an AI coding agent? Teach it CrewAI best practices in one command: **Claude Code:** ```shell /plugin marketplace add crewAIInc/skills +/plugin install crewai-skills@crewai-plugins +/reload-plugins ``` +Four skills that activate automatically when you ask relevant CrewAI questions: + +| Skill | When it runs | +|-------|--------------| +| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.py` / `main.py` | +| `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails | +| `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review | +| `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details | **Cursor, Codex, Windsurf, and others ([skills.sh](https://skills.sh/crewaiinc/skills)):** ```shell diff --git a/docs/ar/guides/coding-tools/build-with-ai.mdx b/docs/ar/guides/coding-tools/build-with-ai.mdx index 744ad1bf4..4ea73abb9 100644 --- a/docs/ar/guides/coding-tools/build-with-ai.mdx +++ b/docs/ar/guides/coding-tools/build-with-ai.mdx @@ -1,15 +1,15 @@ --- -title: "Build with AI" -description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +title: "البناء باستخدام الذكاء الاصطناعي" +description: "كل ما يحتاجه وكلاء البرمجة بالذكاء الاصطناعي للبناء والنشر والتوسع مع CrewAI — المهارات، وثائق مقروءة آلياً، النشر، وميزات المؤسسات." icon: robot mode: "wide" --- -# Build with AI +# البناء باستخدام الذكاء الاصطناعي -CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. +CrewAI مُصمَّم أصلاً للعمل مع الذكاء الاصطناعي. تجمع هذه الصفحة ما يحتاجه وكيل البرمجة بالذكاء الاصطناعي للبناء مع CrewAI — سواءً كان Claude Code أو Codex أو Cursor أو Gemini CLI أو أي مساعد آخر يساعد المطوّر على إيصال الـ crews والـ flows. -### Supported Coding Agents +### وكلاء البرمجة المدعومون @@ -20,128 +20,139 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee - This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + صُممت هذه الصفحة للبشر وللمساعدين الذكيين على حدٍّ سواء. إذا كنت وكيل برمجة، ابدأ بـ **Skills** للحصول على سياق CrewAI، ثم استخدم **llms.txt** للوصول الكامل إلى الوثائق. --- -## 1. Skills — Teach Your Agent CrewAI +## 1. Skills — علِّم وكيلك CrewAI -**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. +**Skills** حزم تعليمات تمنح وكلاء البرمجة معرفة عميقة بـ CrewAI — كيفية إنشاء هيكل Flows، وضبط Crews، استخدام الأدوات، واتباع اتفاقيات الإطار. - + Anthropic - CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: - ``` + مهارات CrewAI متاحة في **سوق إضافات Claude Code** — نفس قناة التوزيع التي تستخدمها شركات رائدة في مجال الذكاء الاصطناعي: + ```shell /plugin marketplace add crewAIInc/skills + /plugin install crewai-skills@crewai-plugins + /reload-plugins ``` + + تُفعَّل أربع مهارات تلقائياً عند طرح أسئلة متعلقة بـ CrewAI: + + | المهارة | متى تُستخدم | + |---------|-------------| + | `getting-started` | مشاريع جديدة، الاختيار بين `LLM.call()` / `Agent` / `Crew` / `Flow`، ربط `crew.py` / `main.py` | + | `design-agent` | ضبط الوكلاء — الدور، الهدف، الخلفية، الأدوات، نماذج اللغة، الذاكرة، الحدود الآمنة | + | `design-task` | وصف المهام، التبعيات، المخرجات المنظمة (`output_pydantic`، `output_json`)، المراجعة البشرية | + | `ask-docs` | الاستعلام من [خادم CrewAI docs MCP](https://docs.crewai.com/mcp) للحصول على تفاصيل واجهة البرمجة الحالية | - - Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + + يعمل مع Claude Code أو Codex أو Cursor أو Gemini CLI أو أي وكيل برمجة: ```shell npx skills add crewaiinc/skills ``` - Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + يُجلب من [سجل skills.sh](https://skills.sh/crewaiinc/skills). - - Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + استخدم إحدى الطريقتين أعلاه — سوق إضافات Claude Code أو `npx skills add`. كلاهما يثبّت الحزمة الرسمية [crewAIInc/skills](https://github.com/crewAIInc/skills). - - The skill pack teaches your agent: - - **Flows** — stateful apps, steps, and crew kickoffs - - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation - - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools - - **Project layout** — CLI scaffolds and repo conventions - - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + تعلّم الحزمة وكيلك: + - **Flows** — تطبيقات ذات حالة، خطوات، وتشغيل crews + - **Crews والوكلاء** — أنماط YAML أولاً، الأدوار، المهام، التفويض + - **الأدوات والتكاملات** — البحث، واجهات API، خوادم MCP، وأدوات CrewAI الشائعة + - **هيكل المشروع** — هياكل CLI واتفاقيات المستودع + - **أنماط محدثة** — يتماشى مع وثائق CrewAI الحالية وأفضل الممارسات - - Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + يمكن لوكيلك الآن إنشاء هيكل وبناء مشاريع CrewAI دون أن تعيد شرح الإطار في كل جلسة. - - How skills work in CrewAI agents — injection, activation, and patterns. + + كيف تعمل المهارات في وكلاء CrewAI — الحقن، التفعيل، والأنماط. - - Overview of the crewAIInc/skills pack and what it includes. + + نظرة على حزمة crewAIInc/skills وما تتضمنه. - - Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + إعداد AGENTS.md لـ Claude Code وCodex وCursor وGemini CLI. - - Official listing — skills, install stats, and audits. + + القائمة الرسمية — المهارات، إحصاءات التثبيت، والتدقيق. --- -## 2. llms.txt — Machine-Readable Docs +## 2. llms.txt — وثائق مقروءة آلياً -CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. +ينشر CrewAI ملف `llms.txt` يمنح المساعدين الذكيين وصولاً مباشراً إلى الوثائق الكاملة بصيغة مقروءة آلياً. ``` https://docs.crewai.com/llms.txt ``` - - [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + [`llms.txt`](https://llmstxt.org/) معيار ناشئ لجعل الوثائق قابلة للاستهلاك من قبل نماذج اللغة الكبيرة. بدلاً من استخراج HTML، يمكن لوكيلك جلب ملف نصي واحد منظم بكل المحتوى المطلوب. - CrewAI's `llms.txt` is **already live** — your agent can use it right now. + ملف `llms.txt` الخاص بـ CrewAI **متاح فعلياً** — يمكن لوكيلك استخدامه الآن. - - Point your coding agent at the URL when it needs CrewAI reference docs: + + وجِّه وكيل البرمجة إلى عنوان URL عندما يحتاج إلى مرجع CrewAI: ``` Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. ``` - Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + يمكن للعديد من وكلاء البرمجة (Claude Code، Cursor، وغيرهما) جلب عناوين URL مباشرة. يحتوي الملف على وثائق منظمة تغطي مفاهيم CrewAI وواجهات البرمجة والأدلة. - - - **No scraping required** — clean, structured content in one request - - **Always up-to-date** — served directly from docs.crewai.com - - **Optimized for LLMs** — formatted for context windows, not browsers - - **Complements skills** — skills teach patterns, llms.txt provides reference + + - **دون استخراج ويب** — محتوى نظيف ومنظم في طلب واحد + - **دائماً محدث** — يُقدَّم مباشرة من docs.crewai.com + - **محسّن لنماذج اللغة** — مُنسَّق لنوافذ السياق لا للمتصفحات + - **يُكمّل Skills** — المهارات تعلّم الأنماط، وllms.txt يوفّر المرجع --- -## 3. Deploy to Enterprise +## 3. النشر للمؤسسات -Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. +انتقل من crew محلي إلى الإنتاج على **CrewAI AMP** (منصة إدارة الوكلاء) في دقائق. - - Scaffold and test your crew or flow: + + أنشئ الهيكل واختبر crew أو flow: ```bash crewai create crew my_crew cd my_crew crewai run ``` - - Ensure your project structure is ready: + + تأكد أن هيكل مشروعك جاهز: ```bash crewai deploy --prepare ``` - See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + راجع [دليل التحضير](/ar/enterprise/guides/prepare-for-deployment) لتفاصيل الهيكل والمتطلبات. - - Push to the CrewAI AMP platform: + + ادفع إلى منصة CrewAI AMP: ```bash crewai deploy ``` - You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + يمكنك أيضاً النشر عبر [تكامل GitHub](/ar/enterprise/guides/deploy-to-amp) أو [Crew Studio](/ar/enterprise/guides/enable-crew-studio). - - Your deployed crew gets a REST API endpoint. Integrate it into any application: + + يحصل الـ crew المنشور على نقطة نهاية REST. دمجه في أي تطبيق: ```bash curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ -H "Authorization: Bearer $CREWAI_API_KEY" \ @@ -152,55 +163,55 @@ Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) - - Full deployment guide — CLI, GitHub, and Crew Studio methods. + + دليل النشر الكامل — CLI وGitHub وCrew Studio. - - Platform overview — what AMP provides for production crews. + + نظرة على المنصة — ما يوفّره AMP لـ crews في الإنتاج. --- -## 4. Enterprise Features +## 4. ميزات المؤسسات -CrewAI AMP is built for production teams. Here's what you get beyond deployment. +CrewAI AMP مُصمَّم لفرق الإنتاج. إليك ما تحصل عليه بعد النشر. - - Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + مسارات تنفيذ مفصّلة، وسجلات، ومقاييس أداء لكل تشغيل crew. راقب قرارات الوكلاء، استدعاءات الأدوات، وإكمال المهام في الوقت الفعلي. - No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + واجهة منخفضة/بدون كود لإنشاء crews وتخصيصها ونشرها بصرياً — ثم التصدير إلى الشيفرة أو النشر مباشرة. - - Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + بث أحداث فورية من تنفيذات الـ crews إلى أنظمتك. تكامل مع Slack أو Zapier أو أي مستهلك ويبهوك. - - SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + SSO وRBAC وضوابط على مستوى المؤسسة. أدر من يمكنه إنشاء crews ونشرها والوصول إليها. - - Publish and share custom tools across your organization. Install community tools from the registry. + + انشر وشارك أدواتاً مخصصة عبر مؤسستك. ثبّت أدوات المجتمع من السجل. - - Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + شغّل CrewAI AMP على بنيتك التحتية. قدرات المنصة كاملة مع ضوابط إقامة البيانات والامتثال. - - AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + لفرق تحتاج نقل سير عمل وكلاء الذكاء الاصطناعي من النماذج الأولية إلى الإنتاج — مع المراقبة وضوابط الوصول والبنية التحتية القابلة للتوسع. سواءً كنت ناشئاً أو مؤسسة كبيرة، يتولى AMP التعقيد التشغيلي لتتفرغ لبناء الوكلاء. - - - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production - - **Factory (self-hosted)** — run on your own infrastructure for full data control - - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + - **السحابة (app.crewai.com)** — تُدار من CrewAI، أسرع طريق إلى الإنتاج + - **Factory (استضافة ذاتية)** — على بنيتك التحتية لسيطرة كاملة على البيانات + - **هجين** — دمج السحابة والاستضافة الذاتية حسب حساسية البيانات - - Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + سجّل في [app.crewai.com](https://app.crewai.com) لمعرفة الخطط الحالية. تسعير المؤسسات وFactory متاح عند الطلب. - - Sign up and deploy your first crew to production. + + سجّل وانشر أول crew لك في الإنتاج. diff --git a/docs/en/guides/coding-tools/build-with-ai.mdx b/docs/en/guides/coding-tools/build-with-ai.mdx index 744ad1bf4..2badb284e 100644 --- a/docs/en/guides/coding-tools/build-with-ai.mdx +++ b/docs/en/guides/coding-tools/build-with-ai.mdx @@ -33,9 +33,20 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee Anthropic CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: - ``` + ```shell /plugin marketplace add crewAIInc/skills + /plugin install crewai-skills@crewai-plugins + /reload-plugins ``` + + Four skills activate automatically when you ask relevant CrewAI questions: + + | Skill | When it runs | + |-------|--------------| + | `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.py` / `main.py` | + | `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails | + | `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review | + | `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details | Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: diff --git a/docs/ko/guides/coding-tools/build-with-ai.mdx b/docs/ko/guides/coding-tools/build-with-ai.mdx index 744ad1bf4..22f6b25d8 100644 --- a/docs/ko/guides/coding-tools/build-with-ai.mdx +++ b/docs/ko/guides/coding-tools/build-with-ai.mdx @@ -1,15 +1,15 @@ --- -title: "Build with AI" -description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +title: "AI와 함께 빌드하기" +description: "CrewAI로 빌드·배포·확장하는 데 필요한 모든 것 — 스킬, 기계가 읽을 수 있는 문서, 배포, 엔터프라이즈 기능을 AI 코딩 에이전트용으로 정리했습니다." icon: robot mode: "wide" --- -# Build with AI +# AI와 함께 빌드하기 -CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. +CrewAI는 AI 네이티브입니다. 이 페이지는 Claude Code, Codex, Cursor, Gemini CLI 등 개발자가 crew와 flow를 배포하도록 돕는 코딩 에이전트가 CrewAI로 빌드할 때 필요한 내용을 한곳에 모았습니다. -### Supported Coding Agents +### 지원 코딩 에이전트 @@ -20,128 +20,139 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee - This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + 이 페이지는 사람과 AI 어시스턴트 모두를 위해 작성되었습니다. 코딩 에이전트라면 CrewAI 맥락은 **Skills**부터, 전체 문서 접근은 **llms.txt**를 사용하세요. --- -## 1. Skills — Teach Your Agent CrewAI +## 1. Skills — 에이전트에게 CrewAI 가르치기 -**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. +**Skills**는 코딩 에이전트에게 Flow 스캐폴딩, Crew 구성, 도구 사용, 프레임워크 관례 등 CrewAI에 대한 깊은 지식을 담은 지침 묶음입니다. - + Anthropic - CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: - ``` + CrewAI 스킬은 **Claude Code 플러그인 마켓플레이스**에서 제공됩니다. AI 네이티브 기업들이 쓰는 것과 같은 배포 채널입니다. + ```shell /plugin marketplace add crewAIInc/skills + /plugin install crewai-skills@crewai-plugins + /reload-plugins ``` + + CrewAI와 관련된 질문을 하면 다음 네 가지 스킬이 자동으로 활성화됩니다. + + | 스킬 | 실행 시점 | + |------|-------------| + | `getting-started` | 새 프로젝트 스캐폴딩, `LLM.call()` / `Agent` / `Crew` / `Flow` 선택, `crew.py` / `main.py` 연결 | + | `design-agent` | 에이전트 구성 — 역할, 목표, 배경 이야기, 도구, LLM, 메모리, 가드레일 | + | `design-task` | 태스크 설명, 의존성, 구조화된 출력(`output_pydantic`, `output_json`), 사람 검토 | + | `ask-docs` | 최신 API 정보를 위해 [CrewAI 문서 MCP 서버](https://docs.crewai.com/mcp) 조회 | - - Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + + Claude Code, Codex, Cursor, Gemini CLI 등 모든 코딩 에이전트에서 사용할 수 있습니다. ```shell npx skills add crewaiinc/skills ``` - Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + [skills.sh 레지스트리](https://skills.sh/crewaiinc/skills)에서 가져옵니다. - - Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + 위 방법 중 하나를 사용하세요 — Claude Code 플러그인 마켓플레이스 또는 `npx skills add`. 둘 다 공식 [crewAIInc/skills](https://github.com/crewAIInc/skills) 팩을 설치합니다. - - The skill pack teaches your agent: - - **Flows** — stateful apps, steps, and crew kickoffs - - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation - - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools - - **Project layout** — CLI scaffolds and repo conventions - - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + 스킬 팩이 에이전트에게 알려 주는 내용: + - **Flow** — 상태ful 앱, 단계, crew 킥오프 + - **Crew 및 에이전트** — YAML 우선 패턴, 역할, 태스크, 위임 + - **도구 및 통합** — 검색, API, MCP 서버, 일반적인 CrewAI 도구 + - **프로젝트 레이아웃** — CLI 스캐폴드와 저장소 관례 + - **최신 패턴** — 현재 CrewAI 문서와 모범 사례 반영 - - Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + 매 세션마다 프레임워크를 다시 설명하지 않아도 에이전트가 CrewAI 프로젝트를 스캐폴딩하고 빌드할 수 있습니다. - - How skills work in CrewAI agents — injection, activation, and patterns. + + CrewAI 에이전트에서 스킬이 동작하는 방식 — 주입, 활성화, 패턴. - - Overview of the crewAIInc/skills pack and what it includes. + + crewAIInc/skills 팩 개요와 포함 내용. - - Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + Claude Code, Codex, Cursor, Gemini CLI용 AGENTS.md 설정. - - Official listing — skills, install stats, and audits. + + 공식 목록 — 스킬, 설치 통계, 감사 정보. --- -## 2. llms.txt — Machine-Readable Docs +## 2. llms.txt — 기계가 읽을 수 있는 문서 -CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. +CrewAI는 AI 어시스턴트가 전체 문서에 기계가 읽을 수 있는 형태로 바로 접근할 수 있도록 `llms.txt` 파일을 제공합니다. ``` https://docs.crewai.com/llms.txt ``` - - [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + [`llms.txt`](https://llmstxt.org/)는 문서를 대규모 언어 모델이 소비하기 쉽게 만드는 새로운 표준입니다. HTML을 스크래핑하는 대신, 필요한 내용이 담긴 하나의 구조화된 텍스트 파일을 가져올 수 있습니다. - CrewAI's `llms.txt` is **already live** — your agent can use it right now. + CrewAI의 `llms.txt`는 **이미 제공 중**이며, 에이전트가 바로 사용할 수 있습니다. - - Point your coding agent at the URL when it needs CrewAI reference docs: + + CrewAI 참고 문서가 필요할 때 코딩 에이전트에 URL을 알려 주세요. ``` Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. ``` - Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + Claude Code, Cursor 등 많은 코딩 에이전트가 URL을 직접 가져올 수 있습니다. 파일에는 CrewAI 개념, API, 가이드를 아우르는 구조화된 문서가 포함되어 있습니다. - - - **No scraping required** — clean, structured content in one request - - **Always up-to-date** — served directly from docs.crewai.com - - **Optimized for LLMs** — formatted for context windows, not browsers - - **Complements skills** — skills teach patterns, llms.txt provides reference + + - **스크래핑 불필요** — 한 번의 요청으로 깔끔한 구조화 콘텐츠 + - **항상 최신** — docs.crewai.com에서 직접 제공 + - **LLM에 최적화** — 브라우저가 아니라 컨텍스트 윈도우에 맞게 포맷 + - **스킬과 상호 보완** — 스킬은 패턴을, llms.txt는 참조를 제공 --- -## 3. Deploy to Enterprise +## 3. 엔터프라이즈에 배포 -Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. +로컬 crew를 몇 분 안에 **CrewAI AMP**(Agent Management Platform) 프로덕션으로 가져가세요. - - Scaffold and test your crew or flow: + + crew 또는 flow를 스캐폴딩하고 테스트합니다. ```bash crewai create crew my_crew cd my_crew crewai run ``` - - Ensure your project structure is ready: + + 프로젝트 구조가 준비되었는지 확인합니다. ```bash crewai deploy --prepare ``` - See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + 구조와 요구 사항은 [준비 가이드](/ko/enterprise/guides/prepare-for-deployment)를 참고하세요. - - Push to the CrewAI AMP platform: + + CrewAI AMP 플랫폼으로 푸시합니다. ```bash crewai deploy ``` - You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + [GitHub 연동](/ko/enterprise/guides/deploy-to-amp) 또는 [Crew Studio](/ko/enterprise/guides/enable-crew-studio)로도 배포할 수 있습니다. - - Your deployed crew gets a REST API endpoint. Integrate it into any application: + + 배포된 crew는 REST API 엔드포인트를 받습니다. 모든 애플리케이션에 통합할 수 있습니다. ```bash curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ -H "Authorization: Bearer $CREWAI_API_KEY" \ @@ -152,55 +163,55 @@ Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) - - Full deployment guide — CLI, GitHub, and Crew Studio methods. + + 전체 배포 가이드 — CLI, GitHub, Crew Studio 방법. - - Platform overview — what AMP provides for production crews. + + 플랫폼 개요 — 프로덕션 crew에 AMP가 제공하는 것. --- -## 4. Enterprise Features +## 4. 엔터프라이즈 기능 -CrewAI AMP is built for production teams. Here's what you get beyond deployment. +CrewAI AMP는 프로덕션 팀을 위해 만들어졌습니다. 배포 외에 제공되는 것은 다음과 같습니다. - - Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + 모든 crew 실행에 대한 상세 실행 추적, 로그, 성능 지표. 에이전트 결정, 도구 호출, 태스크 완료를 실시간으로 모니터링합니다. - No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + 시각적으로 crew를 만들고, 맞춤 설정하고, 배포하는 노코드/로코드 인터페이스 — 코드로 보내거나 바로 배포할 수 있습니다. - - Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + crew 실행에서 실시간 이벤트를 시스템으로 스트리밍합니다. Slack, Zapier 등 웹훅 소비자와 연동할 수 있습니다. - - SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + SSO, RBAC, 조직 단위 제어. 팀 전체에서 crew 생성·배포·접근 권한을 관리합니다. - - Publish and share custom tools across your organization. Install community tools from the registry. + + 조직 전체에 맞춤 도구를 게시하고 공유합니다. 레지스트리에서 커뮤니티 도구를 설치합니다. - - Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + 자체 인프라에서 CrewAI AMP를 실행합니다. 데이터 상주와 규정 준수 제어와 함께 플랫폼 전체 기능을 사용할 수 있습니다. - - AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + AI 에이전트 워크플로를 프로토타입에서 프로덕션으로 옮겨야 하는 팀을 위한 제품입니다. 관측 가능성, 접근 제어, 확장 가능한 인프라를 제공합니다. 스타트업이든 대기업이든 운영 복잡도는 AMP가 맡고, 에이전트 구축에 집중할 수 있습니다. - - - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production - - **Factory (self-hosted)** — run on your own infrastructure for full data control - - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + - **클라우드 (app.crewai.com)** — CrewAI가 관리, 프로덕션까지 가장 빠른 경로 + - **Factory(셀프 호스팅)** — 데이터 통제를 위해 자체 인프라에서 실행 + - **하이브리드** — 민감도에 따라 클라우드와 셀프 호스팅을 혼합 - - Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + [app.crewai.com](https://app.crewai.com)에 가입하면 현재 요금제를 확인할 수 있습니다. 엔터프라이즈 및 Factory 가격은 문의 시 안내합니다. - - Sign up and deploy your first crew to production. + + 가입하고 첫 crew를 프로덕션에 배포해 보세요. diff --git a/docs/pt-BR/guides/coding-tools/build-with-ai.mdx b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx index 744ad1bf4..bc697ea10 100644 --- a/docs/pt-BR/guides/coding-tools/build-with-ai.mdx +++ b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx @@ -1,15 +1,15 @@ --- -title: "Build with AI" -description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features." +title: "Construa com IA" +description: "Tudo o que agentes de codificação com IA precisam para criar, implantar e escalar com CrewAI — skills, documentação legível por máquina, implantação e recursos enterprise." icon: robot mode: "wide" --- -# Build with AI +# Construa com IA -CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows. +O CrewAI é nativo de IA. Esta página reúne o que um agente de codificação com IA precisa para construir com CrewAI — seja Claude Code, Codex, Cursor, Gemini CLI ou qualquer outro assistente que ajude um desenvolvedor a entregar crews e flows. -### Supported Coding Agents +### Agentes de codificação compatíveis @@ -20,128 +20,139 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee - This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access. + Esta página serve para humanos e para assistentes de IA. Se você é um agente de codificação, comece por **Skills** para obter contexto do CrewAI e depois use **llms.txt** para acesso completo à documentação. --- -## 1. Skills — Teach Your Agent CrewAI +## 1. Skills — ensine CrewAI ao seu agente -**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions. +**Skills** são pacotes de instruções que dão aos agentes de codificação conhecimento profundo do CrewAI — como estruturar Flows, configurar Crews, usar ferramentas e seguir convenções do framework. Anthropic - CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies: - ``` + As skills do CrewAI estão no **plugin marketplace do Claude Code** — o mesmo canal usado por empresas líderes em IA: + ```shell /plugin marketplace add crewAIInc/skills + /plugin install crewai-skills@crewai-plugins + /reload-plugins ``` + + Quatro skills são ativadas automaticamente quando você faz perguntas relevantes sobre CrewAI: + + | Skill | Quando é usada | + |-------|----------------| + | `getting-started` | Novos projetos, escolha entre `LLM.call()` / `Agent` / `Crew` / `Flow`, arquivos `crew.py` / `main.py` | + | `design-agent` | Configurar agentes — papel, objetivo, história, ferramentas, LLMs, memória, guardrails | + | `design-task` | Descrever tarefas, dependências, saída estruturada (`output_pydantic`, `output_json`), revisão humana | + | `ask-docs` | Consultar o [servidor MCP da documentação CrewAI](https://docs.crewai.com/mcp) em tempo real para detalhes de API | - - Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent: + + Funciona com Claude Code, Codex, Cursor, Gemini CLI ou qualquer agente de codificação: ```shell npx skills add crewaiinc/skills ``` - Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills). + Obtido do [registro skills.sh](https://skills.sh/crewaiinc/skills). - - Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack. + + Use um dos métodos acima — o plugin marketplace do Claude Code ou `npx skills add`. Ambos instalam o pacote oficial [crewAIInc/skills](https://github.com/crewAIInc/skills). - - The skill pack teaches your agent: - - **Flows** — stateful apps, steps, and crew kickoffs - - **Crews & Agents** — YAML-first patterns, roles, tasks, delegation - - **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools - - **Project layout** — CLI scaffolds and repo conventions - - **Up-to-date patterns** — tracks current CrewAI docs and best practices + + O pacote ensina ao seu agente: + - **Flows** — apps com estado, passos e disparo de crews + - **Crews e agentes** — padrões YAML-first, papéis, tarefas, delegação + - **Ferramentas e integrações** — busca, APIs, servidores MCP e ferramentas comuns do CrewAI + - **Estrutura do projeto** — scaffolds da CLI e convenções de repositório + - **Padrões atualizados** — alinhado à documentação e às melhores práticas atuais do CrewAI - - Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session. + + Seu agente pode estruturar e construir projetos CrewAI sem você precisar reexplicar o framework a cada sessão. - - How skills work in CrewAI agents — injection, activation, and patterns. + + Como skills funcionam em agentes CrewAI — injeção, ativação e padrões. - - Overview of the crewAIInc/skills pack and what it includes. + + Visão geral do pacote crewAIInc/skills e do que ele inclui. - - Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI. + + Configure o AGENTS.md para Claude Code, Codex, Cursor e Gemini CLI. - - Official listing — skills, install stats, and audits. + + Listagem oficial — skills, estatísticas de instalação e auditorias. --- -## 2. llms.txt — Machine-Readable Docs +## 2. llms.txt — documentação legível por máquina -CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format. +O CrewAI publica um arquivo `llms.txt` que dá aos assistentes de IA acesso direto à documentação completa em formato legível por máquinas. ``` https://docs.crewai.com/llms.txt ``` - - [`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs. + + [`llms.txt`](https://llmstxt.org/) é um padrão emergente para tornar a documentação consumível por grandes modelos de linguagem. Em vez de fazer scraping de HTML, seu agente pode buscar um único arquivo de texto estruturado com o conteúdo necessário. - CrewAI's `llms.txt` is **already live** — your agent can use it right now. + O `llms.txt` do CrewAI **já está no ar** — seu agente pode usar agora. - - Point your coding agent at the URL when it needs CrewAI reference docs: + + Indique ao agente de codificação a URL quando precisar da referência do CrewAI: ``` Fetch https://docs.crewai.com/llms.txt for CrewAI documentation. ``` - Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides. + Muitos agentes (Claude Code, Cursor etc.) conseguem buscar URLs diretamente. O arquivo contém documentação estruturada sobre conceitos, APIs e guias do CrewAI. - - - **No scraping required** — clean, structured content in one request - - **Always up-to-date** — served directly from docs.crewai.com - - **Optimized for LLMs** — formatted for context windows, not browsers - - **Complements skills** — skills teach patterns, llms.txt provides reference + + - **Sem scraping** — conteúdo limpo e estruturado em uma requisição + - **Sempre atualizado** — servido diretamente de docs.crewai.com + - **Otimizado para LLMs** — formatado para janelas de contexto, não para navegadores + - **Complementa as skills** — skills ensinam padrões; llms.txt fornece referência --- -## 3. Deploy to Enterprise +## 3. Implantação enterprise -Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes. +Do crew local à produção no **CrewAI AMP** (Agent Management Platform) em minutos. - - Scaffold and test your crew or flow: + + Estruture e teste seu crew ou flow: ```bash crewai create crew my_crew cd my_crew crewai run ``` - - Ensure your project structure is ready: + + Garanta que a estrutura do projeto está pronta: ```bash crewai deploy --prepare ``` - See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements. + Veja o [guia de preparação](/pt-BR/enterprise/guides/prepare-for-deployment) para detalhes de estrutura e requisitos. - - Push to the CrewAI AMP platform: + + Envie para a plataforma CrewAI AMP: ```bash crewai deploy ``` - You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio). + Também é possível implantar pela [integração com GitHub](/pt-BR/enterprise/guides/deploy-to-amp) ou pelo [Crew Studio](/pt-BR/enterprise/guides/enable-crew-studio). - - Your deployed crew gets a REST API endpoint. Integrate it into any application: + + O crew implantado recebe um endpoint REST. Integre em qualquer aplicação: ```bash curl -X POST https://app.crewai.com/api/v1/crews//kickoff \ -H "Authorization: Bearer $CREWAI_API_KEY" \ @@ -152,55 +163,55 @@ Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) - - Full deployment guide — CLI, GitHub, and Crew Studio methods. + + Guia completo de implantação — CLI, GitHub e Crew Studio. - - Platform overview — what AMP provides for production crews. + + Visão da plataforma — o que o AMP oferece para crews em produção. --- -## 4. Enterprise Features +## 4. Recursos enterprise -CrewAI AMP is built for production teams. Here's what you get beyond deployment. +O CrewAI AMP foi feito para equipes em produção. Além da implantação, você obtém: - - Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time. + + Traces de execução, logs e métricas de desempenho para cada execução de crew. Monitore decisões de agentes, chamadas de ferramentas e conclusão de tarefas em tempo real. - No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly. + Interface no-code/low-code para criar, personalizar e implantar crews visualmente — exporte para código ou implante direto. - - Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer. + + Transmita eventos em tempo real das execuções para seus sistemas. Integre com Slack, Zapier ou qualquer consumidor de webhook. - - SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team. + + SSO, RBAC e controles em nível de organização. Gerencie quem pode criar, implantar e acessar crews. - - Publish and share custom tools across your organization. Install community tools from the registry. + + Publique e compartilhe ferramentas customizadas na organização. Instale ferramentas da comunidade a partir do registro. - - Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls. + + Execute o CrewAI AMP na sua infraestrutura. Capacidades completas da plataforma com residência de dados e controles de conformidade. - - AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents. + + Para equipes que precisam levar fluxos de agentes de IA do protótipo à produção — com observabilidade, controles de acesso e infraestrutura escalável. De startups a grandes empresas, o AMP cuida da complexidade operacional para você focar nos agentes. - - - **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production - - **Factory (self-hosted)** — run on your own infrastructure for full data control - - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements + + - **Nuvem (app.crewai.com)** — gerenciada pela CrewAI, caminho mais rápido para produção + - **Factory (self-hosted)** — na sua infraestrutura para controle total dos dados + - **Híbrido** — combine nuvem e self-hosted conforme a sensibilidade dos dados - - Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. + + Cadastre-se em [app.crewai.com](https://app.crewai.com) para ver os planos atuais. Preços enterprise e Factory sob consulta. - - Sign up and deploy your first crew to production. + + Cadastre-se e leve seu primeiro crew à produção. From 6d153284d4de4d3c4a5f458a91e36761e2074190 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Tue, 21 Apr 2026 10:12:24 -0700 Subject: [PATCH 21/32] =?UTF-8?q?fix:=20merge=20execution=20metadata=20on?= =?UTF-8?q?=20duplicate=20batch=20initialization=20in=20Tr=E2=80=A6=20(#55?= =?UTF-8?q?73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: merge execution metadata on duplicate batch initialization in TraceBatchManager - Updated TraceBatchManager to merge execution metadata when a batch is initialized multiple times. - Enhanced logging to reflect the merging of metadata during duplicate initialization. - Added a test case to verify that execution metadata is correctly merged when initializing a batch after a lazy action. * drop env events emitting from traces listener --- .../listeners/tracing/trace_batch_manager.py | 5 ++- .../listeners/tracing/trace_listener.py | 30 +------------- lib/crewai/src/crewai/flow/flow.py | 7 +++- lib/crewai/tests/tracing/test_tracing.py | 40 +++++++++++++++++++ 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py b/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py index d2a0912f6..e35fe66e1 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_batch_manager.py @@ -81,8 +81,11 @@ class TraceBatchManager: """Initialize a new trace batch (thread-safe)""" with self._batch_ready_cv: if self.current_batch is not None: + # Lazy init (e.g. DefaultEnvEvent) may have created the batch without + # execution_type; merge metadata from a later flow/crew initializer. + self.current_batch.execution_metadata.update(execution_metadata) logger.debug( - "Batch already initialized, skipping duplicate initialization" + "Batch already initialized, merged execution metadata and skipped duplicate initialization" ) return self.current_batch diff --git a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py index c4cc6cb71..046bc0f1a 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py @@ -60,12 +60,6 @@ from crewai.events.types.crew_events import ( CrewKickoffFailedEvent, CrewKickoffStartedEvent, ) -from crewai.events.types.env_events import ( - CCEnvEvent, - CodexEnvEvent, - CursorEnvEvent, - DefaultEnvEvent, -) from crewai.events.types.flow_events import ( FlowCreatedEvent, FlowFinishedEvent, @@ -212,7 +206,6 @@ class TraceCollectionListener(BaseEventListener): self._listeners_setup = True return - self._register_env_event_handlers(crewai_event_bus) self._register_flow_event_handlers(crewai_event_bus) self._register_context_event_handlers(crewai_event_bus) self._register_action_event_handlers(crewai_event_bus) @@ -221,25 +214,6 @@ class TraceCollectionListener(BaseEventListener): self._listeners_setup = True - def _register_env_event_handlers(self, event_bus: CrewAIEventsBus) -> None: - """Register handlers for environment context events.""" - - @event_bus.on(CCEnvEvent) - def on_cc_env(source: Any, event: CCEnvEvent) -> None: - self._handle_action_event("cc_env", source, event) - - @event_bus.on(CodexEnvEvent) - def on_codex_env(source: Any, event: CodexEnvEvent) -> None: - self._handle_action_event("codex_env", source, event) - - @event_bus.on(CursorEnvEvent) - def on_cursor_env(source: Any, event: CursorEnvEvent) -> None: - self._handle_action_event("cursor_env", source, event) - - @event_bus.on(DefaultEnvEvent) - def on_default_env(source: Any, event: DefaultEnvEvent) -> None: - self._handle_action_event("default_env", source, event) - def _register_flow_event_handlers(self, event_bus: CrewAIEventsBus) -> None: """Register handlers for flow events.""" @@ -286,8 +260,8 @@ class TraceCollectionListener(BaseEventListener): if self.batch_manager.batch_owner_type != "flow": # Always call _initialize_crew_batch to claim ownership. # If batch was already initialized by a concurrent action event - # (race condition with DefaultEnvEvent), initialize_batch() returns - # early but batch_owner_type is still correctly set to "crew". + # (e.g. LLM/tool before crew_kickoff_started), initialize_batch() + # returns early but batch_owner_type is still correctly set to "crew". # Skip only when a parent flow already owns the batch. self._initialize_crew_batch(source, event) self._handle_trace_event("crew_kickoff_started", source, event) diff --git a/lib/crewai/src/crewai/flow/flow.py b/lib/crewai/src/crewai/flow/flow.py index 88457f7aa..b363ebc71 100644 --- a/lib/crewai/src/crewai/flow/flow.py +++ b/lib/crewai/src/crewai/flow/flow.py @@ -1503,6 +1503,8 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): except Exception: logger.warning("FlowStartedEvent handler failed", exc_info=True) + get_env_context() + context = self._pending_feedback_context emit = context.emit default_outcome = context.default_outcome @@ -2004,7 +2006,6 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): restored = apply_checkpoint(self, from_checkpoint) if restored is not None: return restored.kickoff(inputs=inputs, input_files=input_files) - get_env_context() if self.stream: result_holder: list[Any] = [] current_task_info: TaskInfo = { @@ -2206,6 +2207,10 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): f"Flow started with ID: {self.flow_id}", color="bold magenta" ) + # After FlowStarted (when not suppressed): env events must not pre-empt + # trace batch init with implicit "crew" execution_type. + get_env_context() + if inputs is not None and "id" not in inputs: self._initialize_state(inputs) diff --git a/lib/crewai/tests/tracing/test_tracing.py b/lib/crewai/tests/tracing/test_tracing.py index 640aca832..38bb060bd 100644 --- a/lib/crewai/tests/tracing/test_tracing.py +++ b/lib/crewai/tests/tracing/test_tracing.py @@ -1640,3 +1640,43 @@ class TestBackendInitializedGatedOnSuccess: assert bm.backend_initialized is False assert bm.trace_batch_id is None + + +class TestTraceBatchManagerDuplicateInitMerge: + """Second initialize_batch call merges execution_metadata (flow after lazy action).""" + + def test_duplicate_initialize_merges_execution_metadata(self): + with ( + patch( + "crewai.events.listeners.tracing.trace_batch_manager.should_auto_collect_first_time_traces", + return_value=True, + ), + patch( + "crewai.events.listeners.tracing.trace_batch_manager.is_tracing_enabled_in_context", + return_value=True, + ), + ): + bm = TraceBatchManager() + bm.initialize_batch( + user_context={"privacy_level": "standard"}, + execution_metadata={ + "crew_name": "Unknown Crew", + "crewai_version": "9.9.9", + }, + ) + first_batch_id = bm.current_batch.batch_id + bm.initialize_batch( + user_context={"privacy_level": "standard"}, + execution_metadata={ + "flow_name": "ResearchFlow", + "execution_type": "flow", + "crewai_version": "9.9.9", + "execution_start": "2026-01-01T00:00:00+00:00", + }, + ) + + assert bm.current_batch.batch_id == first_batch_id + meta = bm.current_batch.execution_metadata + assert meta.get("execution_type") == "flow" + assert meta.get("flow_name") == "ResearchFlow" + assert meta.get("crew_name") == "Unknown Crew" From d4f9f875f7bcc9ee3e16d92e85465e1d5903bdd6 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Wed, 22 Apr 2026 01:22:19 +0800 Subject: [PATCH 22/32] fix: bump python-dotenv to >=1.2.2 for GHSA-mf9w-mj56-hr94 --- lib/crewai/pyproject.toml | 2 +- lib/devtools/pyproject.toml | 2 +- uv.lock | 41 ++++++++++++++++++------------------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index 4956c81f0..5b3c81850 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ "tokenizers>=0.21,<1", "openpyxl~=3.1.5", # Authentication and Security - "python-dotenv~=1.1.1", + "python-dotenv>=1.2.2,<2", "pyjwt>=2.9.0,<3", # TUI "textual>=7.5.0", diff --git a/lib/devtools/pyproject.toml b/lib/devtools/pyproject.toml index 7eebc9ea4..88938fa5c 100644 --- a/lib/devtools/pyproject.toml +++ b/lib/devtools/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "click~=8.1.7", "tomlkit~=0.13.2", "openai>=1.83.0,<3", - "python-dotenv~=1.1.1", + "python-dotenv>=1.2.2,<2", "pygithub~=1.59.1", "rich>=13.9.4", ] diff --git a/uv.lock b/uv.lock index 7062d317d..06a687ed3 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-18T07:00:00Z" +exclude-newer = "2026-04-17T16:00:00Z" [manifest] members = [ @@ -981,7 +981,7 @@ name = "coloredlogs" version = "15.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "humanfriendly" }, + { name = "humanfriendly", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ @@ -1382,7 +1382,7 @@ requires-dist = [ { name = "pydantic", specifier = "~=2.11.9" }, { name = "pydantic-settings", specifier = "~=2.10.1" }, { name = "pyjwt", specifier = ">=2.9.0,<3" }, - { name = "python-dotenv", specifier = "~=1.1.1" }, + { name = "python-dotenv", specifier = ">=1.2.2,<2" }, { name = "pyyaml", specifier = "~=6.0" }, { name = "qdrant-client", extras = ["fastembed"], marker = "extra == 'qdrant'", specifier = "~=1.14.3" }, { name = "qdrant-edge-py", marker = "extra == 'qdrant-edge'", specifier = ">=0.6.0" }, @@ -1414,7 +1414,7 @@ requires-dist = [ { name = "click", specifier = "~=8.1.7" }, { name = "openai", specifier = ">=1.83.0,<3" }, { name = "pygithub", specifier = "~=1.59.1" }, - { name = "python-dotenv", specifier = "~=1.1.1" }, + { name = "python-dotenv", specifier = ">=1.2.2,<2" }, { name = "rich", specifier = ">=13.9.4" }, { name = "tomlkit", specifier = "~=0.13.2" }, ] @@ -2205,7 +2205,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2223,14 +2223,14 @@ wheels = [ [[package]] name = "faker" -version = "40.15.0" +version = "40.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/13/6741787bd91c4109c7bed047d68273965cd52ce8a5f773c471b949334b6d/faker-40.15.0.tar.gz", hash = "sha256:20f3a6ec8c266b74d4c554e34118b21c3c2056c0b4a519d15c8decb3a4e6e795", size = 1967447, upload-time = "2026-04-17T20:05:27.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/95/4822ffe94723553789aef783104f4f18fc20d7c4c68e1bbd633e11d09758/faker-40.13.0.tar.gz", hash = "sha256:a0751c84c3abac17327d7bb4c98e8afe70ebf7821e01dd7d0b15cd8856415525", size = 1962043, upload-time = "2026-04-06T16:44:55.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" }, + { url = "https://files.pythonhosted.org/packages/da/8a/708103325edff16a0b0e004de0d37db8ba216a32713948c64d71f6d4a4c2/faker-40.13.0-py3-none-any.whl", hash = "sha256:c1298fd0d819b3688fb5fd358c4ba8f56c7c8c740b411fd3dbd8e30bf2c05019", size = 1994597, upload-time = "2026-04-06T16:44:53.698Z" }, ] [[package]] @@ -3014,7 +3014,7 @@ name = "humanfriendly" version = "10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, + { name = "pyreadline3", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } wheels = [ @@ -3100,11 +3100,11 @@ wheels = [ [[package]] name = "identify" -version = "2.6.19" +version = "2.6.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, + { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, ] [[package]] @@ -5225,13 +5225,12 @@ name = "onnxruntime" version = "1.23.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, + { name = "coloredlogs", marker = "python_full_version < '3.11'" }, + { name = "flatbuffers", marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "protobuf", marker = "python_full_version < '3.11'" }, + { name = "sympy", marker = "python_full_version < '3.11'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/35/d6/311b1afea060015b56c742f3531168c1644650767f27ef40062569960587/onnxruntime-1.23.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a7730122afe186a784660f6ec5807138bf9d792fa1df76556b27307ea9ebcbe3", size = 17195934, upload-time = "2025-10-27T23:06:14.143Z" }, @@ -7167,11 +7166,11 @@ wheels = [ [[package]] name = "python-dotenv" -version = "1.1.1" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] From 42d6c03ebc2ee7f2a7b1ce7b5c85911abb0bf758 Mon Sep 17 00:00:00 2001 From: Renato Nitta Date: Tue, 21 Apr 2026 15:57:19 -0300 Subject: [PATCH 23/32] fix: propagate implicit @CrewBase names to crew events (#5574) * fix: propagate implicit @CrewBase names to crew events * test: appease static analysis for @CrewBase kickoff test --------- Co-authored-by: Greyson LaLonde --- lib/crewai/src/crewai/project/annotations.py | 2 + lib/crewai/tests/test_crew.py | 57 ++++++++++++++++++++ lib/crewai/tests/test_project.py | 51 +++++++++++++++++- 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/lib/crewai/src/crewai/project/annotations.py b/lib/crewai/src/crewai/project/annotations.py index c198c979a..b4b4b69d3 100644 --- a/lib/crewai/src/crewai/project/annotations.py +++ b/lib/crewai/src/crewai/project/annotations.py @@ -237,6 +237,8 @@ def crew( self.tasks = instantiated_tasks crew_instance: Crew = _call_method(meth, self, *args, **kwargs) + if "name" not in crew_instance.model_fields_set: + crew_instance.name = getattr(self, "_crew_name", None) or crew_instance.name def callback_wrapper( hook: Callable[Concatenate[CrewInstance, P2], R2], instance: CrewInstance diff --git a/lib/crewai/tests/test_crew.py b/lib/crewai/tests/test_crew.py index 9db9ef4e2..3d6fe4602 100644 --- a/lib/crewai/tests/test_crew.py +++ b/lib/crewai/tests/test_crew.py @@ -8,6 +8,7 @@ from concurrent.futures import Future from hashlib import md5 import re import sys +from typing import Any, cast from unittest.mock import ANY, MagicMock, call, patch from crewai.agent import Agent @@ -17,6 +18,7 @@ from crewai.crew import Crew from crewai.crews.crew_output import CrewOutput from crewai.events.event_bus import crewai_event_bus from crewai.events.types.crew_events import ( + CrewKickoffStartedEvent, CrewTestCompletedEvent, CrewTestStartedEvent, CrewTrainCompletedEvent, @@ -4741,6 +4743,61 @@ def test_default_crew_name(researcher, writer): assert crew.name == "crew" +@pytest.mark.parametrize( + "explicit_name,expected", + [ + (None, "ResearchAutomation"), + ("My Research Automation", "My Research Automation"), + ], + ids=["class_name_from_decorator", "explicit_name_preserved"], +) +def test_crew_kickoff_started_emits_display_name( + researcher, writer, explicit_name, expected +): + """Kickoff events should use the decorator-provided display name when implicit.""" + from crewai.crews.utils import prepare_kickoff + from crewai.project import CrewBase, agent, crew, task + + @CrewBase + class ResearchAutomation: + agents_config = None + tasks_config = None + + @agent + def researcher(self): + return researcher + + @task + def first_task(self): + return Task( + description="Task 1", + expected_output="output", + agent=self.researcher(), + ) + + @crew + def crew(self): + crew_kwargs: dict[str, Any] = { + "agents": self.agents, + "tasks": self.tasks, + } + if explicit_name is not None: + crew_kwargs["name"] = explicit_name + return Crew(**crew_kwargs) + + captured: list[str | None] = [] + with crewai_event_bus.scoped_handlers(): + + @crewai_event_bus.on(CrewKickoffStartedEvent) + def _capture(_source: Any, event: CrewKickoffStartedEvent) -> None: + captured.append(event.crew_name) + + automation_cls = cast(type[Any], ResearchAutomation) + prepare_kickoff(cast(Any, automation_cls()).crew(), inputs=None) + + assert captured == [expected] + + @pytest.mark.vcr() def test_memory_remember_receives_task_content(): """With memory=True, extract_memories receives raw content with task, agent, expected output, and result.""" diff --git a/lib/crewai/tests/test_project.py b/lib/crewai/tests/test_project.py index 9d7f332da..368afe7fd 100644 --- a/lib/crewai/tests/test_project.py +++ b/lib/crewai/tests/test_project.py @@ -1,4 +1,4 @@ -from typing import Any, ClassVar +from typing import Any, ClassVar, cast from unittest.mock import Mock, create_autospec, patch import pytest @@ -261,6 +261,55 @@ def test_crew_name(): assert crew._crew_name == "InternalCrew" +def test_crew_decorator_propagates_class_name_to_instance(): + """@crew-decorated factory method should set Crew.name to the decorated class name.""" + sample_agent = Agent(role="r", goal="g", backstory="b") + sample_task = Task(description="d", expected_output="o", agent=sample_agent) + + @CrewBase + class ImplicitNameCrewFactory: + agents_config = None + tasks_config = None + agents: list[BaseAgent] = [sample_agent] + tasks: list[Task] = [sample_task] + + @crew + def crew(self): + return Crew( + agents=[sample_agent], + tasks=[sample_task], + ) + + factory_cls = cast(type[Any], ImplicitNameCrewFactory) + crew_instance: Crew = cast(Any, factory_cls()).crew() + assert crew_instance.name == "ImplicitNameCrewFactory" + + +def test_crew_decorator_preserves_explicit_name(): + """Explicit Crew(name=...) inside @crew should win over the @CrewBase class name.""" + sample_agent = Agent(role="r", goal="g", backstory="b") + sample_task = Task(description="d", expected_output="o", agent=sample_agent) + + @CrewBase + class NamedCrewFactory: + agents_config = None + tasks_config = None + agents: list[BaseAgent] = [sample_agent] + tasks: list[Task] = [sample_task] + + @crew + def crew(self): + return Crew( + name="My Explicit Name", + agents=[sample_agent], + tasks=[sample_task], + ) + + factory_cls = cast(type[Any], NamedCrewFactory) + crew_instance: Crew = cast(Any, factory_cls()).crew() + assert crew_instance.name == "My Explicit Name" + + @tool def simple_tool(): """Return 'Hi!'""" From b34b3362734fa569e4ecef62b49cabbb527d3fd8 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Wed, 22 Apr 2026 03:08:52 +0800 Subject: [PATCH 24/32] feat: bump versions to 1.14.3a2 --- lib/crewai-files/src/crewai_files/__init__.py | 2 +- lib/crewai-tools/pyproject.toml | 2 +- lib/crewai-tools/src/crewai_tools/__init__.py | 2 +- lib/crewai/pyproject.toml | 2 +- lib/crewai/src/crewai/__init__.py | 2 +- lib/crewai/src/crewai/cli/templates/crew/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/flow/pyproject.toml | 2 +- lib/crewai/src/crewai/cli/templates/tool/pyproject.toml | 2 +- lib/devtools/src/crewai_devtools/__init__.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/crewai-files/src/crewai_files/__init__.py b/lib/crewai-files/src/crewai_files/__init__.py index 9fba24b55..051eda5d4 100644 --- a/lib/crewai-files/src/crewai_files/__init__.py +++ b/lib/crewai-files/src/crewai_files/__init__.py @@ -152,4 +152,4 @@ __all__ = [ "wrap_file_source", ] -__version__ = "1.14.3a1" +__version__ = "1.14.3a2" diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index 473f2e411..a43e27653 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14" dependencies = [ "pytube~=15.0.0", "requests>=2.33.0,<3", - "crewai==1.14.3a1", + "crewai==1.14.3a2", "tiktoken~=0.8.0", "beautifulsoup4~=4.13.4", "python-docx~=1.2.0", diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 87e53b756..996b63d57 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -313,4 +313,4 @@ __all__ = [ "ZapierActionTools", ] -__version__ = "1.14.3a1" +__version__ = "1.14.3a2" diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index 5b3c81850..001f2b8a6 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -55,7 +55,7 @@ Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] tools = [ - "crewai-tools==1.14.3a1", + "crewai-tools==1.14.3a2", ] embeddings = [ "tiktoken~=0.8.0" diff --git a/lib/crewai/src/crewai/__init__.py b/lib/crewai/src/crewai/__init__.py index b59fc893d..8d1587056 100644 --- a/lib/crewai/src/crewai/__init__.py +++ b/lib/crewai/src/crewai/__init__.py @@ -48,7 +48,7 @@ def _suppress_pydantic_deprecation_warnings() -> None: _suppress_pydantic_deprecation_warnings() -__version__ = "1.14.3a1" +__version__ = "1.14.3a2" _LAZY_IMPORTS: dict[str, tuple[str, str]] = { "Memory": ("crewai.memory.unified_memory", "Memory"), diff --git a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml index bf87e3131..93ee87691 100644 --- a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a1" + "crewai[tools]==1.14.3a2" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml index bdd4fd2a4..a7f5747bc 100644 --- a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a1" + "crewai[tools]==1.14.3a2" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml index fcd42c81f..cac3afab3 100644 --- a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a1" + "crewai[tools]==1.14.3a2" ] [tool.crewai] diff --git a/lib/devtools/src/crewai_devtools/__init__.py b/lib/devtools/src/crewai_devtools/__init__.py index 9df2b583d..14470c742 100644 --- a/lib/devtools/src/crewai_devtools/__init__.py +++ b/lib/devtools/src/crewai_devtools/__init__.py @@ -1,3 +1,3 @@ """CrewAI development tools.""" -__version__ = "1.14.3a1" +__version__ = "1.14.3a2" From 160e25c1a923e94711f07a7785c9bbda2f05d0cc Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Wed, 22 Apr 2026 03:14:00 +0800 Subject: [PATCH 25/32] docs: update changelog and version for v1.14.3a2 --- docs/ar/changelog.mdx | 30 ++++++++++++++++++++++++++++++ docs/en/changelog.mdx | 30 ++++++++++++++++++++++++++++++ docs/ko/changelog.mdx | 30 ++++++++++++++++++++++++++++++ docs/pt-BR/changelog.mdx | 30 ++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/docs/ar/changelog.mdx b/docs/ar/changelog.mdx index c99dba7d7..eb714117d 100644 --- a/docs/ar/changelog.mdx +++ b/docs/ar/changelog.mdx @@ -4,6 +4,36 @@ description: "تحديثات المنتج والتحسينات وإصلاحات icon: "clock" mode: "wide" --- + + ## v1.14.3a2 + + [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) + + ## ما الذي تغير + + ### الميزات + - إضافة دعم لـ bedrock V4 + - إضافة أدوات Daytona sandbox لوظائف محسّنة + - إضافة صفحة "البناء باستخدام الذكاء الاصطناعي" — مستندات أصلية للذكاء الاصطناعي لوكلاء البرمجة + - إضافة "البناء باستخدام الذكاء الاصطناعي" إلى التنقل في صفحة "البدء" وملفات الصفحات لجميع اللغات (en, ko, pt-BR, ar) + + ### إصلاحات الأخطاء + - إصلاح انتشار أسماء @CrewBase الضمنية إلى أحداث الطاقم + - حل مشكلة تكرار تهيئة الدفعات في دمج بيانات التنفيذ الوصفية + - إصلاح تسلسل حقول مرجع فئة Task لعمليات التحقق من النقاط + - التعامل مع نتيجة BaseModel في حلقة إعادة المحاولة للحدود + - تحديث python-dotenv إلى الإصدار >=1.2.2 للامتثال الأمني + + ### الوثائق + - تحديث سجل التغييرات والإصدار لـ v1.14.3a1 + - تحديث الأوصاف وتطبيق الترجمات الفعلية + + ## المساهمون + + @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta + + + ## v1.14.3a1 diff --git a/docs/en/changelog.mdx b/docs/en/changelog.mdx index 26ac50285..5fdd624ff 100644 --- a/docs/en/changelog.mdx +++ b/docs/en/changelog.mdx @@ -4,6 +4,36 @@ description: "Product updates, improvements, and bug fixes for CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a2 + + [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) + + ## What's Changed + + ### Features + - Add support for bedrock V4 + - Add Daytona sandbox tools for enhanced functionality + - Add 'Build with AI' page — AI-native docs for coding agents + - Add Build with AI to Get Started navigation and page files for all languages (en, ko, pt-BR, ar) + + ### Bug Fixes + - Fix propagation of implicit @CrewBase names to crew events + - Resolve issue with duplicate batch initialization in execution metadata merge + - Fix serialization of Task class-reference fields for checkpointing + - Handle BaseModel result in guardrail retry loop + - Bump python-dotenv to version >=1.2.2 for security compliance + + ### Documentation + - Update changelog and version for v1.14.3a1 + - Update descriptions and apply actual translations + + ## Contributors + + @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta + + + ## v1.14.3a1 diff --git a/docs/ko/changelog.mdx b/docs/ko/changelog.mdx index ab9715714..f744341eb 100644 --- a/docs/ko/changelog.mdx +++ b/docs/ko/changelog.mdx @@ -4,6 +4,36 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정" icon: "clock" mode: "wide" --- + + ## v1.14.3a2 + + [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) + + ## 변경 사항 + + ### 기능 + - 베드록 V4 지원 추가 + - 향상된 기능을 위한 데이토나 샌드박스 도구 추가 + - 'AI와 함께 빌드' 페이지 추가 — 코딩 에이전트를 위한 AI 네이티브 문서 + - 모든 언어(en, ko, pt-BR, ar)에 대한 시작하기 탐색 및 페이지 파일에 AI와 함께 빌드 추가 + + ### 버그 수정 + - 크루 이벤트에 대한 암묵적 @CrewBase 이름 전파 수정 + - 실행 메타데이터 병합에서 중복 배치 초기화 문제 해결 + - 체크포인트를 위한 Task 클래스 참조 필드 직렬화 수정 + - 가드레일 재시도 루프에서 BaseModel 결과 처리 + - 보안 준수를 위해 python-dotenv를 버전 >=1.2.2로 업데이트 + + ### 문서 + - v1.14.3a1에 대한 변경 로그 및 버전 업데이트 + - 설명 업데이트 및 실제 번역 적용 + + ## 기여자 + + @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta + + + ## v1.14.3a1 diff --git a/docs/pt-BR/changelog.mdx b/docs/pt-BR/changelog.mdx index a32cc6ff5..ed14c66db 100644 --- a/docs/pt-BR/changelog.mdx +++ b/docs/pt-BR/changelog.mdx @@ -4,6 +4,36 @@ description: "Atualizações de produto, melhorias e correções do CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a2 + + [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) + + ## O que mudou + + ### Recursos + - Adicionar suporte para bedrock V4 + - Adicionar ferramentas de sandbox Daytona para funcionalidade aprimorada + - Adicionar página 'Construir com IA' — documentação nativa de IA para agentes de codificação + - Adicionar Construir com IA à navegação Começar e arquivos de página para todos os idiomas (en, ko, pt-BR, ar) + + ### Correções de Bugs + - Corrigir a propagação de nomes implícitos @CrewBase para eventos da equipe + - Resolver problema com inicialização de lote duplicada na mesclagem de metadados de execução + - Corrigir a serialização de campos de referência de classe Task para checkpointing + - Lidar com o resultado BaseModel no loop de repetição de guardrail + - Atualizar python-dotenv para a versão >=1.2.2 para conformidade de segurança + + ### Documentação + - Atualizar changelog e versão para v1.14.3a1 + - Atualizar descrições e aplicar traduções reais + + ## Contributors + + @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta + + + ## v1.14.3a1 From 3be2fb65dc574fad2aa8712c607bbd35f7d91d4d Mon Sep 17 00:00:00 2001 From: iris-clawd Date: Wed, 22 Apr 2026 02:17:33 -0300 Subject: [PATCH 26/32] perf: lazy-load MCP SDK and event types to reduce cold start by ~29% (#5584) * perf: defer MCP SDK import by fixing import path in agent/core.py - Change 'from crewai.mcp import MCPServerConfig' to direct path 'from crewai.mcp.config import MCPServerConfig' to avoid triggering mcp/__init__.py which eagerly loads the full mcp SDK (~300-400ms) - Move MCPToolResolver import into get_mcp_tools() method body since it's only used at runtime, not in type annotations Saves ~200ms on 'import crewai' cold start. * perf: lazy-load heavy MCP imports in mcp/__init__.py MCPClient, MCPToolResolver, BaseTransport, and TransportType now use __getattr__ lazy loading. These pull in the full mcp SDK (~400ms) but are only needed at runtime when agents actually connect to MCP servers. Lightweight config and filter types remain eagerly imported. * perf: lazy-load all event type modules in events/__init__.py Previously only agent_events were lazy-loaded; all other event type modules (crew, flow, knowledge, llm, guardrail, logging, mcp, memory, reasoning, skill, task, tool_usage) were eagerly imported at package init time. Since events/__init__.py runs whenever ANY crewai.events.* submodule is accessed, this loaded ~12 Pydantic model modules unnecessarily. Now all event types use the same __getattr__ lazy-loading pattern, with TYPE_CHECKING imports preserved for IDE/type-checker support. Saves ~550ms on 'import crewai' cold start. * chore: remove UNKNOWN.egg-info from version control * fix: add MCPToolResolver to TYPE_CHECKING imports Fixes F821 (ruff) and name-defined (mypy) from lazy-loading the MCP import. The type annotation on _mcp_resolver needs the name available at type-check time. * fix: bump lxml to >=5.4.0 for GHSA-vfmq-68hx-4jfw lxml 5.3.2 has a known vulnerability. Bump to 5.4.0+ which includes the fix (libxml2 2.13.8). The previous <5.4.0 pin was for etree import issues that have since been resolved. * fix: bump exclude-newer to 2026-04-22 for lxml 6.1.0 resolution lxml 6.1.0 (GHSA fix) was released April 17 but the exclude-newer date was set to April 17, missing it by timestamp. Bump to April 22. * perf: add import time benchmark script scripts/benchmark_import_time.py measures import crewai cold start in fresh subprocesses. Supports --runs, --json (for CI), and --threshold (fail if median exceeds N seconds). The companion GitHub Action workflow needs to be pushed separately (requires workflow scope). * new action * Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Joao Moura Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/import-time.yml | 103 +++++++ .gitignore | 1 + lib/crewai-tools/pyproject.toml | 2 +- lib/crewai/src/crewai/agent/core.py | 6 +- lib/crewai/src/crewai/events/__init__.py | 356 ++++++++++++++--------- lib/crewai/src/crewai/mcp/__init__.py | 34 ++- pyproject.toml | 2 +- scripts/benchmark_import_time.py | 76 +++++ 8 files changed, 438 insertions(+), 142 deletions(-) create mode 100644 .github/workflows/import-time.yml create mode 100755 scripts/benchmark_import_time.py diff --git a/.github/workflows/import-time.yml b/.github/workflows/import-time.yml new file mode 100644 index 000000000..7c0126b23 --- /dev/null +++ b/.github/workflows/import-time.yml @@ -0,0 +1,103 @@ +name: Import Time Guard + +on: + pull_request: + paths: + - "lib/crewai/src/**" + - "lib/crewai/pyproject.toml" + - "pyproject.toml" + +permissions: + contents: read + +jobs: + import-time: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.12"] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: astral-sh/setup-uv@v6 + with: + version: "0.11.3" + enable-cache: true + + - name: Install the project + run: uv sync --all-extras --no-dev + env: + UV_PYTHON: ${{ matrix.python-version }} + + - name: Benchmark PR branch + id: pr + run: | + result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json) + echo "result=$result" >> "$GITHUB_OUTPUT" + echo "pr_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT" + echo "### PR Branch Import Time" >> "$GITHUB_STEP_SUMMARY" + echo "$result" | python3 -c " + import sys, json + d = json.load(sys.stdin) + print(f'- Median: {d[\"median_s\"]}s') + print(f'- Mean: {d[\"mean_s\"]}s ± {d[\"stdev_s\"]}s') + print(f'- Range: {d[\"min_s\"]}s – {d[\"max_s\"]}s') + " >> "$GITHUB_STEP_SUMMARY" + env: + UV_PYTHON: ${{ matrix.python-version }} + + - name: Checkout base branch + run: git checkout ${{ github.event.pull_request.base.sha }} + + - name: Install base branch + run: uv sync --all-extras --no-dev + env: + UV_PYTHON: ${{ matrix.python-version }} + + - name: Benchmark base branch + id: base + run: | + result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json 2>/dev/null || echo '{"median_s": 0}') + echo "result=$result" >> "$GITHUB_OUTPUT" + echo "base_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT" + echo "### Base Branch Import Time" >> "$GITHUB_STEP_SUMMARY" + echo "$result" | python3 -c " + import sys, json + d = json.load(sys.stdin) + if d.get('median_s', 0) > 0: + print(f'- Median: {d[\"median_s\"]}s') + else: + print('- Benchmark script not present on base branch (skip comparison)') + " >> "$GITHUB_STEP_SUMMARY" + env: + UV_PYTHON: ${{ matrix.python-version }} + + - name: Compare and gate + run: | + pr_median=${{ steps.pr.outputs.pr_median }} + base_median=${{ steps.base.outputs.base_median }} + + python3 -c " + pr = float('$pr_median') + base = float('$base_median') + + if base <= 0: + print('⏭️ No base benchmark available — skipping comparison.') + exit(0) + + change_pct = ((pr - base) / base) * 100 + print(f'Base: {base:.3f}s') + print(f'PR: {pr:.3f}s') + print(f'Change: {change_pct:+.1f}%') + print() + + if change_pct > 5: + print(f'❌ BLOCKED: Import time regressed by {change_pct:.1f}% (threshold: 5%)') + exit(1) + elif change_pct > 0: + print(f'⚠️ Slight regression ({change_pct:.1f}%) but within 5% threshold.') + else: + print(f'✅ Import time improved by {abs(change_pct):.1f}%') + " diff --git a/.gitignore b/.gitignore index 785c2c299..d7e89fcaa 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ chromadb-*.lock .crewai/memory blogs/* secrets/* +UNKNOWN.egg-info/ diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index a43e27653..7cf64465d 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -112,7 +112,7 @@ github = [ ] rag = [ "python-docx>=1.1.0", - "lxml>=5.3.0,<5.4.0", # Pin to avoid etree import issues in 5.4.0 + "lxml>=6.1.0,<7", # 6.1.0+ required for GHSA-vfmq-68hx-4jfw (XXE in iterparse) ] xml = [ "unstructured[local-inference, all-docs]>=0.17.2" diff --git a/lib/crewai/src/crewai/agent/core.py b/lib/crewai/src/crewai/agent/core.py index 74a3e85de..a2df8c2b9 100644 --- a/lib/crewai/src/crewai/agent/core.py +++ b/lib/crewai/src/crewai/agent/core.py @@ -78,8 +78,7 @@ from crewai.knowledge.knowledge import Knowledge from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource from crewai.lite_agent_output import LiteAgentOutput from crewai.llms.base_llm import BaseLLM -from crewai.mcp import MCPServerConfig -from crewai.mcp.tool_resolver import MCPToolResolver +from crewai.mcp.config import MCPServerConfig from crewai.rag.embeddings.types import EmbedderConfig from crewai.security.fingerprint import Fingerprint from crewai.skills.loader import activate_skill, discover_skills @@ -119,6 +118,7 @@ if TYPE_CHECKING: from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig from crewai.agents.agent_builder.base_agent import PlatformAppOrAction + from crewai.mcp.tool_resolver import MCPToolResolver from crewai.task import Task from crewai.tools.base_tool import BaseTool from crewai.tools.structured_tool import CrewStructuredTool @@ -1120,6 +1120,8 @@ class Agent(BaseAgent): Delegates to :class:`~crewai.mcp.tool_resolver.MCPToolResolver`. """ self._cleanup_mcp_clients() + from crewai.mcp.tool_resolver import MCPToolResolver + self._mcp_resolver = MCPToolResolver(agent=self, logger=self._logger) return self._mcp_resolver.resolve(mcps) diff --git a/lib/crewai/src/crewai/events/__init__.py b/lib/crewai/src/crewai/events/__init__.py index bcdafe49a..a9c02a243 100644 --- a/lib/crewai/src/crewai/events/__init__.py +++ b/lib/crewai/src/crewai/events/__init__.py @@ -6,112 +6,20 @@ This module provides the event infrastructure that allows users to: - Build custom logging and analytics - Extend CrewAI with custom event handlers - Declare handler dependencies for ordered execution + +Event type classes are lazy-loaded on first access to avoid importing +~12 Pydantic model modules (and their transitive deps) at package init time. """ from __future__ import annotations +import importlib from typing import TYPE_CHECKING, Any from crewai.events.base_event_listener import BaseEventListener from crewai.events.depends import Depends from crewai.events.event_bus import crewai_event_bus from crewai.events.handler_graph import CircularDependencyError -from crewai.events.types.crew_events import ( - CrewKickoffCompletedEvent, - CrewKickoffFailedEvent, - CrewKickoffStartedEvent, - CrewTestCompletedEvent, - CrewTestFailedEvent, - CrewTestResultEvent, - CrewTestStartedEvent, - CrewTrainCompletedEvent, - CrewTrainFailedEvent, - CrewTrainStartedEvent, -) -from crewai.events.types.flow_events import ( - FlowCreatedEvent, - FlowEvent, - FlowFinishedEvent, - FlowPlotEvent, - FlowStartedEvent, - HumanFeedbackReceivedEvent, - HumanFeedbackRequestedEvent, - MethodExecutionFailedEvent, - MethodExecutionFinishedEvent, - MethodExecutionStartedEvent, -) -from crewai.events.types.knowledge_events import ( - KnowledgeQueryCompletedEvent, - KnowledgeQueryFailedEvent, - KnowledgeQueryStartedEvent, - KnowledgeRetrievalCompletedEvent, - KnowledgeRetrievalStartedEvent, - KnowledgeSearchQueryFailedEvent, -) -from crewai.events.types.llm_events import ( - LLMCallCompletedEvent, - LLMCallFailedEvent, - LLMCallStartedEvent, - LLMStreamChunkEvent, -) -from crewai.events.types.llm_guardrail_events import ( - LLMGuardrailCompletedEvent, - LLMGuardrailStartedEvent, -) -from crewai.events.types.logging_events import ( - AgentLogsExecutionEvent, - AgentLogsStartedEvent, -) -from crewai.events.types.mcp_events import ( - MCPConfigFetchFailedEvent, - MCPConnectionCompletedEvent, - MCPConnectionFailedEvent, - MCPConnectionStartedEvent, - MCPToolExecutionCompletedEvent, - MCPToolExecutionFailedEvent, - MCPToolExecutionStartedEvent, -) -from crewai.events.types.memory_events import ( - MemoryQueryCompletedEvent, - MemoryQueryFailedEvent, - MemoryQueryStartedEvent, - MemoryRetrievalCompletedEvent, - MemoryRetrievalFailedEvent, - MemoryRetrievalStartedEvent, - MemorySaveCompletedEvent, - MemorySaveFailedEvent, - MemorySaveStartedEvent, -) -from crewai.events.types.reasoning_events import ( - AgentReasoningCompletedEvent, - AgentReasoningFailedEvent, - AgentReasoningStartedEvent, - ReasoningEvent, -) -from crewai.events.types.skill_events import ( - SkillActivatedEvent, - SkillDiscoveryCompletedEvent, - SkillDiscoveryStartedEvent, - SkillEvent, - SkillLoadFailedEvent, - SkillLoadedEvent, -) -from crewai.events.types.task_events import ( - TaskCompletedEvent, - TaskEvaluationEvent, - TaskFailedEvent, - TaskStartedEvent, -) -from crewai.events.types.tool_usage_events import ( - ToolExecutionErrorEvent, - ToolSelectionErrorEvent, - ToolUsageErrorEvent, - ToolUsageEvent, - ToolUsageFinishedEvent, - ToolUsageStartedEvent, - ToolValidateInputErrorEvent, -) - if TYPE_CHECKING: from crewai.events.types.agent_events import ( @@ -125,6 +33,223 @@ if TYPE_CHECKING: LiteAgentExecutionErrorEvent, LiteAgentExecutionStartedEvent, ) + from crewai.events.types.crew_events import ( + CrewKickoffCompletedEvent, + CrewKickoffFailedEvent, + CrewKickoffStartedEvent, + CrewTestCompletedEvent, + CrewTestFailedEvent, + CrewTestResultEvent, + CrewTestStartedEvent, + CrewTrainCompletedEvent, + CrewTrainFailedEvent, + CrewTrainStartedEvent, + ) + from crewai.events.types.flow_events import ( + FlowCreatedEvent, + FlowEvent, + FlowFinishedEvent, + FlowPlotEvent, + FlowStartedEvent, + HumanFeedbackReceivedEvent, + HumanFeedbackRequestedEvent, + MethodExecutionFailedEvent, + MethodExecutionFinishedEvent, + MethodExecutionStartedEvent, + ) + from crewai.events.types.knowledge_events import ( + KnowledgeQueryCompletedEvent, + KnowledgeQueryFailedEvent, + KnowledgeQueryStartedEvent, + KnowledgeRetrievalCompletedEvent, + KnowledgeRetrievalStartedEvent, + KnowledgeSearchQueryFailedEvent, + ) + from crewai.events.types.llm_events import ( + LLMCallCompletedEvent, + LLMCallFailedEvent, + LLMCallStartedEvent, + LLMStreamChunkEvent, + ) + from crewai.events.types.llm_guardrail_events import ( + LLMGuardrailCompletedEvent, + LLMGuardrailStartedEvent, + ) + from crewai.events.types.logging_events import ( + AgentLogsExecutionEvent, + AgentLogsStartedEvent, + ) + from crewai.events.types.mcp_events import ( + MCPConfigFetchFailedEvent, + MCPConnectionCompletedEvent, + MCPConnectionFailedEvent, + MCPConnectionStartedEvent, + MCPToolExecutionCompletedEvent, + MCPToolExecutionFailedEvent, + MCPToolExecutionStartedEvent, + ) + from crewai.events.types.memory_events import ( + MemoryQueryCompletedEvent, + MemoryQueryFailedEvent, + MemoryQueryStartedEvent, + MemoryRetrievalCompletedEvent, + MemoryRetrievalFailedEvent, + MemoryRetrievalStartedEvent, + MemorySaveCompletedEvent, + MemorySaveFailedEvent, + MemorySaveStartedEvent, + ) + from crewai.events.types.reasoning_events import ( + AgentReasoningCompletedEvent, + AgentReasoningFailedEvent, + AgentReasoningStartedEvent, + ReasoningEvent, + ) + from crewai.events.types.skill_events import ( + SkillActivatedEvent, + SkillDiscoveryCompletedEvent, + SkillDiscoveryStartedEvent, + SkillEvent, + SkillLoadFailedEvent, + SkillLoadedEvent, + ) + from crewai.events.types.task_events import ( + TaskCompletedEvent, + TaskEvaluationEvent, + TaskFailedEvent, + TaskStartedEvent, + ) + from crewai.events.types.tool_usage_events import ( + ToolExecutionErrorEvent, + ToolSelectionErrorEvent, + ToolUsageErrorEvent, + ToolUsageEvent, + ToolUsageFinishedEvent, + ToolUsageStartedEvent, + ToolValidateInputErrorEvent, + ) + +# Map every event class name → its module path for lazy loading +_LAZY_EVENT_MAPPING: dict[str, str] = { + # agent_events + "AgentEvaluationCompletedEvent": "crewai.events.types.agent_events", + "AgentEvaluationFailedEvent": "crewai.events.types.agent_events", + "AgentEvaluationStartedEvent": "crewai.events.types.agent_events", + "AgentExecutionCompletedEvent": "crewai.events.types.agent_events", + "AgentExecutionErrorEvent": "crewai.events.types.agent_events", + "AgentExecutionStartedEvent": "crewai.events.types.agent_events", + "LiteAgentExecutionCompletedEvent": "crewai.events.types.agent_events", + "LiteAgentExecutionErrorEvent": "crewai.events.types.agent_events", + "LiteAgentExecutionStartedEvent": "crewai.events.types.agent_events", + # crew_events + "CrewKickoffCompletedEvent": "crewai.events.types.crew_events", + "CrewKickoffFailedEvent": "crewai.events.types.crew_events", + "CrewKickoffStartedEvent": "crewai.events.types.crew_events", + "CrewTestCompletedEvent": "crewai.events.types.crew_events", + "CrewTestFailedEvent": "crewai.events.types.crew_events", + "CrewTestResultEvent": "crewai.events.types.crew_events", + "CrewTestStartedEvent": "crewai.events.types.crew_events", + "CrewTrainCompletedEvent": "crewai.events.types.crew_events", + "CrewTrainFailedEvent": "crewai.events.types.crew_events", + "CrewTrainStartedEvent": "crewai.events.types.crew_events", + # flow_events + "FlowCreatedEvent": "crewai.events.types.flow_events", + "FlowEvent": "crewai.events.types.flow_events", + "FlowFinishedEvent": "crewai.events.types.flow_events", + "FlowPlotEvent": "crewai.events.types.flow_events", + "FlowStartedEvent": "crewai.events.types.flow_events", + "HumanFeedbackReceivedEvent": "crewai.events.types.flow_events", + "HumanFeedbackRequestedEvent": "crewai.events.types.flow_events", + "MethodExecutionFailedEvent": "crewai.events.types.flow_events", + "MethodExecutionFinishedEvent": "crewai.events.types.flow_events", + "MethodExecutionStartedEvent": "crewai.events.types.flow_events", + # knowledge_events + "KnowledgeQueryCompletedEvent": "crewai.events.types.knowledge_events", + "KnowledgeQueryFailedEvent": "crewai.events.types.knowledge_events", + "KnowledgeQueryStartedEvent": "crewai.events.types.knowledge_events", + "KnowledgeRetrievalCompletedEvent": "crewai.events.types.knowledge_events", + "KnowledgeRetrievalStartedEvent": "crewai.events.types.knowledge_events", + "KnowledgeSearchQueryFailedEvent": "crewai.events.types.knowledge_events", + # llm_events + "LLMCallCompletedEvent": "crewai.events.types.llm_events", + "LLMCallFailedEvent": "crewai.events.types.llm_events", + "LLMCallStartedEvent": "crewai.events.types.llm_events", + "LLMStreamChunkEvent": "crewai.events.types.llm_events", + # llm_guardrail_events + "LLMGuardrailCompletedEvent": "crewai.events.types.llm_guardrail_events", + "LLMGuardrailStartedEvent": "crewai.events.types.llm_guardrail_events", + # logging_events + "AgentLogsExecutionEvent": "crewai.events.types.logging_events", + "AgentLogsStartedEvent": "crewai.events.types.logging_events", + # mcp_events + "MCPConfigFetchFailedEvent": "crewai.events.types.mcp_events", + "MCPConnectionCompletedEvent": "crewai.events.types.mcp_events", + "MCPConnectionFailedEvent": "crewai.events.types.mcp_events", + "MCPConnectionStartedEvent": "crewai.events.types.mcp_events", + "MCPToolExecutionCompletedEvent": "crewai.events.types.mcp_events", + "MCPToolExecutionFailedEvent": "crewai.events.types.mcp_events", + "MCPToolExecutionStartedEvent": "crewai.events.types.mcp_events", + # memory_events + "MemoryQueryCompletedEvent": "crewai.events.types.memory_events", + "MemoryQueryFailedEvent": "crewai.events.types.memory_events", + "MemoryQueryStartedEvent": "crewai.events.types.memory_events", + "MemoryRetrievalCompletedEvent": "crewai.events.types.memory_events", + "MemoryRetrievalFailedEvent": "crewai.events.types.memory_events", + "MemoryRetrievalStartedEvent": "crewai.events.types.memory_events", + "MemorySaveCompletedEvent": "crewai.events.types.memory_events", + "MemorySaveFailedEvent": "crewai.events.types.memory_events", + "MemorySaveStartedEvent": "crewai.events.types.memory_events", + # reasoning_events + "AgentReasoningCompletedEvent": "crewai.events.types.reasoning_events", + "AgentReasoningFailedEvent": "crewai.events.types.reasoning_events", + "AgentReasoningStartedEvent": "crewai.events.types.reasoning_events", + "ReasoningEvent": "crewai.events.types.reasoning_events", + # skill_events + "SkillActivatedEvent": "crewai.events.types.skill_events", + "SkillDiscoveryCompletedEvent": "crewai.events.types.skill_events", + "SkillDiscoveryStartedEvent": "crewai.events.types.skill_events", + "SkillEvent": "crewai.events.types.skill_events", + "SkillLoadFailedEvent": "crewai.events.types.skill_events", + "SkillLoadedEvent": "crewai.events.types.skill_events", + # task_events + "TaskCompletedEvent": "crewai.events.types.task_events", + "TaskEvaluationEvent": "crewai.events.types.task_events", + "TaskFailedEvent": "crewai.events.types.task_events", + "TaskStartedEvent": "crewai.events.types.task_events", + # tool_usage_events + "ToolExecutionErrorEvent": "crewai.events.types.tool_usage_events", + "ToolSelectionErrorEvent": "crewai.events.types.tool_usage_events", + "ToolUsageErrorEvent": "crewai.events.types.tool_usage_events", + "ToolUsageEvent": "crewai.events.types.tool_usage_events", + "ToolUsageFinishedEvent": "crewai.events.types.tool_usage_events", + "ToolUsageStartedEvent": "crewai.events.types.tool_usage_events", + "ToolValidateInputErrorEvent": "crewai.events.types.tool_usage_events", +} + +_extension_exports: dict[str, Any] = {} + + +def __getattr__(name: str) -> Any: + """Lazy import for event types and registered extensions.""" + if name in _LAZY_EVENT_MAPPING: + module_path = _LAZY_EVENT_MAPPING[name] + module = importlib.import_module(module_path) + val = getattr(module, name) + globals()[name] = val # cache for subsequent access + return val + + if name in _extension_exports: + value = _extension_exports[name] + if isinstance(value, str): + module_path, _, attr_name = value.rpartition(".") + if module_path: + module = importlib.import_module(module_path) + return getattr(module, attr_name) + return importlib.import_module(value) + return value + + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) __all__ = [ @@ -214,42 +339,3 @@ __all__ = [ "_extension_exports", "crewai_event_bus", ] - -_AGENT_EVENT_MAPPING = { - "AgentEvaluationCompletedEvent": "crewai.events.types.agent_events", - "AgentEvaluationFailedEvent": "crewai.events.types.agent_events", - "AgentEvaluationStartedEvent": "crewai.events.types.agent_events", - "AgentExecutionCompletedEvent": "crewai.events.types.agent_events", - "AgentExecutionErrorEvent": "crewai.events.types.agent_events", - "AgentExecutionStartedEvent": "crewai.events.types.agent_events", - "LiteAgentExecutionCompletedEvent": "crewai.events.types.agent_events", - "LiteAgentExecutionErrorEvent": "crewai.events.types.agent_events", - "LiteAgentExecutionStartedEvent": "crewai.events.types.agent_events", -} - -_extension_exports: dict[str, Any] = {} - - -def __getattr__(name: str) -> Any: - """Lazy import for agent events and registered extensions.""" - if name in _AGENT_EVENT_MAPPING: - import importlib - - module_path = _AGENT_EVENT_MAPPING[name] - module = importlib.import_module(module_path) - return getattr(module, name) - - if name in _extension_exports: - import importlib - - value = _extension_exports[name] - if isinstance(value, str): - module_path, _, attr_name = value.rpartition(".") - if module_path: - module = importlib.import_module(module_path) - return getattr(module, attr_name) - return importlib.import_module(value) - return value - - msg = f"module {__name__!r} has no attribute {name!r}" - raise AttributeError(msg) diff --git a/lib/crewai/src/crewai/mcp/__init__.py b/lib/crewai/src/crewai/mcp/__init__.py index e078919fd..bb3dab199 100644 --- a/lib/crewai/src/crewai/mcp/__init__.py +++ b/lib/crewai/src/crewai/mcp/__init__.py @@ -2,9 +2,17 @@ This module provides native MCP client functionality, allowing CrewAI agents to connect to any MCP-compliant server using various transport types. + +Heavy imports (MCPClient, MCPToolResolver, BaseTransport, TransportType) are +lazy-loaded on first access to avoid pulling in the ``mcp`` SDK (~400ms) +when only lightweight config/filter types are needed. """ -from crewai.mcp.client import MCPClient +from __future__ import annotations + +import importlib +from typing import TYPE_CHECKING, Any + from crewai.mcp.config import ( MCPServerConfig, MCPServerHTTP, @@ -18,8 +26,28 @@ from crewai.mcp.filters import ( create_dynamic_tool_filter, create_static_tool_filter, ) -from crewai.mcp.tool_resolver import MCPToolResolver -from crewai.mcp.transports.base import BaseTransport, TransportType + +if TYPE_CHECKING: + from crewai.mcp.client import MCPClient + from crewai.mcp.tool_resolver import MCPToolResolver + from crewai.mcp.transports.base import BaseTransport, TransportType + +_LAZY: dict[str, tuple[str, str]] = { + "MCPClient": ("crewai.mcp.client", "MCPClient"), + "MCPToolResolver": ("crewai.mcp.tool_resolver", "MCPToolResolver"), + "BaseTransport": ("crewai.mcp.transports.base", "BaseTransport"), + "TransportType": ("crewai.mcp.transports.base", "TransportType"), +} + + +def __getattr__(name: str) -> Any: + if name in _LAZY: + mod_path, attr = _LAZY[name] + mod = importlib.import_module(mod_path) + val = getattr(mod, attr) + globals()[name] = val # cache for subsequent access + return val + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") __all__ = [ diff --git a/pyproject.toml b/pyproject.toml index 1b8aea627..754b4d635 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -164,7 +164,7 @@ info = "Commits must follow Conventional Commits 1.0.0." [tool.uv] # Pinned to include the security patch releases (authlib 1.6.11, # langchain-text-splitters 1.1.2) uploaded on 2026-04-16. -exclude-newer = "2026-04-17" +exclude-newer = "2026-04-22" # composio-core pins rich<14 but textual requires rich>=14. # onnxruntime 1.24+ dropped Python 3.10 wheels; cap it so qdrant[fastembed] resolves on 3.10. diff --git a/scripts/benchmark_import_time.py b/scripts/benchmark_import_time.py new file mode 100755 index 000000000..e44b2272a --- /dev/null +++ b/scripts/benchmark_import_time.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Benchmark `import crewai` cold start time. + +Usage: + python scripts/benchmark_import_time.py [--runs N] [--json] + +Spawns a fresh Python subprocess for each run to ensure cold imports. +Prints median, mean, min, max across all runs. +With --json, outputs machine-readable results for CI. +""" +import argparse +import json +import statistics +import subprocess +import sys + + +IMPORT_SCRIPT = "import time; t0 = time.perf_counter(); import crewai; print(time.perf_counter() - t0)" + + +def measure_import(python: str = sys.executable) -> float: + """Run a single cold-import measurement in a subprocess.""" + result = subprocess.run( + [python, "-c", IMPORT_SCRIPT], + capture_output=True, + text=True, + env={"PATH": "", "VIRTUAL_ENV": "", "PYTHONPATH": ""}, + timeout=30, + ) + if result.returncode != 0: + raise RuntimeError(f"Import failed: {result.stderr.strip()}") + return float(result.stdout.strip()) + + +def main(): + parser = argparse.ArgumentParser(description="Benchmark crewai import time") + parser.add_argument("--runs", type=int, default=5, help="Number of runs (default: 5)") + parser.add_argument("--json", action="store_true", help="Output JSON for CI") + parser.add_argument("--threshold", type=float, default=None, + help="Fail if median exceeds this value (seconds)") + args = parser.parse_args() + + times = [] + for i in range(args.runs): + t = measure_import() + times.append(t) + if not args.json: + print(f" Run {i + 1}: {t:.3f}s") + + median = statistics.median(times) + mean = statistics.mean(times) + stdev = statistics.stdev(times) if len(times) > 1 else 0.0 + + result = { + "runs": args.runs, + "median_s": round(median, 3), + "mean_s": round(mean, 3), + "stdev_s": round(stdev, 3), + "min_s": round(min(times), 3), + "max_s": round(max(times), 3), + } + + if args.json: + print(json.dumps(result)) + else: + print(f"\n Median: {median:.3f}s") + print(f" Mean: {mean:.3f}s ± {stdev:.3f}s") + print(f" Range: {min(times):.3f}s – {max(times):.3f}s") + + if args.threshold and median > args.threshold: + print(f"\n ❌ FAILED: median {median:.3f}s exceeds threshold {args.threshold:.3f}s") + sys.exit(1) + + +if __name__ == "__main__": + main() From 944fe6d435038f19503c22b1f678c9c30c78c57d Mon Sep 17 00:00:00 2001 From: alex-clawd Date: Tue, 21 Apr 2026 23:56:41 -0700 Subject: [PATCH 27/32] docs: remove pricing FAQ from build-with-ai page across all locales (#5586) Removes the 'How does pricing work?' accordion from EN, AR, KO, and PT-BR. Co-authored-by: Joao Moura --- docs/ar/guides/coding-tools/build-with-ai.mdx | 3 --- docs/en/guides/coding-tools/build-with-ai.mdx | 3 --- docs/ko/guides/coding-tools/build-with-ai.mdx | 3 --- docs/pt-BR/guides/coding-tools/build-with-ai.mdx | 3 --- 4 files changed, 12 deletions(-) diff --git a/docs/ar/guides/coding-tools/build-with-ai.mdx b/docs/ar/guides/coding-tools/build-with-ai.mdx index 4ea73abb9..88a94e84d 100644 --- a/docs/ar/guides/coding-tools/build-with-ai.mdx +++ b/docs/ar/guides/coding-tools/build-with-ai.mdx @@ -207,9 +207,6 @@ CrewAI AMP مُصمَّم لفرق الإنتاج. إليك ما تحصل علي - **Factory (استضافة ذاتية)** — على بنيتك التحتية لسيطرة كاملة على البيانات - **هجين** — دمج السحابة والاستضافة الذاتية حسب حساسية البيانات - - سجّل في [app.crewai.com](https://app.crewai.com) لمعرفة الخطط الحالية. تسعير المؤسسات وFactory متاح عند الطلب. - diff --git a/docs/en/guides/coding-tools/build-with-ai.mdx b/docs/en/guides/coding-tools/build-with-ai.mdx index 2badb284e..8e6c2b3ea 100644 --- a/docs/en/guides/coding-tools/build-with-ai.mdx +++ b/docs/en/guides/coding-tools/build-with-ai.mdx @@ -207,9 +207,6 @@ CrewAI AMP is built for production teams. Here's what you get beyond deployment. - **Factory (self-hosted)** — run on your own infrastructure for full data control - **Hybrid** — mix cloud and self-hosted based on sensitivity requirements - - Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request. - diff --git a/docs/ko/guides/coding-tools/build-with-ai.mdx b/docs/ko/guides/coding-tools/build-with-ai.mdx index 22f6b25d8..0e56a06cc 100644 --- a/docs/ko/guides/coding-tools/build-with-ai.mdx +++ b/docs/ko/guides/coding-tools/build-with-ai.mdx @@ -207,9 +207,6 @@ CrewAI AMP는 프로덕션 팀을 위해 만들어졌습니다. 배포 외에 - **Factory(셀프 호스팅)** — 데이터 통제를 위해 자체 인프라에서 실행 - **하이브리드** — 민감도에 따라 클라우드와 셀프 호스팅을 혼합 - - [app.crewai.com](https://app.crewai.com)에 가입하면 현재 요금제를 확인할 수 있습니다. 엔터프라이즈 및 Factory 가격은 문의 시 안내합니다. - diff --git a/docs/pt-BR/guides/coding-tools/build-with-ai.mdx b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx index bc697ea10..57704aac9 100644 --- a/docs/pt-BR/guides/coding-tools/build-with-ai.mdx +++ b/docs/pt-BR/guides/coding-tools/build-with-ai.mdx @@ -207,9 +207,6 @@ O CrewAI AMP foi feito para equipes em produção. Além da implantação, você - **Factory (self-hosted)** — na sua infraestrutura para controle total dos dados - **Híbrido** — combine nuvem e self-hosted conforme a sensibilidade dos dados - - Cadastre-se em [app.crewai.com](https://app.crewai.com) para ver os planos atuais. Preços enterprise e Factory sob consulta. - From c94f2e8f28a5f8289a7ab6af9c4451367b9ec16c Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Thu, 23 Apr 2026 00:52:36 +0800 Subject: [PATCH 28/32] fix: upgrade lxml to >=6.1.0 for GHSA-vfmq-68hx-4jfw --- uv.lock | 156 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/uv.lock b/uv.lock index 06a687ed3..de440cae2 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-17T16:00:00Z" +exclude-newer = "2026-04-22T16:00:00Z" [manifest] members = [ @@ -1596,7 +1596,7 @@ requires-dist = [ { name = "hyperbrowser", marker = "extra == 'hyperbrowser'", specifier = ">=0.18.0" }, { name = "langchain-apify", marker = "extra == 'apify'", specifier = ">=0.1.2,<1.0.0" }, { name = "linkup-sdk", marker = "extra == 'linkup-sdk'", specifier = ">=0.2.2" }, - { name = "lxml", marker = "extra == 'rag'", specifier = ">=5.3.0,<5.4.0" }, + { name = "lxml", marker = "extra == 'rag'", specifier = ">=6.1.0,<7" }, { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.6.0" }, { name = "mcpadapt", marker = "extra == 'mcp'", specifier = ">=0.1.9" }, { name = "multion", marker = "extra == 'multion'", specifier = ">=1.1.0" }, @@ -3913,84 +3913,84 @@ wheels = [ [[package]] name = "lxml" -version = "5.3.2" +version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/61/d3dc048cd6c7be6fe45b80cedcbdd4326ba4d550375f266d9f4246d0f4bc/lxml-5.3.2.tar.gz", hash = "sha256:773947d0ed809ddad824b7b14467e1a481b8976e87278ac4a730c2f7c7fcddc1", size = 3679948, upload-time = "2025-04-05T18:31:58.757Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/30/9abc9e34c657c33834eaf6cd02124c61bdf5944d802aa48e69be8da3585d/lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13", size = 4197006, upload-time = "2026-04-18T04:32:51.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/9c/b015de0277a13d1d51924810b248b8a685a4e3dcd02d2ffb9b4e65cc37f4/lxml-5.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c4b84d6b580a9625dfa47269bf1fd7fbba7ad69e08b16366a46acb005959c395", size = 8144077, upload-time = "2025-04-05T18:25:05.832Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6a/30467f6b66ae666d20b52dffa98c00f0f15e0567d1333d70db7c44a6939e/lxml-5.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4c08ecb26e4270a62f81f81899dfff91623d349e433b126931c9c4577169666", size = 4423433, upload-time = "2025-04-05T18:25:10.126Z" }, - { url = "https://files.pythonhosted.org/packages/12/85/5a50121c0b57c8aba1beec30d324dc9272a193ecd6c24ad1efb5e223a035/lxml-5.3.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef926e9f11e307b5a7c97b17c5c609a93fb59ffa8337afac8f89e6fe54eb0b37", size = 5230753, upload-time = "2025-04-05T18:25:12.638Z" }, - { url = "https://files.pythonhosted.org/packages/81/07/a62896efbb74ff23e9d19a14713fb9c808dfd89d79eecb8a583d1ca722b1/lxml-5.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017ceeabe739100379fe6ed38b033cd244ce2da4e7f6f07903421f57da3a19a2", size = 4945993, upload-time = "2025-04-05T18:25:15.63Z" }, - { url = "https://files.pythonhosted.org/packages/74/ca/c47bffbafcd98c53c2ccd26dcb29b2de8fa0585d5afae76e5c5a9dce5f96/lxml-5.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dae97d9435dc90590f119d056d233c33006b2fd235dd990d5564992261ee7ae8", size = 5562292, upload-time = "2025-04-05T18:25:18.744Z" }, - { url = "https://files.pythonhosted.org/packages/8f/79/f4ad46c00b72eb465be2032dad7922a14c929ae983e40cd9a179f1e727db/lxml-5.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:910f39425c6798ce63c93976ae5af5fff6949e2cb446acbd44d6d892103eaea8", size = 5000296, upload-time = "2025-04-05T18:25:21.268Z" }, - { url = "https://files.pythonhosted.org/packages/44/cb/c974078e015990f83d13ef00dac347d74b1d62c2e6ec6e8eeb40ec9a1f1a/lxml-5.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9780de781a0d62a7c3680d07963db3048b919fc9e3726d9cfd97296a65ffce1", size = 5114822, upload-time = "2025-04-05T18:25:24.401Z" }, - { url = "https://files.pythonhosted.org/packages/1b/c4/dde5d197d176f232c018e7dfd1acadf3aeb8e9f3effa73d13b62f9540061/lxml-5.3.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1a06b0c6ba2e3ca45a009a78a4eb4d6b63831830c0a83dcdc495c13b9ca97d3e", size = 4941338, upload-time = "2025-04-05T18:25:27.402Z" }, - { url = "https://files.pythonhosted.org/packages/eb/8b/72f8df23f6955bb0f6aca635f72ec52799104907d6b11317099e79e1c752/lxml-5.3.2-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:4c62d0a34d1110769a1bbaf77871a4b711a6f59c4846064ccb78bc9735978644", size = 5586914, upload-time = "2025-04-05T18:25:30.604Z" }, - { url = "https://files.pythonhosted.org/packages/0f/93/7b5ff2971cc5cf017de8ef0e9fdfca6afd249b1e187cb8195e27ed40bb9a/lxml-5.3.2-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:8f961a4e82f411b14538fe5efc3e6b953e17f5e809c463f0756a0d0e8039b700", size = 5082388, upload-time = "2025-04-05T18:25:33.147Z" }, - { url = "https://files.pythonhosted.org/packages/a3/3e/f81d28bceb4e978a3d450098bdc5364d9c58473ad2f4ded04f679dc76e7e/lxml-5.3.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3dfc78f5f9251b6b8ad37c47d4d0bfe63ceb073a916e5b50a3bf5fd67a703335", size = 5161925, upload-time = "2025-04-05T18:25:36.128Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/1218fcfa0dfc8917ce29c66150cc8f6962d35579f412080aec480cc1a990/lxml-5.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10e690bc03214d3537270c88e492b8612d5e41b884f232df2b069b25b09e6711", size = 5022096, upload-time = "2025-04-05T18:25:38.949Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/8eb6fffecd9c5f129461edcdd7e1ac944f9de15783e3d89c84ed6e0374bc/lxml-5.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa837e6ee9534de8d63bc4c1249e83882a7ac22bd24523f83fad68e6ffdf41ae", size = 5652903, upload-time = "2025-04-05T18:25:41.991Z" }, - { url = "https://files.pythonhosted.org/packages/95/79/80f4102a08495c100014593680f3f0f7bd7c1333b13520aed855fc993326/lxml-5.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:da4c9223319400b97a2acdfb10926b807e51b69eb7eb80aad4942c0516934858", size = 5491813, upload-time = "2025-04-05T18:25:44.983Z" }, - { url = "https://files.pythonhosted.org/packages/15/f5/9b1f7edf6565ee31e4300edb1bcc61eaebe50a3cff4053c0206d8dc772f2/lxml-5.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc0e9bdb3aa4d1de703a437576007d366b54f52c9897cae1a3716bb44fc1fc85", size = 5227837, upload-time = "2025-04-05T18:25:47.433Z" }, - { url = "https://files.pythonhosted.org/packages/dd/53/a187c4ccfcd5fbfca01e6c96da39499d8b801ab5dcf57717db95d7a968a8/lxml-5.3.2-cp310-cp310-win32.win32.whl", hash = "sha256:dd755a0a78dd0b2c43f972e7b51a43be518ebc130c9f1a7c4480cf08b4385486", size = 3477533, upload-time = "2025-04-18T06:15:35.546Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2c/397c5a9d76a7a0faf9e5b13143ae1a7e223e71d2197a45da71c21aacb3d4/lxml-5.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:d64ea1686474074b38da13ae218d9fde0d1dc6525266976808f41ac98d9d7980", size = 3805160, upload-time = "2025-04-05T18:25:52.007Z" }, - { url = "https://files.pythonhosted.org/packages/84/b8/2b727f5a90902f7cc5548349f563b60911ca05f3b92e35dfa751349f265f/lxml-5.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d61a7d0d208ace43986a92b111e035881c4ed45b1f5b7a270070acae8b0bfb4", size = 8163457, upload-time = "2025-04-05T18:25:55.176Z" }, - { url = "https://files.pythonhosted.org/packages/91/84/23135b2dc72b3440d68c8f39ace2bb00fe78e3a2255f7c74f7e76f22498e/lxml-5.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856dfd7eda0b75c29ac80a31a6411ca12209183e866c33faf46e77ace3ce8a79", size = 4433445, upload-time = "2025-04-05T18:25:57.631Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1c/6900ade2294488f80598af7b3229669562166384bb10bf4c915342a2f288/lxml-5.3.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a01679e4aad0727bedd4c9407d4d65978e920f0200107ceeffd4b019bd48529", size = 5029603, upload-time = "2025-04-05T18:26:00.145Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e9/31dbe5deaccf0d33ec279cf400306ad4b32dfd1a0fee1fca40c5e90678fe/lxml-5.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6b37b4c3acb8472d191816d4582379f64d81cecbdce1a668601745c963ca5cc", size = 4771236, upload-time = "2025-04-05T18:26:02.656Z" }, - { url = "https://files.pythonhosted.org/packages/68/41/c3412392884130af3415af2e89a2007e00b2a782be6fb848a95b598a114c/lxml-5.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3df5a54e7b7c31755383f126d3a84e12a4e0333db4679462ef1165d702517477", size = 5369815, upload-time = "2025-04-05T18:26:05.842Z" }, - { url = "https://files.pythonhosted.org/packages/34/0a/ba0309fd5f990ea0cc05aba2bea225ef1bcb07ecbf6c323c6b119fc46e7f/lxml-5.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c09a40f28dcded933dc16217d6a092be0cc49ae25811d3b8e937c8060647c353", size = 4843663, upload-time = "2025-04-05T18:26:09.143Z" }, - { url = "https://files.pythonhosted.org/packages/b6/c6/663b5d87d51d00d4386a2d52742a62daa486c5dc6872a443409d9aeafece/lxml-5.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1ef20f1851ccfbe6c5a04c67ec1ce49da16ba993fdbabdce87a92926e505412", size = 4918028, upload-time = "2025-04-05T18:26:12.243Z" }, - { url = "https://files.pythonhosted.org/packages/75/5f/f6a72ccbe05cf83341d4b6ad162ed9e1f1ffbd12f1c4b8bc8ae413392282/lxml-5.3.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f79a63289dbaba964eb29ed3c103b7911f2dce28c36fe87c36a114e6bd21d7ad", size = 4792005, upload-time = "2025-04-05T18:26:15.081Z" }, - { url = "https://files.pythonhosted.org/packages/37/7b/8abd5b332252239ffd28df5842ee4e5bf56e1c613c323586c21ccf5af634/lxml-5.3.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:75a72697d95f27ae00e75086aed629f117e816387b74a2f2da6ef382b460b710", size = 5405363, upload-time = "2025-04-05T18:26:17.618Z" }, - { url = "https://files.pythonhosted.org/packages/5a/79/549b7ec92b8d9feb13869c1b385a0749d7ccfe5590d1e60f11add9cdd580/lxml-5.3.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:b9b00c9ee1cc3a76f1f16e94a23c344e0b6e5c10bec7f94cf2d820ce303b8c01", size = 4932915, upload-time = "2025-04-05T18:26:20.269Z" }, - { url = "https://files.pythonhosted.org/packages/57/eb/4fa626d0bac8b4f2aa1d0e6a86232db030fd0f462386daf339e4a0ee352b/lxml-5.3.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:77cbcab50cbe8c857c6ba5f37f9a3976499c60eada1bf6d38f88311373d7b4bc", size = 4983473, upload-time = "2025-04-05T18:26:23.828Z" }, - { url = "https://files.pythonhosted.org/packages/1b/c8/79d61d13cbb361c2c45fbe7c8bd00ea6a23b3e64bc506264d2856c60d702/lxml-5.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29424058f072a24622a0a15357bca63d796954758248a72da6d512f9bd9a4493", size = 4855284, upload-time = "2025-04-05T18:26:26.504Z" }, - { url = "https://files.pythonhosted.org/packages/80/16/9f84e1ef03a13136ab4f9482c9adaaad425c68b47556b9d3192a782e5d37/lxml-5.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7d82737a8afe69a7c80ef31d7626075cc7d6e2267f16bf68af2c764b45ed68ab", size = 5458355, upload-time = "2025-04-05T18:26:29.086Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6d/f62860451bb4683e87636e49effb76d499773337928e53356c1712ccec24/lxml-5.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:95473d1d50a5d9fcdb9321fdc0ca6e1edc164dce4c7da13616247d27f3d21e31", size = 5300051, upload-time = "2025-04-05T18:26:31.723Z" }, - { url = "https://files.pythonhosted.org/packages/3f/5f/3b6c4acec17f9a57ea8bb89a658a70621db3fb86ea588e7703b6819d9b03/lxml-5.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2162068f6da83613f8b2a32ca105e37a564afd0d7009b0b25834d47693ce3538", size = 5033481, upload-time = "2025-04-05T18:26:34.312Z" }, - { url = "https://files.pythonhosted.org/packages/79/bd/3c4dd7d903bb9981f4876c61ef2ff5d5473e409ef61dc7337ac207b91920/lxml-5.3.2-cp311-cp311-win32.whl", hash = "sha256:f8695752cf5d639b4e981afe6c99e060621362c416058effd5c704bede9cb5d1", size = 3474266, upload-time = "2025-04-05T18:26:36.545Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ea/9311fa1ef75b7d601c89600fc612838ee77ad3d426184941cba9cf62641f/lxml-5.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:d1a94cbb4ee64af3ab386c2d63d6d9e9cf2e256ac0fd30f33ef0a3c88f575174", size = 3815230, upload-time = "2025-04-05T18:26:39.486Z" }, - { url = "https://files.pythonhosted.org/packages/0d/7e/c749257a7fabc712c4df57927b0f703507f316e9f2c7e3219f8f76d36145/lxml-5.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:16b3897691ec0316a1aa3c6585f61c8b7978475587c5b16fc1d2c28d283dc1b0", size = 8193212, upload-time = "2025-04-05T18:26:42.692Z" }, - { url = "https://files.pythonhosted.org/packages/a8/50/17e985ba162c9f1ca119f4445004b58f9e5ef559ded599b16755e9bfa260/lxml-5.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8d4b34a0eeaf6e73169dcfd653c8d47f25f09d806c010daf074fba2db5e2d3f", size = 4451439, upload-time = "2025-04-05T18:26:46.468Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b5/4960ba0fcca6ce394ed4a2f89ee13083e7fcbe9641a91166e8e9792fedb1/lxml-5.3.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cd7a959396da425022e1e4214895b5cfe7de7035a043bcc2d11303792b67554", size = 5052146, upload-time = "2025-04-05T18:26:49.737Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d1/184b04481a5d1f5758916de087430752a7b229bddbd6c1d23405078c72bd/lxml-5.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cac5eaeec3549c5df7f8f97a5a6db6963b91639389cdd735d5a806370847732b", size = 4789082, upload-time = "2025-04-05T18:26:52.295Z" }, - { url = "https://files.pythonhosted.org/packages/7d/75/1a19749d373e9a3d08861addccdf50c92b628c67074b22b8f3c61997cf5a/lxml-5.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b5f7d77334877c2146e7bb8b94e4df980325fab0a8af4d524e5d43cd6f789d", size = 5312300, upload-time = "2025-04-05T18:26:54.923Z" }, - { url = "https://files.pythonhosted.org/packages/fb/00/9d165d4060d3f347e63b219fcea5c6a3f9193e9e2868c6801e18e5379725/lxml-5.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13f3495cfec24e3d63fffd342cc8141355d1d26ee766ad388775f5c8c5ec3932", size = 4836655, upload-time = "2025-04-05T18:26:57.488Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/06720a33cc155966448a19677f079100517b6629a872382d22ebd25e48aa/lxml-5.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e70ad4c9658beeff99856926fd3ee5fde8b519b92c693f856007177c36eb2e30", size = 4961795, upload-time = "2025-04-05T18:27:00.126Z" }, - { url = "https://files.pythonhosted.org/packages/2d/57/4540efab2673de2904746b37ef7f74385329afd4643ed92abcc9ec6e00ca/lxml-5.3.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:507085365783abd7879fa0a6fa55eddf4bdd06591b17a2418403bb3aff8a267d", size = 4779791, upload-time = "2025-04-05T18:27:03.061Z" }, - { url = "https://files.pythonhosted.org/packages/99/ad/6056edf6c9f4fa1d41e6fbdae52c733a4a257fd0d7feccfa26ae051bb46f/lxml-5.3.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:5bb304f67cbf5dfa07edad904732782cbf693286b9cd85af27059c5779131050", size = 5346807, upload-time = "2025-04-05T18:27:05.877Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/5be91fc91a18f3f705ea5533bc2210b25d738c6b615bf1c91e71a9b2f26b/lxml-5.3.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:3d84f5c093645c21c29a4e972b84cb7cf682f707f8706484a5a0c7ff13d7a988", size = 4909213, upload-time = "2025-04-05T18:27:08.588Z" }, - { url = "https://files.pythonhosted.org/packages/f3/74/71bb96a3b5ae36b74e0402f4fa319df5559a8538577f8c57c50f1b57dc15/lxml-5.3.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:bdc13911db524bd63f37b0103af014b7161427ada41f1b0b3c9b5b5a9c1ca927", size = 4987694, upload-time = "2025-04-05T18:27:11.66Z" }, - { url = "https://files.pythonhosted.org/packages/08/c2/3953a68b0861b2f97234b1838769269478ccf872d8ea7a26e911238220ad/lxml-5.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ec944539543f66ebc060ae180d47e86aca0188bda9cbfadff47d86b0dc057dc", size = 4862865, upload-time = "2025-04-05T18:27:14.194Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9a/52e48f7cfd5a5e61f44a77e679880580dfb4f077af52d6ed5dd97e3356fe/lxml-5.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:59d437cc8a7f838282df5a199cf26f97ef08f1c0fbec6e84bd6f5cc2b7913f6e", size = 5423383, upload-time = "2025-04-05T18:27:16.988Z" }, - { url = "https://files.pythonhosted.org/packages/17/67/42fe1d489e4dcc0b264bef361aef0b929fbb2b5378702471a3043bc6982c/lxml-5.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e275961adbd32e15672e14e0cc976a982075208224ce06d149c92cb43db5b93", size = 5286864, upload-time = "2025-04-05T18:27:19.703Z" }, - { url = "https://files.pythonhosted.org/packages/29/e4/03b1d040ee3aaf2bd4e1c2061de2eae1178fe9a460d3efc1ea7ef66f6011/lxml-5.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:038aeb6937aa404480c2966b7f26f1440a14005cb0702078c173c028eca72c31", size = 5056819, upload-time = "2025-04-05T18:27:22.814Z" }, - { url = "https://files.pythonhosted.org/packages/83/b3/e2ec8a6378e4d87da3af9de7c862bcea7ca624fc1a74b794180c82e30123/lxml-5.3.2-cp312-cp312-win32.whl", hash = "sha256:3c2c8d0fa3277147bff180e3590be67597e17d365ce94beb2efa3138a2131f71", size = 3486177, upload-time = "2025-04-05T18:27:25.078Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8a/6a08254b0bab2da9573735725caab8302a2a1c9b3818533b41568ca489be/lxml-5.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:77809fcd97dfda3f399102db1794f7280737b69830cd5c961ac87b3c5c05662d", size = 3817134, upload-time = "2025-04-05T18:27:27.481Z" }, - { url = "https://files.pythonhosted.org/packages/19/fe/904fd1b0ba4f42ed5a144fcfff7b8913181892a6aa7aeb361ee783d441f8/lxml-5.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:77626571fb5270ceb36134765f25b665b896243529eefe840974269b083e090d", size = 8173598, upload-time = "2025-04-05T18:27:31.229Z" }, - { url = "https://files.pythonhosted.org/packages/97/e8/5e332877b3ce4e2840507b35d6dbe1cc33b17678ece945ba48d2962f8c06/lxml-5.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78a533375dc7aa16d0da44af3cf6e96035e484c8c6b2b2445541a5d4d3d289ee", size = 4441586, upload-time = "2025-04-05T18:27:33.883Z" }, - { url = "https://files.pythonhosted.org/packages/de/f4/8fe2e6d8721803182fbce2325712e98f22dbc478126070e62731ec6d54a0/lxml-5.3.2-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6f62b2404b3f3f0744bbcabb0381c5fe186fa2a9a67ecca3603480f4846c585", size = 5038447, upload-time = "2025-04-05T18:27:36.426Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ac/fa63f86a1a4b1ba8b03599ad9e2f5212fa813223ac60bfe1155390d1cc0c/lxml-5.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea918da00091194526d40c30c4996971f09dacab032607581f8d8872db34fbf", size = 4783583, upload-time = "2025-04-05T18:27:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7a/08898541296a02c868d4acc11f31a5839d80f5b21d4a96f11d4c0fbed15e/lxml-5.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c35326f94702a7264aa0eea826a79547d3396a41ae87a70511b9f6e9667ad31c", size = 5305684, upload-time = "2025-04-05T18:27:42.16Z" }, - { url = "https://files.pythonhosted.org/packages/0b/be/9a6d80b467771b90be762b968985d3de09e0d5886092238da65dac9c1f75/lxml-5.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3bef90af21d31c4544bc917f51e04f94ae11b43156356aff243cdd84802cbf2", size = 4830797, upload-time = "2025-04-05T18:27:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/8d/1c/493632959f83519802637f7db3be0113b6e8a4e501b31411fbf410735a75/lxml-5.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52fa7ba11a495b7cbce51573c73f638f1dcff7b3ee23697467dc063f75352a69", size = 4950302, upload-time = "2025-04-05T18:27:47.979Z" }, - { url = "https://files.pythonhosted.org/packages/c7/13/01aa3b92a6b93253b90c061c7527261b792f5ae7724b420cded733bfd5d6/lxml-5.3.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ad131e2c4d2c3803e736bb69063382334e03648de2a6b8f56a878d700d4b557d", size = 4775247, upload-time = "2025-04-05T18:27:51.174Z" }, - { url = "https://files.pythonhosted.org/packages/60/4a/baeb09fbf5c84809e119c9cf8e2e94acec326a9b45563bf5ae45a234973b/lxml-5.3.2-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:00a4463ca409ceacd20490a893a7e08deec7870840eff33dc3093067b559ce3e", size = 5338824, upload-time = "2025-04-05T18:27:54.15Z" }, - { url = "https://files.pythonhosted.org/packages/69/c7/a05850f169ad783ed09740ac895e158b06d25fce4b13887a8ac92a84d61c/lxml-5.3.2-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:87e8d78205331cace2b73ac8249294c24ae3cba98220687b5b8ec5971a2267f1", size = 4899079, upload-time = "2025-04-05T18:27:57.03Z" }, - { url = "https://files.pythonhosted.org/packages/de/48/18ca583aba5235582db0e933ed1af6540226ee9ca16c2ee2d6f504fcc34a/lxml-5.3.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bf6389133bb255e530a4f2f553f41c4dd795b1fbb6f797aea1eff308f1e11606", size = 4978041, upload-time = "2025-04-05T18:27:59.918Z" }, - { url = "https://files.pythonhosted.org/packages/b6/55/6968ddc88554209d1dba0dca196360c629b3dfe083bc32a3370f9523a0c4/lxml-5.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b3709fc752b42fb6b6ffa2ba0a5b9871646d97d011d8f08f4d5b3ee61c7f3b2b", size = 4859761, upload-time = "2025-04-05T18:28:02.83Z" }, - { url = "https://files.pythonhosted.org/packages/2e/52/d2d3baa1e0b7d04a729613160f1562f466fb1a0e45085a33acb0d6981a2b/lxml-5.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:abc795703d0de5d83943a4badd770fbe3d1ca16ee4ff3783d7caffc252f309ae", size = 5418209, upload-time = "2025-04-05T18:28:05.851Z" }, - { url = "https://files.pythonhosted.org/packages/d3/50/6005b297ba5f858a113d6e81ccdb3a558b95a615772e7412d1f1cbdf22d7/lxml-5.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:98050830bb6510159f65d9ad1b8aca27f07c01bb3884ba95f17319ccedc4bcf9", size = 5274231, upload-time = "2025-04-05T18:28:08.849Z" }, - { url = "https://files.pythonhosted.org/packages/fb/33/6f40c09a5f7d7e7fcb85ef75072e53eba3fbadbf23e4991ca069ab2b1abb/lxml-5.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ba465a91acc419c5682f8b06bcc84a424a7aa5c91c220241c6fd31de2a72bc6", size = 5051899, upload-time = "2025-04-05T18:28:11.729Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3a/673bc5c0d5fb6596ee2963dd016fdaefaed2c57ede82c7634c08cbda86c1/lxml-5.3.2-cp313-cp313-win32.whl", hash = "sha256:56a1d56d60ea1ec940f949d7a309e0bff05243f9bd337f585721605670abb1c1", size = 3485315, upload-time = "2025-04-05T18:28:14.815Z" }, - { url = "https://files.pythonhosted.org/packages/8c/be/cab8dd33b0dbe3af5b5d4d24137218f79ea75d540f74eb7d8581195639e0/lxml-5.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:1a580dc232c33d2ad87d02c8a3069d47abbcdce974b9c9cc82a79ff603065dbe", size = 3814639, upload-time = "2025-04-05T18:28:17.268Z" }, - { url = "https://files.pythonhosted.org/packages/3d/1a/480682ac974e0f8778503300a61d96c3b4d992d2ae024f9db18d5fd895d1/lxml-5.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:521ab9c80b98c30b2d987001c3ede2e647e92eeb2ca02e8cb66ef5122d792b24", size = 3937182, upload-time = "2025-04-05T18:30:39.214Z" }, - { url = "https://files.pythonhosted.org/packages/74/e6/ac87269713e372b58c4334913601a65d7a6f3b7df9ac15a4a4014afea7ae/lxml-5.3.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1231b0f9810289d41df1eacc4ebb859c63e4ceee29908a0217403cddce38d0", size = 4235148, upload-time = "2025-04-05T18:30:42.261Z" }, - { url = "https://files.pythonhosted.org/packages/75/ec/7d7af58047862fb59fcdec6e3abcffc7a98f7f7560e580485169ce28b706/lxml-5.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271f1a4d5d2b383c36ad8b9b489da5ea9c04eca795a215bae61ed6a57cf083cd", size = 4349974, upload-time = "2025-04-05T18:30:45.291Z" }, - { url = "https://files.pythonhosted.org/packages/ff/de/021ef34a57a372778f44182d2043fa3cae0b0407ac05fc35834f842586f2/lxml-5.3.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:6fca8a5a13906ba2677a5252752832beb0f483a22f6c86c71a2bb320fba04f61", size = 4238656, upload-time = "2025-04-05T18:30:48.383Z" }, - { url = "https://files.pythonhosted.org/packages/0a/96/00874cb83ebb2cf649f2a8cad191d8da64fe1cf15e6580d5a7967755d6a3/lxml-5.3.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ea0c3b7922209160faef194a5b6995bfe7fa05ff7dda6c423ba17646b7b9de10", size = 4373836, upload-time = "2025-04-05T18:30:52.189Z" }, - { url = "https://files.pythonhosted.org/packages/6b/40/7d49ff503cc90b03253eba0768feec909b47ce92a90591b025c774a29a95/lxml-5.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0a006390834603e5952a2ff74b9a31a6007c7cc74282a087aa6467afb4eea987", size = 3487898, upload-time = "2025-04-05T18:30:55.122Z" }, + { url = "https://files.pythonhosted.org/packages/02/6e/ee8fc0e01202eb3dd2b9e1ea4f0910d72425d35c66187c63931d7a3ea73f/lxml-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41dcc4c7b10484257cbd6c37b83ddb26df2b0e5aff5ac00d095689015af868ec", size = 8540733, upload-time = "2026-04-18T04:27:33.185Z" }, + { url = "https://files.pythonhosted.org/packages/54/e8/325fe9b942824c773dffe1baf0c35b046a763851fdff4393af4450bceeb7/lxml-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a31286dbb5e74c8e9a5344465b77ab4c5bd511a253b355b5ca2fae7e579fafec", size = 4602805, upload-time = "2026-04-18T04:27:36.097Z" }, + { url = "https://files.pythonhosted.org/packages/2d/81/221aa3ea4a40370bb0358fa454cbe7e5a837e522f7630c24dfef3f9a73b0/lxml-6.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1bc4cc83fb7f66ffb16f74d6dd0162e144333fc36ebcce32246f80c8735b2551", size = 5002652, upload-time = "2026-04-18T04:27:30.603Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e1/fdbfb9019542f1875c093576df7f37adc2983c8ba7ecf17e5f14490bc107/lxml-6.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:20cf4d0651987c906a2f5cba4e3a8d6ba4bfdf973cfe2a96c0d6053888ea2ecd", size = 5155332, upload-time = "2026-04-18T04:27:33.507Z" }, + { url = "https://files.pythonhosted.org/packages/56/b1/4087c782fff397cd03abf9c551069be59bb04a7e548c50fb7b9c4cdaca28/lxml-6.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb34ea45a82dd637c2c97ae1bbb920850c1e59bcae79ce1c15af531d83e7215", size = 5057226, upload-time = "2026-04-18T04:27:37.567Z" }, + { url = "https://files.pythonhosted.org/packages/5d/66/516c79dec8417f3a972327330254c0b5fac93d5c3ecfd8a5b43650a5a4d9/lxml-6.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d9b99e5b2597e4f5aed2484fef835256fa1b68a19e4265c97628ef4bf8bcf4", size = 5287588, upload-time = "2026-04-18T04:27:41.4Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/e578f4cbeb42b9df9f29b0d44a45a7cdfa3a5ae300dd59ec68e3602d29bb/lxml-6.1.0-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:d43aa26dcda363f21e79afa0668f5029ed7394b3bb8c92a6927a3d34e8b610ea", size = 5412438, upload-time = "2026-04-18T04:27:45.589Z" }, + { url = "https://files.pythonhosted.org/packages/47/5b/2aa68307d6d15959e84d4882f9c04f2da63127eac463e1594166f681ef77/lxml-6.1.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:6262b87f9e5c1e5fe501d6c153247289af42eb44ad7660b9b3de17baaf92d6f6", size = 4770997, upload-time = "2026-04-18T04:27:49.853Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c9/3e51fc1228310a836b4eb32595ae00154ab12197fca944676a3ab3b163ea/lxml-6.1.0-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1392c569c032f78a11a25d1de1c43fff13294c793b39e19d84fade3045cbbc3", size = 5359678, upload-time = "2026-04-18T04:31:56.184Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/ab8bc834f977fbbd310e697b120787c153db026f9151e02a88d2645d4e5b/lxml-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:045e387d1f4f42a418380930fa3f45c73c9b392faf67e495e58902e68e8f44a7", size = 5107890, upload-time = "2026-04-18T04:32:00.387Z" }, + { url = "https://files.pythonhosted.org/packages/bb/10/8a143cfa3ac99cb5b0523ff6d0429a9c9dddf25ffeae09caa3866c7964d9/lxml-6.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9f93d5b8b07f73e8c77e3c6556a3db269918390c804b5e5fcdd4858232cc8f16", size = 4803977, upload-time = "2026-04-18T04:32:05.099Z" }, + { url = "https://files.pythonhosted.org/packages/45/fd/ee02faf52fa39c2fe32f824628958b9aa86dff21343dc3161f0e3c6ccd15/lxml-6.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:de550d129f18d8ab819651ffe4f38b1b713c7e116707de3c0c6400d0ef34fbc1", size = 5350277, upload-time = "2026-04-18T04:32:09.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/8c/b3481364b8554b5d36d540189a87fc71e94b0b01c24f8f152bd662dd2e45/lxml-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c08da09dc003c9e8c70e06b53a11db6fb3b250c21c4236b03c7d7b443c318e7a", size = 5309717, upload-time = "2026-04-18T04:32:13.303Z" }, + { url = "https://files.pythonhosted.org/packages/74/e8/a6b21927077a9127afa17473b6576b322616f34ac50ee4f577e763b75ec0/lxml-6.1.0-cp310-cp310-win32.whl", hash = "sha256:37448bf9c7d7adfc5254763901e2bbd6bb876228dfc1fc7f66e58c06368a7544", size = 3598491, upload-time = "2026-04-18T04:27:24.288Z" }, + { url = "https://files.pythonhosted.org/packages/ea/82/14dea800d041274d96c07d49ff9191f011d1427450850de19bf541e2cc12/lxml-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2593a0a6621545b9095b71ad74ed4226eba438a7d9fc3712a99bdb15508cf93a", size = 4020906, upload-time = "2026-04-18T04:27:27.53Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ba/d3539aaf4d9d21456b9a7b902816623227d05d63e7c5aafd8834c4b9bed6/lxml-6.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80807d72f96b96ad5588cb85c75616e4f2795a7737d4630784c51497beb7776", size = 3667787, upload-time = "2026-04-18T04:27:29.407Z" }, + { url = "https://files.pythonhosted.org/packages/5e/5d/3bccad330292946f97962df9d5f2d3ae129cce6e212732a781e856b91e07/lxml-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9", size = 8526232, upload-time = "2026-04-18T04:27:40.389Z" }, + { url = "https://files.pythonhosted.org/packages/a7/51/adc8826570a112f83bb4ddb3a2ab510bbc2ccd62c1b9fe1f34fae2d90b57/lxml-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50", size = 4595448, upload-time = "2026-04-18T04:27:44.208Z" }, + { url = "https://files.pythonhosted.org/packages/54/84/5a9ec07cbe1d2334a6465f863b949a520d2699a755738986dcd3b6b89e3f/lxml-6.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5", size = 4923771, upload-time = "2026-04-18T04:32:17.402Z" }, + { url = "https://files.pythonhosted.org/packages/a7/23/851cfa33b6b38adb628e45ad51fb27105fa34b2b3ba9d1d4aa7a9428dfe0/lxml-6.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e", size = 5068101, upload-time = "2026-04-18T04:32:21.437Z" }, + { url = "https://files.pythonhosted.org/packages/b0/38/41bf99c2023c6b79916ba057d83e9db21d642f473cac210201222882d38b/lxml-6.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512", size = 5002573, upload-time = "2026-04-18T04:32:25.373Z" }, + { url = "https://files.pythonhosted.org/packages/c2/20/053aa10bdc39747e1e923ce2d45413075e84f70a136045bb09e5eaca41d3/lxml-6.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c", size = 5202816, upload-time = "2026-04-18T04:32:29.393Z" }, + { url = "https://files.pythonhosted.org/packages/9a/da/bc710fad8bf04b93baee752c192eaa2210cd3a84f969d0be7830fea55802/lxml-6.1.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5", size = 5329999, upload-time = "2026-04-18T04:32:34.019Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/bf035dedbdf7fab49411aa52e4236f3445e98d38647d85419e6c0d2806b9/lxml-6.1.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289", size = 4659643, upload-time = "2026-04-18T04:32:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4f/22be31f33727a5e4c7b01b0a874503026e50329b259d3587e0b923cf964b/lxml-6.1.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a", size = 5265963, upload-time = "2026-04-18T04:32:41.881Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2b/d44d0e5c79226017f4ab8c87a802ebe4f89f97e6585a8e4166dffcdd7b6e/lxml-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3", size = 5045444, upload-time = "2026-04-18T04:32:44.512Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c3/3f034fec1594c331a6dbf9491238fdcc9d66f68cc529e109ec75b97197e1/lxml-6.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9", size = 4712703, upload-time = "2026-04-18T04:32:47.16Z" }, + { url = "https://files.pythonhosted.org/packages/12/16/0b83fccc158218aca75a7aa33e97441df737950734246b9fffa39301603d/lxml-6.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11", size = 5252745, upload-time = "2026-04-18T04:32:50.427Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ee/12e6c1b39a77666c02eaa77f94a870aaf63c4ac3a497b2d52319448b01c6/lxml-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4", size = 5226822, upload-time = "2026-04-18T04:32:53.437Z" }, + { url = "https://files.pythonhosted.org/packages/34/20/c7852904858b4723af01d2fc14b5d38ff57cb92f01934a127ebd9a9e51aa/lxml-6.1.0-cp311-cp311-win32.whl", hash = "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3", size = 3594026, upload-time = "2026-04-18T04:27:31.903Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/d60c732b56da5085175c07c74b2df4e6d181b0c9a61e1691474f06ef4b39/lxml-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7", size = 4025114, upload-time = "2026-04-18T04:27:34.077Z" }, + { url = "https://files.pythonhosted.org/packages/c2/df/c84dcc175fd690823436d15b41cb920cd5ba5e14cd8bfb00949d5903b320/lxml-6.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39", size = 3667742, upload-time = "2026-04-18T04:27:38.45Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d4/9326838b59dc36dfae42eec9656b97520f9997eee1de47b8316aaeed169c/lxml-6.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d", size = 8570663, upload-time = "2026-04-18T04:27:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a4/053745ce1f8303ccbb788b86c0db3a91b973675cefc42566a188637b7c40/lxml-6.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93", size = 4624024, upload-time = "2026-04-18T04:27:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/90/97/a517944b20f8fd0932ad2109482bee4e29fe721416387a363306667941f6/lxml-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d", size = 4930895, upload-time = "2026-04-18T04:32:56.29Z" }, + { url = "https://files.pythonhosted.org/packages/94/7c/e08a970727d556caa040a44773c7b7e3ad0f0d73dedc863543e9a8b931f2/lxml-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a", size = 5093820, upload-time = "2026-04-18T04:32:58.94Z" }, + { url = "https://files.pythonhosted.org/packages/88/ee/2a5c2aa2c32016a226ca25d3e1056a8102ea6e1fe308bf50213586635400/lxml-6.1.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105", size = 5005790, upload-time = "2026-04-18T04:33:01.272Z" }, + { url = "https://files.pythonhosted.org/packages/e3/38/a0db9be8f38ad6043ab9429487c128dd1d30f07956ef43040402f8da49e8/lxml-6.1.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485", size = 5630827, upload-time = "2026-04-18T04:33:04.036Z" }, + { url = "https://files.pythonhosted.org/packages/31/ba/3c13d3fc24b7cacf675f808a3a1baabf43a30d0cd24c98f94548e9aa58eb/lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814", size = 5240445, upload-time = "2026-04-18T04:33:06.87Z" }, + { url = "https://files.pythonhosted.org/packages/55/ba/eeef4ccba09b2212fe239f46c1692a98db1878e0872ae320756488878a94/lxml-6.1.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32", size = 5350121, upload-time = "2026-04-18T04:33:09.365Z" }, + { url = "https://files.pythonhosted.org/packages/7e/01/1da87c7b587c38d0cbe77a01aae3b9c1c49ed47d76918ef3db8fc151b1ca/lxml-6.1.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad", size = 4694949, upload-time = "2026-04-18T04:33:11.628Z" }, + { url = "https://files.pythonhosted.org/packages/a1/88/7db0fe66d5aaf128443ee1623dec3db1576f3e4c17751ec0ef5866468590/lxml-6.1.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54", size = 5243901, upload-time = "2026-04-18T04:33:13.95Z" }, + { url = "https://files.pythonhosted.org/packages/00/a8/1346726af7d1f6fca1f11223ba34001462b0a3660416986d37641708d57c/lxml-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d", size = 5048054, upload-time = "2026-04-18T04:33:16.965Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b7/85057012f035d1a0c87e02f8c723ca3c3e6e0728bcf4cb62080b21b1c1e3/lxml-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69", size = 4777324, upload-time = "2026-04-18T04:33:19.832Z" }, + { url = "https://files.pythonhosted.org/packages/75/6c/ad2f94a91073ef570f33718040e8e160d5fb93331cf1ab3ca1323f939e2d/lxml-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d", size = 5645702, upload-time = "2026-04-18T04:33:22.436Z" }, + { url = "https://files.pythonhosted.org/packages/3b/89/0bb6c0bd549c19004c60eea9dc554dd78fd647b72314ef25d460e0d208c6/lxml-6.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5", size = 5232901, upload-time = "2026-04-18T04:33:26.21Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d9/d609a11fb567da9399f525193e2b49847b5a409cdebe737f06a8b7126bdc/lxml-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d", size = 5261333, upload-time = "2026-04-18T04:33:28.984Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3a/ac3f99ec8ac93089e7dd556f279e0d14c24de0a74a507e143a2e4b496e7c/lxml-6.1.0-cp312-cp312-win32.whl", hash = "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f", size = 3596289, upload-time = "2026-04-18T04:27:42.819Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a7/0a915557538593cb1bbeedcd40e13c7a261822c26fecbbdb71dad0c2f540/lxml-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366", size = 3997059, upload-time = "2026-04-18T04:27:46.764Z" }, + { url = "https://files.pythonhosted.org/packages/92/96/a5dc078cf0126fbfbc35611d77ecd5da80054b5893e28fb213a5613b9e1d/lxml-6.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819", size = 3659552, upload-time = "2026-04-18T04:27:51.133Z" }, + { url = "https://files.pythonhosted.org/packages/08/03/69347590f1cf4a6d5a4944bb6099e6d37f334784f16062234e1f892fdb1d/lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45", size = 8559689, upload-time = "2026-04-18T04:31:57.785Z" }, + { url = "https://files.pythonhosted.org/packages/3f/58/25e00bb40b185c974cfe156c110474d9a8a8390d5f7c92a4e328189bb60e/lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d", size = 4617892, upload-time = "2026-04-18T04:32:01.78Z" }, + { url = "https://files.pythonhosted.org/packages/f5/54/92ad98a94ac318dc4f97aaac22ff8d1b94212b2ae8af5b6e9b354bf825f7/lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2", size = 4923489, upload-time = "2026-04-18T04:33:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/15/3b/a20aecfab42bdf4f9b390590d345857ad3ffd7c51988d1c89c53a0c73faf/lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491", size = 5082162, upload-time = "2026-04-18T04:33:34.262Z" }, + { url = "https://files.pythonhosted.org/packages/45/26/2cdb3d281ac1bd175603e290cbe4bad6eff127c0f8de90bafd6f8548f0fd/lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc", size = 4993247, upload-time = "2026-04-18T04:33:36.674Z" }, + { url = "https://files.pythonhosted.org/packages/f6/05/d735aef963740022a08185c84821f689fc903acb3d50326e6b1e9886cc22/lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e", size = 5613042, upload-time = "2026-04-18T04:33:39.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b8/ead7c10efff731738c72e59ed6eb5791854879fbed7ae98781a12006263a/lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2", size = 5228304, upload-time = "2026-04-18T04:33:41.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/10/e9842d2ec322ea65f0a7270aa0315a53abed06058b88ef1b027f620e7a5f/lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9", size = 5341578, upload-time = "2026-04-18T04:33:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/89/54/40d9403d7c2775fa7301d3ddd3464689bfe9ba71acc17dfff777071b4fdc/lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe", size = 4700209, upload-time = "2026-04-18T04:33:47.552Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/bbdcc2cf45dfc7dfffef4fd97e5c47b15919b6a365247d95d6f684ef5e82/lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88", size = 5232365, upload-time = "2026-04-18T04:33:50.249Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/b06875665e53aaba7127611a7bed3b7b9658e20b22bc2dd217a0b7ab0091/lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181", size = 5043654, upload-time = "2026-04-18T04:33:52.71Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9c/e71a069d09641c1a7abeb30e693f828c7c90a41cbe3d650b2d734d876f85/lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24", size = 4769326, upload-time = "2026-04-18T04:33:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/cc/06/7a9cd84b3d4ed79adf35f874750abb697dec0b4a81a836037b36e47c091a/lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e", size = 5635879, upload-time = "2026-04-18T04:33:58.509Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/9d57916befc1e54c451712c7ee48e9e74e80ae4d03bdce49914e0aee42cd/lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495", size = 5224048, upload-time = "2026-04-18T04:34:00.943Z" }, + { url = "https://files.pythonhosted.org/packages/99/75/90c4eefda0c08c92221fe0753db2d6699a4c628f76ff4465ec20dea84cc1/lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33", size = 5250241, upload-time = "2026-04-18T04:34:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/5e/73/16596f7e4e38fa33084b9ccbccc22a15f82a290a055126f2c1541236d2ff/lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62", size = 3596938, upload-time = "2026-04-18T04:31:56.206Z" }, + { url = "https://files.pythonhosted.org/packages/8e/63/981401c5680c1eb30893f00a19641ac80db5d1e7086c62cb4b13ed813038/lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16", size = 3995728, upload-time = "2026-04-18T04:31:58.763Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e8/c358a38ac3e541d16a1b527e4e9cb78c0419b0506a070ace11777e5e8404/lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d", size = 3658372, upload-time = "2026-04-18T04:32:03.629Z" }, + { url = "https://files.pythonhosted.org/packages/f2/88/55143966481409b1740a3ac669e611055f49efd68087a5ce41582325db3e/lxml-6.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842", size = 3930134, upload-time = "2026-04-18T04:32:35.008Z" }, + { url = "https://files.pythonhosted.org/packages/b5/97/28b985c2983938d3cb696dd5501423afb90a8c3e869ef5d3c62569282c0f/lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c", size = 4210749, upload-time = "2026-04-18T04:36:03.626Z" }, + { url = "https://files.pythonhosted.org/packages/29/67/dfab2b7d58214921935ccea7ce9b3df9b7d46f305d12f0f532ac7cf6b804/lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de", size = 4318463, upload-time = "2026-04-18T04:36:06.309Z" }, + { url = "https://files.pythonhosted.org/packages/32/a2/4ac7eb32a4d997dd352c32c32399aae27b3f268d440e6f9cfa405b575d2f/lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635", size = 4251124, upload-time = "2026-04-18T04:36:09.056Z" }, + { url = "https://files.pythonhosted.org/packages/33/ef/d6abd850bb4822f9b720cfe36b547a558e694881010ff7d012191e8769c6/lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037", size = 4401758, upload-time = "2026-04-18T04:36:11.803Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/3ee09a5b60cb44c4f2fbc1c9015cfd6ff5afc08f991cab295d3024dcbf2d/lxml-6.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace", size = 3508860, upload-time = "2026-04-18T04:32:48.619Z" }, ] [[package]] From fdf3101b39638634cd9cc0594298bb778f61b538 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Wed, 22 Apr 2026 15:21:35 -0500 Subject: [PATCH 29/32] feat(azure): fall back to DefaultAzureCredential when no API key Enables keyless Azure auth (OIDC Workload Identity Federation, Managed Identity, Azure CLI, env-configured Service Principal) without any crewAI-specific configuration. Customers whose deployment environment already sets the standard azure-identity env vars get keyless auth for free; the existing API-key path is unchanged. Linear: FAC-40 --- lib/crewai/pyproject.toml | 1 + .../crewai/llms/providers/azure/completion.py | 34 ++++++++++++++--- lib/crewai/tests/llms/azure/test_azure.py | 38 +++++++++++++++---- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index 001f2b8a6..cbf017801 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -94,6 +94,7 @@ google-genai = [ ] azure-ai-inference = [ "azure-ai-inference~=1.0.0b9", + "azure-identity>=1.17.0,<2", ] anthropic = [ "anthropic~=0.73.0", diff --git a/lib/crewai/src/crewai/llms/providers/azure/completion.py b/lib/crewai/src/crewai/llms/providers/azure/completion.py index 4b8d842a5..714a7f0e9 100644 --- a/lib/crewai/src/crewai/llms/providers/azure/completion.py +++ b/lib/crewai/src/crewai/llms/providers/azure/completion.py @@ -183,11 +183,6 @@ class AzureCompletion(BaseLLM): AzureCompletion._is_azure_openai_endpoint(self.endpoint) ) - if not self.api_key: - raise ValueError( - "Azure API key is required. Set AZURE_API_KEY environment " - "variable or pass api_key parameter." - ) if not self.endpoint: raise ValueError( "Azure endpoint is required. Set AZURE_ENDPOINT environment " @@ -195,12 +190,39 @@ class AzureCompletion(BaseLLM): ) client_kwargs: dict[str, Any] = { "endpoint": self.endpoint, - "credential": AzureKeyCredential(self.api_key), + "credential": self._resolve_credential(), } if self.api_version: client_kwargs["api_version"] = self.api_version return client_kwargs + def _resolve_credential(self) -> Any: + """Return an Azure credential, preferring the API key when set. + + Without an API key, fall back to ``DefaultAzureCredential`` from + ``azure-identity``. That chain auto-detects the standard keyless + paths the customer's environment may provide — OIDC Workload + Identity Federation (``AZURE_FEDERATED_TOKEN_FILE`` + + ``AZURE_TENANT_ID`` + ``AZURE_CLIENT_ID``), Managed Identity on + AKS/Azure VMs, environment-configured service principals, and + developer tools like the Azure CLI. Installing ``azure-identity`` + is what enables these paths; without it we raise the existing + API-key error. + """ + if self.api_key: + return AzureKeyCredential(self.api_key) + + try: + from azure.identity import DefaultAzureCredential + except ImportError: + raise ValueError( + "Azure API key is required when azure-identity is not " + "installed. Set AZURE_API_KEY, or install azure-identity " + 'for keyless auth: uv add "crewai[azure-ai-inference]"' + ) from None + + return DefaultAzureCredential() + def _get_sync_client(self) -> Any: if self._client is None: self._client = self._build_sync_client() diff --git a/lib/crewai/tests/llms/azure/test_azure.py b/lib/crewai/tests/llms/azure/test_azure.py index d42e2d7fe..774d23f20 100644 --- a/lib/crewai/tests/llms/azure/test_azure.py +++ b/lib/crewai/tests/llms/azure/test_azure.py @@ -389,17 +389,41 @@ def test_azure_raises_error_when_endpoint_missing(): llm._get_sync_client() -def test_azure_raises_error_when_api_key_missing(): - """Credentials are validated lazily: construction succeeds, first +def test_azure_raises_error_when_api_key_missing_without_azure_identity(): + """Without an API key AND without ``azure-identity`` installed, client build raises the descriptive error.""" from crewai.llms.providers.azure.completion import AzureCompletion with patch.dict(os.environ, {}, clear=True): - llm = AzureCompletion( - model="gpt-4", endpoint="https://test.openai.azure.com" - ) - with pytest.raises(ValueError, match="Azure API key is required"): - llm._get_sync_client() + with patch.dict("sys.modules", {"azure.identity": None}): + llm = AzureCompletion( + model="gpt-4", endpoint="https://test.openai.azure.com" + ) + with pytest.raises(ValueError, match="Azure API key is required"): + llm._get_sync_client() + + +def test_azure_uses_default_credential_when_api_key_missing(): + """With ``azure-identity`` installed, a missing API key falls back to + ``DefaultAzureCredential`` instead of raising. This is the path that + enables keyless auth (OIDC WIF on EKS/AKS, Managed Identity, Azure + CLI) without any crewAI-specific config.""" + from unittest.mock import MagicMock + + from crewai.llms.providers.azure.completion import AzureCompletion + + sentinel = MagicMock(name="DefaultAzureCredential()") + with patch.dict(os.environ, {}, clear=True): + with patch( + "azure.identity.DefaultAzureCredential", return_value=sentinel + ) as mock_cls: + llm = AzureCompletion( + model="gpt-4", + endpoint="https://test-ai.services.example.com", + ) + kwargs = llm._make_client_kwargs() + assert kwargs["credential"] is sentinel + mock_cls.assert_called() @pytest.mark.asyncio From 3f7637455c41ea67b1e6192e8b7ac89688918394 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:36:33 -0700 Subject: [PATCH 30/32] feat: supporting e2b --- lib/crewai-tools/pyproject.toml | 5 + lib/crewai-tools/src/crewai_tools/__init__.py | 8 + .../src/crewai_tools/tools/__init__.py | 8 + .../tools/e2b_sandbox_tool/README.md | 120 ++++ .../tools/e2b_sandbox_tool/__init__.py | 12 + .../tools/e2b_sandbox_tool/e2b_base_tool.py | 197 ++++++ .../tools/e2b_sandbox_tool/e2b_exec_tool.py | 62 ++ .../tools/e2b_sandbox_tool/e2b_file_tool.py | 220 ++++++ .../tools/e2b_sandbox_tool/e2b_python_tool.py | 133 ++++ lib/crewai-tools/tool.specs.json | 662 ++++++++++++++++++ uv.lock | 75 +- 11 files changed, 1500 insertions(+), 2 deletions(-) create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/README.md create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/__init__.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_base_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_exec_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_file_tool.py create mode 100644 lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_python_tool.py diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index 7cf64465d..2d3b1bba2 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -143,6 +143,11 @@ daytona = [ "daytona~=0.140.0", ] +e2b = [ + "e2b~=2.20.0", + "e2b-code-interpreter~=2.6.0", +] + [tool.uv] exclude-newer = "3 days" diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index 996b63d57..e03687484 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -71,6 +71,11 @@ from crewai_tools.tools.directory_search_tool.directory_search_tool import ( DirectorySearchTool, ) from crewai_tools.tools.docx_search_tool.docx_search_tool import DOCXSearchTool +from crewai_tools.tools.e2b_sandbox_tool import ( + E2BExecTool, + E2BFileTool, + E2BPythonTool, +) from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool from crewai_tools.tools.file_read_tool.file_read_tool import FileReadTool from crewai_tools.tools.file_writer_tool.file_writer_tool import FileWriterTool @@ -242,6 +247,9 @@ __all__ = [ "DaytonaPythonTool", "DirectoryReadTool", "DirectorySearchTool", + "E2BExecTool", + "E2BFileTool", + "E2BPythonTool", "EXASearchTool", "EnterpriseActionTool", "FileCompressorTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/__init__.py index 40fdb74eb..7cf61c7a3 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/tools/__init__.py @@ -60,6 +60,11 @@ from crewai_tools.tools.directory_search_tool.directory_search_tool import ( DirectorySearchTool, ) from crewai_tools.tools.docx_search_tool.docx_search_tool import DOCXSearchTool +from crewai_tools.tools.e2b_sandbox_tool import ( + E2BExecTool, + E2BFileTool, + E2BPythonTool, +) from crewai_tools.tools.exa_tools.exa_search_tool import EXASearchTool from crewai_tools.tools.file_read_tool.file_read_tool import FileReadTool from crewai_tools.tools.file_writer_tool.file_writer_tool import FileWriterTool @@ -227,6 +232,9 @@ __all__ = [ "DaytonaPythonTool", "DirectoryReadTool", "DirectorySearchTool", + "E2BExecTool", + "E2BFileTool", + "E2BPythonTool", "EXASearchTool", "FileCompressorTool", "FileReadTool", diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/README.md b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/README.md new file mode 100644 index 000000000..81f30996d --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/README.md @@ -0,0 +1,120 @@ +# E2B Sandbox Tools + +Run shell commands, execute Python, and manage files inside an [E2B](https://e2b.dev/) sandbox. E2B provides isolated, ephemeral VMs suitable for agent-driven code execution, with a Jupyter-style code interpreter for rich Python results. + +Three tools are provided so you can pick what the agent actually needs: + +- **`E2BExecTool`** — run a shell command (`sandbox.commands.run`). +- **`E2BPythonTool`** — run a Python cell in the E2B code interpreter (`sandbox.run_code`), returning stdout/stderr and rich results (charts, dataframes). +- **`E2BFileTool`** — read / write / list / delete files (`sandbox.files.*`). + +## Installation + +```shell +uv add "crewai-tools[e2b]" +# or +pip install "crewai-tools[e2b]" +``` + +Set the API key: + +```shell +export E2B_API_KEY="..." +``` + +`E2B_DOMAIN` is also respected if set (for self-hosted or non-default deployments). + +## Sandbox lifecycle + +All three tools share the same lifecycle controls from `E2BBaseTool`: + +| Mode | When the sandbox is created | When it is killed | +| --- | --- | --- | +| **Ephemeral** (default, `persistent=False`) | On every `_run` call | At the end of that same call | +| **Persistent** (`persistent=True`) | Lazily on first use | At process exit (via `atexit`), or manually via `tool.close()` | +| **Attach** (`sandbox_id="…"`) | Never — the tool attaches to an existing sandbox | Never — the tool will not kill a sandbox it did not create | + +Ephemeral mode is the safe default: nothing leaks if the agent forgets to clean up. Use persistent mode when you want filesystem state or installed packages to carry across steps — this is typical when pairing `E2BFileTool` with `E2BExecTool`. + +E2B sandboxes also auto-expire after an idle timeout. Tune it via `sandbox_timeout` (seconds, default `300`). + +## Examples + +### One-shot Python execution (ephemeral) + +```python +from crewai_tools import E2BPythonTool + +tool = E2BPythonTool() +result = tool.run(code="print(sum(range(10)))") +``` + +### Multi-step shell session (persistent) + +```python +from crewai_tools import E2BExecTool, E2BFileTool + +exec_tool = E2BExecTool(persistent=True) +file_tool = E2BFileTool(persistent=True) + +# Each tool keeps its own persistent sandbox. If you need the *same* sandbox +# across two tools, create one tool, grab the sandbox id via +# `tool._persistent_sandbox.sandbox_id`, and pass it to the other via +# `sandbox_id=...`. +``` + +### Attach to an existing sandbox + +```python +from crewai_tools import E2BExecTool + +tool = E2BExecTool(sandbox_id="sbx_...") +``` + +### Custom create params + +```python +tool = E2BExecTool( + persistent=True, + template="my-custom-template", + sandbox_timeout=600, + envs={"MY_FLAG": "1"}, + metadata={"owner": "crewai-agent"}, +) +``` + +## Tool arguments + +### `E2BExecTool` +- `command: str` — shell command to run. +- `cwd: str | None` — working directory. +- `envs: dict[str, str] | None` — extra env vars for this command. +- `timeout: float | None` — seconds. + +### `E2BPythonTool` +- `code: str` — source to execute. +- `language: str | None` — override kernel language (default: Python). +- `envs: dict[str, str] | None` — env vars for the run. +- `timeout: float | None` — seconds. + +### `E2BFileTool` +- `action: "read" | "write" | "append" | "list" | "delete" | "mkdir" | "info" | "exists"` +- `path: str` — absolute path inside the sandbox. +- `content: str | None` — required for `append`; optional for `write`. +- `binary: bool` — if `True`, `content` is base64 on write / returned as base64 on read. +- `depth: int` — for `list`, how many levels to recurse (default 1). + +## Security considerations + +These tools hand the LLM arbitrary shell, Python, and filesystem access inside a remote VM. The threat model to keep in mind: + +- **Prompt-injection is a code-execution vector.** If the agent ingests untrusted content (web pages, scraped documents, user-supplied files, emails, search results), a malicious instruction hidden in that content can coerce the agent into issuing commands to `E2BExecTool` / `E2BPythonTool`. Treat any pipeline that feeds untrusted text into an agent that also has these tools as equivalent to remote code execution — the LLM is the attacker's shell. +- **Ephemeral mode (the default) is the main blast-radius control.** A fresh sandbox is created per call and killed at the end, so injected commands cannot persist state, exfiltrate long-lived secrets, or build up tooling across turns. Leave `persistent=False` unless you have a concrete reason to change it. +- **Avoid this specific combination:** + - untrusted content in the agent's context, **plus** + - `persistent=True` or an explicit long-lived `sandbox_id`, **plus** + - a large `sandbox_timeout` or credentials/secrets seeded into the sandbox via `envs`. + + That stack lets a single injection pivot into a long-running, credentialed shell that survives across turns. If you must run persistently, also keep `sandbox_timeout` short, scope `envs` to the minimum the task needs, and don't feed the same agent untrusted input. +- **Don't mount production credentials.** Anything you put into `envs`, `metadata`, or files written to the sandbox is reachable from the LLM. Use per-task scoped keys, not your personal API tokens. +- **E2B's VM isolation is the final backstop**, not a license to relax the above — isolation prevents escape to the host, but everything the sandbox can reach (the public internet, any service whose token you dropped in) is still fair game for an injected command. diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/__init__.py b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/__init__.py new file mode 100644 index 000000000..8bb3b26b3 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/__init__.py @@ -0,0 +1,12 @@ +from crewai_tools.tools.e2b_sandbox_tool.e2b_base_tool import E2BBaseTool +from crewai_tools.tools.e2b_sandbox_tool.e2b_exec_tool import E2BExecTool +from crewai_tools.tools.e2b_sandbox_tool.e2b_file_tool import E2BFileTool +from crewai_tools.tools.e2b_sandbox_tool.e2b_python_tool import E2BPythonTool + + +__all__ = [ + "E2BBaseTool", + "E2BExecTool", + "E2BFileTool", + "E2BPythonTool", +] diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_base_tool.py b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_base_tool.py new file mode 100644 index 000000000..e22680dfe --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_base_tool.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +import atexit +import logging +import os +import threading +from typing import Any, ClassVar + +from crewai.tools import BaseTool, EnvVar +from pydantic import ConfigDict, Field, PrivateAttr, SecretStr + + +logger = logging.getLogger(__name__) + + +class E2BBaseTool(BaseTool): + """Shared base for tools that act on an E2B sandbox. + + Lifecycle modes: + - persistent=False (default): create a fresh sandbox per `_run` call and + kill it when the call returns. Safer and stateless — nothing leaks if + the agent forgets cleanup. + - persistent=True: lazily create a single sandbox on first use, cache it + on the instance, and register an atexit hook to kill it at process + exit. Cheaper across many calls and lets files/state carry over. + - sandbox_id=: attach to a sandbox the caller already owns. + Never killed by the tool. + """ + + model_config = ConfigDict(arbitrary_types_allowed=True) + + package_dependencies: list[str] = Field(default_factory=lambda: ["e2b"]) + + api_key: SecretStr | None = Field( + default_factory=lambda: ( + SecretStr(val) if (val := os.getenv("E2B_API_KEY")) else None + ), + description="E2B API key. Falls back to E2B_API_KEY env var.", + json_schema_extra={"required": False}, + repr=False, + ) + domain: str | None = Field( + default_factory=lambda: os.getenv("E2B_DOMAIN"), + description="E2B API domain override. Falls back to E2B_DOMAIN env var.", + json_schema_extra={"required": False}, + ) + + template: str | None = Field( + default=None, + description=( + "Optional template/snapshot name or id to create the sandbox from. " + "Defaults to E2B's base template when omitted." + ), + ) + persistent: bool = Field( + default=False, + description=( + "If True, reuse one sandbox across all calls to this tool instance " + "and kill it at process exit. Default False creates and kills a " + "fresh sandbox per call." + ), + ) + sandbox_id: str | None = Field( + default=None, + description=( + "Attach to an existing sandbox by id instead of creating a new " + "one. The tool will never kill a sandbox it did not create." + ), + ) + sandbox_timeout: int = Field( + default=300, + description=( + "Idle timeout in seconds after which E2B auto-kills the sandbox. " + "Applied at create time and when attaching via sandbox_id." + ), + ) + envs: dict[str, str] | None = Field( + default=None, + description="Environment variables to set inside the sandbox at create time.", + ) + metadata: dict[str, str] | None = Field( + default=None, + description="Metadata key-value pairs to attach to the sandbox at create time.", + ) + + env_vars: list[EnvVar] = Field( + default_factory=lambda: [ + EnvVar( + name="E2B_API_KEY", + description="API key for E2B sandbox service", + required=False, + ), + EnvVar( + name="E2B_DOMAIN", + description="E2B API domain (optional)", + required=False, + ), + ] + ) + + _persistent_sandbox: Any | None = PrivateAttr(default=None) + _lock: threading.Lock = PrivateAttr(default_factory=threading.Lock) + _cleanup_registered: bool = PrivateAttr(default=False) + + _sdk_cache: ClassVar[dict[str, Any]] = {} + + @classmethod + def _import_sandbox_class(cls) -> Any: + """Return the Sandbox class used by this tool. + + Subclasses override this to swap in a different SDK (e.g. the code + interpreter sandbox). The default uses plain `e2b.Sandbox`. + """ + cached = cls._sdk_cache.get("e2b.Sandbox") + if cached is not None: + return cached + try: + from e2b import Sandbox # type: ignore[import-untyped] + except ImportError as exc: + raise ImportError( + "The 'e2b' package is required for E2B sandbox tools. " + "Install it with: uv add e2b (or) pip install e2b" + ) from exc + cls._sdk_cache["e2b.Sandbox"] = Sandbox + return Sandbox + + def _connect_kwargs(self) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + if self.api_key is not None: + kwargs["api_key"] = self.api_key.get_secret_value() + if self.domain: + kwargs["domain"] = self.domain + if self.sandbox_timeout is not None: + kwargs["timeout"] = self.sandbox_timeout + return kwargs + + def _create_kwargs(self) -> dict[str, Any]: + kwargs: dict[str, Any] = self._connect_kwargs() + if self.template is not None: + kwargs["template"] = self.template + if self.envs is not None: + kwargs["envs"] = self.envs + if self.metadata is not None: + kwargs["metadata"] = self.metadata + return kwargs + + def _acquire_sandbox(self) -> tuple[Any, bool]: + """Return (sandbox, should_kill_after_use).""" + sandbox_cls = self._import_sandbox_class() + + if self.sandbox_id: + return ( + sandbox_cls.connect(self.sandbox_id, **self._connect_kwargs()), + False, + ) + + if self.persistent: + with self._lock: + if self._persistent_sandbox is None: + self._persistent_sandbox = sandbox_cls.create( + **self._create_kwargs() + ) + if not self._cleanup_registered: + atexit.register(self.close) + self._cleanup_registered = True + return self._persistent_sandbox, False + + sandbox = sandbox_cls.create(**self._create_kwargs()) + return sandbox, True + + def _release_sandbox(self, sandbox: Any, should_kill: bool) -> None: + if not should_kill: + return + try: + sandbox.kill() + except Exception: + logger.debug( + "Best-effort sandbox cleanup failed after ephemeral use; " + "the sandbox may need manual termination.", + exc_info=True, + ) + + def close(self) -> None: + """Kill the cached persistent sandbox if one exists.""" + with self._lock: + sandbox = self._persistent_sandbox + self._persistent_sandbox = None + if sandbox is None: + return + try: + sandbox.kill() + except Exception: + logger.debug( + "Best-effort persistent sandbox cleanup failed at close(); " + "the sandbox may need manual termination.", + exc_info=True, + ) diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_exec_tool.py b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_exec_tool.py new file mode 100644 index 000000000..571be3300 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_exec_tool.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from builtins import type as type_ +from typing import Any + +from pydantic import BaseModel, Field + +from crewai_tools.tools.e2b_sandbox_tool.e2b_base_tool import E2BBaseTool + + +class E2BExecToolSchema(BaseModel): + command: str = Field(..., description="Shell command to execute in the sandbox.") + cwd: str | None = Field( + default=None, + description="Working directory to run the command in. Defaults to the sandbox home dir.", + ) + envs: dict[str, str] | None = Field( + default=None, + description="Optional environment variables to set for this command.", + ) + timeout: float | None = Field( + default=None, + description="Maximum seconds to wait for the command to finish.", + ) + + +class E2BExecTool(E2BBaseTool): + """Run a shell command inside an E2B sandbox.""" + + name: str = "E2B Sandbox Exec" + description: str = ( + "Execute a shell command inside an E2B sandbox and return the exit " + "code, stdout, and stderr. Use this to run builds, package installs, " + "git operations, or any one-off shell command." + ) + args_schema: type_[BaseModel] = E2BExecToolSchema + + def _run( + self, + command: str, + cwd: str | None = None, + envs: dict[str, str] | None = None, + timeout: float | None = None, + ) -> Any: + sandbox, should_kill = self._acquire_sandbox() + try: + run_kwargs: dict[str, Any] = {} + if cwd is not None: + run_kwargs["cwd"] = cwd + if envs is not None: + run_kwargs["envs"] = envs + if timeout is not None: + run_kwargs["timeout"] = timeout + result = sandbox.commands.run(command, **run_kwargs) + return { + "exit_code": getattr(result, "exit_code", None), + "stdout": getattr(result, "stdout", None), + "stderr": getattr(result, "stderr", None), + "error": getattr(result, "error", None), + } + finally: + self._release_sandbox(sandbox, should_kill) diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_file_tool.py b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_file_tool.py new file mode 100644 index 000000000..e39d348c2 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_file_tool.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import base64 +from builtins import type as type_ +import logging +import posixpath +from typing import Any, Literal + +from pydantic import BaseModel, Field, model_validator + +from crewai_tools.tools.e2b_sandbox_tool.e2b_base_tool import E2BBaseTool + + +logger = logging.getLogger(__name__) + + +FileAction = Literal[ + "read", "write", "append", "list", "delete", "mkdir", "info", "exists" +] + + +class E2BFileToolSchema(BaseModel): + action: FileAction = Field( + ..., + description=( + "The filesystem action to perform: 'read' (returns file contents), " + "'write' (create or replace a file with content), 'append' (append " + "content to an existing file — use this for writing large files in " + "chunks to avoid hitting tool-call size limits), 'list' (lists a " + "directory), 'delete' (removes a file/dir), 'mkdir' (creates a " + "directory), 'info' (returns file metadata), 'exists' (returns a " + "boolean for whether the path exists)." + ), + ) + path: str = Field(..., description="Absolute path inside the sandbox.") + content: str | None = Field( + default=None, + description=( + "Content to write or append. If omitted for 'write', an empty file " + "is created. For files larger than a few KB, prefer one 'write' " + "with empty content followed by multiple 'append' calls of ~4KB " + "each to stay within tool-call payload limits." + ), + ) + binary: bool = Field( + default=False, + description=( + "For 'write'/'append': treat content as base64 and upload raw " + "bytes. For 'read': return contents as base64 instead of decoded " + "utf-8." + ), + ) + depth: int = Field( + default=1, + description="For action='list': how many levels deep to recurse (default 1).", + ) + + @model_validator(mode="after") + def _validate_action_args(self) -> E2BFileToolSchema: + if self.action == "append" and self.content is None: + raise ValueError( + "action='append' requires 'content'. Pass the chunk to append " + "in the 'content' field." + ) + return self + + +class E2BFileTool(E2BBaseTool): + """Read, write, and manage files inside an E2B sandbox. + + Notes: + - Most useful with `persistent=True` or an explicit `sandbox_id`. With + the default ephemeral mode, files disappear when this tool call + finishes. + """ + + name: str = "E2B Sandbox Files" + description: str = ( + "Perform filesystem operations inside an E2B sandbox: read a file, " + "write content to a path, append content to an existing file, list a " + "directory, delete a path, make a directory, fetch file metadata, or " + "check whether a path exists. For files larger than a few KB, create " + "the file with action='write' and empty content, then send the body " + "via multiple 'append' calls of ~4KB each to stay within tool-call " + "payload limits." + ) + args_schema: type_[BaseModel] = E2BFileToolSchema + + def _run( + self, + action: FileAction, + path: str, + content: str | None = None, + binary: bool = False, + depth: int = 1, + ) -> Any: + sandbox, should_kill = self._acquire_sandbox() + try: + if action == "read": + return self._read(sandbox, path, binary=binary) + if action == "write": + return self._write(sandbox, path, content or "", binary=binary) + if action == "append": + return self._append(sandbox, path, content or "", binary=binary) + if action == "list": + return self._list(sandbox, path, depth=depth) + if action == "delete": + sandbox.files.remove(path) + return {"status": "deleted", "path": path} + if action == "mkdir": + created = sandbox.files.make_dir(path) + return {"status": "created", "path": path, "created": bool(created)} + if action == "info": + return self._info(sandbox, path) + if action == "exists": + return {"path": path, "exists": bool(sandbox.files.exists(path))} + raise ValueError(f"Unknown action: {action}") + finally: + self._release_sandbox(sandbox, should_kill) + + def _read(self, sandbox: Any, path: str, *, binary: bool) -> dict[str, Any]: + if binary: + data: bytes = sandbox.files.read(path, format="bytes") + return { + "path": path, + "encoding": "base64", + "content": base64.b64encode(data).decode("ascii"), + } + try: + content: str = sandbox.files.read(path) + return {"path": path, "encoding": "utf-8", "content": content} + except UnicodeDecodeError: + data = sandbox.files.read(path, format="bytes") + return { + "path": path, + "encoding": "base64", + "content": base64.b64encode(data).decode("ascii"), + "note": "File was not valid utf-8; returned as base64.", + } + + def _write( + self, sandbox: Any, path: str, content: str, *, binary: bool + ) -> dict[str, Any]: + payload: str | bytes = base64.b64decode(content) if binary else content + self._ensure_parent_dir(sandbox, path) + sandbox.files.write(path, payload) + size = ( + len(payload) + if isinstance(payload, (bytes, bytearray)) + else len(payload.encode("utf-8")) + ) + return {"status": "written", "path": path, "bytes": size} + + def _append( + self, sandbox: Any, path: str, content: str, *, binary: bool + ) -> dict[str, Any]: + chunk: bytes = base64.b64decode(content) if binary else content.encode("utf-8") + self._ensure_parent_dir(sandbox, path) + try: + existing: bytes = sandbox.files.read(path, format="bytes") + except Exception: + existing = b"" + payload = existing + chunk + sandbox.files.write(path, payload) + return { + "status": "appended", + "path": path, + "appended_bytes": len(chunk), + "total_bytes": len(payload), + } + + @staticmethod + def _ensure_parent_dir(sandbox: Any, path: str) -> None: + parent = posixpath.dirname(path) + if not parent or parent in ("/", "."): + return + try: + sandbox.files.make_dir(parent) + except Exception: + logger.debug( + "Best-effort parent-directory create failed for %s; " + "assuming it already exists and proceeding with the write.", + parent, + exc_info=True, + ) + + def _list(self, sandbox: Any, path: str, *, depth: int) -> dict[str, Any]: + entries = sandbox.files.list(path, depth=depth) + return { + "path": path, + "entries": [self._entry_to_dict(e) for e in entries], + } + + def _info(self, sandbox: Any, path: str) -> dict[str, Any]: + return self._entry_to_dict(sandbox.files.get_info(path)) + + @staticmethod + def _entry_to_dict(entry: Any) -> dict[str, Any]: + fields = ( + "name", + "path", + "type", + "size", + "mode", + "permissions", + "owner", + "group", + "modified_time", + "symlink_target", + ) + result: dict[str, Any] = {} + for field in fields: + value = getattr(entry, field, None) + if value is not None and field == "modified_time": + result[field] = ( + value.isoformat() if hasattr(value, "isoformat") else str(value) + ) + else: + result[field] = value + return result diff --git a/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_python_tool.py b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_python_tool.py new file mode 100644 index 000000000..724e92454 --- /dev/null +++ b/lib/crewai-tools/src/crewai_tools/tools/e2b_sandbox_tool/e2b_python_tool.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +from builtins import type as type_ +from typing import Any, ClassVar + +from pydantic import BaseModel, Field + +from crewai_tools.tools.e2b_sandbox_tool.e2b_base_tool import E2BBaseTool + + +class E2BPythonToolSchema(BaseModel): + code: str = Field( + ..., + description="Python source to execute inside the sandbox.", + ) + language: str | None = Field( + default=None, + description=( + "Override the execution language (e.g. 'python', 'r', 'javascript'). " + "Defaults to Python when omitted." + ), + ) + envs: dict[str, str] | None = Field( + default=None, + description="Optional environment variables for the run.", + ) + timeout: float | None = Field( + default=None, + description="Maximum seconds to wait for the code to finish.", + ) + + +class E2BPythonTool(E2BBaseTool): + """Run Python code inside an E2B code interpreter sandbox. + + Uses `e2b_code_interpreter`, which runs cells in a persistent Jupyter-style + kernel so state (imports, variables) carries across calls when + `persistent=True`. + """ + + name: str = "E2B Sandbox Python" + description: str = ( + "Execute a block of Python code inside an E2B code interpreter sandbox " + "and return captured stdout, stderr, the final expression value, and " + "any rich results (charts, dataframes). Use this for data processing, " + "quick scripts, or analysis that should run in an isolated environment." + ) + args_schema: type_[BaseModel] = E2BPythonToolSchema + + package_dependencies: list[str] = Field( + default_factory=lambda: ["e2b_code_interpreter"], + ) + + _ci_cache: ClassVar[dict[str, Any]] = {} + + @classmethod + def _import_sandbox_class(cls) -> Any: + cached = cls._ci_cache.get("Sandbox") + if cached is not None: + return cached + try: + from e2b_code_interpreter import Sandbox # type: ignore[import-untyped] + except ImportError as exc: + raise ImportError( + "The 'e2b_code_interpreter' package is required for the E2B " + "Python tool. Install it with: " + "uv add e2b-code-interpreter (or) " + "pip install e2b-code-interpreter" + ) from exc + cls._ci_cache["Sandbox"] = Sandbox + return Sandbox + + def _run( + self, + code: str, + language: str | None = None, + envs: dict[str, str] | None = None, + timeout: float | None = None, + ) -> Any: + sandbox, should_kill = self._acquire_sandbox() + try: + run_kwargs: dict[str, Any] = {} + if language is not None: + run_kwargs["language"] = language + if envs is not None: + run_kwargs["envs"] = envs + if timeout is not None: + run_kwargs["timeout"] = timeout + execution = sandbox.run_code(code, **run_kwargs) + return self._serialize_execution(execution) + finally: + self._release_sandbox(sandbox, should_kill) + + @staticmethod + def _serialize_execution(execution: Any) -> dict[str, Any]: + logs = getattr(execution, "logs", None) + error = getattr(execution, "error", None) + results = getattr(execution, "results", None) or [] + return { + "text": getattr(execution, "text", None), + "stdout": list(getattr(logs, "stdout", []) or []) if logs else [], + "stderr": list(getattr(logs, "stderr", []) or []) if logs else [], + "error": ( + { + "name": getattr(error, "name", None), + "value": getattr(error, "value", None), + "traceback": getattr(error, "traceback", None), + } + if error + else None + ), + "results": [E2BPythonTool._serialize_result(r) for r in results], + "execution_count": getattr(execution, "execution_count", None), + } + + @staticmethod + def _serialize_result(result: Any) -> dict[str, Any]: + fields = ( + "text", + "html", + "markdown", + "svg", + "png", + "jpeg", + "pdf", + "latex", + "json", + "javascript", + "data", + "is_main_result", + "extra", + ) + return {field: getattr(result, field, None) for field in fields} diff --git a/lib/crewai-tools/tool.specs.json b/lib/crewai-tools/tool.specs.json index 6bd374749..c78dc2c1f 100644 --- a/lib/crewai-tools/tool.specs.json +++ b/lib/crewai-tools/tool.specs.json @@ -8734,6 +8734,668 @@ "type": "object" } }, + { + "description": "Execute a shell command inside an E2B sandbox and return the exit code, stdout, and stderr. Use this to run builds, package installs, git operations, or any one-off shell command.", + "env_vars": [ + { + "default": null, + "description": "API key for E2B sandbox service", + "name": "E2B_API_KEY", + "required": false + }, + { + "default": null, + "description": "E2B API domain (optional)", + "name": "E2B_DOMAIN", + "required": false + } + ], + "humanized_name": "E2B Sandbox Exec", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Run a shell command inside an E2B sandbox.", + "properties": { + "api_key": { + "anyOf": [ + { + "format": "password", + "type": "string", + "writeOnly": true + }, + { + "type": "null" + } + ], + "description": "E2B API key. Falls back to E2B_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "E2B API domain override. Falls back to E2B_DOMAIN env var.", + "required": false, + "title": "Domain" + }, + "envs": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Environment variables to set inside the sandbox at create time.", + "title": "Envs" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Metadata key-value pairs to attach to the sandbox at create time.", + "title": "Metadata" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and kill it at process exit. Default False creates and kills a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id instead of creating a new one. The tool will never kill a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 300, + "description": "Idle timeout in seconds after which E2B auto-kills the sandbox. Applied at create time and when attaching via sandbox_id.", + "title": "Sandbox Timeout", + "type": "integer" + }, + "template": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional template/snapshot name or id to create the sandbox from. Defaults to E2B's base template when omitted.", + "title": "Template" + } + }, + "required": [], + "title": "E2BExecTool", + "type": "object" + }, + "name": "E2BExecTool", + "package_dependencies": [ + "e2b" + ], + "run_params_schema": { + "properties": { + "command": { + "description": "Shell command to execute in the sandbox.", + "title": "Command", + "type": "string" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Working directory to run the command in. Defaults to the sandbox home dir.", + "title": "Cwd" + }, + "envs": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional environment variables to set for this command.", + "title": "Envs" + }, + "timeout": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum seconds to wait for the command to finish.", + "title": "Timeout" + } + }, + "required": [ + "command" + ], + "title": "E2BExecToolSchema", + "type": "object" + } + }, + { + "description": "Perform filesystem operations inside an E2B sandbox: read a file, write content to a path, append content to an existing file, list a directory, delete a path, make a directory, fetch file metadata, or check whether a path exists. For files larger than a few KB, create the file with action='write' and empty content, then send the body via multiple 'append' calls of ~4KB each to stay within tool-call payload limits.", + "env_vars": [ + { + "default": null, + "description": "API key for E2B sandbox service", + "name": "E2B_API_KEY", + "required": false + }, + { + "default": null, + "description": "E2B API domain (optional)", + "name": "E2B_DOMAIN", + "required": false + } + ], + "humanized_name": "E2B Sandbox Files", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Read, write, and manage files inside an E2B sandbox.\n\nNotes:\n - Most useful with `persistent=True` or an explicit `sandbox_id`. With\n the default ephemeral mode, files disappear when this tool call\n finishes.", + "properties": { + "api_key": { + "anyOf": [ + { + "format": "password", + "type": "string", + "writeOnly": true + }, + { + "type": "null" + } + ], + "description": "E2B API key. Falls back to E2B_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "E2B API domain override. Falls back to E2B_DOMAIN env var.", + "required": false, + "title": "Domain" + }, + "envs": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Environment variables to set inside the sandbox at create time.", + "title": "Envs" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Metadata key-value pairs to attach to the sandbox at create time.", + "title": "Metadata" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and kill it at process exit. Default False creates and kills a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id instead of creating a new one. The tool will never kill a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 300, + "description": "Idle timeout in seconds after which E2B auto-kills the sandbox. Applied at create time and when attaching via sandbox_id.", + "title": "Sandbox Timeout", + "type": "integer" + }, + "template": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional template/snapshot name or id to create the sandbox from. Defaults to E2B's base template when omitted.", + "title": "Template" + } + }, + "required": [], + "title": "E2BFileTool", + "type": "object" + }, + "name": "E2BFileTool", + "package_dependencies": [ + "e2b" + ], + "run_params_schema": { + "properties": { + "action": { + "description": "The filesystem action to perform: 'read' (returns file contents), 'write' (create or replace a file with content), 'append' (append content to an existing file \u2014 use this for writing large files in chunks to avoid hitting tool-call size limits), 'list' (lists a directory), 'delete' (removes a file/dir), 'mkdir' (creates a directory), 'info' (returns file metadata), 'exists' (returns a boolean for whether the path exists).", + "enum": [ + "read", + "write", + "append", + "list", + "delete", + "mkdir", + "info", + "exists" + ], + "title": "Action", + "type": "string" + }, + "binary": { + "default": false, + "description": "For 'write'/'append': treat content as base64 and upload raw bytes. For 'read': return contents as base64 instead of decoded utf-8.", + "title": "Binary", + "type": "boolean" + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Content to write or append. If omitted for 'write', an empty file is created. For files larger than a few KB, prefer one 'write' with empty content followed by multiple 'append' calls of ~4KB each to stay within tool-call payload limits.", + "title": "Content" + }, + "depth": { + "default": 1, + "description": "For action='list': how many levels deep to recurse (default 1).", + "title": "Depth", + "type": "integer" + }, + "path": { + "description": "Absolute path inside the sandbox.", + "title": "Path", + "type": "string" + } + }, + "required": [ + "action", + "path" + ], + "title": "E2BFileToolSchema", + "type": "object" + } + }, + { + "description": "Execute a block of Python code inside an E2B code interpreter sandbox and return captured stdout, stderr, the final expression value, and any rich results (charts, dataframes). Use this for data processing, quick scripts, or analysis that should run in an isolated environment.", + "env_vars": [ + { + "default": null, + "description": "API key for E2B sandbox service", + "name": "E2B_API_KEY", + "required": false + }, + { + "default": null, + "description": "E2B API domain (optional)", + "name": "E2B_DOMAIN", + "required": false + } + ], + "humanized_name": "E2B Sandbox Python", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Run Python code inside an E2B code interpreter sandbox.\n\nUses `e2b_code_interpreter`, which runs cells in a persistent Jupyter-style\nkernel so state (imports, variables) carries across calls when\n`persistent=True`.", + "properties": { + "api_key": { + "anyOf": [ + { + "format": "password", + "type": "string", + "writeOnly": true + }, + { + "type": "null" + } + ], + "description": "E2B API key. Falls back to E2B_API_KEY env var.", + "required": false, + "title": "Api Key" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "E2B API domain override. Falls back to E2B_DOMAIN env var.", + "required": false, + "title": "Domain" + }, + "envs": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Environment variables to set inside the sandbox at create time.", + "title": "Envs" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Metadata key-value pairs to attach to the sandbox at create time.", + "title": "Metadata" + }, + "persistent": { + "default": false, + "description": "If True, reuse one sandbox across all calls to this tool instance and kill it at process exit. Default False creates and kills a fresh sandbox per call.", + "title": "Persistent", + "type": "boolean" + }, + "sandbox_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Attach to an existing sandbox by id instead of creating a new one. The tool will never kill a sandbox it did not create.", + "title": "Sandbox Id" + }, + "sandbox_timeout": { + "default": 300, + "description": "Idle timeout in seconds after which E2B auto-kills the sandbox. Applied at create time and when attaching via sandbox_id.", + "title": "Sandbox Timeout", + "type": "integer" + }, + "template": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional template/snapshot name or id to create the sandbox from. Defaults to E2B's base template when omitted.", + "title": "Template" + } + }, + "required": [], + "title": "E2BPythonTool", + "type": "object" + }, + "name": "E2BPythonTool", + "package_dependencies": [ + "e2b_code_interpreter" + ], + "run_params_schema": { + "properties": { + "code": { + "description": "Python source to execute inside the sandbox.", + "title": "Code", + "type": "string" + }, + "envs": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional environment variables for the run.", + "title": "Envs" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Override the execution language (e.g. 'python', 'r', 'javascript'). Defaults to Python when omitted.", + "title": "Language" + }, + "timeout": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Maximum seconds to wait for the code to finish.", + "title": "Timeout" + } + }, + "required": [ + "code" + ], + "title": "E2BPythonToolSchema", + "type": "object" + } + }, { "description": "Search the internet using Exa", "env_vars": [ diff --git a/uv.lock b/uv.lock index de440cae2..768be7983 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-22T16:00:00Z" +exclude-newer = "2026-04-23T07:00:00Z" [manifest] members = [ @@ -700,6 +700,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/76/cab7af7f16c0b09347f2ebe7ffda7101132f786acb767666dce43055faab/botocore_stubs-1.42.41-py3-none-any.whl", hash = "sha256:9423110fb0e391834bd2ed44ae5f879d8cb370a444703d966d30842ce2bcb5f0", size = 66759, upload-time = "2026-02-03T20:46:13.02Z" }, ] +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + [[package]] name = "browserbase" version = "1.8.0" @@ -1489,6 +1498,10 @@ databricks-sdk = [ daytona = [ { name = "daytona" }, ] +e2b = [ + { name = "e2b" }, + { name = "e2b-code-interpreter" }, +] exa-py = [ { name = "exa-py" }, ] @@ -1590,6 +1603,8 @@ requires-dist = [ { name = "cryptography", marker = "extra == 'snowflake'", specifier = ">=43.0.3" }, { name = "databricks-sdk", marker = "extra == 'databricks-sdk'", specifier = ">=0.46.0" }, { name = "daytona", marker = "extra == 'daytona'", specifier = "~=0.140.0" }, + { name = "e2b", marker = "extra == 'e2b'", specifier = "~=2.20.0" }, + { name = "e2b-code-interpreter", marker = "extra == 'e2b'", specifier = "~=2.6.0" }, { name = "exa-py", marker = "extra == 'exa-py'", specifier = ">=1.8.7" }, { name = "firecrawl-py", marker = "extra == 'firecrawl-py'", specifier = ">=1.8.0" }, { name = "gitpython", marker = "extra == 'github'", specifier = ">=3.1.41,<4" }, @@ -1632,7 +1647,7 @@ requires-dist = [ { name = "weaviate-client", marker = "extra == 'weaviate-client'", specifier = ">=4.10.2" }, { name = "youtube-transcript-api", specifier = "~=1.2.2" }, ] -provides-extras = ["apify", "beautifulsoup4", "bedrock", "browserbase", "composio-core", "contextual", "couchbase", "databricks-sdk", "daytona", "exa-py", "firecrawl-py", "github", "hyperbrowser", "linkup-sdk", "mcp", "mongodb", "multion", "mysql", "oxylabs", "patronus", "postgresql", "qdrant-client", "rag", "scrapegraph-py", "scrapfly-sdk", "selenium", "serpapi", "singlestore", "snowflake", "spider-client", "sqlalchemy", "stagehand", "tavily-python", "weaviate-client", "xml"] +provides-extras = ["apify", "beautifulsoup4", "bedrock", "browserbase", "composio-core", "contextual", "couchbase", "databricks-sdk", "daytona", "e2b", "exa-py", "firecrawl-py", "github", "hyperbrowser", "linkup-sdk", "mcp", "mongodb", "multion", "mysql", "oxylabs", "patronus", "postgresql", "qdrant-client", "rag", "scrapegraph-py", "scrapfly-sdk", "selenium", "serpapi", "singlestore", "snowflake", "spider-client", "sqlalchemy", "stagehand", "tavily-python", "weaviate-client", "xml"] [[package]] name = "cryptography" @@ -1975,6 +1990,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] +[[package]] +name = "dockerfile-parse" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/df/929ee0b5d2c8bd8d713c45e71b94ab57c7e11e322130724d54f469b2cd48/dockerfile-parse-2.0.1.tar.gz", hash = "sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc", size = 24556, upload-time = "2023-07-18T13:36:07.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/6c/79cd5bc1b880d8c1a9a5550aa8dacd57353fa3bb2457227e1fb47383eb49/dockerfile_parse-2.0.1-py2.py3-none-any.whl", hash = "sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6", size = 14845, upload-time = "2023-07-18T13:36:06.052Z" }, +] + [[package]] name = "docling" version = "2.84.0" @@ -2125,6 +2149,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, ] +[[package]] +name = "e2b" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "dockerfile-parse" }, + { name = "httpcore" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "python-dateutil" }, + { name = "rich" }, + { name = "typing-extensions" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/87/e9b3bd252a4fe2b3fd6967ff985c7a5a15a31b2d5b8c37e50afb18797b17/e2b-2.20.0.tar.gz", hash = "sha256:52b3a00ac7015bbdce84913b2a57664d2def33d5a4069e34fa2354de31759173", size = 156575, upload-time = "2026-04-02T19:20:32.375Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/ce/e402e2ecebe40ed9af20cddb862386f2ce20336e35c0dea257812129020e/e2b-2.20.0-py3-none-any.whl", hash = "sha256:66f6edcf6b742ca180f3aadcff7966fda86d68430fa6b2becdfa0fcc72224988", size = 296483, upload-time = "2026-04-02T19:20:30.573Z" }, +] + +[[package]] +name = "e2b-code-interpreter" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "e2b" }, + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/dd/f90b56d1597abfcdabdc018ac184fa714066be93d24b97edc2bf0671d483/e2b_code_interpreter-2.6.0.tar.gz", hash = "sha256:67e66531e5cf65c9df6e82aa0bdb1e73223a1ab205f10d47c027eb2ea09b73f9", size = 10683, upload-time = "2026-03-23T17:01:07.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/79/f70d50604584df66064892f3fca7ab57b10ad40c826fd003be53a4cd5fa5/e2b_code_interpreter-2.6.0-py3-none-any.whl", hash = "sha256:a15f1d155566aef98cf2ccc0f8d9b07d15e07582d6cc8a128bc97de371bd617c", size = 13715, upload-time = "2026-03-23T17:01:06.111Z" }, +] + [[package]] name = "effdet" version = "0.4.1" @@ -9407,6 +9466,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, ] +[[package]] +name = "wcmatch" +version = "10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, +] + [[package]] name = "wcwidth" version = "0.6.0" From 3e9deaf9c02b365fde20a8ff7a860961734efe5e Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Thu, 23 Apr 2026 04:55:08 +0800 Subject: [PATCH 31/32] feat: bump versions to 1.14.3a3 --- .github/workflows/import-time.yml | 103 ------------------ lib/crewai-files/src/crewai_files/__init__.py | 2 +- lib/crewai-tools/pyproject.toml | 2 +- lib/crewai-tools/src/crewai_tools/__init__.py | 2 +- lib/crewai/pyproject.toml | 2 +- lib/crewai/src/crewai/__init__.py | 2 +- .../crewai/cli/templates/crew/pyproject.toml | 2 +- .../crewai/cli/templates/flow/pyproject.toml | 2 +- .../crewai/cli/templates/tool/pyproject.toml | 2 +- lib/devtools/src/crewai_devtools/__init__.py | 2 +- scripts/benchmark_import_time.py | 76 ------------- uv.lock | 46 +++++++- 12 files changed, 54 insertions(+), 189 deletions(-) delete mode 100644 .github/workflows/import-time.yml delete mode 100755 scripts/benchmark_import_time.py diff --git a/.github/workflows/import-time.yml b/.github/workflows/import-time.yml deleted file mode 100644 index 7c0126b23..000000000 --- a/.github/workflows/import-time.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: Import Time Guard - -on: - pull_request: - paths: - - "lib/crewai/src/**" - - "lib/crewai/pyproject.toml" - - "pyproject.toml" - -permissions: - contents: read - -jobs: - import-time: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.12"] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: astral-sh/setup-uv@v6 - with: - version: "0.11.3" - enable-cache: true - - - name: Install the project - run: uv sync --all-extras --no-dev - env: - UV_PYTHON: ${{ matrix.python-version }} - - - name: Benchmark PR branch - id: pr - run: | - result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json) - echo "result=$result" >> "$GITHUB_OUTPUT" - echo "pr_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT" - echo "### PR Branch Import Time" >> "$GITHUB_STEP_SUMMARY" - echo "$result" | python3 -c " - import sys, json - d = json.load(sys.stdin) - print(f'- Median: {d[\"median_s\"]}s') - print(f'- Mean: {d[\"mean_s\"]}s ± {d[\"stdev_s\"]}s') - print(f'- Range: {d[\"min_s\"]}s – {d[\"max_s\"]}s') - " >> "$GITHUB_STEP_SUMMARY" - env: - UV_PYTHON: ${{ matrix.python-version }} - - - name: Checkout base branch - run: git checkout ${{ github.event.pull_request.base.sha }} - - - name: Install base branch - run: uv sync --all-extras --no-dev - env: - UV_PYTHON: ${{ matrix.python-version }} - - - name: Benchmark base branch - id: base - run: | - result=$(uv run python scripts/benchmark_import_time.py --runs 5 --json 2>/dev/null || echo '{"median_s": 0}') - echo "result=$result" >> "$GITHUB_OUTPUT" - echo "base_median=$(echo $result | python3 -c 'import sys,json; print(json.load(sys.stdin)["median_s"])')" >> "$GITHUB_OUTPUT" - echo "### Base Branch Import Time" >> "$GITHUB_STEP_SUMMARY" - echo "$result" | python3 -c " - import sys, json - d = json.load(sys.stdin) - if d.get('median_s', 0) > 0: - print(f'- Median: {d[\"median_s\"]}s') - else: - print('- Benchmark script not present on base branch (skip comparison)') - " >> "$GITHUB_STEP_SUMMARY" - env: - UV_PYTHON: ${{ matrix.python-version }} - - - name: Compare and gate - run: | - pr_median=${{ steps.pr.outputs.pr_median }} - base_median=${{ steps.base.outputs.base_median }} - - python3 -c " - pr = float('$pr_median') - base = float('$base_median') - - if base <= 0: - print('⏭️ No base benchmark available — skipping comparison.') - exit(0) - - change_pct = ((pr - base) / base) * 100 - print(f'Base: {base:.3f}s') - print(f'PR: {pr:.3f}s') - print(f'Change: {change_pct:+.1f}%') - print() - - if change_pct > 5: - print(f'❌ BLOCKED: Import time regressed by {change_pct:.1f}% (threshold: 5%)') - exit(1) - elif change_pct > 0: - print(f'⚠️ Slight regression ({change_pct:.1f}%) but within 5% threshold.') - else: - print(f'✅ Import time improved by {abs(change_pct):.1f}%') - " diff --git a/lib/crewai-files/src/crewai_files/__init__.py b/lib/crewai-files/src/crewai_files/__init__.py index 051eda5d4..8ed8d0053 100644 --- a/lib/crewai-files/src/crewai_files/__init__.py +++ b/lib/crewai-files/src/crewai_files/__init__.py @@ -152,4 +152,4 @@ __all__ = [ "wrap_file_source", ] -__version__ = "1.14.3a2" +__version__ = "1.14.3a3" diff --git a/lib/crewai-tools/pyproject.toml b/lib/crewai-tools/pyproject.toml index 2d3b1bba2..40e0fb951 100644 --- a/lib/crewai-tools/pyproject.toml +++ b/lib/crewai-tools/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14" dependencies = [ "pytube~=15.0.0", "requests>=2.33.0,<3", - "crewai==1.14.3a2", + "crewai==1.14.3a3", "tiktoken~=0.8.0", "beautifulsoup4~=4.13.4", "python-docx~=1.2.0", diff --git a/lib/crewai-tools/src/crewai_tools/__init__.py b/lib/crewai-tools/src/crewai_tools/__init__.py index e03687484..af9de2437 100644 --- a/lib/crewai-tools/src/crewai_tools/__init__.py +++ b/lib/crewai-tools/src/crewai_tools/__init__.py @@ -321,4 +321,4 @@ __all__ = [ "ZapierActionTools", ] -__version__ = "1.14.3a2" +__version__ = "1.14.3a3" diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index cbf017801..24f01ea90 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -55,7 +55,7 @@ Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] tools = [ - "crewai-tools==1.14.3a2", + "crewai-tools==1.14.3a3", ] embeddings = [ "tiktoken~=0.8.0" diff --git a/lib/crewai/src/crewai/__init__.py b/lib/crewai/src/crewai/__init__.py index 8d1587056..176b0ca61 100644 --- a/lib/crewai/src/crewai/__init__.py +++ b/lib/crewai/src/crewai/__init__.py @@ -48,7 +48,7 @@ def _suppress_pydantic_deprecation_warnings() -> None: _suppress_pydantic_deprecation_warnings() -__version__ = "1.14.3a2" +__version__ = "1.14.3a3" _LAZY_IMPORTS: dict[str, tuple[str, str]] = { "Memory": ("crewai.memory.unified_memory", "Memory"), diff --git a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml index 93ee87691..65225c0fb 100644 --- a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a2" + "crewai[tools]==1.14.3a3" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml index a7f5747bc..0b0760f5d 100644 --- a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a2" + "crewai[tools]==1.14.3a3" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml index cac3afab3..4dd6b344c 100644 --- a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.3a2" + "crewai[tools]==1.14.3a3" ] [tool.crewai] diff --git a/lib/devtools/src/crewai_devtools/__init__.py b/lib/devtools/src/crewai_devtools/__init__.py index 14470c742..9574be2db 100644 --- a/lib/devtools/src/crewai_devtools/__init__.py +++ b/lib/devtools/src/crewai_devtools/__init__.py @@ -1,3 +1,3 @@ """CrewAI development tools.""" -__version__ = "1.14.3a2" +__version__ = "1.14.3a3" diff --git a/scripts/benchmark_import_time.py b/scripts/benchmark_import_time.py deleted file mode 100755 index e44b2272a..000000000 --- a/scripts/benchmark_import_time.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -"""Benchmark `import crewai` cold start time. - -Usage: - python scripts/benchmark_import_time.py [--runs N] [--json] - -Spawns a fresh Python subprocess for each run to ensure cold imports. -Prints median, mean, min, max across all runs. -With --json, outputs machine-readable results for CI. -""" -import argparse -import json -import statistics -import subprocess -import sys - - -IMPORT_SCRIPT = "import time; t0 = time.perf_counter(); import crewai; print(time.perf_counter() - t0)" - - -def measure_import(python: str = sys.executable) -> float: - """Run a single cold-import measurement in a subprocess.""" - result = subprocess.run( - [python, "-c", IMPORT_SCRIPT], - capture_output=True, - text=True, - env={"PATH": "", "VIRTUAL_ENV": "", "PYTHONPATH": ""}, - timeout=30, - ) - if result.returncode != 0: - raise RuntimeError(f"Import failed: {result.stderr.strip()}") - return float(result.stdout.strip()) - - -def main(): - parser = argparse.ArgumentParser(description="Benchmark crewai import time") - parser.add_argument("--runs", type=int, default=5, help="Number of runs (default: 5)") - parser.add_argument("--json", action="store_true", help="Output JSON for CI") - parser.add_argument("--threshold", type=float, default=None, - help="Fail if median exceeds this value (seconds)") - args = parser.parse_args() - - times = [] - for i in range(args.runs): - t = measure_import() - times.append(t) - if not args.json: - print(f" Run {i + 1}: {t:.3f}s") - - median = statistics.median(times) - mean = statistics.mean(times) - stdev = statistics.stdev(times) if len(times) > 1 else 0.0 - - result = { - "runs": args.runs, - "median_s": round(median, 3), - "mean_s": round(mean, 3), - "stdev_s": round(stdev, 3), - "min_s": round(min(times), 3), - "max_s": round(max(times), 3), - } - - if args.json: - print(json.dumps(result)) - else: - print(f"\n Median: {median:.3f}s") - print(f" Mean: {mean:.3f}s ± {stdev:.3f}s") - print(f" Range: {min(times):.3f}s – {max(times):.3f}s") - - if args.threshold and median > args.threshold: - print(f"\n ❌ FAILED: median {median:.3f}s exceeds threshold {args.threshold:.3f}s") - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/uv.lock b/uv.lock index 768be7983..461c859a4 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-04-23T07:00:00Z" +exclude-newer = "2026-04-22T16:00:00Z" [manifest] members = [ @@ -510,6 +510,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d6/8ebcd05b01a580f086ac9a97fb9fac65c09a4b012161cc97c21a336e880b/azure_core-1.39.0-py3-none-any.whl", hash = "sha256:4ac7b70fab5438c3f68770649a78daf97833caa83827f91df9c14e0e0ea7d34f", size = 218318, upload-time = "2026-03-19T01:31:31.25Z" }, ] +[[package]] +name = "azure-identity" +version = "1.25.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "msal" }, + { name = "msal-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/0e/3a63efb48aa4a5ae2cfca61ee152fbcb668092134d3eb8bfda472dd5c617/azure_identity-1.25.3.tar.gz", hash = "sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6", size = 286304, upload-time = "2026-03-13T01:12:20.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/9a/417b3a533e01953a7c618884df2cb05a71e7b68bdbce4fbdb62349d2a2e8/azure_identity-1.25.3-py3-none-any.whl", hash = "sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c", size = 192138, upload-time = "2026-03-13T01:12:22.951Z" }, +] + [[package]] name = "backoff" version = "2.2.1" @@ -1305,6 +1321,7 @@ aws = [ ] azure-ai-inference = [ { name = "azure-ai-inference" }, + { name = "azure-identity" }, ] bedrock = [ { name = "boto3" }, @@ -1359,6 +1376,7 @@ requires-dist = [ { name = "anthropic", marker = "extra == 'anthropic'", specifier = "~=0.73.0" }, { name = "appdirs", specifier = "~=1.4.4" }, { name = "azure-ai-inference", marker = "extra == 'azure-ai-inference'", specifier = "~=1.0.0b9" }, + { name = "azure-identity", marker = "extra == 'azure-ai-inference'", specifier = ">=1.17.0,<2" }, { name = "boto3", marker = "extra == 'aws'", specifier = "~=1.42.79" }, { name = "boto3", marker = "extra == 'bedrock'", specifier = "~=1.42.79" }, { name = "chromadb", specifier = "~=1.1.0" }, @@ -4484,6 +4502,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] +[[package]] +name = "msal" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/cb/b02b0f748ac668922364ccb3c3bff5b71628a05f5adfec2ba2a5c3031483/msal-1.36.0.tar.gz", hash = "sha256:3f6a4af2b036b476a4215111c4297b4e6e236ed186cd804faefba23e4990978b", size = 174217, upload-time = "2026-04-09T10:20:33.525Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/d3/414d1f0a5f6f4fe5313c2b002c54e78a3332970feb3f5fed14237aa17064/msal-1.36.0-py3-none-any.whl", hash = "sha256:36ecac30e2ff4322d956029aabce3c82301c29f0acb1ad89b94edcabb0e58ec4", size = 121547, upload-time = "2026-04-09T10:20:32.336Z" }, +] + +[[package]] +name = "msal-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, +] + [[package]] name = "msgpack" version = "1.1.2" From bc2fb715601dbee16fbc72beb4092038afc92a84 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Thu, 23 Apr 2026 05:11:06 +0800 Subject: [PATCH 32/32] docs: update changelog and version for v1.14.3a3 --- docs/ar/changelog.mdx | 26 ++++++++++++++++++++++++++ docs/en/changelog.mdx | 26 ++++++++++++++++++++++++++ docs/ko/changelog.mdx | 26 ++++++++++++++++++++++++++ docs/pt-BR/changelog.mdx | 26 ++++++++++++++++++++++++++ 4 files changed, 104 insertions(+) diff --git a/docs/ar/changelog.mdx b/docs/ar/changelog.mdx index eb714117d..acbb285bf 100644 --- a/docs/ar/changelog.mdx +++ b/docs/ar/changelog.mdx @@ -4,6 +4,32 @@ description: "تحديثات المنتج والتحسينات وإصلاحات icon: "clock" mode: "wide" --- + + ## v1.14.3a3 + + [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a3) + + ## ما الذي تغير + + ### الميزات + - إضافة دعم لـ e2b + - تنفيذ التراجع إلى DefaultAzureCredential عند عدم توفير مفتاح API + + ### إصلاحات الأخطاء + - ترقية lxml إلى >=6.1.0 لمعالجة مشكلة الأمان GHSA-vfmq-68hx-4jfw + + ### الوثائق + - إزالة الأسئلة الشائعة حول التسعير من صفحة البناء باستخدام الذكاء الاصطناعي عبر جميع اللغات + + ### الأداء + - تحسين وقت بدء التشغيل البارد بنسبة ~29% من خلال التحميل الكسول لمجموعة أدوات MCP وأنواع الأحداث + + ## المساهمون + + @alex-clawd, @github-advanced-security[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @mattatcha + + + ## v1.14.3a2 diff --git a/docs/en/changelog.mdx b/docs/en/changelog.mdx index 5fdd624ff..8fc991bbf 100644 --- a/docs/en/changelog.mdx +++ b/docs/en/changelog.mdx @@ -4,6 +4,32 @@ description: "Product updates, improvements, and bug fixes for CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a3 + + [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a3) + + ## What's Changed + + ### Features + - Add support for e2b + - Implement fallback to DefaultAzureCredential when no API key is provided + + ### Bug Fixes + - Upgrade lxml to >=6.1.0 to address security issue GHSA-vfmq-68hx-4jfw + + ### Documentation + - Remove pricing FAQ from build-with-ai page across all locales + + ### Performance + - Improve cold start time by ~29% through lazy-loading of MCP SDK and event types + + ## Contributors + + @alex-clawd, @github-advanced-security[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @mattatcha + + + ## v1.14.3a2 diff --git a/docs/ko/changelog.mdx b/docs/ko/changelog.mdx index f744341eb..26c63cad6 100644 --- a/docs/ko/changelog.mdx +++ b/docs/ko/changelog.mdx @@ -4,6 +4,32 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정" icon: "clock" mode: "wide" --- + + ## v1.14.3a3 + + [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a3) + + ## 변경 사항 + + ### 기능 + - e2b 지원 추가 + - API 키가 제공되지 않을 경우 DefaultAzureCredential로 대체 구현 + + ### 버그 수정 + - 보안 문제 GHSA-vfmq-68hx-4jfw를 해결하기 위해 lxml을 >=6.1.0으로 업그레이드 + + ### 문서 + - 모든 지역에서 build-with-ai 페이지의 가격 FAQ 제거 + + ### 성능 + - MCP SDK 및 이벤트 유형의 지연 로딩을 통해 콜드 스타트 시간을 약 29% 개선 + + ## 기여자 + + @alex-clawd, @github-advanced-security[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @mattatcha + + + ## v1.14.3a2 diff --git a/docs/pt-BR/changelog.mdx b/docs/pt-BR/changelog.mdx index ed14c66db..4c7a0234c 100644 --- a/docs/pt-BR/changelog.mdx +++ b/docs/pt-BR/changelog.mdx @@ -4,6 +4,32 @@ description: "Atualizações de produto, melhorias e correções do CrewAI" icon: "clock" mode: "wide" --- + + ## v1.14.3a3 + + [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a3) + + ## O que Mudou + + ### Recursos + - Adicionar suporte para e2b + - Implementar fallback para DefaultAzureCredential quando nenhuma chave de API for fornecida + + ### Correções de Bugs + - Atualizar lxml para >=6.1.0 para resolver problema de segurança GHSA-vfmq-68hx-4jfw + + ### Documentação + - Remover FAQ de preços da página build-with-ai em todos os locais + + ### Desempenho + - Melhorar o tempo de inicialização a frio em ~29% através do carregamento preguiçoso do SDK MCP e tipos de eventos + + ## Contributors + + @alex-clawd, @github-advanced-security[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @mattatcha + + + ## v1.14.3a2