diff --git a/lib/crewai/src/crewai/telemetry/tracing/last_run.py b/lib/crewai/src/crewai/telemetry/tracing/last_run.py index f47385180..e4ed336fc 100644 --- a/lib/crewai/src/crewai/telemetry/tracing/last_run.py +++ b/lib/crewai/src/crewai/telemetry/tracing/last_run.py @@ -34,8 +34,16 @@ def project_dir() -> Path: def recording_enabled() -> bool: - """Off under the test suite, so kickoff-level tests leave no file behind.""" - return os.environ.get("CREWAI_TESTING", "").lower() != "true" + """Off under the test suite, so kickoff-level tests leave no file behind, + and off inside a deployment — the platform's integration token marks one — + so a run there never writes a file the platform never reads. (A deployment + normally never gets here at all: the host binds the trace before crewAI + would start its own; this is the guard for a container that did not.)""" + from crewai.context import get_platform_integration_token + + if os.environ.get("CREWAI_TESTING", "").lower() == "true": + return False + return get_platform_integration_token() is None def last_run_path(directory: Path | None = None) -> Path: @@ -62,8 +70,9 @@ def record_last_run( or the write failed. Never raises — a run is never failed by this. "Last" means the run that FINISHED last: a record already there for a run - that finished later is kept, so two crews finishing together in one - project leave the newer one whichever writer gets to the file last.""" + that finished later — or in the same millisecond — is kept, so two crews + finishing together in one project leave the newer one whichever writer + gets to the file last.""" if not recording_enabled(): return None record: dict[str, Any] = { @@ -109,16 +118,17 @@ def record_last_run( def _newer_than(record: dict[str, Any], existing: dict[str, Any] | None) -> bool: """Is RECORD the later-finished run? A missing or unreadable existing record, - or one without a comparable time, never wins over the run just finished.""" + or one without a comparable time, never wins over the run just finished; a + tie (the same millisecond) keeps what is there — the times are stored to + the millisecond, and no finer order is worth a field in the file.""" if not existing or existing.get("execution_id") == record["execution_id"]: return True ours = record["finished_at"] or record["recorded_at"] theirs = existing.get("finished_at") or existing.get("recorded_at") if not isinstance(theirs, str) or not isinstance(ours, str): return True - return ( - ours >= theirs - ) # both ISO 8601 in UTC with the same precision: text order is time order + # Both ISO 8601 in UTC at the same precision: text order is time order. + return ours > theirs def read_last_run(directory: Path | None = None) -> dict[str, Any] | None: diff --git a/lib/crewai/tests/telemetry/test_last_run.py b/lib/crewai/tests/telemetry/test_last_run.py index 645ccde34..9eda60e32 100644 --- a/lib/crewai/tests/telemetry/test_last_run.py +++ b/lib/crewai/tests/telemetry/test_last_run.py @@ -51,6 +51,15 @@ def test_nothing_is_recorded_under_the_test_suite(monkeypatch, tmp_path): assert not (tmp_path / ".crewai").exists() +def test_nothing_is_recorded_inside_a_deployment(monkeypatch, tmp_path): + """The platform's integration token marks a deployment: no file the platform never reads.""" + monkeypatch.setattr(last_run, "project_dir", lambda: tmp_path) + monkeypatch.delenv("CREWAI_TESTING", raising=False) + monkeypatch.setenv("CREWAI_PLATFORM_INTEGRATION_TOKEN", "platform-token") + assert last_run.record_last_run(execution_id="x", tier="authenticated", started_at_ns=None, finished_at_ns=None, amp_base_url=None) is None + assert not (tmp_path / ".crewai").exists() + + def test_a_missing_or_broken_record_reads_as_none(project): assert last_run.read_last_run(project) is None (project / ".crewai").mkdir() @@ -101,6 +110,10 @@ def test_the_run_that_finished_last_stays_recorded_whichever_writer_comes_last(p last_run.record_last_run(execution_id="newest", tier="ephemeral", started_at_ns=base, finished_at_ns=base + 40 * second, amp_base_url=None) assert last_run.read_last_run(project)["execution_id"] == "newest" + # A tie to the millisecond keeps what is there: the file orders runs no finer than it stores them. + last_run.record_last_run(execution_id="same-instant", tier="ephemeral", started_at_ns=base, finished_at_ns=base + 40 * second + 400_000, amp_base_url=None) + assert last_run.read_last_run(project)["execution_id"] == "newest" + # A record without a comparable time never blocks the run just finished. (project / ".crewai" / "last_run.json").write_text(json.dumps({"execution_id": "legacy"}), encoding="utf-8") last_run.record_last_run(execution_id="fresh", tier=None, started_at_ns=None, finished_at_ns=None, amp_base_url=None)