diff --git a/langfuse/_client/client.py b/langfuse/_client/client.py index 6ce72d27a..e03e79f40 100644 --- a/langfuse/_client/client.py +++ b/langfuse/_client/client.py @@ -2877,9 +2877,10 @@ async def _process_experiment_item( ) final_observation_metadata = { + **(item_metadata if isinstance(item_metadata, dict) else {}), + **(experiment_metadata or {}), "experiment_name": experiment_name, "experiment_run_name": experiment_run_name, - **(experiment_metadata or {}), } trace_id = span.trace_id @@ -2920,9 +2921,6 @@ async def _process_experiment_item( {"dataset_id": dataset_id, "dataset_item_id": dataset_item_id} ) - if isinstance(item_metadata, dict): - final_observation_metadata.update(item_metadata) - experiment_id = dataset_run_id or fallback_experiment_id experiment_item_id = ( dataset_item_id or get_sha256_hash_hex(_serialize(input_data))[:16] diff --git a/tests/unit/test_propagate_attributes.py b/tests/unit/test_propagate_attributes.py index 0ef92ac10..faa992878 100644 --- a/tests/unit/test_propagate_attributes.py +++ b/tests/unit/test_propagate_attributes.py @@ -2748,6 +2748,40 @@ def test_experiment_id_is_stable_across_local_items( assert len(experiment_ids) == 1 assert result.experiment_id == next(iter(experiment_ids)) + def test_experiment_run_metadata_overrides_item_metadata( + self, langfuse_client, memory_exporter + ): + langfuse_client.run_experiment( + name="Metadata precedence", + run_name="run-name", + data=[ + { + "input": "test", + "metadata": {"shared": "item", "item_only": "yes"}, + } + ], + task=lambda *, item, **kwargs: "result", + metadata={"shared": "run"}, + ) + langfuse_client.flush() + + span = self.get_span_by_name(memory_exporter, "experiment-item-run") + self.verify_span_attribute( + span, + f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.shared", + "run", + ) + self.verify_span_attribute( + span, + f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.experiment_run_name", + "run-name", + ) + self.verify_span_attribute( + span, + f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.item_only", + "yes", + ) + def test_experiment_attributes_propagate_with_dataset( self, langfuse_client, memory_exporter, monkeypatch ):