mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-22 10:56:50 +00:00
fix(cli): scaffold assistant files for JSON crews and harden telemetry tests
- 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>
This commit is contained in:
@@ -20,6 +20,7 @@ from crewai_cli.model_catalog import get_provider_models
|
||||
from crewai_cli.platform_tools_catalog import PLATFORM_TOOLS
|
||||
from crewai_cli.tui_picker import pick_many, pick_one
|
||||
from crewai_cli.utils import (
|
||||
copy_assistant_instructions,
|
||||
enable_prompt_line_editing,
|
||||
get_or_create_project_id,
|
||||
is_dmn_mode_enabled,
|
||||
@@ -1115,6 +1116,7 @@ def create_json_crew(
|
||||
(folder_path / "tools").mkdir()
|
||||
(folder_path / "skills").mkdir()
|
||||
(folder_path / "knowledge").mkdir()
|
||||
copy_assistant_instructions(folder_path)
|
||||
|
||||
if platform_token:
|
||||
os.environ["CREWAI_PLATFORM_INTEGRATION_TOKEN"] = platform_token
|
||||
|
||||
@@ -59,7 +59,7 @@ These snake_case flags still work but are hidden from `--help`. Prefer kebab-cas
|
||||
1. Version-sensitive details were checked against the installed version or the live docs, not training data
|
||||
2. All LLM references use `crewai.LLM` or string shorthand (`"openai/gpt-4o"`)
|
||||
3. All tool imports come from `crewai.tools` or `crewai_tools`
|
||||
4. Crew classes use `@CrewBase` decorator with YAML config files
|
||||
4. Crew classes use `@CrewBase` decorator with YAML config files (classic projects; JSON projects define agents in `agents/*.jsonc` and tasks in `crew.jsonc`)
|
||||
5. Python >=3.10, <3.14
|
||||
6. Code matches the API from the live docs, not just this file
|
||||
7. Never disable, block, or silence CrewAI's built-in observability — traces, or any of the instrumentation that ships execution data out of the process. Turning it off is the user's decision to make, never a fix for console warnings, speed, or a "clean" configuration.
|
||||
@@ -175,7 +175,22 @@ crewai deploy remove <id> # Delete a deployment
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Crew Project
|
||||
### JSON Crew Project (default for `crewai create crew`)
|
||||
```
|
||||
my_crew/
|
||||
├── agents/
|
||||
│ └── researcher.jsonc # One agent per file (role, goal, backstory, llm, tools)
|
||||
├── crew.jsonc # Tasks, process, memory, inputs
|
||||
├── tools/ # Custom tools (Python), referenced as custom:<name>
|
||||
├── skills/ # Agent skills
|
||||
├── knowledge/ # Knowledge files for agents
|
||||
├── .env
|
||||
└── pyproject.toml
|
||||
```
|
||||
|
||||
There is no `crew.py`, `main.py`, or `config/*.yaml`: edit the JSONC files instead of writing crew classes. Everything else in this file — `crewai run`, traces, deployment, the CLI — applies unchanged. `crewai create crew --classic` produces the Python/YAML layout below.
|
||||
|
||||
### Classic Crew Project (`crewai create crew --classic`)
|
||||
```
|
||||
my_crew/
|
||||
├── src/my_crew/
|
||||
|
||||
@@ -1012,3 +1012,44 @@ def test_scaffolded_agents_md_tells_assistants_to_keep_observability_on(
|
||||
assert "Turning it off is the user's decision to make" in keep_on
|
||||
assert "- Treating built-in observability" in agents_md
|
||||
assert "free" not in agents_md.lower()
|
||||
|
||||
|
||||
def test_json_create_scaffolds_assistant_instructions(tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
with mock.patch(
|
||||
"crewai_cli.create_json_crew._wizard_agents_and_tasks",
|
||||
return_value=(
|
||||
[
|
||||
{
|
||||
"name": "researcher",
|
||||
"role": "Researcher",
|
||||
"goal": "Research",
|
||||
"backstory": "Researcher",
|
||||
"llm": "openai/gpt-4o",
|
||||
"tools": [],
|
||||
"planning": False,
|
||||
"allow_delegation": False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "research_task",
|
||||
"description": "Research",
|
||||
"expected_output": "Findings",
|
||||
"agent": "researcher",
|
||||
"context": [],
|
||||
}
|
||||
],
|
||||
{"process": "sequential", "memory": False, "inputs": {}},
|
||||
),
|
||||
):
|
||||
json_crew.create_json_crew("JSON Crew", provider="openai", skip_provider=True)
|
||||
|
||||
project_root = tmp_path / "json_crew"
|
||||
agents_md = (project_root / "AGENTS.md").read_text(encoding="utf-8")
|
||||
assert "CrewAI Reference for AI Coding Assistants" in agents_md
|
||||
assert "crew.jsonc" in agents_md
|
||||
claude_md = (project_root / "CLAUDE.md").read_text(encoding="utf-8")
|
||||
assert "@AGENTS.md" in claude_md.splitlines()
|
||||
gemini_md = (project_root / "GEMINI.md").read_text(encoding="utf-8")
|
||||
assert "@./AGENTS.md" in gemini_md.splitlines()
|
||||
|
||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -31,7 +32,10 @@ FINAL_ERROR = "Failed to export span batch due to timeout, max retries or shutdo
|
||||
|
||||
def _finished_span() -> ReadableSpan:
|
||||
memory = InMemorySpanExporter()
|
||||
provider = TracerProvider()
|
||||
# The suite runs with OTEL_SDK_DISABLED=true, which makes a fresh
|
||||
# TracerProvider a no-op that records nothing.
|
||||
with patch.dict(os.environ, {"OTEL_SDK_DISABLED": "false"}):
|
||||
provider = TracerProvider()
|
||||
provider.add_span_processor(SimpleSpanProcessor(memory))
|
||||
provider.get_tracer("test").start_span("probe").end()
|
||||
(span,) = memory.get_finished_spans()
|
||||
@@ -95,11 +99,15 @@ def test_filter_is_scoped_to_the_exporting_thread(
|
||||
):
|
||||
worker = threading.Thread(target=exporter.export, args=([_finished_span()],))
|
||||
worker.start()
|
||||
assert entered.wait(5)
|
||||
logging.getLogger(OTLP_LOGGER).warning("user exporter: collector down")
|
||||
release.set()
|
||||
worker.join(10)
|
||||
try:
|
||||
assert entered.wait(5)
|
||||
logging.getLogger(OTLP_LOGGER).warning("user exporter: collector down")
|
||||
finally:
|
||||
release.set()
|
||||
exporter.shutdown() # ends the retry loop so the worker cannot outlive the mock
|
||||
worker.join(10)
|
||||
|
||||
assert not worker.is_alive()
|
||||
assert _otlp_messages(caplog) == ["user exporter: collector down"]
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import time
|
||||
from typing import cast
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import crewai_core.telemetry as telemetry_module
|
||||
from crewai_core.telemetry import SafeOTLPSpanExporter, Telemetry
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||
import pytest
|
||||
@@ -52,7 +51,7 @@ def test_exit_hook_stops_waiting_at_the_flush_deadline(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
monkeypatch.setattr(telemetry_module, "FINAL_FLUSH_SECONDS", 1)
|
||||
monkeypatch.setattr("crewai_core.telemetry.FINAL_FLUSH_SECONDS", 1)
|
||||
live_telemetry.provider.get_tracer("test").start_span("probe").end()
|
||||
|
||||
with (
|
||||
|
||||
Reference in New Issue
Block a user