Skip to content

Commit eb3187d

Browse files
committed
feat(ai): Add ChatResult converter to exec def
Introduce a ExecutionDefinition builder for created visualizations returned from chat interactions. jira: GDAI-238 risk: low
1 parent 0fe91fa commit eb3187d

File tree

4 files changed

+2396
-1
lines changed

4 files changed

+2396
-1
lines changed

gooddata-sdk/gooddata_sdk/compute/service.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616
from gooddata_api_client.model.search_result import SearchResult
1717

1818
from gooddata_sdk.client import GoodDataApiClient
19-
from gooddata_sdk.compute.model.execution import Execution, ExecutionDefinition, ResultCacheMetadata
19+
from gooddata_sdk.compute.model.execution import (
20+
Execution,
21+
ExecutionDefinition,
22+
ResultCacheMetadata,
23+
TableDimension,
24+
)
25+
from gooddata_sdk.compute.visualization_to_sdk_converter import VisualizationToSdkConverter
2026

2127
logger = logging.getLogger(__name__)
2228

@@ -83,6 +89,32 @@ def retrieve_result_cache_metadata(self, workspace_id: str, result_id: str) -> R
8389
)
8490
return ResultCacheMetadata(result_cache_metadata=result_cache_metadata)
8591

92+
def build_exec_def_from_chat_result(self, chat_result: ChatResult) -> ExecutionDefinition:
93+
"""
94+
Build execution definition from chat result.
95+
96+
Args:
97+
chat_result: ChatResult object containing visualization details from AI chat response
98+
99+
Returns:
100+
ExecutionDefinition: Execution definition built from chat result visualization
101+
"""
102+
vis_object = chat_result.created_visualizations["objects"][0]
103+
metrics_def = vis_object.get("metrics") or []
104+
filters_def = vis_object.get("filters") or []
105+
dimensionality_def = vis_object.get("dimensionality") or []
106+
107+
metrics = [VisualizationToSdkConverter.convert_metric(m) for m in metrics_def]
108+
filters = [VisualizationToSdkConverter.convert_filter(f) for f in filters_def]
109+
attributes = [VisualizationToSdkConverter.convert_attribute(d) for d in dimensionality_def]
110+
dimensions = [
111+
TableDimension(item_ids=[a.local_id for a in attributes]),
112+
TableDimension(item_ids=["measureGroup"]),
113+
]
114+
115+
exec_def = ExecutionDefinition(dimensions=dimensions, metrics=metrics, filters=filters, attributes=attributes)
116+
return exec_def
117+
86118
def ai_chat(self, workspace_id: str, question: str) -> ChatResult:
87119
"""
88120
Chat with AI in GoodData workspace.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# (C) 2025 GoodData Corporation
2+
from typing import Any
3+
4+
from gooddata_sdk.compute.model.attribute import Attribute
5+
from gooddata_sdk.compute.model.base import ObjId
6+
from gooddata_sdk.compute.model.filter import (
7+
AbsoluteDateFilter,
8+
AllTimeFilter,
9+
Filter,
10+
NegativeAttributeFilter,
11+
PositiveAttributeFilter,
12+
RelativeDateFilter,
13+
)
14+
from gooddata_sdk.compute.model.metric import Metric, SimpleMetric
15+
16+
17+
class VisualizationToSdkConverter:
18+
"""
19+
Provides functions to convert visualization objects (dicts) to the SDK Compute model.
20+
The input should be a visualization object as returned by `ai_chat`.
21+
"""
22+
23+
@staticmethod
24+
def convert_attribute(attr_dict: dict[str, Any]) -> Attribute:
25+
"""
26+
Converts a visualization attribute dict to an SDK Attribute.
27+
Expects keys:
28+
- id: str - The identifier of the attribute
29+
- title: str - The display title/label for the attribute
30+
Returns:
31+
Attribute: An SDK Attribute object with local_id and label set
32+
"""
33+
local_id = attr_dict["id"]
34+
label = attr_dict["title"]
35+
return Attribute(local_id=local_id, label=label)
36+
37+
@staticmethod
38+
def convert_filter(filter_dict: dict[str, Any]) -> Filter:
39+
"""
40+
Converts a visualization filter dict to an SDK Filter.
41+
Expects keys:
42+
- using: str - The identifier of the attribute/dataset to filter on
43+
- include: list[str] (optional) - Values to include in positive filter
44+
- exclude: list[str] (optional) - Values to exclude in negative filter
45+
- from: str (optional) - Start date/shift for date filters
46+
- to: str (optional) - End date/shift for date filters
47+
- granularity: str (optional) - Time granularity for relative date filters
48+
Returns:
49+
Filter: One of:
50+
- PositiveAttributeFilter: When include values specified
51+
- NegativeAttributeFilter: When exclude values specified
52+
- RelativeDateFilter: When granularity and from/to shifts specified
53+
- AbsoluteDateFilter: When from/to dates specified
54+
- AllTimeFilter: When no date range specified
55+
"""
56+
using = filter_dict["using"]
57+
include = filter_dict.get("include")
58+
exclude = filter_dict.get("exclude")
59+
_from = filter_dict.get("from")
60+
_to = filter_dict.get("to")
61+
granularity = filter_dict.get("granularity")
62+
63+
if include is not None:
64+
return PositiveAttributeFilter(label=ObjId(using, "label"), values=include)
65+
elif exclude is not None:
66+
return NegativeAttributeFilter(label=ObjId(using, "label"), values=exclude)
67+
elif granularity is not None and _from is not None and _to is not None:
68+
return RelativeDateFilter(
69+
dataset=ObjId(using, "dataset"), granularity=granularity, from_shift=_from, to_shift=_to
70+
)
71+
elif _from is not None and _to is not None:
72+
return AbsoluteDateFilter(dataset=ObjId(using, "dataset"), from_date=_from, to_date=_to)
73+
else:
74+
return AllTimeFilter(dataset=ObjId(using, "dataset"))
75+
76+
@staticmethod
77+
def convert_metric(metric_dict: dict[str, Any]) -> Metric:
78+
"""
79+
Converts a visualization metric dict to an SDK Metric.
80+
Expects keys:
81+
- id: str - The identifier of the metric/fact/attribute
82+
- type: str - The type of object ("metric", "fact", or "attribute")
83+
- aggFunction: str (optional) - Aggregation function for facts/attributes
84+
Returns:
85+
Metric: One of:
86+
- SimpleMetric with no aggregation for metrics
87+
- SimpleMetric with aggregation for facts
88+
- SimpleMetric with "count" aggregation for attributes
89+
"""
90+
local_id = metric_dict["id"]
91+
item = ObjId(metric_dict["id"], metric_dict["type"])
92+
93+
if metric_dict["type"] in ["metric", "fact"]:
94+
aggregation = metric_dict.get("aggFunction")
95+
return SimpleMetric(local_id=local_id, item=item, aggregation=aggregation)
96+
97+
elif metric_dict["type"] == "attribute":
98+
aggregation = "count"
99+
return SimpleMetric(local_id=local_id, item=item, aggregation=aggregation)
100+
101+
else:
102+
raise ValueError(f"Unsupported metric type: {metric_dict['type']}")

0 commit comments

Comments
 (0)