From 10b2fb436cface53e18e7067afcade32dc5130b7 Mon Sep 17 00:00:00 2001 From: etserend Date: Thu, 9 Jul 2026 12:14:07 -0500 Subject: [PATCH 1/2] feat(export): expose include_code_metric_metadata on export_records (HYBIM-788) Port upstream commit e42a569 from rungalileo/galileo-python. Co-Authored-By: Claude Opus 4.7 --- src/splunk_ao/export.py | 10 +++++++- .../models/log_records_export_request.py | 11 +++++++++ tests/test_export.py | 24 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/splunk_ao/export.py b/src/splunk_ao/export.py index 01e6c571..dc9be263 100644 --- a/src/splunk_ao/export.py +++ b/src/splunk_ao/export.py @@ -40,6 +40,7 @@ def records( experiment_id: str | None = None, column_ids: list[str] | None = None, redact: bool = True, + include_code_metric_metadata: bool = False, ) -> Iterator[dict[str, Any]]: if filters is None: filters = [] @@ -56,6 +57,7 @@ def records( column_ids=column_ids, sort=sort, redact=redact, + include_code_metric_metadata=include_code_metric_metadata, ), ) @@ -78,8 +80,9 @@ def export_records( experiment_id: str | None = None, column_ids: list[str] | None = None, redact: bool = True, + include_code_metric_metadata: bool = False, ) -> Iterator[dict[str, Any]]: - """Exports records from a Galileo project. + """Exports records from a Splunk AO project. Defaults to the first logstream if `log_stream_id` and `experiment_id` are not provided. @@ -103,6 +106,10 @@ def export_records( A sort clause to order the exported records. redact Redact sensitive data from the response. + include_code_metric_metadata + If True, include per-row scorer metadata (the dict returned alongside the score by + code-based scorers via the (score, metadata) tuple-return contract) on each + MetricSuccess in the export. Off by default to keep payloads small. Returns ------- @@ -132,4 +139,5 @@ def export_records( column_ids=column_ids, sort=sort, redact=redact, + include_code_metric_metadata=include_code_metric_metadata, ) diff --git a/src/splunk_ao/resources/models/log_records_export_request.py b/src/splunk_ao/resources/models/log_records_export_request.py index 8d3b7cb6..930669d9 100644 --- a/src/splunk_ao/resources/models/log_records_export_request.py +++ b/src/splunk_ao/resources/models/log_records_export_request.py @@ -44,6 +44,9 @@ class LogRecordsExportRequest: 'LogRecordsTextFilter']]]): Filters to apply on the export sort (Union['LogRecordsSortClause', None, Unset]): Sort clause for the export. Defaults to native sort (created_at, id descending). + include_code_metric_metadata (Union[Unset, bool]): If True, include per-row scorer metadata (the dict + returned alongside the score by code-based scorers via the (score, metadata) tuple-return contract) on + each MetricSuccess in the export. Default: False. """ root_type: RootType @@ -69,6 +72,7 @@ class LogRecordsExportRequest: ] ) = UNSET sort: Union["LogRecordsSortClause", None, Unset] = UNSET + include_code_metric_metadata: Unset | bool = False additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -136,6 +140,8 @@ def to_dict(self) -> dict[str, Any]: else: sort = self.sort + include_code_metric_metadata = self.include_code_metric_metadata + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({"root_type": root_type}) @@ -157,6 +163,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["filters"] = filters if sort is not UNSET: field_dict["sort"] = sort + if include_code_metric_metadata is not UNSET: + field_dict["include_code_metric_metadata"] = include_code_metric_metadata return field_dict @@ -313,6 +321,8 @@ def _parse_sort(data: object) -> Union["LogRecordsSortClause", None, Unset]: sort = _parse_sort(d.pop("sort", UNSET)) + include_code_metric_metadata = d.pop("include_code_metric_metadata", UNSET) + log_records_export_request = cls( root_type=root_type, column_ids=column_ids, @@ -324,6 +334,7 @@ def _parse_sort(data: object) -> Union["LogRecordsSortClause", None, Unset]: metrics_testing_id=metrics_testing_id, filters=filters, sort=sort, + include_code_metric_metadata=include_code_metric_metadata, ) log_records_export_request.additional_properties = d diff --git a/tests/test_export.py b/tests/test_export.py index 32a45466..c863e673 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -272,3 +272,27 @@ def test_export_records_redact(mock_export_records_stream, redact_param): mock_export_records_stream.assert_called_once() request_body = mock_export_records_stream.call_args.kwargs["body"] assert request_body.redact == redact_param + + +@patch("splunk_ao.export.export_records_stream") +def test_export_records_include_code_metric_metadata_default(mock_export_records_stream): + project_id = str(uuid4()) + mock_export_records_stream.return_value = iter([]) + + list(export_records(project_id=project_id, log_stream_id=str(uuid4()))) + + request_body = mock_export_records_stream.call_args.kwargs["body"] + assert request_body.include_code_metric_metadata is False + assert request_body.to_dict()["include_code_metric_metadata"] is False + + +@patch("splunk_ao.export.export_records_stream") +def test_export_records_include_code_metric_metadata_opt_in(mock_export_records_stream): + project_id = str(uuid4()) + mock_export_records_stream.return_value = iter([]) + + list(export_records(project_id=project_id, log_stream_id=str(uuid4()), include_code_metric_metadata=True)) + + request_body = mock_export_records_stream.call_args.kwargs["body"] + assert request_body.include_code_metric_metadata is True + assert request_body.to_dict()["include_code_metric_metadata"] is True From 1e42f6ac098b7b903c4caabc3c4f57449a3e178e Mon Sep 17 00:00:00 2001 From: etserend Date: Thu, 9 Jul 2026 16:04:45 -0500 Subject: [PATCH 2/2] feat(export): add JSONL_FLAT export format (HYBIM-788) Port upstream commit 6ccd06a from rungalileo/galileo-python (PR #621). Adds LLMExportFormat.JSONL_FLAT enum value and treats it identically to JSONL in the streaming decode path so callers can request flattened output. Co-Authored-By: Claude Opus 4.7 --- openapi.yaml | 1 + src/splunk_ao/export.py | 2 +- .../resources/models/llm_export_format.py | 1 + tests/test_export.py | 24 +++++++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/openapi.yaml b/openapi.yaml index 5bcb41bd..1023f8b3 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -26606,6 +26606,7 @@ components: enum: - csv - jsonl + - jsonl_flat title: LLMExportFormat LLMIntegration: type: string diff --git a/src/splunk_ao/export.py b/src/splunk_ao/export.py index dc9be263..8c2319dd 100644 --- a/src/splunk_ao/export.py +++ b/src/splunk_ao/export.py @@ -61,7 +61,7 @@ def records( ), ) - if export_format == LLMExportFormat.JSONL: + if export_format in (LLMExportFormat.JSONL, LLMExportFormat.JSONL_FLAT): for line in response_iterator: if line: yield json.loads(line) diff --git a/src/splunk_ao/resources/models/llm_export_format.py b/src/splunk_ao/resources/models/llm_export_format.py index 5f691744..4a895b4b 100644 --- a/src/splunk_ao/resources/models/llm_export_format.py +++ b/src/splunk_ao/resources/models/llm_export_format.py @@ -4,6 +4,7 @@ class LLMExportFormat(str, Enum): CSV = "csv" JSONL = "jsonl" + JSONL_FLAT = "jsonl_flat" def __str__(self) -> str: return str(self.value) diff --git a/tests/test_export.py b/tests/test_export.py index c863e673..96780221 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -296,3 +296,27 @@ def test_export_records_include_code_metric_metadata_opt_in(mock_export_records_ request_body = mock_export_records_stream.call_args.kwargs["body"] assert request_body.include_code_metric_metadata is True assert request_body.to_dict()["include_code_metric_metadata"] is True + + +@patch("splunk_ao.export.export_records_stream") +def test_export_records_jsonl_flat(mock_export_records_stream): + project_id = str(uuid4()) + records = [{"id": "1", "type": "llm"}, {"id": "2", "type": "trace"}] + mock_export_records_stream.return_value = iter(json.dumps(r) for r in records) + + result = list( + export_records( + project_id=project_id, + root_type=RootType.TRACE, + export_format=LLMExportFormat.JSONL_FLAT, + filters=[], + sort=LogRecordsSortClause(column_id="created_at", ascending=False), + log_stream_id=str(uuid4()), + ) + ) + + assert len(result) == 2 + assert result[0] == {"id": "1", "type": "llm"} + assert result[1] == {"id": "2", "type": "trace"} + request_body = mock_export_records_stream.call_args.kwargs["body"] + assert request_body.export_format == LLMExportFormat.JSONL_FLAT