* fix(cli): reject non-serializable literal_eval results in TUI JSON formatting
_try_parse_structured() accepted any dict/list coming out of
ast.literal_eval(), including values json.dumps() cannot encode such as
[Ellipsis] from a literal [...]. _format_json_in_text() then raised
TypeError: Object of type ellipsis is not JSON serializable, which
propagated through _tick and cancelled the whole crew run.
Validate the parsed object with json.dumps() inside
_try_parse_structured() so only JSON-serializable dict/list values are
returned; anything else falls back to the original text. Fixes#7434.
* fix(cli): contain RecursionError in the TUI JSON formatting boundary
A streamed structure nested deeper than the JSON backend can walk could
raise RecursionError out of _try_parse_structured (from json.loads) or
out of the render-path dumps, escaping _tick and losing the TUI update.
Catch RecursionError when loading, and validate literal_eval results
with the exact kwargs the render path uses, so any structure the render
cannot encode is rejected at the boundary and the raw text renders
instead.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
mypy does not narrow on `platform.system()`, so Windows-based contributors
get 8 spurious attr-defined/unused-ignore errors from the termios and
resource imports. Switch to `sys.platform` comparisons, which mypy
understands natively, and drop the now-unneeded type-ignore comments.
Fixes#7400
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(agents): request the forced final answer as a user turn
When an agent reaches max_iter, handle_max_iterations_exceeded appended
the "give your best final answer" instruction as an assistant message and
relied on assistant prefill to make the model continue it. Current Claude
models (Opus 5, Sonnet 5, Fable 5.x, the 4.6+ family) reject a request
that ends on an assistant turn with a 400, after the whole iteration
budget has already been spent.
The instruction is now appended as a user turn, which every provider
accepts. The handler's formatted_answer parameter is dropped: every
caller had already appended that text as the last assistant message, so
prefixing it again only duplicated history.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(agents): stop the lite agent loop after the forced final answer
LiteAgent._invoke_loop fell through after handle_max_iterations_exceeded
and issued a regular LLM call on the same history, discarding the forced
answer. Break out of the loop the way CrewAgentExecutor already does, and
assert a single LLM call in the test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
- create_json_crew, the default `crewai create crew` path, now copies
AGENTS.md, CLAUDE.md and GEMINI.md; AGENTS.md documents the JSON layout
- span helper no longer depends on OTEL_SDK_DISABLED being popped by an
earlier test; thread-scope test stops its worker before leaving the mock
- single import style in the shutdown test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- State the observability rule as the user's decision, never a fix for
console warnings, speed, or a "clean" configuration
- Rewrite the AMP section as built-in capabilities: no "free",
"proactively", "sales pitch", or scripted pitches
- Turn the research mandate into a list of sources to consult when
version details matter
- Retarget the scaffold tests to the new wording
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Updated project creation to include `CLAUDE.md` and `GEMINI.md` that import `AGENTS.md`, ensuring consistent guidance across coding assistants.
- Implemented utility functions to copy assistant instruction files during project setup.
- Enhanced documentation in `AGENTS.md` to emphasize the importance of keeping telemetry enabled for optimal performance.
- Added tests to verify the correct scaffolding of assistant instruction files and their contents.
* fix(agents): keep null in the task output schema embedded in the prompt
build_task_prompt_with_schema embeds the task output schema into the prompt
via generate_model_description, whose strip_null_types defaults to True.
Combined with ensure_all_properties_required, an Optional[str] = None field
reaches the model as a required, non-nullable string, contradicting the
provider-side response schema generated from the same model.
That sanitizer targets OpenAI strict function-calling schemas. This call site
produces prompt prose, where those constraints do not apply.
Pass strip_null_types=False, matching the existing call for tool schemas in
utilities/agent_utils.py.
Fixes#6774
* test(agents): cover the output_json branch of the prompt schema
build_task_prompt_with_schema embeds a schema on both the output_json and
the output_pydantic branch, and this PR changes both. The regression test
only built a Task with output_pydantic, so the output_json branch shipped
unpinned.
Parameterize over both output attributes. Checked on this branch with the
fix reverted: both cases fail on the missing anyOf, and both pass with it.
Also compare the anyOf members as a set, so member order is not part of the
test contract.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* feat(embeddings): add openrouter provider type definitions
* feat(embeddings): implement OpenRouterProvider
* feat(embeddings): export openrouter provider package symbols
* feat(embeddings): register openrouter in allowed embedding providers
* feat(embeddings): register openrouter provider and overloads in factory
* feat(tools): add openrouter embedding service support
* test(embeddings): add comprehensive openrouter provider and factory tests
* test(embeddings): add openrouter build test in embedding factory
* test(embeddings): add openrouter model key alias and env tests
* test(tools): add openrouter tests for embedding service
* docs: add openrouter embedder configuration example
* docs(ar): sync openrouter embedder translation
* docs(ko): sync openrouter embedder translation
* docs(pt-BR): sync openrouter embedder translation
* feat(embeddings): allow model alias and None fields in OpenRouterProviderConfig
* fix(tools): resolve EMBEDDINGS_OPENROUTER_API_KEY before OPENROUTER_API_KEY
* test(tools): add regression tests for openrouter env var precedence and fallback
* feat(embeddings): drop organization_id and resolve api_key via OPENROUTER_API_KEY only
* fix(tools): use OPENROUTER_API_KEY in embedding service and update tests
* docs: switch openrouter knowledge example to model_name and document OPENROUTER_API_KEY
* fix(tools): default openrouter model to namespaced openai/text-embedding-3-small
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(openai): surface gateway errors reported inside an HTTP 200
OpenAI-compatible gateways commit `200 OK` as soon as the upstream provider
accepts a request, so a later provider failure arrives in the body as an
`error` object with no `choices`. That reached the SDK's parse helper and
surfaced as `TypeError: 'NoneType' object is not iterable`, naming neither the
provider, the status, nor the fact that a timeout happened.
The four non-streaming paths now inspect the raw body before parsing and raise
the exception the upstream code maps to, so a masked 504 is catchable exactly
like an honest one. Streaming already had this guard inside the SDK.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(openai): teach the tool-cache fake about with_raw_response
The provider now reads the raw body before parsing, so a client double that
only implements `create` no longer satisfies it. Same shape as the fixes to the
reasoning-effort retry and Snowflake doubles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(tracing): reset the TraceCollectionListener singleton between tests
TraceCollectionListener caches a TraceBatchManager on the class and
`_initialized` short-circuits `__init__`, so batch state survives for the whole
xdist worker. `test_nested_agent_executor_flow_does_not_finalize_parent_batch`
left `trace_batch_id="debug-trace-batch"` behind, which moved every later trace
POST from /tracing/ephemeral/batches to /tracing/batches/<id>/events. The
recorded cassette then stopped matching, the agent retried, and the second call
found the cassette consumed -- surfacing as ConnectionError in an unrelated
test hundreds of tests later.
Reproduced deterministically by running the leaking test followed by
tests/tracing/test_trace_enable_disable.py::test_trace_calls_when_enabled_via_env;
fails on a68b5e903 too, so this predates the gateway fix it was blocking.
An autouse fixture now clears the cached instance after each test. Two canaries
pin the invariant and fail without the fixture.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(tracing): drop the unwritable _listeners_setup canary
Both review bots flagged that the canary read `_listeners_setup` off the class,
where it is always False, so it could never fail. Correct, and the suggested fix
does not work either: `BaseEventListener.__init__` calls `setup_listeners`
(base_event_listener.py:16), which sets the flag on the instance
(trace_listener.py:229), so reading it back through `TraceCollectionListener()`
is always True. Neither read observes a leak, so the canary is deleted rather
than replaced, with the reasoning recorded so it is not re-added.
The same finding showed the fixture was resetting two class attributes that are
never assigned at class level. Only dropping `_instance` is load-bearing, so the
fixture is now one line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(tracing): correct why the _listeners_setup canary is unwritable
setup_listeners returns early when tracing is off and no override applies
(trace_listener.py:213-220), assigning the flag at :229 only when it actually
registers. Construction therefore does not always set it, as the previous note
claimed: with tracing disabled the flag never even reaches the instance dict.
The instance read reports ambient tracing state rather than isolation, which is
a better reason not to assert on it than the one recorded before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tests): clear trace batch state in place instead of dropping the singleton
Dropping `TraceCollectionListener._instance` made the next construction re-run
`setup_listeners`, re-registering its handlers on the event bus. That broke
tests/telemetry/test_task_failure_instrumentation.py, which requires exactly one
handler per event: the re-registered `on_task_failed` made two. Verified against
a53ecc17f, where the same sequence passes -- the regression was mine.
The leak that needed fixing was batch state, not registration, so the fixture
now clears the manager's batch fields in place. Handler cleanup already belongs
to `cleanup_event_handlers`, and `first_time_handler` keeps its reference to the
same manager object.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tests): clear tracing context vars so the listener can re-register
Bugbot flagged that keeping the singleton leaves `_listeners_setup` set, so after
`cleanup_event_handlers` wipes the bus `setup_listeners` returns early
(trace_listener.py:208) and tracing silently registers nothing for the rest of
the worker. Confirmed: after a tracing-enabled run, re-running setup restores 0
of 119 handler entries.
Dropping the singleton fixes that but previously broke
test_task_failure_instrumentation. The real cause was a third leak: the
`_tracing_enabled` context var stayed set, so the replacement listener still
believed tracing was on and re-registered `on_task_failed` next to telemetry's.
Clearing the context vars is what makes replacing the listener safe, so the
fixture now does both, and a canary pins it (fails with `assert True is False`
without the drop).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Push was choosing ZIP vs git from a local origin remote, so adding origin later rebuilt the last ZIP with no files. Prefer AMP zip_deployment from status, and fall back to the old origin heuristic when that field is missing.
`oxylabs` was pinned to exactly 2.0.0, so consumers could not take 3.0.0, out
since March. 3.x keeps the `RealtimeClient` surface these tools use, and all
four tools plus their failure paths were verified against the live API on both
2.0.0 and 3.0.0.
The lockfile keeps oxylabs at 2.0.0, so this permits the upgrade rather than
forcing it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(oxylabs): report scrape failures instead of raising IndexError
The oxylabs SDK logs HTTP errors and returns an empty response rather than
raising, so the unchecked `response.results[0]` in every Oxylabs tool turned a
rejected request into `IndexError: list index out of range`. Invalid credentials
-- the most likely first-run mistake -- gave no indication of the cause. A
result carrying a non-2xx `status_code` had the same problem one level down: the
job ran, the page did not come back, and the tool returned its empty content as
though the scrape had succeeded, handing the agent "[]".
Both are now reported as a `ToolFailure` naming what went wrong, so the agent
gets something it can act on and the framework records the call as failed:
401 Unauthorized
400 Bad Request - Parameter `parsing_instructions` can be used just with
`parse` parameter set to `true`.
Because the SDK keeps the cause only in its own log, the failing call is run
with a handler attached to the `oxylabs` logger and the status, the API's
explanation and timeouts are read back off it. `code` and `retryable` are set
from the status, so 429 and 5xx are marked worth retrying. Nothing about the
caller's logging configuration is changed; an application that has silenced the
SDK still gets the generic failure.
Content that is neither a string nor a dict is also serialized properly:
`parsing_instructions` commonly yields a list, and the previous `str()`
fallback produced a Python repr with single quotes instead of JSON.
The client construction and response handling these four tools duplicated
verbatim now live in a shared `OxylabsBaseTool`, following the existing
`SerpApiBaseTool` pattern, so the handling above exists in one place. The
generated tool specs change only by the new `locale` field, confirming the
tools' public surface is otherwise untouched.
Also add the `locale` option to the Google Search config, which the docs
already documented but the config model silently dropped, and correct two
copy-paste errors in the docs across all four locales.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(oxylabs): keep concurrent scrape diagnoses apart
The error capture attached a fresh handler to the shared `oxylabs` logger for
each scrape, so two scrapes in flight at once each saw both errors. `_diagnose`
reads the first HTTP status it finds, so a timeout could be reported as the
other request's 400 -- `retryable=False` on a failure that was worth retrying.
One handler now serves every scrape and routes each record to the capture of
the call that caused it via a `ContextVar`, which isolates threads and asyncio
tasks alike. Serializing the captures would have fixed the cross-talk too, but
at the cost of running every scrape one at a time. The handler stays attached
once installed: it is inert outside a capture, and detaching it would race with
concurrent scrapes.
The regression test forces the interleaving -- one capture is held open while
the other call logs -- and fails against the previous implementation.
Also drive `config` through the public constructor in the tests instead of
assigning `__dict__["config"]`, so they would catch `__init__` dropping a
supplied config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
get_search_url interpolated ${query} inside an f-string, producing
URLs like https://www.bing.com/search?q=$test. The URL is passed
straight to the SERP request, so every search carried the malformed
query string.
Fixes#7325
The Args block for ConsoleFormatter.handle_llm_stream_chunk listed
'chunk' and 'crew_tree' parameters that no longer exist on the method.
The signature was refactored to (accumulated_text, call_type) but the
docstring was not updated. Callers in event_listener.py pass only the
two real params.
Refs #7312.
* docs(streaming): fix streaming output docstring examples
CrewStreamingOutput's example called crew.kickoff() without setting
stream=True on the Crew, so the snippet returned a CrewOutput and did
not stream anything.
FlowStreamingOutput's example called flow.kickoff_streaming() and
flow.kickoff_streaming_async(); neither method exists. Flow-level
streaming is exposed through Flow.kickoff with stream=True and
returns a StreamSession, not a FlowStreamingOutput.
Refs #7285
* docs(streaming): clarify Flow.kickoff does not take stream param
Flow.kickoff() has no stream parameter; the runtime returns a
StreamSession when self.stream is True. Reword the FlowStreamingOutput
note so callers know to configure the Flow with stream=True before
calling kickoff().
Addresses CodeRabbit review on #7286.
* docs(streaming): restore FlowStreamingOutput example
Add back an Example block showing valid usage of FlowStreamingOutput.
The class is only ever constructed directly with a chunk-producing
iterator (see lib/crewai/tests/test_streaming.py), so the example
mirrors that pattern instead of the original snippet that referenced
non-existent Flow.kickoff_streaming methods.
Addresses review feedback on #7286.
* docs(streaming): swap FlowStreamingOutput example for public Flow streaming path
Replace the test-only FlowStreamingOutput(sync_iterator=...) example
with the actual public flow-streaming path: Flow.stream=True followed
by kickoff() / kickoff_async(), which return StreamSession /
AsyncStreamSession. The example is labeled explicitly to make clear
that Flow.kickoff() does not return a FlowStreamingOutput, and points
readers at the streaming-flow-execution guide.
Addresses review feedback on #7286.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(schema): support list-form "type" arrays in JSON schema conversion
_json_schema_to_pydantic_type already handles anyOf/oneOf for nullable
unions -- the form Pydantic's own schema generation produces for
Optional[T] fields -- but had no handling for the other, equally valid
JSON Schema way of expressing the same thing: a list-form type array,
e.g. {"type": ["string", "null"]}. This is what .NET/System.Text.Json
-based schema generators produce instead, so any MCP tool schema from
a non-Python server using this form crashed create_model_from_schema
outright with "Unsupported JSON schema type: ['string', 'null']" --
taking down the entire MCPServerAdapter connection, not just the one
affected tool.
Confirmed against a real self-hosted MCP server (Equibles,
github.com/daniel3303/Equibles): several of its tools (e.g.
ListCompanyDocuments's startDate/endDate filters) use exactly this
pattern, and MCPServerAdapter couldn't connect to it at all as a
result -- reproduced identically on both Windows and macOS.
Fix mirrors the existing anyOf/oneOf handling: treat each entry in a
list-form type the same way an anyOf member is handled, building a
Union of the corresponding Python types. A single-element list
collapses to that one type via typing.Union's own behavior, and
"null" entries resolve to None (matching how the type == "null"
branch already behaves), producing the same Optional[T] shape as the
anyOf case would.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(schema): preserve union members when applying FORMAT_TYPE_MAP
CodeRabbit flagged this reviewing #7058: the format override in
_json_schema_to_pydantic_field replaced the whole resolved type with
FORMAT_TYPE_MAP[format_], even when that type was a Union built from a
list-form `type` (or anyOf/oneOf) rather than a plain `str`. For a
schema like {"type": ["string", "null"], "format": "date-time"}, this
collapsed Union[str, None] down to plain datetime, silently dropping
the null option -- masked for non-required fields by the
Optional-rewrap at the end of the same function, but not for a
required-but-nullable field (a valid, if unusual, JSON Schema shape).
The same override also drops any non-string members of a multi-type
array (e.g. ["string", "integer", "null"]) regardless of required
status, since nothing rewraps those.
Narrow the override to the `str` member specifically: replace `type_`
outright when it's already plain `str`, or substitute only the `str`
element inside a Union via get_origin/get_args, leaving null and
other type-array members untouched.
Added two tests covering the previously-broken cases: a required
nullable formatted field, and a multi-type array (string/integer/null)
with a format.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
gpt-4o-mini's official context window is 128,000 tokens (OpenAI
announcement and API docs; litellm's model database agrees), but the
shared LLM_CONTEXT_WINDOW_SIZES table and the OpenAI/Azure provider-local
tables listed 200000 - apparently copied from the neighboring o3-mini /
o4-mini entries. With CONTEXT_WINDOW_USAGE_RATIO = 0.85, crews resolved
the usable window to 170000 instead of 108800, letting history grow past
the model's real 128k limit and failing with API 400s on long runs.
Fixes#7293
Organization names are not unique, so the documented `@org/name` form can
resolve to the wrong organization and fail to find the skill. Document the
`@org-uuid/name` form instead, and add a note pointing at `crewai org list`
for the UUID.
Applies to the agent-side registry refs too: they resolve through the same
`/skills/:org/:name` endpoint and the same `~/.crewai/skills/{org}/{name}/`
cache path, so leaving them as `@acme` would contradict the install command.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(bedrock): preserve streaming tool call arguments at contentBlockStop
Streaming Converse handlers accumulate tool input as JSON string deltas in
accumulated_tool_input but never fold it back into current_tool_use["input"],
so function_args reads an empty {} at contentBlockStop. Parse the accumulated
input into the tool-use block (with a {} fallback) in both the sync and async
streaming handlers. This is the streaming counterpart of the non-streaming fix
in #5415 (issue #4972).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(bedrock): coerce non-dict streaming tool input to empty dict
json.loads on the accumulated tool input can return a valid-but-non-object
JSON value (e.g. a string or list), which would fail at fn(**function_args)
with a TypeError. Enforce a dict shape before use in both the sync and async
streaming handlers, and add a regression test for the non-dict case.
Addresses CodeRabbit review feedback on #6150.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
LegacyClient filtered the server response with exact app and action
names. The platform returns canonical app names and provider action
names, so valid aliases produced no tools.
* fix(tools): read octet-stream URLs by sniffing the body
URLReadTool resolved content type from the Content-Type header and then
the URL path extension. Presigned object-store links carry neither: they
pin every object to application/octet-stream and use a content hash for a
path, so a SharePoint download landing in R2 was refused outright.
Sniff the already-fetched body as a third source, consulted only after the
header and both URL extensions come back with nothing. The sniff can turn
a refusal into a read but never a read into a different read, so no URL
that works today changes behavior.
Fails closed: a zip is DOCX only when word/document.xml is in its central
directory, so an .xlsx keeps its honest refusal instead of surfacing a
misleading "failed to read DOCX"; text requires a strict, whole-body UTF-8
decode with no NUL byte; an empty body identifies nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(tools): extract text from XLSX URLs
The reported presigned SharePoint link is a spreadsheet, so sniffing the
body identified it as OOXML but still had nowhere to send it: URLReadTool
had no XLSX extractor, and the file would have been refused even with a
correct spreadsheetml Content-Type.
Read workbooks with openpyxl, already a core crewai dependency, so this
adds no new one. Sheets are emitted as CSV under a "Sheet <name>:" heading,
mirroring the PDF extractor's per-page shape. read_only streams the sheets
instead of building the whole object graph and data_only takes cached
values, both of which matter for a workbook arriving from an untrusted URL.
Cells are written through csv rather than joined, so a comma, quote or
newline inside a cell cannot corrupt the grid, and trailing phantom rows
are trimmed because Excel reports sheet dimensions generously.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): bound xlsx expansion and refuse ambiguous ooxml packages
Bot review found two real defects in the XLSX extractor, both reproduced.
openpyxl pads every row up to a sheet's declared dimension, so a single
stray cell far down the sheet turned a 4.8 KB upload into 100,000 rows and
200,000 cells. Trimming only trailing blanks did not help, because the
stray cell sits at the end and keeps the last row non-empty. Blank rows are
now skipped as they stream, and a cell budget caps what any one workbook
can hand an agent -- announced in the output rather than silently applied.
A zip carrying both word/document.xml and xl/workbook.xml was classified as
DOCX. Two identities is not a positive identification, so it is refused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): keep whitespace-only xlsx cell values
Bot review, verified: openpyxl's row padding arrives as None, so testing
cells for exactly-empty drops it just as well as .strip() did while leaving
a row whose cells the author really did fill with spaces. And rstrip() on
the rendered grid removed a trailing space from the final cell along with
the line terminator; only the terminator should go.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): bound xlsx scan work, not just emitted cells
The cell budget only counted cells that reached the output, and blank rows
skip before that point. A sheet can declare Excel's maximum dimension while
holding two real cells; openpyxl then pads every row out to 16,384 columns
and yields one row per gap. Measured: a 4,848-byte workbook drove 1.64
billion cell normalizations in 15.2 seconds with the budget never touched.
Charge a separate scan budget per row, before the row is normalized and
before the blank check, so the work a hostile sheet can demand is bounded
whether or not any of it is emitted. The regression test asserts the read
completes in under 5 seconds and is mutation-verified: dropping the per-row
charge takes it back to 26 seconds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(deps): clear the six pip-audit advisories
gitpython 3.1.58 has PYSEC-2026-3785 through -3788, fixed in 3.1.59; the
lock now takes 3.1.61. Its exclude-newer-package cutoff is dropped rather
than bumped -- the global 3-day cutoff has long since passed 2026-08-05, so
that per-package pin was only holding the fix back.
snowflake-sqlalchemy 1.10.0 has GHSA-8g6f-qw9x-4q6q (SQL injection and
local file disclosure), fixed in 1.11.0.
unstructured 0.18.32 has GHSA-4mvj-m6j5-pmf7, a full-read SSRF via the url=
argument of partition(). The patched 0.24.0 requires Python >=3.11 while
crewai-tools supports 3.10, so the floor carries a marker and 3.10 stays on
the old line. 0.24+ also requires beautifulsoup4>=4.14.3, so the bs4 pin
widens from ~=4.13.4 to >=4.13.4,<5 -- a widening, so no existing install
breaks. uv resolves bs4 4.13.5 on 3.10 and 4.15.0 on 3.11+.
pip-audit locally: "No known vulnerabilities found, 5 ignored", with no new
--ignore-vuln entries. Only crewai-tools[xml] grows, gaining spacy and
openai-whisper transitively through unstructured's extras.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): narrow bs4 find_all results without a cast
Widening the beautifulsoup4 pin let uv resolve 4.15.0 on Python 3.11+ while
3.10 stays on 4.13.5, because the old unstructured line holds it back there.
4.15 types find_all precisely, so cast(Tag, link) became redundant and mypy
failed the 3.11-3.13 type-checker jobs while 3.10 passed.
isinstance narrowing is correct under both versions and is what AGENTS.md
asks for anyway. Verified by running mypy against 4.15.0 and again against
4.13.5: browser_toolkit is clean under both, leaving only the pre-existing
errors in crewai/rag/embeddings/providers/ibm.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(deps): declare security floors in crewai-tools, not only as overrides
Bot review caught a regression I introduced. override-dependencies replace
the whole requirement including its marker, so gating the unstructured
override on python_version >= '3.11' dropped the dependency outright on
3.10: the lock held only 0.24.1, never the 0.18 line the comment claimed.
crewai-tools[xml] would have installed no unstructured at all there.
Move the floors into lib/crewai-tools/pyproject.toml, where a marker split
means what it says -- >=0.24.0 on 3.11+, >=0.17.2 below -- and drop the
root override for unstructured entirely. The lock now carries both 0.18.32
and 0.24.1 under complementary markers.
Same reasoning applies to the other two, per the nltk precedent already in
that file: a uv override only shapes this workspace's lock, so consumers
installing crewai-tools[snowflake] or [github] were still getting the
vulnerable floors. Declared there now as well.
Also documents the tool as a fit for presigned and share links from S3, R2,
Google Drive, OneDrive and SharePoint -- the case this PR fixes -- while
saying plainly that it reads a URL and does not authenticate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update tool specifications
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: append trailing user turn in native Gemini provider
GeminiCompletion._format_messages_for_gemini maps assistant messages
to Gemini's 'model' role but never guards against the resulting
contents list ending on a model turn. CrewAI's own agent loop (max
iterations, guardrail retries) can produce exactly that history, and
Gemini's generateContent API rejects it with 400 'Requests ending
with a model turn are not supported'.
Mirrors the existing Mistral/Ollama guard in
LLM._format_messages_for_provider, which never applies to Gemini
since gemini/google model strings resolve to this native provider
instead of the LiteLLM fallback path.
Fixes#6972
* fix: append trailing user turn for Gemini on the LiteLLM fallback path
LLM._format_messages_for_provider already guards Mistral/Ollama
against a trailing assistant turn, but Gemini models routed through
the LiteLLM fallback (no google-genai installed, or a model name not
recognized as native) had no equivalent guard. litellm's own
Vertex/Gemini transformation doesn't handle this either, so the
request reaches Gemini's generateContent API unguarded and 400s.
Complements the native-provider fix in GeminiCompletion, covering
both dispatch paths.
* fix: don't append text turn after unresolved Gemini function call
Address CodeRabbit review on #6973: appending a plain 'Please
continue.' user turn after a trailing model turn that contains an
unresolved function_call violates Gemini's function-calling protocol
-- it requires a matching functionResponse, not free text. Raise a
targeted error instead so the caller notices rather than silently
sending a malformed follow-up.
Also strengthens the native-provider formatting tests to assert exact
role sequence and text content (not just the last role), per review,
and adds a regression test for the unresolved-function-call case.
* fix: guard None parts when checking Gemini history for unresolved function call
contents[-1].parts is typed list[Part] | None; iterating it directly
failed mypy (union-attr) on 3.10-3.13. Narrow to [] before the any()
check and document the ValueError in the docstring.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(llms): normalize scheme and port in Ollama base URL
OLLAMA_HOST follows Ollama's own convention and may be a bare host
("0.0.0.0") or a host:port pair ("127.0.0.1:11434") rather than a full
URL. _normalize_ollama_base_url only appended "/v1", so those values
produced invalid base URLs such as "0.0.0.0/v1", and every request
failed with the misleading error "Failed to connect to OpenAI API:
Connection error." - confusing, since no OpenAI model was requested.
Fill in the missing parts the way Ollama's own client does: prepend
http:// when no scheme is present, append the default port 11434 when
none is present and the scheme is http (https implies 443), then append
the /v1 suffix the OpenAI-compatible endpoint requires.
Six of nine realistic OLLAMA_HOST forms were affected, including
127.0.0.1:11434, which is Ollama's documented default.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(llms): strip only the parsed path when normalizing Ollama base URL
Stripping trailing slashes from the whole URL before parsing corrupted
inputs that carry a query or fragment. "http://ollama/?tenant=acme" kept
a "/" path and produced a doubled "//v1", and a query or fragment ending
in "/" silently lost that character.
Parse first, then rstrip only parts.path. Adds regression tests for a
root path alongside a query and for a query value ending in "/".
Reported by CodeRabbit on #7206.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* chore(ci): ignore unpatched nltk GHSA-8mgp-746c-j5xp
No patched PyPI release exists beyond 3.10.3. nltk is transitive via
crewai-tools[xml] -> unstructured; CrewAI does not call the vulnerable
model-artifact APIs.
Co-authored-by: Vidit Ostwal <Vidit-Ostwal@users.noreply.github.com>
* chore(ci): note dropping nltk GHSA ignore on the next bump
Leave an explicit TODO beside the ignore so GHSA-8mgp-746c-j5xp is
removed when nltk moves past the unpatched 3.10.3 floor.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Vidit Ostwal <Vidit-Ostwal@users.noreply.github.com>
* fix: bump pypdf to 6.16.2 for GHSA-jp53-mhqp-8xcg
pypdf 6.15.0 fails pip-audit on three moderate DoS advisories; 6.16.1+ patches them.
* chore: keep existing uv.lock environment markers
A full uv lock refresh rewrote unrelated dependency markers; restore them so the pypdf bump stays isolated.
* chore: drop unused pypdf exclude-newer-package in crewai-files
~=6.16.1 plus the global 3-day cutoff already admits 6.16.2.
* chore: drop pypdf from exclude-newer-package
6.16.2 is already older than the global 3-day cutoff; the version floor is enough.
* Add Clipper integrations client
Implement the internal Clipper discovery and execution contract with
deployment authentication and normalized results. Keep
CrewAIPlatformTools on LegacyClient until the new path is ready for
selection.
* Allow Clipper requests without deployment instances
Local and self-hosted CrewAI executions can have a valid integration
token without a deployment instance UUID. Send the deployment header
when it is available and allow Clipper to attribute other executions to
the organization.
* fixup! Allow Clipper requests without deployment instances
* fixup! Add Clipper integrations client
* fix(telemetry): accept 1/yes/on on disable flags
CREWAI_DISABLE_TELEMETRY=1 was ignored because the gate only matched true, so telemetry stayed on with no warning.
* fix(telemetry): warn once on unrecognized disable values
Stop repeating the same invalid-flag warning on every telemetry check, and drop the undocumented CREWAI_DISABLE_TRACKING alias from docs.
---------
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* fix(flow): resolve @human_feedback emit LLM from the project model
Omitting llm= no longer hardcodes OpenAI. Collapse and learn resolve through create_llm so MODEL / MODEL_NAME / OPENAI_MODEL_NAME win, then DEFAULT_LLM_MODEL.
* fix(flow): fail closed when human-feedback collapse cannot classify
Stop routing to emit[0] when the collapse LLM cannot be called or its response does not match an outcome. Empty skip still uses default_outcome.
* refactor(flow): extract human-feedback collapse matching helpers
Move match/require outcome helpers out of _collapse_to_outcome so the classify path stays flat.
* refactor(flow): catch only LLM call failures in collapse
Keep HumanFeedbackCollapseError from matching outside the call try so it is raised once and does not trigger a second prompt.
* fix(flow): treat non-object JSON as raw collapse text
Avoid AttributeError when the collapse LLM returns JSON that is not an object.
* feat(flows): add now() to the CEL expression environment
CEL expressions in flow definitions had no way to produce the current
date: the environment was built bare, so date-dependent flows failed at
runtime. Register a now() function that returns the current UTC time as
a CEL timestamp. The value is frozen once per kickoff so every
expression in a run sees the same instant, even across midnight.
Standard CEL covers formatting from there: string(now()),
now().getFullYear(), now() - duration('24h').
* chore(flows): drop redundant comment on _cel_now
* refactor(flows): derive CEL env and functions from one registry
A function now lives in one _CelFunctionSpec entry: its annotation for
compile and its implementation factory for evaluate, so the two cannot
drift. Run-scoped values move into _CelRunContext; adding one is a
field, not a new parameter through every helper signature.
* chore(flows): drop _CelRunContext docstring
* fix(flows): freeze a fresh cel now() on human-feedback resume
resume_async never passes through kickoff_async, so a flow restored
with from_pending() had no frozen instant and now() fell back to live
wall-clock per expression. Freeze a fresh instant at resume instead of
persisting the kickoff one: a flow can pause on feedback for days, and
expressions after resume must see today.
* Decouple platform tools from the integrations API
Define normalized selector and tool data so platform tool creation does
not depend on the legacy API response shape. This contract makes the
legacy client easier to replace later.
- Move action discovery and response normalization into LegacyClient.
- Pass ToolInfo from discovery through tool creation and execution.
- Replace the builder flow with direct factory orchestration.
- Preserve app, action, and connection data in immutable models.
- Build sanitized tool names from the full tool identity.
- Preserve legacy request, SSL, and failure behavior with contract tests.
* fixup! Decouple platform tools from the integrations API
* fixup! Decouple platform tools from the integrations API
* fixup! Decouple platform tools from the integrations API
* fixup! Decouple platform tools from the integrations API
* fix(llms): let current claude models use native structured outputs
NATIVE_STRUCTURED_OUTPUT_MODELS only listed 4.5-era prefixes, so Opus 5,
Sonnet 5, Fable 5 and Opus 4.8 fell through to the forced-tool-call
fallback. That path also overwrites params["tools"], so a call combining
tools with a response_model silently lost the caller's tools.
_infer_provider_from_model documented a pattern-matching fallback it never
performed, so a Claude release newer than the constants list resolved to
"openai". Bedrock ('.' in model) and Azure (every OpenAI prefix) are left
out of that fallback because they would capture gpt-* models.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(llms): route bedrock-namespaced anthropic ids to bedrock
"anthropic.claude-*" is Bedrock's namespace, not the Anthropic API, and it
satisfies the anthropic prefix pattern. Settle it before the pattern loop so
an unlisted Bedrock id picks BedrockCompletion. The region-prefixed form
("us.anthropic.claude-*") was resolving to openai, so this repairs that too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(deps): raise snowflake-connector-python floor for CVE-2026-15925
GHSA-5cc2-282f-jjq2 (CRITICAL): the connector does not verify TLS hostnames,
so a network attacker can impersonate the Snowflake endpoint. Fixed in 4.7.1.
crewai-tools[snowflake] declares "snowflake-connector-python>=3.12.4", which
the lock had resolved to 4.6.0. Following the existing convention, the security
floor goes in [tool.uv] override-dependencies rather than the source
declaration, matching how cryptography is handled.
Relocking also refreshes numpy/humanfriendly/nvidia environment markers, which
re-resolution under the relative exclude-newer window produces regardless of
this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add injectable client for CrewAI platform tools
Define an integrations client contract for action discovery and
execution. Keep the existing platform API as the default client to
preserve current behavior.
Allow callers to provide a custom client through CrewaiPlatformTools.
* Fix platform action tool failure tests
* Remove redundant protocol placeholders
* ci: require an open issue for first-time contributor PRs
Gate anyone who is not a returning contributor, and allow the PR only when a closing keyword points at an open issue in this repo.
* ci: accept any open issue mention for first-timer PRs
Drop the closing-keyword regex so #123, owner/repo#N, or an issue URL is enough when that issue is open.
* ci: ignore foreign owner/repo#N in first-timer issue gate
Bare #123 no longer matches the suffix of other/repo#123, so an open local issue cannot keep that PR open.
* docs: update channels guide to current copilotkit channels api
* docs: translate channels and frontend overview guides to ar, ko, pt-BR
---------
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
Stores application, action, and connection values in an internal
selector. Validates the selector with clearer error messages. Keeps
existing application syntax and legacy API requests unchanged.
* fix: let a hook deny reach the caller as a deny
A hook that raised `HookAborted` on `pre_model_call` never reached the code
making the call: the LLM layer caught it and returned `False`, which providers
translated into `ValueError("LLM call blocked by before_llm_call hook")`,
dropping the reason and the source and making a policy decision
indistinguishable from a provider outage. Every internal model call then
absorbed that error through the `except Exception` that keeps a provider hiccup
from failing a run, so memory analysis fell back to defaults and the converter
and reasoning handler retried the call that was just denied. The abort now
propagates out of the LLM layer while the boolean convention keeps its
documented `ValueError` via `LegacyHookBlocked`, and the fail-open handlers
around internal model calls re-raise it instead of degrading.
* fix: dispatch model call hooks on the paths that skipped them
A model call was only checked when the executor loop drove it: the
`from_agent is not None` short-circuit in `base_llm` silenced the hooks
for agent planning and step observation, no provider `acall` dispatched
them at all, and `InternalInstructor` bypassed `llm.call` entirely. This
replaces that short-circuit with an explicit
`model_call_hooks_already_dispatched` window so the enclosing caller
claims the dispatch, adds the pre-call dispatch to every provider's
`acall`, and runs the hooks around the Instructor client call. A denial
now emits a denied event instead of being logged and reported as a
provider failure.
* fix: report a boolean-convention deny as a deny, not an outage
A `before_llm_call` hook that blocks by returning `False` reached the five
native providers as a plain `ValueError`, which fell through to their generic
`except Exception` and was logged and emitted as `OpenAI API call failed: ...`
— the same deny raised as `HookAborted` was already labelled correctly, so the
two dialects disagreed on whether a policy decision was a provider outage. The
LLM layer now converts it into `LLMCallBlockedError`, still a `ValueError` so
the fail-open handlers around internal model calls keep absorbing it, but its
own type so a provider can report the decision it is. Since a block is raised
rather than returned, the thirteen callers that turned the return flag into a
raise by hand drop that line, and `_prepare_llm_call` raises the same type.
* fix: keep a denied plan from letting the agent run unplanned
`AgentExecutor.generate_plan` wraps `handle_agent_reasoning()` in a bare
`except Exception`, so guarding the reasoning handler alone still left the
deny absorbed one frame up: the executor logged "Error during planning" and
the agent proceeded with no plan. It now re-raises `HookAborted` like the
other planning boundaries, and the accompanying test also covers the
boolean convention still degrading at a fail-open site.
* fix: stop a denied knowledge query from running the task without knowledge
`handle_knowledge_retrieval` and its async twin wrap the query rewrite in
their own `except Exception`, so guarding `_get_knowledge_search_query`
alone still let `execute_task` continue on the unaugmented prompt after a
deny. Both now emit the terminal `KnowledgeSearchQueryFailedEvent` and
re-raise `HookAborted`, matching the second-frame guard already added to
`AgentExecutor.generate_plan`. Also documents the abort contract on
`PlannerObserver.observe`.
* fix: stop nine callers from re-swallowing a model call deny
CodeRabbit caught the replan path re-swallowing a deny, so an AST sweep of
every caller of a guarded function found the same defeat in nine places:
classic and replan planning, memory recall and memory save on both `Agent`
and `LiteAgent`, the base executor's save, and `LLMGuardrail.__call__`,
which turned a refused call into validation feedback. Each now re-raises
`HookAborted` after emitting whatever terminal event it owes, while every
other failure keeps degrading as before — the knowledge guards move to that
same idiom instead of duplicating their emit.
* fix: pair a denied guardrail with the event it started
Re-raising from `LLMGuardrail` left `process_guardrail` between its started
and completed events, so a denied validation read as one still in flight
rather than a policy decision. It now emits `LLMGuardrailCompletedEvent`
with the deny reason before the abort leaves, matching what every other
guarded site in this change already does.
* fix: stop retrying a task after a hook denied its model call
`Agent.execute_task` funnels every exception into `_handle_execution_error`,
which re-runs the whole task up to `max_retry_limit` times, so a policy deny
read as a transient blip: a crew whose first model call was denied retried and
returned a normal answer. `HookAborted` now joins `_passthrough_exceptions`,
the tuple already reserved for deliberate stops. The new boundary tests drive
the public entry points instead of the frame that makes the call, and count
model calls so a deny that gets retried fails the assertion — ten of the twelve
fail against `main`.
* fix: stop a denied plan step from being reported as a failed step
Making model call hooks reachable on agent-bearing calls put a deny inside
`StepExecutor.execute`, whose broad `except Exception` turned it into
`StepResult(success=False)` and let the plan carry on; `HookAborted` now
joins `ToolExecutionFailedError` in the passthrough handlers there, and
`execute_todos_parallel` re-raises a deny that `return_exceptions=True`
would otherwise record as one failed todo. `_emit_call_denied_event` also
renders the source through the now-public `source_name`, so a hook that
names itself with a callable reads as its name instead of a repr.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* feat(events): record how a crew run ended, for every user
Crew was the one level with no ungated terminal record. `Crew Execution` and
`end_crew` are both behind `share_crew`, which defaults False, so for
essentially every run there is no end-of-crew span at all - not one with fields
missing. Task outcomes ship ungated, flow outcomes ship ungated; crew being the
exception looks like an accident of history rather than a decision.
Adds `Crew Completed` carrying `outcome` and an explicit `duration_ms`, keyed by
crew_key/crew_id so it joins the existing ungated `Crew Created`. Modelled
directly on `flow_completed_span`, including its reasoning: a separate span
rather than holding `Crew Execution` open, because that span is emitted and
closed at start, so holding it would drop every run that is killed or crashes.
`on_crew_failed` called no telemetry at all before this, so a failed crew
produced nothing. It deliberately does not call `end_crew`, which writes onto
the gated execution span a failed run may never have opened.
Deliberately NOT included, each for a reason:
- Tokens. `crew.token_usage` sums per-agent LLM counters, and two agents sharing
one LLM object share one counter, so the total double-counts today. Putting it
on a span would propagate a known-wrong number into a metric. The dedup keys on
`id(llm._token_usage)`, not `id(llm)` - `Agent.copy()` shallow-copies the LLM -
and it changes the value of public `Crew.calculate_usage_metrics`, so it earns
its own change.
- Models. Already on the ungated `Crew Created` span at 99.86% coverage; this
joins to them by crew_id rather than duplicating.
- Tool counts. The ungated `Tool Usage` span covers only the ReAct path, the
plan/step path double-emits, and nested crews share one RuntimeState - the
count needs a design decision on cache hits before it is worth emitting.
- error_type. Needs 4.1's exception-class field factored out of task_events so
both events share it, rather than duplicated hours after that merged.
Tests use the exporter pattern this suite already uses rather than mocking
`EventListener._telemetry`: EventListener is a singleton, so swapping that leaks
a MagicMock into every later test. The listener tests assert on the stamp
lifecycle instead. Verified order-independent over five randomized runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(telemetry): docstring the crew-completed helpers
Eight of the thirteen functions in this file carried a docstring and five
did not, which is an inconsistency inside the file this PR adds rather
than anything inherited. Documents the two fixtures, the span lookup, the
event-bus runner and the two test methods that were missing one.
No behaviour change: 9 passed, and re-run under random ordering on two
seeds to confirm order-independence.
Deliberately not addressed: the reviewer's 52.17% docstring-coverage
figure is dominated by event_listener.py, where 84 functions - nearly
every pre-existing on_* handler - carry no docstring. That is the file's
convention, and documenting them here would be an unrelated refactor.
The public API this PR adds, Telemetry.crew_completed_span, is documented.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(events): report machine size as a coarse band, not a core count
`runtime_context` says where a process runs but carries no capacity axis, and
its largest bucket is a catch-all: a gunicorn worker on a VM and
`python main.py > out.log` on a MacBook both report `non_interactive`. Docker
Desktop on a laptop reports `container` via /.dockerenv, and a Remote-SSH shell
on a server reports `vscode_terminal`. So "server or laptop" is not answerable
from it today.
Adds `cpu_band` to the common span attributes, so it rides every span the way
`runtime_context` does rather than sitting on `Crew Created` alone - which would
answer nothing for Flow-only, CLI-only or standalone-agent runs.
Six bands, powers of two, top one open-ended: 1-2, 3-4, 5-8, 9-16, 17-32, 33+.
Open-ended because the exact count is the fingerprint - the observed fleet
maximum is 512, and a span reporting 512 identifies one machine. The vocabulary
is closed and asserted, like KNOWN_CODING_AGENTS and KNOWN_RUNTIME_CONTEXTS.
The share_crew-gated exact `cpus` attribute and the four platform* attributes
are untouched. That gating was a deliberate 2024 classification of machine
fingerprint as shareable content (44e38b1d5), and this does not reverse it: a
band is a range, the gated attribute remains the precise value.
Documents a trap in the docstring rather than leaving it to be rediscovered:
os.cpu_count() reports HOST cores, not the cgroup quota, so a 1-vCPU pod on a
96-core node lands in 33+. Right for "what kind of machine", wrong for "what did
this run get" - os.process_cpu_count() gives the latter but needs 3.13, above
this package's floor.
Docs updated in en/ar/ko/pt-BR, in the default-on Execution Environment row,
stating explicitly that the band is a range and the exact count stays opt-in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(telemetry): say where the cpu band comes from
The Execution Environment row ended "Detection reads only whether known
environment variables are set, never their values". True for the assistant
and runtime-context fields, and wrong for the band this PR adds:
detect_cpu_band() reads os.cpu_count() (runtime_env.py:306) and touches no
environment variable. In a privacy disclosure table that is the kind of
inaccuracy worth a line.
Names the source explicitly and scopes the env-var sentence to the two
fields it actually describes. All four locales at parity.
pt-BR also takes the reviewer's wording fix: "um de uma lista fixa" ->
"um valor de uma lista fixa", and the missing comma before o `project_id`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move the canonical API into crewai.flow while preserving experimental imports and declarative references through compatibility aliases.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agents): preserve tool results when final answer is empty
Updated the StepExecutor to ensure that if the final answer from a tool call is empty, the last valid tool result is returned instead. This change enhances the reliability of the output in scenarios where the final answer may not provide useful information. Added tests to verify that both text and native steps correctly preserve tool results under these conditions.
* raise on empty string path
`Create Crew Deployment` fires before the API call that creates the
deployment, so it counts creation ATTEMPTS and cannot carry the uuid - the
call that creates the deployment is the call that returns it. Live effect:
`create_deployment` reads 76,015 events with 0 carrying a uuid (0.000%), so
deployments cannot be joined to anything.
Moving the existing span after the response would fix the uuid and silently
redefine the metric, turning attempts into successes; the deployment churn
figures are built on attempts. So this adds a second span rather than moving
the first.
`Crew Deployment Created` fires after `_validate_response`, which raises
SystemExit on failure - a failed create therefore still counts as an attempt
and reports no creation. Both creation paths, git remote and zip upload,
converge on that line and both return the uuid.
Emits no `deploy:created` feature count: the attempt span already does, and a
second emit would double the deployment count that origin-independent
aggregation depends on.
Tests cover the emitter (uuid carried, distinct span name, no second feature
count, absent-vs-empty uuid) and the call site across both creation paths plus
the failure path.
The warehouse consumer must be widened BEFORE this merges or it delivers
nothing: `mv_span_fanout_forward` filters on a hard-coded 13-name allowlist,
and `mv_fanout_deployment_spans`'s `multiIf` ends in a catch-all `remove_crew`
arm that would mislabel the new span. Runbook prepared separately.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(llms): map default Claude Sonnet 4.6 to its 1M context window
Native AnthropicCompletion fell back to 200k for claude-sonnet-4-6, so the default instance understated the documented limit.
* fix(llms): align Anthropic context windows with active Claude models
Retired Claude 3/2/Instant IDs no longer have dedicated entries; the map now covers the current Claude API lineup and their documented 1M vs 200k windows.
* fix(llms): map Claude Mythos 5 to its documented 1M context window
Native AnthropicCompletion fell back to 200k for claude-mythos-5 even though Anthropic lists a 1M-token window.
* fix(llms): raise Anthropic default max_tokens so large tool calls survive
* fix(llms): default Anthropic to sonnet-4-6 and drop retired models
---------
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* fix(flows): persist custom conversational replies after fallback append
Custom @listen routes that only return a public string were appended after kickoff, so @persist snapshots missed the assistant turn. Snapshot again from the same persist path once the fallback writes state.messages.
* test(flows): cover @persist restore of custom conversational replies
Add regression coverage for custom @listen returns across fresh Flow instances, no double-append on built-in converse, and a single user/assistant message-added event.
* docs: note that public listen returns persist as assistant replies
* fix(agents): render message content parts as text, not a Python repr
A message whose `content` is a multimodal parts list collapsed to
`str(content)` wherever a message had to become a string, so the model
saw `[{'type': 'text', 'text': 'hello'}]` in Current Task, and memory
stored and searched that same repr.
Four sites flattened it that way: the turn promoted into the executor
prompt, the memory recall query, what `_save_kickoff_to_memory` writes,
and `_message_content_text` (token estimation and oversized-message
splitting).
The extraction already existed, inline in `_format_messages_for_summary`
-- text blocks joined, or `[multimodal content]` when a list carries
none. This lifts it to `_content_parts_text` and routes all five callers
through it, so summary, prompt, memory and token counting agree.
`_message_content_text` becomes `message_content_text`: it now has a
caller outside its module, and `agent/core.py` imports only public names
from `agent_utils`. It is not re-exported from any `__init__`, so no
public import path changes.
`test_list_content_uses_str` pinned the repr, so it is intentionally
rewritten to pin the text. Every other existing caller is unchanged:
30 failures on main, 30 on this branch, identical names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
* fix(agents): skip a content part whose text is not a string
`_content_parts_text` joined `block["text"]` straight into a string, and
content blocks are `dict[str, Any]` arriving from a model, so a `text`
key holding an int, dict or None raised `TypeError`. That was contained
to summarization before; routing the prompt, memory and token-estimation
paths through the same helper widened it to `Agent.kickoff`, where the
old `str()` had merely produced an ugly string.
Such a block carries no usable text, so it is skipped. A list left with
nothing usable still falls back to `[multimodal content]`.
Writes the convention down in AGENTS.md rather than leaving it in a
review thread: never `str()` a message's content, use
`message_content_text`. Four sites had independently reached for
`str()`, which is what this whole change is undoing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(mcp): add shared error classifier for HTTP auth failures
When an MCP server refuses a streamable-HTTP connection, the HTTP status is
observed by the client but often buried inside anyio teardown. Add typed
connection exceptions and helpers to recover the status from exception groups,
CancelledError context chains, and httpx errors so later call sites can report
authentication failures instead of guessing.
Groundwork only; no call sites wired yet.
* feat(mcp): raise typed errors from HTTPTransport.connect on HTTP status
When streamable-HTTP connect fails with an httpx HTTPStatusError, classify
the status via the shared MCP exception helpers and raise MCPAuthenticationError
for 401/403 or MCPHTTPError for other refused statuses instead of a generic
ConnectionError that hides the credential problem.
* refactor(mcp): centralize raise_connection_failure and simplify connect
Move connection failure classification into exceptions.py so transports and
clients share one helper. Flatten HTTPTransport.connect to a single except
path that cleans up once and raises, avoiding the outer handler re-classifying
errors the inner handler already typed.
* fix(mcp): classify auth failures in MCPClient.connect before reporting cancelled
When a streamable-HTTP server refuses the connection, the awaiting coroutine
often sees only CancelledError while the HTTP status surfaces during transport
unwind. Inspect cleanup for the status before emitting error_type=cancelled,
and fix HTTPTransport.disconnect so it raises typed errors instead of
suppressing exception groups that carry the refusal.
* fix(mcp): replace speculative tool resolver errors with classifier
Use raise_connection_failure for native MCP discovery instead of hedged
cancel-scope wording, preserve typed MCPConnectionError from setup, and
detect event-loop presence explicitly so ConnectionError is not mistaken
for a missing running loop. Update HTTPS discovery to classify HTTP status
codes via find_http_status.
* refactor(mcp): collapse native tool resolver failure handlers
CancelledError is not an Exception subclass, so handle it alongside
Exception in one except clause and delegate to a shared helper.
* refactor(mcp): call raise_connection_failure directly in tool resolver
* fix(mcp): classify tool execution auth failures in events
Add tool_execution_error_type so call_tool_result emits authentication
instead of server_error for MCPAuthenticationError and HTTP 401/403.
Preserve typed MCPConnectionError in _retry_operation instead of flattening
them into a generic ConnectionError first.
* feat(mcp): add status_code to MCPConnectionFailedEvent
Surface the HTTP status observed during connection failures on the event
payload and in verbose console output, so executions and checkpoints record
401/403 alongside error_type=authentication instead of only the message text.
* fix(mcp): handle cancellation and exception groups in auth paths
Ensure discovery cleanup runs on CancelledError, classify mixed
BaseExceptionGroups during HTTP connect, and fix ExceptionGroup imports
on Python 3.10 with regression tests.
* fix(mcp): preserve auth errors from discovery disconnect cleanup
Re-raise MCPConnectionError from disconnect during cancellation cleanup
instead of logging and swallowing it, with a regression test.
* fix(mcp): unwind transport context to recover auth on cancel
Always exit pending streamable-HTTP contexts before classifying failures,
handle CancelledError during client cleanup, propagate typed HTTPS discovery
errors, and add regression tests for the teardown recovery path.
* fix(mcp): classify auth from groups and timeout teardown
Handle BaseExceptionGroup in HTTPS discovery and recover HTTP 401 from
streamable-HTTP context exit after connect timeouts, with regression tests.
* refactor(mcp): consolidate client connection failure reporting
Extract _report_connection_failure and delegate _http_failure and
_connection_failure to it without changing connect error behavior.
* refactor(mcp): drop redundant client failure helper wrappers
Call _report_connection_failure directly from connect() instead of
_http_failure and _connection_failure delegators.
* fix(mcp): propagate CancelledError after HTTP transport teardown
Re-raise cancellation from disconnect when no HTTP auth status is
recovered during context unwind, with a regression test.
* fix(mcp): preserve typed errors from MCPClient.disconnect
Re-raise MCPConnectionError and CancelledError from exit-stack teardown
instead of wrapping auth failures in RuntimeError, with a regression test.
* fix: skip interception hooks on crewai-internal flows
The `AgentExecutor` and the memory encoding/recall flows are `Flow`
subclasses CrewAI runs for its own bookkeeping, and they were dispatching
interception points as if their methods were the caller's steps — a hook
saw machinery no user wrote, and a policy could deny a run over it.
`Flow._skip_interception` now suppresses every point on a flow marked
`is_crewai_internal`, except the execution boundary on machinery that is
itself the run the caller asked for. A standalone `Agent.kickoff()` keeps
its boundary and stays blockable, while the same executor bound to a crew
or nested in a caller's flow stays silent, so boundaries only ever fire
at the root.
* docs: note that the resume match id feeds the boundary check
`from_pending` seeds `_flow_match_id` from `instance.flow_id` for the usage
listener's filter, and `resume_async` forces `current_flow_id` to it for the
duration of the resume. `AgentExecutor._owns_execution_boundary` compares the
two, so seeding the original persisted id instead would make a resumed
standalone agent disown a boundary its kickoff already opened.
* fix(telemetry): record task failures as failures, not as successes
close_span() sets StatusCode.OK unconditionally, and TaskFailedEvent was routed
to Telemetry.task_ended, which calls it. Every failed task was therefore
exported as OK, which is why error_count downstream is not merely low but
exactly zero: 240.0M task executions across 13 months in
crew_task_executions_daily_target, error_count = 0 in every one of them.
The same line had a second defect. The span was only ended when
source.agent.crew was present, so a task failing without one was popped from the
span map and then never closed - never ended, never exported, invisible rather
than mislabelled. task_failed takes no crew (it reads nothing off one), so that
condition disappears rather than being widened.
Only the exception class name is recorded, never the message, which routinely
contains prompts, model output, file paths and credentials.
close_span_with_error drops any value failing str.isidentifier(), so a message
cannot be recorded even if one is passed by mistake.
This is the task half of closed PR #6781, re-cut onto main as that PR asked for.
The crew half is deliberately left out: crew_execution_span() returns None unless
share_crew=True, so crew._execution_span is None for nearly every user and a
crew-failure handler would exit immediately for the default population.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* fix(telemetry): take the exception class for error_type, not a free-form string
cursor and CodeRabbit both flagged the sanitization, and they were right: the
package already had a stronger convention and this change had not used it.
Telemetry._safe_error_type takes the exception *class*, and its docstring says
why in as many words - "a single-word message such as 'secret_token' is itself a
valid identifier", so filtering a string with isidentifier() is not enough. The
ported code predates that helper and reinvented the weaker check.
TaskFailedEvent.error_type is now type[BaseException] | None, so pydantic itself
rejects a message before any of our code runs, and task_failed routes it through
_safe_error_type. The identifier check in close_span_with_error stays as the
second gate on the derived name, which is the role _safe_error_type's docstring
already describes.
Also adds producer-level tests, which CodeRabbit correctly identified as missing:
every earlier test constructed TaskFailedEvent directly, so a regression in the
two emit sites this change touches in task.py would have passed the whole suite.
The sync and async producers are driven through Task._execute_core and
Task._aexecute_core with a distinctive exception class, and each patches a
different agent method (execute_task vs aexecute_task), which is why they can
regress independently. Verified by dropping error_type from both producers: all
three new tests fail, and pass again when restored.
Removes an unused `import os` left behind when the fixture was rewritten.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(telemetry): capture producer failures at the emit boundary, not via the bus
The producer tests subscribed a handler to crewai_event_bus and asserted on what
it received. That passed this file in isolation and every randomized local run,
then failed in CI inside a 621-test shard with zero events captured:
FAILED tests/telemetry/test_task_failure_instrumentation.py::
test_sync_producer_puts_the_exception_class_on_the_event
assert 0 == 1 + where 0 = len([])
task_failed is an "ending" event, and with an empty scope stack - there is no
real kickoff in these tests - dispatch is conditional on event-context state that
other tests in the same worker process can leave behind. Subscribing made the
assertion depend on the bus choosing to dispatch, which is not what these tests
are about: they are about what the producer in task.py constructs.
Patching crewai_event_bus.emit records the event unconditionally at the point the
producer hands it over, with no dispatch involved. Both producers ignore emit's
return value, so returning None is faithful.
Containment re-verified after the change: dropping error_type from both producers
fails exactly these three tests and nothing else.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* fix(events): keep TaskFailedEvent JSON-serializable with a class-valued error_type
error_type holds an exception class, which is not a JSON type, so
model_dump(mode="json") raised PydanticSerializationError for the whole event -
not just that field. Two real consumers depend on it: the checkpoint listener
dumps every event through EventRecord, and the tracing listener JSON-POSTs events
to AMP. A single task failure therefore took out checkpointing.
field_serializer with when_used="json" returns the class name. The "json" scope is
load-bearing: event_listener hands the live class to Telemetry.task_failed, which
needs it for _safe_error_type, so python-mode dumps must keep the class.
The annotation is a module-level _ExceptionClass alias rather than an inline
type[BaseException], because TaskFailedEvent declares a field named `type` which
shadows the builtin for the rest of the class body - inline, it raises TypeError
at import ("task_failed"[BaseException]) and mypy rejects it as "Variable ... is
not valid as a type". Quoting satisfies neither tool: ruff flags UP037 and mypy
still resolves it in the class scope.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* fix(events): let a dumped error_type restore, instead of degrading the event
The serializer added in the previous commit stopped model_dump(mode="json") from
raising, but nothing accepted the class-name string back. _resolve_event
(state/event_record.py:32-35) wraps cls.model_validate in a bare except and falls
back to BaseEvent, so restoring a checkpoint after a task failure silently
dropped the whole event -- including `error`, a plain string that would otherwise
have survived. Traded a loud failure for a quiet one.
Measured before: dumped error_type='ValueError' and error='boom', restored as
BaseEvent with neither attribute. After: restores as TaskFailedEvent with
error='boom' and error_type is ValueError.
A BeforeValidator resolves a name against real exception classes only -- builtins
first, then a walk of BaseException.__subclasses__(). So this does not reopen the
hole the class-typed field closes: "secret_token" resolves to nothing, is returned
unchanged, and is rejected by the field's own type. Asserted for secret_token,
sk_live_1234, dict and os.
A name whose class is not imported in this process still degrades, which is
deliberate: synthesising a class from an arbitrary string is the injection risk
this field exists to avoid.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* feat(flows): enhance conversational flow documentation and APIs
- Updated the description to clarify the use of `handle_turn` and structured streaming in multi-turn chat applications.
- Added a warning about the experimental nature of the conversational features.
- Improved the overview section to include structured streaming and refined the explanation of session handling.
- Enhanced the API documentation for `handle_turn`, `stream_turn`, and `chat` methods, emphasizing their roles in conversational flows.
- Clarified the turn lifecycle and the handling of user messages within the flow.
- Updated examples to reflect changes in message handling and session tracing.
- Ensured consistency across language versions in the documentation.
* feat(flow): deprecate answer_from_history route
Guide conversational flows toward the existing converse route while preserving compatibility warnings and schema metadata.
Co-authored-by: Cursor <cursoragent@cursor.com>
* stacklevel=3 raising it higher
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agents): keep message roles when Agent.kickoff gets a conversation
`Agent.kickoff` accepts `str | list[LLMMessage]`, but `_prepare_kickoff` joined
every message's content into one string. Measured with a recording LLM, a
three-turn conversation reached the provider as two messages:
system | You are Support...
user | Current Task: my order id is 42\nthanks, checking\nwhere is it?
So the agent's own previous reply was presented as something the user said, and
the model could not tell who said what. `LiteAgent.kickoff` already did this
correctly, which is why the same list gave four messages with roles intact
there.
The last message is now this turn's request and the ones before it travel as
`inputs["history"]` -- the way `inputs["files"]` already does -- which both
executors splice in after the system prompt and before the user prompt. Memory
recall still runs over the whole conversation text, not just the last turn.
A plain string and a single-message list are byte-identical to before, which is
what every existing caller passes.
Also widens `LiteAgentExecutionStartedEvent.messages` from `list[dict[str, str]]`
to `list[LLMMessage]`: it raised a ValidationError for a message whose content
was `None` or a content-part list, both of which are valid `LLMMessage` shapes
that `Agent.kickoff` already accepts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(agents): carry history without a system prompt, tool calls, or dup files
Three review findings on the message-role work, all real.
History was spliced only in the branch that builds a system prompt. With
`use_system_prompt=False` or a custom template there is one combined prompt, so
history went nowhere and only the last turn reached the model -- worse than the
flattening this PR replaced, which at least included the text. Both executors
now splice in either branch, via one `_append_history` helper each.
Filtering on truthy content dropped an assistant turn that only requests tool
calls, leaving a `tool` message with no preceding `tool_calls` message -- a
sequence providers reject. A message now counts when it has content, tool
calls, or a tool_call_id.
And `files` were unioned from every message onto the current request while
history messages kept their own, so prior attachments were sent twice. Only the
current request's attachments travel in `inputs["files"]` now.
Found by Cursor and CodeRabbit on #7065.
* fix(agents): treat an attachment as message payload
_carries_payload counted text, tool calls and tool results, but not files.
A final message whose only payload was an attachment was filtered out, so the
previous message became this turn request and the attachment never reached
inputs["files"].
Found independently by Cursor and CodeRabbit on #7065.
* fix(agents): promote the last user message, not the last message
build_agent_context() appends an agent private thread after the current user
turn, so on a later turn the trailing message is an assistant scratch. That
scratch became Current Task while the real question was demoted to history --
reproduced: the request came back as "internal note: checked warehouse".
The request is now the last user message, with everything else kept as history
in order; with no user message the last one stands in, which is what a
single-message caller has always got. Documented on kickoff and kickoff_async.
The old cross-check against LiteAgent only compared roles on a fixture already
ending in a user message, so it could not catch this. It now asserts what holds
for both -- nothing dropped, nothing duplicated -- since Agent has a task slot
in its prompt and LiteAgent does not.
Reported by Vidit-Ostwal on #7065.
* test(agents): cover history placement on the deprecated executor too
`CrewAgentExecutor` carries its own `_setup_messages`, and `Agent.kickoff`
builds an `AgentExecutor` unconditionally, so nothing reached the twin's
two `_append_history` sites. This drives that executor directly with the
real `SystemPromptResult` / `StandardPromptResult` shapes, so both its
branches are pinned.
Verified by mutation: removing either `_append_history` call in either
executor now fails the matching test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
* fix(agents): keep turns that follow the request after it
`_prepare_kickoff` packed every non-request message into one `history`
list, and both executors spliced that list before the current user
prompt. A conversation ending on a tool result therefore reached the
provider as `assistant tool_calls -> tool -> user`, hoisting the tool
pair above the question it answers. `LiteAgent` sends the same list as
`user -> assistant -> tool`.
Splits the carried messages at the request instead: what came before
stays `history`, what came after travels as `trailing` and is appended
after the user prompt. Promoting the last user message to `{input}` is
unchanged.
`test_a_tool_call_sequence_survives` missed this because it ends on a
follow-up user line, so the tool pair was already before the request.
Reported by lorenzejay, who also confirmed gpt-4o-mini and gpt-5.6-sol
accept the reordered payload -- an order bug, not a provider 400.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
* test(agents): assert the ordering through kickoff_async too
`kickoff_async` shares `_prepare_kickoff`, and the async executor entry
points reach the same `_setup_messages`, but sharing a code path is not
the same as covering it. Pins the tool-result ordering through the async
path, and lifts the tool conversation to a module-level fixture so the
sync and async assertions cannot drift.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
* docs(agents): state where a kickoff conversation's turns land
`concepts/agents.mdx` documents the multi-message form but not which
message becomes the request or what happens to the turns around it --
the contract this branch changed. Corrects the `kickoff` /
`kickoff_async` docstrings to match.
en and ko only: the ar and pt-BR pages do not carry the "Multiple
Messages" section at all, which is a pre-existing translation gap rather
than one this change introduces.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ
* feat(flow): let a declarative agent action receive the conversation (#7066)
* feat(flow): let a declarative agent action receive the conversation
A chat handler could only hand its agent a single string, so a declarative
conversational flow's `agent` action never saw prior turns. Both ends blocked
it: `AgentDefinition.input` was `str` with a validator rejecting anything else,
and `AgentAction.run` raised "agent input must render to a string" once a CEL
template rendered to a list.
`input` now takes a string or a list of messages, and the action normalizes
rather than rejecting. A whole-string `${...}` template keeps its evaluated
type, so `state.messages.map(m, {'role': m.role, 'content': m.content})`
renders the exact shape the agent wants -- no new CEL function needed.
Serialized messages carry `name: None` and `metadata` that the agent event
schema rejects, and `message_to_llm_dict` only drops `None` for a model input,
not the plain dicts a CEL render produces. The normalizer drops them, keeping
`content: None` since that is a valid message shape.
Declarative crews are unaffected: they use `CrewAgentDefinition`, which has no
`input` field at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(flows): pin content=None survival through agent input normalization
A filter-all-None change would silently drop the assistant turn that only
requests tool calls, and no test covered that key.
Found by CodeRabbit on #7066.
* test(flows): cover the agent action through kickoff, not the helper
The existing tests called _normalize_agent_input directly, so nothing pinned
that Expression.render_template -> normalization -> Agent.kickoff_async keeps a
message list intact. Removing the normalization call now fails this test.
Found by CodeRabbit on #7066.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* feat(flow): let a declaration name the router's response format
`conversational.router.response_format` was typed `Any` and dropped with a
warning, because `_router_response_format` hands its value straight to
`llm.call(response_format=...)`, which needs a real class. So the router always
used its synthesized fallback: `intent: str` with the route labels only in a
field description.
The field now takes the same `{"python": "module.path.Class"}` shape a crew
agent's `response_format` uses, resolved through the same
`_resolve_model_class`. That brings the project-root containment with it -- a
declaration cannot reach outside the project to import code -- and gives the
router a `Literal[...]` of the real route labels instead of a bare string.
The DSL projection now emits that shape too, so a live class on a Python flow
round-trips as `{"python": ...}` rather than an opaque `{"ref": ...}` that
nothing could reload.
A bare `module:qualname` ref is now a load-time validation error instead of
being silently discarded; the test that pinned the old drop-with-warning
behavior is updated to assert that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): do not project a response format that cannot be reloaded
`_python_reference` emitted a dotted path for any class, including two that
cannot be imported back: a non-Pydantic class, and one defined inside a
function, whose `__qualname__` carries `<locals>`. The definition then held a
ref that only failed when something tried to resolve it.
Both are dropped at projection time with a warning naming why, so a reload
falls back to the synthesized response format. The live class still drives the
running flow; only the projection omits it.
Found by CodeRabbit on #7063.
* test(flows): assert the response_format omission warnings
The projection tests only checked for None, so removing the warning that tells
an author their response_format was dropped would still pass.
Found by CodeRabbit on #7063.
* fix(flows): only project a response_format ref that imports back
The check rejected <locals> classes but still emitted a path for a nested one.
The loader splits a ref on its last dot, so module.Outer.Route resolves
module.Outer as a module that does not exist - proven: reload raised
JSONProjectError. A create_model() class held only in a local is unreachable
the same way.
Projection now confirms module.qualname resolves back to the class, against the
already-imported module so it never triggers an import.
Found by CodeRabbit on #7063.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(core): map GPT-5.6 to the official 1.05M context window
LiteLLM fallback treated Sol, Terra, Luna, and the gpt-5.6 alias as unknown and used the 8k default.
* fix(openai): give GPT-5.6 its own 1.05M window
Native lookup matched gpt-5 first, so Sol, Terra, and Luna inherited 1,047,576. Longest-prefix matching keeps gpt-5 and gpt-5.4-mini on their own sizes.
* fix(azure): map GPT-5.6 deployments to the official 1.05M window
Azure had no gpt-5 / gpt-5.6 entry, so Sol, Terra, and Luna fell back to 8k.
* feat(cli): list GPT-5.6 Sol, Terra, and Luna in curated catalogs
The family is generally available; keep gpt-5.5 as the offline default.
* refactor: keep context-window tables in longest-prefix order
Drop the runtime sort and document that new keys must be inserted longest-first so startswith matching stays correct.
* fix(core): resolve prefixed LiteLLM models to the GPT-5.6 window
openai/gpt-5.6-luna kept its provider prefix on self.model, so startswith matching missed the 1.05M mapping. Strip recognized prefixes for lookup and leave unknown ones intact.
- Updated the description to clarify the use of `handle_turn` and structured streaming in multi-turn chat applications.
- Added a warning about the experimental nature of the conversational features.
- Improved the overview section to include structured streaming and refined the explanation of session handling.
- Enhanced the API documentation for `handle_turn`, `stream_turn`, and `chat` methods, emphasizing their roles in conversational flows.
- Clarified the turn lifecycle and the handling of user messages within the flow.
- Updated examples to reflect changes in message handling and session tracing.
- Ensured consistency across language versions in the documentation.
* feat(flow): let a chat flow declare its own state shape
A conversational declaration could only use `state: {type: pydantic, ref: ...}`
pointing at a `ConversationState` subclass. Every other shape loaded clean and
then died on the first turn -- inline `json_schema` and a non-subclass ref with
`AttributeError: 'StateWithId' object has no attribute 'messages'`, and
`type: dict` with `AttributeError: 'dict' object has no attribute 'id'`.
`Flow._compose_extension_state_model` is a new runtime extension seam -- the
seventh alongside the existing six -- applied to the model built from `state:`
before the engine wraps it for `id`. The conversational mixin uses it to add
the chat fields to whatever the declaration asked for, so declared fields and
defaults survive; a model that already extends `ConversationState` is returned
untouched, so today's supported shape is a no-op.
`dict` and `unknown` state cannot carry those fields at all, so the default
extension state supplies the real shape (seeded from the declared defaults
where they fit) rather than forbidding it. Raising instead would break
construction, and `Flow[dict]` with `conversational = True` constructs today --
`crewai flow plot` and definition-only consumers would stop working.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): keep declared defaults and cover an unbuildable state model
Two review follow-ups on the declared-state work.
A `dict` state's defaults are arbitrary keys, and the fallback kept only the
ones matching `ConversationState`, so `{"type": "dict", "default": {"topic":
"ai"}}` lost `topic` before the first turn and an action reading `state.topic`
would fail. They are carried as extras now.
And a declared `pydantic`/`json_schema` state whose model cannot be built --
a bad ref, an invalid schema -- fell through to a plain dict with none of the
chat fields, so the turn died on `state.id` instead. The engine now re-asks the
extension in that case, as if nothing had been declared.
Found by Cursor and CodeRabbit on #7061.
* refactor(flows): drop the unreachable extension-state fallback
The _initial_state_t branch sat after an unconditional return. The
state_definition is None case and _conversation_state_with_defaults now cover
every path that used to reach it.
Found by Cursor on #7061.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(telemetry): report project creation with the id minted for it
Acquisition was only observable from a project's first run. That misses every
project created and never run, and dates the rest to the wrong day.
All three scaffolding paths already mint a project_id into the new
pyproject.toml. None of them reported it, and two of them - create_crew and
create_json_crew, which is the default `crewai create crew` path - emitted no
telemetry at all.
`Project Created` carries the kind (crew, json_crew, flow) and the id that was
just minted, and is emitted after the mint so it can carry it.
The attribute is `created_project_id`, not `project_id`, because those are two
different things. CommonAttributesSpanProcessor stamps `project_id` on every
span from get_project_id(), which reads the current working directory and is
cached for the life of the process - during `crewai create` that describes the
directory the command was run from, not the project being created. Reusing the
name would have given one column two meanings depending on span type.
Nothing is emitted for `create_crew(parent_folder=...)`: that adds a crew to a
project which already exists, mints no id, and is not an acquisition.
The existing `Flow Creation` span is left exactly as it is. Note for whoever
reads it: it is emitted from two places with two different meanings - CLI
scaffolding (create_flow.py) and runtime flow construction (event_listener.py on
FlowCreatedEvent) - so it cannot separate acquisition from usage. Not changed
here because it is a live series and renaming it would break continuity.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(telemetry): stop the creation-span tests exporting to the real collector
The recorded_spans fixture has to enable telemetry for its assertions to mean
anything - a disabled Telemetry never builds self.provider, so every assertion
would pass vacuously against a non-recording span. But enabling it is exactly
what makes __init__ wire a BatchSpanProcessor around the real
SafeOTLPSpanExporter, pointed at the production collector.
Measured, with a spy on both SafeOTLPSpanExporter classes reporting at
interpreter exit: before this change the file made 3 real export calls, handed 3
synthetic Project Created spans with invented created_project_id values to the
production exporter, and completed 3 connects to the collector. After: 0, 0, 0.
Sampling at pytest_sessionfinish reports 0 either way and is how this was missed
- BatchSpanProcessor flushes on a background timer, and with no
provider.shutdown() the flush lands in the atexit handler, which runs after
sessionfinish.
--block-network does not prevent it: it is function-scoped and only swaps
socket.connect, which a background batch thread outlives.
Follows telemetry_with_exporter in tests/telemetry/test_tracer_isolation.py:
_NullExporter swapped in before construction, _register_shutdown_handlers
suppressed so no atexit hook is left behind, and provider.shutdown() in finally.
Patch target is crewai_core because that is where this Telemetry comes from.
Also disambiguates the docs rows: the minted ID belongs to the new project, not
to the directory the command ran in, and the two can differ. Reworded in all four
languages rather than renaming the token to created_project_id - this table
documents data, never span-attribute keys (kind and crewai_version on the same
row are unnamed), and `project_id` is already the page's name for the
pyproject.toml key.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* docs(ar): use tanwin fath on the letter, not on the alif
مشروعًا / جديدًا rather than مشروعاً / جديداً, in the row added by this PR.
Both forms appear in docs/edge/ar (3 each), so this is not a house convention
being broken either way; the corrected form is the more standard one and the text
is mine.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* docs: stop the creation row implying the span's project_id holds the minted value
CodeRabbit re-raised this as Major after I rejected the first version, and it was
right to. My rejection argued the page never names wire keys, so introducing
created_project_id would be its only one. That part still holds -- grep finds no
attribute key anywhere in the four files. But it was the wrong conclusion: the row
still used the token `project_id` for a value the span does NOT carry under that
key, while the same span's real project_id holds the cwd-derived value. The row
also already exposes literal wire values (`crew`, `json_crew`, `flow` are the
actual kind values), so "this page has no wire detail" was overstated.
Dropping the token resolves the ambiguity without adding the page's only key
name: the row now says "the project ID minted for that new project", and names
`project_id` only to say the minted value is recorded separately from it.
All four languages. No docs/v*/ touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* docs: use an em dash in the creation row, matching the rest of the page
The two ASCII `--` occurrences in these files were both mine, introduced by this
PR: the page otherwise uses em dashes throughout (en 4, ar 4, ko 2, pt-BR 4).
CodeRabbit flagged pt-BR; the same slip was in en, so both are fixed. Now zero
ASCII `--` across all four language files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* feat(telemetry): record whether a run had inputs, without recording the inputs
The `crew_inputs` payload is gated behind `share_crew` and stays that way, so the
only way to tell a parameterised run from an unparameterised one was to read a
gated key: it is present on roughly 0.02% of spans, all of them opt-in sharers.
That is a measurement of people who opted into sharing, not of users.
`crew_inputs_present` carries just the answer -- "true"/"false" -- on the
already-ungated `Crew Created` span. The payload stays inside the `share_crew`
branch, so nothing new about the contents of anyone's inputs is collected.
A string, for the reason `crew_memory` is a string, and the encoding matters
more here because the majority case is the empty one. Measured over a single day
(312,424,709 spans): `vInt64='0'` occurs 0 times and `vBool='false'` occurs 0
times, while `vStr='0'` does occur. proto3 omits the zero value for ints as well
as bools, so an integer key count would have silently dropped every
unparameterised run -- and among sharers, 54.46% of runs pass `{}`.
`{}` and `None` are both "false": an empty dict parameterises nothing, so
truthiness is the question being asked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(telemetry): assert input keys are absent too, not only input values
The gating test checked only the input value. A regression that emitted the input
keys - json.dumps(sorted(inputs)) or similar - would have passed it, and key
names are user data as much as values are.
Verified by injecting exactly that regression: the new assertion fails on it and
passes once reverted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
pip-audit started failing on every open PR. The advisory is against pip itself:
PYSEC-2026-3721 / CVE-2026-13346, which OSV records as affecting pip up to but
not including 26.2. The floor was already pinned at >=26.1.2, so the previously
patched version became the vulnerable one.
Not caused by any open PR. Reproduced on tag 1.15.17 itself (`b3ab193c3`), which
resolves pip 26.1.2: `uv run pip-audit` with CI's exact arguments reports
"Found 1 known vulnerability" there with no branch changes at all. That is why
this is its own PR rather than a fix inside whichever PR happened to run first.
Raising the floor rather than adding --ignore-vuln, since a patched release
exists: 26.2 fixes it and 26.2.1 is current. The trailing comment follows the
convention already used for setuptools>=83.0.0.
The uv.lock change is deliberately hand-scoped to pip's four lines. Running
`uv lock` -- with either uv 0.11.12 or 0.11.15 -- also re-expands environment
markers for numpy, humanfriendly, grpcio, mcp and a dozen nvidia-* packages,
because the committed lock was produced by a uv that simplifies markers
differently from any version available here. Those rewrites change CUDA and
platform resolution and have no business riding along in a security fix. The
four lines applied here are exactly the ones uv itself produced for pip.
Verified: `uv lock --check` passes, so the lock is consistent with pyproject and
needs no regeneration; pip resolves to 26.2.1; `uv run pip-audit` with CI's
arguments reports "No known vulnerabilities found, 1 ignored"; crewai and
crewai_core still import.
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix(flow): a resumed flow must emit flow_started, not only flow_finished
_resume_async_body gated FlowStartedEvent behind suppress_flow_events while the
matching FlowFinishedEvent a few hundred lines below stayed ungated. A suppressed
resume therefore emitted an unpaired finish: a flow that reported finishing
without ever having started. That is worse than a missing row - it breaks every
started/finished pairing and any duration or funnel built on it, and it removed
the resumed leg from telemetry entirely.
suppress_flow_events is also the wrong gate for emission. It asks for console
quiet: _flow_origin in events/event_listener.py says so explicitly and notes it
"can legitimately be set on a caller's own flow", and the listener already
honours it at each point where it prints. So a user who set it on their own flow
for quiet output silently lost their resumed runs from telemetry.
The emit is now unconditional, matching both the kickoff path - which never gated
it - and the FlowFinishedEvent it pairs with. The method-execution gates in this
function are left alone: _execute_method gates the same events on the same flag,
so those are symmetric and intended.
Internal flows that set this flag (agent_executor, the memory recall/encoding
flows) will now emit a started event when resumed. That is the point, and the
is_crewai_internal marker already keeps them out of user-facing flow metrics -
a distinction _flow_origin draws precisely because this flag cannot carry it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* fix(flow): emit the whole lifecycle on a suppressed resume, not just the start
The first commit on this branch ungated FlowStartedEvent on the resume path
while FlowFinishedEvent stayed gated, which left a suppressed resume emitting a
start with no terminal event. CodeRabbit and cursor both caught it.
The premise that commit was written against was wrong: origin/main gated the
started event and the finished event, so it emitted neither and was symmetric.
It was the detection that was broken, not the code -- a fixed-line lookback for
the enclosing condition missed the multi-line `if (not self.suppress_flow_events
and not self._should_defer_trace_finalization()):` guarding the finish.
The defect is therefore not an unpaired event on main but a silent one: a
resumed run with suppress_flow_events set emits no lifecycle events at all, so
it never reaches a listener or the trace exporter and the run is invisible
downstream. kickoff_async emits them either way and lets listeners filter, and
suppress_flow_events asks for console quiet rather than for telemetry to be
dropped, so resume now matches kickoff.
_should_defer_trace_finalization() still withholds the finish, which is a real
reason: finalize_session_traces() emits it later instead.
respect_suppression is deleted rather than left defaulting to False -- the
resume call site was its only caller, so nothing passes True any more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): accept crew-style LLM config in a conversational declaration
`conversational.llm` / `intent_llm` / `answer_from_history_llm` and
`router.llm` only accepted a model-id string, so a declaration could not set
`max_tokens`, `temperature` or anything else a declarative crew's agent can.
`_coerce_llm` now delegates to `crewai.utilities.llm_utils.create_llm` -- the
same helper the crew/agent declaration layer resolves through -- so the shapes
match: a model-id string, a config mapping, an `LLMDefinition`, or a live
`LLM`/`BaseLLM` passed straight through.
It keeps one thing `create_llm` does not: `create_llm(1234)` takes the int as a
model name and returns an LLM that only fails later with a provider error. A
declaration is hand-written, so a non-string, non-mapping value raises now
instead. A mapping missing `model` keeps `create_llm`'s own message.
The contract fields stay permissively typed and gain descriptions naming the
accepted shapes. Tightening them to `str | LLMDefinition` would break the DSL
projection: a live custom `BaseLLM` whose config dump lacks a `model` key
degrades to a `{"ref": ...}` mapping, which such a type would reject -- turning
`flow_definition()` on an existing Python flow into a validation error.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): resolve declared LLM mappings for intent classification
`intent_llm` and `answer_from_history_llm` were documented as taking the same
shapes as `llm`, but both route through `_collapse_to_outcome`, whose own
coercion accepts only `str | BaseLLM`. A declared config mapping reached it
unchanged and raised "Invalid llm type: <class 'dict'>" mid-turn -- so the
documentation promised something that failed.
Also fixes the field descriptions. The previous commit's `llm` description
landed on `FlowConversationalRouterDefinition.llm` rather than
`FlowConversationalDefinition.llm`, because both fields are literally
`llm: Any = None` and the first match won. All four now describe their own
field and name every accepted shape, including `LLMDefinition` and a live
instance.
Test changes: adds an `LLMDefinition` resolution case, and the declared-mapping
turn test now patches `create_llm` to prove the mapping reaches it instead of
swapping the config out beforehand, which proved nothing.
Found by Cursor and CodeRabbit on #7062.
* docs(flows): name the full router LLM precedence; pin the coercion path
The conversational llm description skipped intent_llm in the router fallback
order (router.llm, then intent_llm, then llm), and the intent_llm test replaced
the declared mapping with a scripted LLM before the turn, so it never exercised
the coercion. Patch create_llm instead: removing the coercion now fails the
test with "Invalid llm type: <class 'dict'>".
Found by CodeRabbit on #7062.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): open the conversational TUI for a declarative chat flow
`crewai run` refused a declarative conversational flow and told the user to
drive it from Python. That was wrong: the conversational TUI already exists and
already does this job. `CrewRunApp(conversational=True)` renders a chat pane and
drives `handle_turn` per message (crew_run_tui.py:833-935), and
`kickoff_flow._run_conversational_flow_tui` launches it for a Python
conversational Flow.
A declaration-built flow satisfies everything that TUI needs -- `handle_turn`,
a settable `defer_trace_finalization`, and `finalize_session_traces()` -- so it
now routes there instead of exiting.
A chat loop still needs a terminal. A headless run (`is_interactive()` false,
which folds in CREWAI_DMN) says what it would have needed rather than kicking
off a single turn and presenting that as the whole conversation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): do not send a human-feedback chat flow to the Textual TUI
A declaration can carry both a `conversational:` block and a method with
`human_feedback:` -- verified, both predicates return True on the same flow.
Routing it to the chat TUI hangs: the runtime collects feedback with a blocking
`input()` (flow/runtime/__init__.py:3719) that Textual cannot service, so the
prompt is never shown. The STEPS TUI already declines these for exactly this
reason. Such a flow now falls back to the terminal REPL, which can prompt.
Also updates the guide in en/ar/ko/pt-BR: it still said `crewai run` has no
chat loop and exits, which is now the opposite of what the CLI does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): reject --inputs on a conversational flow instead of dropping it
The conversational branch returns before `_resolve_flow_inputs`, and the TUI
calls `handle_turn(message)` -- which owns the kickoff inputs, passing
`{"id": session_id}` itself. So any `--inputs` value was silently discarded and
the conversation ran as if it had been applied. It now errors, and says that
resuming a session by id is not wired up yet rather than implying it worked.
Also corrects the Arabic guide: `مُوجّه محجوز` reads as "reserved router", not
the blocking prompt it describes.
Both found by CodeRabbit on #7060.
* fix(cli): document the conversational routing exceptions
Three review follow-ups:
- The Arabic guide read خدمته (masculine) against the feminine
مُطالبة introduced by the last fix.
- Both docstrings described a routing path that now has exceptions: a
conversational declaration rejects --inputs and skips state-schema
resolution, and a human-feedback one uses the terminal REPL.
- The --inputs rejection test accepted SystemExit(0); it now pins code 1.
Found by CodeRabbit on #7060.
* fix(cli): reject --inputs on a chat flow even when it parses empty
parse_inputs_json returns {} both when the option is absent and when the user
passes --inputs "{}", so the falsy check started the TUI for the second case
while the docs said it was unsupported. The conversational path now takes
whether the option was supplied, not what it parsed to.
Documents the restriction in en, ar, ko and pt-BR.
Found by CodeRabbit on #7060.
* test(cli): pin the headless conversational exit status
pytest.raises(SystemExit) also accepts SystemExit(0), so the error path could
regress to a successful exit unnoticed.
Found by CodeRabbit on #7060.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This pipeline cannot carry a false boolean. Measured across 218,400,577 spans,
not one carries vBool=false - proto3 omits the bool zero value, so false is
never serialized and arrives as the key simply being absent. "Memory disabled"
was therefore structurally unrepresentable, and presence had to stand in for the
value, which is why crew_memory read 1 for 99.8% of crews against a field that
defaults to False.
The fix is the convention this file already documents and applies to `resumed`
and `conversational`; crew_memory is the attribute those comments name as the
outstanding case. It was the only remaining CrewAI-emitted boolean attribute -
checked empirically: every other attribute appearing in vBool comes from
third-party instrumentation.
Truthiness rather than `is True`, per the decision that memory counts as enabled
when set by any means: a Memory, MemoryScope or MemorySlice instance is enabled
just as much as `memory=True`. None of those classes defines __bool__ or __len__,
so an instance is always truthy.
Tests cover all four inputs - True, False, None and an instance - and reuse the
existing guard that no attribute is ever passed as a bare boolean. Verified they
fail against the unpatched emitter.
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(cli): backfill project_id from every user-invoked project command
`crewai run` has always backfilled: a project declaring [tool.crewai] without a
project_id gets one minted the first time it runs. No other command did, so a
project driven entirely through `crewai test`, `crewai deploy` or
`crewai traces enable` never acquired an id and every one of its runs stayed
unattributable - which is the denominator problem, not a cosmetic gap.
Adds the same call to train, replay, test, login, deploy create, deploy push,
flow add-crew, enterprise configure and traces enable. Every one is an action the
user explicitly invoked, which is the condition run_crew already relies on, so this
is the existing principle applied evenly rather than a new policy. It is still never
called from the SDK during kickoff, and get_or_create_project_id still refuses to
create the [tool.crewai] table, so an unrelated directory is never rewritten.
`crewai flow kickoff` is deliberately untouched: it delegates to run_crew and
already inherits the backfill. A test pins that so the delegation is not
accidentally duplicated. There is no `crewai evaluate` command - `crewai test` is
that path.
The call is the first statement in each command so a command that later fails still
leaves the project with an id. The tests patch the backfill to raise, which proves
the call happened and guarantees nothing after it runs, so no test touches user
settings, spawns a subprocess or reaches the network. Verified they fail against the
unpatched module: 9 command tests fail, the 2 guard tests still pass.
Tests live under lib/crewai/tests/cli/ because that is the path the required CI job
runs; nothing runs lib/cli/tests/.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(cli): assert the backfill at runtime instead of reading source text
Addresses CodeRabbit and github-code-quality on #7057. The two guard tests
grepped module source for a call string, which asserts on formatting rather than
behavior: a reformat would break them and a real regression could slip past.
They now invoke the commands in an isolated project and assert on observed calls.
The flow-kickoff test patches the two distinct import sites separately and asserts
run_crew's is called exactly once while cli's is not called at all, which is what
makes 'delegates' and 'duplicates' distinguishable at runtime rather than by
reading the file.
Verified both catch what they claim: injecting a duplicate call into flow_run
fails the delegation test, and removing run_crew's own call fails the run test.
This also drops the module-level 'import crewai_cli.cli as cli_module' that mixed
import styles with the existing 'from crewai_cli.cli import crewai', which is the
code-quality finding - the rewrite removes the need for it entirely.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(cli): make the exact-once assertion observable
Addresses CodeRabbit on #7057, and the finding was correct: with
side_effect=_BackfillReached the mock raised on first use, so call_count == 1 was
guaranteed by the mock rather than by the code. A second backfill call inside the
same run_crew execution could never have been observed.
Both backfill mocks now return normally and execution is stopped at the first call
AFTER the backfill (configured_project_json_crew), so the recorded count is real.
Verified the difference this makes: injecting a duplicate get_or_create_project_id()
INSIDE run_crew now fails both tests, which the previous version could not detect at
all. The flow_run duplicate case is still caught.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(cli): assert flow kickoff reaches the post-backfill boundary
Addresses CodeRabbit on #7057, and the finding was right: the flow-kickoff test
discarded the runner.invoke() result, so if the path returned or raised after one
backfill call but before configured_project_json_crew, both call-count assertions
would still have passed - for the wrong reason.
test_run_still_backfills already asserted the boundary; this makes the pair
consistent.
Verified it earns its place: injecting an early return after the backfill and
before the boundary now fails both tests, and previously would have failed
neither.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* test(cli): pin that the backfill precedes command-specific work
Addresses CodeRabbit on #7057. The finding is valid: the parametrized test proves
the backfill is reached, not that nothing ran before it, so its assertion message
claimed more than the test established.
Fixed in two parts rather than as proposed. The message now states what the test
actually proves, and a new test pins the ordering on login: , whose first action
goes through a module-level name that can be patched without reaching into the
command.
Deliberately not parameterized across all nine commands, which is what the finding
suggested: that would mean naming each command's current first action, and those
change as commands evolve, so the suite would end up tracking their internals
rather than this ordering property. One representative command establishes it, and
placement is visible in the diff for the rest.
Verified it catches the regression: swapping login's first two statements so its
own work runs before the backfill fails the new test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(telemetry): always emit project_id so absent and empty stay distinct
common_span_attributes() stamped project_id onto every span only when the project
declared one, and omitted the key otherwise. That makes two different situations
indistinguishable downstream: a client too old to report a project id at all, and
a current client whose project simply declares none.
The consequence is not cosmetic. The share of clients that COULD have reported an
id is the denominator of every attribution rate, and with both cases collapsed into
"key absent" that denominator cannot be computed at all - it can only be inferred
from a version floor, which is fragile and silently wrong for any client that
backports or pins.
The key is now always present and is the empty string when the project declares
none. It still never invents an identity: get_project_id() remains read-only and
minting stays with the CLI commands a user explicitly invoked.
Two existing tests asserted the old contract and are updated rather than deleted,
one of them renamed because its name described the behaviour that changed. A third
test is added pinning the distinction itself. The test asserting that a foreign
application's spans are never annotated is unaffected and still passes: this
changes what our processor stamps, not where it is attached.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
* docs(telemetry): note that a failed project_id lookup also yields empty
Addresses CodeRabbit on #7056. The docstring and inline comment described the
empty string only as an undeclared project, but the except branch sets
project_id to None and so lands on the same empty value. Both are deliberately
indistinguishable - neither yields an id - and saying so matters to anyone
debugging an empty value, since an unreadable pyproject.toml looks identical to
a project that simply declares nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a declaration drive conversational mode
`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.
Resolution rules, applied consistently:
- Structure (enabled, methods, route labels, builtin/internal routes) comes
from `self._definition`, which is the loaded declaration for a declarative
flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
can hold live objects — a configured LLM, a custom BaseLLM, a response_format
model class — that the serializable definition degrades to a config dict or a
`module:qualname` ref. Reading the definition first would silently downgrade
every decorated Python flow. A declaration-built flow has no class config, so
`_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
is consulted before `_create_definition_state`, so returning `ConversationState`
there discarded every field the declaration asked for. It now yields to a
declared state and only supplies the default when nothing else does.
The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.
A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): synthesize the built-in conversational methods for declarations
A declaration carrying `conversational: {}` loaded clean and then ran zero
methods and returned `None`, because the four built-in graph handlers are
inherited from `_ConversationalMixin` and a declaration has nothing to inherit
from. Authors had to name `crewai.experimental.conversational_mixin:_Conversational
Mixin.route_conversation` and three siblings by hand.
`Flow._extend_definition` is a new runtime extension hook, called once
`_definition` is resolved and before methods are bound. The conversational
mixin overrides it to fill in `route_conversation`, `converse_turn`,
`end_conversation` and `answer_from_history_turn` when they are missing, using
the same code refs the DSL projection already emits so a declaration and a
class projection of the same flow produce identical method definitions.
Synthesis is deliberately a runtime concern, not a contract one:
`FlowDefinition` stays independent of the authoring layer and of the engine,
as `test_flow_definition_contract_is_dsl_agnostic` requires, and a loaded
declaration still serializes back to exactly what its author wrote.
Route descriptions are now carried by the contract. The DSL projects a handler
docstring's first line into `FlowMethodDefinition.description`, and the router
catalog reads that before falling back to the live docstring. This also fixes
a real defect: for a declarative flow `getattr(type(self), handler_name, None)`
is `None`, and the old code read `None.__doc__` — so the router LLM was told a
route's description was "The type of the None singleton."
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): let an agent or crew handler reply in a conversation (#7034)
`handle_turn` promotes a handler's return value to the assistant message when
the handler did not append one itself, but the check required `isinstance(result,
str)`. Declarative `agent` and `crew` actions return `LiteAgentOutput` and
`CrewOutput`, whose text lives on `.raw` — so the most natural declarative
handler was exactly the one whose reply never reached the transcript.
`_is_public_turn_result` now unwraps `.raw` before deciding, matching
`_stringify_result`, which already did. The routing-artefact guards are applied
to the unwrapped text, so an output echoing a route label or this turn's intent
is still not promoted.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): keep privately recorded agent results out of the transcript
Unwrapping `.raw` in `_is_public_turn_result` made the end-of-turn fallback
promote `LiteAgentOutput` / `CrewOutput` objects that the handler had already
recorded via `append_agent_result` with the default private visibility. That
call does not set `_assistant_reply_appended`, so the fallback republished the
very object the handler asked to keep private — defeating
`visible_agent_outputs`.
Reproduced against `main` for contrast: no leak before the unwrap, leak after.
`append_agent_result` now remembers the object it recorded for the duration of
the turn, and the fallback skips anything already routed that way. The check is
identity-based on purpose: a handler that records scratch work privately and
then returns a user-facing summary still gets that summary promoted, which a
simple "handler already handled it" flag would have broken.
Found by Cursor Bugbot on #7033.
* feat(flow): mark a declarative chat flow conversational on the instance
A declaration enables chat through `conversational.enabled`, without the
`conversational = True` class attribute. Callers outside this package
capability-check that attribute -- the AG-UI serving guide states it as a
requirement -- so it disagreed with `_is_conversational_enabled()` and a
declarative conversational flow looked non-conversational from outside.
`_extend_definition` now sets it on the instance when the definition enables
chat. Instance-only on purpose: the DSL projection reads the attribute off the
*class* to decide whether to emit a conversational block, so setting it there
would make every later subclass look conversational.
Verified on a real declarative flow: `conversational` and `stream_turn` both
now satisfy the documented capability check, while `Flow.conversational` and
any later subclass stay False.
* refactor(flow): derive routing-artefact labels from the effective routes
`_is_public_turn_result` matched a literal set of route labels, duplicating
knowledge that `_effective_builtin_routes()` already owns. A declaration that
adds a builtin route was not covered, so a handler echoing that label could be
promoted into the transcript -- the same class of divergence already fixed for
`route_turn`.
Verified the derived set is byte-identical to the old literal one for a
class-based flow, so this is a pure generalization: `conversation` and
`route_to_flow` stay explicit because neither is a route.
Also replaces a tuple-index lambda in the chat REPL test with a named
`input_fn`; it relied on tuple evaluation order and on the list being mutated
before its length was read.
Both found by CodeRabbit on #7033.
* docs(flow): document declarative conversational flows
The authoring skill told LLM authors "use top-level `conversational` only when
the user asks for a chat flow" while documenting none of its 19 fields — there
was no ModelSpec for either conversational model, so the API reference appendix
skipped them entirely.
- Adds both conversational models to the skill reference, with field
descriptions, and registers them under the existing `conversational` skip so
`skills(skips=["conversational"])` still suppresses the whole block.
- Adds authoring rules: do not declare the built-in graph, do not name a
handler after the route it listens to, do not declare state unless it needs
extra fields, and give every route handler a description.
- Documents the declarative form in the conversational-flows guide across en,
ar, ko and pt-BR, including what is supplied automatically, how to run it,
and what a declaration cannot express (live LLM objects, a response_format
class, route_turn overrides).
- `crewai run` on a conversational declaration now says it has no chat loop and
points at handle_turn/chat, instead of quietly running a single turn and
exiting. It fails closed: a flow that cannot be inspected runs normally.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): render the conversational router section in the skill
Both conversational models shared one `Conversational` section, and the
template renders only the first model of a non-union section. The router's
fields were therefore dropped from the API reference and the generated link to
them pointed at a heading that did not exist.
Also softens the built-in-handler rule: `_extend_definition` keeps an
author-supplied entry and the guide documents that override, so the skill
should say to omit those handlers by default rather than never declare them.
Adds regression tests for both sections rendering, for every field of both
models appearing, and for `skips=["conversational"]` suppressing both.
Both found by CodeRabbit on #7035.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(flow): correct the route-description rule in the authoring skill
The rule said every route handler must define `description`, but
`conversational.router.route_descriptions` is the higher-precedence source --
`_build_route_catalog` checks the overrides before falling back to the method
description. Either one describes a route; the rule now says so, and says what
happens when a route has neither.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a declaration drive conversational mode
`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.
Resolution rules, applied consistently:
- Structure (enabled, methods, route labels, builtin/internal routes) comes
from `self._definition`, which is the loaded declaration for a declarative
flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
can hold live objects — a configured LLM, a custom BaseLLM, a response_format
model class — that the serializable definition degrades to a config dict or a
`module:qualname` ref. Reading the definition first would silently downgrade
every decorated Python flow. A declaration-built flow has no class config, so
`_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
is consulted before `_create_definition_state`, so returning `ConversationState`
there discarded every field the declaration asked for. It now yields to a
declared state and only supplies the default when nothing else does.
The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.
A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): synthesize the built-in conversational methods for declarations
A declaration carrying `conversational: {}` loaded clean and then ran zero
methods and returned `None`, because the four built-in graph handlers are
inherited from `_ConversationalMixin` and a declaration has nothing to inherit
from. Authors had to name `crewai.experimental.conversational_mixin:_Conversational
Mixin.route_conversation` and three siblings by hand.
`Flow._extend_definition` is a new runtime extension hook, called once
`_definition` is resolved and before methods are bound. The conversational
mixin overrides it to fill in `route_conversation`, `converse_turn`,
`end_conversation` and `answer_from_history_turn` when they are missing, using
the same code refs the DSL projection already emits so a declaration and a
class projection of the same flow produce identical method definitions.
Synthesis is deliberately a runtime concern, not a contract one:
`FlowDefinition` stays independent of the authoring layer and of the engine,
as `test_flow_definition_contract_is_dsl_agnostic` requires, and a loaded
declaration still serializes back to exactly what its author wrote.
Route descriptions are now carried by the contract. The DSL projects a handler
docstring's first line into `FlowMethodDefinition.description`, and the router
catalog reads that before falling back to the live docstring. This also fixes
a real defect: for a declarative flow `getattr(type(self), handler_name, None)`
is `None`, and the old code read `None.__doc__` — so the router LLM was told a
route's description was "The type of the None singleton."
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): let an agent or crew handler reply in a conversation (#7034)
`handle_turn` promotes a handler's return value to the assistant message when
the handler did not append one itself, but the check required `isinstance(result,
str)`. Declarative `agent` and `crew` actions return `LiteAgentOutput` and
`CrewOutput`, whose text lives on `.raw` — so the most natural declarative
handler was exactly the one whose reply never reached the transcript.
`_is_public_turn_result` now unwraps `.raw` before deciding, matching
`_stringify_result`, which already did. The routing-artefact guards are applied
to the unwrapped text, so an output echoing a route label or this turn's intent
is still not promoted.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): keep privately recorded agent results out of the transcript
Unwrapping `.raw` in `_is_public_turn_result` made the end-of-turn fallback
promote `LiteAgentOutput` / `CrewOutput` objects that the handler had already
recorded via `append_agent_result` with the default private visibility. That
call does not set `_assistant_reply_appended`, so the fallback republished the
very object the handler asked to keep private — defeating
`visible_agent_outputs`.
Reproduced against `main` for contrast: no leak before the unwrap, leak after.
`append_agent_result` now remembers the object it recorded for the duration of
the turn, and the fallback skips anything already routed that way. The check is
identity-based on purpose: a handler that records scratch work privately and
then returns a user-facing summary still gets that summary promoted, which a
simple "handler already handled it" flag would have broken.
Found by Cursor Bugbot on #7033.
* feat(flow): mark a declarative chat flow conversational on the instance
A declaration enables chat through `conversational.enabled`, without the
`conversational = True` class attribute. Callers outside this package
capability-check that attribute -- the AG-UI serving guide states it as a
requirement -- so it disagreed with `_is_conversational_enabled()` and a
declarative conversational flow looked non-conversational from outside.
`_extend_definition` now sets it on the instance when the definition enables
chat. Instance-only on purpose: the DSL projection reads the attribute off the
*class* to decide whether to emit a conversational block, so setting it there
would make every later subclass look conversational.
Verified on a real declarative flow: `conversational` and `stream_turn` both
now satisfy the documented capability check, while `Flow.conversational` and
any later subclass stay False.
* refactor(flow): derive routing-artefact labels from the effective routes
`_is_public_turn_result` matched a literal set of route labels, duplicating
knowledge that `_effective_builtin_routes()` already owns. A declaration that
adds a builtin route was not covered, so a handler echoing that label could be
promoted into the transcript -- the same class of divergence already fixed for
`route_turn`.
Verified the derived set is byte-identical to the old literal one for a
class-based flow, so this is a pure generalization: `conversation` and
`route_to_flow` stay explicit because neither is a route.
Also replaces a tuple-index lambda in the chat REPL test with a named
`input_fn`; it relied on tuple evaluation order and on the list being mutated
before its length was read.
Both found by CodeRabbit on #7033.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a declaration drive conversational mode
`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.
Resolution rules, applied consistently:
- Structure (enabled, methods, route labels, builtin/internal routes) comes
from `self._definition`, which is the loaded declaration for a declarative
flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
can hold live objects — a configured LLM, a custom BaseLLM, a response_format
model class — that the serializable definition degrades to a config dict or a
`module:qualname` ref. Reading the definition first would silently downgrade
every decorated Python flow. A declaration-built flow has no class config, so
`_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
is consulted before `_create_definition_state`, so returning `ConversationState`
there discarded every field the declaration asked for. It now yields to a
declared state and only supplies the default when nothing else does.
The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.
A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
Connection events used the raw endpoint as `server_name`, so traces
titled the row with the full Bright Data URL including query params.
HTTP and SSE `_get_server_info` now emit the hostname and keep the
full URL on `server_url`.
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: close the agent scope on every failed attempt
`_check_execution_error` only emitted `AgentExecutionErrorEvent` once the
retries were exhausted, but each retry re-enters `execute_task` and opens a
new `agent_execution_started` scope. The scopes left open were then popped
by the next ending event, so `task_failed` closed an agent scope instead of
`task_started` and the task never got its own terminal pairing. Passthrough
exceptions keep bubbling untouched, since a HITL pause must leave its scope
open for the resume.
* fix: return the retried result instead of finalizing it twice
A retry reenters `execute_task`, whose own `_finalize_task_execution`
already emitted `AgentExecutionCompletedEvent`, and the outer frame then
finalized the same result again. The duplicate used to be absorbed by the
`agent_execution_started` scope that a failed attempt left open, so
closing every attempt exposed it: the extra completed event popped
`task_started`, and the task and crew ends paired with the wrong scopes.
---------
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
`Telemetry.tool_usage_error` has always accepted `tool_name` and writes the
attribute when it is truthy, but no caller passed it, so every `Tool Usage Error`
span landed with an empty name. Per-tool error rates were therefore not
computable at all: named tools read zero errors while the unnamed bucket held
all of them.
Passes the tool name at the four sites where the tool is known. They are the same
two failures in both execution modes - usage-limit and execution-error, each once
in the sync `_use` and once in the async `_ause` - so the fix is symmetric across
the sync/async matrix rather than four unrelated edits.
Leaves the fifth site in `_tool_calling` unattributed on purpose, with a comment
saying why: that path is a tool-call PARSING failure, so the tool the model wanted
was never identified. The only string available is the raw, unparsed model output,
and putting that into a metrics dimension would give it unbounded cardinality. An
empty name is the honest representation there.
Adds tests over the full matrix, including the parsing case pinning the opposite
expectation. Verified they fail against the unpatched module: the four attribution
tests fail and the parsing test still passes, which is the intended split.
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
An MCP tool's name is derived from the server URL, so nothing on the
resolved tool records which reference it was requested by. `MCPNativeTool`
now keeps that reference as `server_reference` when `_resolve_amp` built
it, leaving servers requested by URL untouched. Hooks can then attribute a
tool call to the server the user actually selected.
* fix(OSS-128): split oversized messages before context chunking
Normalize single messages that exceed the token budget before boundary
chunking so summarization LLM calls do not replay the same context error.
Reserve summarization prompt overhead from the chunk budget for large
context windows.
* refactor(OSS-128): inline message content text extraction as lambda
* refactor(OSS-128): restore _message_content_text as a function
Revert the lambda assignment to satisfy ruff E731 and keep the helper
readable alongside the LLMMessage content shape.
* refactor(OSS-128): drop summarization prompt overhead from chunk budget
Use the full context window size for message chunking instead of
subtracting a fixed prompt overhead.
* fix(OSS-128): preserve LLMMessage fields when splitting oversized content
Copy the original message attributes into each sub-message and only
replace content when expanding oversized entries for chunking.
* test(OSS-128): assert rendered summarization requests fit raw context
Verify each chunked summarization payload, including system and
instruction overhead, stays within the model limit implied by the
85% context window usage ratio.
* fix(tools): pin SSRF checks to each redirect hop and peer IP
validate_url only inspected the original URL string, so scraping fetches
could follow a 302 to an internal address or rebind DNS between check and
connect. Route safe_get through an HTTPAdapter that re-validates every hop
and connects to the authorised sockaddr, and let FORCE_SAFE_PATHS ignore a
tenant-supplied escape hatch on managed workers.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* Potential fix for pull request finding 'Except block handles 'BaseException''
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* test(azure): use a plain stand-in for Responses API delegate mocks
MagicMock instances are not reliably stored on Pydantic PrivateAttr via
BaseLLM.__setattr__, which left _responses_delegate as None and failed
last_response_id / reset_chain assertions on CI.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix(agent): recognize OpenAI Responses API tool-call shape in native tool loop
is_tool_call_list() and extract_tool_call_info() only recognized
Chat-Completions-style ({"function": {...}}), Anthropic-style
({"name", "input"}), and Gemini-style tool-call shapes. The Responses
API's function_call output items are flat dicts shaped
{"id", "name", "arguments"} with no nested "function" key and no
"input" key, so they matched none of the checks.
This caused is_tool_call_list() to misclassify a genuine tool call as
a plain text answer, so the native tool loop returned the raw
tool-call list as the agent's final output instead of executing the
tool. Even after recognizing the shape, extract_tool_call_info() would
have passed an empty arguments dict, since it only read "input" for
the dict fallback.
Verified against LLM(api="responses") with tools attached: the agent
now correctly executes the tool with the parsed arguments instead of
returning the unexecuted tool-call JSON as its answer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* test(agent_utils): cover OpenAI Responses API tool-call shape
Regression tests for is_tool_call_list() and extract_tool_call_info()
against the Responses API's flat {"id", "name", "arguments"} dict
shape, alongside existing Chat-Completions and Bedrock/Anthropic
shapes to confirm no regression there.
Confirmed these tests fail against the pre-fix version of
agent_utils.py (3 failures matching exactly the Responses API cases)
and pass against the fix in 37087b7e1.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(llms/openai): convert Chat-Completions tool messages to Responses API input items
_prepare_responses_params() passed non-system messages straight through
as the Responses API "input" array without converting them. That's
fine for plain user/assistant text (matches the API's lenient "easy
input message" shape), but Chat-Completions-style assistant messages
carrying "tool_calls" and "tool"-role messages have no equivalent
shape in the Responses API - it expects standalone "function_call" and
"function_call_output" input items instead. Sending the raw
Chat-Completions shapes gets rejected with a 400 (union-type
validation failure against every Responses API input item variant).
This broke every multi-turn tool-calling conversation over
api="responses" that doesn't rely on auto_chain/previous_response_id
(i.e. the common case: resending full history each turn instead of
referencing server-side state).
Added _convert_message_to_responses_input_items() to translate:
- assistant + tool_calls -> one function_call item per call
- tool role -> function_call_output item
- everything else -> passed through unchanged
Verified against a real multi-turn tool-calling run: the agent now
completes the full conversation and returns the actual extracted
answer instead of erroring on the second turn.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(llms/openai): fix mypy list-item type error in message conversion
_convert_message_to_responses_input_items() was annotated to return
list[dict[str, Any]], but the passthrough branch returns the LLMMessage
argument unchanged. Lists are invariant in mypy, so a bare LLMMessage
(TypedDict) isn't assignable into a list[dict[str, Any]] return - this
was flagged by CI's type-checker job across all Python versions.
Widened the return type (and the local list built in the tool_calls
branch) to list[dict[str, Any] | LLMMessage], matching what the
function actually returns.
Confirmed with a local mypy run and the full openai/agent_utils test
suites (176 passed).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: harden Responses API tool call conversion
* style: format Responses API conversion
* fix: upgrade nltk to 3.10.0 to resolve path traversal vulnerabilities
Upgrades nltk from 3.9.4 to 3.10.0 which fixes three path traversal
vulnerabilities (GHSA-qvv7-cg9c-w4x3, GHSA-fg7f-2386-8897,
GHSA-xh95-f55m-82fw) that were causing the pip-audit CI job to fail.
Also removes the now-obsolete PYSEC-2026-597 ignore entry from the
vulnerability-scan workflow since the vulnerability is fixed in 3.10.0.
* fix: bump aiohttp to >=3.14.2 and cryptography to >=50.0.0 to fix pip-audit vulns
Co-authored-by: theCyberTech <84775494+theCyberTech@users.noreply.github.com>
* fix: default empty Responses tool-call arguments to {}
Missing or empty Chat Completions tool-call arguments were forwarded
as an empty string, which is invalid JSON for Responses API
function_call items and breaks parse_tool_call_args. Normalize to
"{}" and cover the case in regression tests. Also assert a supplied
tool-call id is preserved as call_id.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* Removing fallback from call_id
* Removing fallback from call_id
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: João Moura <joaomdmoura@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
* fix(core): record the running release on every emitted span
Nine of twenty-four span kinds never recorded crewai_version, including the
two highest-volume ones - Task Created and Task Execution - plus Human
Feedback, Flow Plotting, and the whole deployment family. add_crew_attributes
writes crew_key, crew_id and crew_fingerprint but never the release, so any
question filtered by version silently returned nothing for those spans and
per-release comparison was blind to them.
Add it at the fourteen sites that were missing it across both emitters,
matching each module's existing convention: version("crewai") in crewai,
get_crewai_version() with the file's local-import pattern in crewai_core.
Guarded by a test that parses both modules and fails when any method creates
a span without recording the release, so a span added later cannot
reintroduce the gap. Verified non-vacuous: removing the attribute from one
span makes it fail and names that method.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* test(core): count spans against release attributes, and cover both emitters
Two review findings, both real.
The guard only asked whether a method mentioned crewai_version anywhere, so a
method opening two spans while recording the release on one of them passed.
task_started is exactly that shape. It now counts start_span calls against
_add_attribute(..., "crewai_version", ...) calls and fails when the second is
smaller, naming the method and both counts. Verified non-vacuous: removing the
attribute from Task Execution alone - which the previous version accepted -
now fails with "task_started (2 span(s), 1 version attribute(s))".
The behavioural cases only ever ran against crewai's emitter, because _emit
builds that singleton, so the five changed crewai_core methods had no
behavioural coverage at all. Added a parametrized case over all eight spans
crewai_core emits, using the fixture already in that file - covering the three
that already recorded the release as well, so a regression there is caught too.
Also removed the function-local `import crewai`: the paths now come from
inspect.getfile() on the two classes, which is both consistent with the file's
existing import style and more direct than guessing the module layout.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(execution): introduce execution context management with UUID support
- Added `execution.py` to manage execution UUIDs for tracking nested execution contexts.
- Implemented `begin_execution` and `end_execution` functions to handle the lifecycle of execution contexts.
- Updated `Crew` and `Flow` classes to utilize the new execution context management, ensuring proper tracking during execution.
- Added tests for execution UUID creation, inheritance, and lifecycle management to ensure functionality and correctness.
* feat(execution): enhance execution context management in Crew class
- Introduced `begin_execution` and `end_execution` calls in the `Crew` class to manage execution tokens effectively.
- Updated the `akickoff` method to ensure proper lifecycle handling of execution contexts.
- Added tests to verify the creation and clearing of execution UUIDs during the `akickoff` process, ensuring correct behavior in various scenarios.
* feat(execution): add execution_uuid to PendingFeedbackContext for flow resumption
- Introduced `execution_uuid` to the `PendingFeedbackContext` class to maintain the UUID across flow pauses and resumes, ensuring traceability of execution contexts.
- Updated the `Flow` class to utilize the new `execution_uuid` during execution management, enhancing the handling of paused flows.
- Added tests to verify that the execution UUID is correctly persisted and restored during flow operations, ensuring consistent behavior across sessions.
* refactor(execution): streamline execution UUID management and update tests
- Removed the `ensure_execution_uuid` function to simplify UUID handling, consolidating logic into `begin_execution` and `end_execution`.
- Updated the `clear_execution_uuid` function to ensure it correctly restores previous UUIDs using context tokens.
- Modified tests to reflect changes in execution UUID management, ensuring proper creation, inheritance, and clearing of UUIDs during execution contexts.
- Enhanced the `PendingFeedbackContext` documentation to clarify the handling of `execution_uuid` for pending rows.
* feat(telemetry): record what kind of exception ended a flow
Flow failures are visible but undiagnosable. Live data shows roughly 17% of
flows ending in outcome=failed, and 72% of AgentExecutor failures completing
in under 200ms - far too fast to be an LLM call - but nothing records what
the failure actually is, so there is no way to tell a real defect from a
user pressing Ctrl-C.
Record the exception's class name as error_type on Flow Completed and Flow
Method Failed. The class name only: str(error) is never read, because it
routinely carries prompts, model output, file paths and credentials. The
isidentifier() check is the allowlist that enforces it - any message text
reaching that argument carries a space or punctuation and is dropped - and
it lives inside Telemetry rather than at the call site so a future caller
cannot bypass it. Method names and flow state remain unrecorded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* docs(frontend): point the frontend guides at their edge paths
The frontend guides added in #6686 exist only under edge - they are not in
any frozen version snapshot - but their 80 internal links use the bare
/en/guides/frontend/... form, which resolves against the released versions
where those pages do not exist. mint broken-links fails on every one of
them, which blocks every open PR, not only the one that added them.
Use the /edge/en/... form the repo already uses for other edge-only pages
(concepts/streaming, learn/execution-boundary-hooks). Anchors are preserved.
No page content changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: add Frontend guides section (CopilotKit + AG-UI)
Add a Frontend sub-group under Guides documenting how to build user
interfaces for CrewAI Crews and Flows with CopilotKit over the AG-UI
protocol. Pages: overview, generative UI, tool-based generative UI,
agentic generative UI, human-in-the-loop, shared state, frontend
actions, predictive state updates, and channels.
* docs: mirror Frontend guides into v1.15.5 (Latest)
Also register the Frontend sub-group and pages under the default
v1.15.5 version so the section is visible without switching to Edge.
* docs(frontend): address audit — correct APIs and claims
- Use useRenderTool for display-only tool rendering (was useFrontendTool)
- Correct state 'auto-streams' claims: snapshot at step boundaries,
document copilotkit_emit_state for mid-step progress
- Fix setState usage to spread full state (replace, not merge)
- Add tool description to the frontend-action example
- Rewrite Channels with the real @copilotkit/channels createBot API
(Slack + Discord adapters); drop unsupported platform claims
- Note self-hosted vs managed CopilotKit paths and pin package versions
* docs(frontend): remove versions callout from overview
* docs(frontend): drop package-generation framing from emit_state note
* docs(frontend): add generative UI spectrum (A2UI, reasoning) + Conversational Flows
Rewrite generative-ui as the controlled/declarative/open-ended spectrum;
add A2UI (declarative), Reasoning (controlled), and a Conversational Flows
page; add a backend-tools section to tool-based; note the three execution
shapes in the overview.
* docs(frontend): address review — edge-only, attribute access, safe defaults
Remove the docs/v1.15.5 mirror (versioned snapshots are cut from edge by
the release tooling; the docs-snapshots CI guard rejects manual docs/v*
writes). Use attribute access on the LiteLLM message in shared-state,
guard setState against undefined agent/recipe, and use
Field(default_factory=list) for the agent-state list.
* feat(tracing): record when a trace batch is shared with amp
A trace batch is sent to AMP on every traced run, but nothing on the OSS
side recorded that it happened, so a project's first touch with AMP was
invisible in telemetry. Emit a Feature Usage span on successful
finalization: tracing:ephemeral_sent before the user has an account,
tracing:authenticated_sent after.
Emitted on finalize rather than init because a batch that initializes and
then fails to send never lands in AMP. Reading is_ephemeral from batch
state at finalize also means a run that starts authenticated and falls
back to ephemeral on a 401 reports ephemeral, which is what happened.
Rides the existing Feature Usage span, so no new pipeline is needed to
read it, and it carries project_id and coding_agent for free through the
common attributes processor. Records only that a batch arrived - never
trace contents, crew or flow names, inputs, or outputs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* docs(telemetry): disclose the attributes the trace signal carries
The new row said only that a batch arrived was recorded, which reads as
excluding the common attributes every span carries. project_id and the
coding assistant are disclosed in the Execution Environment row, but
"only" actively contradicted that. Name them here too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deployments were only countable through two span types that no aggregation
reads, and cli_usage:deploy counted the TUI button rather than deployments.
Emit deploy:created and deploy:pushed alongside the existing spans so a
deployment is countable from the feature-usage aggregation no matter how it
was started, and tag Create Crew Deployment / Start Deployment with
source=cli|tui so the two origins stay distinguishable.
Separately, the TUI's `t` and `d` key bindings dispatch straight to
action_view_traces / action_deploy_crew, which never recorded anything -
only on_button_pressed did. Every keyboard-driven trace view and deploy was
therefore invisible. Move the recording into the actions, which both input
paths funnel through, and past the completed guard so a mid-run keypress
that does nothing is not counted as usage.
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(telemetry): stop a failed turn from marking the next one failed
A conversational session that opts out of deferred finalization ends each
turn with its own FlowFailedEvent, emitted inside kickoff() before
handle_turn() emits ConversationTurnFailedEvent. The flag was therefore set
after the run that owned it had already cleared it, survived on the
instance, and reported the next healthy turn as failed.
Gate the flag on the run still having its start stamp: a deferring session
keeps it (no per-turn terminal event), so it still reports a failed turn at
session end.
Also aligns the Flow Lifecycle Signals privacy row with the rest of the
telemetry table, which qualifies every user-authored field it records with
"should not include personal info", and fixes a telemetry test that built
InputResponse with an unsupported `value` keyword - ask() swallowed the
TypeError, so the test asserted the signals while exercising the
provider-error path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(telemetry): cover the streamed turn emitter of the failure flag
stream_turn() is the second emitter of ConversationTurnFailedEvent and
leaks the same flag as handle_turn(). Both regression tests fail on
77c68bd with ['failed', 'failed'].
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): report flow outcome and human-in-the-loop signals
A flow reported only that it started. FlowFinishedEvent, FlowFailedEvent,
MethodExecutionFailedEvent, MethodExecutionPausedEvent and FlowPausedEvent all
reached the console formatter and stopped there, and FlowInputRequestedEvent,
FlowInputReceivedEvent and ConversationTurnFailedEvent had no listener at all -
so success rate, failure rate and every HITL pause were unmeasurable.
Adds flow:completed, flow:failed, flow:method_failed, flow:paused,
flow:hitl_paused, flow:input_requested, flow:input_received and
flow:conversation_turn_failed as feature-usage spans, which the existing
feature-usage aggregation already reads.
Deliberately does not hold the Flow Execution span open to measure duration:
flow_executions_daily_target counts those spans at start, so a run that never
finishes would disappear from the count entirely. Duration needs its own span.
Counts only - flow names, method names, error text and flow state are never
recorded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* feat(flow): record how long a flow ran
Adds a Flow Completed span carrying flow_name, duration_ms and outcome,
emitted when a flow finishes or fails. Elapsed time comes from a monotonic
stamp taken at flow start and cleared on use.
Kept separate from the Flow Execution span rather than holding that one open:
it is emitted and closed at start and the daily aggregate counts it, so
holding it would drop every run that is killed or crashes from the execution
count. A killed run now simply has no Flow Completed row, and the count is
unaffected.
Elapsed time is an explicit duration_ms attribute rather than the span's own
duration, which the ingestion pipeline stores as a suffixed string
("0.0000184s") that downstream aggregation parses to zero.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* feat(flow): tag flow origin and report resumed runs
Two gaps found while testing the pause/resume path end to end.
Resumed runs were invisible. There is no resume event: a restored run re-enters
through kickoff(), so it looked identical to a fresh start. flow:resumed is
derived from _is_execution_resuming at flow start, which makes
flow:paused - flow:resumed the abandonment rate.
Flow counts are dominated by CrewAI's own AgentExecutor, which is itself a Flow
and runs once per agent execution - it is the top flow in the warehouse by a
wide margin. Nothing distinguished it from a user's flows except guessing at the
name. Both Flow Execution and Flow Completed now carry origin: "internal" when
the flow class is defined under crewai.*, "user" otherwise. Tagging only the new
span would have left the existing daily count unsplittable.
Both span methods take origin with a default, so their signatures stay
backward compatible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(flow): scope outcome and resume signals to user flows
Two findings from review, both confirmed against the code.
Outcome features counted CrewAI's own flows. The agent executor, memory
encoding and memory recall are all Flows and all set suppress_flow_events;
they run far more often than anything a user wrote, so flow:completed,
flow:failed and flow:method_failed were mostly bookkeeping. Those three are now
emitted only for flows the caller wrote. Internal outcomes are still recorded
on the Flow Completed span, which carries origin.
flow:resumed counted checkpoint restores. _is_execution_resuming is set both by
from_pending (a human pause) and by a checkpoint restore that never paused for
anyone, so resumes could exceed pauses and the abandonment rate was unusable.
Keyed off _pending_feedback_context instead, which only from_pending sets.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(flow): declare internal flows instead of inferring them
Three findings from review, all confirmed against the code.
Gating on suppress_flow_events was wrong. That flag asks for console quiet and
is a public field, so a caller who set it on their own flow silently lost
flow:completed, flow:failed and flow:method_failed.
Deciding origin from the defining module was also wrong. Flow.from_declaration()
returns a Flow typed in crewai.flow.flow, so a caller's declarative flow was
reported as one of CrewAI's own - the inversion this split exists to prevent.
Both had the same root cause: the discriminator was inferred. Flow now declares
is_crewai_internal, set on the agent executor and the memory encoding/recall
flows, and one helper serves both origin and the outcome gate.
A failed conversational session was reported as completed. Its session closes
with FlowFinishedEvent whatever happened, so a failed turn produced
flow:conversation_turn_failed and flow:completed together. The turn failure is
now recorded on the flow and read back when the session finishes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* refactor(flow): report flow lifecycle as spans, not feature usage
Flow start, completion, pause and method failure are lifecycle facts, and the
lifecycle is reported as spans everywhere else. Reporting them through
feature usage put them in a table that aggregates on the feature string alone -
it cannot carry origin, duration or outcome, so those signals could never be
split between a user's flows and the ones CrewAI runs for itself.
Adds Flow Paused and Flow Method Failed spans, and a resumed marker on Flow
Execution so a run restored from a pause is not counted as a second fresh
start. Removes the duplicate feature rows for completed, failed, method_failed,
paused and resumed - every one of those facts is now on a span, with more
attached to it than the feature row ever carried.
Feature usage keeps only genuine adoption signals: flow:hitl_paused,
flow:input_requested, flow:input_received and flow:conversation_turn_failed.
Also clears the conversational turn-failure flag on every terminal path. A turn
that failed without deferred finalization ends via FlowFailedEvent, and the flag
left set there marked the next run on that instance as failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* test(flow): update the flow_execution_span caller for the resumed argument
Adding the resumed marker changed a signature that tests/utilities/test_events.py
asserts on exactly, and that assertion was not re-run before pushing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* test(flow): make the checkpoint-restore guard actually guard
The test asserted that flow:resumed was absent from feature usage, but that
signal moved onto the Flow Execution span. The assertion could no longer fail,
so a regression that mis-tagged checkpoint restores as resumes would have gone
unnoticed.
Now asserts the resumed attribute, and waits for the handlers: the manual emit
dispatches asynchronously, so the previous shape also read its result before the
listener had run.
Confirmed it discriminates - keying resumed off _is_execution_resuming again
fails it with [('RestoredFlow', True)] == [('RestoredFlow', False)].
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(telemetry): record the resumed marker as a string
Verified end to end against the live collector and ClickHouse: the pipeline
encodes a boolean attribute as the presence of a vBool key, so false arrives as
the key simply being absent. That is invisible in the schema and easy to read
wrongly - crew_memory is extracted as "the attribute exists" and consequently
reports 1 for 99.8% of crews against a field that defaults to False.
A string leaves nothing to infer. Confirmed in the warehouse: the emitted span
reads resumed = "false".
Adds direct coverage for the attributes each flow span records, including both
resumed values, and resets the Telemetry singleton in the helper so more than
one span method can be exercised per session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* feat: bump versions to 1.15.15
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): report flow outcome and human-in-the-loop signals
A flow reported only that it started. FlowFinishedEvent, FlowFailedEvent,
MethodExecutionFailedEvent, MethodExecutionPausedEvent and FlowPausedEvent all
reached the console formatter and stopped there, and FlowInputRequestedEvent,
FlowInputReceivedEvent and ConversationTurnFailedEvent had no listener at all -
so success rate, failure rate and every HITL pause were unmeasurable.
Adds flow:completed, flow:failed, flow:method_failed, flow:paused,
flow:hitl_paused, flow:input_requested, flow:input_received and
flow:conversation_turn_failed as feature-usage spans, which the existing
feature-usage aggregation already reads.
Deliberately does not hold the Flow Execution span open to measure duration:
flow_executions_daily_target counts those spans at start, so a run that never
finishes would disappear from the count entirely. Duration needs its own span.
Counts only - flow names, method names, error text and flow state are never
recorded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* feat(flow): record how long a flow ran
Adds a Flow Completed span carrying flow_name, duration_ms and outcome,
emitted when a flow finishes or fails. Elapsed time comes from a monotonic
stamp taken at flow start and cleared on use.
Kept separate from the Flow Execution span rather than holding that one open:
it is emitted and closed at start and the daily aggregate counts it, so
holding it would drop every run that is killed or crashes from the execution
count. A killed run now simply has no Flow Completed row, and the count is
unaffected.
Elapsed time is an explicit duration_ms attribute rather than the span's own
duration, which the ingestion pipeline stores as a suffixed string
("0.0000184s") that downstream aggregation parses to zero.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* feat(flow): tag flow origin and report resumed runs
Two gaps found while testing the pause/resume path end to end.
Resumed runs were invisible. There is no resume event: a restored run re-enters
through kickoff(), so it looked identical to a fresh start. flow:resumed is
derived from _is_execution_resuming at flow start, which makes
flow:paused - flow:resumed the abandonment rate.
Flow counts are dominated by CrewAI's own AgentExecutor, which is itself a Flow
and runs once per agent execution - it is the top flow in the warehouse by a
wide margin. Nothing distinguished it from a user's flows except guessing at the
name. Both Flow Execution and Flow Completed now carry origin: "internal" when
the flow class is defined under crewai.*, "user" otherwise. Tagging only the new
span would have left the existing daily count unsplittable.
Both span methods take origin with a default, so their signatures stay
backward compatible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(flow): scope outcome and resume signals to user flows
Two findings from review, both confirmed against the code.
Outcome features counted CrewAI's own flows. The agent executor, memory
encoding and memory recall are all Flows and all set suppress_flow_events;
they run far more often than anything a user wrote, so flow:completed,
flow:failed and flow:method_failed were mostly bookkeeping. Those three are now
emitted only for flows the caller wrote. Internal outcomes are still recorded
on the Flow Completed span, which carries origin.
flow:resumed counted checkpoint restores. _is_execution_resuming is set both by
from_pending (a human pause) and by a checkpoint restore that never paused for
anyone, so resumes could exceed pauses and the abandonment rate was unusable.
Keyed off _pending_feedback_context instead, which only from_pending sets.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(flow): declare internal flows instead of inferring them
Three findings from review, all confirmed against the code.
Gating on suppress_flow_events was wrong. That flag asks for console quiet and
is a public field, so a caller who set it on their own flow silently lost
flow:completed, flow:failed and flow:method_failed.
Deciding origin from the defining module was also wrong. Flow.from_declaration()
returns a Flow typed in crewai.flow.flow, so a caller's declarative flow was
reported as one of CrewAI's own - the inversion this split exists to prevent.
Both had the same root cause: the discriminator was inferred. Flow now declares
is_crewai_internal, set on the agent executor and the memory encoding/recall
flows, and one helper serves both origin and the outcome gate.
A failed conversational session was reported as completed. Its session closes
with FlowFinishedEvent whatever happened, so a failed turn produced
flow:conversation_turn_failed and flow:completed together. The turn failure is
now recorded on the flow and read back when the session finishes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* refactor(flow): report flow lifecycle as spans, not feature usage
Flow start, completion, pause and method failure are lifecycle facts, and the
lifecycle is reported as spans everywhere else. Reporting them through
feature usage put them in a table that aggregates on the feature string alone -
it cannot carry origin, duration or outcome, so those signals could never be
split between a user's flows and the ones CrewAI runs for itself.
Adds Flow Paused and Flow Method Failed spans, and a resumed marker on Flow
Execution so a run restored from a pause is not counted as a second fresh
start. Removes the duplicate feature rows for completed, failed, method_failed,
paused and resumed - every one of those facts is now on a span, with more
attached to it than the feature row ever carried.
Feature usage keeps only genuine adoption signals: flow:hitl_paused,
flow:input_requested, flow:input_received and flow:conversation_turn_failed.
Also clears the conversational turn-failure flag on every terminal path. A turn
that failed without deferred finalization ends via FlowFailedEvent, and the flag
left set there marked the next run on that instance as failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* test(flow): update the flow_execution_span caller for the resumed argument
Adding the resumed marker changed a signature that tests/utilities/test_events.py
asserts on exactly, and that assertion was not re-run before pushing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* test(flow): make the checkpoint-restore guard actually guard
The test asserted that flow:resumed was absent from feature usage, but that
signal moved onto the Flow Execution span. The assertion could no longer fail,
so a regression that mis-tagged checkpoint restores as resumes would have gone
unnoticed.
Now asserts the resumed attribute, and waits for the handlers: the manual emit
dispatches asynchronously, so the previous shape also read its result before the
listener had run.
Confirmed it discriminates - keying resumed off _is_execution_resuming again
fails it with [('RestoredFlow', True)] == [('RestoredFlow', False)].
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(telemetry): record the resumed marker as a string
Verified end to end against the live collector and ClickHouse: the pipeline
encodes a boolean attribute as the presence of a vBool key, so false arrives as
the key simply being absent. That is invisible in the schema and easy to read
wrongly - crew_memory is extracted as "the attribute exists" and consequently
reports 1 for 99.8% of crews against a field that defaults to False.
A string leaves nothing to infer. Confirmed in the warehouse: the emitted span
reads resumed = "false".
Adds direct coverage for the attributes each flow span records, including both
resumed values, and resets the Telemetry singleton in the helper so more than
one span method can be exercised per session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: emit FlowStartedEvent when a boundary hook aborts the flow
A HookAborted at EXECUTION_START or INPUT propagated before
`FlowStartedEvent` was emitted, so a policy deny left logs but no
record of the execution. On abort, stamp the state id and open the
flow scope before re-raising: the deny surfaces as a started -> failed
execution while normal runs keep the existing ordering — the started
event carries hook-resolved inputs and `id` rewrites keep redirecting
persistence restoration.
* docs: translate execution-boundary-hooks page to ar, ko, and pt-BR
The English page updated on this branch had never been localized.
Translate it into the three supported locales following
`DOCS_TRANSLATIONS.md` and register the page in each locale's
navigation in `docs/docs.json`. Untranslated link targets (the
step-hooks page and the aborting-an-operation anchor) are omitted
rather than pointed at English, matching the locale navigation
convention.
* refactor: update date injection functionality in agents
- Changed the description of the parameter to clarify that it injects the current date into the agent's prompt instead of tasks.
- Removed the method as it was no longer needed.
- Implemented a new method in the class to handle date injection directly into the prompt.
- Updated tests to ensure the date is correctly injected into the system prompt and user messages based on the flag.
* translations
* nit
* Standardize CLI flags to kebab-case with deprecated snake_case aliases.
Unify active long-option naming across create, train, test, and replay while keeping hidden backward-compatible aliases and documenting the migration in edge docs and AGENTS.md.
* Emit deprecation warnings when snake_case CLI flag aliases are used.
Route hidden legacy flags through separate internal params so warnings fire only when the alias is supplied, and extend CLI tests for create, replay, and --help coverage.
* Merge CLI deprecation warn helpers into warn_deprecated(kind=...).
Replace warn_deprecated_command and warn_deprecated_flag with one helper that accepts kind="command" or kind="flag".
Force torch>=2.13.0 via override-dependencies so the transitive
docling/unstructured stack picks up the CVE-2025-3000 fix, and drop the
now-unnecessary pip-audit ignore. chromadb's CVE-2026-45829 remains
ignored: the upstream fix is merged but not released on PyPI.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* fix(telemetry): stop exporting third-party spans to the collector
set_tracer() installed CrewAI's TracerProvider as the global one, so every
OTel-instrumented library in the host process - HTTP servers, Redis clients,
ORMs - resolved trace.get_tracer() to our provider and exported to CrewAI's
endpoint. A 20M-row sample of the telemetry table found 18,866 distinct
operation names under our serviceName; CrewAI emits 21.
The same wiring lost data in the other direction: when an application had
already installed its own provider, our spans were created by theirs and went
to their collector, so CrewAI received nothing from instrumented processes.
Spans are now created from the private provider in both packages. Deletes
_attach_common_attributes and its WeakSet/lock, whose multi-provider dedupe
guarded a state that can no longer occur.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(telemetry): keep process context on crewai-core spans
Isolating each package to its own TracerProvider removed an accident the CLI
spans depended on: crewai_core.telemetry had no CommonAttributesSpanProcessor,
so its spans only ever carried coding_agent/runtime_context/project_id by
riding the global provider that crewai installed at import.
A differential capture of every span reaching the exporter showed 8 of 54 spans
losing those attributes - Feature Usage (cli_usage:*), Start Deployment,
Template Installed, Create Crew Deployment, Get Crew Logs, Remove Crew,
Deploy Signup Error and Flow Creation.
Moves the marker tables and the detect_* helpers to crewai_core.runtime_env and
the processor plus common_span_attributes() to crewai_core.telemetry, so both
implementations share one source of truth. crewai.telemetry.utils and
crewai.utilities.constants re-export the moved names, so their import paths are
unchanged.
Also fixes a gap that predates the isolation change: a CLI-only process never
imports crewai, so it never reported either attribute. It does now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* fix(core): type test helpers and stop tests reaching the collector
mypy runs over lib/crewai-core/tests (the one test tree not excluded), so the
new test file needed full annotations and a narrowed span.attributes.
Also patches SafeOTLPSpanExporter before Telemetry is constructed and shuts the
provider down afterwards: __init__ wires a BatchSpanProcessor around the real
OTLP exporter, so each test was attempting a live export and leaving its batch
worker thread running.
Corrects the marker-precedence docstring, which named Cursor third when the
table checks it last so that assistants running inside its terminal are not
masked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* style(core): use one import form per module in telemetry tests
Both test modules imported their telemetry module twice - once aliased for the
monkeypatch target and once via from-import for the names. Dropping the alias
in favour of monkeypatch's dotted-string target leaves a single import form and
removes the need to qualify every reference.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
* docs(core): drop comments that restate the code
The TRACER_NAME constants were annotated with what their name and
set_tracer()'s docstring already say, and the test fixtures narrated
provider.shutdown() and the exporter patch at more length than either needed.
Keeps the ones carrying something the code cannot: the resource-attribute
ingestion quirk, why the marker tables moved packages, and the two ordering
traps the fixtures exist to avoid.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASfWmW3RGy4qAQm6s8U9jH
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gitpython 3.1.57 has GHSA-9rj7-rf2p-w77r, GHSA-4gmw-gg2m-w46p,
GHSA-hh9p-6wh2-4mfc, GHSA-wvpp-8hx9-p66j and GHSA-jm78-9fvv-mhgr
(further unguarded git option forwarding / arbitrary file read via
--pathspec-from-file). All are fixed in 3.1.58, which the root
override-dependencies already require; align the crewai-tools github
extra with it and regenerate the lockfile.
* feat(telemetry): split runtime context from coding agent, add project id
The coding-agent field answered two questions at once. A run with no TTY
reported "non_interactive" and an editor's integrated terminal reported
"vscode_terminal", both in the same field as the assistant name, so a run
that never had an assistant to detect was indistinguishable from one
whose assistant we failed to recognize. Together those two values were
the majority of what the field reported.
detect_coding_agent now answers only which assistant, returning "unknown"
when no marker matches. detect_runtime_context answers where the process
runs: ci, serverless, hosted_ide, notebook, container, the editor
terminals, and the interactive/non_interactive fallback. Both ride on
every span, so an assistant running inside CI reports both rather than
one masking the other.
The runtime markers are published platform contracts - CI providers,
container and serverless runtimes, hosted IDEs - so unlike the assistant
table they need no per-tool verification step. Presence is checked; no
value is read. The assistant table is unchanged: its entries still
require a confirmed, session-scoped variable, and the existing guard test
still enforces that.
Spans also carry project_id when the project declares one. It is read
through the read-only accessor, since minting an id belongs to the CLI
commands a user invoked rather than to a library call during execution,
and it is omitted entirely for projects without one. The attributes are
computed once per process and memoized, so the project file is not
re-read for each provider.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: document execution environment telemetry attributes
Adds the execution-environment row to the data table in en, ar, ko and
pt-BR. Covers the assistant and runtime fields this branch splits apart
and the project id, and states that detection reads only whether known
environment variables are set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(telemetry): detect runtime markers by presence, split paas from serverless
Three findings from the CodeRabbit, code-quality and Cursor reviews.
The runtime loop tested truthiness while constants.py documented presence,
so a platform exporting a bare CI= fell through to the TTY fallback and
was mislabelled as an ordinary local run. Presence is now what it says.
The assistant markers keep truthiness deliberately: there an empty value
means the tool set a placeholder rather than claiming the session.
DYNO and WEBSITE_INSTANCE_ID marked Heroku dynos and Azure App Service
instances as serverless, and since serverless is checked first they could
never reach the container label. They move to a paas context, which is
what they are: long-lived containers rather than per-invocation
functions. AWS_EXECUTION_ENV is dropped entirely - it is set on ECS and
EC2 as well as Lambda, and AWS_LAMBDA_FUNCTION_NAME already covers Lambda
without the collision.
The container probe no longer wraps os.path.exists in a try/except.
os.path.exists handles OSError internally and returns False, so the
handler guarded a condition that cannot occur and only hid the intent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(telemetry): widen assistant detection from published marker sets
The table previously covered three assistants because the rest were
unverified. They are documented after all: vercel/detect-agent publishes
a machine-readable detection matrix (agents.json), corroborated by the
proposal in agentsmd/agents.md#136 and by microsoft/vscode#311734.
Adds cline, gemini_cli, augment, opencode, antigravity and junie, plus
CLAUDE_CODE alongside CLAUDECODE. Gemini's marker is confirmed by its own
docs, which state that run_shell_command sets GEMINI_CLI=1 in the
subprocess environment.
Rule 2 excluded several entries those sources list. Goose's
GOOSE_PROVIDER and Copilot's COPILOT_MODEL and COPILOT_GITHUB_TOKEN are
user configuration, and a committed .env carrying one would relabel every
ordinary run - the AIDER_MODEL trap the guard test already pins, now
parametrized over all four. Replit's REPL_ID names a hosted environment
rather than an assistant, so it stays a runtime context. Copilot sets no
session marker at all today; that is an open request upstream.
The new assistants are ordered ahead of Cursor, since CURSOR_* is set for
every integrated terminal and would otherwise mask anything spawned
inside it - the same ordering Codex already needed.
Also adds the proposed cross-vendor AI_AGENT marker as a last resort,
reported as "other". It establishes that an assistant is present without
naming one, and its value is an arbitrary vendor string, so the value is
never read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(deps): raise gitpython and pypdf floors for new advisories
gitpython 3.1.57 carries GHSA-9rj7-rf2p-w77r, GHSA-4gmw-gg2m-w46p,
GHSA-hh9p-6wh2-4mfc, GHSA-wvpp-8hx9-p66j and GHSA-jm78-9fvv-mhgr: further
unguarded git option forwarding in Repo.init, read-tree and git-config,
plus arbitrary file read via --pathspec-from-file. Fixed in 3.1.58.
pypdf 6.14.2 carries GHSA-fwg2-594c-jp42 and GHSA-fp3f-mc75-235c,
unbounded runtime and memory on large content and /ToUnicode streams.
Fixed in 6.15.0.
Both floors were already pinned, so only the versions move. Their
exclude-newer-package cutoffs had to move with them - 3.1.58 landed
2026-08-04 and 6.15.0 on 2026-08-06, both past the existing dates, so the
resolver could not have seen either release.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(telemetry): share assistant precedence with the env-context path
Four findings from the Cursor and CodeRabbit reviews, three of them the
same root cause.
get_env_context restated the precedence the shared table already defines,
so every marker added for telemetry was invisible to it: a session
exposing only CLAUDE_CODE reported claude_code on spans while emitting
DefaultEnvEvent, and an assistant running inside a Cursor terminal
reported that assistant on spans while emitting CursorEnvEvent. It now
walks CODING_AGENT_ENV_MARKERS and maps the three assistants that have an
event class of their own, defaulting the rest to DefaultEnvEvent. A test
now asserts the two paths agree for every marker in the table, so they
cannot drift again.
The generic AI_AGENT marker was documented as presence-only but ran
through the truthiness loop with everything else, so an empty value fell
through to unknown. It moves out of the table and is checked by presence
after it, which also keeps the named markers' truthiness intact.
Azure Functions run on the App Service host and inherit
WEBSITE_INSTANCE_ID, so moving that marker to paas would have relabelled
them. The FUNCTIONS_* markers are checked first to keep them serverless.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(telemetry): stop export assertions depending on test order
test_all_common_attributes_land_on_exported_spans failed in CI with an
IndexError on an empty span list, and only in one shard: the suite runs
with OTEL_SDK_DISABLED set, so TracerProvider hands out no-op tracers and
an export-based assertion sees zero spans rather than a wrong attribute.
It passed only when it happened to run after a test whose fixture flips
the variable, which random ordering decides.
Adds an otel_enabled fixture that sets the variable for the four tests
asserting on exported spans. Three of them predate this branch and had
the same latent dependency - they are fixed here because the new test
made the ordering hit reachable, and leaving them would keep the required
check red.
Verified by running every test in the file individually, all of which
previously exposed the dependency, and the telemetry suite three times
under random ordering.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: preserve provider on LiteLLM-routed models
LiteLLM construction computed the real provider in `__new__` but never
passed it into init, so `BaseLLM` silently defaulted every shared-path
model to `openai`. Infer the provider from a `provider/model` prefix
when none is supplied so groq, cohere, mistral, and the rest report
themselves correctly to callers like the policy engine.
* fix: avoid double-prefixing instructor model strings
With LiteLLM models now carrying a real `provider` while `model` keeps
its `provider/name` form, `InternalInstructor` was building
`groq/groq/...` for `instructor.from_provider`. Skip the prefix when
the model string is already qualified.
* fix: format LiteLLM multimodal content as OpenAI-shaped blocks
Preserving the real provider on the LiteLLM path made
`format_multimodal_content` emit Anthropic-native blocks for
`anthropic/...` models, which LiteLLM rejects. Keep `provider` as the
model identity for policies, but format multimodal blocks with the
OpenAI chat schema when `is_litellm` is set. Expose the formatter helper
on `BaseLLM` so native OpenAI/Azure completions share the same API.
* fix: patch the event-bus singleton in usage event tests
`TestEmitCallCompletedEventPassesUsage` mocked `CrewAIEventsBus.emit`
on the class, but `_emit_call_completed_event` calls the
`crewai_event_bus` singleton. Under CI that left the real bus emitting
and the mock never seeing a call. Patch the singleton where
`base_llm` imports it instead.
* fix: harden llm call-failed event test against VCR fallback
`test_llm_emits_call_failed_event` marked VCR while mocking
`_handle_completion`, and its cassette recorded a successful reply.
When the class-level patch missed under xdist, VCR replayed success
and the expected exception never fired. Patch the instance method and
drop the cassette so the failure path cannot silently succeed.
* fix: patch the event-bus singleton instance in emit mocks
`patch.object(CrewAIEventsBus, "emit")` misses callers when an
instance-level `emit` shadows the class method — common under xdist
after other suites touch the singleton. Patch `crewai_event_bus.emit`
on the shared instance instead across the LLM event mock fixtures.
* Fix Anthropic native provider to include cache tokens in input totals.
Anthropic reports cache read and cache creation separately from input_tokens; fold them into input_tokens and total_tokens so billed usage is not underreported on cached workloads.
* Reconcile unreconciled Anthropic cache tokens in UsageMetrics.
LiteLLM and flow event paths can pass raw Anthropic usage where input_tokens excludes cache counters; fold them into prompt and total tokens without double-counting native provider payloads.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Document UsageMetrics token field semantics for flows and crews.
Clarify that total_tokens is prompt plus completion, breakdown fields are not additive, and Anthropic cache counters are folded into prompt_tokens.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Document Anthropic cache token accounting in LLM provider docs.
Explain how split Anthropic input counters map to UsageMetrics and link to the flows field semantics section.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Restore cache_creation_tokens breakdown in UsageMetrics normalization.
Keep cache writes as a separate breakdown field while they remain folded into prompt_tokens for billed totals.
* Replace cross-page doc links with plain-text section references.
Avoid internal hyperlinks between concept pages for SEO; point readers to section names in prose instead.
* Apply ruff formatting to usage metrics helpers.
* Sync ar, ko, and pt-BR docs for Anthropic usage metrics updates.
Translate UsageMetrics semantics and Anthropic cache token accounting changes in crews, flows, and llms concept pages.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: add DOCS_TRANSLATIONS workflow for locale sync
Document how to git-detect English MDX changes and sync ar, ko, and pt-BR
translations, and reference it from AGENTS.md.
* docs: add fence language tags in DOCS_TRANSLATIONS.md
Mark the checklist and example output blocks with markdown and text
identifiers for correct rendering.
* docs: fix broken README links, TOC, and contribution guidance
Update stale docs/AMP URLs, repair the Learning Resources heading and TOC
nesting, align contribution commands with the monorepo CONTRIBUTING guide,
and clean up small copy issues in examples and FAQ.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* docs: restore tiktoken troubleshooting wording in README
Keep the more explicit install guidance per review feedback.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* feat(tools): add URLReadTool for reading arbitrary URLs
FileReadTool is confined to the local filesystem, so there was no way for
an agent to read a document that lives behind an http(s) URL. Rather than
adding a flag to FileReadTool, this adds a separate tool: granting it
grants network egress to addresses an LLM picks at runtime, and that
should be a deliberate choice rather than a toggle on a filesystem tool.
URLReadTool fetches a URL and returns its content as text. PDF and DOCX
bodies have their text extracted, HTML is stripped to visible text, and
text-shaped types (plain text, Markdown, JSON, XML, YAML, CSV) are
decoded using the charset the server declares. Any other content type is
refused rather than returned as base64, keeping the output text-only.
Requests reuse the existing SSRF protections in security/safe_requests:
validate_url resolves every hostname and rejects private, loopback,
link-local and reserved addresses (covering cloud metadata endpoints),
and safe_get never auto-follows redirects, revalidating each hop and
dropping credentials on cross-origin ones. Resolving before validating
also normalizes encoded forms, so http://2130706433/ is rejected as
127.0.0.1 without needing a string blocklist.
Adds safe_get_bounded on top of that, which streams the body and
abandons it once it crosses max_bytes. The cap counts decoded bytes,
which is what a compressed response expands into -- Content-Length
describes the wire size and cannot bound that. It also closes the
redirect hops, which stream=True would otherwise leave holding their
connections.
Two risks are documented rather than closed. Validation resolves the
hostname and requests resolves it again to connect, so DNS rebinding
remains possible; closing it needs the connection pinned to the
validated address, which would change behavior for all existing
safe_get callers. And the returned text is untrusted remote content
entering an agent's context, which input validation cannot address.
Also fixes a temp file leak in PDFLoader, which reached the same
pymupdf-from-URL path. It wrote downloads to NamedTemporaryFile with
delete=False and never unlinked them, so every PDF ingested from a URL
left a file behind. It now opens from memory, the way URLReadTool does,
which removes the leak by construction instead of relying on cleanup on
each error path; its doc.close() also moves into a finally so a failure
mid-extraction still releases the handle. PDFLoader had no test file, so
this adds one covering both paths plus a regression test asserting no
temp file is created.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): address review feedback on URLReadTool
Bound start_line and line_count with ge=1 in the args schema. Both were
unbounded, and _window computes stop as start + line_count, so a negative
line_count reached islice, which rejects a negative stop. The windowing
runs outside the tool's error handling, so that escaped _run as a raw
ValueError instead of an error string. BaseTool.run validates kwargs
against args_schema, so the constraint refuses the value before any
request is made. _window also clamps its own bounds now, so it cannot
raise if called directly.
Bound the PDFLoader download with safe_get_bounded. The body is held in
memory for the whole extraction, so it needed a ceiling; it defaults to
50 MiB and takes a max_bytes kwarg to load() for callers ingesting
larger documents.
Patch the loader's own seam in its tests rather than requests.get. Both
safe_get and safe_get_bounded resolve the hostname before requesting, so
the previous mocks made the tests depend on DNS for example.com and fail
in a network-isolated runner for reasons unrelated to the loader.
Exercise the content-type fallback through run() rather than asserting on
_resolve_kind, so the tests survive a refactor of the classification
internals, and cover the octet-stream PDF, missing-type, query-string and
unknown-extension cases as observable behavior.
Add docstrings to the new tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): close streamed redirect hops and widen type fallback
Four findings from the Copilot and Cursor reviews.
safe_get leaked its accumulated hops on every failure path. It closed the
response it was about to abandon but not the ones already in history, and
a caller handed an exception has no handle on them -- under stream=True
each holds its connection until its body is read or closed. The loop now
closes history before re-raising. Hops are still the caller's on success,
where they arrive via response.history.
safe_get_bounded rejected a non-positive max_bytes only after issuing the
request, and then reported it as an oversized body. It now fails before
the request. Its oversized-body error also named the requested URL rather
than the one that served the body, which differ after a redirect.
The content-type fallback consulted only the final URL for an extension,
so a .pdf link redirecting to an extensionless CDN or presigned path was
refused even though the requested URL identified the type. It now checks
the final URL first, then the requested one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: add app metadata to platform action tools
Platform policy matching needs an explicit integration slug on every
`CrewAIPlatformActionTool` at runtime. The builder now propagates each API
app key and tests cover the resulting tool metadata.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: remove redundant platform tool app assignment
`CrewAIPlatformActionTool` already receives `app` through Pydantic during base initialization. Remove the duplicate assignment to retain one owner for field state.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Document crewai create tool/skill/template, lifecycle commands, and
deprecated-but-supported scaffolding aliases in the project template
AGENTS.md. Remove the no-op --skip_provider flag from the flow example.
* fix(flow): clarify route/handler name collision validation errors
When @listen(...) includes the handler's own name, FlowDefinition validation
fails. This change improves the error text and how it surfaces for Python
Flow classes.
What changed
- _self_listen_error in flow_definition.py: two message variants (conversational
vs default), both include the listen condition
- build_flow_definition in dsl/_utils.py: wraps FlowDefinition ValidationError
with the Python Flow class name
- Tests for declarative and DSL-built flows; docs follow in a separate commit
When each error surfaces
1. Conversational message — FlowDefinition validation when
conversational.enabled is true and listen references the handler name.
Example: @listen("create_video") on def create_video in a conversational flow.
Surfaces via:
- FlowDefinition.from_declaration(dict/yaml) → pydantic ValidationError for
FlowDefinition (Value error, methods.create_video.listen listen condition...)
- MyFlow.flow_definition() / MyFlow() → ValueError Invalid flow definition
for MyFlow: ... (wrapped by pydantic as ValidationError for MyFlow on
instantiation)
2. Default (non-conversational) message — same trigger check when the flow is
not conversational. Example: @listen("publish") on def publish.
Surfaces via the same paths as (1).
3. Class-name wrapper — only on the Python DSL path when build_flow_definition
catches FlowDefinition ValidationError. Prepends Invalid flow definition for
{ClassName}: to the underlying message from (1) or (2). Does not apply to
from_declaration without a Flow class.
* docs(flow): explain conversational handler naming vs route labels
Document why @listen route labels must differ from handler method names and
recommend the handle_* naming pattern.
* docs(cli): warn against matching @listen labels to handler names
Add AGENTS.md guidance for crew and flow scaffolding so coding assistants
do not name handlers the same as their @listen route or event labels.
* docs(cli): clarify @listen self-reference fails at validation
Document that matching @listen labels to handler names raises a validation
error at flow instantiation, and that the runtime loop only occurs if
validation is bypassed.
* feat(cli): add canonical `crewai create tool` command
Unify tool scaffolding under the create verb and deprecate
`crewai tool create` with a yellow warning while keeping backward
compatibility.
* feat(cli): add canonical `crewai create skill` command
Unify skill scaffolding under the create verb and deprecate
`crewai skill create` with a yellow warning while keeping backward
compatibility.
* feat(cli): add canonical `crewai create template` command
Unify template scaffolding under the create verb and deprecate
`crewai template add` with a yellow warning while keeping backward
compatibility.
* docs: document unified `crewai create` scaffolding commands
Document canonical create forms for tool, skill, and template projects,
note deprecated aliases, and update skills and agents-md guides.
* feat(cli): extend create picker and DMN guidance for all types
Show tool, skill, and template in the interactive create picker and
list every supported type in the CREWAI_DMN usage error.
* fix(cli): allow create tool/skill/template in CREWAI_DMN mode
Only set skip_provider in DMN mode for crew creation, since tool,
skill, and template paths reject that flag as a crew-only option.
* test(cli): patch TemplateCommand at cli lookup site in DMN test
create() resolves TemplateCommand from crewai_cli.cli, not from
remote_template.main directly.
Skipped matrix jobs never post status for branch-protection names like
tests (3.10). Add lightweight skip jobs with the same names so Actions-only
and docs-only PRs can merge without waiting forever.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
Exclude .github/** from the code path filter used by tests, lint,
type-checker, and vulnerability-scan so workflow-only changes (including
CodeQL) do not run the Python matrix. CodeQL itself is unchanged and still
analyzes Actions YAML on those PRs.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* fix: clear CodeQL incomplete URL substring sanitization alerts
Replace hostname substring checks with urlparse hostname matching in
RAG DataType classification, and assert the full mocked Stagehand
navigate result instead of searching for a URL substring.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* test: call DataTypes.from_content in GitHub hostname tests
from_content lives on DataTypes, not the DataType enum.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* test: harden tool-call streaming emit mock against instance shadowing
CI failed when class-level CrewAIEventsBus.emit patches were shadowed by
the singleton instance. Patch both the class and crewai_event_bus.emit,
and read events from kwargs/args explicitly.
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
Add a patch-minor-updates group for routine version bumps while keeping
the existing security-updates grouping. Ignore semver-major updates so
breaking upgrades stay manual.
`HookDispatchedEvent` was already emitted from the dispatcher but never
landed in Feature Usage. Wire it through `hook_dispatched_span` so hook
adoption and abort outcomes (e.g. policy checks) show up in the same
ClickHouse aggregation as other features.
Raise override floors to aiohttp>=3.14.3 and cryptography>=50.0.0 so pip-audit
no longer reports WebSocket smuggling, C parser OOB read, wildcard cert, path
building, and PKCS#7 oracle issues in the locked dependency tree.
dorny/paths-filter treats exclusion-only patterns as matching every
non-excluded path under the default "some" quantifier, so docs-only
PRs still set code=true. Add an explicit "**" include and use
predicate-quantifier: every in tests, type-checker, and linter.
Also gate Vulnerability Scan the same way on pull_request while
keeping schedule and main push runs unconditional.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
* feat: add project_id to link OSS usage to an enterprise account
Adds a stable per-project identifier so a project's OSS traces and runs can
be attributed to an account after signup. There was no such identifier
before: [tool.crewai] held only `type`, the deploy UUID was printed to the
console but never persisted, Settings.org_uuid is global rather than
per-project, and trace batches carried only crew_fingerprint/crew_name.
The id lives in the project's pyproject.toml, so it is committed with the
repository and stays stable across machines, teammates, CI, and containers -
unlike a machine- or user-derived identifier, which is unstable in exactly
the containerized production environments that matter most.
crewai-core:
- get_project_id(): read-only lookup of [tool.crewai].project_id. Safe for
library code; never creates or modifies anything.
- get_or_create_project_id(): mints a uuid4 and persists it, returning
(id, created) so callers can tell the user. Best-effort - returns
(None, False) for a missing, malformed, or read-only pyproject.toml rather
than raising.
- Insertion edits the raw TOML text instead of round-tripping through a
writer, so comments, key order, and formatting elsewhere survive. The key
is placed at the end of the [tool.crewai] table, before the next table
header, so it cannot land in a neighbouring section.
- LoginPayload and TraceExecutionContext gain optional project_id.
Sent on two paths:
- Traces: project_id is added to execution_context, which is sent on both
the ephemeral and authenticated paths, so a project's traces remain
attributable before and after the user creates an account.
- Login: `crewai login` already sends the pseudonymous user_identifier on an
authenticated request; adding project_id means one request carries account
+ user + project, which is the link itself.
Minting is restricted to CLI commands the user explicitly invoked - `crewai
create` for new projects and `crewai run` to backfill existing ones - and is
announced when it happens. Library code only ever reads. Silently rewriting
a user's pyproject.toml during Crew.kickoff() would be surprising.
Privacy: project_id is a random uuid4 in a file the user commits. It is
visible in a diff, contains nothing personal, and identifies a project
rather than a person - so this needs none of the notice changes that
attaching a user identifier to all telemetry would require.
Tests: 18 new tests covering minting, stability, table placement, comment
and formatting preservation, five pyproject layouts, the neighbouring-table
regression, and graceful handling of missing/malformed/read-only files.
Verified end-to-end that both create paths mint distinct ids, that the trace
payload carries project_id on both the ephemeral and authenticated paths,
and that the login payload carries user_identifier and project_id together.
Follow-ups, deliberately not included: adding project_id to telemetry spans,
and backend persistence of the (account, user_identifier, project_id) triple.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
* refactor: drop the console announcement when minting project_id
Minting now happens silently. With no message to print, the (id, created)
tuple had no consumer, so simplify the API rather than keep the flag around
for a hypothetical caller:
- get_or_create_project_id() returns `str | None` instead of
`tuple[str | None, bool]`.
- Remove crewai_cli.utils.ensure_project_id, which existed only to print the
message and discard the flag. The four call sites (crewai create crew,
crewai create flow, crewai run, and tool-repository login) now call
get_or_create_project_id directly.
- Update tests for the simplified signature; still 18 tests covering minting,
stability, table placement, formatting preservation, five pyproject
layouts, and missing/malformed/read-only handling.
Behaviour is otherwise unchanged: minting stays restricted to CLI commands
the user invoked, library code still only reads via get_project_id, and a
missing or read-only pyproject.toml still returns None rather than raising.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
* fix: harden project_id minting against TOML corruption; address review
Several reviewers found ways the raw-text edit could produce invalid TOML.
Each is now fixed and covered by a test that fails without the fix.
Duplicate project_id key (Cursor bugbot, Copilot x2):
- get_project_id() reports a blank or non-string value as "absent", so a file
containing `project_id = ""` took the insert path and gained a second
project_id line - a duplicate key, and therefore invalid TOML that no
tomli-based tool could read afterwards.
- _insert_project_id is now _set_project_id: it replaces an existing
assignment inside [tool.crewai] instead of appending unconditionally.
Table header with a trailing comment (CodeRabbit major, Cursor bugbot):
- `[tool.crewai] # config` is valid TOML but failed exact string equality,
so the fallback appended a second [tool.crewai] header - a redefined table,
also invalid TOML, and silent because get_project_id swallows the resulting
decode error.
- Added _is_table_header(), which tolerates a trailing comment and does not
match similar names such as [tool.crewai-extra].
Writing into malformed TOML (Cursor bugbot, Copilot):
- get_or_create_project_id relied on get_project_id, which cannot distinguish
"no id" from "unparsable file", so it appended to files it could not parse.
- The locked path now parses explicitly and bails on a decode error, and
re-parses the updated content before writing, so this feature can never be
the reason a project's pyproject.toml stops parsing.
Concurrency and atomicity (CodeRabbit major):
- Two CLI processes could both see no id, mint different uuids, and clobber
each other, leaving a caller holding an id that is not on disk. Minting now
takes the existing crewai_core cross-process lock, re-reads under it, and
returns the id that persists.
- Writes go through a temp file in the same directory plus os.replace, so an
interruption cannot truncate pyproject.toml. File mode is copied across, and
the temp file is removed on failure.
- os.replace only needs a writable directory, which would have let an atomic
write silently overwrite a file the user marked read-only; writability is
now checked explicitly so that case still returns None.
Line endings (CodeRabbit):
- Path.read_text/write_text normalized CRLF to LF, so minting would rewrite a
CRLF-committed file entirely. Read and write now use newline="" and the
inserted line ending is derived from the existing content.
Default create path skipped minting (Cursor bugbot):
- `crewai create crew` defaults to create_json_crew; only the --classic and
flow paths minted, so most new projects had no id until a later command.
Wired into create_json_crew as well. Verified all three paths now mint
distinct ids.
Do not mint during login (CodeRabbit major):
- ToolCommand.login ran get_or_create_project_id, which is outside the
sanctioned minting commands and is invoked by `crewai tools create` from a
freshly scaffolded directory before the project is persisted. It now uses
the read-only get_project_id. Verified login leaves pyproject.toml
untouched.
Not applied: Copilot asked for a console message when an id is written, in
create_crew and create_flow. Minting was made deliberately silent in the
previous commit, so the (id, created) tuple and the announcement are both
gone by design.
Tests: 32 in test_project_id.py, up from 18. New cases cover blank and
non-string existing ids, three commented-header forms, similar table names,
malformed input, CRLF and LF preservation, concurrent minting convergence,
file-mode preservation, and temp-file cleanup. Confirmed the header and
duplicate-key tests fail when the fixes are reverted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
* fix: never create [tool.crewai], treat whitespace ids as absent, harden test
`crewai run` could rewrite unrelated projects (Cursor bugbot, high):
- get_or_create_project_id ran before the cwd was established as a CrewAI
project, and _set_project_id appended a [tool.crewai] table when none
existed. Any directory with a pyproject.toml could therefore gain one -
including on `crewai run --definition`, which may otherwise succeed.
- _set_project_id no longer creates the table; it returns None when
[tool.crewai] is absent, so a key is only ever added to a table the project
already declares. The templates all ship the table, so no create path needs
the old fallback.
- The minting call in run_crew moved after the --definition early return, so
an explicit-flow run does not touch the cwd at all.
- Presence is checked, not truthiness: an empty [tool.crewai] is still a
CrewAI marker, and get_crewai_project_config returns {} both for that and
for an absent table.
- Verified an unrelated project's pyproject.toml is byte-identical after a
mint attempt.
Whitespace-only project_id accepted as valid (CodeRabbit):
- `project_id = " "` is truthy, so it was returned as an identity and would
have propagated into login payloads and tracing context. It also meant the
'" "' parameter of the replacement test asserted nothing.
- Added _usable_project_id, which strips before deciding, used by both
get_project_id and the locked mint path.
Concurrency test could hang CI (CodeRabbit, major):
- Neither the barrier nor the joins had timeouts, so a thread dying early or
blocking on the lock would hang the job rather than fail it. The result
count was also unchecked, so a dead thread still passed.
- Added timeouts, an explicit liveness assertion, a result-count assertion, a
lock around the shared result list, and corrected the docstring: this covers
the read-modify-write race with threads, not the cross-process backend.
Tests: 35, up from 32. New coverage for the absent-table refusal and three
whitespace forms; the blank-id replacement case now asserts a real uuid
replaced the blank value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
* chore(deps): force gitpython 3.1.57+ for GHSA-p538-c434-8v24 and GHSA-3f7w-8rr8-f37f
Unrelated to project_id; bundled here only because it blocks this PR's
vulnerability scan. Two advisories were published for gitpython 3.1.55 after
main last passed the scan:
- GHSA-p538-c434-8v24: arbitrary file truncation via `git rev-list --output`
argument injection. Fixed in 3.1.56.
- GHSA-3f7w-8rr8-f37f: unguarded git option forwarding in
IndexFile.checkout() and TagReference. Fixed in 3.1.57.
- Bump the override floor to gitpython>=3.1.57 and declare the same floor in
crewai-tools, so consumers installing the published package are covered and
not only this repo's lock.
- 3.1.57 was published 2026-07-26, past gitpython's exclude-newer-package
cutoff of 2026-07-24, so that cutoff moves to 2026-07-27. Without it the
floor is unresolvable.
pip-audit against the updated lock reports no known vulnerabilities.
Verified gitpython 3.1.57 resolves and that crewai_tools and crewai_cli.git
still import.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(crewai-tools): add db2 search tool
* refactor(crewai-tools): improve db2 search tool implementation
* feat(tools): improve DB2VectorSearchTool validation, security, and configurability
* docs: add DB2SearchTool documentation
* feat: add DB2 search tool
* docs: update DB2SearchTool documentation
* fix: address CodeRabbit review feedback
* fix: validate non-empty filter_by in DB2ToolSchema
* chore: trigger CodeRabbit re-review
* feat: fortify DB2 tool; fixed JSON response shape, added input guards and config validation
* refactor(db2): replace DB2Config with connection_string field
* refactor(db2): remove dead _setup_db2 validator and importlib import
* refactor(db2): remove dead guard in _connect as _disconnect() is called at the end of every
_run, so self.connection is always None when _connect is called next.
The 'if not self.connection' guard was dead code.
* fix(db2): tighten _validate_identifier regex. Old regex allowed leading digits, multiple periods and dot-only strings (e.g. '.....' passed).
* fix(db2): replace __import__ with importlib.import_module in _generate_embedding as keeping openai as a lazy optional import since it is not always required.
* perf(db2): cache OpenAI client in _openai_client to avoid re-instantiation as OpenAI(api_key=...) was recreated on every _generate_embedding call. Extract into _get_openai_client() which lazily initialises and caches self._openai_client on first use, reusing it for all subsequent queries.
* docs(db2): clarify tool description to mention embedding fallback
* docs(db2): update README supported features to clarify embedding behaviour. 'OpenAI embedding fallback' implied it was
optional. Replaced with 'Uses a custom embedding function if supplied,
otherwise OpenAI embeddings.'
* updated both code examples to use the correct import path and public run() method.
* feat(crewai-tools): add db2 search tool
* refactor(crewai-tools): improve db2 search tool implementation
* feat(tools): improve DB2VectorSearchTool validation, security, and configurability
* docs: add DB2SearchTool documentation
* feat: add DB2 search tool
* docs: update DB2SearchTool documentation
* fix: address CodeRabbit review feedback
* fix: validate non-empty filter_by in DB2ToolSchema
* chore: trigger CodeRabbit re-review
* feat: fortify DB2 tool; fixed JSON response shape, added input guards and config validation
* fix(db2): address ruff and mypy linter errors
* style(db2): apply ruff format to db2_search_tool.py
* fix(db2-search-tool): address PR review comments
- Restore DirectoryReadTool export accidentally removed; add DB2VectorSearchTool
and DB2ToolSchema to crewai_tools.tools __init__ and __all__
- Align _ALLOWED_METRICS whitelist with Db2 VECTOR_DISTANCE API:
replace DOT_PRODUCT/L2_DISTANCE with EUCLIDEAN_SQUARED/DOT/HAMMING/MANHATTAN
- Replace ImportString fields for db2_package/db2_dbi_package with plain Any +
lazy importlib.import_module in new _resolve_db2_packages() to avoid Pydantic
default-validation gap where strings were never resolved at construction time
- Move docs from frozen docs/v1.13.0/ snapshot to docs/edge/en/tools/database-data/
and register in docs/docs.json; update examples to match actual API
(connection_string constructor, not DB2Config), correct return format, and
align documented distance metrics with the whitelist
* fix(db2-search-tool): resolve default and string db2 package imports dynamically
* fix(db2-search-tool): export DB2VectorSearchTool and DB2ToolSchema from package-level crewai_tools
* docs(db2-search-tool): fix installation command and import path in README
---------
Co-authored-by: priyanshu-krishnan1 <priyanshu.krishnan1@ibm.com>
Co-authored-by: GeetikaChugh24 <geetika@ibm.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
Co-authored-by: Dhruv Chaturvedi <dhruv_insights@Dhruvs-MacBook-Pro.local>
* feat(tracing): collect skill usage events
PR #6652 added SkillUsedEvent but deliberately shipped no listener wiring,
so the event reached no collector. The trace listener subscribed to the five
setup events -- discovery, load, activation, failure -- and none of them can
answer the question skills observability is for: activation is idempotent and
fires once at setup, so an agent using a skill across twenty turns produces
exactly one event.
SkillUsedEvent is the only runtime signal and the only one that re-fires per
execution. Subscribing to it lets a trace attribute skill usage to an agent
and a task.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tests): scope the trace-listener handlers, assert the forwarded event
CrewAIEventsBus is a singleton, so constructing one in the fixture still
registered against the process-wide bus. _register_action_event_handlers
attached every action handler with no cleanup, leaving them live after the
patch ended -- firing against a listener built with __new__, which has no
batch_manager, in whatever test ran next. scoped_handlers clears them.
Also assert the event object itself is forwarded, not just its type: the
collector serializes the event, so dropping or replacing it would lose every
attribution field while still passing a type-only check.
Both raised in review on #6727.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: assert the forwarded skill event by identity
Comparing field values would still pass if a handler forwarded a
reconstructed copy rather than the event itself. Bind the event and assert
`forwarded is event`.
Raised in review on #6727.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(tools): surface tool failures instead of reporting them as success
A tool can finish without raising and still fail to do what it was asked.
Slack answers HTTP 200 with `{"ok": false, "error": "channel_not_found"}`;
an MCP server sets `isError`; a CrewAI AMP action returns
`API request failed: ...`. In every case the call "worked", so the error
text reached the agent as an ordinary result, the agent narrated the
problem in prose, and the run was recorded as a success.
Concretely: five failed `slackbot_send_message` calls each rendered as
"Tool Execution Completed", the task passed, and the crew passed -- with
the only evidence being a sentence in the final answer. Nothing
downstream could tell the difference, and an agent that keeps going on a
step that silently did nothing builds the rest of its work on it.
Give that outcome a type and a reaction:
- `ToolFailure` -- what a tool returns instead of an error string. The
agent still reads prose via `as_agent_message()`, so model behavior is
unchanged; the framework now knows the call failed.
- `ToolFailurePolicy` -- `ignore` (previous behavior), `warn` (default:
record + emit, keep going), `raise` (abort with
`ToolExecutionFailedError`). Resolved most-specific-first: tool, task,
agent, crew.
- `ToolFailureDetectedEvent` -- emitted before a `raise` aborts, so
subscribers always observe the failure. `ToolUsageFinishedEvent` also
carries a `failure` field so a trace UI can mark the call failed
without correlating two events.
- `tool_failures` on `TaskOutput`, `CrewOutput` and `LiteAgentOutput`,
plus `has_tool_failures`, so consumers never parse a string.
Detection is strictly declarative -- no string sniffing, so a tool that
legitimately returns text about an error is never misread as failing.
Failures come from a returned `ToolFailure`, a raised exception, MCP
`isError`, a spent `max_usage_count`, or an unknown tool.
Wired into all four tool-execution paths (the ReAct path and the three
native function-calling implementations). Sources updated to report
structurally: `MCPClient.call_tool_result()` preserves `isError` that
`call_tool()` dropped, and `CrewAIPlatformActionTool` returns a
`ToolFailure` for non-2xx and for caught exceptions.
Two latent bugs fixed along the way: `ToolUsage` assumed every agent has
a `fingerprint` (LiteAgent does not), and policy resolution now tolerates
malformed values rather than letting telemetry take down a tool call.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): address review round 1 on tool-failure signalling
Five real defects from Bugbot, none of them cosmetic.
Tool-scoped policy never applied (high). `resolve_tool_failure_policy`
read `tool_failure_policy` off the object handed to it, but every
execution path passes the `CrewStructuredTool` wrapper, which never
carried the attribute -- and `BaseTool` never declared it in the first
place. A tool-scoped `raise`/`ignore` was silently ignored while the
docs and a unit test claimed otherwise; the test passed only because it
called the resolver directly with an authored tool. Declared the field on
`BaseTool`, propagated it through `to_structured_tool()` and
`CrewStructuredTool`, and made resolution fall back through
`_original_tool` so either shape works.
A failed call still printed the green "Completed" panel, then the red
one. That is the terminal version of the exact bug this PR is about.
Suppressed the success panel when the call reported failure.
A raised tool printed twice: `ToolUsageErrorEvent` already renders a red
panel, and the new failure panel repeated it. The event is still emitted
-- policy and traces need it -- but the duplicate console output is gone.
Both decisions now live in named predicates on `ConsoleFormatter` rather
than inline in the listener closure, so they are directly testable.
Unknown tools were reported on the ReAct path but silently ignored on all
three native paths, so the same miss was loud or silent depending on
executor style. Native paths now record `UNKNOWN_TOOL` too. This also
surfaced a live `NameError`: ruff had pruned `ToolFailureReason` from
`agent_utils` as unused, so the new branch would have crashed at runtime.
`LiteAgentOutput` had `tool_failures` but not `has_tool_failures`, which
the PR promised on all three output types -- an `AttributeError` for any
caller sharing one check across result types.
Testing: 16 further tests, 45 total. Two console tests were passing
vacuously because `emit()` dispatches sync handlers on a thread pool, so
the assertions raced the handler; they now assert on the predicates
directly, and the native-path test drains the bus with `flush()` and
checks the synchronously-written record. Full suite still matches
baseline exactly at 377 pre-existing failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* chore: update tool specifications
* fix(tools): address review round 2 and fix CI type failure
CI caught a type error I should have: widening `agent` to accept a
`LiteAgent` (so a standalone LiteAgent resolves its own policy) left the
declared signatures behind. Widened `execute_tool_and_check_finality`,
its async twin, and `ToolCallHookContext` to `Agent | BaseAgent |
LiteAgent | None`, which is what those actually receive now.
Seven CodeRabbit findings, all verified against the code first:
`raise` was being downgraded by three enclosing handlers. With
`max_execution_time` set, `_execute_with_timeout` wrapped every exception
in `RuntimeError`, so `_check_execution_error` no longer recognized the
passthrough and sent the task through the retry loop instead of aborting.
`StepExecutor.execute` turned it into `StepResult(success=False)` and let
the plan continue. `LiteAgent.kickoff` ran it through
`handle_unknown_error` and printed "This is likely a bug - please report
it" for what is a deliberate, configured stop.
Failure records were dropped on two paths. `reset_tool_failures()` only
ran in `_prepare_task_execution`, so `Agent.kickoff()` / `kickoff_async()`
— which enter through `_prepare_kickoff` — accumulated records across
runs. And a guardrail retry calls `execute_task` again, which resets the
agent, so a tool that failed on a blocked attempt vanished from the final
output entirely: a run could report zero failures having demonstrably
failed one. Failures now accumulate across guardrail attempts.
Writing the tests for that surfaced a further miss of my own:
`Agent.kickoff()` builds its `LiteAgentOutput` in `agent/core.py` via
`AgentExecutor`, not through `LiteAgent`, so `tool_failures` was always
empty there regardless of the recording fix. Wired up, and the LiteAgent
path now reads from whichever agent the executor was handed
(`original_agent` under kickoff, `self` standalone) rather than assuming.
`last_tool_failures` returns a copy, so a caller cannot mutate the
agent's record or watch it shift mid-run.
Testing: 7 further tests, 52 total, covering the timeout wrapper, the
retry limit, kickoff reset, the kickoff output path, copy semantics and
guardrail accumulation. Full suite matches baseline exactly at 377
pre-existing failures; mypy clean on every changed file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): make crew-scoped policy real and close the last raise leak
Two findings, and the first was a documented feature that never worked.
`resolve_tool_failure_policy` consulted a crew, and the docs advertised
crew as a scope, but `Crew` had no `tool_failure_policy` field at all --
and even with one it was unreachable, because `BaseAgent` defaulted the
policy to `WARN` rather than `None`, so resolution always stopped at the
agent. Crew-level configuration was silently ignored.
Fixed by making "inherit" the default everywhere instead of baking `warn`
into one layer: `Crew` gains the field, and `BaseAgent`/`LiteAgent`
default to `None` like `Task` and `BaseTool` already did. The resolver
owns the single fallback, so the chain is genuinely
tool > task > agent > crew > warn and the effective default with nothing
configured is still `warn`. Reading `agent.tool_failure_policy` now
returns `None` (meaning "inherit") rather than `WARN`.
The other: `StepExecutor` re-raised `ToolExecutionFailedError` from its
outer handler, but the nested handler around the native-to-text tooling
fallback still caught it and returned `StepResult(success=False)`. An
agent whose LLM lacked native tool calling would therefore not abort
under `raise`. That is the third distinct place this exception was being
downgraded; it now re-raises there too.
Testing: 8 further tests, 60 total, including the full precedence chain
walked one level at a time and crew-scoped `raise`/`ignore` driven
end-to-end through `kickoff()` rather than only through the resolver --
the gap that let the original crew bug pass review. Full suite matches
baseline exactly at 377 pre-existing failures; mypy clean on every
changed file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* docs: trim comments and docstrings on tool-failure signalling
Prose only -- no behavior change. Cut the module docstring, the longer
class and method docstrings, the multi-line inline comments, and the
verbose Field descriptions down to what actually earns its place. Net 87
lines lighter.
Kept the "why" in every case where the reason is non-obvious (why the
event fires before a raise, why the policy reads through the tool wrapper,
why the bus needs draining in tests) and dropped the restatements of what
the code already says.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): make ignore truly silent, stop caching failures, close 4 gaps
Six findings from the latest review round, all verified against the code
before touching it.
`ignore` was not silent. `ToolUsageFinishedEvent.failure` was set before
the policy ran, so traces still saw a failed call under a policy documented
as surfacing nothing. Worse, the console then showed *no* panel at all:
green was suppressed because `failure` was present, red was skipped because
`ignore` never emits `ToolFailureDetectedEvent`. New `reportable_failure()`
resolves the policy before the finished event and drops the flag under
`ignore`; wired into all four execution paths.
Failures were being cached. `CacheHandler.add` stored a `ToolFailure` like
any other result, so a transient error became permanent for the rest of the
run and every later hit re-reported a call that never re-ran. The cache now
refuses to store declared failures -- fixed at the single choke point rather
than at each of the four call sites.
A spent `max_usage_count` was invisible on the shared native path.
`BaseTool._claim_usage` returned a bare string that only the executors
recognising that exact message treated as a failure. It now returns a
`ToolFailure` with `USAGE_LIMIT`, so every path records it.
A guardrail returning a whole `TaskOutput` replaced the output without
carrying accumulated failures over, so earlier attempts vanished from
`CrewOutput.tool_failures`. New `merge_tool_failures()` combines and
deduplicates, and the retry-rebuild path uses it too.
A hook-blocked call inherited a cached failure and attributed it to a call
that never ran. Now cleared. Not reachable through the built-in cache once
failures stop being cached, so the test injects a custom cache handler that
does retain them -- verified to fail without the guard.
Also removed a `datetime` import left unused by the earlier console-test
rewrite.
Testing: 13 further tests, 73 total. Full suite matches baseline exactly at
377 pre-existing failures; the usage-limit suites that `_claim_usage`
touches pass unchanged; mypy clean on every changed file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): let raise through the parallel native path, guard all handlers
Chasing down CodeRabbit's note about callers of
execute_single_native_tool_call turned up a fifth place this exception was
being downgraded: the experimental executor's parallel branch wrapped
future.result() in a broad except and folded the abort into a fake tool
result, so the remaining parallel calls carried on. The sequential path and
crew_agent_executor's parallel branch were already fine.
Five separate handlers have swallowed this during review, so added a guard
test asserting the passthrough at every site rather than trusting the next
one gets spotted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): keep a failed tool out of the final answer, finish crew scope
Three more findings, all confirmed against the code.
A failed `result_as_answer` tool still became the task's output. The native
paths already excluded raised errors and hook blocks from short-circuiting,
but not declared failures -- so an error message silently became the answer,
which is the exact shape of bug this PR exists to prevent. Fixed on all
paths, and there were three independent override points, not one:
`ToolResult.result_as_answer` in tool_utils, the `execution_result`
finality checks in both executors, and `process_tool_results()`, which
reads `agent.tools_results` back separately. The first two fixes alone left
the behavior unchanged; only the third made the test pass.
`ToolUsage` never received a crew, so a crew-level `ignore` half-applied:
recording and `ToolFailureDetectedEvent` stayed quiet, but the flag was
still attached to `ToolUsageFinishedEvent`. It now takes and stores `crew`.
`CrewAgentExecutor.invoke`/`ainvoke` routed a deliberate stop through
`handle_unknown_error`, printing "An unknown error occurred" on verbose
runs. LiteAgent already special-cased this; both now do.
Testing: 5 further tests, 79 total, including that a *successful*
`result_as_answer` tool still short-circuits. Full suite matches baseline
exactly at 377 pre-existing failures; mypy clean on every changed file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): report malformed tool args, correlate the failure event
Two findings from the latest round.
Malformed native tool arguments returned early with a plain error dict and
never reported a failure, so `ToolFailureReason.INVALID_INPUT` was declared
but unreferenced -- a bad tool call was absent from records, events and
`raise` aborts. `parse_tool_call_args` now carries an INVALID_INPUT failure
on the error dict and both executors report it before returning.
`ToolFailureDetectedEvent` never set `agent_id`, so a trace could not tie it
to a specific agent instance. Fixing that exposed the same gap running the
other way: `ToolUsage`'s own started/finished/error events never set
`agent_id` either, so on the ReAct path the paired finished event had
nothing to correlate against. Both now set it.
Set explicitly rather than via `from_agent`, which would also overwrite
`agent_role` and lose the `_original_role` preference those events already
apply -- a behavior change that has nothing to do with correlation.
Testing: 5 further tests, 84 total, asserting the ids match between the
failure event and its paired finished event. One existing test pinned the
exact key set of the parse-error dict and was updated for the new key. Full
suite matches baseline at 377 pre-existing failures; the one apparent
addition was the known `test_trace_enable_disable` order-flake, confirmed by
re-running rather than assumed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): scope failure accumulation per execution, drop deprecated executor
Two review requests from @lorenzejay.
Accumulation no longer lives as mutable state on the shared agent. A
ContextVar collector is opened around each execution -- task, kickoff, and
each guardrail retry -- and the output reads that collector directly instead
of copying the agent's list. ContextVars are copied per asyncio task and per
thread, so concurrent executions cannot see each other's records, and
nesting is safe for retries. `last_tool_failures` prefers the active
collector and falls back to the last completed execution, so the accessor is
correct during a run too. The per-execution reset that caused the erasure is
gone.
Reproducing this took some digging and the finding is worth recording: crew
tasks *cannot* hit it, because `AgentExecutor` refuses concurrent reuse of
one instance and raises. `agent.kickoff()` has no such guard, and there the
bug reproduces exactly as reported -- two concurrent kickoffs each returned
two records. The regression test forces the overlap with a barrier so it is
deterministic rather than timing-dependent, and I verified it reports [2, 2]
against the old behavior and [1, 1] now.
Removed the tool-failure integration from `CrewAgentExecutor` entirely; that
file is back to its state on main. Note the shared ReAct helper it calls
still records failures, since that is common code rather than new behavior in
the deprecated file -- so a `raise` policy will be swallowed by that
executor's generic handler. Flagged on the PR rather than papered over.
Testing: 89 total. Two tests I wrote for this were vacuous on the first
attempt -- they passed against the simulated pre-fix code -- so each
concurrency test was checked against the old behavior before being kept.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): report malformed calls everywhere, drop the unused block reason
Four findings.
`execute_single_native_tool_call` swallowed a JSON decode error into an empty
args dict and ran the tool with no input at all -- worse than not reporting
it. It now routes through `parse_tool_call_args` like the executors do, so
the StepExecutor/planning path reports INVALID_INPUT and returns instead of
executing. That also removes a duplicated inline parse.
The ReAct path returned a `ToolUsageError` message as an ordinary result
without reporting it, so a malformed call there was invisible while the
equivalent native failure was recorded. Now reported as INVALID_INPUT too.
`Agent.kickoff` opened a collector but no longer reset the agent-level list,
so `last_tool_failures` grew across kickoffs. Reset restored, matching task
execution.
`ToolFailureReason.BLOCKED_BY_HOOK` was declared and never produced. Rather
than start reporting hook blocks as failures, the member is removed: a block
is a deliberate decision by the hook author, and treating it as a failure
would make `raise` abort on an intentional veto. Added a guard test that every
remaining reason is actually produced somewhere, so a dead member cannot
reappear -- the same smell that flagged INVALID_INPUT last round.
Also switched the deprecation guard test to a single import style.
Testing: 6 further tests, 95 total, including that the tool does not run when
its args fail to parse. Full suite matches baseline at 377 pre-existing
failures; mypy clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
* fix(tools): merge failures across kickoff guardrail retries, cancel siblings
Kickoff guardrail retries discarded the blocked attempt's failures. Each
retry calls `_execute_and_build_output`, which opens a fresh collector and
builds a new output, so a run could report zero failures having demonstrably
failed one -- the same bug already fixed on the task guardrail path, which
merges. Now merged there too. Verified the test fails without the fix.
Under `raise`, one parallel native tool aborting left its siblings running:
the pool waited for them and pending ones still started. It now shuts the
pool down with `cancel_futures=True` so a not-yet-started sibling never runs.
Threads already in flight cannot be interrupted in Python, so a concurrent
tool may still complete before the abort surfaces; that is noted at the call
site rather than left implied.
Also satisfied CodeQL by materialising the enum in the guard test's loop.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ETacm2dMASfpMAYUiDu5YG
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* feat: emit FlowFailedEvent when a flow execution fails
A failed flow never emitted a terminal lifecycle event, so the
`flow_started` scope stayed open and consumers such as tracing closed the
root span with a generic orphaned message instead of the real error.
`kickoff_async` and the resume path now emit `FlowFailedEvent`, paired
with `flow_started` and carrying the exception, after draining pending
handlers and background memory writes. The resume path also emits the
`MethodExecutionStartedEvent` it was missing for the method being resumed,
so its finished or failed event pairs with its own scope instead of
popping the flow's.
* fix: skip FlowFailedEvent when the run never opened a scope
The `kickoff_async` try block starts before `FlowStartedEvent` is emitted,
so an abort in the execution-start hooks, in input handling or in state
restore emitted a `flow_failed` with no opener, which pops an unrelated
scope and warns about an empty scope stack. The failure event is now
gated on the flow scope actually being open, either from this kickoff's
`flow_started` or from a restored deferred session scope.
* fix(tools): sandbox FileWriterTool writes and fix file tool rough edges
FileReadTool confined reads to the working directory, but FileWriterTool
only checked that `filename` stayed inside `directory` — and `directory`
itself is an LLM-supplied schema field. An agent could therefore write
anywhere the process had permission to, including ~/.ssh and site-packages,
while the reader refused to read back what the writer had just written.
FileWriterTool was the only filesystem tool in the package that did not go
through validate_file_path; files_compressor_tool validates even its
output path.
Writes are now confined to base_dir (the working directory by default):
the resolved directory must sit inside base_dir, and the resolved file
must sit inside that directory. The pre-existing filename containment
check is kept as-is and still applies even when the unsafe-paths escape
hatch is on, so no existing guarantee is weakened.
Both tools gain a base_dir field so a developer can widen the sandbox
deliberately instead of reaching for the process-wide
CREWAI_TOOLS_ALLOW_UNSAFE_PATHS kill switch. FileReadTool also stops
rejecting a file_path given to its own constructor: that is
developer-declared intent, and declaring one file does not expose its
siblings.
Also fixed:
- FileReadTool scanned the whole file when reading a line window; it now
stops via islice once the requested lines are collected.
- FileWriterTool._run(**kwargs) made the documented positional call
signature raise TypeError and turned a missing overwrite into
"error accessing key". It now takes named parameters in the documented
(filename, content, directory) order.
- A directory naming an existing file reported "already exists and
overwrite option was not passed" even with overwrite=True; it now
explains the real problem.
- Subdirectories inside filename are created, matching what passing
directory already did.
- Both tools now write and decode UTF-8 by default instead of the
platform locale encoding, with an encoding field to override. The docs
already claimed UTF-8 and recommended the writer to Windows users.
- The writer's schema fields had no descriptions for the LLM.
- Docs claimed FileReadTool parses JSON into a dict (it never has),
shipped a snippet that raised TypeError, and did not mention the path
sandbox. The writer README also began with a stray "Here's the
rewritten README" preamble.
BREAKING CHANGE: FileWriterTool no longer writes outside the working
directory. Pass base_dir to authorize a different tree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update tool specifications
* fix(tools): make the declared FileReadTool file reachable by agents
Addresses review feedback on #6692.
The constructor-path exemption did not actually work the way an agent
calls the tool. The description only advertises a redacted label (the
basename, when the file sits outside the sandbox), but resolution
required the exact absolute path, so the model's call was sandboxed and
the declared file was never read. Worse, file_path was a required schema
field, so the long-documented "call with no arguments to read the default
file" raised a validation error instead:
FileReadTool(file_path="/outside/declared.txt")
.run() -> ValueError: validation failed
.run(file_path="declared.txt") -> Error: File not found
.run(file_path="/outside/declared.txt") -> works, but the model was
never told this path
file_path is now optional in the schema, so omitting it reads the default,
and the declared file is addressable by the label the description shows
the model as well as by its real path. Declaring one file still does not
expose its siblings.
The declared path is also pinned to its real path at construction, so a
later chdir cannot silently repoint it at a different file — previously a
relative constructor path re-resolved against the new working directory
on every call.
Also guards the writer's filepath resolution, which could raise
ValueError out of _run for a filename containing a null byte, breaking
the contract of always returning a descriptive string. The directory and
read paths were already guarded.
Adds docstrings to strtobool and both _run methods, corrects an Arabic
tanween spelling and a kaf-as-descriptor calque in the localized read
docs, and regenerates tool.specs.json for the schema change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): anchor the declared read path to base_dir, not the cwd
Addresses the second round of review feedback on #6692.
The previous commit pinned a relative constructor file_path with
os.path.realpath, which anchors to the working directory, while both
format_path_for_display and validate_file_path anchor a relative path to
base_dir. With the two roots disagreeing, the same relative string meant
two different files — and the tool served the cwd one under a label that
looks like it belongs to the sandbox:
FileReadTool(file_path="data.txt", base_dir="/allowed") # cwd=/work
label advertised to the model -> "data.txt"
run(file_path="data.txt") -> contents of /work/data.txt
That reads a file from outside base_dir, so it was a sandbox escape
introduced by the exemption itself, not just a wrong-file bug.
Resolution now goes through a single _resolve_against_base helper that
anchors relative paths exactly the way the sandbox does, so the pinned
path, the advertised label and the containment check all agree. Covered
by test_relative_declared_path_anchors_to_base_dir.
Also softens "always readable" to "always allowed past the containment
check" in the docstring, README and docs, since bypassing containment
does not guarantee the read succeeds — it can still fail on a missing
file, a directory, or permissions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): tell the LLM about the path sandbox in tool descriptions
Addresses the low-confidence notes from the Copilot review on #6692.
Both tools' descriptions were pre-sandbox wording, so the model learned
about containment only by attempting a path and reading the error back.
Both now state that access is confined to the tool's allowed directory
and that a path resolving outside it is rejected.
The wording deliberately says "the tool's allowed directory" rather than
"the working directory", because the root is base_dir when one is set,
and naming the absolute root would leak it into the prompt — the same
reason paths are redacted in errors.
Not changed: the notes also suggested advertising `encoding`. That is a
constructor-only field the model cannot set, so describing it to the LLM
would be misleading.
Also fixes a test docstring that contradicted its own assertion — the
public run() path does raise on schema validation failure, which is what
the test asserts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): anchor base_dir at construction so the sandbox cannot move
Addresses the third review round on #6692.
Both remaining findings came from the same habit: storing an unanchored
string and re-resolving it later.
A relative base_dir was kept verbatim and re-resolved against getcwd() on
every call, while the declared file was pinned once at construction. After
a chdir the sandbox root moved but the declared default did not, so one
tool applied two different roots. base_dir is now resolved once — in the
reader's __init__, and via a field_validator on the writer so it also
applies on the model_validate path.
That also covers the serialization concern. model_dump drops the private
pin, and __init__ re-runs on restore, so a relative file_path was
re-anchored against whatever the working directory happened to be at load
time. With base_dir anchored, restore rebuilds the identical pin.
The residual case is a relative file_path with no base_dir, where the
sandbox root is the working directory too — so both move together and the
tool stays self-consistent. Covered by
test_declared_path_survives_a_serialization_round_trip and
test_relative_base_dir_is_anchored_at_construction on both tools.
Also corrects the writer's 'directory' description, README and docs: the
default resolves inside the tool's allowed directory, which is base_dir
when one is set, not always the working directory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(tools): add WaitTool for pausing on long-running jobs
Agents that kick off out-of-band work (a sandbox build, a deployment, an
async API job) have no way to let clock time pass: they either poll in a
tight loop or give up before the work finishes.
WaitTool pauses for a given number of seconds, with an optional reason
echoed back for traces. A single call waits at most max_seconds (default
300, configurable). Longer requests are clamped to the cap and the result
says so, so the model calls again rather than failing. Sync and async
execution are both implemented; stdlib only, no new dependencies.
The tool description spells out when to reach for it (builds, deploys,
batch jobs, async polling, backoff) and when not to, so models pick it up
for the right reason.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): enforce non-negative wait on positional calls, fix doc snippets
BaseTool.run() skips args_schema validation when called with positional
arguments, so tool.run(-5) reached time.sleep(-5) and failed with an
unrelated error. _resolve_duration now enforces the seconds >= 0 contract
itself, covered for both run() and arun().
Docs and README examples are now self-contained: check_build_status_tool
is defined with the @tool decorator instead of referenced out of nowhere,
and the async example awaits inside asyncio.run() rather than at top level.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: point the wait tool card at the edge path
Unprefixed links resolve against the default docs version (v1.15.7),
where the wait tool page does not exist, so the card 404'd in the broken
link check. Prefixing with /edge matches how other edge pages link.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): never cache waits and keep the advertised cap accurate
Two issues from review, both confirmed against the code.
Waits inherited the default cache_function, which always allows caching.
With crew cache enabled, a repeat call with the same arguments returned
"Waited N seconds." straight from the cache without sleeping, turning a
poll-wait-check loop into a busy loop. WaitTool now declares a
cache_function that always refuses.
The description advertising the cap was only rebuilt when max_seconds
reached __init__ without an explicit description. Passing both (as a
platform building from tool.specs.json init params would), calling
model_validate, or assigning max_seconds left the text claiming 300
seconds while clamping to something else. A model_validator now derives
the description from max_seconds on construction, validation, and
assignment, and leaves a caller-supplied description untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(tools): reject NaN waits and pluralize single-second results
_resolve_duration now rejects NaN with its own message instead of letting
time.sleep raise "Invalid value NaN (not a number)" from a positional
call. Infinity keeps clamping to the cap like any other oversized wait.
Result and description text no longer says "1 seconds". Tests use the
public WaitTool().description as the baseline rather than reaching for
module-private helpers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
E2B_API_KEY was declared with required=False on the shared E2B tool
base (E2BExecTool, E2BFileTool, E2BPythonTool), even though none of
the three tools can create or attach to a sandbox without it.
E2B_DOMAIN stays optional since it genuinely defaults to e2b.dev.
Regenerated lib/crewai-tools/tool.specs.json via
generate_tool_specs.py to reflect the change.
`mintlify broken-links` walks every frozen version snapshot under
`docs/` (currently 22 of them), pushing docs PR checks past 14 minutes
and growing with each release. Prune the immutable snapshots — keeping
`edge` and the default (latest) version, which unprefixed links resolve
against — before running the checker, cutting the run to ~30 seconds.
`workflow_dispatch` still checks the full tree, and the deprecated
`mintlify` CLI is swapped for `mint`, which drops the `yes` prompt hack.
* fix(skills): resolve registry skills through the installed AMP client
Skill downloads built their own `PlusAPI` and authenticated it from
`CREWAI_USER_PAT`, the platform integration token, or the saved CLI login.
Managed runtimes have no user credential to offer: they install a client of
their own, which `load_agent_from_repository` already resolves through, so
Agent Repository lookups worked while the skill downloads beside them failed
with 401.
Skills now resolve their client the same way, via `resolve_plus_client()` next
to the hook it reads. A client that can't fetch skills falls back to
environment credentials and warns, so older runtimes behave as they do today.
`resolve_plus_response()` shares the sync/async bridging both lookups need,
since `PlusAPI` is synchronous while managed clients are not.
Version pinning, which the same bug was hiding:
- Registry refs accept `@org/name@version`, and `@org/name@v1.2.0` since people
write it both ways. `parse_skill_ref()` returns a `SkillRef(org, name,
version)`; `parse_registry_ref()` keeps its `(org, name)` shape and drops the
pin, so existing callers are unaffected
- Agent Repository agents record a version per skill, which was parsed off the
response and dropped. Those pins now travel with the refs, so publishing a
new version of a skill no longer changes every agent that uses it
- A pinned ref only accepts a project-local copy declaring that version in its
`metadata.version` frontmatter, and the cache reports a miss when the version
it recorded differs — so a pin re-resolves rather than loading another
version. Unpinned refs keep hitting the cache as before
- An unknown pin fails instead of quietly falling back to the newest version
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(skills): reject a blank version pin instead of floating to latest
A blank `version` passed to `download_skill` read as "unpinned" and quietly
resolved the latest version, which is not what a caller supplying one asked
for — and it disagreed with `parse_skill_ref`, which already rejects empty
pins. Not reachable through `resolve_registry_ref` or the Agent Repository
auto-pinning, both of which only ever pass a non-empty version.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(skills): carry the caller's context into the worker thread
When resolve_plus_response bridges an async client from inside a running loop
it runs the coroutine on a worker thread, which starts with empty ContextVars.
A client reading runtime state there — the platform integration token, flow
context — would see defaults rather than the caller's values, which is hard to
diagnose from the resulting auth or routing failure.
Copy the context across, matching how the parallel-summarization bridge in this
module already does it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
An ordinary agent with a tool fails on the whole GPT-5.6 family:
Agent(role=..., goal=..., backstory=...,
llm=LLM(model="openai/gpt-5.6-sol"), tools=[multiply])
Function tools with reasoning_effort are not supported for gpt-5.6-sol in
/v1/chat/completions. To use function tools, use /v1/responses or set
reasoning_effort to 'none'.
Nothing sets reasoning_effort -- not the user, not CrewAI. The family applies a
server-side default and then refuses it once tools are present. Confirmed with
raw HTTP, no CrewAI involved, on a payload with no reasoning_effort key at all:
gpt-5.6-sol tools, no reasoning_effort key -> 400
gpt-5.6-sol tools, reasoning_effort="none" -> OK
gpt-5.5 tools, no reasoning_effort key -> OK
gpt-5.4 tools, no reasoning_effort key -> OK
gpt-5.2 tools, no reasoning_effort key -> OK
So this is GPT-5.6 only, and it needs an explicit "none" -- dropping the key is
what the rejected request already looked like.
Recovered from the error rather than a model list: catch the 400, resend with
reasoning_effort="none", once. No model names, so a family OpenAI restricts later
works without a release here. Detection matches the structured `param` field plus
the message, so the unrelated "Unsupported value" 400 that o1/o3 return for
"none" isn't mistaken for this one, and the retry can't loop.
Verified against main with real agents (no reasoning_effort anywhere):
model no tools tools tools + reasoning=True
gpt-5.6-sol ok / ok 400 / ok hang / ok
gpt-5.6-terra ok / ok 400 / ok - / ok
gpt-5.6-luna ok / ok 400 / ok - / ok
gpt-5.5 ok ok -
gpt-5.2 ok ok ok / ok
gpt-4o ok ok -
On main, tools + Agent(reasoning=True) produced no output and no error and was
killed at 420s; gpt-5.2 with the same config finishes in ~40s. Both the 400 and
that hang are fixed.
Tests: 15 cases, including agent definitions with tools, with tools plus
reasoning=True, and without tools. tests/llms + tests/agents -> 961 passed
(1 pre-existing unrelated failure from a local OLLAMA_API_KEY env leak).
Ruff + mypy clean.
* fix(openai): make tool calling work on the Responses API path
An agent with tools on api="responses" never produced an answer. It returned the
raw tool-call list instead:
[{'id': 'call_...', 'name': 'multiply', 'arguments': '{"a":17,"b":23}'}]
Three defects in the chain, all on the Responses side only:
1. `is_tool_call_list()` knew the OpenAI-nested, Anthropic, Bedrock and Gemini
shapes but not the Responses one ({"id", "name", "arguments"} -- no nested
"function", no "input"). The list wasn't recognized as tool calls, so the
executor handed it back verbatim as the final answer.
2. `extract_tool_call_info()` read "arguments" only from a nested "function"
object, falling back to "input". For the Responses shape both missed and the
arguments silently became {}, so the tool would have run with no input.
3. With those fixed the tool ran, then the follow-up request 400'd:
Invalid type for 'input[1].content': expected one of an array of objects
or string, but got null instead.
Tool calling is expressed differently by the two APIs. Chat Completions uses an
assistant message carrying `tool_calls` with content: None, then role: "tool"
results. The Responses API uses flat function_call / function_call_output items
keyed by call_id. Those messages were passed through untranslated.
`_to_responses_input()` now converts them. Messages without tool calls pass
through unchanged, so nothing else moves.
Verified end to end against the live API:
api="responses" + tools -> 391 (was raw tool-call JSON)
chained multi-step tool calls -> 400 (17*23, then +9)
completions path (control) -> 391 (unchanged)
The generated `input` payload was also posted to /v1/responses directly and
accepted, and the pre-fix chat-shaped payload confirmed as a 400.
This is why api="responses" never worked for agents: the provider side has had a
full Responses implementation since #4258/c4c9208, but the executor never learned
the shape it emits. Fixing it also unblocks routing gpt-5.4+ tool calls to the
Responses API instead of dropping reasoning_effort.
Tests: 12 cases covering recognition, extraction (including that the Chat
Completions and Bedrock shapes are unaffected), translation of assistant/tool
messages, parallel calls, assistant text alongside tool calls, non-string tool
output, and the full prepared `input` list.
* fix(openai): prefer Responses "call_id" over the item's own "id"
Per CodeRabbit review. A raw Responses function_call item carries both keys with
different values, confirmed against the live API:
keys ['arguments', 'call_id', 'id', 'name', 'status', 'type']
id fc_0adeb715c5d740c7006a65ccb72b948199872ad8b5a5c53108
call_id call_dEoHFrYnOgWYvk17FymdcDZ5
function_call_output must reference call_id. Reading the item's own "id" would
produce a tool result the model can't correlate back to its invocation.
Our own _extract_function_calls_from_response already maps item.call_id into "id",
so the normal path was correct and the existing tests passed. But
extract_tool_call_info is a shared helper reached from every provider's tool loop,
and a raw Responses item is a plausible thing to hand it -- silently picking the
wrong identifier is a bad trap to leave in place for one line of guard.
Tests: raw item extraction asserting call_id is chosen over id, and a round-trip
check that the id extracted from a call is the one sent back with its result.
997 passed across tests/llms, test_agent_utils and tests/agents (1 pre-existing
unrelated failure from a local OLLAMA_API_KEY env leak). Real two-agent chained-tool
run still returns the correct answer.
---------
Co-authored-by: João Moura <joaomdmoura@gmail.com>
The pro tier is not served by /v1/chat/completions. Probing the live endpoints:
model /v1/chat/completions /v1/responses
gpt-5-pro 404 OK
gpt-5.5-pro 404 OK
gpt-5.4-pro 404 OK
gpt-5.2-pro 404 OK
o1-pro 404 OK
o3-pro 404 OK
Since api defaults to "completions", LLM(model="openai/gpt-5-pro") fails with
"Model ... not found", which is misleading -- the model exists, the endpoint is
wrong. OpenAI's own 404 text ("This is not a chat model") doesn't make the fix
obvious either.
These requests now route to the Responses API automatically, which is verified to
work for every model above. An explicit api= setting is always honoured.
Model matching normalizes the configured string first, so "openai/gpt-5-pro" and
"gpt-5-pro-2025-10-06" both resolve to "gpt-5-pro". It's an exact list rather
than a "-pro" substring, so a custom deployment named "gpt-4-pro-custom" isn't
swept up.
The chat-completions 404 handler also gained an actionable message: when the
response says responses-only, or the model is a known pro model, the error names
api="responses" instead of just reporting "not found".
Tests: 29 cases covering name normalization, detection, routing (including that
call() reaches the Responses handler), and both 404 message paths.
bedrock-agentcore 1.7.0 has GHSA-j6g5-3hh3-pgw8 (CVE-2026-16796, high):
argument-delimiter injection in CodeInterpreter.install_packages(). It fails
the pip-audit vulnerability scan on every PR in the repo.
The patch is 1.18.1, which requires boto3>=1.43.31. The old <1.8.0 cap plus
aiobotocore~=3.5.0 (botocore<1.42.92) made that unsatisfiable, so the AWS
stack moves together:
- bedrock-agentcore >=1.7.0,<1.8.0 -> >=1.18.1,<2.0.0
- boto3 ~=1.42.90 -> ~=1.43.46 (aws + bedrock extras)
- aiobotocore ~=3.5.0 -> ~=3.8.0 (aws + bedrock extras)
aiobotocore 3.8.0 allows botocore <1.43.47 and boto3 1.43.46 pins botocore
1.43.46, so the ranges overlap.
Verified: uv lock resolves, pip-audit reports no vulnerabilities (3 existing
ignores, none new), 48 bedrock tests pass, and both bedrock toolkits import
cleanly. BrowserClient.{start,stop,generate_ws_headers} and
CodeInterpreter.{start,stop,invoke} are unchanged in 1.18.1.
* chore: bump json-repair to 0.60.1 and un-ignore fixed vulns in scan
- json-repair 0.25.3 -> 0.60.1 (fixes GHSA-xf7x-x43h-rpqh)
- pyOpenSSL already at 26.2.0 in lock (covers CVE-2026-27448, CVE-2026-27459)
- remove the corresponding --ignore-vuln flags from vulnerability-scan.yml
* fix: adapt _safe_repair_json to json-repair 0.60 semantics
json-repair >= 0.60 returns an empty string for plain-text input and
wraps brace-enclosed junk in a single-element list instead of the old
""/{} sentinel values. Treat both as unrepairable so the original
tool input is preserved.
* chore: fix CI - bump gitpython/pyasn1, drop stale type ignores
- gitpython 3.1.50 -> 3.1.52 (GHSA-2f96-g7mh-g2hx, GHSA-v396-v7q4-x2qj,
GHSA-956x-8gvw-wg5v; fixed in 3.1.51)
- pyasn1 0.6.3 -> 0.6.4 (GHSA-8ppf-4f7h-5ppj, GHSA-hm4w-wwcw-mr6r)
- json-repair 0.60 ships type stubs; remove now-unused
type: ignore[import-untyped] comments flagged by mypy
* fix: dispatch execution_end hook on failed crew and flow executions
The `execution_end` interception point only fired after a successful
kickoff, so consumers never learned about failed runs. Crew kickoff
paths (`kickoff`/`akickoff`) and the flow runtime (`kickoff_async`,
`resume_async`) now dispatch it on the failure path too, with new
additive `status` ("completed"/"failed") and `error` fields on
`ExecutionEndContext`. Pairing flags guarantee exactly-once dispatch,
keep the start/end pairing invariant, and the original exception
propagates unchanged.
* fix: track execution_end pairing per invocation for reentrant flows
Reentrant kickoffs on the same Flow instance are supported (usage
aggregation already accommodates them), but the instance-level pairing
booleans let an inner kickoff's completion mark the outer execution as
ended, skipping the outer failure's `execution_end`. The pairing state
now lives in each `kickoff_async` invocation's locals, and the resume
path passes a per-invocation holder into `_resume_async_body`. Crew
keeps its instance flags since crew kickoffs are not reentrant on the
same instance (`kickoff_for_each` copies the crew).
* fix: handle async get_agent in load_agent_from_repository
The enterprise PlusClient.get_agent() is async, but
load_agent_from_repository() calls it synchronously. When the enterprise
client is hooked in, client.get_agent() returns a coroutine instead of a
response, causing "'coroutine' object has no attribute 'status_code'".
This adds an inspect.isawaitable() check after the call: if the response
is a coroutine, it is properly awaited via asyncio.run() (or via a
thread-pool executor if an event loop is already running).
Co-authored-by: Joe Moura <joao@crewai.com>
* fix: resolve mypy type-checker errors for async awaitable handling
* fix: remove unused type: ignore comment
---------
Co-authored-by: Joe Moura <joao@crewai.com>
Registry downloads initialized PlusAPI without credentials, so uncached skills failed outside CLI-authenticated flows and were blocked entirely in non-interactive environments.
Use CREWAI_USER_PAT first, then the platform integration token, then the saved login token, and pass CREWAI_ORGANIZATION_UUID. Remove the non-interactive cache-only restriction so runtime downloads work.
* feat(skills)!: promote Skills Repository out of experimental
The registry-backed Skills Repository (crewai skill create/publish/
install/list, @org/name refs, global cache) is now mainline:
- CLI: `crewai skill ...` is a top-level group; the CREWAI_EXPERIMENTAL
gate and the now-empty `crewai experimental` group are removed.
- Runtime: registry.py, cache.py, and events.py move from
crewai.experimental.skills into crewai.skills next to the loader;
the require_experimental_skills() gate is gone.
crewai.experimental.skills remains as a deprecated re-export shim.
- Docs: concepts/skills now leads with the CLI workflow and documents
the create -> publish -> install lifecycle.
Linear: n/a (requested promotion)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(skills): org-scoped publish only + docs in all languages
Skills are always scoped to the publishing organization, like tools:
drop the --public/--private flags from `crewai skill publish` and
always send is_public=False to the registry. CLI tests assert the flag
is rejected and the API never receives a public publish.
Translate the new CLI-first Quick Start and the create -> publish ->
install lifecycle section into ar, pt-BR, and ko concepts/skills docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(skills): address review comments on the promotion PR
- Back-compat shim now aliases the old submodules in sys.modules so
`crewai.experimental.skills.registry/cache/events` imports (and patch
targets) resolve to the real crewai.skills modules, not just the
package-root re-exports.
- `crewai skill publish` actually enforces the git-state check that
--force claims to skip: unsynced repos block publishing (mirroring
tool publish); standalone skill dirs outside any git repo publish
without a check.
- Explicit UTF-8 encoding on SKILL.md and cache-metadata reads/writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(skills): fail closed when git state cannot be validated on publish
Follow deploy's pattern: construct git.Repository(fetch=False) and only
treat "not a Git repository" as skippable — any other git error
(fetch/auth/misconfiguration) now blocks publish with a --force escape
hatch instead of silently bypassing the sync check.
Also single-style imports in the shim test (CodeQL) with the dotted
shim import covered via importlib.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(skills): fetch before sync check on publish; bump mcp past advisories
Publish now refreshes remote-tracking refs (repository.fetch()) before
is_synced(), so ahead/behind is judged against the actual remote rather
than stale local refs; a failing fetch blocks publish with the --force
escape hatch. Adds a fail-closed test for fetch errors.
Raise mcp to >=1.28.1,<2 (locks 1.28.1): the ~=1.26.0 pin blocked
GHSA-hvrp-rf83-w775 / GHSA-jpw9-pfvf-9f58 (fixed 1.27.2) and
GHSA-vj7q-gjh5-988w (fixed 1.28.1), which were failing pip-audit on
this PR.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vinicius Brasil <vini@hey.com>
* docs: add Flows in Studio documentation
Documents the new Flows build mode in Studio: why deterministic
workflows with agentic steps matter, the three core node types
(Single Agent, Crews, Router), and Agent Repository publish/pull
sync across organizations.
Includes a rollout banner for the week of July 20th, English source
plus pt-BR, Korean, and Arabic translations, and nav entries for
both edge and v1.15.2 (Crew Studio group renamed to Studio).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: limit Flows in Studio docs to edge version
Versioned snapshots are updated by a separate script, so remove the
v1.15.2 copies and revert its nav changes; the page now lives only
under edge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: sync kickoff-completed event with OUTPUT hook result
`CrewKickoffCompletedEvent` still carried the pre-hook `TaskOutput`, so
AMP/OTEL consumers never saw `OUTPUT` mutations even though the returned
`CrewOutput` was updated. Sync `final_task_output.raw` from the post-hook
payload before emit, matching `FlowFinishedEvent`.
* style: drop OUTPUT sync comment and rename crew output test
This commit adds the organization ID parameter to the PlusAPI client, in
addition to the settings file. This allows for settings the organization
programmatically.
Repository responses can include null optional fields such as
`reasoning`. Treat them as omitted so Agent defaults apply instead of
failing validation.
* docs: group execution hooks and document all hook contexts
The hooks pages sat flat in the learn nav and only documented the LLM
and tool call contexts, with examples built on the legacy decorators.
Groups them under a collapsible "Execution Hooks" section, adds
`step-hooks` and `execution-boundary-hooks` pages covering every hook
context from source, reworks the LLM and tool pages to lead with `@on`
while keeping the decorators, and links the orphaned
`before-and-after-kickoff-hooks` page into the group.
* docs: prefix hook cross-links with /en so they resolve in edge
The new edge-only hooks pages linked each other with versionless paths
like `/learn/step-hooks`, which mintlify resolves against the frozen
default version where those pages do not exist, breaking the CI link
check. Uses the `/en/learn/...` form the other edge pages already use.
* docs: use /edge prefix for links to edge-only hooks pages
The `/en/learn/...` form still resolves against the default frozen
version, where `step-hooks` and `execution-boundary-hooks` do not exist
yet, so the link checker kept failing. Links now use the explicit
`/edge/en/learn/...` form, matching how `consuming-streams.mdx` linked
to edge-only streaming pages before they were frozen.
* feat: add pre_step and post_step interception points on task execution
Introduces `StepContext` and the two step points in the dispatcher, and
wires them around agent execution in `task.py` (sync and async paths):
`pre_step` fires after `TaskStartedEvent` with the task context as
payload, `post_step` fires before `TaskCompletedEvent` with the
`TaskOutput`, and hook replacements are rebound in both directions.
* feat: wire pre_step and post_step on flow method execution
Dispatches the step points around each flow method with
kind="flow_method": `pre_step` receives the dumped call params and maps
returned edits back onto args/kwargs, `post_step` can rewrite the method
result before it is recorded. Conformance tests cover per-method firing
and output rewriting.
* docs: rework execution hooks page around the @on api
Replaces the standalone interception hooks catalog with a single
`execution-hooks.mdx` page that teaches `@on` as the primary way to
write hooks, covering the full ten-point catalog across task, flow, and
LLM execution. The legacy per-point decorators stay documented in a
closing section, and the `docs.json` navigation drops the removed page.
* feat: wire execution-boundary interception points
Adds the typed interception contexts (`crewai/hooks/contexts.py`) and wires
the `execution_start`, `input`, `output`, and `execution_end` points for both
crews and flows through the dispatcher. `prepare_kickoff` and
`Flow.kickoff_async` fire `execution_start`/`input` so a hook can rewrite
resolved inputs before the run, while `Crew._create_crew_output` and the flow
tail fire `output`/`execution_end` so the final result can be observed or
replaced. Closes the eight critical-path points without touching the legacy
hooks.
* fix: correct execution-boundary hook ordering and input aliasing
Reworks the crew and flow boundary seams flagged in review. `OUTPUT` and
`EXECUTION_END` now run before the completion event (`CrewKickoffCompletedEvent`
and `FlowFinishedEvent`) so a `HookAborted` no longer leaves a spurious
completed signal and a returned payload replacement is honored on the emitted
and returned result. Boundary contexts alias `inputs` to the same object as
`payload` instead of a fresh dict from `or`, so in-place edits survive
read-back. Flows re-publish the resolved inputs into `flow_inputs` baggage
after the `INPUT` hook so trigger-payload injection observes hook rewrites, and
a resumed flow now dispatches `OUTPUT`/`EXECUTION_END` on its completion path.
* chore: drop redundant seam comments from execution-boundary wiring
Removes two inline comments narrating the OUTPUT/EXECUTION_END dispatch
ordering in `crew.py` and the flow runtime, plus a stray sentence about
enterprise adapters in the conformance-suite docstring. Comment-only
cleanup, no behavior change.
* fix: keep crew output typed across boundary hook dispatch
`_create_crew_output` reassigned `crew_output` from the hook contexts'
`payload`, which is typed `Any`, so mypy flagged `no-any-return` at the
function's return. Cast the payload back to `CrewOutput` after each
dispatch and split the `ExecutionEndContext` construction to satisfy
`ruff format`'s line-length limit.
* feat: add generic interception-hook dispatcher
Introduces `crewai/hooks/dispatch.py` as a single engine behind every
interception point: a hook receives a typed context, may mutate or replace
its `payload`, or raise `HookAborted(reason, source)` to stop the operation.
The full `InterceptionPoint` catalog is frozen from day zero, with global and
contextvar-scoped registries, an `@on` decorator, a no-op fast path, and a
`HookDispatchedEvent` for telemetry. The four existing `before/after_llm_call`
and `before/after_tool_call` hooks become adapters over the dispatcher, so the
legacy dialect and `return False` semantics keep working unchanged while
gaining the new contract.
* fix: harden interception dispatcher against review findings
Corrects several dispatcher edge cases surfaced in review. `_default_reducer`
now reports a modification only when a `payload` is actually applied, the
`agents=` filter falls back to `agent_role` for contexts without an `agent`
object, and `unregister` resolves the filter wrapper stashed by `on` so a
filtered hook can be removed. The tool-hook runners honor the executing
agent's `verbose` flag instead of silently swallowing hook errors, and the
ReAct tool path now runs `POST_TOOL_CALL` on blocked calls to match the
native paths. Also adds abort-telemetry coverage and replaces the flaky
absolute no-op timing budget with a relative one.
* fix: honor scoped hooks on direct llm calls and register @on crew methods
Direct agent-less LLM calls short-circuited on the empty global hook list,
so hooks registered only for the current `scoped_hooks()` context never
ran; the direct-call helpers now defer to `dispatch`, which resolves
scoped hooks behind its own no-op fast path. `CrewBase` likewise only
scanned the legacy `is_*_hook` markers, so `@on(InterceptionPoint.X)`
methods were silently dropped — it now registers them on the dispatcher
with filters applied and `self` bound. Also tightens result typing across
the tool-call seams so `mypy` stays green.
* refactor: scope InterceptionPoint to the points this layer wires
The dispatcher only fires the model- and tool-call boundaries, so
`InterceptionPoint` now lists just those four rather than the full future
catalog. New points are introduced alongside the seams that dispatch them,
keeping every layer free of enum members with no live consumer. The
dispatcher unit tests that borrowed unused points as generic examples are
remapped onto the four kept points.
* test: pin per-hook fail-open at the LLM and tool seams
The dispatcher swallows a hook's exception per hook rather than around the
whole loop, so one buggy hook no longer silently skips every hook registered
after it. These seam-level tests pin that behavior through
`_setup_before_llm_call_hooks` and `run_before/after_tool_call_hooks`, and
confirm an intentional `return False` block still short-circuits later hooks.
* fix: run execution-scoped hooks on the agent executor model seams
`_setup_before/after_llm_call_hooks` only ran the executor's snapshot
hook lists, so hooks registered via `scoped_hooks()` never fired on
`PRE/POST_MODEL_CALL` during normal agent execution, while the tool
seams (which go through `dispatch`) merged them. The seams now append
the current scope's hooks after the snapshot via `get_scoped_hooks`,
matching dispatch's global-then-scoped ordering, and a scoped-only
registration no longer short-circuits the seam.
* fix: don't clobber native tool-call responses in after-LLM hooks
Registering any `after_llm_call` hook broke native tool execution: the
executor invokes `_setup_after_llm_call_hooks` on the intermediate
response that carries the model's tool calls, the non-str payload was
stringified for the response-rewrite pass, and the executor then treated
that string as a final answer instead of executing the tools. Structured
payloads (neither `str` nor `BaseModel`) now pass through untouched,
mirroring the isinstance guard `_invoke_after_llm_call_hooks` already
applies on the direct-call path; hooks still fire on the follow-up
textual response.
Fixes#6529
* style: shorten the tool-call guard comment
The vulnerability scan started failing when PYSEC-2026-2132 (click) and
PYSEC-2026-2253..2257 (pillow) were published on Jul 12. Both have fixed
releases within our constraints, so `uv.lock` upgrades click to 8.4.2 and
pillow to 12.3.0. A newer json-repair advisory (GHSA-xf7x-x43h-rpqh) also
surfaced; its fix is outside the `json-repair~=0.25.2` pin and 0.25.x lacks
the vulnerable `schema_repair` module, so it joins the ignore list in
`vulnerability-scan.yml` with a justification.
handle_turn() (and stream_turn) decided "did the handler append its
reply?" by snapshotting the assistant-message count before kickoff and
appending the stringified result when the count came back unchanged. A
handler that appends its reply and then trims state.messages to a cap —
a normal bounded-context pattern — left the count unchanged, so the
fallback appended the reply a second time on every turn once trimming
engaged, and the duplicates then crowded real turns out of the capped
window.
Replace the count heuristic with an explicit per-turn flag:
append_assistant_message() sets _assistant_reply_appended, handle_turn
and stream_turn clear it before kickoff and only fall back when no
assistant message was appended during the turn. The now-unused
_assistant_message_count() helper is removed.
Fixes EPD-181.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(tools)!: make tool-result caching opt-in instead of on by default
Tool-result caching defaulted to on (Crew.cache=True, and standalone
agents self-wired a CacheHandler at construction), so an LLM calling
the same tool with identical arguments twice in one run silently got
the first result back without the tool executing. For live-data tools
that is a confidently stale answer; for state-mutating tools the second
action is silently dropped.
Caching is now opt-in with the machinery unchanged:
- Crew.cache defaults to False; Crew(cache=True) restores today's
behavior exactly (agents still default to participating when a crew
offers its handler, and Agent(cache=False) still opts an agent out).
- Standalone agents no longer self-wire a cache; Agent(cache=True) or
an explicit cache_handler opts in. Previously even Crew(cache=False)
agents cached via this self-wired handler.
- Per-tool cache_function write gating is unchanged once opted in.
Existing tests that exercised the caching machinery now opt in
explicitly; new regression tests cover the default (both identical
calls execute), crew-level opt-in dedup, and agent-level wiring.
Fixes EPD-180.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): don't let copy() turn the cache default into an explicit opt-in
Agent.copy() rebuilds from model_dump(), which includes the field
default cache=True, so the copy's model_fields_set contained "cache"
and _setup_agent_executor wired a CacheHandler the source agent never
opted into (Bugbot review finding). Drop "cache" from the dump when it
was not explicitly set on the source; explicit opt-ins still survive
copying.
Also sync the Crew and BaseAgent class docstrings with the new opt-in
cache semantics (CodeRabbit review findings).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): preserve cache_handler-only opt-in across Agent.copy()
copy() excludes cache_handler from the rebuilt agent, so an agent that
opted into tool-result caching solely via an explicit cache_handler
lost caching after copy() (Bugbot review finding). Carry the consent as
cache=True on the copy when the source has a handler wired and hasn't
explicitly disabled caching — the copy wires its own fresh handler,
matching pre-change copy semantics (copies never shared the source's
handler instance).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(crew): offer the crew cache handler to the hierarchical manager
The hierarchical manager agent is created in _create_manager_agent,
outside the validation-time agents loop that offers the crew's cache
handler — and managers no longer self-wire a handler — so
Crew(cache=True) hierarchical runs never cached the manager's
delegation tool calls (Bugbot review finding). Offer the shared crew
handler when the crew opted in; a user-provided manager with
cache=False stays excluded via the existing set_cache_handler gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): only construction-time cache opt-ins survive Agent.copy()
The previous copy() fix treated any wired cache_handler as consent, but
agents that merely received the crew's shared handler at kickoff
(set_cache_handler from Crew(cache=True)) never opted in themselves —
their copies must not become standalone cachers (Bugbot review
finding). Record the opt-in signal in _setup_agent_executor, which runs
at construction before any crew wiring can happen, and have copy()
consult that flag instead of inspecting cache_handler after the fact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(tools): stop rewriting the authored tool description at construction
BaseTool.model_post_init silently replaced the public description field
with the LLM-facing composite ("Tool Name: ...\nTool Arguments: ...\n
Tool Description: <authored>"), breaking equality assertions on authored
text and hiding the extra prompt tokens from token-careful authors.
The authored description now survives construction as written. The
composite is composed on demand via a new formatted_description property
on BaseTool and CrewStructuredTool (shared format_description_for_llm
helper), and every prompt path that relied on the baked-in composite —
render_text_description_and_args, ToolUsage._render, and tool-usage
error messages — now renders through it, so the text the LLM sees is
unchanged.
The helper strips any pre-existing composite block before composing, so
tools deserialized from old checkpoints and adapters that still bake the
composite into the field (e.g. the crewai-tools MCP adapter) don't get
double-wrapped. BaseTool._generate_description remains as a no-op hook
because subclasses override it and model_post_init still calls it.
Fixes EPD-179.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(tools): harden composite-description handling after review
- Anchor the pre-baked-composite check to the actual three-line block
shape instead of a naive substring match, so authored prose that
merely mentions "Tool Description:" is never truncated (CodeRabbit /
Bugbot review finding). Shared as
strip_composite_description_prefix() and reused by the function-
calling schema builder, which had the same naive split.
- Make render_text_description_and_args tolerate duck-typed tools
without a real formatted_description string (fixes CI: step-executor
tests pass Mock tools whose auto-created attribute is not a str).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
Agent.kickoff() returned LiteAgentOutput with a plain dict at
.usage_metrics and no token_usage attribute, while Crew.kickoff()
returned CrewOutput with a UsageMetrics object at .token_usage and no
usage_metrics attribute — so a usage accessor written for one path
raised AttributeError on the other, and every consumer had to
duck-type both shapes.
Give both result types both surfaces, each name with one consistent
shape everywhere: .token_usage is a UsageMetrics object and
.usage_metrics is a plain dict, on both LiteAgentOutput and CrewOutput.
Added as read-only properties, so existing fields, serialization, and
constructors are unchanged.
Fixes EPD-178.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): report per-call usage metrics on kickoff results
Agent.kickoff() populated result.usage_metrics from the LLM instance's
lifetime token accumulator, so counts grew across calls and pooled
across agents sharing one LLM object — a second agent's first turn
appeared to cost the whole preceding session.
Snapshot the accumulator when a kickoff starts and report the delta on
the result (guardrail retries included), via the new
UsageMetrics.delta_since(). The LLM instance's cumulative counters are
untouched: get_token_usage_summary() keeps lifetime totals for
crew-level aggregation, and its docstring now states that scope
explicitly. Applies to both Agent and the deprecated LiteAgent, sync
and async paths.
Fixes EPD-177.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(agent): drop lite_agent.py diff, add guardrail-retry usage test
Per review: LiteAgent's kickoff path is no longer used, so the per-call
usage snapshot only needs to live in agent/core.py — revert the
lite_agent.py changes entirely. This also removes the duplicated
_current_usage_summary helper and the instance-attr baseline CodeRabbit
flagged.
Add the requested guardrail-retry regression test: a guardrail that
rejects the first attempt and accepts the second must yield
usage_metrics covering both attempts (2x a single-attempt kickoff).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
In conversational flows, a falsy return from an overridden route_turn()
fell back to the sticky state.last_intent from a previous turn, silently
re-running the prior turn's handler for an unhandled input.
The fallback exists for the legacy default_intents path, where
receive_user_message() classifies the intent fresh each turn. Track that
per-turn classification in _turn_classified_intent (cleared on every turn
reset) and route on it instead, so a falsy route_turn() now falls through
to the built-in answer_from_history/converse defaults and never reuses
stale routing state.
Fixes EPD-176.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* feat(cli): run declarative flows on the TUI with a headless terminal fallback
Declarative flows now run on the CrewRunApp TUI when interactive, matching
declarative crews and conversational flows. Headless contexts — CREWAI_DMN
(deploy), piped output, CI, any non-TTY — fall back to the direct-terminal
kickoff, gated by is_interactive() (folds in the CREWAI_DMN check and requires
a real TTY).
The TUI shows per-method progress: a new STEPS panel driven by flow method
events (FlowStarted / MethodExecutionStarted/Finished/Failed), each labeled
with its declarative call type (crew/agent/expression/…) read from the flow
definition. Crews/agents inside a method keep streaming in the main panel via
the existing crew/task/LLM handlers.
- crew_run_tui.py: _run_flow_worker (flow.kickoff in a thread worker; reuses
_on_crew_done/_on_crew_failed + _stringify_output), _is_flow_run gate so crew
rendering is byte-identical, flow-event subscriptions building _flow_steps,
and the STEPS sidebar + flow-aware header.
- run_declarative_flow.py: is_interactive() branch → _run_declarative_flow_tui
(EventListener, method-type map from flow._definition, crew-parity exit codes
and deploy chaining) or the existing terminal path.
Deviation from the approved plan: gate on is_interactive() rather than
is_dmn_mode_enabled() alone, so non-TTY runs (CI/pipes/CliRunner) never launch
a TUI — this also keeps existing headless flow tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): force flow events on for the TUI so STEPS renders under suppress_flow_events
Review follow-up: the STEPS panel and header are driven by flow method events
(FlowStarted / MethodExecution*), but the declarative runtime skips emitting
those when the flow declared config.suppress_flow_events. Interactive TUI runs
would then keep STEPS on "waiting…" and the header on "Starting flow…" while
nested crews still execute.
_run_declarative_flow_tui now forces flow.suppress_flow_events = False for the
interactive run (mirroring how the conversational path mutates the flow for the
TUI). The headless/terminal path never reaches this and keeps the flow's
declared setting. Regression test: test_run_declarative_flow_tui_enables_flow_events.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): clear flow header's current method when a method ends
Review follow-up: the flow header keys off _current_method, which was set on
MethodExecutionStarted but never cleared on Finished/Failed. Between steps (or
after a failed method before kickoff exits) the header kept spinning the old
method name while the STEPS sidebar already showed it done/failed.
_clear_current_method now drops the header's active method when it ends,
falling back to another still-active step (methods can overlap) or none. The
header's idle fallback shows "Working…" once a step has run and "Starting
flow…" only before the first method.
Tests: test_current_method_clears_and_falls_back_across_overlap, plus a
_current_method assertion in test_flow_method_events_build_steps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix: suppress flow console panels in TUI mode; clear header agent on method change
Two review follow-ups:
1) Method panels break Textual TUI (Cursor): forcing suppress_flow_events off
so the STEPS panel receives events also un-gated the EventListener's Rich
flow/method panels (ConsoleFormatter.print_panel prints is_flow=True panels
regardless of verbose), which interleave with Textual and corrupt the TUI.
print_panel now skips is_flow panels when is_tui_mode() is set (the same
context the TUI worker already establishes and the tracing listeners already
honor). Non-TUI/headless flow runs are unaffected. Test:
test_console_formatter_tui_mode.
2) Flow header showed a stale agent (CodeRabbit): _current_agent persisted
across methods. It's now cleared when a method starts and when the active
method changes, so the header never shows the previous method's agent until
a new agent event arrives. Test: test_flow_method_transitions_clear_current_agent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): keep flow name over nested crews; show paused flow methods
Two review follow-ups on the flow TUI:
1) Crew kickoff renamed the flow (Cursor): CrewKickoffStartedEvent overwrote
_crew_name / the app title with a nested `call: crew` step's crew name, so
the post-run summary could be labeled with a child crew. The rename is now
gated on `not _is_flow_run`, preserving the flow's name; crew runs still
adopt the crew name. Tests: test_crew_kickoff_does_not_rename_flow_run,
test_crew_kickoff_renames_in_crew_mode.
2) Paused methods showed active (Cursor): the TUI didn't handle
MethodExecutionPausedEvent, so a @human_feedback pause left the STEPS
spinner running (flow status panels are suppressed in TUI mode). It now
marks the step "paused" (⏸, teal) and the header shows "waiting for
feedback" instead of a spinner. Test: test_method_paused_marks_step_paused.
Note: interactively *providing* human feedback from the flow TUI is a separate
follow-up; this only makes the pause visible instead of a silent stuck spinner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): run human-feedback declarative flows on the terminal, not the TUI
Two review follow-ups, both rooted in @human_feedback methods:
- Paused flow marked complete (Cursor): async human feedback makes kickoff
RETURN a HumanFeedbackPending marker (not raise), which _run_flow_worker
would stringify and report as a successful completion with exit 0.
- Sync feedback breaks TUI (Cursor): default (sync) @human_feedback collects
input via the flow runtime's Rich console.print + blocking input(), which
interleaves with Textual and leaves the user unable to review output or
submit feedback.
run_declarative_flow now routes any flow whose declarative definition declares
human feedback (_flow_uses_human_feedback) to the terminal path, where blocking
input and Rich prompts work natively — regardless of interactivity. Non-feedback
flows still get the TUI. Tests: test_flow_uses_human_feedback_detection,
test_human_feedback_flow_uses_terminal_even_when_interactive.
Fully interactive human feedback inside the TUI remains a separate follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* refactor(cli): address review — Flow typing, debug logging, flow-vs-crew naming
Review follow-ups from @lucasgomide:
- Type flow helpers as Flow[Any] (via TYPE_CHECKING import) instead of Any and
drop the defensive getattr chains — _definition is a typed PrivateAttr and
name/suppress_flow_events are typed fields, so attribute access is safe.
- Replace the silent `except Exception: pass` blocks with logger.debug(...,
exc_info=True) so unexpected failures are diagnosable in the field
(_flow_method_types, _flow_uses_human_feedback, suppress_flow_events toggle).
- Flow-vs-crew naming: the flow worker now uses group="flow" (was the
misleading "crew"), and the shared completion/failure handlers report the
run with an entity-aware noun ("flow" vs "crew") via _run_noun.
Deferred (separate PR): the os._exit(130) hard-kill on user quit is kept as-is
to match the existing crew convention (run_crew._run_json_crew).
Tests: test_flow_done_uses_flow_wording_for_unfinished_tool; existing crew
wording tests unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Support legacy OpenAI base URL env var
* Add custom OpenAI-compatible endpoint support
* Refactor OpenAI completion module test to restore original module state
- Added logic to save and restore the original OpenAI completion module during the test to prevent issues with class re-imports affecting subsequent tests.
- Ensured that the test checks for the presence of the module and its attributes only after the module is properly reloaded.
- Improved test reliability by avoiding potential failures due to module state changes across tests.
* addressing comments
* fix: drain memory writes before kickoff and flow completion events
Background memory saves from the final task could still be in flight
when `CrewKickoffCompletedEvent`/`FlowFinishedEvent` fired, so telemetry
listeners tore down before `MemorySaveCompletedEvent` arrived and the
save span surfaced as "Span orphaned" errors in traces despite the
record persisting. `Crew` now drains all pending saves — including
per-agent `agent.memory` pools, which the old `finally`-only drain
missed entirely — before emitting the completion event, with the same
ordering applied to both `FlowFinishedEvent` emit paths in the flow
runtime.
* fix: address review findings on the memory drain paths
Bugbot and CodeRabbit flagged gaps in the drain coverage: the
hierarchical `manager_agent` memory pool was never drained,
`Crew.akickoff` lacked the exception-path safety net that sync
`kickoff` has, and `finalize_session_traces` emitted the deferred
session-end `FlowFinishedEvent` without draining first. Also offloads
the pre-emit drains in the flow runtime to `asyncio.to_thread` so the
blocking wait doesn't stall other coroutines sharing the event loop.
* fix: flush event bus after memory drain in flow completion paths
Bugbot flagged that flow paths went straight from the memory drain to
`FlowFinishedEvent`, while crew kickoff flushes the bus in between.
Save completion events emitted during the drain could still have
pending async handlers when flow-finished triggered trace teardown.
Adds a `crewai_event_bus.flush()` after the drain at both flow runtime
emit sites and in `finalize_session_traces`, mirroring
`Crew._create_crew_output`.
Follow-ups to #6462's caching:
1. Key the catalog cache by the exact API key (via a short, non-reversible
sha256 digest — never the key itself), not just key-present vs absent.
Switching to a different key for the same provider now misses the previous
account's entry and refetches, instead of showing the old account's models.
2. Never cache local providers (Ollama). /api/tags is fast and installed
models change out-of-band, so caching could keep offering a model the user
just deleted until the entry expired. _is_cacheable() gates both cache read
and write; the picker now re-probes every call and reflects what's installed.
3. Shorten the dynamic catalog TTL from 6h to 5m — a stale list (new/removed
models, account changes) is worse than a ~1s refetch, and the cache only
needs to spare repeated fetches within a wizard session.
Tests: distinct-key cache entries, digest never stores the raw key, Ollama not
cached (reflects deletions / never written), and dynamic TTL expiry.
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): unify `crewai run` flow input resolution; prompt from state schema
`crewai run` resolved the configured [tool.crewai] flow, but `--inputs` was
hard-gated behind `--definition` and routed through a separate branch — the two
ways of pointing at the same flow didn't share resolution, and required inputs
were never detected, prompted, or validated (a missing field only blew up at
runtime).
Now inputs and definition come from one place:
- Remove the "--inputs requires --definition" gate (cli.py, run_crew.py,
run_declarative_flow.py). `--inputs` alone resolves the configured flow,
exactly like a bare `crewai run`; `--definition` is purely an override. The
project-env re-exec forwards `--inputs` instead of rejecting it.
- Read the flow's state schema from the runtime Flow instance
(`type(flow.state).model_json_schema()`), which is reliable for both inline
`json_schema` and ref-imported `pydantic` state (the static definition's
json_schema is None for the common ref case).
- Plain `crewai run` detects required state fields (minus those satisfied by
state defaults) and prompts for them interactively, showing each field's
description; skipped in non-interactive / CREWAI_DMN mode.
- Validate against the schema before kickoff: pointed
"Missing required input 'x' — <description>" errors, and warn on unknown keys
with a did-you-mean suggestion (catches typos like `prospect_emai`).
`--inputs` on a non-flow project now errors clearly ("only supported for
declarative flows") instead of the old confusing gate.
Tests: schema-driven prompt/validate/override paths, unknown-key warning,
defaults-satisfy-required, type validation, and re-exec input forwarding.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): forward reserved `id` input to flow kickoff; ruff format
- Cursor: the schema filter treated an `id` key in --inputs as unknown and
dropped it, regressing kickoff's persistence-restore support (inputs["id"]).
Let `id` pass through untouched (test: reserved_id_input_is_forwarded).
- Apply ruff format to run_declarative_flow.py (fixes the lint-run
`ruff format --check` step).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): don't block persistence-restore resume on schema validation
Cursor (High): `crewai run --inputs '{"id":"…"}'` is a persistence resume —
kickoff hydrates full state from storage, so schema-required fields may come
from the restored state rather than --inputs. The new required-field
prompt/validation was erroring/prompting before kickoff, breaking resume. When
`id` is present in --inputs, forward the inputs unchanged and skip the
prompt/validation. Test: test_id_only_input_skips_required_validation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): load project .env in the declarative-flow runner
The declarative-flow path never loaded .env — flow projects (type = "flow")
missed API keys/config that crew projects pick up. The JSON-crew path loads
Path.cwd()/.env with override=True (run_crew._run_json_crew); mirror that at
the top of run_declarative_flow() so flow projects behave the same regardless
of where crewai is installed. Test: run_declarative_flow_loads_project_env.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* feat(cli): unify runtime-input prompting across declarative flows and crews
Declarative (JSON) crews now resolve inputs the same way declarative flows
do, via a shared crewai_cli.input_prompt module (prompt_for_inputs,
parse_inputs_json, closest_name, is_interactive):
- accept --inputs (previously rejected for crews), forwarded to the crew
subprocess via CREWAI_JSON_CREW_INPUTS and validated before spinning up uv
- layer --inputs over the crew's declared `inputs` defaults
- prompt for missing {placeholder}s with the same UX as flows, and error
cleanly with a pointed per-name message when non-interactive
- warn on unknown keys with a "did you mean" suggestion
Unlike flows — whose state schema is authoritative, so unknown keys are
dropped — the crew placeholder scan is heuristic (agent/task text fields
only), so unrecognized keys are warned about but kept, to avoid discarding a
value a field the scan doesn't cover may rely on.
--inputs remains rejected for classic (Python/YAML) crews, which take their
inputs from main.py. run_declarative_flow's private input helpers move to the
shared module with no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* test(crewai): update mirrored CLI test after run_crew input refactor
lib/crewai/tests/cli/test_run_crew.py imports crewai_cli internals and is
collected by the lib/crewai test job (Run Tests). It still imported
_prompt_for_missing_inputs, which was replaced by _resolve_crew_inputs, so
the module failed to import — erroring pytest at collection and cancelling
the rest of the matrix via fail-fast.
Point it at _resolve_crew_inputs and patch the prompt in the shared
crewai_cli.input_prompt module where prompting now lives.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): filter unknown --inputs keys even on flow persistence restore
Review follow-up: the `id` (persistence-restore) branch of
_resolve_flow_inputs returned the raw payload, so typo keys passed alongside
`id` skipped the unknown-key warning/drop and reached kickoff — which can
fail strict (extra="forbid") flow state models. The restore path now still
warns on and drops unknown keys (keeping `id` and known state fields); it
only skips the required-field prompt and pre-kickoff validation, which
persistence hydrates. Regression test: test_id_restore_still_drops_unknown_keys.
Also drop the duplicate module import in test_input_prompt.py (both `import`
and `from ... import` of crewai_cli.input_prompt) flagged by the code-quality
bot; monkeypatching now uses the string target form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: rename ACP rules to policies across edge and v1.15.1 locales with redirects
* docs: point ACP policies pages at the new policy screenshots
* docs: address review — revert frozen v1.15.1 edits and fix redirect ordering
* docs: prefix edge policies cross-links so they resolve until the next version cut
* docs: move policies redirects above all wildcard redirects
* feat(cli): pull latest LLM models dynamically in the crew wizard
The JSON-crew creation wizard hardcoded a short model list per provider,
which goes stale as vendors ship new models every few weeks. Add a
three-tier resolver that prefers live data and falls back to a curated list.
- New `model_catalog.get_provider_models(provider, fallback)`:
1. Vendor API (openai/anthropic/gemini/groq/cerebras/ollama) when the
provider key is already in the environment — the only reliably-fresh
source (real release dates / display names).
2. Curated hardcoded fallback — hand-verified, used when no key is set.
3. LiteLLM feed — only for providers with no curated list; it lags real
releases, so it must never preempt the curated fallback.
- Rank by date/version parsed from model ids, humanize labels, 6h cache,
short timeouts, silent fallback on any error.
- Wire it into `create_json_crew._select_model()` (picker only).
- Refresh the curated fallback against each vendor's official model docs
(Anthropic Fable 5 / Opus 4.8 / Sonnet 5; OpenAI GPT-5.5(+pro); Gemini
3.5 Flash / 3.1 Pro preview / 3 Flash preview; Groq Llama 4 / GPT-OSS).
- Tests for ranking, chat filtering, caching, and the tier order (17 tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): address model_catalog review findings
- Key the Ollama catalog cache by its base URL so a changed OLLAMA_API_BASE /
API_BASE no longer serves the previous host's models for up to the TTL.
- Negatively cache the curated fallback after a failed/empty fetch (short
_NEGATIVE_TTL) so the picker doesn't repeat a timeout-prone vendor/LiteLLM
request on every call — most impactful for a down local Ollama server.
- Guard _read_catalog_cache / _write_catalog_cache against a non-dict cache
root (corrupt JSON array no longer raises AttributeError).
- Replace the two empty `except OSError: pass` blocks with
contextlib.suppress(OSError) plus an explanatory comment (CodeQL empty-except).
- Tests: negative cache, base-keyed Ollama cache, corrupt-cache no-crash (20 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): guard null litellm_provider and paginate Gemini models
- _from_litellm: coerce a present-but-null `litellm_provider` before string
ops so it's skipped instead of raising AttributeError (keeps the documented
"never raises" contract).
- _fetch_gemini: walk models.list pages via nextPageToken (bounded to 10) —
the API is paginated and not guaranteed newest-first, so a single page could
drop models the ranking should consider.
- Tests for both (22 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* ci: ignore nltk PYSEC-2026-597 in pip-audit (no fix, not reachable)
pip-audit newly flags nltk 3.9.4 for PYSEC-2026-597 (CVE-2026-12243), a path
traversal via percent-encoded `..%2f` in nltk.data.load()/find(). It affects
all nltk versions <=3.9.4 with no patched release, so it can't be resolved by a
version bump — same situation as the already-ignored PYSEC-2026-97.
nltk is a transitive dependency (unstructured[local-inference, all-docs] in
crewai-tools) used for text tokenization; we never pass untrusted resource
URLs/paths to nltk.data, so the traversal is not reachable. Add it to the
curated --ignore-vuln list with a justification, matching the existing pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): address second Cursor review round on model_catalog
- Cache ignores new API keys: include API-key presence in the cache key
(`<provider>#key|#nokey`), so a key added after a no-key/negative-cached
lookup triggers a fresh live fetch instead of serving the stale fallback.
- Bad LiteLLM cache crashes picker: `_from_litellm` now requires a dict from
`_load_litellm_data` (a non-mapping JSON root is skipped, not `.items()`'d).
- Stale LiteLLM refetch loop: memoize the feed load once per process
(`_litellm_memo` + `_reset_litellm_memo` test hook) so repeated uncurated-
provider lookups don't each re-attempt a timed download when offline.
- Tests: new-key bypass, corrupt-litellm-cache no-crash, one-fetch-per-process
(25 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): keep partial Gemini results on later-page fetch error
_fetch_gemini paginates; a network/HTTP error on page 2+ previously raised out
through _from_vendor, discarding models already parsed from earlier pages and
forcing the curated fallback. Catch per-page fetch errors and return the
partial set instead (a first-page failure still yields an empty list -> fallback).
Test: test_vendor_gemini_keeps_partial_on_later_page_error (26 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): don't let an invalid fresh LiteLLM cache block download
_fetch_litellm_data treated any truthy JSON root in a fresh provider_cache.json
as the feed and returned it, so a non-mapping root (e.g. a JSON array) was
memoized and the tier never re-downloaded until the file aged out — leaving
uncurated providers with an empty picker despite a recoverable cache. Only
short-circuit on a usable dict; otherwise fall through to the download.
Test renamed to test_invalid_litellm_cache_falls_through_to_download (asserts
recovery via refetch).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): honor OLLAMA_HOST and treat empty vendor list as authoritative
- Ollama host mismatch: _ollama_base now also reads OLLAMA_HOST (the Ollama
runtime convention) after OLLAMA_API_BASE/API_BASE, normalizing a scheme-less
value (e.g. "127.0.0.1:11434" -> "http://127.0.0.1:11434"), so users who set
only OLLAMA_HOST see models from the server the crew will actually use.
- Empty vendor list: a successful vendor fetch returning no models is now
authoritative instead of collapsing to the curated fallback. A reachable
Ollama with nothing installed yields an empty list (the picker prompts for
manual entry) rather than offering hardcoded models that aren't installed; a
failed fetch still falls back. _from_vendor now returns [] on success-empty
and None only when the tier is unavailable.
- Tests: ollama empty->manual, ollama down->fallback, OLLAMA_HOST resolution
(29 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): Gemini first-page failure falls back instead of showing empty
Interaction from the prior two fixes: _fetch_gemini swallowed a first-page
error and returned [], which _from_vendor reported as a successful-empty result
and get_provider_models treated as authoritative — skipping the curated Gemini
fallback and jumping to manual entry. Now a first-page failure (nothing gathered
yet) re-raises so _from_vendor returns None and the curated list is used; a
later-page failure still keeps the partial results.
Test: test_vendor_gemini_first_page_error_uses_fallback (30 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): Gemini GOOGLE_API_KEY + Ollama recovery not blocked by cache
- Gemini ignores GOOGLE_API_KEY: _PROVIDER_KEY_ENV now maps each provider to a
tuple of accepted env vars; Gemini accepts GEMINI_API_KEY or GOOGLE_API_KEY
(matching crewai's own Gemini provider). A new _provider_api_key() resolver
is used by both _from_vendor and the cache key, so a GOOGLE_API_KEY user gets
the live models API instead of the stale curated fallback.
- Ollama recovery blocked by cache: skip the negative (fallback) cache for
Ollama. It's a local, fast-failing server, so re-probing each call is cheap
and lets the picker pick up real installed models as soon as the server comes
up, instead of serving suggestions for the negative-cache TTL.
- Tests: GOOGLE_API_KEY live fetch, Ollama down->recover (32 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* style(cli): ruff format model_catalog.py
Add the blank line ruff format expects after _provider_api_key; no behavior
change. Fixes the lint-run `ruff format --check lib/` step.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): don't treat 'search' substring as a non-chat model marker
The 'search' entry in _NON_CHAT_MARKERS matched anywhere in a model id, dropping
legitimate completion models like gpt-4o-search-preview and anything containing
'research' (e.g. o3-deep-research, since 'search' is a substring). Remove it;
the remaining markers (embedding/audio/image/moderation/etc.) still filter
genuine non-chat models. Test: test_search_substring_not_treated_as_non_chat (33).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* Revert "ci: ignore nltk PYSEC-2026-597 in pip-audit"
Do not suppress an unpatched security advisory to make CI green. Remove
PYSEC-2026-597 from the pip-audit ignore list; leave the scan failing so it
keeps surfacing the nltk path traversal (CVE-2026-12243). This PR should not be
merged until nltk ships a fix (or the vulnerable transitive dep is otherwise
resolved).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): exclude fine-tuned models and checkpoints from the picker
With a live OPENAI_API_KEY, /v1/models returns the user's fine-tunes and
training checkpoints (ft:..., ...:ckpt-step-N). Their recent `created`
timestamps ranked them above the base models and filled every slot, so the
picker showed a wall of `ft:gpt-4o-mini-...:crewai::...` with mangled labels and
no foundation models at all. Skip fine-tunes/checkpoints in the OpenAI-shaped
fetcher so clean base models surface; a user who wants a fine-tune can still
enter it via the picker's "Other" option. Test:
test_openai_excludes_fine_tunes_and_checkpoints (34 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
* fix(cli): cleaner model labels + filter Ollama non-chat models
Anthropic/Gemini already use vendor display names; OpenAI/Groq/Cerebras/Ollama
fall to _humanize for anything outside the curated map, which produced mediocre
labels ("GPT Oss 120b", "qwen3 32b", "Deepseek r1", "llama3.3:70b").
Improve _humanize:
- split on ':' too (Ollama tags: llama3.3:70b -> "Llama3.3 70B")
- uppercase size suffixes (70b -> 70B), acronyms OSS/IT, brand casing
(DeepSeek, ChatGPT, QwQ)
- capitalize the leading letter of fused family+version tokens (qwen3 -> Qwen3)
while preserving OpenAI o-series lowercase (o3, o1-mini)
Also fix _fetch_ollama: /api/tags lists everything installed, so filter
non-chat (embedding) and fine-tune entries the same way the other tiers do.
Tests: expanded test_humanize + test_ollama_excludes_embedding_models (35 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* Implement message setup and feedback handling in AgentExecutor
- Added method to streamline message preparation for agent execution, allowing for integration with human input providers.
- Introduced and methods to manage the state during feedback processing.
- Enhanced and methods to re-run the executor flow using existing feedback messages.
- Updated tests to verify the new message setup and feedback handling functionality, ensuring compatibility with human input providers.
* dont commit runner
* Remove xfail marker from test_crew_train_success as training feedback migration to AgentExecutor is complete.
* fix runtype errors
* fix test
* revert
* mypy fix
* handled reset iterations
- added client_name header to the 4 tavily tools to classify incoming requests as 'crewai' requests.
- This is for internal analysis
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
Inline agent and crew actions can now use repository-backed agents
without duplicating role, goal, and backstory in each definition.
Examples:
* `agent.with.from_repository: support_specialist`
* `crew.with.agents.researcher.from_repository: researcher`
`PlusAPI.get_agent` now uses the shared synchronous request path so
project loaders can fetch repository agents without nested event loops.
Flow action inputs now support `${...}` inside strings, not only
strings that are fully wrapped in one expression. This lets authored
flows use simple prompt-like values such as:
* `query: "News about ${state.topic}"`
* `input: "Ticket ${text(state, "ticket.id", "unknown")}"`
* `sources: ["${state.primary_source}", "archive-${state.topic}"]`
Whole-expression values still preserve their runtime type, so
`${state.limit}` remains a number and `${state.domains}` remains a list.
Mixed literal and expression strings render as text.
This removes the need to build labeled strings with CEL concatenation,
which was hard to read, easy to quote incorrectly in YAML, and a poor
fit for the Flow authoring skill examples.
### Overview
`BedrockCompletion.acall()` (the async completion path used when a crew is kicked off asynchronously) requires `aiobotocore` to build its async client. The `bedrock` extra, however, only declared `boto3`. Crews configured with an AWS Bedrock model work fine under a synchronous `kickoff()`, since that path only needs `boto3`, but raise `NotImplementedError: Async support for AWS Bedrock requires aiobotocore` as soon as they're kicked off asynchronously, since `aiobotocore` was never installed.
The fix adds `aiobotocore` to the `bedrock` extra, so `crewai[bedrock]` installs both the sync (`boto3`) and async (`aiobotocore`) dependencies the native Bedrock provider needs. The lockfile is regenerated to match. The exception message is also corrected — it previously pointed to a `bedrock-async` extra that never existed in `pyproject.toml`.
### Changes
- `lib/crewai/pyproject.toml`: add `aiobotocore~=3.5.0` to the `bedrock` extra
- `uv.lock`: regenerated to reflect the updated `bedrock` extra
- `lib/crewai/src/crewai/llms/providers/bedrock/completion.py`: fix the install hint in the `NotImplementedError` message to reference the real `bedrock` extra instead of the nonexistent `bedrock-async`
* Document flow agent options
Document and type inline Flow agent options so authored flows can set:
* `llm.model`, `llm.max_tokens`, and `llm.max_completion_tokens`
* `planning_config.max_attempts`
* `allow_delegation`
* `max_iter`
* `max_rpm`
* `max_execution_time` in seconds
Also tell the flow skill to omit optional fields unless needed.
* Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
A method that listens to its own name can re-trigger itself or collide
with router events. Rejecting that definition keeps declarative and
Python-authored flows aligned before kickoff.
CEL string concatenation currently fails when prompt builders read
missing or null fields. This commit adds `text(root, "path", "default")`
custom CEL helper so prompt text can safely read nested state/output
values.
Point `crewai template list`/`template add` at the crewAIInc-fde GitHub
org so the FDE template_* repos are listed and installed instead of the
crewAIInc ones.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* Support inline skill definitions
This commit adds inline skill loading without a need for a file. It also
DRYs the skill loading feature.
* Address code review suggestions
* Type tool and app in CrewDefinition
This commit fixes a bug in the CrewDefinition class where the tool and
app were not being added.
* Type mcps= parameter
* Add generated Flow Definition authoring skill
Generate a portable skill from the Flow Definition schema so agents can
author valid declarative flows with the same reference CrewAI uses to
validate them. New declarative flow projects now write this skill.
```python
from crewai.flow.flow_definition import FlowDefinition
skill = FlowDefinition.skill(skips=(), examples_format="yaml")
```
* `examples_format` accepts `"yaml"` or `"json"`.
* Supported skips: `conversational`, `non_linear_flows`, `each`, `hitl`, `persistence`, `config`, `expression_action`, `script_action`, `tool_action`
The generated skill includes authoring rules, a routed crew example, and
an API reference extracted from the Flow, action, state, agent, crew,
and task Pydantic schemas.
* Fix declarative flow scaffold without framework import
* Fix skipped expression action guidance
* Fix markdown links in skill
* fix: freeze docs version nav from Edge instead of previous release
The docs cut copied every Edge file into the new `docs/v<X.Y.Z>/`
snapshot but built that version's `docs.json` navigation by cloning the
previous frozen release and only rewriting path prefixes. Pages added to
Edge since the last release were therefore copied to disk yet never
linked in the version selector, which is why the v1.15.0 cut shipped
without the Datadog guide. `_build_new_entry` now clones the Edge nav
entry and rewrites `edge/<locale>/` to `v<new>/<locale>/`, so promoting
Edge to Latest carries every current page and nav restructuring.
* docs: link the v1.15.0 Datadog guide dropped during the cut
The v1.15.0 freeze copied `enterprise/guides/datadog` into the snapshot
for every locale but never linked it in `docs.json`, because the cut
cloned the v1.14.7 nav instead of Edge. This backfills the missing nav
reference in the `en`, `pt-BR`, `ko`, and `ar` v1.15.0 blocks so the
already-shipped page is reachable from the version selector. Pairs with
the `_build_new_entry` fix that prevents future cuts from dropping pages.
* docs: link the v1.15.1 Datadog guide dropped during the cut
The v1.15.1 cut ran before the freeze-from-Edge fix landed, so it
inherited the same bug as v1.15.0: `enterprise/guides/datadog` was
copied into the snapshot for every locale but never linked in
`docs.json`. This backfills the missing nav reference in the `en`,
`pt-BR`, `ko`, and `ar` v1.15.1 blocks so the page is reachable from the
version selector.
JSON-formatted stdout is now the only supported log shape in CrewAI
Enterprise — the `CREWAI_LOG_FORMAT=json` opt-in env var is gone and
no longer needs to be configured in AMP. Removes the "Enabling JSON
output" section, the env-var setup step, the troubleshooting check,
and the `legacy text mode` comparison across the four locale copies
(`en`, `ko`, `pt-BR`, `ar`) of `docs/edge/<lang>/enterprise/guides/datadog.mdx`.
* Require explicit CrewAI project definitions
JSON crews and declarative flows now resolve from `[tool.crewai]`
metadata instead of implicit filename discovery. This makes project type
selection deterministic, prevents stray `crew.json(c)` files from changing
CLI behavior, and centralizes definition path validation for run, install,
deploy validation, plotting, and memory reset paths.
`[tool.crewai].definition` must be a project-local file path. Absolute
paths, `~`, missing files, directories, and paths escaping the project root
are rejected so deploy and runtime commands use the same contract.
Breaking changes and migration paths:
* JSON crew projects are no longer discovered from `crew.json` or
`crew.jsonc` alone. Add explicit metadata:
```toml
[tool.crewai]
type = "crew"
definition = "crew.jsonc"
```
* Declarative flow projects must use a valid project-local definition path:
```toml
[tool.crewai]
type = "flow"
definition = "flows/research.yaml"
```
* `Flow.from_definition(definition)` is removed. Use:
```python
Flow.from_declaration(contents=definition)
```
* `FlowDefinition.to_json()` and `FlowDefinition.to_yaml()` are removed.
Use `FlowDefinition.to_dict()` and serialize with the caller's JSON or
YAML library.
* `FlowDefinition.from_dict()` is removed. Use:
```python
FlowDefinition.from_declaration(contents=data)
```
* `FlowDefinition.json_schema()` is removed. Use Pydantic's schema API only
where schema generation is intentionally needed:
```python
FlowDefinition.model_json_schema(by_alias=True)
```
* `crewai_cli.run_crew.find_crew_json_file()` and `_has_json_crew()` are
removed. Use `configured_project_json_crew()` or the shared
`crewai_core.project.configured_project_definition("crew")` helper.
* `crewai reset-memories` now only loads JSON crews declared through
`[tool.crewai].definition`, and invalid declared JSON crew definitions
fail instead of silently falling back to classic crew discovery.
* Address code review comments
* Track conversational flow turn usage in telemetry
* adjusted name to flow:conversation_turn
* only mark on turn completed event
* ensure tui also emits these events
* fix: enforce owner-only permissions on credential files
Credentials stored at rest were left world-readable on multi-user hosts:
- TokenManager._get_secure_storage_path() documented its credential dir as
mode 0o700 but created it via mkdir() with default perms (0o755), leaving
the Fernet secret.key and encrypted tokens.enc in a traversable dir.
- Settings.dump() persisted tool_repository_password (plaintext) to
settings.json via open("w"), producing a 0o644 file, and created the
config dir at 0o755 — despite the sibling token_manager already writing
secrets atomically at 0o600.
Fixes:
- TokenManager: chmod the credential dir to 0o700 after mkdir (robust against
umask and pre-existing dirs).
- Settings: write settings.json atomically at 0o600 (mkstemp + chmod +
os.replace) and chmod the dedicated config dir to 0o700. The /tmp and cwd
fallback parents are deliberately not chmod'd; the 0o600 file mode protects
the credential there.
Adds regression tests asserting 0o600 files and 0o700 dirs, and that shared
fallback dirs are not globally tightened.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Close temp fd on secure settings write failure
* Log secure settings fd close failures
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
`StateProxy` looked like a thread-safety boundary, but it only protected
a small slice of state operations. Some examples of operations that were
not covered:
- `self.state.counter += 1`, `self.state["counter"] += 1` (increments)
- `self.state.user.profile.score += 1` (nested object mutations)
- `self.state.config["limits"]["max"] = 10` (mutation through model fields)
- `self.state.items[0].status = "done"` (list/container mutations)
This commit decided to remove it completely for simplicity and
performance:
- Simpler runtime code
- attr read: 24x faster, attr write: 27x faster, list append: 19x faster (local benchmark)
- Clearer concurrency contract (lifecycle locks remain, but arbitrary
shared state mutation is not presented as thread-safe)
Declarative flows already used `module:qualname` refs for runtime
objects, but crew JSON tools still had their own lookup path. That meant
examples like `project_tools:LookupTool` were treated as named
`crewai_tools` lookups and failed with guidance that only mentioned
`SerperDevTool` or `custom:<name>`. Invalid refs such as
`not_tools:NotATool` also missed the same BaseTool validation used by
flow tool actions.
Move ref resolution into a shared declarative helper, use it from flow
tool actions and crew JSON loading, and require tool refs to resolve to
`BaseTool` classes before instantiation. Validation still checks tool
refs structurally, so validating a crew does not import or execute
project code.
Allow required JSON schema state fields to be supplied by kickoff inputs
instead of requiring every field to exist in state.default before
runtime.
Example: a flow with required lead_name and no state.default can now run
with kickoff inputs={"lead_name": "Ada Lovelace"}.
The page itself already landed on main via #6247. This rebases onto main
and applies the two remaining changes:
- Nest crew-studio + merged-step-card into a collapsible "Crew Studio"
nav group (pencil icon), across edge and v1.14.7 in en, pt-BR, ko, ar.
- Remove the temporary "Rolling out" Note banner (feature ships today).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix symlink path traversal in skill archive extraction
`_safe_extractall` (the Python < 3.12 fallback used by `crewai skills`
archive unpacking) validated each member's *name* against the destination
but never validated symlink/hardlink *targets*. A malicious skill tarball
could plant a symlink escaping the destination (e.g. `link -> /home/user/.ssh`)
followed by a regular member written through it (`link/authorized_keys`),
escaping `dest` even though every member name resolves inside it — the
classic symlink-extraction traversal.
The 3.12+ path (`extractall(..., filter="data")`) already blocks this; the
fallback now mirrors it by rejecting absolute link targets and any link
target that resolves outside the destination directory.
Adds regression tests covering absolute and relative escaping symlinks plus
benign in-tree symlinks and ordinary archives.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Harden skill cache archive extraction
* Reject special skill archive members
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add a single declaration loader shared by API and CLI callers.
- Add FlowDefinition.from_declaration for FlowDefinition instances, dictionaries, YAML/JSON strings, and file paths
- Add Flow.from_declaration to build runnable flows directly from the same inputs
- Route declarative flow CLI loading through Flow.from_declaration so path handling and validation stay centralized
```
# Load just the serializable definition when you do not need to run it yet.
definition = FlowDefinition.from_declaration(path="flows/research.crewai")
definition = FlowDefinition.from_declaration(contents=flow_yaml)
definition = FlowDefinition.from_declaration(contents=flow_dict)
# Build a runnable flow directly from the same declaration inputs.
flow = Flow.from_declaration(path="flows/research.crewai")
flow = Flow.from_declaration(contents=flow_yaml)
flow = Flow.from_declaration(contents=flow_dict)
flow = Flow.from_declaration(contents=definition)
# Run it like any other flow.
result = flow.kickoff(inputs={"topic": "AI agents"})
# The CLI now goes through the same path-based loader.
# crewai run --definition flows/research.crewai
```
The previous `~=1.34.0` pin kept us on the unmaintained 1.34 line —
last patched as `1.34.1` in June 2025, eight minor releases behind
upstream — and caused `_create_exp_backoff_generator` `ImportError`
crashes in factory deployments where the OpenTelemetry Operator's
injected init container shadows
`opentelemetry.exporter.otlp.proto.common._internal` with >=1.35 while
our `opentelemetry-exporter-otlp-proto-grpc==1.34.1` still imports the
removed private symbol. Pinning to `~=1.42.0` tracks the current
upstream stable line; the resolver now lands on 1.42.1 and our public
OTel trace API usage is unaffected.
Remove redundant startup logs from `crewai run` and make the legacy flow
command warning actionable.
- Stop printing `Running the Flow` and `Running the Crew` before project
execution.
- Stop printing the redundant `Flow started with ID: ...` line while
preserving flow lifecycle event emission.
- Replace Click's generic `kickoff` deprecation warning with a clearer
message that tells users to use `crewai run`.
Inline crews default to `verbose=False`. They set the shared formatter's
`verbose` value in `lib/crewai/src/crewai/crew.py`, which could hide
flow method status from `lib/crewai/src/crewai/events/utils/
console_formatter.py`.
Remove that `verbose` check for flow method status. Flow output is still
controlled by `suppress_flow_events`.
Normal quiet crews are unchanged because crew, task, and agent logs
still use their own `verbose` checks.
* Add declarative Flow CLI support
Currently, declarative flows can be loaded by the runtime, but the CLI
still treats them as an experimental definition file instead of a
first-class Flow project shape.
With this PR, `crewai create flow --declarative` scaffolds a YAML-backed
Flow project, and `crewai run`, `crewai flow kickoff`, and `crewai flow
plot` can run against the configured definition.
This also lets crew actions reference reusable crew definition files or
folders and override their inputs from the Flow definition, so
declarative flows can compose existing declarative crews without
inlining everything.
* Address code review comments
This commit fixes a bug where a router method could not be the start
method of a flow.
This is useful when you want to route against the initial state, or even
stack two routers.
Currently, tools have a strong input contract through `args_schema`, but no
output contract. This means that anything a tool outputs is converted to
string.
Not only the contract is weak, but the "invisible" conversion to string can
have unexpected effects when the tool returns complex objects like dicts and
arrays.
With this PR, a tool can _optionally_ define an output contract with
`output_schema`. CrewAI validates the raw result and sends the agent JSON.
```python
class ProductResult(BaseModel):
sku: str
name: str
in_stock: bool
class ProductLookupTool(BaseTool):
name: str = "Product Lookup"
description: str = "Look up product availability by SKU."
def _run(self, sku: str) -> ProductResult:
return ProductResult(sku=sku, name="USB-C dock", in_stock=True)
```
If the result does not match the schema, CrewAI warns and falls back to
`str(raw_result)` instead of failing the run:
```python
@tool("Product Lookup", output_schema=ProductResult)
def product_lookup(sku: str) -> dict[str, object]:
return {"sku": sku, "name": "USB-C dock", "in_stock": True}
#=> RuntimeWarning: Failed to validate or serialize output from tool 'Bad Product Lookup' using output_schema 'ProductResult'... Falling back to str(raw_result).
```
This is additive and non-breaking. Existing tools do not need to change. Tools
without `output_schema` keep the old string behavior. Invalid typed outputs
warn and fall back to the old formatting path.
* docs: add "One Card per Step" Studio page (AGE-107)
Document the merge of the task and agent nodes into a single step card on
the Studio canvas. Written as evergreen present-tense feature docs with a
dated rollout banner (June 24th) for the pre-launch customer announcement;
the banner is the only time-bound content and is flagged for removal after
ship. Added in edge + v1.14.7 across en, pt-BR, ko, and ar, with nav entries
in docs.json and three canvas/editor/swap screenshots.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: bump bedrock agentcore dependencies
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: alex-clawd <alex@crewai.com>
* Add single agent action to Flow definitions
Lets a flow method build and run a single CrewAI agent directly, without
wrapping it in a crew. Same idea as the existing `crew` action, but for
one agent.
methods:
answer:
do:
call: agent
with:
role: Analyst
goal: Answer questions
backstory: Knows things.
input: "${state.question}"
start: true
* `input` is required and interpolated from flow state, like
`${state.question}` or `${item}` inside an `each` loop
* optional `response_format` points at a Pydantic model (`{"python":
"models.AnswerModel"}`) to get structured output
* `input` must be a string and its CEL is validated at load time, so bad
expressions like `${state.}` fail early
* Simplify test code
Adds a consolidated `datadog.mdx` under `docs/edge/{en,pt-BR,ko,ar}/enterprise/guides/`
covering both the Datadog Agent path (stdout JSON logs via `CREWAI_LOG_FORMAT=json`)
and the Datadog OTLP intake, with a JSON log schema reference and a ready-to-import
operations dashboard (`datadog_dashboard.json`). Reframes `capture_telemetry_logs.mdx`
to lead with OpenTelemetry as the vendor-neutral path and point readers to the new
Datadog page for that ecosystem's setup.
* Validate flow CEL expressions at definition load time
Promote CEL expression handling to a public Expression API and validate expressions when a FlowDefinition is built instead of when it executes.
Invalid CEL syntax or unknown roots now raise ValidationError from FlowDefinition.from_yaml() and FlowDefinition.from_dict(). Expressions may reference state and outputs, plus item inside each.do; bare identifiers are rejected as unknown roots.
For with values, the CEL contract is intentionally simple: after trimming whitespace, a string is evaluated as CEL only if it starts with ${ and ends with }. Anything else is treated as a literal value, so partial interpolation is not supported. If the content inside the wrapper is not valid CEL, validation fails.
Examples:
```text
"${state.topic}" -> evaluated, returns state.topic
"topic is ${state.topic}" -> literal string
"${state.topic} suffix" -> literal string
"${'a'}${'b'}" -> invalid CEL
```
* Honor explicit empty-context overrides in evaluate() / render_template()
* Use explicit name/action shape for each.do steps
* Add optional `if` expression to `each.do` steps
Lets a step inside an `each` action run conditionally based on a CEL
expression evaluated against `item` and prior step `outputs`.
* feat: update pyproject.toml to specify wheel targets
Added a new section to the pyproject.toml file to include only specific files in the wheel build, enhancing the packaging process. Updated tests to verify the inclusion of these targets.
* feat: add memory save event handling to activity log
Implemented event handlers for MemorySaveStartedEvent, MemorySaveCompletedEvent, and MemorySaveFailedEvent in the crew_run_tui module. This allows the application to log memory save operations, capturing their status and details in the activity log. Added corresponding tests to verify the correct logging behavior for successful and failed memory saves.
* feat: enhance memory save event handling in activity log
Added functionality to suppress nested memory save events and updated the handling of MemorySaveStartedEvent, MemorySaveCompletedEvent, and MemorySaveFailedEvent to improve logging accuracy. Introduced new tests to verify the correct behavior of memory save events, including scenarios for nested events and completion updates for timed-out entries.
* Fix memory save activity log handling
* Normalize alpha package versions
* Update scaffolded crew dependency
* feat: add button to copy setup instructions for CrewAI coding agents
Introduced a button in the documentation that allows users to easily copy setup instructions for CrewAI coding agents. The instructions include installation steps, environment setup, and best practices for using the CrewAI CLI. This enhancement aims to streamline the onboarding process for new users.
* Improve missing CrewAI install guidance
* fix: address pr review feedback
* fix: avoid mismatched memory save rows
* fix: wait for queued memory save events
* fix: avoid matching memory saves on missing ids
* chore: normalize prerelease version to 1.14.8a1
2026-06-18 14:14:54 -03:00
21992 changed files with 3797220 additions and 1041312 deletions
- PRs over 500 lines are labeled `size/XL` automatically
- Title must follow the same conventional commit format
- Link related issues where applicable
- Link related issues where applicable (`#123`, `Fixes #123`, or the issue URL)
- First-time contributors must open or pick an existing **open** issue first, then mention it in the PR title or body (for example `#123`). PRs without a linked open issue are closed automatically and labeled `needs-issue`.
# PYSEC-2025-189..197 - torch 2.11.0: memory-corruption/DoS in functions only reachable via untrusted models; no fix available
# PYSEC-2025-210, PYSEC-2026-139 - torch 2.11.0: profiler/deserialization issues; no fix available
# GHSA-rrmf-rvhw-rf47 - torch 2.11.0 (CVE-2025-3000, alias of PYSEC-2025-194): memory corruption in torch.jit.script, CVSS 1.9, local-only; affected <=2.12.0, no fix available. pip-audit reports it under the GHSA id so the PYSEC ignore above does not catch it.
# PYSEC-2025-211..218 - transformers 5.5.4: deserialization/code injection via malicious model checkpoints; no fix available
# GHSA-f4j7-r4q5-qw2c - chromadb 1.1.1 (CVE-2026-45829): pre-auth RCE via /api/v2/tenants/{tenant}/databases/{db}/collections when trust_remote_code=true.
# Advisory: vulnerable >=1.0.0,<=1.5.9, firstPatchedVersion=none. We only use chromadb.PersistentClient (lib/crewai/src/crewai/rag/chromadb/factory.py)
# and chromadb.utils.embedding_functions; the chromadb HTTP server is never started, so the vulnerable route is not exposed.
### Fast and Flexible Multi-Agent Automation Framework
> CrewAI is a lean, lightning-fast Python framework built entirely from scratch—completely **independent of LangChain or other agent frameworks**.
> It empowers developers with both high-level simplicity and precise low-level control, ideal for creating autonomous AI agents tailored to any scenario.
> CrewAI is an open-source Python framework with high-level abstractions and low-level APIs for building production-ready multi-agent workflows.
> It gives developers autonomous agent collaboration through Crews and precise, event-driven control through Flows.
- **CrewAI Crews**: Optimize for autonomy and collaborative intelligence.
- **CrewAI Flows**: The **enterprise and production architecture** for building and deploying multi-agent systems. Enable granular, event-driven control, single LLM calls for precise task orchestration and supports Crews natively
- **CrewAI Crews**: Optimize for autonomy and collaborative intelligence with role-based AI agents.
- **CrewAI Flows**: Build event-driven automations that combine precise workflow control, single LLM calls, and native support for Crews.
With over 100,000 developers certified through our community courses at [learn.crewai.com](https://learn.crewai.com), CrewAI is rapidly becoming the
standard for enterprise-ready AI automation.
standard for production-ready agentic automation.
# CrewAI AMP Suite
CrewAI AMP Suite is a comprehensive bundle tailored for organizations that require secure, scalable, and easy-to-manage agent-driven automation.
For organizations that need a commercial control plane around CrewAI, [CrewAI AMP Suite](https://crewai.com/amp) adds managed deployment, observability, governance, security, and enterprise support.
You can try one part of the suite the [Crew Control Plane for free](https://app.crewai.com)
You can try one part of the suite, the [Crew Control Plane, for free](https://app.crewai.com).
## Crew Control Plane Key Features:
@@ -86,9 +88,12 @@ intelligent automations.
- [Build with AI](#build-with-ai)
- [Why CrewAI?](#why-crewai)
- [Getting Started](#getting-started)
- [Learning Resources](#learning-resources)
- [Understanding Flows and Crews](#understanding-flows-and-crews)
- [Installation](#1-installation)
- [Setting Up Your Crew](#2-setting-up-your-crew)
- [Running Your Crew](#3-running-your-crew)
- [Key Features](#key-features)
- [Understanding Flows and Crews](#understanding-flows-and-crews)
CrewAI unlocks the true potential of multi-agent automation, delivering the best-in-class combination of speed, flexibility, and control with either Crews of AI Agents or Flows of Events:
CrewAI unlocks the true potential of multi-agent automation, delivering speed, flexibility, and control through Crews of AI agents and event-driven Flows:
- **Standalone Framework**: Built from scratch, independent of LangChain or any other agent framework.
- **Purpose-built architecture**: Designed specifically for agent orchestration, with a lightweight Python core and clean primitives for real-world automation.
- **High Performance**: Optimized for speed and minimal resource usage, enabling faster execution.
- **Flexible LowLevel Customization**: Complete freedom to customize at both high and low levels - from overall workflows and system architecture to granular agent behaviors, internal prompts, and execution logic.
- **Ideal for Every Use Case**: Proven effective for both simple tasks and highly complex, real-world, enterprise-grade scenarios.
- **Flexible Low-Level Customization**: Complete freedom to customize everything from workflows and system architecture to agent behaviors, internal prompts, and execution logic.
- **Ideal for Every Use Case**: Proven effective for simple tasks, complex workflows, and production-grade automation.
- **Robust Community**: Backed by a rapidly growing community of over **100,000 certified** developers offering comprehensive support and resources.
CrewAI empowers developers and enterprises to confidently build intelligent automations, bridging the gap between simplicity, flexibility, and performance.
CrewAI empowers developers and teams to build intelligent automations that balance simplicity, flexibility, and production-grade control.
## Getting Started
@@ -150,9 +155,7 @@ Setup and run your first CrewAI agents by following this tutorial.
[](https://www.youtube.com/watch?v=-kSOTtYzgEw "CrewAI Getting Started Tutorial")
###
Learning Resources
### Learning Resources
Learn CrewAI through our comprehensive courses:
@@ -186,47 +189,76 @@ The true power of CrewAI emerges when combining Crews and Flows. This synergy al
### Getting Started with Installation
To get started with CrewAI, follow these simple steps:
To get started with CrewAI, follow these simple steps. The full walkthrough lives in the [installation guide](https://docs.crewai.com/en/installation).
### 1. Installation
Ensure you have Python >=3.10 <3.14 installed on your system. CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
CrewAI requires `Python >=3.10 and <3.14`. Check your version with:
First, install CrewAI:
```shell
uv pip install crewai
```bash
python3 --version
```
If you want to install the 'crewai' package along with its optional features that include additional tools for agents, you can do so by using the following command:
CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling. If you haven't installed `uv` yet, install it first.
**macOS/Linux:**
```shell
uv pip install 'crewai[tools]'
curl -LsSf https://astral.sh/uv/install.sh | sh
```
The command above installs the basic package and also adds extra components which require more dependencies to function.
If your system doesn't have `curl`, you can use `wget`:
### Troubleshooting Dependencies
```shell
wget -qO- https://astral.sh/uv/install.sh | sh
```
If you encounter issues during installation or usage, here are some common solutions:
- If issues persist, use a pre-built wheel: `uv pip install tiktoken --prefer-binary`
If you encounter a `PATH` warning, run:
### 2. Setting Up Your Crew with the YAML Configuration
```shell
uv tool update-shell
```
To create a new CrewAI project, run the following CLI (Command Line Interface) command:
If you encounter the `chroma-hnswlib==0.7.6` build error (`fatal error C1083: Cannot open include file: 'float.h'`) on Windows, install [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/) with *Desktop development with C++*.
Verify the install:
```shell
uv tool list
```
You should see something like:
```shell
crewai v0.102.0
- crewai
```
To upgrade the global CLI later:
```shell
uv tool install crewai --upgrade
```
This upgrades the **global `crewai` CLI tool** only. To upgrade the `crewai` version inside a project's virtual environment, see [Upgrading CrewAI in a project](https://docs.crewai.com/en/guides/migration/upgrading-crewai).
### 2. Setting Up Your Crew
`crewai create crew` creates a JSON-first crew project. Agents live in `agents/*.jsonc`, tasks and crew-level settings live in `crew.jsonc`, and `crewai run` loads that JSON definition directly.
```shell
crewai create crew <project_name>
@@ -237,219 +269,146 @@ This command creates a new project folder with the following structure:
```
my_project/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── pyproject.toml
├── README.md
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
You can now start developing your crew by editing the files in the `src/my_project` folder. The `main.py` file is the entry point of the project, the`crew.py` file is where you define your crew, the `agents.yaml` file is where you define your agents, and the `tasks.yaml` file is where you define your tasks.
If you need the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`, run:
```shell
crewai create crew <project_name> --classic
```
See [Using Annotations](https://docs.crewai.com/en/learn/using-annotations) for the classic pattern.
#### To customize your project, you can:
- Modify `src/my_project/config/agents.yaml` to define your agents.
- Modify `src/my_project/config/tasks.yaml` to define your tasks.
-Modify `src/my_project/crew.py` to add your own logic, tools, and specific arguments.
-Modify `src/my_project/main.py` to add custom inputs for your agents and tasks.
- Modify `agents/*.jsonc` to define each agent's role, goal, backstory, LLM, tools, and behavior.
- Modify `crew.jsonc` to define tasks, process, and input defaults.
-Add custom tools in `tools/` and reference them as `"custom:<name>"`.
-Add optional knowledge files in `knowledge/` and skill files in `skills/`.
- Add your environment variables into the `.env` file.
Use `{placeholder}` values in agent and task text, then set defaults in `crew.jsonc` under `inputs`. When you run `crewai run`, the CLI prompts for any missing values.
#### Example of a simple crew with a sequential process:
Instantiate your crew:
```shell
crewai create crew latest-ai-development
cd latest_ai_development
```
Modify the files as needed to fit your use case:
Then edit the generated files:
**agents.yaml**
**agents/researcher.jsonc**
```yaml
# src/my_project/config/agents.yaml
researcher:
role:>
{topic} Senior Data Researcher
goal:>
Uncover cutting-edge developments in {topic}
backstory:>
You're a seasoned researcher with a knack for uncovering the latest
developments in {topic}. Known for your ability to find the most relevant
information and present it in a clear and concise manner.
reporting_analyst:
role:>
{topic} Reporting Analyst
goal:>
Create detailed reports based on {topic} data analysis and research findings
backstory:>
You're a meticulous analyst with a keen eye for detail. You're known for
your ability to turn complex data into clear and concise reports, making
it easy for others to understand and act on the information you provide.
```jsonc
{
"role":"{topic} Senior Data Researcher",
"goal":"Uncover cutting-edge developments in {topic}",
"backstory":"You're a seasoned researcher who finds relevant information and presents it clearly.",
"llm":"openai/gpt-4o",
"tools":["SerperDevTool"],
"settings":{
"verbose":true
}
}
```
**tasks.yaml**
**agents/reporting_analyst.jsonc**
````yaml
# src/my_project/config/tasks.yaml
research_task:
description: >
Conduct a thorough research about {topic}
Make sure you find any interesting and relevant information given
the current year is 2025.
expected_output: >
A list with 10 bullet points of the most relevant information about {topic}
agent: researcher
reporting_task:
description: >
Review the context you got and expand each topic into a full section for a report.
Make sure the report is detailed and contains any and all relevant information.
expected_output: >
A fully fledge reports with the mains topics, each with a full section of information.
Formatted as markdown without '```'
agent: reporting_analyst
output_file: report.md
````
**crew.py**
```python
# src/my_project/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
agents: List[BaseAgent]
tasks: List[Task]
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config['researcher'],
verbose=True,
tools=[SerperDevTool()]
)
@agent
def reporting_analyst(self) -> Agent:
return Agent(
config=self.agents_config['reporting_analyst'],
verbose=True
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config['research_task'],
)
@task
def reporting_task(self) -> Task:
return Task(
config=self.tasks_config['reporting_task'],
output_file='report.md'
)
@crew
def crew(self) -> Crew:
"""Creates the LatestAiDevelopment crew"""
return Crew(
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=True,
)
```jsonc
{
"role":"{topic} Reporting Analyst",
"goal":"Create detailed reports based on {topic} data analysis and research findings",
"backstory":"You're a meticulous analyst who turns complex data into clear, concise reports.",
"llm":"openai/gpt-4o",
"settings":{
"verbose":true
}
}
```
**main.py**
**crew.jsonc**
```python
#!/usr/bin/env python
# src/my_project/main.py
import sys
from latest_ai_development.crew import LatestAiDevelopmentCrew
def run():
"""
Run the crew.
"""
inputs = {
'topic': 'AI Agents'
```jsonc
{
"name":"Latest AI Development",
"agents":["researcher","reporting_analyst"],
"tasks":[
{
"name":"research_task",
"description":"Conduct thorough research about {topic}. Find recent, relevant information.",
"expected_output":"A list with 10 bullet points of the most relevant information about {topic}.",
"agent":"researcher"
},
{
"name":"reporting_task",
"description":"Review the research and expand each topic into a full section for a report.",
"expected_output":"A markdown report with the main topics, each with a full section of information. No fenced code blocks around the whole document.",
Before running your crew, make sure you have the following keys set as environment variables in your `.env` file:
Before running your crew, set the required keys in your `.env` file:
- An [OpenAI API key](https://platform.openai.com/account/api-keys) (or other LLM API key): `OPENAI_API_KEY=sk-...`
- A [Serper.dev](https://serper.dev/) API key: `SERPER_API_KEY=YOUR_KEY_HERE`
- Your model provider API key — see [LLM setup](https://docs.crewai.com/en/concepts/llms#setting-up-your-llm)
- A [Serper.dev](https://serper.dev/) API key if you use web search: `SERPER_API_KEY=YOUR_KEY_HERE`
Lock the dependencies and install them by using the CLI command but first, navigate to your project directory:
Then install dependencies and run from the project directory:
```shell
cd my_project
crewai install (Optional)
```
To run your crew, execute the following command in the root of your project:
```bash
crewai install
crewai run
```
or
If you need additional packages, use `uv add <package-name>`.
```bash
python src/my_project/main.py
```
You should see the output in the console, and `output/report.md` should be created in the project root.
If an error happens due to the usage of poetry, please run the following command to update your crewai package:
In addition to the sequential process, you can use the hierarchical process, which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. [See more about the processes here](https://docs.crewai.com/en/concepts/processes).
```bash
crewai update
```
You should see the output in the console and the `report.md` file should be created in the root of your project with the full final report.
In addition to the sequential process, you can use the hierarchical process, which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. [See more about the processes here](https://docs.crewai.com/core-concepts/Processes/).
For a Flow-first walkthrough, see the [Quickstart](https://docs.crewai.com/en/quickstart).
## Key Features
CrewAI stands apart as a lean, standalone, high-performance multi-AI Agent framework delivering simplicity, flexibility, and precise control—free from the complexity and limitations found in other agent frameworks.
CrewAI gives developers a practical foundation for building agentic systems that move from prototype to production: autonomous collaboration where it helps, explicit workflow control where it matters, and Python-native customization throughout.
- **Standalone & Lean**: Completely independent from other frameworks like LangChain, offering faster execution and lighter resource demands.
- **Flexible & Precise**: Easily orchestrate autonomous agents through intuitive [Crews](https://docs.crewai.com/concepts/crews) or precise [Flows](https://docs.crewai.com/concepts/flows), achieving perfect balance for your needs.
- **Seamless Integration**: Effortlessly combine Crews (autonomy) and Flows (precision) to create complex, real-world automations.
- **Deep Customization**: Tailor every aspect—from high-level workflows down to low-level internal prompts and agent behaviors.
- **Reliable Performance**: Consistent results across simple tasks and complex, enterprise-level automations.
- **Thriving Community**: Backed by robust documentation and over 100,000 certified developers, providing exceptional support and guidance.
- **Crews for autonomy**: Model teams of specialized AI agents with roles, goals, tools, and tasks.
- **Flows for control**: Build event-driven workflows with state, branching, routing, and production logic.
- **Seamless integration**: Combine Crews and Flows to create complex, real-world automations.
- **Python-native customization**: Customize prompts, tools, execution paths, state, and integrations without fighting the framework.
- **Agent-ready capabilities**: Use tools, memory, knowledge, checkpointing, async execution, and MCP/A2A support for more capable production agents.
- **Production-ready patterns**: Add deterministic steps, human input, structured outputs, and checkpointing as your system grows.
- **Thriving community**: Backed by robust documentation and over 100,000 certified developers, providing exceptional support and guidance.
Choose CrewAI to easily build powerful, adaptable, and production-ready AI automations.
Choose CrewAI to build powerful, adaptable, and production-ready AI automations.
## Examples
You can test different real life examples of AI crews in the [CrewAI-examples repo](https://github.com/crewAIInc/crewAI-examples?tab=readme-ov-file):
@@ -481,7 +440,7 @@ CrewAI's power truly shines when combining Crews with Flows to create sophistica
CrewAI flows support logical operators like `or_` and `and_` to combine multiple conditions. This can be used with `@start`, `@listen`, or `@router` decorators to create complex triggering conditions.
-`or_`: Triggers when any of the specified conditions are met.
- `and_`Triggers when all of the specified conditions are met.
-`and_`: Triggers when all of the specified conditions are met.
Here's how you can orchestrate multiple Crews within a Flow:
@@ -578,28 +537,42 @@ This example demonstrates how to:
CrewAI supports using various LLMs through a variety of connection options. By default your agents will use the OpenAI API when querying the model. However, there are several other ways to allow your agents to connect to models. For example, you can configure your agents to use a local model via the Ollama tool.
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/how-to/LLM-Connections/) page for details on configuring your agents' connections to models.
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/en/learn/llm-connections) page for details on configuring your agents' connections to models.
## How CrewAI Compares
## When to Use CrewAI
**CrewAI's Advantage**: CrewAI combines autonomous agent intelligence with precise workflow control through its unique Crews and Flows architecture. The framework excels at both high-level orchestration and low-level customization, enabling complex, production-grade systems with granular control.
Use CrewAI when you need more than a single prompt or chatbot: multi-step work, specialized agents, tool use, structured outputs, human review, or workflows that combine autonomous reasoning with explicit business logic.
- **LangGraph**: While LangGraph provides a foundation for building agent workflows, its approach requires significant boilerplate code and complex state management patterns. The framework's tight coupling with LangChain can limit flexibility when implementing custom agent behaviors or integrating with external systems.
CrewAI is especially useful when you want to:
_P.S. CrewAI demonstrates significant performance advantages over LangGraph, executing 5.76x faster in certain cases like this QA task example ([see comparison](https://github.com/crewAIInc/crewAI-examples/tree/main/Notebooks/CrewAI%20Flows%20%26%20Langgraph/QA%20Agent)) while achieving higher evaluation scores with faster completion times in certain coding tasks, like in this example ([detailed analysis](https://github.com/crewAIInc/crewAI-examples/blob/main/Notebooks/CrewAI%20Flows%20%26%20Langgraph/Coding%20Assistant/coding_assistant_eval.ipynb))._
- **Autogen**: While Autogen excels at creating conversational agents capable of working together, it lacks an inherent concept of process. In Autogen, orchestrating agents' interactions requires additional programming, which can become complex and cumbersome as the scale of tasks grows.
- **ChatDev**: ChatDev introduced the idea of processes into the realm of AI agents, but its implementation is quite rigid. Customizations in ChatDev are limited and not geared towards production environments, which can hinder scalability and flexibility in real-world applications.
- Coordinate multiple agents with clear roles and tasks.
- Wrap agent work in deterministic, event-driven workflows.
- Keep application logic in regular Python.
- Move from experiment to production without changing frameworks.
- Add tools, memory, checkpointing, and async execution as your system grows.
## Contribution
CrewAI is open-source and we welcome contributions. If you're looking to contribute, please:
CrewAI is open-source and we welcome contributions. See
[`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for the full setup guide,
branching conventions, and PR checklist.
- Fork the repository.
- Create a new branch for your feature.
- Add your feature or improvement.
- Send a pull request.
- We appreciate your input!
Quick start:
```bash
git clone https://github.com/crewAIInc/crewAI.git
cd crewAI
uv sync --all-groups --all-extras
uv run pre-commit install
```
```bash
# Tests
uv run pytest lib/crewai/tests/ -x -q
# Type checks
uv run mypy lib/
```
### Contributing to the docs
@@ -611,51 +584,8 @@ immediately and are frozen into a new versioned snapshot under
`docs/v<X.Y.Z>/` at the next release cut. Frozen snapshots are immutable — CI
rejects PRs that modify them without a `[docs-freeze]` title prefix. The
release CLI (`devtools release`) handles the freeze automatically; see
[`AGENTS.md`](AGENTS.md) for the full contributor guide and
[`RELEASING.md`](RELEASING.md) for the release-cut runbook.
### Installing Dependencies
```bash
uv lock
uv sync
```
### Virtual Env
```bash
uv venv
```
### Pre-commit hooks
```bash
pre-commit install
```
### Running Tests
```bash
uv run pytest .
```
### Running static type checks
```bash
uvx mypy src
```
### Packaging
```bash
uv build
```
### Installing Locally
```bash
uv pip install dist/*.tar.gz
```
[`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for contributor guidance and
[`lib/devtools/README.md`](lib/devtools/README.md) for release tooling.
## Telemetry
@@ -698,7 +628,7 @@ CrewAI is released under the [MIT License](https://github.com/crewAIInc/crewAI/b
- [What exactly is CrewAI?](#q-what-exactly-is-crewai)
- [How do I install CrewAI?](#q-how-do-i-install-crewai)
- [Does CrewAI depend on LangChain?](#q-does-crewai-depend-on-langchain)
- [Is CrewAI a standalone framework?](#q-is-crewai-a-standalone-framework)
- [Does CrewAI collect data from users?](#q-does-crewai-collect-data-from-users)
@@ -707,7 +637,6 @@ CrewAI is released under the [MIT License](https://github.com/crewAIInc/crewAI/b
- [Can CrewAI handle complex use cases?](#q-can-crewai-handle-complex-use-cases)
- [Can I use CrewAI with local AI models?](#q-can-i-use-crewai-with-local-ai-models)
- [What makes Crews different from Flows?](#q-what-makes-crews-different-from-flows)
- [How is CrewAI better than LangChain?](#q-how-is-crewai-better-than-langchain)
- [Does CrewAI support fine-tuning or training custom models?](#q-does-crewai-support-fine-tuning-or-training-custom-models)
### Resources and Community
@@ -723,25 +652,21 @@ CrewAI is released under the [MIT License](https://github.com/crewAIInc/crewAI/b
### Q: What exactly is CrewAI?
A: CrewAI is a standalone, lean, and fast Python framework built specifically for orchestrating autonomous AI agents. Unlike frameworks like LangChain, CrewAI does not rely on external dependencies, making it leaner, faster, and simpler.
A: CrewAI is a lean, fast Python framework built specifically for orchestrating autonomous AI agents and production-ready agentic workflows.
### Q: How do I install CrewAI?
A: Install CrewAI using pip:
A: Install the CrewAI CLI with [UV](https://docs.astral.sh/uv/):
```shell
uv pip install crewai
uv tool install crewai
```
For additional tools, use:
Then create a project with `crewai create crew <project_name>`, run `crewai install`, and start it with `crewai run`. See the [installation guide](https://docs.crewai.com/en/installation) for details.
```shell
uv pip install 'crewai[tools]'
```
### Q: Is CrewAI a standalone framework?
### Q: Does CrewAI depend on LangChain?
A: No. CrewAI is built entirely from the ground up, with no dependencies on LangChain or other agent frameworks. This ensures a lean, fast, and flexible experience.
A: Yes. CrewAI is a standalone Python framework with its own primitives for agents, tasks, crews, flows, tools, and orchestration.
### Q: Can CrewAI handle complex use cases?
@@ -749,16 +674,12 @@ A: Yes. CrewAI excels at both simple and highly complex real-world scenarios, of
### Q: Can I use CrewAI with local AI models?
A: Absolutely! CrewAI supports various language models, including local ones. Tools like Ollama and LM Studio allow seamless integration. Check the [LLM Connections documentation](https://docs.crewai.com/how-to/LLM-Connections/) for more details.
A: Absolutely! CrewAI supports various language models, including local ones. Tools like Ollama and LM Studio allow seamless integration. Check the [LLM Connections documentation](https://docs.crewai.com/en/learn/llm-connections) for more details.
### Q: What makes Crews different from Flows?
A: Crews provide autonomous agent collaboration, ideal for tasks requiring flexible decision-making and dynamic interaction. Flows offer precise, event-driven control, ideal for managing detailed execution paths and secure state management. You can seamlessly combine both for maximum effectiveness.
### Q: How is CrewAI better than LangChain?
A: CrewAI provides simpler, more intuitive APIs, faster execution speeds, more reliable and consistent results, robust documentation, and an active community—addressing common criticisms and limitations associated with LangChain.
### Q: Is CrewAI open-source?
A: Yes, CrewAI is open-source and actively encourages community contributions and collaboration.
@@ -773,7 +694,7 @@ A: Check out practical examples in the [CrewAI-examples repository](https://gith
### Q: How can I contribute to CrewAI?
A: Contributions are warmly welcomed! Fork the repository, create your branch, implement your changes, and submit a pull request. See the Contribution section of the README for detailed guidelines.
A: Contributions are warmly welcomed! Fork the repository, create your branch, implement your changes, and submit a pull request. See [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for detailed guidelines.
### Q: What additional features does CrewAI AMP offer?
@@ -797,11 +718,11 @@ A: Absolutely! CrewAI agents can easily integrate with external tools, APIs, and
### Q: Is CrewAI suitable for production environments?
A: Yes, CrewAI is explicitly designed with production-grade standards, ensuring reliability, stability, and scalability for enterprise deployments.
A: Yes, CrewAI is designed with production-grade patterns that support reliable, stable, and scalable agentic workflows.
### Q: How scalable is CrewAI?
A: CrewAI is highly scalable, supporting simple automations and large-scale enterprise workflows involving numerous agents and complex tasks simultaneously.
A: CrewAI is highly scalable, supporting simple automations and large-scale workflows involving numerous agents and complex tasks simultaneously.
### Q: Does CrewAI offer debugging and monitoring tools?
| **احترام نافذة السياق** _(اختياري)_ | `respect_context_window` | `bool` | إبقاء الرسائل تحت حجم نافذة السياق عبر التلخيص. الافتراضي True. |
| **وضع تنفيذ الكود** _(اختياري)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | وضع تنفيذ الكود: 'safe' (باستخدام Docker) أو 'unsafe' (مباشر). الافتراضي 'safe'. |
| **متعدد الوسائط** _(اختياري)_ | `multimodal` | `bool` | ما إذا كان الوكيل يدعم القدرات متعددة الوسائط. الافتراضي False. |
| **حقن التاريخ** _(اختياري)_ | `inject_date` | `bool` | ما إذا كان يتم حقن التاريخ الحالي تلقائيًا في المهام. الافتراضي False. |
| **حقن التاريخ** _(اختياري)_ | `inject_date` | `bool` | ما إذا كان يتم حقن التاريخ الحالي تلقائيًا في أمر الوكيل. الافتراضي False. |
| **تنسيق التاريخ** _(اختياري)_ | `date_format` | `str` | سلسلة تنسيق التاريخ عند تفعيل inject_date. الافتراضي "%Y-%m-%d" (تنسيق ISO). |
| **الاستدلال** _(اختياري)_ | `reasoning` | `bool` | ما إذا كان يجب على الوكيل التأمل وإنشاء خطة قبل تنفيذ المهمة. الافتراضي False. |
| **الحد الأقصى لمحاولات الاستدلال** _(اختياري)_ | `max_reasoning_attempts` | `Optional[int]` | الحد الأقصى لمحاولات الاستدلال قبل تنفيذ المهمة. إذا None، سيحاول حتى الاستعداد. |
@@ -287,7 +287,7 @@ analysis_agent = Agent(
- `multimodal`: تفعيل القدرات متعددة الوسائط لمعالجة النص والمحتوى المرئي
- `reasoning`: تمكين الوكيل من التأمل وإنشاء خطط قبل تنفيذ المهام
- `inject_date`: حقن التاريخ الحالي تلقائيًا في أوصاف المهام
- `inject_date`: حقن التاريخ الحالي تلقائيًا في أمر الوكيل
افتراضيًا، ينشئ `crewai create crew` مشروعًا JSON-first يحتوي على `crew.jsonc` و `agents/*.jsonc`. استخدم `crewai create crew my_new_crew --classic` فقط إذا أردت البنية القديمة Python/YAML مع `crew.py` و `config/agents.yaml` و `config/tasks.yaml`.
#### أسماء مستعار قديمة للأعلام (مهملة)
لا تزال أعلام snake_case القديمة تعمل، لكنها مخفية من `--help`. يُفضّل استخدام صيغ kebab-case الموثّقة في أقسام الأوامر أدناه.
| مهمل | استخدم بدلاً منه |
| :--- | :--- |
| `--skip_provider` (في `crewai create crew`) | `--skip-provider` |
| `--n_iterations` (في `crewai train`، `crewai test`) | `--n-iterations` |
| `--task_id` (في `crewai replay`) | `--task-id` |
### 2. الإصدار
عرض الإصدار المثبت من CrewAI.
@@ -72,7 +82,7 @@ crewai version [OPTIONS]
crewai train [OPTIONS]
```
- `-n, --n_iterations INTEGER`: عدد تكرارات التدريب (افتراضي: 5)
- `-n, --n-iterations INTEGER`: عدد تكرارات التدريب (افتراضي: 5)
بعد تنفيذ الطاقم، يمكنك الوصول إلى خاصية `usage_metrics` لعرض مقاييس استخدام نموذج اللغة (LLM) لجميع المهام المنفذة.
`total_tokens` هو الإجمالي المفوتر (`prompt_tokens + completion_tokens`). حقول التفصيل مثل `cached_prompt_tokens` و`cache_creation_tokens` تصف أجزاءً مُدرجة بالفعل ضمن تلك الإجماليات ولا تُضاف مرة أخرى إلى `total_tokens`. راجع قسم **UsageMetrics field semantics** في توثيق مفهوم Flows للحصول على العقد الكامل.
| `cached_prompt_tokens` | جزء قراءة الذاكرة المؤقتة من رموز المطالبة (تفصيل فقط) |
| `cache_creation_tokens` | جزء كتابة الذاكرة المؤقتة من رموز المطالبة (تفصيل فقط، Anthropic) |
| `reasoning_tokens` | جزء التفكير/الاستدلال حيث يبلّغ المزود عنه بشكل منفصل (تفصيل فقط) |
| `successful_requests` | عدد استدعاءات LLM المُجمّعة |
حقول التفصيل مثل `cached_prompt_tokens` و`cache_creation_tokens` و`reasoning_tokens` **لا تُضاف** فوق `total_tokens` — بل تصف أجزاءً مُدرجة بالفعل ضمن `prompt_tokens` أو `completion_tokens`.
بالنسبة إلى Anthropic، تُدمج عدادات قراءة وكتابة الذاكرة المؤقتة ضمن `prompt_tokens`، لذا تنعكس أعباء العمل المخزنة مؤقتًا بالكامل في `total_tokens`. يُدرج مزودو OpenAI الرموز المخزنة مؤقتًا بالفعل داخل `prompt_tokens`؛ يعرض CrewAI الجزء المخزن مؤقتًا بشكل منفصل للوضوح.
كل حقل في [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) المُعاد هو مجموع جميع استدعاءات نموذج اللغة التي حدثت خلال استدعاء واحد لـ `flow.kickoff()`. تتم إعادة تعيين العدادات عند الاستدعاء التالي لـ `kickoff()` (وفي كل تكرار من `kickoff_for_each`)، لذلك لن تتكرر العدّات عبر التشغيلات المتتالية. يمكن قراءة هذه الخاصية بأمان في أي وقت بعد اكتمال `kickoff()`؛ قراءتها أثناء التنفيذ تُرجع المجموع الجزئي المتراكم حتى تلك اللحظة.
## إدارة حالة التدفق
@@ -959,7 +977,7 @@ source .venv/bin/activate
بعد تفعيل البيئة الافتراضية، يمكنك تشغيل التدفق بتنفيذ أحد الأوامر التالية:
```bash
crewai flow kickoff
crewai run
```
أو
@@ -1160,10 +1178,4 @@ crewai run
يكتشف هذا الأمر تلقائيًا ما إذا كان مشروعك تدفقًا (بناءً على إعداد `type = "flow"` في pyproject.toml الخاص بك) ويشغّله وفقًا لذلك. هذه هي الطريقة الموصى بها لتشغيل التدفقات من سطر الأوامر.
للتوافق مع الإصدارات السابقة، يمكنك أيضًا استخدام:
```shell
crewai flow kickoff
```
ومع ذلك، فإن أمر `crewai run` هو الطريقة المفضلة الآن لأنه يعمل لكل من فرق Crew والتدفقات.
أمر `crewai flow kickoff` القديم deprecated. استخدم `crewai run` لكل من فرق Crew والتدفقات.
تتحكم درجة الحرارة (0.0 إلى 1.0) في عشوائية الاستجابة. القيم المنخفضة (مثل 0.2) تنتج مخرجات أكثر تركيزًا وحتمية، بينما القيم الأعلى (مثل 0.8) تزيد الإبداع والتنوع.
درجة الحرارة هي أداة للتحكم في أخذ العينات تدعمها بعض النماذج. تجعل القيم المنخفضة أخذ العينات أكثر تركيزًا عمومًا، بينما تزيد القيم الأعلى التباين. تتجاهل بعض نماذج الاستدلال الأحدث هذا المعامل أو توقف دعمه أو ترفضه، لذا راجع وثائق النموذج المحدد قبل ضبطه.
</Card>
<Card title="اختيار المزود" icon="server">
يقدم كل مزود LLM (مثل OpenAI و Anthropic و Google) نماذج مختلفة بقدرات وأسعار وميزات متفاوتة. اختر بناءً على احتياجاتك من الدقة والسرعة والتكلفة.
@@ -38,7 +38,7 @@ mode: "wide"
أبسط طريقة للبدء. عيّن النموذج في بيئتك مباشرة، من خلال ملف `.env` أو في كود تطبيقك. إذا استخدمت `crewai create` لبدء مشروعك، سيكون مُعيّنًا بالفعل.
```bash .env
MODEL=model-id # e.g. gpt-4o, gemini-2.0-flash, claude-3-sonnet-...
MODEL=provider/model-id # e.g. openai/gpt-5.6-terra
# Be sure to set your API keys here too. See the Provider
# section below.
@@ -57,7 +57,7 @@ mode: "wide"
goal: Conduct comprehensive research and analysis
backstory: A dedicated research professional with years of experience
verbose: true
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
llm: provider/model-id # e.g. anthropic/claude-sonnet-4-6
# (see provider configuration examples below for more)
temperature=0.7, # Higher for more creative outputs
timeout=120, # Seconds to wait for response
max_tokens=4000, # Maximum length of response
top_p=0.9, # Nucleus sampling parameter
frequency_penalty=0.1 , # Reduce repetition
presence_penalty=0.1, # Encourage topic diversity
model="provider/model-id",
timeout=120,
max_tokens=4000,
response_format={"type": "json"}, # For structured outputs
seed=42 # For reproducible results
)
```
<Info>
شرح المعاملات:
- `temperature`: تتحكم في العشوائية (0.0-1.0)
- `timeout`: أقصى وقت انتظار للاستجابة
- `max_tokens`: تحدد طول الاستجابة
- `top_p`: بديل لدرجة الحرارة للعينات
- `frequency_penalty`: تقلل تكرار الكلمات
- `presence_penalty`: تشجع موضوعات جديدة
- `response_format`: تحدد هيكل المخرجات
- `seed`: تضمن مخرجات متسقة
عناصر التحكم في أخذ العينات مثل `temperature` و`top_p`، ومعاملات العقوبة، وأسماء حدود الرموز، وعناصر التحكم في الاستدلال خاصة بكل نموذج. أضفها فقط عندما يدعمها المزود والنموذج المحددان. راجع أمثلة المزودين أدناه ووثائق النموذج لدى المزود.
</Info>
</Tab>
</Tabs>
@@ -120,6 +112,10 @@ mode: "wide"
يدعم CrewAI العديد من مزودي LLM، كل منهم يقدم ميزات فريدة وطرق مصادقة وقدرات نماذج.
في هذا القسم، ستجد أمثلة مفصلة تساعدك في اختيار وإعداد وتحسين LLM الأنسب لاحتياجات مشروعك.
<Warning>
يتغير توفر النماذج باستمرار وقد يختلف حسب الحساب والمنطقة والمنصة السحابية. تستخدم الأمثلة أدناه نماذج متاحة وقت كتابة هذا الدليل، لكنها ليست قوائم دعم شاملة. قبل النشر، تحقق من معرّف النموذج وحالة دورة حياته في كتالوج المزود المرتبط.
</Warning>
<AccordionGroup>
<Accordion title="OpenAI">
يوفر CrewAI تكاملًا أصليًا مع OpenAI من خلال OpenAI Python SDK.
| o4-mini | 200,000 tokens | استدلال فعال من الجيل التالي |
تضيف OpenAI نماذج جديدة وتسحب snapshots قديمة بانتظام. راجع [كتالوج نماذج OpenAI](https://developers.openai.com/api/docs/models) للحصول على معرّفات النماذج الحالية ونوافذ السياق وتوافق endpoints ومعلومات دورة الحياة.
**Responses API:**
@@ -276,14 +248,7 @@ mode: "wide"
)
```
جميع النماذج المدرجة هنا https://llama.developer.meta.com/docs/models/ مدعومة.
| معرّف النموذج | طول سياق الإدخال | طول سياق المخرجات | وسائط الإدخال | وسائط المخرجات |
| --- | --- | --- | --- | --- |
| `meta_llama/Llama-4-Scout-17B-16E-Instruct-FP8` | 128k | 4028 | نص، صورة | نص |
| `meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8` | 128k | 4028 | نص، صورة | نص |
| `meta_llama/Llama-3.3-70B-Instruct` | 128k | 4028 | نص | نص |
| `meta_llama/Llama-3.3-8B-Instruct` | 128k | 4028 | نص | نص |
راجع [نظرة عامة على نماذج Meta Llama](https://ai.meta.com/llama/get-started/) للتعرّف على عائلات النماذج والوسائط وإرشادات حدود السياق الحالية.
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
```bash
@@ -353,7 +318,7 @@ mode: "wide"
from crewai import LLM
llm = LLM(
model="anthropic/claude-3-5-sonnet-20241022",
model="anthropic/claude-sonnet-4-6",
api_key="your-api-key", # Or set ANTHROPIC_API_KEY
يدعم CrewAI ميزة التفكير الموسّع من Anthropic، التي تتيح لـ Claude التفكير في المشكلات بطريقة أكثر شبهًا بالبشر قبل الاستجابة. مفيد بشكل خاص لمهام الاستدلال والتحليل وحل المشكلات المعقدة.
@@ -386,14 +349,14 @@ mode: "wide"
# Enable extended thinking with default settings
llm = LLM(
model="anthropic/claude-sonnet-4",
model="anthropic/claude-sonnet-4-6",
thinking={"type": "enabled"},
max_tokens=10000
)
# Configure thinking with budget control
llm = LLM(
model="anthropic/claude-sonnet-4",
model="anthropic/claude-sonnet-4-6",
thinking={
"type": "enabled",
"budget_tokens": 5000 # Limit thinking tokens
@@ -406,9 +369,7 @@ mode: "wide"
- `type`: عيّن إلى `"enabled"` لتفعيل وضع التفكير الموسّع
- `budget_tokens` (اختياري): أقصى رموز للتفكير (يساعد في التحكم بالتكاليف)
**النماذج التي تدعم التفكير الموسّع:**
- `claude-sonnet-4` والنماذج الأحدث
- `claude-3-7-sonnet` (مع قدرات التفكير الموسّع)
تختلف أوضاع التفكير والمعاملات المقبولة بين أجيال Claude. تحقق من قدرات النموذج المحدد قبل تفعيل `thinking`.
**متى تستخدم التفكير الموسّع:**
- الاستدلال المعقد وحل المشكلات متعددة الخطوات
@@ -424,13 +385,29 @@ mode: "wide"
**الميزات:**
- دعم استخدام الأدوات الأصلي لنماذج Claude 3+
- دعم التفكير الموسّع لـ Claude Sonnet 4+
- دعم التفكير الموسّع لنماذج Claude المتوافقة
- دعم البث للاستجابات في الوقت الفعلي
- معالجة تلقائية لرسائل النظام
- تسلسلات التوقف للتحكم في المخرجات
- تتبع استخدام الرموز
- محادثات استخدام أدوات متعددة الأدوار
**استخدام الرموز والتخزين المؤقت للمطالبة:**
يُبلّغ Anthropic عن الإدخال المفوتر في عدادات منفصلة — `input_tokens` (غير المخزن مؤقتًا)، و`cache_read_input_tokens`، و`cache_creation_input_tokens`. يدمج CrewAI الثلاثة ضمن `prompt_tokens` (و`input_tokens` الأصلي في استجابات المزود) بحيث يعكس `total_tokens` الاستخدام المفوتر الكامل على أعباء العمل المخزنة مؤقتًا.
يسجّل `cached_prompt_tokens` جزء قراءة الذاكرة المؤقتة كتفصيل فقط؛ وهو مُدرج بالفعل ضمن `prompt_tokens` ولا يجب إضافته مرة أخرى إلى `total_tokens`. يسجّل `cache_creation_tokens` عمليات الكتابة في الذاكرة المؤقتة بنفس الطريقة.
| claude-2 | 100,000 tokens | نموذج متعدد الاستخدامات |
| claude-instant | 100,000 tokens | سريع وفعال من حيث التكلفة للمهام اليومية |
راجع [نظرة عامة على نماذج Anthropic](https://platform.claude.com/docs/en/about-claude/models/overview) للحصول على معرّفات النماذج وقدراتها الحالية، وراجع [جدول إيقاف النماذج](https://platform.claude.com/docs/en/about-claude/model-deprecations) قبل تثبيت نموذج في الإنتاج.
| gemini-1.5-flash | 1M tokens | نموذج متعدد الوسائط متوازن، جيد لمعظم المهام |
| gemini-1.5-flash-8b | 1M tokens | الأسرع والأكثر كفاءة من حيث التكلفة |
| gemini-1.0-pro | 32,768 tokens | نموذج الجيل السابق |
تنشر Google معرّفات Gemini الحالية وقدراتها ومراحل دورة حياتها في [كتالوج نماذج Gemini](https://ai.google.dev/gemini-api/docs/models). تحقق من [جدول الإيقاف](https://ai.google.dev/gemini-api/docs/deprecations) قبل اختيار نموذج مستقر أو preview. وتستضيف Gemini API أيضًا [نماذج Gemma](https://ai.google.dev/gemma/docs).
**ملاحظة:** لاستخدام Google Gemini، ثبّت التبعيات المطلوبة:
```bash
uv add "crewai[google-genai]"
```
القائمة الكاملة للنماذج متاحة في [وثائق نماذج Gemini](https://ai.google.dev/gemini-api/docs/models).
</Accordion>
<Accordion title="Google (Vertex AI)">
احصل على بيانات الاعتماد من Google Cloud Console واحفظها في ملف JSON، ثم حمّلها بالكود التالي:
صادِق باستخدام [بيانات الاعتماد التلقائية للتطبيق](https://cloud.google.com/docs/authentication/provide-credentials-adc)، ثم اضبط مزود Gemini الأصلي لاستخدام Vertex AI:
```toml .env
GOOGLE_GENAI_USE_VERTEXAI=true
GOOGLE_CLOUD_PROJECT=<your-project-id>
GOOGLE_CLOUD_LOCATION=<location>
```
مثال الاستخدام في مشروع CrewAI:
@@ -605,15 +544,15 @@ mode: "wide"
from crewai import LLM
llm = LLM(
model="gemini-1.5-pro-latest", # or vertex_ai/gemini-1.5-pro-latest
temperature=0.7,
vertex_credentials=vertex_credentials_json
model="gemini/gemini-3.6-flash"
)
```
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
تختلف إتاحة Vertex AI باختلاف المنطقة. استخدم [كتالوج نماذج Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models) للتحقق من معرّف النموذج والمنطقة قبل النشر.
**ملاحظة:** يستخدم هذا المسار تكامل Gemini الأصلي في CrewAI. أضفه كتبعية لمشروعك:
aws_access_key_id="your-access-key", # Or set AWS_ACCESS_KEY_ID
aws_secret_access_key="your-secret-key", # Or set AWS_SECRET_ACCESS_KEY
aws_session_token="your-session-token", # For temporary credentials
@@ -719,37 +658,9 @@ mode: "wide"
- يجب أن تكون الرسالة الأولى من المستخدم (يتم التعامل معها تلقائيًا)
- بعض النماذج (مثل Cohere) تتطلب أن تنتهي المحادثة برسالة المستخدم
[Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html) هو خدمة مُدارة توفر الوصول إلى نماذج أساسية متعددة من أبرز شركات الذكاء الاصطناعي عبر واجهة API موحدة.
| Amazon Nova Pro | حتى 300k tokens | أداء عالٍ، نموذج يوازن بين الدقة والسرعة والفعالية من حيث التكلفة عبر مهام متنوعة. |
| Amazon Nova Micro | حتى 128k tokens | نموذج نصي فقط عالي الأداء وفعال من حيث التكلفة ومحسّن لأقل وقت استجابة. |
| Amazon Nova Lite | حتى 300k tokens | معالجة متعددة الوسائط بأسعار معقولة للصور والفيديو والنص مع قدرات في الوقت الفعلي. |
| Claude 3.7 Sonnet | حتى 128k tokens | الأفضل أداءً للاستدلال المعقد والبرمجة ووكلاء الذكاء الاصطناعي |
| Claude 3.5 Sonnet v2 | حتى 200k tokens | نموذج متطور متخصص في هندسة البرمجيات والقدرات الوكيلية والتفاعل مع الحاسوب بتكلفة محسّنة. |
| Claude 3.5 Sonnet | حتى 200k tokens | نموذج عالي الأداء يقدم ذكاءً واستدلالًا فائقين عبر مهام متنوعة مع توازن مثالي بين السرعة والتكلفة. |
| Claude 3.5 Haiku | حتى 200k tokens | نموذج متعدد الوسائط سريع وصغير محسّن للاستجابات السريعة والتفاعلات الشبيهة بالبشر |
| Claude 3 Sonnet | حتى 200k tokens | نموذج متعدد الوسائط يوازن بين الذكاء والسرعة للنشر بكميات كبيرة. |
| Claude 3 Haiku | حتى 200k tokens | نموذج متعدد الوسائط صغير وسريع محسّن للاستجابات السريعة والتفاعلات المحادثية الطبيعية |
| Claude 3 Opus | حتى 200k tokens | أكثر النماذج متعددة الوسائط تقدمًا يتفوق في المهام المعقدة بالاستدلال الشبيه بالبشر والفهم السياقي الفائق. |
| Claude 2.1 | حتى 200k tokens | إصدار محسّن بنافذة سياق موسّعة وموثوقية محسّنة وهلوسات أقل لتطبيقات النصوص الطويلة وRAG |
| Claude | حتى 100k tokens | نموذج متعدد الاستخدامات يتفوق في الحوار المتقدم والمحتوى الإبداعي واتباع التعليمات الدقيقة. |
| Claude Instant | حتى 100k tokens | نموذج سريع وفعال من حيث التكلفة للمهام اليومية مثل الحوار والتحليل والتلخيص والأسئلة والأجوبة |
| Llama 3.1 405B Instruct | حتى 128k tokens | نموذج LLM متقدم لتوليد البيانات الاصطناعية والتقطير والاستدلال لروبوتات المحادثة والبرمجة والمهام المتخصصة. |
| Llama 3.1 70B Instruct | حتى 128k tokens | يدعم المحادثات المعقدة مع فهم سياقي فائق واستدلال وتوليد نص. |
| Llama 3.1 8B Instruct | حتى 128k tokens | نموذج متطور مع فهم اللغة واستدلال فائق وتوليد النص. |
| Llama 3 70B Instruct | حتى 8k tokens | يدعم المحادثات المعقدة مع فهم سياقي فائق واستدلال وتوليد نص. |
| Llama 3 8B Instruct | حتى 8k tokens | نموذج LLM متطور مع فهم اللغة واستدلال فائق وتوليد النص. |
| Titan Text G1 - Lite | حتى 4k tokens | نموذج خفيف وفعال من حيث التكلفة محسّن لمهام اللغة الإنجليزية والضبط الدقيق مع التركيز على التلخيص وتوليد المحتوى. |
| Titan Text G1 - Express | حتى 8k tokens | نموذج متعدد الاستخدامات لمهام اللغة العامة والمحادثة وتطبيقات RAG مع دعم الإنجليزية وأكثر من 100 لغة. |
| Cohere Command | حتى 4k tokens | نموذج متخصص في اتباع أوامر المستخدم وتقديم حلول عملية للمؤسسات. |
| Jurassic-2 Mid | حتى 8,191 tokens | نموذج فعال من حيث التكلفة يوازن بين الجودة والسعر لمهام اللغة المتنوعة مثل الأسئلة والأجوبة والتلخيص وتوليد المحتوى. |
| Jurassic-2 Ultra | حتى 8,191 tokens | نموذج لتوليد النص المتقدم والفهم، يتفوق في المهام المعقدة مثل التحليل وإنشاء المحتوى. |
| Jamba-Instruct | حتى 256k tokens | نموذج بنافذة سياق موسّعة محسّن لتوليد النص الفعال من حيث التكلفة والتلخيص والأسئلة والأجوبة. |
| Mistral 7B Instruct | حتى 32k tokens | نموذج LLM يتبع التعليمات ويكمل الطلبات ويولد نصًا إبداعيًا. |
| Mistral 8x7B Instruct | حتى 32k tokens | نموذج LLM بمعمارية MOE يتبع التعليمات ويكمل الطلبات ويولد نصًا إبداعيًا. |
| nvidia/mistral-nemo-minitron-8b-8k-instruct | 8,192 tokens | نموذج لغة صغير متطور يقدم دقة فائقة لروبوتات المحادثة والمساعدين الافتراضيين وتوليد المحتوى. |
| nvidia/nemotron-4-mini-hindi-4b-instruct | 4,096 tokens | نموذج لغة صغير ثنائي اللغة هندي-إنجليزي للاستدلال على الجهاز، مصمم خصيصًا للغة الهندية. |
| Llama 3.1 70B/8B | 131,072 tokens | مهام عالية الأداء بسياق كبير |
| Llama 3.2 Series | 8,192 tokens | مهام ذات أغراض عامة |
| Mixtral 8x7B | 32,768 tokens | أداء متوازن وسياق جيد |
تميز Groq بين نماذج production وpreview وتسحب معرّفات النماذج بانتظام. تحقق من [كتالوج نماذج Groq](https://console.groq.com/docs/models) و[صفحة الإيقاف](https://console.groq.com/docs/deprecations) قبل اختيار نموذج للإنتاج.
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
```bash
@@ -1033,11 +873,12 @@ mode: "wide"
مثال الاستخدام في مشروع CrewAI:
```python Code
llm = LLM(
model="llama-3.1-sonar-large-128k-online",
base_url="https://api.perplexity.ai/"
model="perplexity/sonar-pro"
)
```
راجع [كتالوج نماذج Perplexity](https://docs.perplexity.ai/getting-started/models) و[changelog](https://docs.perplexity.ai/docs/resources/changelog) للحصول على معرّفات النماذج الحالية وإشعارات الإيقاف.
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
قد تتغير النماذج المستضافة في SambaNova Cloud بصورة مستقلة عن CrewAI. استعلم من [models endpoint](https://docs.sambanova.ai/docs/api-reference/models/get-environments-available-model-list-metadata) وراجع [دليل الإيقاف](https://docs.sambanova.ai/docs/en/models/deprecations) قبل النشر.
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
```bash
@@ -1101,7 +937,7 @@ mode: "wide"
مثال الاستخدام في مشروع CrewAI:
```python Code
llm = LLM(
model="cerebras/llama3.1-70b",
model="cerebras/gpt-oss-120b",
temperature=0.7,
max_tokens=8192
)
@@ -1115,6 +951,8 @@ mode: "wide"
- دعم نوافذ سياق طويلة
</Info>
راجع [كتالوج نماذج Cerebras](https://inference-docs.cerebras.ai/models/overview) و[إشعارات الإيقاف](https://inference-docs.cerebras.ai/support/deprecation) للحصول على معرّفات endpoints العامة الحالية.
**ملاحظة:** يستخدم هذا المزود LiteLLM. أضفه كتبعية لمشروعك:
يدعم CrewAI الاستجابات المهيكلة من استدعاءات LLM من خلال السماح لك بتحديد `response_format` باستخدام نموذج Pydantic. يمكّن هذا الإطار من تحليل المخرجات والتحقق منها تلقائيًا، مما يسهّل دمج الاستجابة في تطبيقك دون معالجة لاحقة يدوية.
يختلف دعم المخرجات المهيكلة باختلاف المزوّد والنموذج. اختبر النموذج الذي اخترته قبل الاعتماد على الاستجابات المهيكلة في بيئة الإنتاج.
- درجة حرارة منخفضة (0.1 إلى 0.3) للاستجابات الواقعية
- درجة حرارة عالية (0.7 إلى 0.9) للمهام الإبداعية
استخدم عناصر التحكم التي يدعمها النموذج المحدد. حسب المزود، قد تكون `temperature` أو مستوى reasoning أو thinking، أو تعليمات prompt تحدد الأسلوب والتباين المطلوبين.
</Tip>
</Step>
@@ -1500,6 +1339,38 @@ llm = LLM(
llm = LLM(model="gpt-4")
```
</Tab>
<Tab title="أخطاء البوابة">
<Tip>
تُعيد البوابات مثل OpenRouter الرمز `200 OK` بمجرد قبول المزود الأساسي للطلب، لذلك يصل انتهاء مهلة المزود داخل جسم الاستجابة بدلًا من رمز الحالة.
</Tip>
تُطلق CrewAI الاستثناء نفسه الذي كان رمز الحالة الأصلي سيُنتجه، ومن ثم يلتقط منطق إعادة المحاولة الموجود لديك هذا الفشل المُقنَّع:
result = llm.call("Summarize the incident", response_model=Report)
except openai.InternalServerError as e:
# "z-ai/glm-5.3 via openrouter.ai returned HTTP 200 with an upstream error
# and no choices: The operation was aborted (upstream code 504)"
print(f"Upstream provider failed, safe to retry: {e}")
```
<Warning>
إن استخدام `response_model` كبير أو متداخل بعمق يزيد احتمال انتهاء مهلة المزود. تعامل مع هذه الحالات كأعطال مؤقتة في المزود، وليس كإنتاج النموذج مخرجات منظمة تالفة.
واجهة سطر الأوامر هي الطريقة المدعومة لإنشاء مهارة — فهي تُنشئ لك هيكل المجلد وملف `SKILL.md` صالحًا:
```shell Terminal
crewai skill create code-review
```
داخل مشروع طاقم (حيث يوجد `pyproject.toml`) يُنشئ هذا الأمر `./skills/code-review/`؛ وخارج المشروع يُنشئ `./code-review/` في المجلد الحالي (يمكنك فرض هذا السلوك باستخدام `--no-project`):
للمهارات دورة حياة كاملة تُدار عبر واجهة سطر الأوامر: **أنشئها باستخدام `crewai skill create`، وانشرها باستخدام `crewai skill publish`** — إنشاء المجلدات يدويًا يصلح للتجارب المحلية، لكن واجهة سطر الأوامر هي سير العمل المقصود، وهي تحافظ على صحة هيكل المهارة وبياناتها الوصفية.
### الإنشاء
```shell Terminal
crewai skill create my-skill
```
يُنشئ هذا الأمر المجلد (داخل `./skills/` في مشروع الطاقم) مع قالب `SKILL.md`، بالإضافة إلى مجلدات فارغة `scripts/` و `references/` و `assets/`. عدّل `SKILL.md` لتعريف التعليمات.
### النشر
نفّذ الأمر من داخل مجلد المهارة (حيث يوجد `SKILL.md`):
```shell Terminal
cd skills/my-skill
crewai skill publish
```
يقرأ النشر الحقول `name` و `description` و `metadata.version` من البيانات الوصفية في مقدمة `SKILL.md` ويدفع المهارة إلى سجل CrewAI. **المهارات المنشورة تكون دائمًا مقيّدة بنطاق مؤسستك** — مثل الأدوات، لا يستطيع رؤيتها وتثبيتها إلا أعضاء المؤسسة الناشرة؛ ولا توجد رؤية عامة. أعلام مفيدة:
| العلم | التأثير |
| :--- | :--- |
| `--org <slug>` | النشر تحت مؤسسة محددة (يتجاوز الإعدادات). |
| `--force` | تخطي التحقق من حالة git (تغييرات غير مُثبتة، إلخ). |
### التثبيت
ثبّت مهارة منشورة عبر مرجعها `@org-uuid/name`:
```shell Terminal
crewai skill install @your-org-uuid/code-review
```
<Note>
استخدم **UUID** الخاص بمؤسستك وليس اسمها — فأسماء المؤسسات ليست فريدة، وقد يشير الاسم إلى مؤسسة خاطئة فيفشل التثبيت برسالة "غير موجود". شغّل `crewai org list` لعرض الـ UUID (عمود `ID`) لكل مؤسسة تنتمي إليها.
</Note>
داخل مشروع الطاقم تُثبَّت المهارة في `./skills/{name}/`؛ وخارج المشروع تذهب إلى ذاكرة التخزين المؤقتة المشتركة في `~/.crewai/skills/{org-uuid}/{name}/`.
يمكن للوكلاء أيضًا الإشارة إلى مهارات السجل مباشرة — يتم حلّها من ذاكرة التخزين المؤقتة المحلية (أو من مجلد `skills/` في المشروع) وقت التشغيل:
```python
agent = Agent(
role="Senior Code Reviewer",
goal="Review pull requests for quality and security issues",
backstory="Staff engineer with expertise in secure coding practices.",
تبويب **Automations** هو عرض العمليات للقراءة فقط في [Agent Control Plane](/ar/enterprise/features/agent-control-plane/overview). يجمع بين بطاقتَي مقاييس و sankey تفاعلي وجدولين فرعيين — **Automations** و **Consumption** — يمكنك البحث والتصفية والفرز فيهما.
<Frame>

</Frame>
تحترم جميع المخططات والجداول مُحدّد **آخر 24 ساعة / الأسبوع الماضي / آخر 30 يوماً** في أعلى اليمين. تقارن قيم الفرق النافذة المختارة بالنافذة السابقة بنفس الطول.
<Note>
تعرض الصفوف بيانات فقط لعمليات النشر على **crewAI v1.13 أو أحدث** — تظهر عمليات النشر الأقدم في لافتة *"We've detected N other automations that we can't display"* أسفل sankey ولا تساهم بأي مقاييس حتى يتم تحديثها وإعادة نشرها. راجع [نظرة عامة — المتطلبات](/ar/enterprise/features/agent-control-plane/overview#المتطلبات).
</Note>
## لوحة المعلومات
يحتوي رأس الصفحة على بطاقتَي مقاييس و sankey تفاعلي. النقر على أي من البطاقتين يبدّل sankey بين وضعَين:
- **وضع الصحة** — `إجمالي الأتمتات → حِزم الحالة (Critical / Warning / Healthy)`. انقر على حِزمة لتصفية جدول Automations إلى عمليات النشر تلك فقط.
- **وضع الاستهلاك** — `مزودو النماذج → الأتمتات → التكلفة الإجمالية`. انقر على مزود لتصفية جدول Consumption إلى ذلك المزود.
| البطاقة | ما تعرضه |
|------|---------------|
| **Automations** | الأتمتات `active` (والعدد الإجمالي)، إجمالي `errors` في النافذة، `active executions` الحالية (والإجمالي في النافذة)، مع الفرق مقابل الفترة السابقة. |
| **Consumption** | إجمالي `cost` و `tokens used`، مع فرق التكلفة مقابل الفترة السابقة. |
<Frame>

</Frame>
## جدول Automations
التبويب الفرعي **Automations** هو تفصيل صحة الأسطول لكل deployment. كل صف هو crew أو flow منشور.
<Frame>

</Frame>
| العمود | ما يعرضه |
|--------|---------------|
| **Automation** | اسم الـ deployment وأي وسوم مُسنَدة إليه (مثل `production`، `financial`). |
| **Last execution** | الوقت المنقضي منذ آخر تنفيذ. |
| **Health Status Breakdown** | شريط مكدّس بنسب `Critical` / `Warning` / `Healthy` لعمليات التنفيذ في النافذة. |
| **Executions with Errors** | إجمالي عمليات التنفيذ الفاشلة في النافذة. |
| **PII detection applied** | `Yes` إذا كان هناك تكوين PII لكل deployment أو [قاعدة PII](/ar/enterprise/features/agent-control-plane/rules) مطابِقة نشطة. |
| **Executions** | إجمالي عمليات التنفيذ في النافذة. |
| **Last updated** | متى أُعيد نشر الـ deployment آخر مرة. |
| **Crew Version** | إصدار `crewai` الذي يُبلِّغ عنه الـ deployment. يشير أيقونة المعلومات بجانب الإصدارات الأقل من `1.13` إلى صفوف لا يمكنها المساهمة بالمقاييس. |
ابحث بالاسم، صفِّ حسب `Status` (`Healthy` / `Warning` / `Critical`)، وافرز بأي رأس عمود. انقر على اسم الـ deployment لفتح **لوحة الأتمتة**.
## جدول Consumption
التبويب الفرعي **Consumption** هو تفصيل إنفاق LLM واستخدام الرموز لكل deployment.
<Frame>

</Frame>
| العمود | ما يعرضه |
|--------|---------------|
| **Automation** | اسم الـ deployment. |
| **Last execution** | الوقت المنقضي منذ آخر تنفيذ. |
| **Tokens used** | صف واحد لكل مزود LLM تستخدمه هذه الأتمتة، مع الفرق مقابل الفترة السابقة. |
| **Cost** | التكلفة لكل مزود LLM، مع الفرق مقابل الفترة السابقة. |
| **Total cost** | المجموع عبر جميع المزودين، مع الفرق. |
| **Executions** | إجمالي عمليات التنفيذ في النافذة. |
| **Last updated** | متى أُعيد نشر الـ deployment آخر مرة. |
| **Crew Version** | إصدار `crewai` الذي يُبلِّغ عنه الـ deployment. |
صفِّ حسب **LLM provider** وافرز حسب `Cost` أو `Executions` أو `Last run`.
<Info>
**عادة ما تعني الخلايا الفارغة (`—` أو `$0.00`) أن الـ deployment أدنى من crewAI v1.13.** في اللقطة أعلاه، تظهر *Automation F* (`1.7.0`) و *Automation I* (`1.12.2`) فارغة في الرموز والتكلفة — لا تزال عمليات التنفيذ تعمل، لكنها لا تُصدِر التليمتري على مستوى المزود الذي يُغذِّي هذا الجدول. حدّث هذه الـ crews وأعد نشرها لبدء جمع بيانات الاستهلاك.
</Info>
## ذو صلة
<CardGroup cols={2}>
<Card title="Agent Control Plane — نظرة عامة" icon="book-open" href="/ar/enterprise/features/agent-control-plane/overview">
ما هو ACP، المتطلبات، مستويات الخطط، و RBAC.
</Card>
<Card title="Agent Control Plane — القواعد" icon="shield-check" href="/ar/enterprise/features/agent-control-plane/rules">
طبّق قواعد PII Redaction على مستوى المؤسسة عبر العديد من الأتمتات.
**Agent Control Plane** (ACP) هو مركز العمليات لكل ما يعمل لديك على CrewAI AMP. إنها شاشة واحدة — مقسّمة إلى تبويبَي **Automations** و **Rules** — تمنح فريقك القدرة على:
- مراقبة **حالة (الصحة)** كل أتمتة حيّة (crew أو flow) بتفصيل `Critical` / `Warning` / `Healthy` وعدد عمليات التنفيذ.
- تتبع **استهلاك LLM** — الرموز (tokens) والتكلفة — لكل أتمتة ولكل مزود ولكل نموذج، مع الفرق مقابل الفترة السابقة.
- التعمّق في أي أتمتة منفردة أو مزود نماذج لرؤية المخططات الزمنية وتفصيل البيانات لكل مزود.
- تطبيق **قواعد (Rules)** على مستوى المؤسسة (اليوم: PII Redaction) عبر العديد من الأتمتات دفعة واحدة بدلاً من تعديل كل deployment على حدة.
<Frame>

</Frame>
<Note>
Agent Control Plane مُوسوم حالياً بـ **Beta** في CrewAI Platform.
- **Rules** — *"كيف أفرض سياسة (مثل PII redaction) عبر العديد من عمليات النشر دون إعادة نشر كل واحدة؟"* راجع [القواعد](/ar/enterprise/features/agent-control-plane/rules).
## المتطلبات
<Warning>
يُشترط **crewAI v1.13 أو أحدث** ليتمكن أي أتمتة من تعبئة أي بيانات على هذه الصفحة — تمر بيانات الصحة وعمليات التنفيذ والأخطاء والرموز والتكلفة عبر التليمتري الذي تم تفعيله في `crewai==1.13`. تظهر عمليات النشر الأقدم في لافتة *"We've detected N other automations that we can't display"* ولا تساهم بأي صفوف حتى يتم تحديثها وإعادة نشرها.
</Warning>
<Warning>
يُشترط **خطة Enterprise أو Ultra** لإنشاء أو تعديل [القواعد](/ar/enterprise/features/agent-control-plane/rules). يمكن للمؤسسات على الخطط الأدنى فتح تبويب Rules وعرض القواعد الموجودة، ولكن يُعرض المحرر للقراءة فقط مع شارة قفل "Enterprise" والتنبيه *"PII Redaction rules require an Enterprise plan."*. المراقبة (تبويب Automations) متاحة في جميع الخطط حيث يكون هذا الميزة مفعّلة.
</Warning>
- يجب أن تكون ميزة **Agent Control Plane** مفعّلة لمؤسستك. إن لم ترها في الشريط الجانبي، اطلب من مالك الحساب تفعيلها.
- داخل ACP، يحكم [RBAC](/ar/enterprise/features/rbac) الوصول: `read` للعرض في لوحة المعلومات والقواعد، و`manage` لإنشاء وتعديل وتشغيل/إيقاف وحذف القواعد.
- يمكن ضبط نطاق جميع المخططات والجداول إلى **آخر 24 ساعة** أو **الأسبوع الماضي** أو **آخر 30 يوماً** عبر مُحدّد الوقت في أعلى اليمين. تقارن قيم الفرق (`↑ 8 vs yesterday`, `↓ $20.57 vs yesterday` وغيرها) النافذة المختارة بالنافذة السابقة بنفس الطول.
تتيح لك القواعد تطبيق سياسات — اليوم: **PII Redaction** — عبر العديد من الأتمتات دفعة واحدة، بدلاً من ضبط كل deployment على حدة. افتح تبويب **Rules** في [Agent Control Plane](/ar/enterprise/features/agent-control-plane/overview) لإدارتها.
تعرض كل بطاقة قاعدة الاسم والوصف و**النطاق (scope)** الذي تنطبق عليه القاعدة (الأدوات والوسوم المختارة) وعدد **الأتمتات المُفعَّلة** — عمليات النشر التي تطابق النطاق حالياً. يقوم المُفتاح على اليمين بتشغيل القاعدة أو إيقافها دون حذفها.
## المتطلبات
<Warning>
يُشترط **خطة Enterprise أو Ultra** لإنشاء أو تعديل قواعد PII Redaction. يمكن للمؤسسات على الخطط الأدنى فتح تبويب Rules وعرض القواعد الموجودة، ولكن يُعرض المحرر للقراءة فقط مع شارة قفل "Enterprise" والتنبيه *"PII Redaction rules require an Enterprise plan."* — تواصل مع مالك حسابك أو المبيعات للترقية.
</Warning>
- يجب أن تكون ميزة **Agent Control Plane** مفعّلة لمؤسستك. راجع [نظرة عامة — المتطلبات](/ar/enterprise/features/agent-control-plane/overview#المتطلبات).
- تحتاج إلى صلاحية `manage` ضمن [RBAC](/ar/enterprise/features/rbac) على Agent Control Plane لإنشاء وتعديل وتشغيل/إيقاف وحذف القواعد. صلاحية `read` كافية لعرضها.
- تُسجَّل جميع تغييرات القواعد بإصدارات للتدقيق.
## أنواع القواعد المتاحة
| النوع | ما تفعله |
|------|---------------|
| **PII Redaction** | تطبّق PII redaction على عمليات التنفيذ لكل أتمتة مطابِقة، باستخدام نفس كتالوج الكيانات و recognizers المخصصة الموثَّقة في [PII Redaction للـ Traces](/ar/enterprise/features/pii-trace-redactions). |
انقر على **+ Create new** في أعلى يمين تبويب Rules، أو على **View Details** في بطاقة قاعدة موجودة.
</Step>
<Step title="سَمِّ القاعدة وصِفها">
أعطِ القاعدة اسماً واضحاً (مثل *Mask PII (CC)*) ووصفاً يشرح متى تنطبق. يظهر كلاهما على بطاقة القاعدة وفي مودال Engaged Automations.
</Step>
<Step title="اختر النوع">
اليوم **PII Redaction** فقط متاحة.
</Step>
<Step title="حدّد الشروط">
تحدد الشروط الأتمتات التي تنخرط معها القاعدة. كلاهما اختياري ويستخدم دلالات **مساواة المجموعات (set-equality)**:
- **Tools** — تنخرط فقط الأتمتات التي تتطابق مجموعة أدواتها **تطابقاً تامّاً** مع الأدوات المختارة. اختر من تطبيقات Studio و MCPs والأدوات مفتوحة المصدر وأدوات سجل Tool Repository.
- **Automations** — تنخرط فقط الأتمتات التي تتطابق مجموعة وسومها **تطابقاً تامّاً** مع الوسوم المختارة.
ترك مُحدِّد فارغ يعني "بدون تصفية على هذا البعد". ترك كليهما فارغَين يعني أن القاعدة تنطبق على **كل** أتمتة في المؤسسة.
</Step>
<Step title="اضبط جدول PII Mask Type">
حدّد كل نوع كيان تريد تغطيته واختر **Mask** (يستبدل بتسمية الكيان مثل `<CREDIT_CARD>`) أو **Redact** (يحذف النص المطابِق بالكامل). راجع [PII Redaction للـ Traces](/ar/enterprise/features/pii-trace-redactions) للاطلاع على كتالوج الكيانات الكامل وكيفية إضافة recognizers مخصصة على مستوى المؤسسة.
</Step>
<Step title="احفظ">
تنطبق القاعدة على عمليات التنفيذ **المستقبلية** لكل أتمتة مُفعَّلة بمجرد الحفظ. لا حاجة لإعادة النشر.
</Step>
</Steps>
## الأتمتات المُفعَّلة
انقر على **Engaged N automations** في أي بطاقة قاعدة لرؤية أي عمليات النشر تطابقها القاعدة حالياً بالضبط، إلى جانب آخر تنفيذ لكل منها.
هذه هي أسرع طريقة للتحقق من نطاق قاعدة قبل تمكينها — على سبيل المثال، للتأكد من أن قاعدة محدَّدة بنطاق وسم `production` لا تطابق عن طريق الخطأ deployment تجريبي.
## قواعد على مستوى المؤسسة مقابل إعدادات لكل deployment
يمكن ضبط PII Redaction في مكانين:
- **لكل deployment** — ضمن **Settings → PII Protection** على كل deployment على حدة ([الدليل](/ar/enterprise/features/pii-trace-redactions))
- **على مستوى المؤسسة** — كقاعدة في هذه الصفحة
عندما يتطابق نطاق قاعدة مُفعَّلة على مستوى المؤسسة مع deployment، يُجاوز تكوين الكيانات الخاص بالقاعدة **إعدادات PII المملوكة من قبل الـ deployment** لعمليات تنفيذ ذلك الـ deployment — تصبح القاعدة المصدر الوحيد للحقيقة طالما هي مرتبطة. عطّل القاعدة أو فُكَّ ارتباطها (أو غيِّر نطاقها بحيث لا تتطابق بعد الآن) ويعود الـ deployment إلى إعدادات PII Protection الخاصة به.
فضّل القواعد على مستوى المؤسسة عندما تريد فرض سياسة متسقة عبر العديد من عمليات النشر؛ احتفظ بالضبط لكل deployment للاستثناءات الفردية.
## ذو صلة
<CardGroup cols={2}>
<Card title="Agent Control Plane — نظرة عامة" icon="book-open" href="/ar/enterprise/features/agent-control-plane/overview">
ما هو ACP، المتطلبات، مستويات الخطط، و RBAC.
</Card>
<Card title="Agent Control Plane — المراقبة" icon="gauge" href="/ar/enterprise/features/agent-control-plane/monitoring">
description: 'تعرّف على كيفية استخدام مستودعات الوكلاء لمشاركة وإعادة استخدام وكلائك عبر الفرق والمشاريع'
icon: 'people-group'
mode: "wide"
---
تتيح مستودعات الوكلاء لمستخدمي المؤسسات تخزين ومشاركة وإعادة استخدام تعريفات الوكلاء عبر الفرق والمشاريع. تُمكّن هذه الميزة المؤسسات من الاحتفاظ بمكتبة مركزية من الوكلاء الموحدين، مما يعزز الاتساق ويقلل من ازدواجية الجهود.
1. **اصطلاح التسمية**: استخدم أسماء واضحة ووصفية لوكلاء المستودع
2. **التوثيق**: أدرج أوصافًا شاملة لكل وكيل
3. **إدارة الأدوات**: تأكد من توفر الأدوات المشار إليها بواسطة وكلاء المستودع في بيئتك
4. **التحكم في الوصول**: أدر الصلاحيات لضمان أن أعضاء الفريق المصرّح لهم فقط يمكنهم تعديل وكلاء المستودع
## إدارة المؤسسة
للتبديل بين المؤسسات أو عرض مؤسستك الحالية، استخدم واجهة سطر أوامر CrewAI:
```bash
# عرض المؤسسة الحالية
crewai org current
# التبديل إلى مؤسسة مختلفة
crewai org switch <org_id>
# عرض جميع المؤسسات المتاحة
crewai org list
```
<Note>
عند تحميل الوكلاء من المستودعات، يجب أن تكون مصادقًا ومتحولًا إلى المؤسسة الصحيحة. إذا تلقيت أخطاء، تحقق من حالة المصادقة وإعدادات المؤسسة باستخدام أوامر CLI أعلاه.
description: "إدارة ونشر ومراقبة أطقمك المباشرة (الأتمتة) في مكان واحد."
icon: "rocket"
mode: "wide"
---
## نظرة عامة
الأتمتة هي مركز العمليات المباشرة لأطقمك المنشورة. استخدمها للنشر من GitHub أو ملف ZIP، وإدارة متغيرات البيئة، وإعادة النشر عند الحاجة، ومراقبة حالة كل أتمتة.
يعكس اللوح سير العمل كعُقد وأسهم مع ثلاث لوحات داعمة تتيح لك تهيئة سير العمل بسهولة بدون كتابة كود؛ ما يُعرف بـ "**البرمجة الحدسية لوكلاء الذكاء الاصطناعي**".
يمكنك استخدام وظيفة السحب والإفلات لإضافة الوكلاء والمهام والأدوات إلى اللوح أو استخدام قسم الدردشة لبناء الوكلاء. يتشارك كلا النهجين الحالة ويمكن استخدامهما بالتبادل.
- **أفكار AI (يسار)**: الاستدلال المتدفق أثناء تصميم سير العمل
description: "مراجعة بشرية بمستوى المؤسسات للتدفقات مع إشعارات البريد الإلكتروني أولاً وقواعد التوجيه وإمكانيات الاستجابة التلقائية"
icon: "users-gear"
mode: "wide"
---
<Note>
تتطلب ميزات إدارة Flow HITL مزيّن `@human_feedback`، المتاح في **CrewAI الإصدار 1.8.0 أو أحدث**. تنطبق هذه الميزات تحديدًا على **التدفقات (Flows)**، وليس الأطقم (Crews).
</Note>
يوفر CrewAI Enterprise نظامًا شاملًا لإدارة الإنسان في الحلقة (HITL) للتدفقات يحوّل سير عمل الذكاء الاصطناعي إلى عمليات تعاونية بين الإنسان والذكاء الاصطناعي. تستخدم المنصة **بنية البريد الإلكتروني أولاً** التي تمكّن أي شخص لديه عنوان بريد إلكتروني من الرد على طلبات المراجعة — بدون الحاجة لحساب على المنصة.
يمكن للمستجيبين الرد مباشرة على رسائل الإشعار لتقديم الملاحظات
</Card>
<Card title="توجيه مرن" icon="route">
توجيه الطلبات إلى بريد إلكتروني محدد بناءً على أنماط الطرق أو حالة التدفق
</Card>
<Card title="استجابة تلقائية" icon="clock">
تهيئة استجابات احتياطية تلقائية عندما لا يرد أي شخص في الوقت المحدد
</Card>
</CardGroup>
### الفوائد الرئيسية
- **نموذج ذهني بسيط**: عناوين البريد الإلكتروني عالمية؛ لا حاجة لإدارة مستخدمين أو أدوار المنصة
- **مستجيبون خارجيون**: يمكن لأي شخص لديه بريد إلكتروني الرد، حتى غير مستخدمي المنصة
- **تعيين ديناميكي**: سحب بريد المعيّن مباشرة من حالة التدفق (مثل `sales_rep_email`)
- **تهيئة مخفضة**: إعدادات أقل للتهيئة، وقت أسرع للقيمة
- **البريد الإلكتروني كقناة رئيسية**: يفضل معظم المستخدمين الرد عبر البريد الإلكتروني بدلاً من تسجيل الدخول إلى لوحة التحكم
## إعداد نقاط المراجعة البشرية في التدفقات
هيّئ نقاط تفتيش المراجعة البشرية داخل تدفقاتك باستخدام مزيّن `@human_feedback`. عندما يصل التنفيذ إلى نقطة مراجعة، يتوقف النظام ويُخطر المعيّن عبر البريد الإلكتروني وينتظر الاستجابة.
```python
from crewai.flow.flow import Flow, start, listen, or_
from crewai.flow.human_feedback import human_feedback, HumanFeedbackResult
class ContentApprovalFlow(Flow):
@start()
def generate_content(self):
return "Generated marketing copy for Q1 campaign..."
@human_feedback(
message="Please review this content for brand compliance:",
description: "منع واكتشاف هلوسات الذكاء الاصطناعي في مهام CrewAI"
icon: "shield-check"
mode: "wide"
---
## نظرة عامة
حاجز الهلوسة هو ميزة مؤسسية تتحقق من المحتوى المولّد بالذكاء الاصطناعي لضمان أنه مبني على الحقائق ولا يحتوي على هلوسات. يحلل مخرجات المهام مقابل سياق مرجعي ويوفر ملاحظات مفصلة عند اكتشاف محتوى محتمل الهلوسة.
## ما هي الهلوسات؟
تحدث هلوسات الذكاء الاصطناعي عندما تولّد نماذج اللغة محتوى يبدو معقولاً لكنه غير صحيح من الناحية الواقعية أو غير مدعوم بالسياق المقدم. يساعد حاجز الهلوسة في منع هذه المشكلات من خلال:
- مقارنة المخرجات مع السياق المرجعي
- تقييم الأمانة للمادة المصدرية
- توفير ملاحظات مفصلة حول المحتوى المشكل
- دعم عتبات مخصصة لصرامة التحقق
## الاستخدام الأساسي
### إعداد الحاجز
```python
from crewai.tasks.hallucination_guardrail import HallucinationGuardrail
from crewai import LLM
# الاستخدام الأساسي - سيستخدم expected_output للمهمة كسياق
guardrail = HallucinationGuardrail(
llm=LLM(model="gpt-4o-mini")
)
# مع سياق مرجعي صريح
context_guardrail = HallucinationGuardrail(
context="AI helps with various tasks including analysis and generation.",
llm=LLM(model="gpt-4o-mini")
)
```
### الإضافة إلى المهام
```python
from crewai import Task
# إنشاء مهمتك مع الحاجز
task = Task(
description="Write a summary about AI capabilities",
expected_output="A factual summary based on the provided context",
agent=my_agent,
guardrail=guardrail # إضافة الحاجز للتحقق من المخرجات
)
```
## التهيئة المتقدمة
### التحقق بعتبة مخصصة
للتحقق الأكثر صرامة، يمكنك تعيين عتبة أمانة مخصصة (مقياس 0-10):
```python
# حاجز صارم يتطلب درجة أمانة عالية
strict_guardrail = HallucinationGuardrail(
context="Quantum computing uses qubits that exist in superposition states.",
llm=LLM(model="gpt-4o-mini"),
threshold=8.0 # يتطلب درجة >= 8 لاجتياز التحقق
)
```
### تضمين سياق استجابة الأدوات
عندما تستخدم مهمتك أدوات، يمكنك تضمين استجابات الأدوات لتحقق أكثر دقة:
```python
# حاجز مع سياق استجابة الأدوات
weather_guardrail = HallucinationGuardrail(
context="Current weather information for the requested location",
llm=LLM(model="gpt-4o-mini"),
tool_response="Weather API returned: Temperature 22°C, Humidity 65%, Clear skies"
)
```
## كيف يعمل
### عملية التحقق
1. **تحليل السياق**: يقارن الحاجز مخرجات المهمة مع السياق المرجعي المقدم
2. **تسجيل الأمانة**: يستخدم مقيّمًا داخليًا لتعيين درجة أمانة (0-10)
3. **تحديد الحكم**: يحدد ما إذا كان المحتوى أمينًا أو يحتوي على هلوسات
4. **التحقق من العتبة**: إذا تم تعيين عتبة مخصصة، يتحقق مقابل تلك الدرجة
5. **توليد الملاحظات**: يوفر أسبابًا مفصلة عند فشل التحقق
### منطق التحقق
- **الوضع الافتراضي**: يستخدم التحقق المبني على الحكم (FAITHFUL مقابل HALLUCINATED)
- **وضع العتبة**: يتطلب أن تلبي درجة الأمانة العتبة المحددة أو تتجاوزها
"feedback": "Content appears to be hallucinated (score: 4.2/10, verdict: HALLUCINATED). The output contains information not supported by the provided context."
}
```
### خصائص النتيجة
- **valid**: قيمة منطقية تشير إلى ما إذا اجتازت المخرجات التحقق
- **feedback**: شرح مفصل عند فشل التحقق، يتضمن:
- درجة الأمانة
- تصنيف الحكم
- أسباب محددة للفشل
## التكامل مع نظام المهام
### التحقق التلقائي
عند إضافة حاجز إلى مهمة، يتحقق تلقائيًا من المخرجات قبل اعتبار المهمة مكتملة:
```python
# تدفق التحقق من مخرجات المهمة
task_output = agent.execute_task(task)
validation_result = guardrail(task_output)
if validation_result.valid:
# المهمة تكتمل بنجاح
return task_output
else:
# المهمة تفشل مع ملاحظات التحقق
raise ValidationError(validation_result.feedback)
```
### تتبع الأحداث
يتكامل الحاجز مع نظام أحداث CrewAI لتوفير المراقبة:
- **بدء التحقق**: عند بدء تقييم الحاجز
- **اكتمال التحقق**: عند انتهاء التقييم بالنتائج
- **فشل التحقق**: عند حدوث أخطاء تقنية أثناء التقييم
## أفضل الممارسات
### إرشادات السياق
<Steps>
<Step title="توفير سياق شامل">
أدرج جميع المعلومات الواقعية ذات الصلة التي يجب أن يبني عليها الذكاء الاصطناعي مخرجاته:
```python
context = """
Company XYZ was founded in 2020 and specializes in renewable energy solutions.
They have 150 employees and generated $50M revenue in 2023.
Their main products include solar panels and wind turbines.
"""
```
</Step>
<Step title="الحفاظ على صلة السياق">
أدرج فقط المعلومات المرتبطة مباشرة بالمهمة لتجنب الارتباك:
```python
# جيد: سياق مركّز
context = "The current weather in New York is 18°C with light rain."
# تجنب: معلومات غير ذات صلة
context = "The weather is 18°C. The city has 8 million people. Traffic is heavy."
```
</Step>
<Step title="تحديث السياق بانتظام">
تأكد من أن السياق المرجعي يعكس معلومات حالية ودقيقة.
</Step>
</Steps>
### اختيار العتبة
<Steps>
<Step title="البدء بالتحقق الافتراضي">
ابدأ بدون عتبات مخصصة لفهم الأداء الأساسي.
</Step>
<Step title="الضبط بناءً على المتطلبات">
- **محتوى عالي الأهمية**: استخدم عتبة 8-10 للدقة القصوى
- **محتوى عام**: استخدم عتبة 6-7 للتحقق المتوازن
- **محتوى إبداعي**: استخدم عتبة 4-5 أو التحقق الافتراضي المبني على الحكم
</Step>
<Step title="المراقبة والتكرار">
تتبع نتائج التحقق واضبط العتبات بناءً على الإيجابيات/السلبيات الكاذبة.
</Step>
</Steps>
## اعتبارات الأداء
### التأثير على زمن التنفيذ
- **عبء التحقق**: يضيف كل حاجز حوالي 1-3 ثوانٍ لكل مهمة
- **كفاءة LLM**: اختر نماذج فعالة للتقييم (مثل gpt-4o-mini)
### تحسين التكلفة
- **اختيار النموذج**: استخدم نماذج أصغر وفعالة لتقييم الحاجز
- **حجم السياق**: اجعل السياق المرجعي موجزًا لكن شاملًا
- **التخزين المؤقت**: فكّر في تخزين نتائج التحقق مؤقتًا للمحتوى المتكرر
## استكشاف الأخطاء وإصلاحها
<Accordion title="فشل التحقق دائمًا">
**الأسباب المحتملة:**
- السياق مقيّد جدًا أو غير مرتبط بمخرجات المهمة
- العتبة معينة عالية جدًا لنوع المحتوى
- السياق المرجعي يحتوي على معلومات قديمة
**الحلول:**
- مراجعة وتحديث السياق ليتطابق مع متطلبات المهمة
- خفض العتبة أو استخدام التحقق الافتراضي المبني على الحكم
- التأكد من أن السياق حالي ودقيق
</Accordion>
<Accordion title="إيجابيات كاذبة (محتوى صالح يُعلّم كغير صالح)">
**الأسباب المحتملة:**
- العتبة عالية جدًا للمهام الإبداعية أو التفسيرية
- السياق لا يغطي جميع الجوانب الصالحة للمخرجات
- نموذج التقييم محافظ بشكل مفرط
**الحلول:**
- خفض العتبة أو استخدام التحقق الافتراضي
- توسيع السياق ليشمل محتوى مقبول أوسع
- الاختبار مع نماذج تقييم مختلفة
</Accordion>
<Accordion title="أخطاء التقييم">
**الأسباب المحتملة:**
- مشكلات في الاتصال بالشبكة
- نموذج LLM غير متاح أو محدود المعدل
- مخرجات مهمة أو سياق غير صالح
**الحلول:**
- التحقق من الاتصال بالشبكة وحالة خدمة LLM
- تنفيذ منطق إعادة المحاولة للأعطال المؤقتة
- التحقق من تنسيق مخرجات المهمة قبل تقييم الحاجز
</Accordion>
<Card title="هل تحتاج مساعدة؟" icon="headset" href="mailto:support@crewai.com">
تواصل مع فريق الدعم للمساعدة في تهيئة حاجز الهلوسة أو استكشاف الأخطاء وإصلاحها.
description: "إخفاء البيانات الحساسة تلقائياً من تتبعات تنفيذ الطواقم والتدفقات"
icon: "lock"
mode: "wide"
---
## نظرة عامة
إخفاء البيانات الشخصية (PII Redaction) هو ميزة في CrewAI AMP تكتشف تلقائياً وتُقنّع معلومات التعريف الشخصية (PII) في تتبعات تنفيذ الطواقم والتدفقات. يضمن ذلك عدم كشف البيانات الحساسة مثل أرقام بطاقات الائتمان وأرقام الضمان الاجتماعي وعناوين البريد الإلكتروني والأسماء في تتبعات CrewAI AMP. يمكنك أيضاً إنشاء مُعرّفات مخصصة لحماية البيانات الخاصة بمؤسستك.
<Info>
إخفاء البيانات الشخصية متاح في خطة Enterprise.
يجب أن يكون إصدار النشر 1.8.0 أو أعلى.
</Info>
<Frame>

</Frame>
## أهمية إخفاء البيانات الشخصية
عند تشغيل وكلاء الذكاء الاصطناعي في بيئة الإنتاج، غالباً ما تمر معلومات حساسة عبر طواقمك:
- بيانات العملاء من تكاملات CRM
- معلومات مالية من معالجات الدفع
- تفاصيل شخصية من إرسالات النماذج
- بيانات الموظفين الداخلية
بدون إخفاء مناسب، تظهر هذه البيانات في التتبعات، مما يجعل الامتثال للوائح مثل GDPR وHIPAA وPCI-DSS أمراً صعباً. يحل إخفاء البيانات الشخصية هذه المشكلة عن طريق إقناع البيانات الحساسة تلقائياً قبل تخزينها في التتبعات.
## كيف يعمل
1. **الاكتشاف** - مسح بيانات أحداث التتبع بحثاً عن أنماط PII المعروفة
2. **التصنيف** - تحديد نوع البيانات الحساسة (بطاقة ائتمان، SSN، بريد إلكتروني، إلخ.)
3. **الإقناع/الإخفاء** - استبدال البيانات الحساسة بقيم مُقنّعة بناءً على تهيئتك
```
Original: "Contact john.doe@company.com or call 555-123-4567"
Redacted: "Contact <EMAIL_ADDRESS> or call <PHONE_NUMBER>"
```
## تفعيل إخفاء البيانات الشخصية
<Info>
يجب أن تكون على خطة Enterprise وأن يكون إصدار النشر 1.8.0 أو أعلى لاستخدام هذه الميزة.
</Info>
<Steps>
<Step title="الانتقال إلى إعدادات الطاقم">
في لوحة تحكم CrewAI AMP، اختر طاقمك المنشور وانتقل إلى أحد عمليات النشر/الأتمتة، ثم انتقل إلى **Settings** → **PII Protection**.
</Step>
<Step title="تفعيل حماية البيانات الشخصية">
فعّل **PII Redaction for Traces**. سيؤدي ذلك إلى تفعيل المسح والإخفاء التلقائي لبيانات التتبع.
<Info>
تحتاج إلى تفعيل إخفاء البيانات الشخصية يدوياً لكل عملية نشر.
</Info>
<Frame>

</Frame>
</Step>
<Step title="تهيئة أنواع الكيانات">
اختر أنواع البيانات الشخصية التي تريد اكتشافها وإخفاءها. يمكن تفعيل أو تعطيل كل كيان بشكل فردي.
- **Entity Type**: تسمية الكيان التي ستظهر في المخرجات المُخفاة (مثل `EMPLOYEE_ID`، `SALARY`)
- **Type**: اختر بين Regex Pattern أو Deny List
- **Pattern/Values**: نمط Regex أو قائمة نصوص للمطابقة
- **Confidence Threshold**: الحد الأدنى للنتيجة (0.0-1.0) المطلوبة لتفعيل الإخفاء عند المطابقة. القيم الأعلى (مثل 0.8) تقلل الإيجابيات الخاطئة لكن قد تفوّت بعض المطابقات. القيم الأقل (مثل 0.5) تلتقط المزيد من المطابقات لكن قد تُفرط في الإخفاء. القيمة الافتراضية هي 0.8.
- **Context Words** (اختياري): كلمات تزيد ثقة الاكتشاف عند وجودها بالقرب
</Step>
<Step title="الحفظ">
احفظ المُعرّف. سيكون متاحاً للتفعيل في عمليات النشر الخاصة بك.
</Step>
</Steps>
### فهم أنواع الكيانات
يحدد **Entity Type** كيفية ظهور المحتوى المُطابق في التتبعات المُخفاة:
```
Entity Type: SALARY
Pattern: salary:\s*\$\s*\d+
Input: "Employee salary: $50,000"
Output: "Employee <SALARY>"
```
### استخدام كلمات السياق
تحسّن كلمات السياق الدقة عن طريق زيادة الثقة عند ظهور مصطلحات محددة بالقرب من النمط المُطابق:
```
Context Words: "project", "code", "internal"
Entity Type: PROJECT_CODE
Pattern: PRJ-\d{4}
```
عندما تظهر كلمة "project" أو "code" بالقرب من "PRJ-1234"، يكون لدى المُعرّف ثقة أعلى بأنها مطابقة حقيقية، مما يقلل الإيجابيات الخاطئة.
## عرض التتبعات المُخفاة
بمجرد تفعيل إخفاء البيانات الشخصية، ستعرض تتبعاتك قيماً مُخفاة بدلاً من البيانات الحساسة:
```
Task Output: "Customer <PERSON> placed order #12345.
Payment processed for card ending in <CREDIT_CARD>."
```
القيم المُخفاة مُعلّمة بوضوح بأقواس زاوية وتسمية نوع الكيان (مثل `<EMAIL_ADDRESS>`)، مما يسهّل فهم البيانات التي تمت حمايتها مع السماح لك بتصحيح الأخطاء ومراقبة سلوك الطاقم.
## أفضل الممارسات
### اعتبارات الأداء
<Steps>
<Step title="فعّل الكيانات المطلوبة فقط">
كل كيان مُفعّل يضيف عبء معالجة. فعّل فقط الكيانات ذات الصلة ببياناتك.
</Step>
<Step title="استخدم أنماطاً محددة">
للمُعرّفات المخصصة، استخدم أنماطاً محددة لتقليل الإيجابيات الخاطئة وتحسين الأداء. أنماط Regex هي الأفضل عند تحديد أنماط معينة في التتبعات مثل الرواتب ومعرّفات الموظفين ورموز المشاريع وغيرها. مُعرّفات قائمة الحظر هي الأفضل عند تحديد نصوص بعينها في التتبعات مثل أسماء الشركات والأسماء الرمزية الداخلية وغيرها.
</Step>
<Step title="استفد من كلمات السياق">
تحسّن كلمات السياق الدقة عن طريق تفعيل الاكتشاف فقط عندما يتطابق النص المحيط.
</Step>
</Steps>
## استكشاف الأخطاء وإصلاحها
<Accordion title="البيانات الشخصية لا تُخفى">
**الأسباب المحتملة:**
- نوع الكيان غير مُفعّل في التهيئة
- النمط لا يتطابق مع تنسيق البيانات
- المُعرّف المخصص يحتوي على أخطاء في الصياغة
**الحلول:**
- تحقق من أن الكيان مُفعّل في Settings → Security
- اختبر أنماط Regex مع بيانات نموذجية
- تحقق من السجلات بحثاً عن أخطاء التهيئة
</Accordion>
<Accordion title="إخفاء بيانات أكثر من اللازم">
**الأسباب المحتملة:**
- أنواع كيانات واسعة جداً مُفعّلة (مثل `DATE_TIME` تلتقط التواريخ في كل مكان)
- أنماط المُعرّف المخصص عامة جداً
**الحلول:**
- عطّل الكيانات التي تسبب إيجابيات خاطئة
- اجعل الأنماط المخصصة أكثر تحديداً
- أضف كلمات سياق لتحسين الدقة
</Accordion>
<Accordion title="مشاكل الأداء">
**الأسباب المحتملة:**
- عدد كبير جداً من الكيانات المُفعّلة
- الكيانات القائمة على NLP (مثل `PERSON` و`LOCATION` و`NRP`) مكلفة حسابياً لأنها تستخدم نماذج تعلم الآلة
**الحلول:**
- فعّل فقط الكيانات التي تحتاجها فعلاً
- فكّر في استخدام بدائل قائمة على الأنماط حيثما أمكن
- راقب أوقات معالجة التتبعات في لوحة التحكم
</Accordion>
---
## مثال عملي: مطابقة نمط الراتب
يوضح هذا المثال كيفية إنشاء مُعرّف مخصص لاكتشاف وإقناع معلومات الرواتب في تتبعاتك.
### حالة الاستخدام
يعالج طاقمك بيانات موظفين أو بيانات مالية تتضمن معلومات رواتب بتنسيقات مثل:
- `salary: $50,000`
- `salary: $125,000.00`
- `salary:$1,500.50`
تريد إقناع هذه القيم تلقائياً لحماية بيانات التعويضات الحساسة.
| `(\.\d{2})?` | يطابق اختيارياً السنتات (مثل ".00"، ".50") |
### أمثلة على النتائج
```
Original: "Employee record shows salary: $125,000.00 annually"
Redacted: "Employee record shows <SALARY> annually"
Original: "Base salary:$50,000 with bonus potential"
Redacted: "Base <SALARY> with bonus potential"
```
<Tip>
إضافة كلمات سياق مثل "salary" و"compensation" و"pay" و"wage" و"income" تساعد في زيادة ثقة الاكتشاف عند ظهور هذه المصطلحات بالقرب من النمط المُطابق، مما يقلل الإيجابيات الخاطئة.
</Tip>
### تفعيل المُعرّف لعمليات النشر
<Warning>
إنشاء مُعرّف مخصص على مستوى المؤسسة لا يفعّله تلقائياً لعمليات النشر. يجب عليك تفعيل كل مُعرّف يدوياً لكل عملية نشر تريد تطبيقه عليها.
</Warning>
بعد إنشاء المُعرّف المخصص، فعّله لكل عملية نشر:
<Steps>
<Step title="الانتقال إلى عملية النشر">
انتقل إلى عملية النشر/الأتمتة وافتح **Settings** → **PII Protection**.
</Step>
<Step title="اختيار المُعرّفات المخصصة">
تحت **Mask Recognizers**، سترى المُعرّفات المحددة على مستوى مؤسستك. حدد المربع بجانب المُعرّفات التي تريد تفعيلها.
احفظ تغييراتك. سيكون المُعرّف نشطاً في جميع عمليات التنفيذ اللاحقة لعملية النشر هذه.
</Step>
</Steps>
<Info>
كرر هذه العملية لكل عملية نشر تحتاج فيها إلى المُعرّف المخصص. يمنحك ذلك تحكماً دقيقاً في المُعرّفات النشطة في البيئات المختلفة (مثل بيئة التطوير مقابل بيئة الإنتاج).
| **Owner** | وصول كامل لجميع الميزات والإعدادات. لا يمكن تقييده. |
| **Member** | وصول للقراءة لمعظم الميزات، وصول إدارة لمتغيرات البيئة واتصالات LLM ومشاريع Studio. لا يمكنه تعديل إعدادات المؤسسة أو الإعدادات الافتراضية. |
| `default_settings` | Manage | No access | Manage / No access | تعديل الإعدادات الافتراضية على مستوى المؤسسة |
| `organization_settings` | Manage | No access | Manage / No access | إدارة الفوترة والخطط وتهيئة المؤسسة |
| `studio_projects` | Manage | Manage | Manage / No access | إنشاء وتعديل المشاريع في Studio |
<Tip>
عند إنشاء دور مخصص، يمكن ضبط معظم الميزات على **Manage** أو **Read** أو **No access**. ومع ذلك، فإن `environment_variables` و`llm_connections` و`default_settings` و`organization_settings` و`studio_projects` تدعم فقط **Manage** أو **No access** — لا يوجد خيار للقراءة فقط لهذه الميزات.
</Tip>
---
## النشر من GitHub أو Zip
من أكثر أسئلة RBAC شيوعاً: _"ما الصلاحيات التي يحتاجها عضو الفريق للنشر؟"_
### النشر من GitHub
لنشر أتمتة من مستودع GitHub، يحتاج المستخدم إلى:
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات حيث يتم إنشاء عمليات النشر
2. **الوصول إلى مستودع Git** (إذا كان RBAC على مستوى الكيان لمستودعات Git مفعلاً): يجب منح دور المستخدم الوصول إلى مستودع Git المحدد عبر صلاحيات مستوى الكيان
3. **`studio_projects`: `Manage`** — إذا كان يبني الطاقم في Studio قبل النشر
### النشر من Zip
لنشر أتمتة من ملف Zip، يحتاج المستخدم إلى:
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات
2. **تفعيل نشر Zip**: يجب ألا تكون المؤسسة قد عطلت نشر Zip في إعدادات المؤسسة
description: تكوين AWS Secrets Manager عبر Workload Identity للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
sidebarTitle: بـ Workload Identity
icon: "id-badge"
---
## نظرة عامة
يُكوِّن هذا الدليل AWS Secrets Manager كمزود أسرار باستخدام **Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على بيانات اعتماد AWS عبر STS، وتقرأ أسرارك — دون تخزين أي مفتاح وصول AWS طويل الأمد في أي مكان.
<Note>
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة ولا تهتم بانتشار التدوير، راجع الدليل الأبسط [AWS — المفاتيح الثابتة / AssumeRole](/ar/enterprise/features/secrets-manager/aws).
</Note>
### كيف يعمل وقت التشغيل
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
2. يستدعي العامل `sts:AssumeRoleWithWebIdentity` على دور IAM الذي ستُعدّه أدناه، مُقدِّماً الـ JWT.
3. تتحقق AWS STS من الـ JWT مقابل مُصدر OIDC العام لـ CrewAI Platform (لذا يجب أن يكون تنصيب منصتك قابلاً للوصول من AWS)، ثم تُعيد بيانات اعتماد AWS قصيرة الأمد.
4. يستخدم العامل تلك البيانات لاستدعاء `secretsmanager:GetSecretValue`.
5. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
- حساب AWS لديه إذن إنشاء مزوّدي OIDC وأدوار وسياسات IAM.
- منطقة AWS التي تعيش (أو ستعيش) فيها أسرارك، مثلاً `us-east-1`.
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
- **UUID مؤسسة CrewAI الخاصة بك.** يمكنك العثور عليه في صفحة إعدادات المؤسسة في CrewAI Platform — تُربط سياسة الثقة في الخطوة 3 دور IAM بهذه المؤسسة تحديداً.
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من AWS عبر HTTPS** ليتمكّن AWS STS من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت (أو أن AWS يمكنه الوصول إليه شبكياً عبر VPC peering أو ما يعادله).
</Note>
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` في تلك الوثيقة هو الرابط الذي ستُسجِّله AWS كمزود OIDC موثوق.
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
<Tip>
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
</Tip>
## الخطوة 2 — تسجيل CrewAI Platform كمزود هوية OIDC في IAM
افتح [وحدة تحكم IAM ← Identity providers](https://console.aws.amazon.com/iam/home#/identity_providers) وانقر على **Add provider**.
- **Provider type:** OpenID Connect.
- **Provider URL:** قيمة `issuer` من الخطوة 1 (مثلاً `https://app.crewai.com`).
انسخ **OpenIDConnectProviderArn** من المخرجات (أو ARN المزود من الوحدة). ستستخدمه في الخطوة 3.
<Note>
لا تتحقق AWS فعلياً من بصمة الإبهام لاستدعاءات STS WebIdentity — فهي دائماً تُعيد جلب JWKS وقت التحقق — لكن واجهة الـ API تتطلب وجود الحقل.
</Note>
{/* SCREENSHOT: AWS IAM "Add identity provider" form filled with the Platform issuer URL and audience sts.amazonaws.com → /images/secrets-manager/aws-wi/01-add-oidc-provider.png */}
احفظ كـ `trust-policy.json`، مع استبدال `<YOUR_ACCOUNT_ID>` و `<your-platform-host>` (مضيف المُصدر **بدون** `https://` أو `http://`، مثلاً `app.crewai.com`) و `<YOUR_CREWAI_ORG_UUID>` (من المتطلبات المسبقة):
انسخ **Role Arn** من المخرجات — هذا هو `aws_role_arn` الخاص بك. ستلصقه في CrewAI Platform في الخطوة 6.
<Tip>
يحدّد الشرطان نطاق الثقة بدقة: يقيّد `aud` افتراض الدور إلى الرموز ذات جمهور AWS STS، ويقصر `sub` الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة. تُعيّن CrewAI Platform كلا الادّعاءين دائماً على رموز AWS workload identity.
</Tip>
{/* SCREENSHOT: IAM "Create role" with Web Identity trust type, federated provider selector pointing at the CrewAI Platform OIDC provider → /images/secrets-manager/aws-wi/03-create-role-trust.png */}
## الخطوة 4 — إنشاء وإرفاق سياسة IAM لوصول Secrets Manager + KMS
احفظ كـ `secrets-policy.json`، مع استبدال العناصر النائبة بمعرّف حسابك ومنطقتك وبادئة اسم السر و ARN(s) مفاتيح KMS التي تُشفّر تلك الأسرار:
تُشغّل `SecretsManagerListForUI` ميزة **الاقتراح التلقائي لاسم السر** في نموذج متغيرات البيئة وزر **Test Connection** على بيانات الاعتماد. يقبل `secretsmanager:ListSecrets` فقط `Resource: "*"` — فهو محصور على مستوى الحساب في طبقة IAM.
أرفق السياسة بالدور إما عبر CLI (سياسة مضمنة، أبسط) أو واجهة الوحدة؛ للبيئات التي تعيد استخدام نفس الأذونات عبر أدوار متعددة، استخدم علامة التبويب **Managed policy** لسياسة مُسمّاة قابلة لإعادة الاستخدام.
<Tabs>
<Tab title="سياسة مضمنة (CLI)">
```bash
aws iam put-role-policy \
--role-name crewai-secrets-reader \
--policy-name SecretsManagerRead \
--policy-document file://secrets-policy.json
```
يُرفق هذا السياسة **مضمنةً** بالدور. السياسات المضمنة مرتبطة بالدور ولا يمكن إعادة استخدامها على أدوار أخرى.
السياسة المُدارة هي مورد IAM مستقل يمكنك إرفاقه بأدوار متعددة.
</Tab>
<Tab title="وحدة التحكم (UI)">
1. افتح [وحدة تحكم IAM ← Roles](https://console.aws.amazon.com/iam/home#/roles) واختر **crewai-secrets-reader**.
2. في علامة التبويب **Permissions**، انقر على **Add permissions** ← **Create inline policy**.
3. بدّل إلى محرر **JSON** والصق محتوى `secrets-policy.json`.
4. انقر على **Next**، أعطِ السياسة اسماً (مثلاً `SecretsManagerRead`)، وانقر على **Create policy**.
لإنشاء سياسة مُدارة قابلة لإعادة الاستخدام بدلاً من ذلك، استخدم **IAM ← Policies ← Create policy** ثم أرفقها بالدور من علامة التبويب **Permissions** الخاصة بالدور.
{/* SCREENSHOT: IAM Role detail → Permissions → Create inline policy with JSON editor → /images/secrets-manager/aws-wi/03b-attach-inline-policy.png */}
</Tab>
</Tabs>
## الخطوة 5 — إنشاء سر واحد على الأقل في AWS
إذا لم يكن لديك سر للاختبار، أنشئ واحداً الآن:
```bash
aws secretsmanager create-secret \
--region <REGION> \
--name crewai-test-keyword \
--secret-string "hello from aws"
```
أو عبر [وحدة تحكم AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/) ← **Store a new secret**.
{/* SCREENSHOT: AWS Secrets Manager "Store a new secret" page with a sample value → /images/secrets-manager/aws-wi/04-create-secret.png */}
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
{/* SCREENSHOT: Empty state of Workload Identity page with "Add Workload Identity Config" button → /images/secrets-manager/aws-wi/06-amp-wi-empty-state.png */}
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `aws-prod`.
- **Cloud Provider:** `AWS`.
- **AWS Role ARN:** **Role Arn** من الخطوة 3.
- **AWS Region:** المنطقة التي تعيش فيها أسرارك، مثلاً `us-east-1`.
- (اختياري) حدّد **Set as default for AWS** إذا كنت ترغب في أن يكون تكوين WI هذا هو الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ AWS.
انقر على **Create**.
{/* SCREENSHOT: "Add Workload Identity Config" form with AWS, role ARN, and region filled in → /images/secrets-manager/aws-wi/07-amp-add-wi-config-aws.png */}
{/* SCREENSHOT: Workload Identity list showing the new AWS row with "(default)" badge if applicable → /images/secrets-manager/aws-wi/08-amp-wi-list-with-aws.png */}
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6 (مثلاً `aws-prod`).
- (اختياري) حدّد **Set as default credential for this provider**.
سيطلب النموذج فقط **AWS Region** ضمن Workload Identity — حقول بيانات الاعتماد الثابتة (Access Key ID و Secret Access Key و Role ARN و External ID) مخفية عمداً لأنها لا تنطبق على هذا المسار؛ يأتي ARN الدور من تكوين WI المرتبط.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + Workload Identity + WI config dropdown selected → /images/secrets-manager/aws-wi/09-amp-add-credential-aws-wi.png */}
## الخطوة 8 — اختبار الاتصال
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT، وتبادله مع AWS STS عبر `sts:AssumeRoleWithWebIdentity`، وتؤكد أن بيانات الاعتماد الناتجة يمكنها استدعاء `sts:GetCallerIdentity` مقابل الدور المُفترَض. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
نجاح Test Connection يُثبت أن سياسة الثقة وتسجيل مزود OIDC وشرط الجمهور موصولة جميعها بشكل صحيح. لا يُثبت ذلك أن IAM لكل سر صحيح — يُمارَس `secretsmanager:GetSecretValue` على ARN سر محدد بشكل منفصل عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
الآن أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
الفرق الوحيد بين متغيرات البيئة المدعومة بـ WI والمدعومة بمفاتيح ثابتة هو **متى** يُقرأ السر:
- **مدعوم بـ WI:** تُقرأ قيمة السر طازجة في كل إطلاق أتمتة.
- **مدعوم بمفاتيح ثابتة:** تُقرأ قيمة السر وقت النشر وتُدمج في صورة النشر.
## الخطوة 10 — التحقق من التدوير
بعد تشغيل عملية النشر، دوّر السر في AWS:
```bash
aws secretsmanager update-secret \
--region <REGION> \
--secret-id crewai-test-keyword \
--secret-string "rotated value"
```
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
للتأكد في السجلات (إذا كان لديك وصول إلى العامل)، ابحث عن:
```
Workload identity config '<id>' (aws): N secret(s) resolved
```
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `GetSecretValue` طازج مقابل AWS.
## استكشاف الأخطاء
| العَرَض | السبب المحتمل |
|---|---|
| يفشل Test Connection بخطأ مصافحة | رُفض استدعاء `sts:AssumeRoleWithWebIdentity`. تحقق من أن ARN الكيان الموحَّد في سياسة الثقة يشير إلى `oidc-provider/<your-platform-host>` (المضيف **بدون** `https://` أو `http://` وبدون شرطة مائلة لاحقة)، وأن شرط الجمهور هو بالضبط `sts.amazonaws.com`، وأن شرط `sub` يطابق UUID مؤسسة CrewAI الخاصة بك، وأن رابط اكتشاف OIDC للمنصة قابل للوصول من AWS عبر الإنترنت العام. |
| `InvalidIdentityToken: Couldn't retrieve verification key from your identity provider` | لا يمكن لـ AWS STS الوصول إلى مضيف CrewAI Platform لجلب JWKS. تأكد من أن المضيف متاح عبر الإنترنت من AWS، وأن رابط اكتشاف OIDC يُعيد 200، وأن نقطة نهاية JWKS قابلة للوصول. |
| `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity` | عدم تطابق سياسة الثقة. تحقق من الخطوة 3 من جديد: يجب أن يتضمن ARN الكيان الموحَّد `oidc-provider/<your-platform-host>` (المضيف **بدون** `https://` أو `http://` وبدون شرطة مائلة لاحقة)، ويجب أن يكون شرط الجمهور بالضبط `sts.amazonaws.com`، وأن يساوي شرط `sub` بالضبط `organization:<YOUR_CREWAI_ORG_UUID>`. |
| يُظهر الاقتراح التلقائي لاسم السر `AccessDenied: secretsmanager:ListSecrets` | يفتقد الدور إلى `secretsmanager:ListSecrets` مع `Resource: "*"`. أضف بيان `SecretsManagerListForUI` من الخطوة 4. |
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن IAM المحصور بالمورد مفقود على السر الفاشل. راجع أذونات `secretsmanager:GetSecretValue` و `kms:Decrypt` للدور على ARN ذلك السر بعينه ومفتاح KMS الخاص به. |
| `RegionDisabledException` / لم يُعثر على أسرار | لا تطابق المنطقة في تكوين Workload Identity المكان الفعلي للسر. تحقق من الخطوة 6 من جديد. |
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
- AWS: [Configuring a role for OpenID Connect federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_relying-party.html)
- AWS: [STS:AssumeRoleWithWebIdentity API reference](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
## الخطوات التالية
- [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
- للتنوع متعدد السحاب، راجع أيضاً [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) و [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity).
description: تكوين AWS Secrets Manager كمزود أسرار لـ CrewAI Platform باستخدام مفاتيح الوصول الثابتة أو AssumeRole
sidebarTitle: ببيانات اعتماد ثابتة
icon: "key"
---
## نظرة عامة
يأخذك هذا الدليل عبر تكوين AWS Secrets Manager كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **بيانات الاعتماد الثابتة** (مفاتيح الوصول، اختيارياً مع AssumeRole). بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في حساب AWS الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
<Note>
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة (بدون إعادة نشر)، راجع [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity).
</Note>
<Note>
يغطي هذا الدليل التكوين من جانب AWS وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
</Note>
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- حساب AWS لديه إذن إنشاء مستخدمي IAM وسياسات يديرها العميل و(اختيارياً) أدوار IAM.
- منطقة AWS التي تعيش (أو ستعيش) فيها أسرارك، مثلاً `us-east-1`.
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
</Note>
## اختر طريقة المصادقة
تدعم CrewAI Platform طريقتين لمصادقة المنصة مع AWS Secrets Manager. اختر واحدة قبل أن تبدأ — تختلف الخطوات أدناه بناءً على اختيارك.
| الطريقة | متى تُستخدم | المقايضات |
|---|---|---|
| **مفاتيح الوصول الثابتة** | البداية، عمليات نشر بحساب واحد | أبسط إعداد؛ يجب تدوير مفاتيح الوصول يدوياً |
| **AssumeRole** | عبر الحسابات، تشديد الإنتاج | بيانات اعتماد قصيرة الأمد؛ يدعم External ID؛ يتطلب دور IAM إضافي |
تستخدم بقية هذا الدليل علامات تبويب في الخطوات 3–5 لتتمكن من اتباع المسار المطابق لاختيارك.
## الخطوة 1 — إنشاء مستخدم IAM
افتح [وحدة تحكم IAM](https://console.aws.amazon.com/iam/)، انتقل إلى **Users**، ثم انقر على **Create user**.
- الاسم المقترح: `crewai-secrets-reader`.
- اترك **Provide user access to the AWS Management Console** بدون تحديد — هذا الكيان تستخدمه CrewAI Platform برمجياً، وليس البشر.
- انقر على **Next**.
في صفحة **Set permissions**، اترك الاختيار الافتراضي. ستُرفق السياسة في الخطوة 3.
انقر على **Next**، راجع، وانقر على **Create user**.
للتفاصيل الكاملة، راجع وثائق AWS: [Create an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
{/* SCREENSHOT: AWS IAM "Create user" form filled with name "crewai-secrets-reader" → /images/secrets-manager/aws/01-create-iam-user.png */}
## الخطوة 2 — إنشاء سياسة IAM
تحتاج CrewAI Platform إلى وصول للقراءة فقط إلى AWS Secrets Manager وإذن لفك تشفير الأسرار عبر KMS. أنشئ سياسة يديرها العميل بـ JSON التالي.
في وحدة تحكم IAM، انتقل إلى **Policies**، ثم انقر على **Create policy**.
اختر علامة التبويب **JSON** واستبدل المحتوى بـ:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecretsManagerRead",
"Effect": "Allow",
"Action": [
"secretsmanager:ListSecrets",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "*"
},
{
"Sid": "KMSDecrypt",
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:Decrypt"
],
"Resource": "*"
}
]
}
```
انقر على **Next**، ثم في صفحة **Review and create**:
- **Policy name:** `CrewAISecretsManagerRead`
- **Description (optional):** `Read-only access to AWS Secrets Manager for CrewAI Platform`
انقر على **Create policy**.
<Tip>
تمنح السياسة أعلاه `*` على `Resource` للبساطة. في الإنتاج، حدّد نطاق `Resource` إلى ARNs الخاصة بالأسرار التي يجب على CrewAI Platform الوصول إليها، وحدّد نطاق `kms:Decrypt` إلى ARNs مفاتيح KMS التي تُشفّر تلك الأسرار. راجع [إرشادات AWS حول أقل الامتيازات](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html).
</Tip>
{/* SCREENSHOT: AWS IAM "Create policy" → JSON tab with the policy above pasted → /images/secrets-manager/aws/02-create-policy-json-editor.png */}
{/* SCREENSHOT: AWS IAM "Review and create policy" page with name "CrewAISecretsManagerRead" → /images/secrets-manager/aws/03-policy-review-and-create.png */}
## الخطوة 3 — إرفاق السياسة
<Tabs>
<Tab title="مفاتيح الوصول الثابتة">
1. في وحدة تحكم IAM، انتقل إلى **Users** وانقر على المستخدم الذي أنشأته في الخطوة 1.
2. في علامة التبويب **Permissions**، انقر على **Add permissions** ← **Attach policies directly**.
3. ابحث عن `CrewAISecretsManagerRead`، حدّدها، وانقر على **Next**.
مع AssumeRole، تُرفَق السياسة بـ **دور** IAM منفصل (وليس مباشرة بالمستخدم). يحتاج المستخدم من الخطوة 1 فقط إلى إذن لاستدعاء `sts:AssumeRole` على ذلك الدور.
**إنشاء الدور:**
1. في وحدة تحكم IAM، انتقل إلى **Roles** وانقر على **Create role**.
2. **Trusted entity type:** AWS account. اختر **This account** (أو **Another AWS account** لإعدادات عبر الحسابات، ثم أدخل معرّف حساب AWS الذي يستضيف مستخدم IAM من الخطوة 1).
3. (موصى به) حدّد **Require external ID** وأدخل قيمة تُولّدها بنفسك — هذا سر مشترك ستلصقه في CrewAI Platform في الخطوة 5.
4. انقر على **Next**.
5. أرفق سياسة `CrewAISecretsManagerRead`.
6. انقر على **Next**، سمِّ الدور `CrewAISecretsManagerRole`، وانقر على **Create role**.
**اسمح لمستخدم IAM بافتراض الدور:**
1. افتح الدور الذي أنشأته للتو وانسخ **ARN** الخاص به.
2. في وحدة تحكم IAM، انتقل إلى **Users**، انقر على المستخدم من الخطوة 1، وفي علامة التبويب **Permissions** انقر على **Add permissions** ← **Create inline policy**.
3. في علامة التبويب **JSON**، الصق ما يلي (استبدل `ROLE_ARN_FROM_ABOVE`):
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "ROLE_ARN_FROM_ABOVE"
}
]
}
```
4. سمِّ السياسة `CrewAIAssumeSecretsRole` وانقر على **Create policy**.
{/* SCREENSHOT: IAM "Create role" trust policy step with External ID checkbox enabled → /images/secrets-manager/aws/04b-create-role-trust-policy.png */}
{/* SCREENSHOT: Inline sts:AssumeRole policy attached to the IAM user → /images/secrets-manager/aws/04c-attach-assumerole-on-user.png */}
</Tab>
</Tabs>
## الخطوة 4 — الحصول على بيانات الاعتماد
<Tabs>
<Tab title="مفاتيح الوصول الثابتة">
1. في وحدة تحكم IAM، افتح المستخدم من الخطوة 1.
2. انقر على علامة التبويب **Security credentials**.
3. تحت **Access keys**، انقر على **Create access key**.
4. اختر **Application running outside AWS** (أو **Other**) كحالة استخدام. انقر على **Next**.
5. (اختياري) أضف وسماً وصفياً. انقر على **Create access key**.
6. انقر على **Show** للكشف عن مفتاح الوصول السري، ثم انسخ كلاً من **Access key ID** و **Secret access key**، أو انقر على **Download .csv file**.
<Warning>
يظهر مفتاح الوصول السري مرة واحدة فقط. إذا أغلقت هذه الصفحة دون نسخه، فستحتاج إلى حذف المفتاح وإنشاء واحد جديد.
</Warning>
للتفاصيل الكاملة، راجع وثائق AWS: [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
{/* SCREENSHOT: Empty state of Secret Provider Credentials page with "Add Credential" button → /images/secrets-manager/usage/02-amp-credentials-empty-state.png */}
<Tabs>
<Tab title="مفاتيح الوصول الثابتة">
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `aws-prod`.
- **Provider:** `AWS Secrets Manager`.
- **Region:** منطقة AWS التي تعيش فيها أسرارك، مثلاً `us-east-1`. يجب أن تطابق منطقة الأسرار التي تريد قراءتها.
- **Access Key ID:** القيمة من الخطوة 4.
- **Secret Access Key:** القيمة من الخطوة 4.
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار AWS بدون تحديد بيانات اعتماد صراحةً.
اترك **Role ARN** و **External ID** فارغين.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + static access keys filled in → /images/secrets-manager/usage/03a-amp-add-credential-form-aws-static.png */}
</Tab>
<Tab title="AssumeRole">
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `aws-prod-assumerole`.
- **Provider:** `AWS Secrets Manager`.
- **Region:** منطقة AWS التي تعيش فيها أسرارك.
- **Access Key ID:** مفتاح وصول مستخدم IAM من الخطوة 4 (يُستخدم لاستدعاء STS).
- **Secret Access Key:** مفتاح الوصول السري لمستخدم IAM من الخطوة 4.
- **Role ARN:** Role ARN الذي نسخته في الخطوة 4.
- **External ID:** External ID الذي عيّنته على سياسة الثقة الخاصة بالدور (احذفه إن لم يوجد).
- (اختياري) حدّد **Set as default credential for this provider**.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + AssumeRole fields filled in → /images/secrets-manager/usage/03b-amp-add-credential-form-aws-assumerole.png */}
</Tab>
</Tabs>
<Note>
**كيف تتصرف الطريقتان وقت التشغيل:**
- مع **مفاتيح الوصول الثابتة** فقط، تستدعي CrewAI Platform AWS Secrets Manager مباشرةً باستخدام المفاتيح التي قدّمتها.
- عند تعيين **Role ARN**، تستدعي CrewAI Platform أولاً `sts:AssumeRole` بمفاتيح الوصول المقدَّمة (و External ID إن كان مكوَّناً)، ثم تستخدم بيانات الاعتماد قصيرة الأمد التي تُعيدها STS لقراءة أسرارك.
</Note>
{/* SCREENSHOT: Credentials list showing the new AWS row, with "(default)" badge if applicable → /images/secrets-manager/usage/04-amp-credential-created.png */}
## الخطوة 6 — إنشاء سر واحد على الأقل في AWS
إذا لم يكن لديك بالفعل أسرار في AWS Secrets Manager، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 7.
في [وحدة تحكم AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/)، انقر على **Store a new secret**.
- **Secret type:** اختر **Other type of secret**.
- **Key/value pairs** — إما:
- إدخال زوج أو أكثر من مفتاح/قيمة (موصى به للأسرار المهيكلة)، أو
- استخدام علامة التبويب **Plaintext** لقيمة نصية واحدة.
- **Encryption key:** استخدم `aws/secretsmanager` (المفتاح الذي يديره AWS) ما لم تكن لديك متطلبات محددة لمفتاح KMS.
انقر على **Next**، ثم أدخل:
- **Secret name:** اسم فريد، مثلاً `crewai/openai-api-key`.
- **Description (optional):** ملاحظة قصيرة عن غرض السر.
انقر على **Next** عبر خطوات التدوير والمراجعة، ثم انقر على **Store**.
<Note>
**صيغة الإشارة بمفتاح JSON.** إذا خزّنت سراً بأزواج مفتاح/قيمة متعددة (كائن JSON)، يمكن لـ CrewAI Platform استخراج حقل محدد باستخدام صيغة `secret-name#json_key` في إشارات متغيرات البيئة. على سبيل المثال، يمكن الإشارة إلى سر باسم `database-credentials` بـ `{"username": "...", "password": "..."}` باسم `database-credentials#password`. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
| `AccessDenied` على `secretsmanager:ListSecrets` | السياسة غير مُرفقة، أو المستخدم خاطئ. تحقق من الخطوة 3 من جديد. |
| `AccessDenied` على `kms:Decrypt` | بيان `KMSDecrypt` مفقود، أو أن أسرارك تستخدم مفتاح KMS يديره العميل لا يغطّيه `Resource: "*"`. |
| `InvalidClientTokenId` / `SignatureDoesNotMatch` | معرّف مفتاح الوصول أو مفتاح الوصول السري خاطئ. تحقق من الخطوتين 4 و 5 من جديد. |
| `RegionDisabledException` / لم يُعثر على أسرار | لا تطابق **Region** الخاصة ببيانات الاعتماد المكان الفعلي لأسرارك. |
| `AccessDenied` على `sts:AssumeRole` (AssumeRole فقط) | سياسة `sts:AssumeRole` المضمنة مفقودة على مستخدم IAM، أو لا تسمح سياسة الثقة الخاصة بالدور بهذا الكيان، أو لا يتطابق External ID. |
| ينجح الاختبار فوراً بعد إنشاء مستخدم IAM، لكنه يفشل في المرة التالية | تستغرق بيانات اعتماد IAM أحياناً دقيقة أو دقيقتين للانتشار عالمياً. أعد المحاولة. |
## الخطوات التالية
الآن وقد اتصلت AWS، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
- الإشارة إلى أسرار AWS الخاصة بك من متغيرات بيئة CrewAI Platform.
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) — نفس مخزن الأسرار، بدون بيانات اعتماد ثابتة، وتُجلب الأسرار في كل إطلاق.
description: تكوين Azure Key Vault عبر Microsoft Entra Workload Identity Federation للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
sidebarTitle: بـ Workload Identity
icon: "id-badge"
---
## نظرة عامة
يُكوِّن هذا الدليل Azure Key Vault كمزود أسرار باستخدام **Microsoft Entra Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على رمز وصول Entra عبر منصة هوية Microsoft، وتقرأ أسرارك — دون تخزين أي سر عميل في أي مكان.
<Note>
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة، راجع الدليل الأبسط [Azure Key Vault — سر العميل](/ar/enterprise/features/secrets-manager/azure).
</Note>
### كيف يعمل وقت التشغيل
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
2. يُقدّم العامل الـ JWT إلى Microsoft Entra على `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token` كـ `client_assertion` (`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`)، مع الإشارة إلى App Registration الذي يطابق **Federated Identity Credential** الخاص به مُصدر الـ JWT وموضوعه.
3. تتحقق Entra من الـ JWT مقابل وثيقة اكتشاف OIDC و JWKS لمنصتك، ثم تُعيد رمز وصول قصير الأمد محصور بـ `https://vault.azure.net/.default`.
4. يستدعي العامل Azure Key Vault لقراءة السر.
5. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
- اشتراك Azure ومستأجر Microsoft Entra يمكنك إدارته.
- Key Vault يستخدم **Azure RBAC** للترخيص (وليس النموذج القديم لسياسة الوصول).
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من Microsoft Entra عبر HTTPS** ليتمكّن Entra من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت.
</Note>
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` هناك هو الرابط الذي ستُسجِّله Microsoft Entra كمُصدر اتحاد موثوق.
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
<Tip>
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
</Tip>
## الخطوة 2 — إنشاء App Registration
في [بوابة Microsoft Entra](https://entra.microsoft.com)، انتقل إلى **App registrations** وانقر على **New registration**.
- **Name:** `crewai-secrets-reader`
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
- اترك **Redirect URI** فارغاً.
انقر على **Register**. سجّل **Application (client) ID** و **Directory (tenant) ID** في لوحة نظرة عامة التطبيق — ستستخدمها في الخطوة 6.
{/* SCREENSHOT: Azure portal "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure-wi/01-register-app.png */}
## الخطوة 3 — إضافة Federated Identity Credential
يُخبر Federated Identity Credential Microsoft Entra: *ثِق برموز JWT المُصدَرة من هذا المُصدر، بهذا الموضوع، عندما تُقدَّم كتأكيد عميل لهذا App Registration.*
في App Registration، انتقل إلى **Certificates & secrets** ← **Federated credentials** ← **Add credential**.
- **Subject identifier:** `organization:<YOUR_CREWAI_ORG_UUID>` — قيمة ادّعاء `sub` في JWT بالضبط. اعثر على UUID مؤسستك في إعدادات مؤسسة CrewAI Platform. يقصر هذا الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة.
- **Name:** أي تسمية وصفية، مثلاً `crewai-org-prod`.
- **Audience:** `api://AzureADTokenExchange`. هذا هو الجمهور الثابت الذي تتطلبه Microsoft Entra للبيانات الموحَّدة، وهو ما تُعيّنه CrewAI Platform في ادّعاء `aud` في JWT.
انقر على **Add**.
<Tip>
**العزل لكل مؤسسة.** يقيّد معرّف الموضوع (`organization:<UUID>`) Federated Identity Credential لرموز مؤسسة CrewAI محددة. إذا كان من المفترض أن تتشارك مؤسسات CrewAI متعددة App Registration واحداً، أضف Federated Identity Credential لكل مؤسسة (كل منها بـ UUID المؤسسة).
</Tip>
للتفاصيل الكاملة، راجع وثائق Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust).
## الخطوة 4 — منح App Registration وصولاً إلى Key Vault
امنح App Registration دور **Key Vault Secrets User** على الخزنة المستهدفة — نفس الدور الذي تستخدمه لمسار بيانات الاعتماد الثابتة. استخدم إما على مستوى الخزنة (أبسط) أو لكل سر (أقل الامتيازات).
<Tabs>
<Tab title="على مستوى الخزنة (أبسط)">
```bash
az role assignment create \
--assignee <APPLICATION_CLIENT_ID> \
--role "Key Vault Secrets User" \
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
```
يمنح النطاق على مستوى الخزنة إذن `secrets/list` الذي يعتمد عليه **الاقتراح التلقائي لاسم السر** في نموذج متغير البيئة لـ CrewAI Platform. اختر هذه التبويبة إذا أردت أن يعمل الاقتراح التلقائي.
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure-wi/03-grant-vault-rbac.png */}
</Tab>
<Tab title="لكل سر (أقل الامتيازات)">
```bash
az role assignment create \
--assignee <APPLICATION_CLIENT_ID> \
--role "Key Vault Secrets User" \
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
```
تُعطّل الارتباطات لكل سر **الاقتراح التلقائي لاسم السر** في نموذج متغير البيئة لـ CrewAI Platform (يتطلب الاقتراح التلقائي `secrets/list`، وهو محصور بنطاق الخزنة فقط). اكتب اسم السر الكامل بدلاً من ذلك.
{/* SCREENSHOT: Per-secret IAM panel with the App Registration assigned **Key Vault Secrets User** at the secret resource scope → /images/secrets-manager/azure-wi/04-per-secret-rbac.png */}
</Tab>
<Tab title="البوابة (UI)">
لتعيين **على مستوى الخزنة**:
1. افتح Key Vault الخاص بك في بوابة Azure.
2. انقر على **Access control (IAM)** ← **Add** ← **Add role assignment**.
3. اختر الدور **Key Vault Secrets User** ← **Next**.
4. انقر على **Select members**، ابحث عن App Registration `crewai-secrets-reader`، انقر على **Select**.
5. انقر على **Review + assign**.
لتعيين **لكل سر**، استخدم نفس التدفق لكن ابدأ من **Objects** ← **Secrets** ← اختر السر ← لوحة **Access control (IAM)** الخاصة به. تُعطّل الارتباطات لكل سر الاقتراح التلقائي (راجع تبويبة لكل سر أعلاه).
</Tab>
</Tabs>
## الخطوة 5 — إنشاء سر واحد على الأقل في Key Vault
إذا لم يكن لديك سر للاختبار، أنشئ واحداً عبر Azure CLI:
```bash
az keyvault secret set \
--vault-name <VAULT_NAME> \
--name openai-api-key \
--value "sk-your-actual-key"
```
أو عبر بوابة Azure:
1. افتح Key Vault الخاص بك وانتقل إلى **Objects** ← **Secrets**.
**اصطلاحات اسم السر.** لا يمكن أن تحتوي أسماء أسرار Azure Key Vault على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`)، لذا يمكنك الاحتفاظ بأسماء متغيرات بيئة بنمط الشرطة السفلية — لكن السر الأساسي في Key Vault يجب أن يستخدم الشرطات.
</Note>
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `azure-prod`.
- **Cloud Provider:** `Azure`.
- **Tenant ID:** **Directory (tenant) ID** الخاص بـ Microsoft Entra من الخطوة 2.
- **Client ID:** **Application (client) ID** الخاص بـ App Registration من الخطوة 2.
- (اختياري) حدّد **Set as default for Azure** إذا كنت ترغب في أن يكون هذا هو تكوين WI الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ Azure.
**Audience** ثابت على `api://AzureADTokenExchange` — تتطلب Microsoft Entra هذا الجمهور بالضبط للبيانات الموحَّدة، لذا لا يظهر حقل Audience في النموذج.
انقر على **Create**.
{/* SCREENSHOT: "Add Workload Identity Config" form with Azure, tenant ID, client ID populated → /images/secrets-manager/azure-wi/05-amp-add-wi-config-azure.png */}
{/* SCREENSHOT: Workload Identity list showing AWS, GCP, and Azure rows → /images/secrets-manager/azure-wi/06-amp-wi-list-with-azure.png */}
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `azure-prod-wi`.
- **Provider:** `Azure Key Vault`.
- **Authentication Method:** `Workload Identity`.
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6.
- **Key Vault URL:** اسم مضيف DNS للخزنة، مثلاً `https://my-vault.vault.azure.net`.
- (اختياري) حدّد **Set as default credential for this provider**.
سيطلب النموذج فقط **Key Vault URL** ضمن Workload Identity — حقول بيانات الاعتماد الثابتة (Tenant ID و Client ID و Client Secret) مخفية عمداً لأنها لا تنطبق على هذا المسار؛ يأتي المستأجر والعميل من تكوين WI المرتبط.
انقر على **Create**.
<Tip>
**App Registration واحد، خزائن متعددة.** يعيش Key Vault URL على بيانات الاعتماد، وليس على تكوين WI. لذا يمكن لـ App Registration واحد (وتكوين WI واحد) خدمة عدة Key Vaults — فقط أنشئ بيانات اعتماد مزود أسرار واحدة لكل خزنة، جميعها مرتبطة بنفس تكوين WI.
</Tip>
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure + Workload Identity + WI config dropdown + vault URL → /images/secrets-manager/azure-wi/07-amp-add-credential-azure-wi.png */}
## الخطوة 8 — اختبار الاتصال
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT، وتُقدّمه إلى Microsoft Entra كـ `client_assertion` موحَّد، وتؤكد أن Entra تُعيد رمز وصول محصور بالخزنة. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
نجاح Test Connection يُثبت أن مُصدر Federated Identity Credential وموضوعه وجمهوره كلها متطابقة، وأن App Registration قابل للوصول. لا يُثبت ذلك أن RBAC لكل سر في Key Vault صحيح — يُمارَس `getSecret` على سر محدد بشكل منفصل عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
## الخطوة 10 — التحقق من التدوير
بعد تشغيل عملية النشر، دوّر السر في Key Vault:
```bash
az keyvault secret set \
--vault-name <VAULT_NAME> \
--name openai-api-key \
--value "rotated value"
```
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
للتأكد في سجلات العامل، ابحث عن:
```
Workload identity config '<id>' (azure): N secret(s) resolved
```
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `getSecret` طازج مقابل Azure Key Vault.
للتحقق من البداية إلى النهاية باستخدام البصمة، راجع [التحقق من التدوير من البداية إلى النهاية](/ar/enterprise/features/secrets-manager/verify-rotation).
## استكشاف الأخطاء
| العَرَض | السبب المحتمل |
|---|---|
| يفشل Test Connection بخطأ مصافحة | رفضت Microsoft Entra `client_assertion` الموحَّد. تحقق من أن **Issuer** في Federated Identity Credential يطابق قيمة `issuer` للمنصة بالضبط، وأن **Subject** هو `organization:<your-org-uuid>` (يطابق ادّعاء `sub` في JWT)، وأن **Audience** هو `api://AzureADTokenExchange`، وأن رابط اكتشاف OIDC للمنصة قابل للوصول من Entra عبر الإنترنت العام. |
| `AADSTS70021: No matching federated identity record found for presented assertion` | لا يتطابق **Issuer** + **Subject** + **Audience** في Federated Identity Credential مع الـ JWT بالضبط. تحقق من الخطوة 3 من جديد: يجب أن يكون الموضوع `organization:<your-org-uuid>` (يطابق ادّعاء `sub` في JWT)، ويجب أن يكون الجمهور `api://AzureADTokenExchange`. |
| `AADSTS700024: Client assertion is not within its valid time range` | ساعة مضيف CrewAI Platform منحرفة بشكل كبير عن الوقت الحقيقي. تحقق من NTP على المضيف. |
| `AADSTS50013: Assertion failed signature validation` | لم تستطع Microsoft Entra التحقق من توقيع الـ JWT. تأكد من أن `https://<your-platform-host>/oauth2/jwks` قابل للوصول من الإنترنت العام ويُقدّم JWKS صالحاً. |
| يُظهر الاقتراح التلقائي لاسم السر `Forbidden — does not have permission to perform action 'Microsoft.KeyVault/vaults/secrets/.../list'` | دور **Key Vault Secrets User** الخاص بـ App Registration محصور بسر واحد. امنح الدور على نطاق الخزنة ليُسمح بإجراء `list` في مستوى البيانات. راجع الخطوة 4. |
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن RBAC لكل سر في Key Vault مفقود على السر الفاشل. راجع **Key Vault Secrets User** على ذلك السر تحديداً (أو وسّع تعيين الدور إلى نطاق الخزنة). |
| `Forbidden — request was not authorized` (الخزنة تستخدم سياسات الوصول القديمة) | لم يتم تحويل الخزنة إلى Azure RBAC. ضمن **Access configuration** للخزنة، عيّن نموذج الإذن إلى **Azure role-based access control** وأعد منح الدور من الخطوة 4. |
| `azure_vault_url is required for Azure secret resolution` (سجلات العامل) | تفتقد بيانات اعتماد مزود الأسرار إلى **Key Vault URL**. تحقق من الخطوة 7 من جديد. |
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
- Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust)
- [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
- للتنوع متعدد السحاب، إعداد ما يعادله لـ AWS موجود في [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) وما يعادله لـ GCP في [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity).
## مرجع لقطات الشاشة
تُربط العناصر النائبة أعلاه بـ:
- `01-register-app.png` — نموذج "Register an application" في بوابة Azure مع `crewai-secrets-reader`.
- `03-grant-vault-rbac.png` — Key Vault ← Access control (IAM) ← Add role assignment، مع **Key Vault Secrets User** و App Registration المختار.
- `04-per-secret-rbac.png` — نفس النموذج لكن في نطاق IAM سر واحد (مسار أقل الامتيازات البديل).
- `05-amp-add-wi-config-azure.png` — نموذج "Add Workload Identity Config" في CrewAI Platform مع Cloud Provider = Azure و Tenant ID و Client ID مأهولين.
- `06-amp-wi-list-with-azure.png` — صفحة قائمة Workload Identity بعد الإنشاء، تُظهر صفوفاً لـ AWS و GCP وتكوين Azure الجديد.
- `07-amp-add-credential-azure-wi.png` — نموذج "Add Secret Provider Credential" مع Provider = Azure Key Vault، Auth = Workload Identity، تكوين WI المختار، و Key Vault URL مأهول.
description: تكوين Azure Key Vault كمزود أسرار لـ CrewAI Platform من البداية إلى النهاية
sidebarTitle: ببيانات اعتماد ثابتة
icon: "key"
---
## نظرة عامة
يأخذك هذا الدليل عبر تكوين Azure Key Vault كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **App Registration في Microsoft Entra مع سر عميل**. بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في Azure Key Vault الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
<Note>
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة، راجع [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity).
</Note>
<Note>
يغطي هذا الدليل التكوين من جانب Azure وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
</Note>
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- اشتراك Azure لديه إذن إنشاء App Registrations في Microsoft Entra ومنح تعيينات أدوار على موارد Key Vault.
- Key Vault يستخدم **Azure RBAC** للترخيص (وليس النموذج القديم لسياسة الوصول). إذا كان الخزنة لا تزال تستخدم سياسات الوصول، فحوّلها إلى RBAC ضمن لوحة **Access configuration** للخزنة.
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
</Note>
## الخطوة 1 — إنشاء App Registration
App Registration هي الهوية من جانب Microsoft Entra التي ستُصادق بها CrewAI Platform.
في [بوابة Microsoft Entra](https://entra.microsoft.com)، انتقل إلى **App registrations** وانقر على **New registration**.
- **Name:** `crewai-secrets-reader`
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
- اترك **Redirect URI** فارغاً.
انقر على **Register**. سجّل **Application (client) ID** و **Directory (tenant) ID** في لوحة نظرة عامة التطبيق — ستلصق كليهما في CrewAI Platform في الخطوة 4.
للتفاصيل الكاملة، راجع وثائق Microsoft: [Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).
{/* SCREENSHOT: Azure "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure/01-register-app.png */}
## الخطوة 2 — إنشاء سر عميل
في App Registration، انتقل إلى **Certificates & secrets** ← **Client secrets** ← **New client secret**.
- **Description:** `crewai-platform`
- **Expires:** اختر مدة تتطابق مع سياسة التدوير لديك (تحدّد Microsoft هذا بـ 24 شهراً كحد أقصى).
انقر على **Add**. انسخ عمود **Value** فوراً — لا يمكن إعادة عرضه أبداً بمجرد مغادرة الصفحة.
<Warning>
أسرار العميل هي بيانات اعتماد ثابتة طويلة الأمد. خزّن القيمة بأمان (في مدير كلمات مرور أو مخزن أسرارك الخاص) ودوّرها قبل انتهاء الصلاحية. للقضاء على بيانات الاعتماد الثابتة تماماً، استخدم [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity) بدلاً من ذلك.
</Warning>
{/* SCREENSHOT: "Client secrets" tab with the new secret row and the "Value" column highlighted → /images/secrets-manager/azure/02-create-client-secret.png */}
## الخطوة 3 — منح App Registration وصولاً إلى Key Vault
تحتاج CrewAI Platform إلى وصول قراءة للأسرار في Key Vault الخاص بك. استخدم أحد نطاقين — **على مستوى الخزنة** للبساطة، أو **لكل سر** لأقل الامتيازات.
<Tabs>
<Tab title="على مستوى الخزنة (أبسط)">
في [وحدة تحكم Key Vault](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.KeyVault%2Fvaults)، افتح الخزنة الهدف، ثم انتقل إلى **Access control (IAM)** ← **Add** ← **Add role assignment**.
- **Role:** **Key Vault Secrets User**
- **Assign access to:** User, group, or service principal
- **Members:** ابحث عن App Registration الخاص بك (`crewai-secrets-reader`) واختره.
انقر على **Review + assign**.
أو عبر Azure CLI:
```bash
az role assignment create \
--assignee <APPLICATION_CLIENT_ID> \
--role "Key Vault Secrets User" \
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
```
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure/03-grant-vault-rbac.png */}
</Tab>
<Tab title="لكل سر (أقل الامتيازات)">
امنح الدور على مستوى سر فردي. كرّر لكل سر ينبغي أن تصل إليه CrewAI Platform:
```bash
az role assignment create \
--assignee <APPLICATION_CLIENT_ID> \
--role "Key Vault Secrets User" \
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
```
{/* SCREENSHOT: Per-secret "Access control (IAM)" panel showing role assignment scoped to one secret → /images/secrets-manager/azure/04-per-secret-rbac.png */}
</Tab>
</Tabs>
<Tip>
يسمح دور **Key Vault Secrets User** بقراءة قيم الأسرار لكن ليس سرد جميع الأسرار في الخزنة. يستدعي الاقتراح التلقائي لاسم السر في CrewAI Platform أيضاً `list` — هذا الإذن مُضمَّن في الدور على نطاق الخزنة، لكن **ليس** على نطاق لكل سر. مع ارتباطات لكل سر، لن يقترح الإكمال التلقائي أسراراً؛ اكتب اسم السر الكامل بدلاً من ذلك.
</Tip>
## الخطوة 4 — إضافة بيانات الاعتماد في CrewAI Platform
في CrewAI Platform، انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
- **Key Vault URL:** اسم مضيف DNS للخزنة، مثلاً `https://my-vault.vault.azure.net`.
- **Tenant ID:** **Directory (tenant) ID** الخاص بـ Microsoft Entra من الخطوة 1.
- **Client ID:** **Application (client) ID** الخاص بـ App Registration من الخطوة 1.
- **Client Secret:** **Value** الذي نسخته في الخطوة 2.
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار Azure بدون تحديد بيانات اعتماد صراحةً.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure fields filled in → /images/secrets-manager/azure/05-amp-add-credential-form-azure.png */}
## الخطوة 5 — إنشاء سر واحد على الأقل في Azure Key Vault
إذا لم يكن لديك بالفعل أسرار في Key Vault، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 6.
في وحدة تحكم Key Vault، انتقل إلى **Objects** ← **Secrets** ← **Generate/Import**.
- **Upload options:** `Manual`
- **Name:** مثلاً `openai-api-key`
- **Secret value:** الصق قيمة سرّك
- اترك الباقي على القيم الافتراضية.
انقر على **Create**.
أو عبر Azure CLI:
```bash
az keyvault secret set \
--vault-name <VAULT_NAME> \
--name openai-api-key \
--value "sk-your-actual-key"
```
<Note>
**اصطلاحات اسم السر.** لا يمكن أن تحتوي أسماء أسرار Azure Key Vault على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`)، لذا يمكنك الاحتفاظ بأسماء متغيرات بيئة بنمط الشرطة السفلية — لكن السر الأساسي في Key Vault يجب أن يستخدم الشرطات.
</Note>
<Note>
**صيغة الإشارة بمفتاح JSON.** يتعامل Key Vault مع قيم الأسرار كسلاسل معتمة. إذا حدث أن كانت قيمة سرّك كائن JSON، يمكن لـ CrewAI Platform استخراج حقل واحد باستخدام صيغة `secret-name#json_key` (مثلاً `database-credentials#password`). راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
</Note>
للتفاصيل الكاملة، راجع وثائق Microsoft: [Set and retrieve a secret](https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli).
{/* SCREENSHOT: Azure Key Vault "Create a secret" form with name and value → /images/secrets-manager/azure/06-create-secret.png */}
## الخطوة 6 — اختبار الاتصال
عُد إلى CrewAI Platform، في صفحة **Secret Provider Credentials**، اعثر على بيانات الاعتماد التي أنشأتها للتو وانقر على **Test Connection**.
تؤكد رسالة نجاح أن CrewAI Platform يمكنها المصادقة مع Microsoft Entra وقراءة الأسرار من خزنتك.
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the Azure credential → /images/secrets-manager/azure/07-test-connection-success.png */}
إذا فشل الاختبار، تحقق من الأسباب الأكثر شيوعاً:
| العَرَض | السبب المحتمل |
|---|---|
| `AADSTS7000215: Invalid client secret provided` | **Client Secret** الملصوق خاطئ أو منتهي الصلاحية. أعد إنشاء السر (الخطوة 2) وحدّث بيانات الاعتماد. |
| `AADSTS700016: Application not found in the directory` | لا يطابق **Tenant ID** أو **Client ID** الـ App Registration. تحقق من الخطوة 4 من جديد. |
| `Forbidden — caller does not have permission` | يفتقد App Registration إلى دور **Key Vault Secrets User** على الخزنة (أو لكل سر). تحقق من الخطوة 3 من جديد. |
| `Vault not found` / أخطاء DNS | **Key Vault URL** خاطئ، أو أن خزنتك لديها نقاط نهاية خاصة تمنع الوصول العام. تأكد من أن المضيف يستجيب لـ `curl https://<vault-name>.vault.azure.net/secrets?api-version=7.4`. |
| `Forbidden — request was not authorized` (الخزنة تستخدم سياسات الوصول القديمة) | لم يتم تحويل الخزنة إلى Azure RBAC. ضمن **Access configuration** للخزنة، عيّن نموذج الإذن إلى **Azure role-based access control** وأعد منح الدور من الخطوة 3. |
## الخطوات التالية
الآن وقد اتصل Azure Key Vault، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
- الإشارة إلى أسرار Azure الخاصة بك من متغيرات بيئة CrewAI Platform.
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity) — نفس الخزنة، بدون سر عميل للتدوير، وتُجلب الأسرار في كل إطلاق.
## مرجع لقطات الشاشة
تُربط العناصر النائبة أعلاه بـ:
- `01-register-app.png` — نموذج "Register an application" في بوابة Azure مع `crewai-secrets-reader`.
- `02-create-client-secret.png` — App Registration ← Certificates & secrets ← Client secrets، مع صف السر المُنشأ حديثاً (عمود Value مُميَّز قبل تمويهه).
- `03-grant-vault-rbac.png` — Key Vault ← Access control (IAM) ← Add role assignment، مع اختيار **Key Vault Secrets User** و App Registration كعضو.
- `04-per-secret-rbac.png` — نفس اللوحة لكن بنطاق سر واحد (مسار أقل الامتيازات البديل).
- `05-amp-add-credential-form-azure.png` — نموذج "Add Secret Provider Credential" في CrewAI Platform: Provider = Azure Key Vault، جميع الحقول الخمسة مأهولة.
- `06-create-secret.png` — لوحة "Create a secret" في Azure Key Vault مع `openai-api-key` وقيمة ملصوقة.
- `07-test-connection-success.png` — رسالة نجاح / حالة صف في CrewAI Platform بعد النقر على **Test Connection** على بيانات الاعتماد.
description: تكوين Google Cloud Secret Manager عبر Workload Identity Federation للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
sidebarTitle: بـ Workload Identity
icon: "id-badge"
---
## نظرة عامة
يُكوِّن هذا الدليل Google Cloud Secret Manager كمزود أسرار باستخدام **Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على بيانات اعتماد Google Cloud عبر خدمة Security Token Service، وتقرأ أسرارك — دون تخزين أي مفتاح حساب خدمة طويل الأمد في أي مكان.
<Note>
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة، راجع الدليل الأبسط [GCP — مفتاح حساب الخدمة](/ar/enterprise/features/secrets-manager/gcp).
</Note>
### كيف يعمل وقت التشغيل
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
2. يبادل العامل الـ JWT للحصول على بيانات اعتماد Google موحَّدة عبر [Security Token Service](https://cloud.google.com/iam/docs/reference/sts/rest)، مع الإشارة إلى Workload Identity Pool Provider الذي ستُعدّه أدناه.
3. يستدعي العامل `secretmanager.googleapis.com:accessSecretVersion` لقراءة السر، باستخدام بيانات الاعتماد الموحَّدة مباشرةً (يمتلك الكيان الموحَّد `roles/secretmanager.secretAccessor` — راجع الخطوة 4).
4. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
- مشروع Google Cloud مع تفعيل **Secret Manager API** و **Security Token Service API** و **IAM Credentials API**. فعّلها عبر الوحدة أو:
- إذن في المشروع لإنشاء Workload Identity Pools وأدوار IAM وحسابات الخدمة و(إن لزم) الأسرار.
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من Google Cloud عبر HTTPS** ليتمكّن GCP STS من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت.
</Note>
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` هناك هو الرابط الذي ستُسجِّله Google كمزود OIDC موثوق.
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
<Tip>
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
</Tip>
## الخطوة 2 — إنشاء Workload Identity Pool
Workload Identity Pool هو حاوية من جانب Google Cloud للهويات الخارجية الموثوقة. ستُسجِّل CrewAI Platform كمزود داخل هذه الحوض.
```bash
gcloud iam workload-identity-pools create crewai-pool \
--project=<YOUR_PROJECT_ID> \
--location=global \
--display-name="CrewAI Platform"
```
أو في [وحدة تحكم Workload Identity Pools](https://console.cloud.google.com/iam-admin/workload-identity-pools)، انقر على **Create Pool**.
{/* SCREENSHOT: GCP "Create Workload Identity Pool" form with name "crewai-pool" → /images/secrets-manager/gcp-wi/01-create-pool.png */}
## الخطوة 3 — إضافة CrewAI Platform كمزود OIDC في الحوض
```bash
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
هذه هي قيمة **Workload Identity Provider** الخاصة بك في CrewAI Platform في الخطوة 6. تحسب CrewAI Platform تلقائياً جمهور OIDC كـ `//iam.googleapis.com/<this-resource-name>` عند إصدار الرموز.
{/* SCREENSHOT: "Add provider to pool" form with OIDC selected, issuer URI, audience defaults, attribute mapping → /images/secrets-manager/gcp-wi/02-add-oidc-provider.png */}
## الخطوة 4 — منح الوصول إلى Secret Manager للكيان الموحَّد
اربط دوري Secret Manager كليهما على نطاق المشروع بالكيان الموحَّد — دور يُفعّل الاقتراح التلقائي لاسم السر في نموذج متغير البيئة، والآخر يسمح بقراءة قيم الأسرار عند إطلاق الأتمتة. كلاهما مطلوبان لتعمل الميزة من البداية إلى النهاية.
استبدل `<PROJECT_NUMBER>` برقم المشروع الرقمي (`gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)'`) و `<YOUR_CREWAI_ORG_UUID>` بـ UUID مؤسسة CrewAI Platform التي يجب أن يُسمح لها بقراءة أسرارك. يمكنك العثور على UUID المؤسسة في واجهة المنصة في صفحة إعدادات المؤسسة، أو عبر الـ API. يقصر هذا الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة.
أو عبر وحدة تحكم Google Cloud:
1. افتح **IAM & Admin** ← **IAM** لمشروعك.
2. انقر على **GRANT ACCESS**.
3. **New principals:** الصق سلسلة `principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID>` الكاملة.
4. عيّن الدور **Secret Manager Viewer** (`roles/secretmanager.viewer`).
5. انقر على **SAVE**.
6. انقر على **GRANT ACCESS** مرة أخرى وكرّر مع الدور **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
<Tip>
**العزل لكل مؤسسة.** يقيّد النمط `principalSet://...attribute.organization/<UUID>` الوصول إلى رموز مؤسسة محددة. إذا كانت لديك مؤسسات CrewAI متعددة تتشارك مشروع Google Cloud واحد، كرّر كلا الارتباطين لكل مؤسسة بالـ UUID الصحيح — أو استخدم شرط سمة أقل تقييداً إن لم يكن العزل ضرورياً.
</Tip>
<Tip>
**تحديد نطاق `secretAccessor` لكل سر (اختياري).** إذا كنت تفضّل عدم منح `roles/secretmanager.secretAccessor` على نطاق المشروع، احذف الارتباط الثاني أعلاه واربط لكل سر بدلاً من ذلك:
أبقِ `roles/secretmanager.viewer` على نطاق المشروع في كلا الحالتين — `secretmanager.secrets.list` (الذي يعتمد عليه الاقتراح التلقائي) لا يمكن منحه لكل سر.
</Tip>
## الخطوة 5 — إنشاء سر واحد على الأقل في GCP
إذا لم يكن لديك سر للاختبار، أنشئ واحداً عبر CLI `gcloud`:
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `gcp-prod`.
- **Cloud Provider:** `GCP`.
- **Workload Identity Provider:** اسم مورد المزود من الخطوة 3، مثلاً `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider`.
- (اختياري) بدّل **Default Configuration** إذا كنت ترغب في أن يكون هذا هو تكوين WI الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ GCP.
انقر على **Create**.
{/* SCREENSHOT: "Add Workload Identity Config" form with GCP and provider resource name → /images/secrets-manager/gcp-wi/03-amp-add-wi-config-gcp.png */}
{/* SCREENSHOT: Workload Identity list showing both AWS and GCP rows → /images/secrets-manager/gcp-wi/04-amp-wi-list-with-gcp.png */}
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
املأ النموذج:
- **Name:** اسم وصفي، مثلاً `gcp-prod-wi`.
- **Provider:** `Google Cloud Secret Manager`.
- **Authentication Method:** `Workload Identity`.
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6.
- **Project ID:** معرّف مشروع GCP الخاص بك (نفس المشروع الذي يملك الأسرار).
- (اختياري) حدّد **Set as default credential for this provider**.
سيطلب النموذج فقط **Project ID** ضمن Workload Identity — حقل **Service Account JSON** مخفي عمداً لأنه لا ينطبق على هذا المسار؛ تأتي الهوية الموحَّدة من تكوين WI المرتبط.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP + Workload Identity + WI config dropdown → /images/secrets-manager/gcp-wi/05-amp-add-credential-gcp-wi.png */}
## الخطوة 8 — اختبار الاتصال
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT وتبادله عبر Security Token Service للحصول على رمز وصول Google موحَّد. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
نجاح Test Connection يُثبت أن Workload Identity Pool ومزود OIDC وربط السمات وشرط السمة موصولة جميعها بشكل صحيح. لا يُثبت ذلك أن IAM في Secret Manager صحيح — يُمارَس `secretmanager.secrets.list` و `secretmanager.versions.access` بشكل منفصل عند تحميل الاقتراح التلقائي لاسم السر أو عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
## الخطوة 10 — التحقق من التدوير
بعد تشغيل عملية النشر، دوّر السر في GCP بإضافة إصدار جديد (يقرأ Secret Manager دائماً أحدث إصدار مفعَّل افتراضياً):
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
للتأكد في سجلات العامل، ابحث عن:
```
Workload identity config '<id>' (gcp): N secret(s) resolved
```
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `accessSecretVersion` طازج مقابل GCP.
## استكشاف الأخطاء
| العَرَض | السبب المحتمل |
|---|---|
| يفشل Test Connection بخطأ مصافحة | رُفض تبادل رمز STS. تحقق من وجود Workload Identity Pool، وأن مُصدر مزود OIDC يطابق قيمة `issuer` للمنصة، وأن شرط السمة يقبل ادّعاءات JWT. تأكد من أن رابط اكتشاف OIDC للمنصة قابل للوصول من GCP عبر الإنترنت العام. |
| `Could not refresh access token: invalid_target` | لا يطابق ادّعاء الجمهور الجمهور المتوقع لمزود Workload Identity. تُعيّن CrewAI Platform الجمهور تلقائياً؛ إذا خصّصته، فتأكد من أنه يطابق `//iam.googleapis.com/<provider-resource-name>`. |
| `Failed to fetch JWKS from issuer` | لا يمكن لـ GCP STS الوصول إلى مضيف CrewAI Platform. تأكد من أن المضيف متاح عبر الإنترنت وأن `/.well-known/openid-configuration` يُعيد 200. |
| `Attribute condition rejected token` | يتطلب شرط السمة لمزود OIDC (الخطوة 3) `organization_id`. تُعيّن CrewAI Platform هذا الادّعاء دائماً، لذا يعني هذا عادةً تكوين حوض/مزود خاطئاً. تحقق من شرط السمة للمزود من جديد. |
| يُظهر الاقتراح التلقائي لاسم السر `PERMISSION_DENIED: secretmanager.secrets.list` | يفتقد الكيان الموحَّد إلى `roles/secretmanager.viewer` على نطاق المشروع. إذن `secretmanager.secrets.list` محصور بنطاق المشروع فقط ولا يمكن منحه لكل سر. راجع الخطوة 4. |
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن `secretmanager.versions.access` مفقود على السر الفاشل. راجع `roles/secretmanager.secretAccessor` (على نطاق المشروع، أو لكل سر إذا حدّدت النطاق بهذه الطريقة في الخطوة 4). |
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
description: تكوين Google Cloud Secret Manager كمزود أسرار لـ CrewAI Platform من البداية إلى النهاية
sidebarTitle: ببيانات اعتماد ثابتة
icon: "key"
---
## نظرة عامة
يأخذك هذا الدليل عبر تكوين Google Cloud Secret Manager كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **بيانات اعتماد حساب خدمة**. بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في مشروع Google Cloud الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
<Note>
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة، راجع [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity).
</Note>
<Note>
يغطي هذا الدليل التكوين من جانب GCP وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
</Note>
## المتطلبات المسبقة
<Note>
قبل البدء، تأكد من امتلاكك:
- مشروع Google Cloud مع تفعيل **Secret Manager API**. فعّله في [وحدة تحكم APIs & Services](https://console.cloud.google.com/apis/library/secretmanager.googleapis.com) أو عبر `gcloud`:
- إذن في المشروع لإنشاء حسابات خدمة ومنح أدوار IAM و(إن لزم) إنشاء الأسرار.
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
</Note>
## الخطوة 1 — إنشاء حساب خدمة
حساب الخدمة هو الهوية من جانب GCP التي ستُصادق بها CrewAI Platform.
في [وحدة تحكم IAM & Admin ← Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)، انقر على **Create Service Account**.
{/* SCREENSHOT: GCP IAM "Grant access" panel with the service account and Secret Manager Secret Accessor role → /images/secrets-manager/gcp/02-iam-grant-access.png */}
</Tab>
<Tab title="لكل سر (أقل الامتيازات)">
امنح الدور فقط على الأسرار المحددة التي ينبغي أن تصل إليها CrewAI Platform. كرّر لكل سر:
أو في الوحدة: افتح كل سر في [Secret Manager](https://console.cloud.google.com/security/secret-manager)، انقر على **Permissions** في اللوحة اليمنى، وامنح **Secret Manager Secret Accessor** لحساب الخدمة.
{/* SCREENSHOT: Per-secret "Permissions" panel in Secret Manager with the service account granted accessor role → /images/secrets-manager/gcp/03-per-secret-permissions.png */}
</Tab>
</Tabs>
<Tip>
يمنح دور `roles/secretmanager.secretAccessor` وصول قراءة فقط لقيم الأسرار. تستدعي CrewAI Platform أيضاً `secretmanager.secrets.list` لتجربة الاقتراح التلقائي في نموذج متغير البيئة — هذا الإذن مُضمَّن في الدور على نطاق المشروع، لكن **ليس** على نطاق لكل سر. مع ارتباطات لكل سر، لن يقترح الإكمال التلقائي أسراراً؛ ستحتاج إلى كتابة اسم السر الكامل.
</Tip>
## الخطوة 3 — إنشاء مفتاح حساب الخدمة
افتح حساب الخدمة من الخطوة 1 في [وحدة تحكم IAM & Admin ← Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts).
- انقر على علامة التبويب **Keys**.
- انقر على **Add Key** ← **Create new key**.
- **Key type:** JSON.
- انقر على **Create**. يُنزّل المتصفح ملف JSON — احتفظ به بأمان؛ لا يمكن إعادة تنزيله.
أو عبر `gcloud`:
```bash
gcloud iam service-accounts keys create ./crewai-secrets-reader.json \
مفتاح حساب الخدمة هو بيانات اعتماد ثابتة طويلة الأمد. خزّنه بأمان (في مدير كلمات مرور أو مخزن أسرارك الخاص) ودوّره بشكل منتظم. للقضاء على بيانات الاعتماد الثابتة تماماً، استخدم [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) بدلاً من ذلك.
</Warning>
{/* SCREENSHOT: Service account "Keys" tab with the "Create new key" → JSON option → /images/secrets-manager/gcp/04-create-service-account-key.png */}
## الخطوة 4 — إضافة بيانات الاعتماد في CrewAI Platform
في CrewAI Platform، انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
- **Project ID:** معرّف مشروع GCP الخاص بك (مثلاً `my-crewai-prod`).
- **Service Account JSON:** الصق المحتوى الكامل لملف JSON الذي نزّلته في الخطوة 3.
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار GCP بدون تحديد بيانات اعتماد صراحةً.
انقر على **Create**.
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP fields filled in → /images/secrets-manager/gcp/05-amp-add-credential-form-gcp.png */}
## الخطوة 5 — إنشاء سر واحد على الأقل في GCP
إذا لم يكن لديك بالفعل أسرار في GCP Secret Manager، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 6.
في [وحدة تحكم Secret Manager](https://console.cloud.google.com/security/secret-manager)، انقر على **Create secret**.
- **Name:** اسم فريد، مثلاً `openai-api-key`.
- **Secret value:** إما لصق قيمة خام أو رفع ملف.
- اترك إعدادات التدوير والتكرار وغيرها على القيم الافتراضية ما لم تكن لديك متطلبات محددة.
**صيغة الإشارة بمفتاح JSON.** يتعامل GCP Secret Manager مع قيم الأسرار كبيانات معتمة. إذا حدث أن كانت قيمة سرّك سلسلة JSON، يمكن لـ CrewAI Platform استخراج حقل واحد باستخدام صيغة `secret-name#json_key` (مثلاً `database-credentials#password`). راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
</Note>
للتفاصيل الكاملة، راجع وثائق GCP: [Create a secret](https://cloud.google.com/secret-manager/docs/create-secret-quickstart).
{/* SCREENSHOT: GCP "Create secret" form with name and value → /images/secrets-manager/gcp/06-create-secret.png */}
## الخطوة 6 — اختبار الاتصال
عُد إلى CrewAI Platform، في صفحة **Secret Provider Credentials**، اعثر على بيانات الاعتماد التي أنشأتها للتو وانقر على **Test Connection**.
تؤكد رسالة نجاح أن CrewAI Platform يمكنها المصادقة مع GCP وقراءة الأسرار من مشروعك.
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the GCP credential → /images/secrets-manager/gcp/07-test-connection-success.png */}
إذا فشل الاختبار، تحقق من الأسباب الأكثر شيوعاً:
| العَرَض | السبب المحتمل |
|---|---|
| `PERMISSION_DENIED` عند سرد الأسرار | يفتقد حساب الخدمة إلى `roles/secretmanager.secretAccessor`، أو حدّدت نطاقه لكل سر (لا يُمنح `list`). تحقق من الخطوة 2 من جديد. |
| `PERMISSION_DENIED` على `secretmanager.secrets.access` | نفس ما سبق، لكن لسر محدد. تأكد من أن حساب الخدمة يمتلك دور accessor على السر المعني. |
| `unauthorized_client` / `invalid_grant` | ملف Service Account JSON الملصوق غير صالح أو منتهي الصلاحية أو لحساب خدمة محذوف. أعد إنشاء المفتاح (الخطوة 3) والصقه من جديد. |
| `Project ID does not match` | لا يطابق حقل Project ID في CrewAI Platform المشروع الذي يملك حساب الخدمة / الأسرار. تحقق من الخطوة 4 من جديد. |
| `API not enabled` | Secret Manager API غير مفعَّل في المشروع. راجع المتطلبات المسبقة. |
## الخطوات التالية
الآن وقد اتصل GCP، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
- الإشارة إلى أسرار GCP الخاصة بك من متغيرات بيئة CrewAI Platform.
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) — نفس مخزن الأسرار، بدون بيانات اعتماد ثابتة، وتُجلب الأسرار في كل إطلاق.
description: ربط مخازن الأسرار الخارجية بمنصة CrewAI Platform والإشارة إلى الأسرار المُدارة من متغيرات البيئة
sidebarTitle: نظرة عامة
icon: "book-open"
---
## نظرة عامة
تُتيح ميزة مدير الأسرار لمؤسستك ربط مخزن أسرار خارجي — AWS Secrets Manager أو Google Cloud Secret Manager أو Azure Key Vault — والإشارة إلى تلك الأسرار مباشرةً من متغيرات البيئة على الأتمتات والطواقم لديك. بدلاً من لصق قيم نصية صريحة في المنصة، فإنك تخزّن مجموعة واحدة من بيانات الاعتماد لكل مزود وتُشير إلى الأسرار بالاسم.
يمنحك هذا:
- **تخزين مركزي** — إدارة الأسرار في مزوّدك بدلاً من تعديل إعدادات CrewAI Platform. لا تحتفظ CrewAI Platform بأي نسخة نصية صريحة من قيمة السر.
- **تقليل التعرّض** — لا تظهر القيم الحساسة أبداً كنص صريح في إعدادات CrewAI Platform.
- **قابلية تدقيق سحابية المنشأ** — يسجّل سجل التدقيق الخاص بمزوّدك كل قراءة لسر.
<Note>
يتطلب مدير الأسرار (مساران: بيانات الاعتماد الثابتة و Workload Identity) إصدار CrewAI runtime رقم `1.14.5` أو أحدث في صورة حاوية الأتمتة.
</Note>
## مساران: بيانات اعتماد ثابتة مقابل Workload Identity
هناك طريقتان لربط CrewAI Platform بمخزن أسرار السحابة لديك. **يختلفان اختلافاً كبيراً في سلوك التدوير**، لذا اختر بناءً على مدى تكرار تدوير أسرارك ومدى صرامة وضعك الأمني.
| الجانب | بيانات الاعتماد الثابتة | Workload Identity (اتحاد OIDC) |
|---|---|---|
| **المصادقة** | مفاتيح وصول / ملف JSON لحساب خدمة طويلة الأمد مخزّنة في CrewAI Platform | رموز قصيرة الأمد تُصدر لكل عملية عامل؛ لا تُخزَّن بيانات اعتماد ثابتة في أي مكان |
| **انتشار التدوير** | تُحَلّ وقت النشر و**تُدمج في صورة حاوية النشر** — تتطلب القيم المُدوَّرة إعادة نشر | تُحَلّ **وقت تنفيذ الأتمتة** — تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر |
| **جهد الإعداد** | أقل — لصق المفاتيح / رفع ملف JSON لحساب الخدمة | أعلى — تسجيل CrewAI Platform كمزود OIDC في سحابتك وتكوين سياسات الثقة |
| **الأنسب لـ** | البداية، الأسرار قليلة التدوير، عمليات نشر بحساب واحد | الإنتاج، الأسرار كثيرة التدوير، البيئات التي تحكمها الامتثال وتمنع بيانات الاعتماد طويلة الأمد |
<Note>
**يستخدم كلا المسارين نفس تدفق الواجهة** للإشارة إلى الأسرار في متغيرات البيئة (راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage)). الفرق بالكامل في كيفية مصادقة المنصة لسحابتك ومتى تقرأ قيمة السر.
</Note>
### اختر دليل الإعداد الخاص بك
| المزود | بيانات الاعتماد الثابتة | Workload Identity |
واجهتا مدير الأسرار و Workload Identity مُوسومتان حالياً بـ **Beta** في CrewAI Platform.
</Note>
## كيف تتلاءم الأجزاء معاً
إعداد مدير الأسرار هو تدفق من ثلاث خطوات يشمل كلاً من مزود السحابة و CrewAI Platform:
1. **يُكوِّن المسؤول بيانات اعتماد المزود.** هذا هو العمل من جانب السحابة — ويختلف العمل اعتماداً على المسار (بيانات الاعتماد الثابتة أو Workload Identity) الذي تختاره. تغطي أدلة المزودين هذا من البداية إلى النهاية.
2. **يُشير المسؤول (أو عضو مصرَّح له) إلى سر في متغير بيئة.** من صفحة متغيرات البيئة، يختار المستخدم بيانات اعتماد المزود ويُحدّد اسم السر. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables).
3. **تتلقى الأتمتة القيمة المحلولة وقت التشغيل.** عندما يعمل طاقم أو أتمتة، تجلب CrewAI Platform السر من مزوّدك وتحقنه كقيمة لمتغير البيئة. مع Workload Identity، يحدث هذا الجلب في كل إطلاق (مراعٍ للتدوير). مع بيانات الاعتماد الثابتة، يحدث الجلب وقت النشر وتُدمج القيمة في صورة النشر.
## الرؤية والنطاق
<Note>
تتّبع متغيرات البيئة المدعومة بـ WI نفس نموذج الإسناد الذي تتّبعه متغيرات البيئة العادية: لا تحلّ الأتمتة سوى متغيرات البيئة المدعومة بـ WI المُسنَدة إليها صراحةً. أَسنِد متغير WI إلى أتمتة من صفحة متغيرات البيئة الخاصة بتلك الأتمتة؛ المتغيرات المُعرَّفة على مستوى المنظمة أو في مشروع Studio لا تُحلّ عند الإطلاق حتى تُسنِدها.
</Note>
<Note>
تُشغَّل مرحلة جلب الأسرار في كل إطلاق، لكنها لا تقوم بعمل فعلي إلا حين تكون هناك متغيرات بيئة مدعومة بـ WI مُسنَدة إلى النشر. لكل متغير مُسنَد، يُحلّ وقت التشغيل القيمة من مزوّدك السحابي في كل إطلاق لـ crew أو flow أو training أو test أو checkpoint-restore ويكتبها في بيئة العملية. عند عدم وجود أي متغير مُسنَد، تكون المرحلة بلا أثر (no-op). وإلا فإن التكلفة تتناسب مع عدد المتغيرات المُسنَدة: تأخّر إضافي بسيط لكل إطلاق بالإضافة إلى إدخال واحد في سجل تدقيق السحابة لكل متغير.
</Note>
<Warning>
على مستوى *تكوينات* Workload Identity، لا يزال النطاق اليوم عاماً على مستوى المنظمة. تُهيَّأ كل أتمتة في المنظمة استناداً إلى جميع تكوينات Workload Identity التي سجّلتها المنظمة، ولا يمكنك اليوم ربط تكوين Workload Identity محدد بأتمتة بعينها. تحديد نطاق Workload Identity لكل أتمتة موجود في خارطة الطريق. حتى ذلك الحين، سجِّل فقط تكوينات Workload Identity التي يحقّ لكل أتمتة في منظمتك استخدامها.
</Warning>
## الأذونات
تتحكم ميزتان في CrewAI Platform بالوصول إلى مدير الأسرار:
- `secret_providers` — تتحكم بمن يستطيع عرض أو إدارة بيانات اعتماد المزودين.
- `environment_variables` — تتحكم بمن يستطيع إنشاء وتحرير متغيرات البيئة (بما فيها تلك التي تُشير إلى أسرار).
تتحكم ميزة ثالثة بإعداد Workload Identity:
- `workload_identity_configs` — تتحكم بمن يستطيع عرض أو إدارة تكوينات Workload Identity. مطلوبة فقط إذا كنت تستخدم مسار Workload Identity.
يتمتع المالكون دائماً بالوصول الكامل. لا يحصل الأعضاء على وصول إلى `secret_providers` أو `workload_identity_configs` افتراضياً ويجب منحهم الإذن عبر دور مخصص. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac) للحصول على المصفوفة الكاملة والتعليمات خطوة بخطوة.
## الخطوات التالية
اختر مسارك:
- **بيانات الاعتماد الثابتة** (أبسط، تتطلب إعادة نشر عند التدوير):
description: إدارة الأذونات والإشارة إلى الأسرار المُدارة من متغيرات البيئة في CrewAI Platform
sidebarTitle: الاستخدام والأذونات
icon: "list-check"
---
## نظرة عامة
هذا الدليل محايد تجاه المزود. يفترض أنك (أو مسؤول آخر) قد كوّنت بالفعل بيانات اعتماد واحدة على الأقل لمزود أسرار. اختر دليل الإعداد الخاص بك بناءً على المسار الذي تريده:
- بيانات الاعتماد الثابتة: [AWS](/ar/enterprise/features/secrets-manager/aws) · [GCP](/ar/enterprise/features/secrets-manager/gcp)
- الإشارة إلى الأسرار من متغيرات البيئة على أتمتاتك.
- التحقق من أن كل شيء يُحَلّ بشكل صحيح وقت التشغيل.
## الأذونات (RBAC)
ثلاث ميزات في CrewAI Platform ذات صلة عند العمل مع مدير الأسرار:
- `secret_providers` — تتحكم بالوصول إلى صفحة **بيانات اعتماد مزود الأسرار**.
- `workload_identity_configs` — تتحكم بالوصول إلى صفحة **Workload Identity** (ذات صلة فقط إذا كنت تستخدم مسار WI).
- `environment_variables` — تتحكم بمن يستطيع إنشاء أو تحرير متغيرات البيئة.
لكل ميزة مستويا إجراء: `read` و `manage`. منح `manage` يستلزم تلقائياً `read`.
### ما يجب منحه
| الهدف | `secret_providers` | `workload_identity_configs` | `environment_variables` |
|---|---|---|---|
| استخدام بيانات اعتماد ثابتة موجودة في متغيرات البيئة (بدون تعديل المزود) | `read` | — | `manage` |
| إنشاء أو تحرير أو حذف بيانات الاعتماد الثابتة | `manage` | — | `manage` |
| استخدام بيانات اعتماد مدعومة بـ Workload Identity موجودة في متغيرات البيئة | `read` | — | `manage` |
| إنشاء أو تحرير أو حذف تكوينات Workload Identity (وبيانات الاعتماد التي تشير إليها) | `manage` | `manage` | `manage` |
<Note>
يتمتع **المالكون** تلقائياً بالوصول الكامل إلى كل ميزة. يستبعد دور **العضو** الافتراضي عمداً `secret_providers` و `workload_identity_configs` — يجب على المسؤولين تضمين الأعضاء صراحةً عبر دور مخصص.
</Note>
### كيفية التعيين
1. في CrewAI Platform، انتقل إلى **Settings** ← **Roles**. من هذه الصفحة يمكنك إنشاء أدوار جديدة وتحرير أذونات كل دور وتعيين الأدوار للأعضاء الحاليين في المؤسسة.
{/* SCREENSHOT: Roles list page with "Create Role" button visible → /images/secrets-manager/usage/07-amp-roles-list.png */}
2. انقر على **Create Role** لإنشاء دور جديد، أو افتح دوراً موجوداً لتحرير أذوناته.
3. في محرر أذونات الدور، بدّل الميزات ذات الصلة وفق الجدول أعلاه:
- `secret_providers`: اختر **read** إذا كان هذا الدور يحتاج فقط إلى استخدام بيانات الاعتماد الموجودة، أو **manage** إذا كان ينبغي أن يكون قادراً أيضاً على إنشاء بيانات الاعتماد وتحريرها وحذفها.
- `environment_variables`: اختر **manage** ليتمكن الدور من إنشاء متغيرات بيئة تُشير إلى الأسرار.
{/* SCREENSHOT: Role editor showing the secret_providers feature with read/manage toggles → /images/secrets-manager/usage/08-amp-role-editor-secret-providers-toggles.png */}
{/* SCREENSHOT: Role editor showing environment_variables toggles → /images/secrets-manager/usage/09-amp-role-editor-env-vars-toggles.png */}
4. احفظ الدور.
5. عيّن الدور للأعضاء ذوي الصلة من نفس صفحة Roles (أو قائمة أعضاء المؤسسة).
{/* SCREENSHOT: Member assignment screen where the new role is applied to a user → /images/secrets-manager/usage/10-amp-assign-role-to-member.png */}
## الإشارة إلى الأسرار في متغيرات البيئة
بمجرد وجود بيانات اعتماد للمزود وامتلاك دورك للأذونات الصحيحة، يمكنك الإشارة إلى الأسرار المُدارة من أي متغير بيئة.
في CrewAI Platform، انتقل إلى **Environment Variables** وانقر على **Add Environment Variables**.
{/* SCREENSHOT: Environment Variables empty state with "Add" button → /images/secrets-manager/usage/11-amp-env-vars-empty.png */}
املأ النموذج:
- **Key** — اسم متغير البيئة. يجب أن يبدأ بحرف أو شرطة سفلية ويحتوي فقط على حروف وأرقام وشرطات سفلية. عادةً بأحرف كبيرة، مثل `OPENAI_API_KEY`.
- **Value Source** — اختر من أين تأتي القيمة:
- **Direct Value** — قيمة نصية صريحة تكتبها. استخدم هذا عندما لا ترغب في إشراك مزود.
- **Use AWS default** (أو ما يعادله لمزوّدك) — تستخدم بيانات الاعتماد المُعلَّمة حالياً كافتراضية لذلك النوع من المزود.
- **بيانات اعتماد مُسمَّاة محددة** — اختر بيانات الاعتماد بالاسم. استخدم هذا إذا كانت لديك بيانات اعتماد متعددة لنفس المزود (مثلاً `aws-prod` و `aws-staging`) وتريد اختيار واحدة صراحةً.
{/* SCREENSHOT: Env var form with the "Value Source" dropdown open, showing "AWS default" + named credentials → /images/secrets-manager/usage/12-amp-env-var-form-source-selector.png */}
- **Secret Name** — اسم السر في مزوّدك. بمجرد اختيار بيانات الاعتماد، يُقدّم هذا الحقل اقتراحاً تلقائياً: ابدأ بالكتابة، وتستعلم CrewAI Platform مزوّدك عن أسماء الأسرار المطابقة.
استخدم الصيغة `secret-name#json_key` لاستخراج حقل واحد من سر مهيكل (JSON). على سبيل المثال، عند وجود سر `database-credentials` بقيمة `{"username": "...", "password": "..."}`، أَشِر إلى `database-credentials#password` لحقن كلمة المرور فقط.
{/* SCREENSHOT: Env var form with the secret name autocomplete dropdown showing live results → /images/secrets-manager/usage/13-amp-env-var-form-secret-name-autocomplete.png */}
<Note>
**ملاحظة Azure Key Vault:** لا يمكن أن تحتوي أسماء أسرار Azure على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية في حقل **Secret Name** إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`).
</Note>
انقر على **Create** لحفظ المتغير.
{/* SCREENSHOT: Env var list with the new variable showing masked value and a "secret" indicator → /images/secrets-manager/usage/14-amp-env-var-created.png */}
<Tip>
عند تحرير متغير بيئة موجود، يحافظ ترك حقل **Value** فارغاً على القيمة الحالية. هذا مقصود — فهو يتيح لك تغيير حقول أخرى (مثل اسم السر أو بيانات الاعتماد) دون إعادة إدخال القيمة.
</Tip>
## التحقق من العمل
للتحقق من البداية إلى النهاية:
1. أَشِر إلى متغير البيئة على أتمتة أو طاقم أو عملية نشر تماماً كما تفعل مع أي متغير بيئة آخر.
2. انشر الأتمتة.
3. أطلق تشغيلاً وتأكد من اكتماله بنجاح.
### يعتمد سلوك التدوير على مسار بيانات الاعتماد
| مسار بيانات الاعتماد | متى يُقرأ السر | ما يتطلبه التدوير |
|---|---|---|
| **بيانات الاعتماد الثابتة** (مفاتيح AWS، ملف JSON لحساب خدمة GCP) | **وقت النشر** — تُدمج القيمة في صورة النشر | إعادة نشر الأتمتة بعد تدوير السر |
| **Workload Identity** (اتحاد OIDC، AWS أو GCP) | **في كل إطلاق أتمتة** — تُجلب القيمة طازجة من سحابتك | لا شيء — يرى الإطلاق التالي بعد التدوير القيمة الجديدة |
<Note>
**إذا كنت تحتاج أسراراً مراعية للتدوير** (بدون إعادة نشر عند التدوير)، استخدم مسار Workload Identity: [AWS WI](/ar/enterprise/features/secrets-manager/aws-workload-identity) أو [GCP WI](/ar/enterprise/features/secrets-manager/gcp-workload-identity). المقايضة هي مزيد من جهد الإعداد مقدماً (تسجيل CrewAI Platform كمزود OIDC في سحابتك) ولكن عمليات أبسط على المدى الطويل.
</Note>
إذا فشل النشر أو التشغيل بخطأ متعلق بسرك، تحقق من الأسباب الأكثر شيوعاً:
| العَرَض | السبب المحتمل |
|---|---|
| `no credential found` | يُشير متغير البيئة إلى مزود ولكن لم تُحدَّد بيانات اعتماد بعينها، ولا توجد بيانات اعتماد افتراضية مُعيّنة لذلك النوع من المزود. إما اختر بيانات اعتماد صراحةً على المتغير، أو علِّم بيانات اعتماد كافتراضية على صفحة **Secret Provider Credentials**. |
| `secret not found` | خطأ مطبعي في **Secret Name**، أو أن السر غير موجود في حساب/منطقة المزود التي تشير إليها بيانات الاعتماد. تحقق من كليهما. |
| تعمل الأتمتة بالقيمة القديمة بعد التدوير (مسار بيانات الاعتماد الثابتة) | القيمة السابقة مدمجة في صورة حاوية النشر. أعد نشر الأتمتة لاستيعاب القيمة المُدوَّرة. لتجنّب ذلك تماماً، حوّل بيانات الاعتماد إلى مسار Workload Identity. |
| تعمل الأتمتة بالقيمة القديمة بعد التدوير (مسار Workload Identity) | تأكد من أن متغير البيئة يُشير إلى بيانات اعتماد مدعومة بـ WI (وليس مفاتيح ثابتة). مع WI، ينبغي أن يرى الإطلاق التالي بعد التدوير القيمة الجديدة. إن لم يحدث ذلك، تحقق من أن السر قد تم تحديثه فعلاً في سحابتك (مثلاً، `aws secretsmanager get-secret-value`). |
| `JSON key not found` | عند استخدام `secret-name#json_key`، يجب أن يكون السر الأساسي كائن JSON صالحاً يحتوي على ذلك المفتاح. تحقق بقراءة السر مباشرة في مزوّدك. |
## الخطوات التالية
- [العودة إلى نظرة عامة على مدير الأسرار](/ar/enterprise/features/secrets-manager/overview)
- بيانات الاعتماد الثابتة: [AWS](/ar/enterprise/features/secrets-manager/aws) · [GCP](/ar/enterprise/features/secrets-manager/gcp)
description: مثال طاقم مستقل يُثبت أن تدوير الأسرار ينتشر إلى عمليات النشر الجارية دون إعادة نشر.
sidebarTitle: التحقق من التدوير
icon: "arrows-rotate"
---
## نظرة عامة
يوضّح لك هذا الدليل كيفية التحقق من أن **السر المُدوَّر في مزود السحابة لديك يُلتقط في أول إطلاق أتمتة لاحق** — بدون إعادة نشر ولا إعادة تشغيل عامل. هذا ذو صلة فقط عندما تكون قد كوّنت بيانات اعتماد مدعومة بـ Workload Identity ([AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity)، [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)، [Azure](/ar/enterprise/features/secrets-manager/azure-workload-identity)). تتطلب عمليات نشر بيانات الاعتماد الثابتة إعادة نشر بعد التدوير؛ ليس هناك ما يجب التحقق منه هنا.
تستخدم الوصفة أدناه طاقماً صغيراً مستقلاً بأداة واحدة ووكيل واحد ومهمة واحدة. لا يُشير موجه الطاقم أبداً إلى قيمة السر — بدلاً من ذلك، تقرأ أداة القيمة من `os.environ` وتُفيد ببصمة SHA-256 لما تراه. دوّر السر في مزود السحابة، أطلق مرة أخرى، وتتغير البصمة.
<Note>
لماذا بصمة وليس القيمة الخام؟ وضع الأسرار الخام في إخراج LLM وسجلات التتبع هو متجه تسرب. البصمة كافية لتأكيد "أن القيمة تغيّرت" دون كتابة القيمة الفعلية في أي مكان يمكن رصده.
</Note>
## المتطلبات المسبقة
قبل تشغيل هذا التحقق:
- بيانات اعتماد مزود أسرار مدعومة بـ WI مكوَّنة ([AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity)، [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)، [Azure](/ar/enterprise/features/secrets-manager/azure-workload-identity)).
- متغير بيئة على عملية النشر بـ `Secret = true`، المفتاح `API_KEY` (أو أي اسم تفضّله — اضبط الأداة أدناه لتطابقه)، يُشير إلى سر في مزود السحابة.
- طريقة لتحديث قيمة السر في مزود السحابة (وصول CLI أو وحدة تحكم السحابة).
- طريقة لإطلاق عملية النشر عبر HTTP (curl أو Postman أو علامة التبويب **Run** في CrewAI Platform).
## الخطوة 1 — هيكلة طاقم التحقق
أنشئ مشروع crew كلاسيكيًا لأن هذا المثال يربط أداة Python عبر `crew.py`:
انشر هذا الطاقم على CrewAI Platform تماماً كما تنشر أي طاقم آخر. ثم على صفحة **Environment Variables** الخاصة بعملية النشر:
- **Key:** `API_KEY` (يجب أن يطابق `ENV_VAR_NAME` في الأداة)
- **Value Source:** بيانات الاعتماد المدعومة بـ WI التي أعدّتها في [AWS WI](/ar/enterprise/features/secrets-manager/aws-workload-identity) أو [GCP WI](/ar/enterprise/features/secrets-manager/gcp-workload-identity)
- **Secret Name:** اسم السر في Secret Manager الخاص بمزود السحابة لديك
{/* SCREENSHOT: Environment Variables form with key=API_KEY, secret-backed value source selected, secret name filled → /images/secrets-manager/verify-rotation/01-env-var-form.png */}
## الخطوة 6 — تشغيل الإطلاق الأول
استبدل `<DEPLOYMENT_AUTH_TOKEN>` و `<DEPLOYMENT_HOST>` بالقيم من علامة التبويب **Run** الخاصة بعملية النشر.
يُثبت هذا أن التدوير التُقط بواسطة عملية النشر الجارية دون إعادة نشر ولا إعادة تشغيل عامل ولا أي إجراء آخر من قِبل المشغّل.
## ما يتحقق منه هذا — وما لا يتحقق منه
**يتحقق من:**
- يعمل إصدار رمز OIDC الخاص بـ WI من CrewAI Platform.
- تقبل الثقة من جانب السحابة (مزود IAM OIDC لـ AWS، Workload Identity Pool لـ GCP، Federated Identity Credential لـ Azure) الرمز.
- تمتلك الهوية من جانب السحابة (IAM Role / حساب خدمة GCP / Entra App Registration) وصولاً لقراءة السر.
- تصل قيمة السر إلى `os.environ` لعملية العامل وقت الإطلاق.
- تنتشر عمليات التدوير اللاحقة إلى الإطلاق التالي.
**لا يتحقق من:**
- أن طواقم الإنتاج الفعلية لديك تتعامل مع التدوير بسلاسة — مثلاً، المهام طويلة الأمد التي تقرأ متغير البيئة مرة واحدة عند البدء ستستمر في استخدام القيمة القديمة حتى تنتهي المهمة. خطّط وفقاً لذلك: اقرأ الأسرار عند نقطة الاستخدام، وليس عند استيراد الوحدة.
## لماذا لا نُشير إلى السر مباشرةً في الموجه؟
سيضع عرض توضيحي يبدو أبسط قيمة السر مباشرةً في وصف مهمة (مثلاً، "البحث عن `{api_key}`") ويتفحص الموجه. **لا تفعل ذلك.** لسببين:
1. **يُسرّب السر إلى تتبعات استدعاء LLM والسجلات من جانب المزود.** يمكن لأي شخص لديه وصول للتتبعات قراءته.
2. **يُغيّر وصف المهمة في كل إطلاق.** تُحدّد CrewAI Platform المهام بتجزئة MD5 للوصف؛ القيمة المُدوَّرة تعني أن التجزئة تتغير لكل إطلاق، مما يكسر ربط المهمة من وقت النشر إلى وقت التشغيل. العَرَض: تُسجَّل سجلات المهام كـ `pending_run` إلى الأبد، أو تُسجَّل بعض مهام طاقم متعدد المهام فقط.
يتجاوز النمط القائم على الأداة في هذا الدليل كلتا المشكلتين: الموجه ثابت، تقرأ الأداة متغير البيئة وقت التشغيل، وتصل فقط بصمة القيمة إلى LLM.
## الخطوات التالية
- [العودة إلى نظرة عامة على مدير الأسرار](/ar/enterprise/features/secrets-manager/overview)
- بمجرد التحقق، أَسقط طاقم التحقق. يجب أن تتبع الطواقم الفعلية النمط نفسه: الوصول إلى الأسرار عبر `os.environ` داخل أداة، وعدم استبدالها أبداً في الموجهات.
### عمليات النشر المحددة النطاق (مؤسسات متعددة المستخدمين)
يمكنك تحديد نطاق كل تكامل لمستخدم معين. على سبيل المثال، طاقم يتصل بـ Google يمكنه استخدام حساب Gmail لمستخدم محدد.
{" "}
<Tip>مفيد عندما تحتاج فرق/مستخدمون مختلفون للحفاظ على فصل الوصول إلى البيانات.</Tip>
استخدم `user_bearer_token` لتحديد نطاق المصادقة للمستخدم الطالب. إذا لم يكن المستخدم مسجل الدخول، فلن يستخدم الطاقم التكاملات المتصلة. وإلا فسيعود إلى رمز الحامل الافتراضي المهيأ لعملية النشر.
description: "استخدام بث Webhook لإرسال الأحداث إلى webhook الخاص بك"
icon: "webhook"
mode: "wide"
---
## نظرة عامة
يتيح لك بث أحداث Enterprise تلقي تحديثات webhook في الوقت الفعلي حول طواقمك وتدفقاتك المنشورة على CrewAI AMP، مثل استدعاءات النماذج واستخدام الأدوات وخطوات التدفق.
## الاستخدام
عند استخدام Kickoff API، أضف كائن `webhooks` إلى طلبك، على سبيل المثال:
إذا تم تعيين `realtime` إلى `true`، يتم تسليم كل حدث بشكل فردي وفوري، على حساب أداء الطاقم/التدفق.
## تنسيق Webhook
يرسل كل webhook قائمة بالأحداث:
```json
{
"events": [
{
"id": "event-id",
"execution_id": "crew-run-id",
"timestamp": "2025-02-16T10:58:44.965Z",
"type": "llm_call_started",
"data": {
"model": "gpt-4",
"messages": [
{ "role": "system", "content": "You are an assistant." },
{ "role": "user", "content": "Summarize this article." }
]
}
}
]
}
```
يختلف هيكل كائن `data` حسب نوع الحدث. راجع [قائمة الأحداث](https://github.com/crewAIInc/crewAI/tree/main/lib/crewai/src/crewai/events/types) على GitHub.
نظراً لأن الطلبات تُرسل عبر HTTP، لا يمكن ضمان ترتيب الأحداث. إذا كنت تحتاج الترتيب، استخدم حقل `timestamp`.
## الأحداث المدعومة
يدعم CrewAI كلاً من أحداث النظام والأحداث المخصصة في بث أحداث Enterprise. تُرسل هذه الأحداث إلى نقطة نهاية webhook المُهيأة أثناء تنفيذ الطاقم والتدفق.
### أحداث التدفق:
- `flow_created`
- `flow_started`
- `flow_finished`
- `flow_plot`
- `method_execution_started`
- `method_execution_finished`
- `method_execution_failed`
### أحداث الوكيل:
- `agent_execution_started`
- `agent_execution_completed`
- `agent_execution_error`
- `lite_agent_execution_started`
- `lite_agent_execution_completed`
- `lite_agent_execution_error`
- `agent_logs_started`
- `agent_logs_execution`
- `agent_evaluation_started`
- `agent_evaluation_completed`
- `agent_evaluation_failed`
### أحداث الطاقم:
- `crew_kickoff_started`
- `crew_kickoff_completed`
- `crew_kickoff_failed`
- `crew_train_started`
- `crew_train_completed`
- `crew_train_failed`
- `crew_test_started`
- `crew_test_completed`
- `crew_test_failed`
- `crew_test_result`
### أحداث المهام:
- `task_started`
- `task_completed`
- `task_failed`
- `task_evaluation`
### أحداث استخدام الأدوات:
- `tool_usage_started`
- `tool_usage_finished`
- `tool_usage_error`
- `tool_validate_input_error`
- `tool_selection_error`
- `tool_execution_error`
### أحداث LLM:
- `llm_call_started`
- `llm_call_completed`
- `llm_call_failed`
- `llm_stream_chunk`
### أحداث حواجز LLM:
- `llm_guardrail_started`
- `llm_guardrail_completed`
### أحداث الذاكرة:
- `memory_query_started`
- `memory_query_completed`
- `memory_query_failed`
- `memory_save_started`
- `memory_save_completed`
- `memory_save_failed`
- `memory_retrieval_started`
- `memory_retrieval_completed`
### أحداث المعرفة:
- `knowledge_search_query_started`
- `knowledge_search_query_completed`
- `knowledge_search_query_failed`
- `knowledge_query_started`
- `knowledge_query_completed`
- `knowledge_query_failed`
### أحداث الاستدلال:
- `agent_reasoning_started`
- `agent_reasoning_completed`
- `agent_reasoning_failed`
تتطابق أسماء الأحداث مع ناقل الأحداث الداخلي. راجع GitHub للقائمة الكاملة للأحداث.
يمكنك إصدار أحداثك المخصصة الخاصة، وسيتم تسليمها عبر تدفق webhook جنباً إلى جنب مع أحداث النظام.
description: "فهم كيفية عمل مشغلات CrewAI AMP وكيفية إدارتها وأين تجد أدلة التكامل الخاصة بكل خدمة"
icon: "face-smile"
mode: "wide"
---
تربط مشغلات CrewAI AMP أتمتاتك بالأحداث الفورية عبر الأدوات التي تستخدمها فرقك بالفعل. بدلاً من الاستعلام المتكرر عن الأنظمة أو الاعتماد على التشغيل اليدوي، تستمع المشغلات للتغييرات — رسائل بريد إلكتروني جديدة، تحديثات التقويم، تغييرات حالة CRM — وتطلق فوراً الطاقم أو التدفق الذي تحدده.
<Frame>

</Frame>
### أدلة التكامل
تقدم الأدلة المفصلة شرحاً لعملية الإعداد وأمثلة على سير العمل لكل تكامل:
<CardGroup cols={2}>
<Card title="مشغل Gmail" icon="envelope">
<a href="/ar/enterprise/guides/gmail-trigger">فعّل الطواقم عند وصول رسائل بريد إلكتروني أو تحديث سلاسل المحادثات.</a>
</Card>
{" "}
<Card title="مشغل Google Calendar" icon="calendar-days">
description: "تهيئة Azure OpenAI مع Crew Studio لاتصالات LLM المؤسسية"
icon: "microsoft"
mode: "wide"
---
يرشدك هذا الدليل خلال ربط Azure OpenAI مع Crew Studio لعمليات الذكاء الاصطناعي المؤسسية السلسة.
## عملية الإعداد
<Steps>
<Step title="الوصول إلى Azure AI Foundry">
1. في Azure، انتقل إلى [Azure AI Foundry](https://ai.azure.com/) > اختر نشر Azure OpenAI الخاص بك.
2. في القائمة اليسرى، انقر على `Deployments`. إذا لم يكن لديك نشر، أنشئ واحداً بالنموذج المطلوب.
3. بمجرد الإنشاء، اختر النشر وحدد موقع `Target URI` و`Key` على الجانب الأيمن من الصفحة. أبقِ هذه الصفحة مفتوحة، حيث ستحتاج هذه المعلومات.
<Frame>
<img src="/images/enterprise/azure-openai-studio.png" alt="Azure AI Foundry" />
</Frame>
</Step>
<Step title="تهيئة اتصال CrewAI AMP">
4. في علامة تبويب أخرى، افتح `CrewAI AMP > LLM Connections`. سمِّ اتصال LLM، واختر Azure كمزود، واختر نفس النموذج الذي اخترته في Azure.
5. في نفس الصفحة، أضف متغيرات البيئة من الخطوة 3:
- واحد بالاسم `AZURE_DEPLOYMENT_TARGET_URL` (باستخدام Target URI). يجب أن يبدو الرابط هكذا: https://your-deployment.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-08-01-preview
- آخر بالاسم `AZURE_API_KEY` (باستخدام Key).
6. انقر على `Add Connection` لحفظ اتصال LLM.
</Step>
<Step title="ضبط التهيئة الافتراضية">
7. في `CrewAI AMP > Settings > Defaults > Crew Studio LLM Settings`، عيّن اتصال LLM والنموذج الجديدين كافتراضيين.
</Step>
<Step title="تهيئة الوصول إلى الشبكة">
8. تأكد من إعدادات الوصول إلى الشبكة:
- في Azure، انتقل إلى `Azure OpenAI > اختر النشر`.
- انتقل إلى `Resource Management > Networking`.
- تأكد من تفعيل `Allow access from all networks`. إذا كان هذا الإعداد مقيداً، فقد يُحظر وصول CrewAI إلى نقطة نهاية Azure OpenAI.
</Step>
</Steps>
## التحقق
أنت جاهز! سيستخدم Crew Studio الآن اتصال Azure OpenAI الخاص بك. اختبر الاتصال بإنشاء طاقم أو مهمة بسيطة للتأكد من أن كل شيء يعمل بشكل صحيح.
## استكشاف الأخطاء وإصلاحها
إذا واجهت مشكلات:
- تحقق من أن تنسيق Target URI يتطابق مع النمط المتوقع
- تحقق من صحة مفتاح API وأنه يملك الصلاحيات المناسبة
- تأكد من تهيئة الوصول إلى الشبكة للسماح باتصالات CrewAI
- تأكد من أن نموذج النشر يتطابق مع ما هيأته في CrewAI
description: "تصدير التتبعات والسجلات من عمليات نشر CrewAI AMP إلى مجمّع OpenTelemetry الخاص بك"
icon: "magnifying-glass-chart"
mode: "wide"
---
يمكن لـ CrewAI AMP تصدير **التتبعات** و**السجلات** من OpenTelemetry من عمليات النشر مباشرة إلى مجمّعك الخاص. يتيح لك ذلك مراقبة أداء الوكلاء وتتبع استدعاءات LLM وتصحيح الأخطاء باستخدام مجموعة المراقبة الحالية.
تتبع بيانات القياس [اتفاقيات OpenTelemetry GenAI الدلالية](https://opentelemetry.io/docs/specs/semconv/gen-ai/) بالإضافة إلى سمات خاصة بـ CrewAI.
## المتطلبات المسبقة
<CardGroup cols={2}>
<Card title="حساب CrewAI AMP" icon="users">
يجب أن يكون لدى مؤسستك حساب CrewAI AMP نشط.
</Card>
<Card title="مجمّع OpenTelemetry" icon="server">
تحتاج إلى نقطة نهاية مجمّع متوافقة مع OpenTelemetry (مثل OTel Collector الخاص بك أو Datadog أو Grafana أو أي واجهة خلفية متوافقة مع OTLP).
</Card>
</CardGroup>
## إعداد مجمّع
1. في CrewAI AMP، انتقل إلى **Settings** > **OpenTelemetry Collectors**.
2. انقر على **Add Collector**.
3. اختر تكاملاً:
- **OpenTelemetry Traces** و**OpenTelemetry Logs** — صدّر إلى أي مجمّع أو واجهة خلفية متوافقة مع OTLP.
- **Datadog** — أرسل التتبعات مباشرة إلى استقبال OTLP الخاص بـ Datadog، دون الحاجة إلى مجمّع منفصل أو Datadog Agent.
4. هيّئ الاتصال. تعتمد الحقول على التكامل الذي اخترته:
<Tabs>
<Tab title="OpenTelemetry Traces / Logs">
إن **OpenTelemetry Traces** و**OpenTelemetry Logs** تكاملان منفصلان يتشاركان نفس الحقول — اختر التكامل المطابق للإشارة التي تريد تصديرها.
- **Endpoint** — نقطة نهاية OTLP لمجمّعك (مثل `https://otel-collector.example.com:4317`).
- **Service Name** — اسم لتعريف هذه الخدمة في منصة المراقبة.
- **Datadog Site Domain** — مضيف OTLP لموقع Datadog الخاص بك فقط، دون بروتوكول أو مسار. يقوم CrewAI ببناء نقطة نهاية HTTPS OTLP الكاملة نيابةً عنك. استخدم المضيف المطابق لـ [موقع Datadog](https://docs.datadoghq.com/getting_started/site/) الخاص بك:
- `otlp.datadoghq.com` (US1)
- `otlp.us3.datadoghq.com` (US3)
- `otlp.us5.datadoghq.com` (US5)
- `otlp.datadoghq.eu` (EU1)
- `otlp.ap1.datadoghq.com` (AP1)
- **API Key** — مفتاح واجهة برمجة تطبيقات Datadog الخاص بك. راجع [كيفية إنشاء واحد](https://docs.datadoghq.com/account_management/api-app-keys/#api-keys).
description: "اربط خوادم MCP الخاصة بك بـ CrewAI AMP مع وصول عام أو مصادقة بمفتاح API أو OAuth 2.0"
icon: "plug"
mode: "wide"
---
يدعم CrewAI AMP الاتصال بأي خادم MCP يُنفّذ [Model Context Protocol](https://modelcontextprotocol.io/). يمكنك إحضار خوادم عامة لا تتطلب مصادقة، وخوادم محمية بمفتاح API أو رمز حامل، وخوادم تستخدم OAuth 2.0 للوصول المفوّض الآمن.
## المتطلبات المسبقة
<CardGroup cols={2}>
<Card title="حساب CrewAI AMP" icon="user">
تحتاج إلى حساب [CrewAI AMP](https://app.crewai.com) نشط.
</Card>
<Card title="رابط خادم MCP" icon="link">
رابط خادم MCP الذي تريد الاتصال به. يجب أن يكون الخادم متاحاً من الإنترنت ويدعم نقل Streamable HTTP.
</Card>
</CardGroup>
## إضافة خادم MCP مخصص
<Steps>
<Step title="فتح الأدوات والتكاملات">
انتقل إلى **Tools & Integrations** في الشريط الجانبي الأيسر لـ CrewAI AMP، ثم اختر علامة تبويب **Connections**.
</Step>
<Step title="بدء إضافة خادم MCP مخصص">
انقر على زر **Add Custom MCP Server**. سيظهر مربع حوار مع نموذج التهيئة.
</Step>
<Step title="ملء المعلومات الأساسية">
- **Name** (مطلوب): اسم وصفي لخادم MCP (مثل "My Internal Tools Server").
- **Description**: ملخص اختياري لما يقدمه خادم MCP هذا.
- **Server URL** (مطلوب): الرابط الكامل لنقطة نهاية خادم MCP (مثل `https://my-server.example.com/mcp`).
</Step>
<Step title="اختيار طريقة المصادقة">
اختر إحدى طرق المصادقة الثلاث المتاحة بناءً على كيفية تأمين خادم MCP. راجع الأقسام أدناه لتفاصيل كل طريقة.
</Step>
<Step title="إضافة رؤوس مخصصة (اختياري)">
إذا كان خادم MCP يتطلب رؤوساً إضافية في كل طلب (مثل معرّفات المستأجر أو رؤوس التوجيه)، انقر على **+ Add Header** وقدم اسم الرأس وقيمته. يمكنك إضافة رؤوس مخصصة متعددة.
</Step>
<Step title="إنشاء الاتصال">
انقر على **Create MCP Server** لحفظ الاتصال. سيظهر خادم MCP المخصص الآن في قائمة الاتصالات وستكون أدواته متاحة للاستخدام في طواقمك.
</Step>
</Steps>
## طرق المصادقة
### بدون مصادقة
اختر هذا الخيار عندما يكون خادم MCP متاحاً للجمهور ولا يتطلب أي بيانات اعتماد. هذا شائع للخوادم مفتوحة المصدر أو الخوادم الداخلية العاملة خلف VPN.
### رمز المصادقة
استخدم هذه الطريقة عندما يكون خادم MCP محمياً بمفتاح API أو رمز حامل.
| **Client Secret** | لا | سر عميل OAuth. غير مطلوب للعملاء العامين باستخدام PKCE. |
| **Scopes** | لا | قائمة نطاقات مفصولة بمسافات للطلب (مثل `read write`). |
| **Token Auth Method** | لا | كيفية إرسال بيانات اعتماد العميل عند تبادل الرموز — **Standard (POST body)** أو **Basic Auth (header)**. الافتراضي هو Standard. |
| **PKCE Supported** | لا | فعّل إذا كان مزود OAuth يدعم Proof Key for Code Exchange. موصى به لتحسين الأمان. |
<Info>
**اكتشاف تهيئة OAuth**: إذا كان مزود OAuth يدعم OpenID Connect Discovery، انقر على رابط **Discover OAuth Config** لملء نقاط نهاية التفويض والرمز تلقائياً من رابط `/.well-known/openid-configuration` الخاص بالمزود.
</Info>
#### إعداد OAuth 2.0 خطوة بخطوة
<Steps>
<Step title="تسجيل رابط إعادة التوجيه">
انسخ **Redirect URI** المعروض في النموذج وأضفه كرابط إعادة توجيه مصرّح به في إعدادات تطبيق مزود OAuth.
</Step>
<Step title="إدخال نقاط النهاية وبيانات الاعتماد">
اختر **Token Auth Method** المناسبة. معظم المزودين يستخدمون الافتراضي **Standard (POST body)**. بعض المزودين القدامى يتطلبون **Basic Auth (header)**.
</Step>
<Step title="تفعيل PKCE (موصى به)">
حدد **PKCE Supported** إذا كان مزودك يدعمه. يضيف PKCE طبقة أمان إضافية لتدفق رمز التفويض وموصى به لجميع التكاملات الجديدة.
</Step>
<Step title="الإنشاء والتفويض">
انقر على **Create MCP Server**. سيتم توجيهك إلى مزود OAuth لتفويض الوصول. بمجرد التفويض، سيخزن CrewAI الرموز ويحدّثها تلقائياً حسب الحاجة.
</Step>
</Steps>
## استخدام خادم MCP المخصص
بمجرد الاتصال، تظهر أدوات خادم MCP المخصص جنباً إلى جنب مع الاتصالات المدمجة في صفحة **Tools & Integrations**. يمكنك:
- **تعيين الأدوات للوكلاء** في طواقمك تماماً كأي أداة CrewAI أخرى.
- **إدارة الرؤية** للتحكم في أعضاء الفريق الذين يمكنهم استخدام الخادم.
- **تعديل أو إزالة** الاتصال في أي وقت من قائمة الاتصالات.
<Warning>
إذا أصبح خادم MCP غير قابل للوصول أو انتهت صلاحية بيانات الاعتماد، ستفشل استدعاءات الأدوات التي تستخدم ذلك الخادم. تأكد من استقرار رابط الخادم وتحديث بيانات الاعتماد.
يمكنك أيضاً نشر طواقمك أو تدفقاتك مباشرة عبر واجهة ويب CrewAI AMP بربط حساب GitHub. لا يتطلب هذا النهج استخدام CLI على جهازك المحلي. تكتشف المنصة تلقائياً نوع مشروعك وتتعامل مع البناء بشكل مناسب.
<Steps>
<Step title="الدفع إلى GitHub">
تحتاج لدفع طاقمك إلى مستودع GitHub. إذا لم تكن قد أنشأت طاقماً بعد، يمكنك [اتباع هذا الدليل](/ar/quickstart).
</Step>
<Step title="ربط GitHub بـ CrewAI AMP">
1. سجّل الدخول إلى [CrewAI AMP](https://app.crewai.com)
## الخيار 3: إعادة النشر باستخدام API (تكامل CI/CD)
لعمليات النشر الآلية في خطوط أنابيب CI/CD، يمكنك استخدام CrewAI API لتشغيل إعادة نشر الطواقم الحالية. هذا مفيد بشكل خاص لـ GitHub Actions وJenkins أو سير عمل الأتمتة الأخرى.
<Steps>
<Step title="الحصول على رمز الوصول الشخصي">
انتقل إلى إعدادات حساب CrewAI AMP لإنشاء رمز API:
1. انتقل إلى [app.crewai.com](https://app.crewai.com)
2. انقر على **Settings** → **Account** → **Personal Access Token**
3. أنشئ رمزاً جديداً وانسخه بأمان
4. خزّن هذا الرمز كسر في نظام CI/CD
</Step>
<Step title="إيجاد UUID الأتمتة">
حدد موقع المعرّف الفريد لطاقمك المنشور:
1. انتقل إلى **Automations** في لوحة تحكم CrewAI AMP
description: "تشغيل الطواقم عند إنشاء أو تحديث أو إلغاء أحداث Google Calendar"
icon: "calendar"
mode: "wide"
---
## نظرة عامة
استخدم مشغل Google Calendar لإطلاق الأتمتات كلما تغيرت أحداث التقويم. تشمل حالات الاستخدام الشائعة إحاطة الفريق قبل اجتماع، وإخطار أصحاب المصلحة عند إلغاء حدث هام، أو تلخيص الجداول اليومية.
<Tip>
تأكد من ربط Google Calendar في **Tools & Integrations** وتفعيله
لعملية النشر التي تريد أتمتتها.
</Tip>
## تفعيل مشغل Google Calendar
1. افتح عملية النشر في CrewAI AMP
2. انتقل إلى علامة تبويب **Triggers**
3. حدد موقع **Google Calendar** وبدّل مفتاح التبديل للتفعيل
<Frame>
<img
src="/images/enterprise/calendar-trigger.png"
alt="تفعيل أو تعطيل المشغلات بالتبديل"
/>
</Frame>
## مثال: تلخيص تفاصيل الاجتماع
المقتطف أدناه يعكس مثال `calendar-event-crew.py` في مستودع المشغلات. يحلل الحمولة، ويحلل الحاضرين والتوقيت، وينتج ملخصاً للاجتماع للأدوات اللاحقة.
```python
from calendar_event_crew import GoogleCalendarEventTrigger
crew = GoogleCalendarEventTrigger().crew()
result = crew.kickoff({
"crewai_trigger_payload": calendar_payload,
})
print(result.raw)
```
استخدم `crewai_trigger_payload` تماماً كما يتم تسليمه من المشغل حتى يتمكن الطاقم من استخراج الحقول المناسبة.
## الاختبار المحلي
اختبر تكامل مشغل Google Calendar محلياً باستخدام CrewAI CLI:
```bash
# عرض جميع المشغلات المتاحة
crewai triggers list
# محاكاة مشغل Google Calendar بحمولة واقعية
crewai triggers run google_calendar/event_changed
```
سينفذ أمر `crewai triggers run` طاقمك بحمولة Calendar كاملة، مما يتيح لك اختبار منطق التحليل قبل النشر.
<Warning>
استخدم `crewai triggers run google_calendar/event_changed` (وليس `crewai run`) لمحاكاة
تنفيذ المشغل أثناء التطوير. بعد النشر، سيتلقى طاقمك
حمولة المشغل تلقائياً.
</Warning>
## مراقبة عمليات التنفيذ
تتبع قائمة **Executions** في لوحة تحكم النشر كل عملية تشغيل مُشغّلة وتعرض بيانات الحمولة الوصفية وملخصات المخرجات والأخطاء.
<Frame>
<img
src="/images/enterprise/list-executions.png"
alt="قائمة عمليات التنفيذ المُشغّلة بواسطة الأتمتة"
/>
</Frame>
## استكشاف الأخطاء وإصلاحها
- تأكد من ربط حساب Google الصحيح وتفعيل المشغل
- اختبر محلياً بـ `crewai triggers run google_calendar/event_changed` لرؤية هيكل الحمولة بالضبط
- تأكد من أن سير عملك يتعامل مع أحداث اليوم الكامل (الحمولات تستخدم `start.date` و`end.date` بدلاً من الطوابع الزمنية)
- تحقق من سجلات التنفيذ إذا كانت التذكيرات أو مصفوفات الحاضرين مفقودة — قد تحد صلاحيات التقويم من الحقول في الحمولة
- تذكر: استخدم `crewai triggers run` (وليس `crewai run`) لمحاكاة تنفيذ المشغل
description: "الاستجابة لأحداث ملفات Google Drive بطواقم آلية"
icon: "folder"
mode: "wide"
---
## نظرة عامة
شغّل أتمتاتك عند إنشاء أو تحديث أو حذف ملفات في Google Drive. تشمل سير العمل النموذجية تلخيص المحتوى المُحمّل حديثاً، وتطبيق سياسات المشاركة، أو إخطار المالكين عند تغيير ملفات هامة.
<Tip>
اربط Google Drive في **Tools & Integrations** وتأكد من تفعيل المشغل
للأتمتة التي تريد مراقبتها.
</Tip>
## تفعيل مشغل Google Drive
1. افتح عملية النشر في CrewAI AMP
2. انتقل إلى علامة تبويب **Triggers**
3. حدد موقع **Google Drive** وبدّل مفتاح التبديل للتفعيل
description: "تشغيل طواقم CrewAI مباشرة من سير عمل HubSpot"
icon: "hubspot"
mode: "wide"
---
يقدم هذا الدليل عملية خطوة بخطوة لإعداد مشغلات HubSpot لـ CrewAI AMP، مما يتيح لك بدء الطواقم مباشرة من سير عمل HubSpot.
## المتطلبات المسبقة
- حساب CrewAI AMP
- حساب HubSpot مع ميزة [HubSpot Workflows](https://knowledge.hubspot.com/workflows/create-workflows)
## خطوات الإعداد
<Steps>
<Step title="ربط حساب HubSpot بـ CrewAI AMP">
- سجّل الدخول إلى `حساب CrewAI AMP > Triggers` - اختر `HubSpot` من
قائمة المشغلات المتاحة - اختر حساب HubSpot الذي تريد ربطه
بـ CrewAI AMP - اتبع التعليمات على الشاشة لتفويض وصول CrewAI AMP
إلى حساب HubSpot - ستظهر رسالة تأكيد بمجرد
ربط HubSpot بنجاح مع CrewAI AMP
</Step>
<Step title="إنشاء سير عمل HubSpot">
- سجّل الدخول إلى `حساب HubSpot > Automations > Workflows > New workflow`
- اختر نوع سير العمل المناسب لاحتياجاتك (مثل Start from scratch) -
في منشئ سير العمل، انقر على أيقونة Plus (+) لإضافة إجراء جديد. -
اختر `Integrated apps > CrewAI > Kickoff a Crew`. - اختر الطاقم الذي
تريد تشغيله. - انقر على `Save` لإضافة الإجراء إلى سير عملك
<Frame>
<img
src="/images/enterprise/hubspot-workflow-1.png"
alt="سير عمل HubSpot 1"
/>
</Frame>
</Step>
<Step title="استخدام نتائج الطاقم مع إجراءات أخرى">
- بعد خطوة Kickoff a Crew، انقر على أيقونة Plus (+) لإضافة
إجراء جديد. - على سبيل المثال، لإرسال إشعار بريد إلكتروني داخلي، اختر
`Communications > Send internal email notification` - في حقل Body،
انقر على `Insert data`، اختر `View properties or action outputs from > Action
outputs > Crew Result` لتضمين بيانات الطاقم في البريد الإلكتروني
<Frame>
<img
src="/images/enterprise/hubspot-workflow-2.png"
alt="سير عمل HubSpot 2"
/>
</Frame>
- هيّئ أي إجراءات إضافية حسب الحاجة - راجع خطوات
سير عملك للتأكد من إعداد كل شيء بشكل صحيح - فعّل سير العمل
<Frame>
<img
src="/images/enterprise/hubspot-workflow-3.png"
alt="سير عمل HubSpot 3"
/>
</Frame>
</Step>
</Steps>
لمزيد من المعلومات المفصلة حول الإجراءات المتاحة وخيارات التخصيص، راجع [وثائق HubSpot Workflows](https://knowledge.hubspot.com/workflows/create-workflows).
description: "تعلم كيفية تنفيذ سير عمل Human-In-The-Loop في CrewAI لتعزيز اتخاذ القرار"
icon: "user-check"
mode: "wide"
---
Human-In-The-Loop (HITL) هو نهج قوي يجمع بين الذكاء الاصطناعي والخبرة البشرية لتعزيز اتخاذ القرار وتحسين نتائج المهام. يوضح هذا الدليل كيفية تنفيذ HITL داخل CrewAI Enterprise.
## نهجا HITL في CrewAI
يقدم CrewAI نهجين لتنفيذ سير عمل Human-In-The-Loop:
| النهج | الأفضل لـ | الإصدار |
|-------|-----------|---------|
| **قائم على التدفق** (مُزخرف `@human_feedback`) | الإنتاج مع واجهة Enterprise، سير عمل البريد الإلكتروني أولاً، ميزات المنصة الكاملة | **1.8.0+** |
| **قائم على Webhook** | التكاملات المخصصة، الأنظمة الخارجية (Slack، Teams، إلخ.)، الإعدادات القديمة | جميع الإصدارات |
## HITL القائم على التدفق مع منصة Enterprise
<Note>
يتطلب مُزخرف `@human_feedback` **إصدار CrewAI 1.8.0 أو أعلى**.
</Note>
عند استخدام مُزخرف `@human_feedback` في تدفقاتك، يوفر CrewAI Enterprise **نظام HITL يعتمد على البريد الإلكتروني أولاً** يمكّن أي شخص لديه عنوان بريد إلكتروني من الاستجابة لطلبات المراجعة:
بمجرد إتمام الطاقم للمهمة التي تتطلب إدخالاً بشرياً، ستتلقى إشعار webhook يحتوي على:
- **معرّف التنفيذ**
- **معرّف المهمة**
- **مخرجات المهمة**
</Step>
<Step title="مراجعة مخرجات المهمة">
سيتوقف النظام في حالة `Pending Human Input`. راجع مخرجات المهمة بعناية.
</Step>
<Step title="إرسال التغذية الراجعة البشرية">
استدعِ نقطة نهاية الاستئناف لطاقمك بالمعلومات التالية:
<Frame>
<img src="/images/enterprise/crew-resume-endpoint.png" alt="نقطة نهاية استئناف الطاقم" />
</Frame>
<Warning>
**هام: يجب تقديم روابط Webhook مرة أخرى**:
**يجب** تقديم نفس روابط webhook (`taskWebhookUrl`، `stepWebhookUrl`، `crewWebhookUrl`) في استدعاء الاستئناف التي استخدمتها في استدعاء التشغيل. لا تُنقل تهيئات Webhook تلقائياً من التشغيل — يجب تضمينها صراحة في طلب الاستئناف لمواصلة تلقي الإشعارات لاكتمال المهام وخطوات الوكيل واكتمال الطاقم.
description: "تشغيل الطواقم من نشاط محادثات Microsoft Teams"
icon: "microsoft"
mode: "wide"
---
## نظرة عامة
استخدم مشغل Microsoft Teams لبدء الأتمتات كلما أُنشئت محادثة جديدة. تشمل الأنماط الشائعة تلخيص الطلبات الواردة وتوجيه الرسائل العاجلة لفرق الدعم أو إنشاء مهام متابعة في أنظمة أخرى.
<Tip>
تأكد من ربط Microsoft Teams تحت **Tools & Integrations** و
تفعيله في علامة تبويب **Triggers** لعملية النشر.
</Tip>
## تفعيل مشغل Microsoft Teams
1. افتح عملية النشر في CrewAI AMP
2. انتقل إلى علامة تبويب **Triggers**
3. حدد موقع **Microsoft Teams** وبدّل مفتاح التبديل للتفعيل
<Frame caption="اتصال مشغل Microsoft Teams">
<img
src="/images/enterprise/msteams-trigger.png"
alt="تفعيل أو تعطيل المشغلات بالتبديل"
/>
</Frame>
## مثال: تلخيص سلسلة محادثة جديدة
```python
from teams_chat_created_crew import MicrosoftTeamsChatTrigger
crew = MicrosoftTeamsChatTrigger().crew()
result = crew.kickoff({
"crewai_trigger_payload": teams_payload,
})
print(result.raw)
```
يحلل الطاقم بيانات المحادثة الوصفية (الموضوع، وقت الإنشاء، قائمة الأعضاء) وينشئ خطة عمل للفريق المستقبل.
## الاختبار المحلي
اختبر تكامل مشغل Microsoft Teams محلياً باستخدام CrewAI CLI:
```bash
# عرض جميع المشغلات المتاحة
crewai triggers list
# محاكاة مشغل Microsoft Teams بحمولة واقعية
crewai triggers run microsoft_teams/teams_message_created
```
سينفذ أمر `crewai triggers run` طاقمك بحمولة Teams كاملة، مما يتيح لك اختبار منطق التحليل قبل النشر.
<Warning>
استخدم `crewai triggers run microsoft_teams/teams_message_created` (وليس `crewai
run`) لمحاكاة تنفيذ المشغل أثناء التطوير. بعد النشر، سيتلقى
طاقمك حمولة المشغل تلقائياً.
</Warning>
## استكشاف الأخطاء وإصلاحها
- تأكد من أن اتصال Teams نشط؛ يجب تحديثه إذا سحب المستأجر الصلاحيات
- اختبر محلياً بـ `crewai triggers run microsoft_teams/teams_message_created` لرؤية هيكل الحمولة بالضبط
- تأكد من أن اشتراك webhook في Microsoft 365 لا يزال صالحاً إذا توقفت الحمولات عن الوصول
- راجع سجلات التنفيذ لعدم تطابق شكل الحمولة — قد تحذف إشعارات Graph حقولاً عندما تكون المحادثة خاصة أو مقيدة
- تذكر: استخدم `crewai triggers run` (وليس `crewai run`) لمحاكاة تنفيذ المشغل
ابدأ الأتمتات عند تغيير الملفات داخل OneDrive. يمكنك إنشاء ملخصات تدقيق وإخطار فرق الأمان بشأن المشاركة الخارجية أو تحديث أنظمة الأعمال اللاحقة ببيانات المستندات الوصفية الجديدة.
<Tip>
اربط OneDrive في **Tools & Integrations** وبدّل المشغل لعملية
النشر.
</Tip>
## تفعيل مشغل OneDrive
1. افتح عملية النشر في CrewAI AMP
2. انتقل إلى علامة تبويب **Triggers**
3. حدد موقع **OneDrive** وبدّل مفتاح التبديل للتفعيل
<Frame caption="اتصال مشغل Microsoft OneDrive">
<img
src="/images/enterprise/onedrive-trigger.png"
alt="تفعيل أو تعطيل المشغلات بالتبديل"
/>
</Frame>
## مثال: تدقيق صلاحيات الملفات
```python
from onedrive_file_crew import OneDriveFileTrigger
crew = OneDriveFileTrigger().crew()
crew.kickoff({
"crewai_trigger_payload": onedrive_payload,
})
```
يفحص الطاقم بيانات الملف الوصفية ونشاط المستخدم وتغييرات الصلاحيات لإنتاج ملخص متوافق مع متطلبات الامتثال.
## الاختبار المحلي
اختبر تكامل مشغل OneDrive محلياً باستخدام CrewAI CLI:
```bash
# عرض جميع المشغلات المتاحة
crewai triggers list
# محاكاة مشغل OneDrive بحمولة واقعية
crewai triggers run microsoft_onedrive/file_changed
```
سينفذ أمر `crewai triggers run` طاقمك بحمولة OneDrive كاملة، مما يتيح لك اختبار منطق التحليل قبل النشر.
<Warning>
استخدم `crewai triggers run microsoft_onedrive/file_changed` (وليس `crewai run`)
لمحاكاة تنفيذ المشغل أثناء التطوير. بعد النشر، سيتلقى طاقمك
حمولة المشغل تلقائياً.
</Warning>
## استكشاف الأخطاء وإصلاحها
- تأكد من أن الحساب المتصل لديه صلاحية قراءة بيانات الملف الوصفية المضمنة في webhook
- اختبر محلياً بـ `crewai triggers run microsoft_onedrive/file_changed` لرؤية هيكل الحمولة بالضبط
- إذا كان المشغل يعمل لكن الحمولة تفتقد `permissions`، تأكد من أن إعدادات المشاركة على مستوى الموقع تسمح لـ Graph بإرجاع هذا الحقل
- للمستأجرين الكبار، صفّ الإشعارات مسبقاً حتى يعمل الطاقم فقط على المجلدات ذات الصلة
- تذكر: استخدم `crewai triggers run` (وليس `crewai run`) لمحاكاة تنفيذ المشغل
description: "إطلاق الأتمتات من رسائل Outlook وتحديثات التقويم"
icon: "microsoft"
mode: "wide"
---
## نظرة عامة
أتمت الاستجابات عندما يسلّم Outlook رسالة جديدة أو عند إزالة حدث من التقويم. تقوم الفرق عادة بتوجيه التصعيدات وإنشاء تذاكر أو تنبيه الحاضرين بالإلغاءات.
<Tip>
اربط Outlook في **Tools & Integrations** وتأكد من تفعيل المشغل
لعملية النشر.
</Tip>
## تفعيل مشغل Outlook
1. افتح عملية النشر في CrewAI AMP
2. انتقل إلى علامة تبويب **Triggers**
3. حدد موقع **Outlook** وبدّل مفتاح التبديل للتفعيل
<Frame caption="اتصال مشغل Microsoft Outlook">
<img
src="/images/enterprise/outlook-trigger.png"
alt="تفعيل أو تعطيل المشغلات بالتبديل"
/>
</Frame>
## مثال: تلخيص رسالة بريد إلكتروني جديدة
```python
from outlook_message_crew import OutlookMessageTrigger
crew = OutlookMessageTrigger().crew()
crew.kickoff({
"crewai_trigger_payload": outlook_payload,
})
```
يستخرج الطاقم تفاصيل المرسل والموضوع ومعاينة النص والمرفقات قبل إنشاء استجابة منظمة.
## الاختبار المحلي
اختبر تكامل مشغل Outlook محلياً باستخدام CrewAI CLI:
```bash
# عرض جميع المشغلات المتاحة
crewai triggers list
# محاكاة مشغل Outlook بحمولة واقعية
crewai triggers run microsoft_outlook/email_received
```
سينفذ أمر `crewai triggers run` طاقمك بحمولة Outlook كاملة، مما يتيح لك اختبار منطق التحليل قبل النشر.
<Warning>
استخدم `crewai triggers run microsoft_outlook/email_received` (وليس `crewai run`)
لمحاكاة تنفيذ المشغل أثناء التطوير. بعد النشر، سيتلقى طاقمك
حمولة المشغل تلقائياً.
</Warning>
## استكشاف الأخطاء وإصلاحها
- تحقق من أن موصل Outlook لا يزال مفوّضاً؛ يجب تجديد الاشتراك دورياً
- اختبر محلياً بـ `crewai triggers run microsoft_outlook/email_received` لرؤية هيكل الحمولة بالضبط
- إذا كانت المرفقات مفقودة، تأكد من أن اشتراك webhook يتضمن علامة `includeResourceData`
- راجع سجلات التنفيذ عندما تفشل الأحداث في المطابقة — حمولات الإلغاء تفتقد قوائم الحاضرين حسب التصميم ويجب أن يأخذ الطاقم ذلك في الاعتبار
- تذكر: استخدم `crewai triggers run` (وليس `crewai run`) لمحاكاة تنفيذ المشغل
description: "تأكد من جاهزية طاقمك أو تدفقك للنشر على CrewAI AMP"
icon: "clipboard-check"
mode: "wide"
---
<Note>
قبل النشر على CrewAI AMP، من الضروري التحقق من صحة بنية مشروعك.
يمكن نشر كل من الطواقم والتدفقات كـ "أتمتات"، لكن لهما بنى مشاريع
ومتطلبات مختلفة يجب استيفاؤها لنجاح النشر.
</Note>
## فهم الأتمتات
في CrewAI AMP، **الأتمتات** هو المصطلح الشامل لمشاريع الذكاء الاصطناعي الوكيل القابلة للنشر. يمكن أن تكون الأتمتة إما:
- **طاقم**: فريق مستقل من وكلاء الذكاء الاصطناعي يعملون معاً على المهام
- **تدفق**: سير عمل مُنسّق يمكنه الجمع بين طواقم متعددة واستدعاءات LLM المباشرة والمنطق الإجرائي
فهم النوع الذي تنشره ضروري لأن لهما بنى مشاريع ونقاط دخول مختلفة.
## الطواقم مقابل التدفقات: الفروقات الرئيسية
<CardGroup cols={2}>
<Card title="مشاريع الطاقم" icon="users">
فرق وكلاء ذكاء اصطناعي مستقلة. الـ crews الجديدة تستخدم بنية JSON-first مع `crew.jsonc` و `agents/`؛ ويمكن للـ crews الكلاسيكية الاستمرار في استخدام `crew.py`.
| **Azure DevOps Artifacts** | `https://pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/` | أي نص غير فارغ (مثل `token`) | Personal Access Token (PAT) بنطاق Packaging Read |
| **GitHub Packages** | `https://pypi.pkg.github.com/{owner}/simple/` | اسم مستخدم GitHub | Personal Access Token (classic) بنطاق `read:packages` |
| **GitLab Package Registry** | `https://gitlab.com/api/v4/projects/{project_id}/packages/pypi/simple/` | `__token__` | Project أو Personal Access Token بنطاق `read_api` |
| **AWS CodeArtifact** | استخدم الرابط من `aws codeartifact get-repository-endpoint` | `aws` | رمز من `aws codeartifact get-authorization-token` |
| **Google Artifact Registry** | `https://{region}-python.pkg.dev/{project}/{repo}/simple/` | `_json_key_base64` | مفتاح حساب الخدمة بتشفير Base64 |
| **JFrog Artifactory** | `https://{instance}.jfrog.io/artifactory/api/pypi/{repo}/simple/` | اسم المستخدم أو البريد الإلكتروني | مفتاح API أو رمز الهوية |
| **مستضاف ذاتياً (devpi، Nexus، إلخ.)** | رابط Simple API لسجلك | اسم مستخدم السجل | كلمة مرور السجل |
<Tip>
لـ **AWS CodeArtifact**، تنتهي صلاحية رمز التفويض دورياً.
ستحتاج لتحديث قيمة `UV_INDEX_*_PASSWORD` عند انتهاء صلاحيتها.
فكّر في أتمتة هذا في خط أنابيب CI/CD.
</Tip>
## تعيين متغيرات البيئة في AMP
يجب تهيئة بيانات اعتماد السجل الخاص كمتغيرات بيئة في CrewAI AMP.
لديك خياران:
<Tabs>
<Tab title="واجهة الويب">
1. سجّل الدخول إلى [CrewAI AMP](https://app.crewai.com)
2. انتقل إلى أتمتتك
3. افتح علامة تبويب **Environment Variables**
4. أضف كل متغير (`UV_INDEX_*_USERNAME` و`UV_INDEX_*_PASSWORD`) مع قيمته
راجع خطوة [النشر على AMP — تعيين متغيرات البيئة](/ar/enterprise/guides/deploy-to-amp#set-environment-variables) للتفاصيل.
</Tab>
<Tab title="النشر عبر CLI">
أضف المتغيرات إلى ملف `.env` المحلي قبل تشغيل `crewai deploy create`.
description: "تشغيل طواقم CrewAI من سير عمل Salesforce لأتمتة CRM"
icon: "salesforce"
mode: "wide"
---
يمكن تشغيل CrewAI AMP من Salesforce لأتمتة سير عمل إدارة علاقات العملاء وتعزيز عمليات المبيعات.
## نظرة عامة
Salesforce هي منصة رائدة لإدارة علاقات العملاء (CRM) تساعد الشركات على تبسيط عمليات المبيعات والخدمة والتسويق. من خلال إعداد مشغلات CrewAI من Salesforce، يمكنك:
- أتمتة تسجيل وتأهيل العملاء المحتملين
- إنشاء مواد مبيعات مخصصة
- تعزيز خدمة العملاء بردود مدعومة بالذكاء الاصطناعي
1. **تواصل مع الدعم**: تواصل مع دعم CrewAI AMP للمساعدة في إعداد مشغل Salesforce
2. **مراجعة المتطلبات**: تأكد من أن لديك صلاحيات Salesforce اللازمة والوصول إلى API
3. **تهيئة الاتصال**: اعمل مع فريق الدعم لإنشاء الاتصال بين CrewAI ومثيل Salesforce الخاص بك
4. **اختبار المشغلات**: تحقق من عمل المشغلات بشكل صحيح مع حالات الاستخدام المحددة
## حالات الاستخدام
سيناريوهات Salesforce + CrewAI الشائعة تشمل:
- **معالجة العملاء المحتملين**: تحليل وتسجيل العملاء المحتملين الوافدين تلقائياً
- **إنشاء العروض**: إنشاء عروض مخصصة بناءً على بيانات الفرص
- **رؤى العملاء**: إنشاء تقارير تحليلية من سجل تفاعلات العملاء
- **أتمتة المتابعة**: إنشاء رسائل متابعة وتوصيات مخصصة
## الخطوات التالية
للحصول على تعليمات الإعداد المفصلة وخيارات التهيئة المتقدمة، يرجى التواصل مع دعم CrewAI AMP الذي يمكنه تقديم إرشادات مخصصة لبيئة Salesforce واحتياجات عملك المحددة.
- صلاحيات الوصول للنشر أو التثبيت في مؤسسة CrewAI AMP
## تثبيت الأدوات
لتثبيت أداة:
```bash
crewai tool install <tool-name>
```
يثبّت هذا الأداة ويضيفها إلى `pyproject.toml`.
يمكنك استخدام الأداة باستيرادها وإضافتها إلى وكلائك:
```python
from your_tool.tool import YourTool
custom_tool = YourTool()
researcher = Agent(
role='Market Research Analyst',
goal='Provide up-to-date market analysis of the AI industry',
backstory='An expert analyst with a keen eye for market trends.',
tools=[custom_tool],
verbose=True
)
```
## إضافة حزم أخرى بعد تثبيت أداة
بعد تثبيت أداة من مستودع أدوات CrewAI AMP، تحتاج لاستخدام أمر `crewai uv` لإضافة حزم أخرى لمشروعك.
استخدام أوامر `uv` المباشرة سيفشل لأن المصادقة لمستودع الأدوات يتم التعامل معها عبر CLI. باستخدام أمر `crewai uv`، يمكنك إضافة حزم أخرى لمشروعك دون القلق بشأن المصادقة.
يمكن استخدام أي أمر `uv` مع أمر `crewai uv`، مما يجعله أداة قوية لإدارة اعتماديات مشروعك دون عناء إدارة المصادقة عبر متغيرات البيئة أو طرق أخرى.
لنفرض أنك ثبّت أداة مخصصة من مستودع أدوات CrewAI AMP تسمى "my-tool":
```bash
crewai tool install my-tool
```
والآن تريد إضافة حزمة أخرى لمشروعك، يمكنك استخدام الأمر التالي:
```bash
crewai uv add requests
```
أوامر أخرى مثل `uv sync` أو `uv remove` يمكن أيضاً استخدامها مع أمر `crewai uv`:
```bash
crewai uv sync
```
```bash
crewai uv remove requests
```
سيضيف هذا الحزمة لمشروعك ويحدّث `pyproject.toml` وفقاً لذلك.
## إنشاء ونشر الأدوات
لإنشاء مشروع أداة جديد:
```bash
crewai tool create <tool-name>
```
يولّد هذا مشروع أداة مُهيكل محلياً.
بعد إجراء التغييرات، أنشئ مستودع Git وارفع الكود:
```bash
git init
git add .
git commit -m "Initial version"
```
لنشر الأداة:
```bash
crewai tool publish
```
افتراضياً، تُنشر الأدوات كخاصة. لجعل الأداة عامة:
```bash
crewai tool publish --public
```
لمزيد من التفاصيل حول بناء الأدوات، راجع [إنشاء أدواتك الخاصة](/ar/concepts/tools#creating-your-own-tools).
## تحديث الأدوات
لتحديث أداة منشورة:
1. عدّل الأداة محلياً
2. حدّث الإصدار في `pyproject.toml` (مثل من `0.1.0` إلى `0.1.1`)
3. ارفع التغييرات وانشر
```bash
git commit -m "Update version to 0.1.1"
crewai tool publish
```
## حذف الأدوات
لحذف أداة:
1. انتقل إلى [CrewAI AMP](https://app.crewai.com)
2. انتقل إلى **Tools**
3. اختر الأداة
4. انقر على **Delete**
<Warning>
الحذف نهائي. لا يمكن استعادة أو إعادة تثبيت الأدوات المحذوفة.
</Warning>
## فحوصات الأمان
كل إصدار منشور يخضع لفحوصات أمان آلية، ولا يكون متاحاً للتثبيت إلا بعد اجتيازها.
description: "قم بتدريب طواقمك المنشورة مباشرة من منصة CrewAI AMP لتحسين أداء الوكلاء بمرور الوقت"
icon: "dumbbell"
mode: "wide"
---
يتيح لك التدريب تحسين أداء الطاقم من خلال تشغيل جلسات تدريب تكرارية مباشرة من علامة تبويب **Training** في CrewAI AMP. تستخدم المنصة **وضع التدريب التلقائي** — حيث تتولى العملية التكرارية تلقائياً، على عكس تدريب CLI الذي يتطلب ملاحظات بشرية تفاعلية لكل تكرار.
بعد اكتمال التدريب، يقوم CrewAI بتقييم مخرجات الوكلاء ودمج الملاحظات في اقتراحات قابلة للتنفيذ لكل وكيل. يتم بعد ذلك تطبيق هذه الاقتراحات على تشغيلات الطاقم المستقبلية لتحسين جودة المخرجات.
<Tip>
للحصول على تفاصيل حول كيفية عمل تدريب CrewAI، راجع صفحة [مفاهيم التدريب](/ar/concepts/training).
</Tip>
## المتطلبات الأساسية
<CardGroup cols={2}>
<Card title="نشر نشط" icon="rocket">
تحتاج إلى حساب CrewAI AMP مع نشر نشط في حالة **Ready** (نوع Crew).
</Card>
<Card title="صلاحية التشغيل" icon="key">
يجب أن يكون لحسابك صلاحية تشغيل للنشر الذي تريد تدريبه.
</Card>
</CardGroup>
## كيفية تدريب طاقم
<Steps>
<Step title="افتح علامة تبويب Training">
انتقل إلى **Deployments**، انقر على نشرك، ثم اختر علامة تبويب **Training**.
</Step>
<Step title="أدخل اسم التدريب">
قدم **Training Name** — سيصبح هذا اسم ملف `.pkl` المستخدم لتخزين نتائج التدريب. على سبيل المثال، "Expert Mode Training" ينتج `expert_mode_training.pkl`.
</Step>
<Step title="املأ مدخلات الطاقم">
أدخل حقول إدخال الطاقم. هذه هي نفس المدخلات التي ستقدمها للتشغيل العادي — يتم تحميلها ديناميكياً بناءً على تكوين طاقمك.
</Step>
<Step title="ابدأ التدريب">
انقر على **Train Crew**. يتغير الزر إلى "Training..." مع مؤشر دوران أثناء تشغيل العملية.
خلف الكواليس:
- يتم إنشاء سجل تدريب للنشر الخاص بك
- تستدعي المنصة نقطة نهاية التدريب التلقائي للنشر
- يقوم الطاقم بتشغيل تكراراته تلقائياً — لا حاجة لملاحظات يدوية
</Step>
<Step title="راقب التقدم">
تعرض لوحة **Current Training Status**:
- **Status** — الحالة الحالية لجلسة التدريب
- **Nº Iterations** — عدد تكرارات التدريب المُهيأة
- **Filename** — ملف `.pkl` الذي يتم إنشاؤه
- **Started At** — وقت بدء التدريب
- **Training Inputs** — المدخلات التي قدمتها
</Step>
</Steps>
## فهم نتائج التدريب
بمجرد اكتمال التدريب، سترى بطاقات نتائج لكل وكيل تحتوي على المعلومات التالية:
- **Agent Role** — اسم/دور الوكيل في طاقمك
- **Final Quality** — درجة من 0 إلى 10 تقيّم جودة مخرجات الوكيل
- **Final Summary** — ملخص لأداء الوكيل أثناء التدريب
في بطاقة نتائج أي وكيل، انقر على زر **Edit** بجوار الاقتراحات.
</Step>
<Step title="عدّل الاقتراحات">
حدّث نص الاقتراحات ليعكس التحسينات التي تريدها بشكل أفضل.
</Step>
<Step title="احفظ التغييرات">
انقر على **Save**. تتم مزامنة الاقتراحات المُعدّلة مع النشر وتُستخدم في جميع التشغيلات المستقبلية.
</Step>
</Steps>
## استخدام بيانات التدريب
لتطبيق نتائج التدريب على طاقمك:
1. لاحظ **Training Filename** (ملف `.pkl`) من جلسة التدريب المكتملة.
2. حدد اسم الملف هذا في تكوين kickoff أو التشغيل الخاص بنشرك.
3. يقوم الطاقم تلقائياً بتحميل ملف التدريب وتطبيق الاقتراحات المخزنة على كل وكيل.
هذا يعني أن الوكلاء يستفيدون من الملاحظات المُنشأة أثناء التدريب في كل تشغيل لاحق.
## التدريبات السابقة
يعرض الجزء السفلي من علامة تبويب Training **سجل جميع جلسات التدريب السابقة** للنشر. استخدم هذا لمراجعة التدريبات السابقة، ومقارنة النتائج، أو اختيار ملف تدريب مختلف للاستخدام.
## معالجة الأخطاء
إذا فشل تشغيل التدريب، تعرض لوحة الحالة حالة خطأ مع رسالة تصف ما حدث خطأ.
الأسباب الشائعة لفشل التدريب:
- **لم يتم تحديث وقت تشغيل النشر** — تأكد من أن نشرك يعمل بأحدث إصدار
- **أخطاء تنفيذ الطاقم** — مشاكل في منطق مهام الطاقم أو تكوين الوكيل
- **مشاكل الشبكة** — مشاكل الاتصال بين المنصة والنشر
## القيود
<Info>
ضع هذه القيود في الاعتبار عند التخطيط لسير عمل التدريب الخاص بك:
- **تدريب نشط واحد في كل مرة** لكل نشر — انتظر حتى ينتهي التشغيل الحالي قبل بدء آخر
- **وضع التدريب التلقائي فقط** — لا تدعم المنصة الملاحظات التفاعلية لكل تكرار مثل CLI
- **بيانات التدريب خاصة بالنشر** — ترتبط نتائج التدريب بمثيل وإصدار النشر المحدد
description: "أتمتة سير عمل CrewAI AMP باستخدام webhooks مع منصات مثل ActivePieces وZapier وMake.com"
icon: "webhook"
mode: "wide"
---
يتيح لك CrewAI AMP أتمتة سير عملك باستخدام webhooks. ستوجهك هذه المقالة خلال عملية إعداد واستخدام webhooks لبدء تنفيذ طاقمك، مع التركيز على التكامل مع ActivePieces، وهي منصة أتمتة سير العمل مشابهة لـ Zapier وMake.com.
## إعداد Webhooks
<Steps>
<Step title="الوصول إلى واجهة البدء">
- انتقل إلى لوحة تحكم CrewAI AMP
- ابحث عن قسم `/kickoff`، الذي يُستخدم لبدء تنفيذ الطاقم
في قسم محتوى JSON، ستحتاج إلى تقديم المعلومات التالية:
- **inputs**: كائن JSON يحتوي على:
- `company`: اسم الشركة (مثال: "tesla")
- `product_name`: اسم المنتج (مثال: "crewai")
- `form_response`: نوع الاستجابة (مثال: "financial")
- `icp_description`: وصف موجز لملف العميل المثالي
- `product_description`: وصف قصير للمنتج
- `taskWebhookUrl`، `stepWebhookUrl`، `crewWebhookUrl`: عناوين URL لنقاط نهاية webhook المختلفة (ActivePieces أو Zapier أو Make.com أو منصة أخرى متوافقة)
</Step>
<Step title="التكامل مع ActivePieces">
في هذا المثال سنستخدم ActivePieces. يمكنك استخدام منصات أخرى مثل Zapier وMake.com
**ملاحظة:** أي كائن `meta` مُقدم في طلب البدء الخاص بك سيتم تضمينه في جميع حمولات webhook، مما يتيح لك تتبع الطلبات والحفاظ على السياق عبر دورة حياة تنفيذ الطاقم بالكامل.
<Tabs>
<Tab title="Step Webhook">
`stepWebhookUrl` - رد نداء يتم تنفيذه عند كل فكرة داخلية للوكيل
```json
{
"prompt": "Research the financial industry for potential AI solutions",
"thought": "I need to conduct preliminary research on the financial industry",
"tool": "research_tool",
"tool_input": "financial industry AI solutions",
"result": "**Preliminary Research Report on the Financial Industry for crewai Enterprise Solution**\n1. Industry Overview and Trends\nThe financial industry in ....\nConclusion:\nThe financial industry presents a fertile ground for implementing AI solutions like crewai, particularly in areas such as digital customer engagement, risk management, and regulatory compliance. Further engagement with the lead is recommended to better tailor the crewai solution to their specific needs and scale.",
`taskWebhookUrl` - رد نداء يتم تنفيذه عند انتهاء كل مهمة
```json
{
"description": "Using the information gathered from the lead's data, conduct preliminary research on the lead's industry, company background, and potential use cases for crewai. Focus on finding relevant data that can aid in scoring the lead and planning a strategy to pitch them crewai.",
"name": "Industry Research Task",
"expected_output": "Detailed research report on the financial industry",
"summary": "The financial industry presents a fertile ground for implementing AI solutions like crewai, particularly in areas such as digital customer engagement, risk management, and regulatory compliance. Further engagement with the lead is recommended to better tailor the crewai solution to their specific needs and scale.",
"agent": "Research Agent",
"output": "**Preliminary Research Report on the Financial Industry for crewai Enterprise Solution**\n1. Industry Overview and Trends\nThe financial industry in ....\nConclusion:\nThe financial industry presents a fertile ground for implementing AI solutions like crewai, particularly in areas such as digital customer engagement, risk management, and regulatory compliance.",
"result": "**Final Analysis Report**\n\nLead Score: Customer service enhancement and compliance are particularly relevant.\n\nTalking Points:\n- Highlight how crewai's AI solutions can transform customer service\n- Discuss crewai's potential for sustainability goals\n- Emphasize compliance capabilities\n- Stress adaptability for various operation scales",
"result_json": {
"lead_score": "Customer service enhancement, and compliance are particularly relevant.",
"talking_points": [
"Highlight how crewai's AI solutions can transform customer service with automated, personalized experiences and 24/7 support, improving both customer satisfaction and operational efficiency.",
"Discuss crewai's potential to help the institution achieve its sustainability goals through better data analysis and decision-making, contributing to responsible investing and green initiatives.",
"Emphasize crewai's ability to enhance compliance with evolving regulations through efficient data processing and reporting, reducing the risk of non-compliance penalties.",
"Stress the adaptability of crewai to support both extensive multinational operations and smaller, targeted projects, ensuring the solution grows with the institution's needs."
- تأكد من أن مدخلات CrewAI AMP مربوطة بشكل صحيح من رسالة Slack.
- اختبر Zap الخاص بك جيدًا قبل تفعيله لاكتشاف أي مشاكل محتملة.
- فكر في إضافة خطوات معالجة الأخطاء لإدارة حالات الفشل المحتملة في سير العمل.
باتباع هذه الخطوات، ستكون قد أعددت بنجاح مشغلات Zapier لـ CrewAI AMP، مما يتيح سير عمل آلي يتم تشغيله بواسطة رسائل Slack وينتج عنه إشعارات بالبريد الإلكتروني مع مخرجات CrewAI AMP.
description: "تنسيق مهام الفريق والمشاريع مع تكامل Asana لـ CrewAI."
icon: "circle"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة المهام والمشاريع وتنسيق الفريق عبر Asana. أنشئ المهام وحدّث حالة المشروع وأدر التعيينات وبسّط سير عمل فريقك مع الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Asana، تأكد من أن لديك:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك نشط
- حساب Asana مع الأذونات المناسبة
- ربط حساب Asana الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Asana
### 1. ربط حساب Asana الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Asana** في قسم تكاملات المصادقة
3. انقر على **ربط** وأكمل تدفق OAuth
4. امنح الأذونات اللازمة لإدارة المهام والمشاريع
5. انسخ رمز Enterprise الخاص بك من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز Enterprise الخاص بك.
- `task` (string, مطلوب): معرف المهمة - معرف المهمة التي سيُضاف إليها التعليق. سيُنسب التعليق للمستخدم المصادق عليه حاليًا.
- `text` (string, مطلوب): النص (مثال: "This is a comment.").
</Accordion>
<Accordion title="asana/create_project">
**الوصف:** إنشاء مشروع في Asana.
**المعاملات:**
- `name` (string, مطلوب): الاسم (مثال: "Stuff to buy").
- `workspace` (string, مطلوب): مساحة العمل - استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار مساحة العمل لإنشاء المشاريع فيها. الافتراضي هو أول مساحة عمل للمستخدم إذا تُرك فارغًا.
- `team` (string, اختياري): الفريق - استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار الفريق لمشاركة هذا المشروع معه. الافتراضي هو أول فريق للمستخدم إذا تُرك فارغًا.
- `notes` (string, اختياري): ملاحظات (مثال: "These are things we need to purchase.").
</Accordion>
<Accordion title="asana/get_projects">
**الوصف:** الحصول على قائمة المشاريع في Asana.
**المعاملات:**
- `archived` (string, اختياري): مؤرشف - اختر "true" لعرض المشاريع المؤرشفة، "false" لعرض المشاريع النشطة فقط، أو "default" لعرض كليهما.
- الخيارات: `default`, `true`, `false`
</Accordion>
<Accordion title="asana/get_project_by_id">
**الوصف:** الحصول على مشروع بواسطة المعرف في Asana.
- `name` (string, مطلوب): الاسم (مثال: "Task Name").
- `workspace` (string, اختياري): مساحة العمل - استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار مساحة العمل لإنشاء المهام فيها. الافتراضي هو أول مساحة عمل للمستخدم إذا تُرك فارغًا.
- `project` (string, اختياري): المشروع - استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار المشروع لإنشاء هذه المهمة فيه.
- `notes` (string, اختياري): ملاحظات.
- `dueOnDate` (string, اختياري): تاريخ الاستحقاق - التاريخ الذي تستحق فيه هذه المهمة. لا يمكن استخدامه مع Due At. (مثال: "YYYY-MM-DD").
- `dueAtDate` (string, اختياري): الاستحقاق في - التاريخ والوقت (طابع زمني ISO) الذي تستحق فيه هذه المهمة. لا يمكن استخدامه مع Due On. (مثال: "2019-09-15T02:06:58.147Z").
- `assignee` (string, اختياري): المُكلف - معرف مستخدم Asana الذي سيتم تعيين هذه المهمة له. استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار المُكلف.
- `gid` (string, اختياري): معرف خارجي - معرف من تطبيقك لربط هذه المهمة به. يمكنك استخدام هذا المعرف لمزامنة التحديثات لهذه المهمة لاحقًا.
</Accordion>
<Accordion title="asana/update_task">
**الوصف:** تحديث مهمة في Asana.
**المعاملات:**
- `taskId` (string, مطلوب): معرف المهمة - معرف المهمة التي سيتم تحديثها.
- `completeStatus` (string, اختياري): حالة الإكمال.
- الخيارات: `true`, `false`
- `name` (string, اختياري): الاسم (مثال: "Task Name").
- `notes` (string, اختياري): ملاحظات.
- `dueOnDate` (string, اختياري): تاريخ الاستحقاق - التاريخ الذي تستحق فيه هذه المهمة. لا يمكن استخدامه مع Due At. (مثال: "YYYY-MM-DD").
- `dueAtDate` (string, اختياري): الاستحقاق في - التاريخ والوقت (طابع زمني ISO) الذي تستحق فيه هذه المهمة. لا يمكن استخدامه مع Due On. (مثال: "2019-09-15T02:06:58.147Z").
- `assignee` (string, اختياري): المُكلف - معرف مستخدم Asana الذي سيتم تعيين هذه المهمة له.
- `gid` (string, اختياري): معرف خارجي - معرف من تطبيقك لربط هذه المهمة به.
</Accordion>
<Accordion title="asana/get_tasks">
**الوصف:** الحصول على قائمة المهام في Asana.
**المعاملات:**
- `workspace` (string, اختياري): مساحة العمل - معرف مساحة العمل لتصفية المهام عليها.
- `project` (string, اختياري): المشروع - معرف المشروع لتصفية المهام عليه.
- `completedSince` (string, اختياري): مكتملة منذ - إرجاع المهام غير المكتملة فقط أو التي اكتملت منذ هذا الوقت (طابع زمني ISO أو Unix). (مثال: "2014-04-25T16:15:47-04:00").
</Accordion>
<Accordion title="asana/get_tasks_by_id">
**الوصف:** الحصول على قائمة المهام بواسطة المعرف في Asana.
**المعاملات:**
- `taskId` (string, مطلوب): معرف المهمة.
</Accordion>
<Accordion title="asana/get_task_by_external_id">
**الوصف:** الحصول على مهمة بواسطة المعرف الخارجي في Asana.
**المعاملات:**
- `gid` (string, مطلوب): المعرف الخارجي - المعرف الذي ترتبط أو تتزامن به هذه المهمة، من تطبيقك.
</Accordion>
<Accordion title="asana/add_task_to_section">
**الوصف:** إضافة مهمة إلى قسم في Asana.
**المعاملات:**
- `sectionId` (string, مطلوب): معرف القسم - معرف القسم لإضافة هذه المهمة إليه.
- `beforeTaskId` (string, اختياري): معرف المهمة السابقة - معرف مهمة في هذا القسم سيتم إدراج هذه المهمة قبلها. لا يمكن استخدامه مع After Task ID. (مثال: "1204619611402340").
- `afterTaskId` (string, اختياري): معرف المهمة التالية - معرف مهمة في هذا القسم سيتم إدراج هذه المهمة بعدها. لا يمكن استخدامه مع Before Task ID. (مثال: "1204619611402340").
</Accordion>
<Accordion title="asana/get_teams">
**الوصف:** الحصول على قائمة الفرق في Asana.
**المعاملات:**
- `workspace` (string, مطلوب): مساحة العمل - إرجاع الفرق في مساحة العمل هذه المرئية للمستخدم المصرح له.
</Accordion>
<Accordion title="asana/get_workspaces">
**الوصف:** الحصول على قائمة مساحات العمل في Asana.
**المعاملات:** لا توجد معاملات مطلوبة.
</Accordion>
</AccordionGroup>
## أمثلة الاستخدام
### إعداد وكيل Asana الأساسي
```python
from crewai import Agent, Task, Crew
# Create an agent with Asana capabilities
asana_agent = Agent(
role="Project Manager",
goal="Manage tasks and projects in Asana efficiently",
backstory="An AI assistant specialized in project management and task coordination.",
apps=['asana'] # All Asana actions will be available
)
# Task to create a new project
create_project_task = Task(
description="Create a new project called 'Q1 Marketing Campaign' in the Marketing workspace",
agent=asana_agent,
expected_output="Confirmation that the project was created successfully with project ID"
)
# Run the task
crew = Crew(
agents=[asana_agent],
tasks=[create_project_task]
)
crew.kickoff()
```
### تصفية أدوات Asana محددة
```python
from crewai import Agent, Task, Crew
# Create agent with specific Asana actions only
task_manager_agent = Agent(
role="Task Manager",
goal="Create and manage tasks efficiently",
backstory="An AI assistant that focuses on task creation and management.",
apps=[
'asana/create_task',
'asana/update_task',
'asana/get_tasks'
] # Specific Asana actions
)
# Task to create and assign a task
task_management = Task(
description="Create a task called 'Review quarterly reports' and assign it to the appropriate team member",
agent=task_manager_agent,
expected_output="Task created and assigned successfully"
)
crew = Crew(
agents=[task_manager_agent],
tasks=[task_management]
)
crew.kickoff()
```
### إدارة المشاريع المتقدمة
```python
from crewai import Agent, Task, Crew
project_coordinator = Agent(
role="Project Coordinator",
goal="Coordinate project activities and track progress",
backstory="An experienced project coordinator who ensures projects run smoothly.",
description: "تخزين الملفات وإدارة المستندات مع تكامل Box لـ CrewAI."
icon: "box"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة الملفات والمجلدات والمستندات عبر Box. ارفع الملفات، ونظّم هياكل المجلدات، وابحث في المحتوى، وبسّط إدارة مستندات فريقك باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Box، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Box بالصلاحيات المناسبة
- ربط حساب Box الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Box
### 1. ربط حساب Box الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Box** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة الملفات والمجلدات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
- `file` (string, مطلوب): عنوان URL للملف - يجب أن يكون حجم الملفات أقل من 50 ميجابايت. (مثال: "https://picsum.photos/200/300").
</Accordion>
<Accordion title="box/save_file_from_object">
**الوصف:** حفظ ملف في Box.
**المعاملات:**
- `file` (string, مطلوب): الملف - يقبل كائن ملف يحتوي على بيانات الملف. يجب أن يكون حجم الملفات أقل من 50 ميجابايت.
- `fileName` (string, مطلوب): اسم الملف (مثال: "qwerty.png").
- `folder` (string, اختياري): المجلد - استخدم إعدادات سير عمل بوابة الاتصال للسماح للمستخدمين باختيار وجهة مجلد الملف. يستخدم المجلد الجذري افتراضياً إذا تُرك فارغاً.
</Accordion>
<Accordion title="box/get_file_by_id">
**الوصف:** الحصول على ملف بواسطة المعرّف في Box.
**المعاملات:**
- `fileId` (string, مطلوب): معرّف الملف - المعرّف الفريد الذي يمثل ملفاً. (مثال: "12345").
</Accordion>
<Accordion title="box/list_files">
**الوصف:** عرض قائمة الملفات في Box.
**المعاملات:**
- `folderId` (string, مطلوب): معرّف المجلد - المعرّف الفريد الذي يمثل مجلداً. (مثال: "0").
- `filterFormula` (object, اختياري): فلتر بصيغة التعبير العادي المنفصل - OR لمجموعات AND من شروط فردية.
```json
{
"operator": "OR",
"conditions": [
{
"operator": "AND",
"conditions": [
{
"field": "direction",
"operator": "$stringExactlyMatches",
"value": "ASC"
}
]
}
]
}
```
</Accordion>
<Accordion title="box/create_folder">
**الوصف:** إنشاء مجلد في Box.
**المعاملات:**
- `folderName` (string, مطلوب): الاسم - اسم المجلد الجديد. (مثال: "New Folder").
- `folderParent` (object, مطلوب): المجلد الأصلي - المجلد الأصلي الذي سيُنشأ فيه المجلد الجديد.
```json
{
"id": "123456"
}
```
</Accordion>
<Accordion title="box/move_folder">
**الوصف:** نقل مجلد في Box.
**المعاملات:**
- `folderId` (string, مطلوب): معرّف المجلد - المعرّف الفريد الذي يمثل مجلداً. (مثال: "0").
- `folderName` (string, مطلوب): الاسم - اسم المجلد. (مثال: "New Folder").
- `folderParent` (object, مطلوب): المجلد الأصلي - وجهة المجلد الأصلي الجديد.
```json
{
"id": "123456"
}
```
</Accordion>
<Accordion title="box/get_folder_by_id">
**الوصف:** الحصول على مجلد بواسطة المعرّف في Box.
**المعاملات:**
- `folderId` (string, مطلوب): معرّف المجلد - المعرّف الفريد الذي يمثل مجلداً. (مثال: "0").
description: "إدارة المهام والإنتاجية مع تكامل ClickUp لـ CrewAI."
icon: "list-check"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة المهام والمشاريع وسير عمل الإنتاجية عبر ClickUp. أنشئ المهام وحدّثها، ونظّم المشاريع، وأدر تعيينات الفريق، وبسّط إدارة إنتاجيتك باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل ClickUp، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب ClickUp بالصلاحيات المناسبة
- ربط حساب ClickUp الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل ClickUp
### 1. ربط حساب ClickUp الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **ClickUp** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة المهام والمشاريع
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
**الوصف:** الحصول على المهام في قائمة محددة في ClickUp.
**المعاملات:**
- `listId` (string, مطلوب): القائمة - اختر قائمة للحصول على المهام منها. استخدم إعدادات المستخدم في بوابة الاتصال للسماح للمستخدمين باختيار قائمة ClickUp.
- `taskFilterFormula` (string, اختياري): البحث عن المهام التي تطابق الفلاتر المحددة. مثال: name=task1.
</Accordion>
<Accordion title="clickup/create_task">
**الوصف:** إنشاء مهمة في ClickUp.
**المعاملات:**
- `listId` (string, مطلوب): القائمة - اختر قائمة لإنشاء هذه المهمة فيها.
- `name` (string, مطلوب): الاسم - اسم المهمة.
- `description` (string, اختياري): الوصف - وصف المهمة.
- `status` (string, اختياري): الحالة - اختر حالة لهذه المهمة.
- `assignees` (string, اختياري): المكلّفون - اختر عضواً (أو مصفوفة من معرّفات الأعضاء) ليتم تعيينهم لهذه المهمة.
- `dueDate` (string, اختياري): تاريخ الاستحقاق - حدد تاريخ استحقاق لهذه المهمة.
- `additionalFields` (string, اختياري): حقول إضافية - حدد حقولاً إضافية لتضمينها في هذه المهمة بصيغة JSON.
</Accordion>
<Accordion title="clickup/update_task">
**الوصف:** تحديث مهمة في ClickUp.
**المعاملات:**
- `taskId` (string, مطلوب): معرّف المهمة - معرّف المهمة المراد تحديثها.
- `listId` (string, مطلوب): القائمة - اختر قائمة لإنشاء هذه المهمة فيها.
- `name` (string, اختياري): الاسم - اسم المهمة.
- `description` (string, اختياري): الوصف - وصف المهمة.
- `status` (string, اختياري): الحالة - اختر حالة لهذه المهمة.
- `assignees` (string, اختياري): المكلّفون - اختر عضواً (أو مصفوفة من معرّفات الأعضاء) ليتم تعيينهم لهذه المهمة.
- `dueDate` (string, اختياري): تاريخ الاستحقاق - حدد تاريخ استحقاق لهذه المهمة.
- `additionalFields` (string, اختياري): حقول إضافية - حدد حقولاً إضافية لتضمينها في هذه المهمة بصيغة JSON.
</Accordion>
<Accordion title="clickup/delete_task">
**الوصف:** حذف مهمة في ClickUp.
**المعاملات:**
- `taskId` (string, مطلوب): معرّف المهمة - معرّف المهمة المراد حذفها.
</Accordion>
<Accordion title="clickup/get_list">
**الوصف:** الحصول على معلومات القائمة في ClickUp.
**المعاملات:**
- `spaceId` (string, مطلوب): معرّف المساحة - معرّف المساحة التي تحتوي على القوائم.
description: "اربط وكلاء CrewAI بـ Databricks Genie وSQL وUnity Catalog Functions وVector Search عبر خوادم MCP المُدارة من Databricks."
icon: "layer-group"
mode: "wide"
---
## نظرة عامة
اربط وكلاء CrewAI مباشرةً بمساحة عمل Databricks الخاصة بك عبر [خوادم MCP المُدارة من Databricks](https://docs.databricks.com/aws/en/generative-ai/mcp/managed-mcp). يتيح تكامل Databricks لوكلائك طرح أسئلة بلغة طبيعية باستخدام **Genie**، وتنفيذ **SQL** خاضع للحوكمة، واستدعاء **Unity Catalog Functions**، واسترجاع المستندات باستخدام **Vector Search** — كل ذلك دون كتابة أو استضافة أي كود موصِّل، مع تطبيق أذونات Unity Catalog في كل استدعاء.
في الخلفية، يُعدّ تكامل Databricks غلافًا مُدارًا حول دعم [خوادم MCP المخصصة](/ar/enterprise/guides/custom-mcp-server) في CrewAI. تكشف Databricks عن كل قدرة كنقطة نهاية [Model Context Protocol](https://modelcontextprotocol.io/) خاصة بها، ويتصل بها CrewAI بأمان نيابةً عنك. ولأن كل خادم يُضاف بشكل منفصل، يمكنك تفعيل القدرات التي تحتاجها فرقك (crews) بالضبط.
## القدرات الرئيسية
<CardGroup cols={2}>
<Card title="Genie" icon="comments">
اطرح أسئلة بلغة طبيعية واحصل على إجابات مستندة إلى بياناتك باستخدام [Genie](https://docs.databricks.com/aws/en/genie/)، الذي يستعلم من Genie Spaces وUnity Catalog ويوفّر روابط تعود إلى واجهة Databricks.
</Card>
<Card title="Databricks SQL" icon="database">
نفّذ SQL خاضعًا للحوكمة على مستودعات Databricks لديك للاستعلام عن البيانات وتحويلها وإنشاء خطوط أنابيب البيانات مباشرةً من وكلائك.
استرجع المستندات ذات الصلة لسير عمل RAG والمعرفة من فهارس [Mosaic AI Vector Search](https://docs.databricks.com/aws/en/generative-ai/vector-search) باستخدام التشابه الدلالي.
</Card>
</CardGroup>
تعمل جميع الخوادم خلف Unity AI Gateway وتطبّق ضوابط الوصول في Unity Catalog، بحيث لا يرى وكلاؤك سوى البيانات والأدوات المصرَّح لهم باستخدامها.
## المتطلبات المسبقة
قبل استخدام تكامل Databricks، تأكّد من توفّر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) باشتراك نشط
- مساحة عمل Databricks تحتوي على القدرات التي تريد كشفها (Genie Spaces، مستودعات SQL، دوال Unity Catalog، أو فهارس Vector Search)
- [امتيازات Unity Catalog](https://docs.databricks.com/aws/en/data-governance/unity-catalog) المناسبة على الكائنات الأساسية
- اسم مضيف مساحة عمل Databricks الخاص بك (مثال: `your-workspace.cloud.databricks.com`)
## خوادم MCP المُدارة من Databricks
تنشر Databricks خادم MCP مُدارًا منفصلًا لكل قدرة. يكشف CrewAI عنها كاتصالات فردية، يُهيَّأ كل منها باستخدام مضيف مساحة العمل ومعرّفات Unity Catalog ذات الصلة. تتبع نقاط النهاية الأنماط التالية:
| الخادم | الوظيفة | نمط عنوان MCP |
|--------|---------|---------------|
| **Genie** | أسئلة وأجوبة بلغة طبيعية على Genie Space | `https://<workspace-hostname>/api/2.0/mcp/genie/{genie_space_id}` |
| **Databricks SQL** | تنفيذ SQL على مستودعاتك | `https://<workspace-hostname>/api/2.0/mcp/sql` |
لا حاجة لإنشاء عناوين URL هذه يدويًا — يُنشئ CrewAI كل نقطة نهاية من مضيف مساحة العمل والمعرّفات (Genie Space ID، أو catalog/schema) التي تقدّمها عند تهيئة الاتصال. للاطّلاع على المواصفات الكاملة وأحدث تفاصيل نقاط النهاية، راجع [وثائق MCP المُدارة من Databricks](https://docs.databricks.com/aws/en/generative-ai/mcp/managed-mcp).
</Note>
## ربط Databricks في CrewAI AMP
<Frame>
<img src="/images/enterprise/databricks-configure.png" alt="تهيئة خادم MCP مُدار من Databricks في CrewAI AMP" />
</Frame>
تظهر كل قدرة من قدرات Databricks — **Databricks Genie** و**Databricks SQL** و**Databricks Unity Catalog Functions** و**Databricks Vector Search** — كخادم MCP خاص بها ضمن مجموعة Databricks في صفحة **Tools & Integrations**. هيّئ ما تحتاجه:
<Steps>
<Step title="افتح Tools & Integrations">
انتقل إلى **Tools & Integrations** في الشريط الجانبي الأيسر في CrewAI AMP وحدِّد مجموعة **Databricks** في قائمة Connections. سترى خوادم Genie وSQL وUnity Catalog Functions وVector Search مُدرجة أسفلها.
</Step>
<Step title="هيّئ خادمًا">
انقر على **Configure** بجوار القدرة التي تريد تفعيلها وقدّم تفاصيل الاتصال الخاصة بها:
- **Workspace Host** — اسم مضيف مساحة عمل Databricks الخاص بك (مثال: `my-workspace.cloud.databricks.com`).
- **Genie** — **Genie Space ID** المراد الاستعلام عنه.
- **Unity Catalog Functions** — الـ **catalog** والـ **schema** اللذان يحتويان على دوالك.
- **Vector Search** — الـ **catalog** والـ **schema** اللذان يحتويان على الفهرس.
- **Databricks SQL** — لا توجد معرّفات إضافية؛ تُنفَّذ الاستعلامات على مستودعات SQL في مساحة عملك.
</Step>
<Step title="اختر طريقة المصادقة">
اختر كيف يصادق CrewAI على Databricks. يُوصى باستخدام **OAuth**.
- **Use OAuth** — اتصل بأمان باستخدام OAuth 2.0. يصادق كل مستخدم على حدة، وتُصدر Databricks رموزًا (tokens) محدّدة النطاق للقدرة (`genie` أو `sql` أو `unity-catalog` أو `vector-search`). يتولّى CrewAI تدفّق التفويض ويُجدّد الرموز تلقائيًا.
- **Use personal access token** — صادِق باستخدام [رمز وصول شخصي من Databricks](https://docs.databricks.com/aws/en/dev-tools/auth/pat). استخدم هوية بأقل الامتيازات للحدّ من التعرّض.
</Step>
<Step title="صادِق">
أكمل المصادقة. بمجرد الاتصال، تصبح أدوات الخادم متاحة لفرقك. كرّر العملية لأي قدرات Databricks أخرى تريد تفعيلها.
</Step>
</Steps>
<Tip>
لأن كل قدرة هي اتصال منفصل، يمكنك المزج والمطابقة — على سبيل المثال، فعّل Genie وVector Search لفريق بحث، مع حجز SQL وUnity Catalog Functions لفريق هندسة البيانات. تتيح لك إعدادات الرؤية (Visibility) التحكّم في أعضاء الفريق الذين يمكنهم استخدام كل منها.
</Tip>
## استخدام أدوات Databricks في فرقك
بمجرد الاتصال، تظهر الأدوات التي يكشفها كل خادم MCP جنبًا إلى جنب مع الاتصالات المدمجة في صفحة **Tools & Integrations**. يمكنك:
- **إسناد الأدوات إلى الوكلاء** في فرقك تمامًا مثل أي أداة أخرى في CrewAI.
- **إدارة الرؤية** للتحكّم في أعضاء الفريق الذين يمكنهم استخدام كل اتصال.
- **تعديل أو إزالة** أي اتصال في أي وقت من قائمة Connections.
يمكن لوكلائك الآن طلب إجابات مستندة من Genie، وتنفيذ SQL على مستودعاتك، واستدعاء دوال Unity Catalog، والبحث في فهارس Vector Search — مع تدفّق النتائج تلقائيًا إلى استدلالهم.
<Warning>
تطبّق Databricks الحوكمة عبر Unity Catalog وUnity AI Gateway: لا يمكن للمستخدم اكتشاف الأدوات واستدعاؤها إلا تلك المصرَّح بها لهوية مساحة عمله. إذا فشل استدعاء أداة، فتأكّد من أن المستخدم المتصل (أو هوية الرمز) يمتلك امتيازات Unity Catalog المطلوبة على Genie Space أو المستودع أو الدالة أو الفهرس. تُنفَّذ بعض استعلامات Genie وSQL بشكل غير متزامن وقد تستغرق لحظة لإرجاع النتائج.
</Warning>
## مزيد من المعلومات
<CardGroup cols={2}>
<Card title="خوادم MCP المُدارة من Databricks" icon="layer-group" href="https://docs.databricks.com/aws/en/generative-ai/mcp/managed-mcp">
description: "إدارة المستودعات والمشكلات مع تكامل GitHub لـ CrewAI."
icon: "github"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة المستودعات والمشكلات والإصدارات عبر GitHub. أنشئ المشكلات وحدّثها، وأدر الإصدارات، وتتبع تطور المشاريع، وبسّط سير عمل تطوير البرمجيات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل GitHub، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب GitHub بصلاحيات المستودع المناسبة
- ربط حساب GitHub الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل GitHub
### 1. ربط حساب GitHub الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **GitHub** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة المستودعات والمشكلات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
- `assignees` (string, اختياري): المكلّفون - حدد اسم (أسماء) تسجيل الدخول في GitHub للمكلّفين كمصفوفة من السلاسل النصية لهذه المشكلة. (مثال: `["octocat"]`).
</Accordion>
<Accordion title="github/update_issue">
**الوصف:** تحديث مشكلة في GitHub.
**المعاملات:**
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذه المشكلة. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذه المشكلة.
- `issue_number` (string, مطلوب): رقم المشكلة - حدد رقم المشكلة المراد تحديثها.
- `title` (string, مطلوب): عنوان المشكلة - حدد عنوان المشكلة المراد تحديثها.
- `assignees` (string, اختياري): المكلّفون - حدد اسم (أسماء) تسجيل الدخول في GitHub للمكلّفين كمصفوفة من السلاسل النصية لهذه المشكلة. (مثال: `["octocat"]`).
- `state` (string, اختياري): الحالة - حدد الحالة المحدّثة للمشكلة.
- الخيارات: `open`, `closed`
</Accordion>
<Accordion title="github/get_issue_by_number">
**الوصف:** الحصول على مشكلة بواسطة الرقم في GitHub.
**المعاملات:**
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذه المشكلة. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذه المشكلة.
- `issue_number` (string, مطلوب): رقم المشكلة - حدد رقم المشكلة المراد جلبها.
</Accordion>
<Accordion title="github/lock_issue">
**الوصف:** قفل مشكلة في GitHub.
**المعاملات:**
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذه المشكلة. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذه المشكلة.
- `issue_number` (string, مطلوب): رقم المشكلة - حدد رقم المشكلة المراد قفلها.
- `lock_reason` (string, مطلوب): سبب القفل - حدد سبب قفل محادثة المشكلة أو طلب السحب.
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذا الإصدار. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذا الإصدار.
- `tag_name` (string, مطلوب): الاسم - حدد اسم وسم الإصدار المراد إنشاؤه. (مثال: "v1.0.0").
- `target_commitish` (string, اختياري): الهدف - حدد هدف الإصدار. يمكن أن يكون اسم فرع أو SHA لعملية إيداع. الافتراضي هو الفرع الرئيسي. (مثال: "master").
- `body` (string, اختياري): المحتوى - حدد وصفاً لهذا الإصدار.
- `draft` (string, اختياري): مسودة - حدد ما إذا كان الإصدار المُنشأ يجب أن يكون مسودة (غير منشور).
- الخيارات: `true`, `false`
- `prerelease` (string, اختياري): إصدار تجريبي - حدد ما إذا كان الإصدار المُنشأ يجب أن يكون إصداراً تجريبياً.
- الخيارات: `true`, `false`
- `discussion_category_name` (string, اختياري): اسم فئة المناقشة - إذا حُدد، يتم إنشاء مناقشة من الفئة المحددة وربطها بالإصدار.
- `generate_release_notes` (string, اختياري): ملاحظات الإصدار - حدد ما إذا كان يجب إنشاء ملاحظات الإصدار تلقائياً.
- الخيارات: `true`, `false`
</Accordion>
<Accordion title="github/update_release">
**الوصف:** تحديث إصدار في GitHub.
**المعاملات:**
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذا الإصدار. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذا الإصدار.
- `tag_name` (string, اختياري): الاسم - حدد اسم وسم الإصدار المراد تحديثه. (مثال: "v1.0.0").
- `target_commitish` (string, اختياري): الهدف - حدد هدف الإصدار. يمكن أن يكون اسم فرع أو SHA لعملية إيداع. الافتراضي هو الفرع الرئيسي. (مثال: "master").
- `body` (string, اختياري): المحتوى - حدد وصفاً لهذا الإصدار.
- `draft` (string, اختياري): مسودة - حدد ما إذا كان الإصدار يجب أن يكون مسودة (غير منشور).
- الخيارات: `true`, `false`
- `prerelease` (string, اختياري): إصدار تجريبي - حدد ما إذا كان الإصدار يجب أن يكون إصداراً تجريبياً.
- الخيارات: `true`, `false`
- `discussion_category_name` (string, اختياري): اسم فئة المناقشة - إذا حُدد، يتم إنشاء مناقشة من الفئة المحددة وربطها بالإصدار.
- `generate_release_notes` (string, اختياري): ملاحظات الإصدار - حدد ما إذا كان يجب إنشاء ملاحظات الإصدار تلقائياً.
- الخيارات: `true`, `false`
</Accordion>
<Accordion title="github/get_release_by_id">
**الوصف:** الحصول على إصدار بواسطة المعرّف في GitHub.
**المعاملات:**
- `owner` (string, مطلوب): المالك - حدد اسم مالك الحساب للمستودع المرتبط بهذا الإصدار. (مثال: "abc").
- `repo` (string, مطلوب): المستودع - حدد اسم المستودع المرتبط بهذا الإصدار.
description: "إدارة البريد الإلكتروني وجهات الاتصال مع تكامل Gmail لـ CrewAI."
icon: "envelope"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة رسائل البريد الإلكتروني وجهات الاتصال والمسودات عبر Gmail. أرسل رسائل البريد الإلكتروني، وابحث في الرسائل، وأدر جهات الاتصال، وأنشئ المسودات، وبسّط اتصالات البريد الإلكتروني باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Gmail، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Gmail بالصلاحيات المناسبة
- ربط حساب Gmail الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Gmail
### 1. ربط حساب Gmail الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Gmail** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة البريد الإلكتروني وجهات الاتصال
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة الأحداث والجداول الزمنية مع تكامل Google Calendar لـ CrewAI."
icon: "calendar"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة أحداث التقويم والجداول الزمنية والتوفر عبر Google Calendar. أنشئ الأحداث وحدّثها، وأدر الحضور، وتحقق من التوفر، وبسّط سير عمل الجدولة باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Google Calendar، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Calendar
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Google Calendar
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Calendar** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى التقويم
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة جهات الاتصال والدليل مع تكامل Google Contacts لـ CrewAI."
icon: "address-book"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة جهات الاتصال ومعلومات الدليل عبر Google Contacts. يمكنك الوصول إلى جهات الاتصال الشخصية، والبحث في أشخاص الدليل، وإنشاء معلومات الاتصال وتحديثها، وإدارة مجموعات جهات الاتصال باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Google Contacts، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Contacts
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Google Contacts
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Contacts** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى جهات الاتصال والدليل
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
- `pageSize` (integer, اختياري): عدد النتائج المراد إرجاعها. الحد الأدنى: 1، الحد الأقصى: 30
- `pageToken` (string, اختياري): رمز يحدد صفحة النتائج المراد إرجاعها.
- `sources` (array, اختياري): المصادر المراد البحث فيها. الخيارات: READ_SOURCE_TYPE_CONTACT, READ_SOURCE_TYPE_PROFILE. الافتراضي: READ_SOURCE_TYPE_CONTACT
**الوصف:** عرض قائمة الأشخاص في دليل المستخدم المصادق عليه.
**المعاملات:**
- `sources` (array, مطلوب): مصادر الدليل المراد البحث فيها. الخيارات: DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE, DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT. الافتراضي: DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE
- `pageSize` (integer, اختياري): عدد الأشخاص المراد إرجاعهم. الحد الأدنى: 1، الحد الأقصى: 1000
- `pageToken` (string, اختياري): رمز يحدد صفحة النتائج المراد إرجاعها.
description: "إنشاء المستندات وتحريرها مع تكامل Google Docs لـ CrewAI."
icon: "file-lines"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إنشاء وتحرير وإدارة مستندات Google Docs مع معالجة النصوص والتنسيق. أتمت إنشاء المستندات، وأدرج النصوص واستبدلها، وأدر نطاقات المحتوى، وبسّط سير عمل المستندات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Google Docs، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Docs
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Google Docs
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Docs** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى المستندات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description="In document 'your_document_id', insert the text 'Executive Summary: ' at the beginning, then replace all instances of 'TODO' with 'COMPLETED'.",
agent=text_editor,
expected_output="Document updated with new text inserted and TODO items replaced."
)
crew = Crew(
agents=[text_editor],
tasks=[edit_content_task]
)
crew.kickoff()
```
### عمليات المستندات المتقدمة
```python
from crewai import Agent, Task, Crew
# Create an agent for advanced document operations
document_formatter = Agent(
role="Document Formatter",
goal="Apply advanced formatting and structure to Google Docs",
backstory="An AI assistant that handles complex document formatting and organization.",
description="In document 'your_document_id', insert a page break at position 100, create a named range called 'Introduction' for characters 1-50, and apply batch formatting updates.",
agent=document_formatter,
expected_output="Document formatted with page break, named range, and styling applied."
)
crew = Crew(
agents=[document_formatter],
tasks=[format_doc_task]
)
crew.kickoff()
```
## استكشاف الأخطاء وإصلاحها
### المشاكل الشائعة
**أخطاء المصادقة**
- تأكد من أن حساب Google الخاص بك لديه الصلاحيات اللازمة للوصول إلى Google Docs.
- تحقق من أن اتصال OAuth يتضمن جميع النطاقات المطلوبة (`https://www.googleapis.com/auth/documents`).
**مشاكل معرّف المستند**
- تحقق جيداً من صحة معرّفات المستندات.
- تأكد من وجود المستند وإمكانية الوصول إليه من حسابك.
- يمكن العثور على معرّفات المستندات في عنوان URL لـ Google Docs.
**عمليات إدراج النص والنطاقات**
- عند استخدام `insert_text` أو `delete_content_range`، تأكد من صحة مواضع الفهرس.
- تذكر أن Google Docs يستخدم فهرسة قائمة على الصفر.
- يجب أن يحتوي المستند على محتوى في مواضع الفهرس المحددة.
**تنسيق طلبات التحديث الدفعي**
- عند استخدام `batch_update`، تأكد من صحة تنسيق مصفوفة `requests` وفقاً لتوثيق Google Docs API.
- تتطلب التحديثات المعقدة هياكل JSON محددة لكل نوع طلب.
**عمليات استبدال النص**
- لـ `replace_text`، تأكد من مطابقة معامل `containsText` تماماً للنص المراد استبداله.
- استخدم معامل `matchCase` للتحكم في حساسية حالة الأحرف.
description: "تخزين الملفات وإدارتها مع تكامل Google Drive لـ CrewAI."
icon: "google"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة الملفات والمجلدات عبر Google Drive. ارفع الملفات وحمّلها ونظّمها وشاركها، وأنشئ المجلدات، وبسّط سير عمل إدارة المستندات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Google Drive، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Drive
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Google Drive
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Drive** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة الملفات والمجلدات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "مزامنة بيانات جداول البيانات مع تكامل Google Sheets لـ CrewAI."
icon: "google"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة بيانات جداول البيانات عبر Google Sheets. اقرأ الصفوف، وأنشئ إدخالات جديدة، وحدّث البيانات الموجودة، وبسّط سير عمل إدارة البيانات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي. مثالي لتتبع البيانات وإعداد التقارير وإدارة البيانات التعاونية.
## المتطلبات الأساسية
قبل استخدام تكامل Google Sheets، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Sheets
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
- جداول بيانات بترويسات أعمدة مناسبة لعمليات البيانات
## إعداد تكامل Google Sheets
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Sheets** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى جداول البيانات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إنشاء العروض التقديمية وإدارتها مع تكامل Google Slides لـ CrewAI."
icon: "chart-bar"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إنشاء وتحرير وإدارة عروض Google Slides التقديمية. أنشئ العروض التقديمية، وحدّث المحتوى، واستورد البيانات من Google Sheets، وأدر الصفحات والصور المصغرة، وبسّط سير عمل العروض التقديمية باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Google Slides، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Google مع إمكانية الوصول إلى Google Slides
- ربط حساب Google الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Google Slides
### 1. ربط حساب Google الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Google Slides** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى العروض التقديمية وجداول البيانات وDrive
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "تتبع المشكلات وإدارة المشاريع مع تكامل Jira لـ CrewAI."
icon: "bug"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة المشكلات والمشاريع وسير العمل عبر Jira. أنشئ المشكلات وحدّثها، وتتبع تقدم المشاريع، وأدر التعيينات، وبسّط إدارة مشاريعك باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Jira، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Jira بصلاحيات المشروع المناسبة
- ربط حساب Jira الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Jira
### 1. ربط حساب Jira الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Jira** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة المشكلات والمشاريع
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة المشاريع البرمجية وتتبع الأخطاء مع تكامل Linear لـ CrewAI."
icon: "list-check"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إدارة المشكلات والمشاريع وسير عمل التطوير عبر Linear. أنشئ المشكلات وحدّثها، وأدر جداول المشاريع الزمنية، ونظّم الفرق، وبسّط عملية تطوير البرمجيات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Linear، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Linear بصلاحيات مساحة العمل المناسبة
- ربط حساب Linear الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Linear
### 1. ربط حساب Linear الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Linear** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة لإدارة المشكلات والمشاريع
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة المصنفات والبيانات مع تكامل Microsoft Excel لـ CrewAI."
icon: "table"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من إنشاء وإدارة مصنفات Excel وأوراق العمل والجداول والرسوم البيانية في OneDrive أو SharePoint. تعامل مع نطاقات البيانات، وأنشئ المرئيات، وأدر الجداول، وبسّط سير عمل جداول البيانات باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Microsoft Excel، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Microsoft 365 مع إمكانية الوصول إلى Excel وOneDrive/SharePoint
- ربط حساب Microsoft الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Microsoft Excel
### 1. ربط حساب Microsoft الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Microsoft Excel** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى الملفات ومصنفات Excel
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة الملفات والمجلدات مع تكامل Microsoft OneDrive لـ CrewAI."
icon: "cloud"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من رفع وتحميل وإدارة الملفات والمجلدات في Microsoft OneDrive. أتمت عمليات الملفات، ونظّم المحتوى، وأنشئ روابط المشاركة، وبسّط سير عمل التخزين السحابي باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Microsoft OneDrive، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Microsoft مع إمكانية الوصول إلى OneDrive
- ربط حساب Microsoft الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Microsoft OneDrive
### 1. ربط حساب Microsoft الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Microsoft OneDrive** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى الملفات
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
description: "إدارة البريد الإلكتروني والتقويم وجهات الاتصال مع تكامل Microsoft Outlook لـ CrewAI."
icon: "envelope"
mode: "wide"
---
## نظرة عامة
مكّن وكلاءك من الوصول إلى رسائل Outlook الإلكترونية وأحداث التقويم وجهات الاتصال وإدارتها. أرسل رسائل البريد الإلكتروني، واسترجع الرسائل، وأدر أحداث التقويم، ونظّم جهات الاتصال باستخدام الأتمتة المدعومة بالذكاء الاصطناعي.
## المتطلبات الأساسية
قبل استخدام تكامل Microsoft Outlook، تأكد من توفر ما يلي:
- حساب [CrewAI AMP](https://app.crewai.com) مع اشتراك فعّال
- حساب Microsoft مع إمكانية الوصول إلى Outlook
- ربط حساب Microsoft الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/crewai_plus/connectors)
## إعداد تكامل Microsoft Outlook
### 1. ربط حساب Microsoft الخاص بك
1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
2. ابحث عن **Microsoft Outlook** في قسم تكاملات المصادقة
3. انقر على **Connect** وأكمل عملية OAuth
4. امنح الصلاحيات اللازمة للوصول إلى البريد والتقويم وجهات الاتصال
5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
### 2. تثبيت الحزمة المطلوبة
```bash
uv add crewai-tools
```
### 3. إعداد متغير البيئة
<Note>
لاستخدام التكاملات مع `Agent(apps=[])`, يجب تعيين متغير البيئة
`CREWAI_PLATFORM_INTEGRATION_TOKEN` برمز المؤسسة الخاص بك.
تواصل مع فريق الدعم للحصول على المساعدة في إعداد تكامل Microsoft Outlook
أو استكشاف الأخطاء وإصلاحها.
</Card>
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.