|
54 | 54 | from ddtrace.llmobs._constants import EXPERIMENT_CSV_FIELD_MAX_SIZE |
55 | 55 | from ddtrace.llmobs._constants import EXPERIMENT_EXPECTED_OUTPUT |
56 | 56 | from ddtrace.llmobs._constants import EXPERIMENT_ID_KEY |
| 57 | +from ddtrace.llmobs._constants import EXPERIMENTS_INPUT |
| 58 | +from ddtrace.llmobs._constants import EXPERIMENTS_OUTPUT |
57 | 59 | from ddtrace.llmobs._constants import INPUT_DOCUMENTS |
58 | 60 | from ddtrace.llmobs._constants import INPUT_MESSAGES |
59 | 61 | from ddtrace.llmobs._constants import INPUT_PROMPT |
@@ -278,9 +280,18 @@ def _llmobs_span_event(self, span: Span) -> Optional[LLMObsSpanEvent]: |
278 | 280 |
|
279 | 281 | if span.context.get_baggage_item(EXPERIMENT_ID_KEY): |
280 | 282 | _dd_attrs["scope"] = "experiments" |
281 | | - expected_output = span._get_ctx_item(EXPERIMENT_EXPECTED_OUTPUT) |
282 | | - if span_kind == "experiment" and expected_output: |
283 | | - meta["expected_output"] = expected_output |
| 283 | + if span_kind == "experiment": |
| 284 | + expected_output = span._get_ctx_item(EXPERIMENT_EXPECTED_OUTPUT) |
| 285 | + if expected_output: |
| 286 | + meta["expected_output"] = expected_output |
| 287 | + |
| 288 | + input_data = span._get_ctx_item(EXPERIMENTS_INPUT) |
| 289 | + if input_data: |
| 290 | + meta["input"] = input_data |
| 291 | + |
| 292 | + output_data = span._get_ctx_item(EXPERIMENTS_OUTPUT) |
| 293 | + if output_data: |
| 294 | + meta["output"] = output_data |
284 | 295 |
|
285 | 296 | input_messages = span._get_ctx_item(INPUT_MESSAGES) |
286 | 297 | if span_kind == "llm" and input_messages is not None: |
@@ -1366,6 +1377,8 @@ def annotate( |
1366 | 1377 | error = cls._tag_embedding_io(span, input_documents=input_data, output_text=output_data) |
1367 | 1378 | elif span_kind == "retrieval": |
1368 | 1379 | error = cls._tag_retrieval_io(span, input_text=input_data, output_documents=output_data) |
| 1380 | + elif span_kind == "experiment": |
| 1381 | + cls._tag_freeform_io(span, input_value=input_data, output_value=output_data) |
1369 | 1382 | else: |
1370 | 1383 | cls._tag_text_io(span, input_value=input_data, output_value=output_data) |
1371 | 1384 | finally: |
@@ -1447,6 +1460,18 @@ def _tag_text_io(cls, span, input_value=None, output_value=None): |
1447 | 1460 | if output_value is not None: |
1448 | 1461 | span._set_ctx_item(OUTPUT_VALUE, safe_json(output_value)) |
1449 | 1462 |
|
| 1463 | + @classmethod |
| 1464 | + def _tag_freeform_io(cls, span, input_value=None, output_value=None): |
| 1465 | + """Tags input/output values for experient spans. |
| 1466 | + Will be mapped to span's `meta.{input,output}` fields. |
| 1467 | + this is meant to be non restrictive on user's data, experiments allow |
| 1468 | + arbitrary structured or non structured IO values in its spans |
| 1469 | + """ |
| 1470 | + if input_value is not None: |
| 1471 | + span._set_ctx_item(EXPERIMENTS_INPUT, safe_json(input_value)) |
| 1472 | + if output_value is not None: |
| 1473 | + span._set_ctx_item(EXPERIMENTS_OUTPUT, safe_json(output_value)) |
| 1474 | + |
1450 | 1475 | @staticmethod |
1451 | 1476 | def _set_dict_attribute(span: Span, key, value: Dict[str, Any]) -> None: |
1452 | 1477 | """Sets a given LLM Obs span attribute with a dictionary key/values. |
|
0 commit comments