Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions packages/gooddata-sdk/src/gooddata_sdk/compute/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@
from gooddata_api_client.model.chat_history_result import ChatHistoryResult
from gooddata_api_client.model.chat_request import ChatRequest
from gooddata_api_client.model.chat_result import ChatResult
from gooddata_api_client.model.generate_description_request import GenerateDescriptionRequest
from gooddata_api_client.model.generate_description_response import GenerateDescriptionResponse
from gooddata_api_client.model.generate_title_request import GenerateTitleRequest
from gooddata_api_client.model.generate_title_response import GenerateTitleResponse
from gooddata_api_client.model.saved_visualization import SavedVisualization
from gooddata_api_client.model.search_request import SearchRequest
from gooddata_api_client.model.search_result import SearchResult
from gooddata_api_client.model.trending_objects_result import TrendingObjectsResult

from gooddata_sdk.client import GoodDataApiClient
from gooddata_sdk.compute.model.execution import (
Expand Down Expand Up @@ -276,6 +281,7 @@ def search_ai(
workspace_id: str,
question: str,
deep_search: bool | None = None,
enable_hybrid_search: bool | None = None,
limit: int | None = None,
object_types: list[str] | None = None,
relevant_score_threshold: float | None = None,
Expand All @@ -288,6 +294,8 @@ def search_ai(
workspace_id (str): workspace identifier
question (str): keyword/sentence input for search
deep_search (bool): turn on deep search - if true, content of complex objects will be searched as well
enable_hybrid_search (Optional[bool]): if true, enables hybrid search combining vector similarity and
keyword matching. Defaults to None.
limit (Optional[int]): maximum number of results to return. Defaults to None.
object_types (Optional[list[str]]): list of object types to search for. Enum items: "attribute", "metric", "fact",
"label", "date", "dataset", "visualization" and "dashboard". Defaults to None.
Expand All @@ -303,6 +311,8 @@ def search_ai(
search_params: dict[str, Any] = {}
if deep_search is not None:
search_params["deep_search"] = deep_search
if enable_hybrid_search is not None:
search_params["enable_hybrid_search"] = enable_hybrid_search
if limit is not None:
search_params["limit"] = limit
if object_types is not None:
Expand All @@ -315,6 +325,63 @@ def search_ai(
response = self._actions_api.ai_search(workspace_id, search_request, _check_return_type=False)
return response

def generate_description(
self,
workspace_id: str,
object_id: str,
object_type: str,
) -> GenerateDescriptionResponse:
"""
Generate a description for an analytics catalog object.

Args:
workspace_id (str): workspace identifier
object_id (str): identifier of the object to describe
object_type (str): type of the object to describe.
One of: "Visualization", "Dashboard", "Metric", "Fact", "Attribute"

Returns:
GenerateDescriptionResponse: Generated description and optional note
"""
request = GenerateDescriptionRequest(object_id=object_id, object_type=object_type, _check_type=False)
response = self._actions_api.generate_description(workspace_id, request, _check_return_type=False)
return response

def generate_title(
self,
workspace_id: str,
object_id: str,
object_type: str,
) -> GenerateTitleResponse:
"""
Generate a title for an analytics catalog object.

Args:
workspace_id (str): workspace identifier
object_id (str): identifier of the object to generate a title for
object_type (str): type of the object to generate a title for.
One of: "Visualization", "Dashboard", "Metric", "Fact", "Attribute"

Returns:
GenerateTitleResponse: Generated title and optional note
"""
request = GenerateTitleRequest(object_id=object_id, object_type=object_type, _check_type=False)
response = self._actions_api.generate_title(workspace_id, request, _check_return_type=False)
return response

def get_trending_objects(self, workspace_id: str) -> TrendingObjectsResult:
"""
Get trending analytics catalog objects for a workspace.

Args:
workspace_id (str): workspace identifier

Returns:
TrendingObjectsResult: List of trending analytics catalog objects
"""
response = self._actions_api.trending_objects(workspace_id, _check_return_type=False)
return response

def cancel_executions(self, executions: dict[str, dict[str, str]]) -> None:
"""
Try to cancel given executions using the cancel api endpoint.
Expand Down
2 changes: 2 additions & 0 deletions packages/gooddata-sdk/tests/compute/fixtures/ai_chat.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
2 changes: 2 additions & 0 deletions packages/gooddata-sdk/tests/compute/fixtures/ai_search.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
interactions: []
version: 1
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ attributes:
- Campaign channels
title: Type
dataSourceTableId:
dataSourceId: pg_local_docker-demo
dataSourceId: demo-test-ds
id: campaign_channels
path:
- demo_6d9051d9069a8468
- demo
- campaign_channels
type: dataSource
description: Campaign channels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ attributes:
- Campaigns
title: Campaign name
dataSourceTableId:
dataSourceId: pg_local_docker-demo
dataSourceId: demo-test-ds
id: campaigns
path:
- demo_6d9051d9069a8468
- demo
- campaigns
type: dataSource
description: Campaigns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ attributes:
- Customers
title: State
dataSourceTableId:
dataSourceId: pg_local_docker-demo
dataSourceId: demo-test-ds
id: customers
path:
- demo_6d9051d9069a8468
- demo
- customers
type: dataSource
description: Customers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ attributes:
- Order lines
title: Order status
dataSourceTableId:
dataSourceId: pg_local_docker-demo
dataSourceId: demo-test-ds
id: order_lines
path:
- demo_6d9051d9069a8468
- demo
- order_lines
type: dataSource
description: Order lines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ attributes:
- Products
title: Category
dataSourceTableId:
dataSourceId: pg_local_docker-demo
dataSourceId: demo-test-ds
id: products
path:
- demo_6d9051d9069a8468
- demo
- products
type: dataSource
description: Products
Expand Down
82 changes: 75 additions & 7 deletions packages/gooddata-sdk/tests/compute/test_compute_service.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
# (C) 2025 GoodData Corporation
from pathlib import Path

import pytest
from gooddata_sdk import CatalogWorkspace
from gooddata_sdk.sdk import GoodDataSdk
from tests_support.vcrpy_utils import get_vcr

# Skip all tests in this module
pytest.skip(
"Skipping all tests in this module because it requires gen-ai which is not available in the test environment.",
allow_module_level=True,
)

gd_vcr = get_vcr()

_current_dir = Path(__file__).parent.absolute()
Expand Down Expand Up @@ -219,6 +212,81 @@ def test_ai_chat_stream(test_config):
sdk.compute.reset_ai_chat_history(test_workspace_id)


@gd_vcr.use_cassette(str(_fixtures_dir / "ai_search_hybrid.yaml"))
def test_search_ai_with_hybrid_search(test_config):
"""Test AI search with enable_hybrid_search parameter."""
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
path = _current_dir / "load" / "ai"
test_workspace_id = test_config["workspace_test"]

try:
_setup_test_workspace(sdk, test_workspace_id, path)
result = sdk.compute.search_ai(
workspace_id=test_workspace_id,
question="What is the total revenue?",
enable_hybrid_search=True,
)
assert result is not None
assert hasattr(result, "results")
finally:
sdk.catalog_workspace.delete_workspace(test_workspace_id)


@gd_vcr.use_cassette(str(_fixtures_dir / "generate_description.yaml"))
def test_generate_description(test_config):
"""Test generate description for an analytics catalog object."""
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
path = _current_dir / "load" / "ai"
test_workspace_id = test_config["workspace_test"]

try:
_setup_test_workspace(sdk, test_workspace_id, path)
# Use a metric object type for description generation
result = sdk.compute.generate_description(
workspace_id=test_workspace_id,
object_id="revenue",
object_type="Metric",
)
assert result is not None
finally:
sdk.catalog_workspace.delete_workspace(test_workspace_id)


@gd_vcr.use_cassette(str(_fixtures_dir / "generate_title.yaml"))
def test_generate_title(test_config):
"""Test generate title for an analytics catalog object."""
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
path = _current_dir / "load" / "ai"
test_workspace_id = test_config["workspace_test"]

try:
_setup_test_workspace(sdk, test_workspace_id, path)
result = sdk.compute.generate_title(
workspace_id=test_workspace_id,
object_id="revenue",
object_type="Metric",
)
assert result is not None
finally:
sdk.catalog_workspace.delete_workspace(test_workspace_id)


@gd_vcr.use_cassette(str(_fixtures_dir / "trending_objects.yaml"))
def test_get_trending_objects(test_config):
"""Test get trending analytics catalog objects."""
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
path = _current_dir / "load" / "ai"
test_workspace_id = test_config["workspace_test"]

try:
_setup_test_workspace(sdk, test_workspace_id, path)
result = sdk.compute.get_trending_objects(test_workspace_id)
assert result is not None
assert hasattr(result, "objects")
finally:
sdk.catalog_workspace.delete_workspace(test_workspace_id)


@gd_vcr.use_cassette(str(_fixtures_dir / "build_exec_def_from_chat_result.yaml"))
def test_build_exec_def_from_chat_result(test_config):
"""Test build execution definition from chat result."""
Expand Down
Loading