From dcad5d1499d9b7f86f0f94c9ea570d6ceb192e94 Mon Sep 17 00:00:00 2001 From: langfuse-bot Date: Thu, 16 Jul 2026 11:33:04 +0000 Subject: [PATCH] feat(api): update API spec from langfuse/langfuse 51cf9d9 --- langfuse/api/unstable/__init__.py | 82 +- langfuse/api/unstable/client.py | 21 + .../unstable/dashboard_widgets/__init__.py | 15 + .../api/unstable/dashboard_widgets/client.py | 520 ++- .../unstable/dashboard_widgets/raw_client.py | 1785 +++++++++- .../dashboard_widgets/types/__init__.py | 15 + .../types/create_dashboard_widget_request.py | 19 +- .../types/dashboard_widget.py | 5 +- .../dashboard_widget_chart_config_input.py | 29 + .../types/dashboard_widget_filter.py | 18 +- .../types/dashboard_widget_list.py | 17 + .../dashboard_widget_view_with_legacy.py | 35 + .../types/delete_dashboard_widget_response.py | 14 + .../types/update_dashboard_widget_request.py | 34 + langfuse/api/unstable/dashboards/__init__.py | 97 + langfuse/api/unstable/dashboards/client.py | 934 +++++ .../api/unstable/dashboards/raw_client.py | 3165 +++++++++++++++++ .../api/unstable/dashboards/types/__init__.py | 99 + .../create_dashboard_placement_request.py | 46 + .../types/create_dashboard_request.py | 19 + .../types/create_preset_placement.py | 40 + .../types/create_widget_placement.py | 40 + .../unstable/dashboards/types/dashboard.py | 32 + .../dashboards/types/dashboard_definition.py | 15 + .../dashboards/types/dashboard_list.py | 17 + .../dashboards/types/dashboard_placement.py | 60 + .../delete_dashboard_placement_response.py | 14 + .../types/delete_dashboard_response.py | 14 + .../dashboards/types/preset_placement.py | 21 + .../update_dashboard_placement_request.py | 32 + .../types/update_dashboard_request.py | 19 + .../dashboards/types/widget_placement.py | 21 + 32 files changed, 7157 insertions(+), 137 deletions(-) create mode 100644 langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_chart_config_input.py create mode 100644 langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_list.py create mode 100644 langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_view_with_legacy.py create mode 100644 langfuse/api/unstable/dashboard_widgets/types/delete_dashboard_widget_response.py create mode 100644 langfuse/api/unstable/dashboard_widgets/types/update_dashboard_widget_request.py create mode 100644 langfuse/api/unstable/dashboards/__init__.py create mode 100644 langfuse/api/unstable/dashboards/client.py create mode 100644 langfuse/api/unstable/dashboards/raw_client.py create mode 100644 langfuse/api/unstable/dashboards/types/__init__.py create mode 100644 langfuse/api/unstable/dashboards/types/create_dashboard_placement_request.py create mode 100644 langfuse/api/unstable/dashboards/types/create_dashboard_request.py create mode 100644 langfuse/api/unstable/dashboards/types/create_preset_placement.py create mode 100644 langfuse/api/unstable/dashboards/types/create_widget_placement.py create mode 100644 langfuse/api/unstable/dashboards/types/dashboard.py create mode 100644 langfuse/api/unstable/dashboards/types/dashboard_definition.py create mode 100644 langfuse/api/unstable/dashboards/types/dashboard_list.py create mode 100644 langfuse/api/unstable/dashboards/types/dashboard_placement.py create mode 100644 langfuse/api/unstable/dashboards/types/delete_dashboard_placement_response.py create mode 100644 langfuse/api/unstable/dashboards/types/delete_dashboard_response.py create mode 100644 langfuse/api/unstable/dashboards/types/preset_placement.py create mode 100644 langfuse/api/unstable/dashboards/types/update_dashboard_placement_request.py create mode 100644 langfuse/api/unstable/dashboards/types/update_dashboard_request.py create mode 100644 langfuse/api/unstable/dashboards/types/widget_placement.py diff --git a/langfuse/api/unstable/__init__.py b/langfuse/api/unstable/__init__.py index 75b3d705a..f294aba5f 100644 --- a/langfuse/api/unstable/__init__.py +++ b/langfuse/api/unstable/__init__.py @@ -21,7 +21,14 @@ UnauthorizedError, UnprocessableContentError, ) - from . import commons, dashboard_widgets, errors, evaluation_rules, evaluators + from . import ( + commons, + dashboard_widgets, + dashboards, + errors, + evaluation_rules, + evaluators, + ) from .commons import ( ArrayOptionsEvaluationRuleFilter, BooleanEvaluationRuleFilter, @@ -77,14 +84,39 @@ CreateDashboardWidgetRequest, DashboardWidget, DashboardWidgetChartConfig, + DashboardWidgetChartConfigInput, DashboardWidgetChartType, DashboardWidgetDefaultSort, DashboardWidgetDimension, DashboardWidgetFilter, + DashboardWidgetList, DashboardWidgetMetric, DashboardWidgetMetricAggregation, DashboardWidgetSortOrder, DashboardWidgetView, + DashboardWidgetViewWithLegacy, + DeleteDashboardWidgetResponse, + UpdateDashboardWidgetRequest, + ) + from .dashboards import ( + CreateDashboardPlacementRequest, + CreateDashboardPlacementRequest_Preset, + CreateDashboardPlacementRequest_Widget, + CreateDashboardRequest, + CreatePresetPlacement, + CreateWidgetPlacement, + Dashboard, + DashboardDefinition, + DashboardList, + DashboardPlacement, + DashboardPlacement_Preset, + DashboardPlacement_Widget, + DeleteDashboardPlacementResponse, + DeleteDashboardResponse, + PresetPlacement, + UpdateDashboardPlacementRequest, + UpdateDashboardRequest, + WidgetPlacement, ) from .evaluation_rules import ( CodeEvaluationRuleEvaluatorReference, @@ -127,6 +159,10 @@ "ConflictError": ".errors", "CreateCodeEvaluationRuleRequest": ".evaluation_rules", "CreateCodeEvaluatorRequest": ".evaluators", + "CreateDashboardPlacementRequest": ".dashboards", + "CreateDashboardPlacementRequest_Preset": ".dashboards", + "CreateDashboardPlacementRequest_Widget": ".dashboards", + "CreateDashboardRequest": ".dashboards", "CreateDashboardWidgetRequest": ".dashboard_widgets", "CreateEvaluationRuleRequest": ".evaluation_rules", "CreateEvaluatorRequest": ".evaluators", @@ -134,17 +170,31 @@ "CreateEvaluatorRequest_LlmAsJudge": ".evaluators", "CreateLlmAsJudgeEvaluationRuleRequest": ".evaluation_rules", "CreateLlmAsJudgeEvaluatorRequest": ".evaluators", + "CreatePresetPlacement": ".dashboards", + "CreateWidgetPlacement": ".dashboards", + "Dashboard": ".dashboards", + "DashboardDefinition": ".dashboards", + "DashboardList": ".dashboards", + "DashboardPlacement": ".dashboards", + "DashboardPlacement_Preset": ".dashboards", + "DashboardPlacement_Widget": ".dashboards", "DashboardWidget": ".dashboard_widgets", "DashboardWidgetChartConfig": ".dashboard_widgets", + "DashboardWidgetChartConfigInput": ".dashboard_widgets", "DashboardWidgetChartType": ".dashboard_widgets", "DashboardWidgetDefaultSort": ".dashboard_widgets", "DashboardWidgetDimension": ".dashboard_widgets", "DashboardWidgetFilter": ".dashboard_widgets", + "DashboardWidgetList": ".dashboard_widgets", "DashboardWidgetMetric": ".dashboard_widgets", "DashboardWidgetMetricAggregation": ".dashboard_widgets", "DashboardWidgetSortOrder": ".dashboard_widgets", "DashboardWidgetView": ".dashboard_widgets", + "DashboardWidgetViewWithLegacy": ".dashboard_widgets", "DateTimeEvaluationRuleFilter": ".commons", + "DeleteDashboardPlacementResponse": ".dashboards", + "DeleteDashboardResponse": ".dashboards", + "DeleteDashboardWidgetResponse": ".dashboard_widgets", "DeleteEvaluationRuleResponse": ".evaluation_rules", "DeleteEvaluatorResponse": ".evaluators", "EvaluationRule": ".evaluation_rules", @@ -195,6 +245,7 @@ "NullEvaluationRuleFilter": ".commons", "NumberEvaluationRuleFilter": ".commons", "NumberObjectEvaluationRuleFilter": ".commons", + "PresetPlacement": ".dashboards", "PublicApiError": ".errors", "PublicApiErrorCode": ".errors", "PublicApiErrorDetails": ".errors", @@ -213,9 +264,14 @@ "TooManyRequestsError": ".errors", "UnauthorizedError": ".errors", "UnprocessableContentError": ".errors", + "UpdateDashboardPlacementRequest": ".dashboards", + "UpdateDashboardRequest": ".dashboards", + "UpdateDashboardWidgetRequest": ".dashboard_widgets", "UpdateEvaluationRuleRequest": ".evaluation_rules", + "WidgetPlacement": ".dashboards", "commons": ".commons", "dashboard_widgets": ".dashboard_widgets", + "dashboards": ".dashboards", "errors": ".errors", "evaluation_rules": ".evaluation_rules", "evaluators": ".evaluators", @@ -261,6 +317,10 @@ def __dir__(): "ConflictError", "CreateCodeEvaluationRuleRequest", "CreateCodeEvaluatorRequest", + "CreateDashboardPlacementRequest", + "CreateDashboardPlacementRequest_Preset", + "CreateDashboardPlacementRequest_Widget", + "CreateDashboardRequest", "CreateDashboardWidgetRequest", "CreateEvaluationRuleRequest", "CreateEvaluatorRequest", @@ -268,17 +328,31 @@ def __dir__(): "CreateEvaluatorRequest_LlmAsJudge", "CreateLlmAsJudgeEvaluationRuleRequest", "CreateLlmAsJudgeEvaluatorRequest", + "CreatePresetPlacement", + "CreateWidgetPlacement", + "Dashboard", + "DashboardDefinition", + "DashboardList", + "DashboardPlacement", + "DashboardPlacement_Preset", + "DashboardPlacement_Widget", "DashboardWidget", "DashboardWidgetChartConfig", + "DashboardWidgetChartConfigInput", "DashboardWidgetChartType", "DashboardWidgetDefaultSort", "DashboardWidgetDimension", "DashboardWidgetFilter", + "DashboardWidgetList", "DashboardWidgetMetric", "DashboardWidgetMetricAggregation", "DashboardWidgetSortOrder", "DashboardWidgetView", + "DashboardWidgetViewWithLegacy", "DateTimeEvaluationRuleFilter", + "DeleteDashboardPlacementResponse", + "DeleteDashboardResponse", + "DeleteDashboardWidgetResponse", "DeleteEvaluationRuleResponse", "DeleteEvaluatorResponse", "EvaluationRule", @@ -329,6 +403,7 @@ def __dir__(): "NullEvaluationRuleFilter", "NumberEvaluationRuleFilter", "NumberObjectEvaluationRuleFilter", + "PresetPlacement", "PublicApiError", "PublicApiErrorCode", "PublicApiErrorDetails", @@ -347,9 +422,14 @@ def __dir__(): "TooManyRequestsError", "UnauthorizedError", "UnprocessableContentError", + "UpdateDashboardPlacementRequest", + "UpdateDashboardRequest", + "UpdateDashboardWidgetRequest", "UpdateEvaluationRuleRequest", + "WidgetPlacement", "commons", "dashboard_widgets", + "dashboards", "errors", "evaluation_rules", "evaluators", diff --git a/langfuse/api/unstable/client.py b/langfuse/api/unstable/client.py index 642b0b985..c948dd77b 100644 --- a/langfuse/api/unstable/client.py +++ b/langfuse/api/unstable/client.py @@ -12,6 +12,7 @@ AsyncDashboardWidgetsClient, DashboardWidgetsClient, ) + from .dashboards.client import AsyncDashboardsClient, DashboardsClient from .evaluation_rules.client import ( AsyncEvaluationRulesClient, EvaluationRulesClient, @@ -24,6 +25,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawUnstableClient(client_wrapper=client_wrapper) self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[DashboardWidgetsClient] = None + self._dashboards: typing.Optional[DashboardsClient] = None self._evaluation_rules: typing.Optional[EvaluationRulesClient] = None self._evaluators: typing.Optional[EvaluatorsClient] = None @@ -48,6 +50,14 @@ def dashboard_widgets(self): ) return self._dashboard_widgets + @property + def dashboards(self): + if self._dashboards is None: + from .dashboards.client import DashboardsClient # noqa: E402 + + self._dashboards = DashboardsClient(client_wrapper=self._client_wrapper) + return self._dashboards + @property def evaluation_rules(self): if self._evaluation_rules is None: @@ -72,6 +82,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._raw_client = AsyncRawUnstableClient(client_wrapper=client_wrapper) self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[AsyncDashboardWidgetsClient] = None + self._dashboards: typing.Optional[AsyncDashboardsClient] = None self._evaluation_rules: typing.Optional[AsyncEvaluationRulesClient] = None self._evaluators: typing.Optional[AsyncEvaluatorsClient] = None @@ -96,6 +107,16 @@ def dashboard_widgets(self): ) return self._dashboard_widgets + @property + def dashboards(self): + if self._dashboards is None: + from .dashboards.client import AsyncDashboardsClient # noqa: E402 + + self._dashboards = AsyncDashboardsClient( + client_wrapper=self._client_wrapper + ) + return self._dashboards + @property def evaluation_rules(self): if self._evaluation_rules is None: diff --git a/langfuse/api/unstable/dashboard_widgets/__init__.py b/langfuse/api/unstable/dashboard_widgets/__init__.py index 5e96961c1..288f89614 100644 --- a/langfuse/api/unstable/dashboard_widgets/__init__.py +++ b/langfuse/api/unstable/dashboard_widgets/__init__.py @@ -10,27 +10,37 @@ CreateDashboardWidgetRequest, DashboardWidget, DashboardWidgetChartConfig, + DashboardWidgetChartConfigInput, DashboardWidgetChartType, DashboardWidgetDefaultSort, DashboardWidgetDimension, DashboardWidgetFilter, + DashboardWidgetList, DashboardWidgetMetric, DashboardWidgetMetricAggregation, DashboardWidgetSortOrder, DashboardWidgetView, + DashboardWidgetViewWithLegacy, + DeleteDashboardWidgetResponse, + UpdateDashboardWidgetRequest, ) _dynamic_imports: typing.Dict[str, str] = { "CreateDashboardWidgetRequest": ".types", "DashboardWidget": ".types", "DashboardWidgetChartConfig": ".types", + "DashboardWidgetChartConfigInput": ".types", "DashboardWidgetChartType": ".types", "DashboardWidgetDefaultSort": ".types", "DashboardWidgetDimension": ".types", "DashboardWidgetFilter": ".types", + "DashboardWidgetList": ".types", "DashboardWidgetMetric": ".types", "DashboardWidgetMetricAggregation": ".types", "DashboardWidgetSortOrder": ".types", "DashboardWidgetView": ".types", + "DashboardWidgetViewWithLegacy": ".types", + "DeleteDashboardWidgetResponse": ".types", + "UpdateDashboardWidgetRequest": ".types", } @@ -65,12 +75,17 @@ def __dir__(): "CreateDashboardWidgetRequest", "DashboardWidget", "DashboardWidgetChartConfig", + "DashboardWidgetChartConfigInput", "DashboardWidgetChartType", "DashboardWidgetDefaultSort", "DashboardWidgetDimension", "DashboardWidgetFilter", + "DashboardWidgetList", "DashboardWidgetMetric", "DashboardWidgetMetricAggregation", "DashboardWidgetSortOrder", "DashboardWidgetView", + "DashboardWidgetViewWithLegacy", + "DeleteDashboardWidgetResponse", + "UpdateDashboardWidgetRequest", ] diff --git a/langfuse/api/unstable/dashboard_widgets/client.py b/langfuse/api/unstable/dashboard_widgets/client.py index ff842576b..e2ab4bb5a 100644 --- a/langfuse/api/unstable/dashboard_widgets/client.py +++ b/langfuse/api/unstable/dashboard_widgets/client.py @@ -6,12 +6,14 @@ from ...core.request_options import RequestOptions from .raw_client import AsyncRawDashboardWidgetsClient, RawDashboardWidgetsClient from .types.dashboard_widget import DashboardWidget -from .types.dashboard_widget_chart_config import DashboardWidgetChartConfig +from .types.dashboard_widget_chart_config_input import DashboardWidgetChartConfigInput from .types.dashboard_widget_chart_type import DashboardWidgetChartType from .types.dashboard_widget_dimension import DashboardWidgetDimension from .types.dashboard_widget_filter import DashboardWidgetFilter +from .types.dashboard_widget_list import DashboardWidgetList from .types.dashboard_widget_metric import DashboardWidgetMetric from .types.dashboard_widget_view import DashboardWidgetView +from .types.delete_dashboard_widget_response import DeleteDashboardWidgetResponse # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -32,27 +34,81 @@ def with_raw_response(self) -> RawDashboardWidgetsClient: """ return self._raw_client + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardWidgetList: + """ + List dashboard widgets in the project, ordered by most recently + updated first. + + Responses may include legacy `traces` widgets created before this + API existed. New widgets cannot be created with `view: traces`. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidgetList + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboard_widgets.list() + """ + _response = self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + def create( self, *, name: str, - description: str, view: DashboardWidgetView, dimensions: typing.Sequence[DashboardWidgetDimension], metrics: typing.Sequence[DashboardWidgetMetric], filters: typing.Sequence[DashboardWidgetFilter], chart_type: DashboardWidgetChartType, - chart_config: DashboardWidgetChartConfig, - min_version: typing.Optional[int] = OMIT, + description: typing.Optional[str] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> DashboardWidget: """ - Create a reusable dashboard widget. + Create a dashboard widget (a standalone chart definition you place on + any dashboard). - This endpoint creates the widget. It does not place the widget on a dashboard grid, this has to be done in the UI. + This endpoint creates the widget only; place it on a dashboard via + `POST /dashboards/{dashboardId}/placements`. Supported views are `observations`, `scores-numeric`, and `scores-categorical`. - The legacy `traces` view is not supported by this unstable API, `minVersion` defaults to `2`; values below `2` are rejected. + The legacy `traces` view is not supported by this unstable API. + Widgets are created as v2 internally. + + `chartConfig` is optional and defaults to the plain config for + `chartType`; when `chartConfig.type` is given it must match + `chartType`. Unstable API note: - This surface may evolve while dashboard/widget APIs are being finalized. @@ -61,8 +117,6 @@ def create( ---------- name : str - description : str - view : DashboardWidgetView dimensions : typing.Sequence[DashboardWidgetDimension] @@ -73,9 +127,11 @@ def create( chart_type : DashboardWidgetChartType - chart_config : DashboardWidgetChartConfig + description : typing.Optional[str] + Defaults to an empty string. - min_version : typing.Optional[int] + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + Defaults to the plain config for `chartType`. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -88,7 +144,7 @@ def create( -------- from langfuse import LangfuseAPI from langfuse.unstable.dashboard_widgets import ( - DashboardWidgetChartConfig, + DashboardWidgetChartConfigInput, DashboardWidgetChartType, DashboardWidgetDimension, DashboardWidgetMetric, @@ -121,14 +177,132 @@ def create( ], filters=[], chart_type=DashboardWidgetChartType.HORIZONTAL_BAR, - chart_config=DashboardWidgetChartConfig( - type=DashboardWidgetChartType.HORIZONTAL_BAR, + chart_config=DashboardWidgetChartConfigInput( row_limit=10, ), - min_version=2, ) """ _response = self._raw_client.create( + name=name, + view=view, + dimensions=dimensions, + metrics=metrics, + filters=filters, + chart_type=chart_type, + description=description, + chart_config=chart_config, + request_options=request_options, + ) + return _response.data + + def get( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DashboardWidget: + """ + Get a dashboard widget by id. + + The response may use `view: traces` for legacy widgets. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidget + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboard_widgets.get( + widget_id="widgetId", + ) + """ + _response = self._raw_client.get(widget_id, request_options=request_options) + return _response.data + + def update( + self, + widget_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + view: typing.Optional[DashboardWidgetView] = OMIT, + dimensions: typing.Optional[typing.Sequence[DashboardWidgetDimension]] = OMIT, + metrics: typing.Optional[typing.Sequence[DashboardWidgetMetric]] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + chart_type: typing.Optional[DashboardWidgetChartType] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardWidget: + """ + Update a dashboard widget. + + All fields are optional; at least one field is required. + Changing `chartType` without sending `chartConfig` resets the config + to the new chart type's defaults. When `chartConfig.type` is given + it must match the widget's (possibly updated) `chartType`. + + `view` cannot be changed to the legacy `traces` value. Existing + `traces` widgets may be updated on other fields. + + Parameters + ---------- + widget_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + view : typing.Optional[DashboardWidgetView] + + dimensions : typing.Optional[typing.Sequence[DashboardWidgetDimension]] + + metrics : typing.Optional[typing.Sequence[DashboardWidgetMetric]] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + chart_type : typing.Optional[DashboardWidgetChartType] + + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidget + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboard_widgets.update( + widget_id="widgetId", + ) + """ + _response = self._raw_client.update( + widget_id, name=name, description=description, view=view, @@ -137,11 +311,49 @@ def create( filters=filters, chart_type=chart_type, chart_config=chart_config, - min_version=min_version, request_options=request_options, ) return _response.data + def delete( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDashboardWidgetResponse: + """ + Delete a dashboard widget. + + The API returns `409` while the widget is still placed on a dashboard. + Remove those placements first. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardWidgetResponse + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboard_widgets.delete( + widget_id="widgetId", + ) + """ + _response = self._raw_client.delete(widget_id, request_options=request_options) + return _response.data + class AsyncDashboardWidgetsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -158,27 +370,89 @@ def with_raw_response(self) -> AsyncRawDashboardWidgetsClient: """ return self._raw_client + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardWidgetList: + """ + List dashboard widgets in the project, ordered by most recently + updated first. + + Responses may include legacy `traces` widgets created before this + API existed. New widgets cannot be created with `view: traces`. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidgetList + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboard_widgets.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + async def create( self, *, name: str, - description: str, view: DashboardWidgetView, dimensions: typing.Sequence[DashboardWidgetDimension], metrics: typing.Sequence[DashboardWidgetMetric], filters: typing.Sequence[DashboardWidgetFilter], chart_type: DashboardWidgetChartType, - chart_config: DashboardWidgetChartConfig, - min_version: typing.Optional[int] = OMIT, + description: typing.Optional[str] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> DashboardWidget: """ - Create a reusable dashboard widget. + Create a dashboard widget (a standalone chart definition you place on + any dashboard). - This endpoint creates the widget. It does not place the widget on a dashboard grid, this has to be done in the UI. + This endpoint creates the widget only; place it on a dashboard via + `POST /dashboards/{dashboardId}/placements`. Supported views are `observations`, `scores-numeric`, and `scores-categorical`. - The legacy `traces` view is not supported by this unstable API, `minVersion` defaults to `2`; values below `2` are rejected. + The legacy `traces` view is not supported by this unstable API. + Widgets are created as v2 internally. + + `chartConfig` is optional and defaults to the plain config for + `chartType`; when `chartConfig.type` is given it must match + `chartType`. Unstable API note: - This surface may evolve while dashboard/widget APIs are being finalized. @@ -187,8 +461,6 @@ async def create( ---------- name : str - description : str - view : DashboardWidgetView dimensions : typing.Sequence[DashboardWidgetDimension] @@ -199,9 +471,11 @@ async def create( chart_type : DashboardWidgetChartType - chart_config : DashboardWidgetChartConfig + description : typing.Optional[str] + Defaults to an empty string. - min_version : typing.Optional[int] + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + Defaults to the plain config for `chartType`. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -216,7 +490,7 @@ async def create( from langfuse import AsyncLangfuseAPI from langfuse.unstable.dashboard_widgets import ( - DashboardWidgetChartConfig, + DashboardWidgetChartConfigInput, DashboardWidgetChartType, DashboardWidgetDimension, DashboardWidgetMetric, @@ -252,17 +526,153 @@ async def main() -> None: ], filters=[], chart_type=DashboardWidgetChartType.HORIZONTAL_BAR, - chart_config=DashboardWidgetChartConfig( - type=DashboardWidgetChartType.HORIZONTAL_BAR, + chart_config=DashboardWidgetChartConfigInput( row_limit=10, ), - min_version=2, ) asyncio.run(main()) """ _response = await self._raw_client.create( + name=name, + view=view, + dimensions=dimensions, + metrics=metrics, + filters=filters, + chart_type=chart_type, + description=description, + chart_config=chart_config, + request_options=request_options, + ) + return _response.data + + async def get( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DashboardWidget: + """ + Get a dashboard widget by id. + + The response may use `view: traces` for legacy widgets. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidget + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboard_widgets.get( + widget_id="widgetId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + widget_id, request_options=request_options + ) + return _response.data + + async def update( + self, + widget_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + view: typing.Optional[DashboardWidgetView] = OMIT, + dimensions: typing.Optional[typing.Sequence[DashboardWidgetDimension]] = OMIT, + metrics: typing.Optional[typing.Sequence[DashboardWidgetMetric]] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + chart_type: typing.Optional[DashboardWidgetChartType] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardWidget: + """ + Update a dashboard widget. + + All fields are optional; at least one field is required. + Changing `chartType` without sending `chartConfig` resets the config + to the new chart type's defaults. When `chartConfig.type` is given + it must match the widget's (possibly updated) `chartType`. + + `view` cannot be changed to the legacy `traces` value. Existing + `traces` widgets may be updated on other fields. + + Parameters + ---------- + widget_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + view : typing.Optional[DashboardWidgetView] + + dimensions : typing.Optional[typing.Sequence[DashboardWidgetDimension]] + + metrics : typing.Optional[typing.Sequence[DashboardWidgetMetric]] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + chart_type : typing.Optional[DashboardWidgetChartType] + + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardWidget + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboard_widgets.update( + widget_id="widgetId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + widget_id, name=name, description=description, view=view, @@ -271,7 +681,55 @@ async def main() -> None: filters=filters, chart_type=chart_type, chart_config=chart_config, - min_version=min_version, request_options=request_options, ) return _response.data + + async def delete( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteDashboardWidgetResponse: + """ + Delete a dashboard widget. + + The API returns `409` while the widget is still placed on a dashboard. + Remove those placements first. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardWidgetResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboard_widgets.delete( + widget_id="widgetId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + widget_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/unstable/dashboard_widgets/raw_client.py b/langfuse/api/unstable/dashboard_widgets/raw_client.py index df0bde333..afd535f2d 100644 --- a/langfuse/api/unstable/dashboard_widgets/raw_client.py +++ b/langfuse/api/unstable/dashboard_widgets/raw_client.py @@ -10,13 +10,16 @@ from ...commons.errors.method_not_allowed_error import ( MethodNotAllowedError as commons_errors_method_not_allowed_error_MethodNotAllowedError, ) -from ...commons.errors.not_found_error import NotFoundError +from ...commons.errors.not_found_error import ( + NotFoundError as commons_errors_not_found_error_NotFoundError, +) from ...commons.errors.unauthorized_error import ( UnauthorizedError as commons_errors_unauthorized_error_UnauthorizedError, ) from ...core.api_error import ApiError from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ...core.serialization import convert_and_respect_annotation_metadata @@ -24,22 +27,28 @@ AccessDeniedError as unstable_errors_errors_access_denied_error_AccessDeniedError, ) from ..errors.errors.bad_request_error import BadRequestError +from ..errors.errors.conflict_error import ConflictError from ..errors.errors.internal_server_error import InternalServerError from ..errors.errors.method_not_allowed_error import ( MethodNotAllowedError as unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError, ) +from ..errors.errors.not_found_error import ( + NotFoundError as unstable_errors_errors_not_found_error_NotFoundError, +) from ..errors.errors.too_many_requests_error import TooManyRequestsError from ..errors.errors.unauthorized_error import ( UnauthorizedError as unstable_errors_errors_unauthorized_error_UnauthorizedError, ) from ..errors.types.public_api_error import PublicApiError from .types.dashboard_widget import DashboardWidget -from .types.dashboard_widget_chart_config import DashboardWidgetChartConfig +from .types.dashboard_widget_chart_config_input import DashboardWidgetChartConfigInput from .types.dashboard_widget_chart_type import DashboardWidgetChartType from .types.dashboard_widget_dimension import DashboardWidgetDimension from .types.dashboard_widget_filter import DashboardWidgetFilter +from .types.dashboard_widget_list import DashboardWidgetList from .types.dashboard_widget_metric import DashboardWidgetMetric from .types.dashboard_widget_view import DashboardWidgetView +from .types.delete_dashboard_widget_response import DeleteDashboardWidgetResponse # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -49,97 +58,50 @@ class RawDashboardWidgetsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def create( + def list( self, *, - name: str, - description: str, - view: DashboardWidgetView, - dimensions: typing.Sequence[DashboardWidgetDimension], - metrics: typing.Sequence[DashboardWidgetMetric], - filters: typing.Sequence[DashboardWidgetFilter], - chart_type: DashboardWidgetChartType, - chart_config: DashboardWidgetChartConfig, - min_version: typing.Optional[int] = OMIT, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[DashboardWidget]: + ) -> HttpResponse[DashboardWidgetList]: """ - Create a reusable dashboard widget. - - This endpoint creates the widget. It does not place the widget on a dashboard grid, this has to be done in the UI. - - Supported views are `observations`, `scores-numeric`, and `scores-categorical`. - The legacy `traces` view is not supported by this unstable API, `minVersion` defaults to `2`; values below `2` are rejected. + List dashboard widgets in the project, ordered by most recently + updated first. - Unstable API note: - - This surface may evolve while dashboard/widget APIs are being finalized. + Responses may include legacy `traces` widgets created before this + API existed. New widgets cannot be created with `view: traces`. Parameters ---------- - name : str + page : typing.Optional[int] + 1-based page number. Defaults to `1`. - description : str - - view : DashboardWidgetView - - dimensions : typing.Sequence[DashboardWidgetDimension] - - metrics : typing.Sequence[DashboardWidgetMetric] - - filters : typing.Sequence[DashboardWidgetFilter] - - chart_type : DashboardWidgetChartType - - chart_config : DashboardWidgetChartConfig - - min_version : typing.Optional[int] + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[DashboardWidget] + HttpResponse[DashboardWidgetList] """ _response = self._client_wrapper.httpx_client.request( "api/public/unstable/dashboard-widgets", - method="POST", - json={ - "name": name, - "description": description, - "view": view, - "dimensions": convert_and_respect_annotation_metadata( - object_=dimensions, - annotation=typing.Sequence[DashboardWidgetDimension], - direction="write", - ), - "metrics": convert_and_respect_annotation_metadata( - object_=metrics, - annotation=typing.Sequence[DashboardWidgetMetric], - direction="write", - ), - "filters": convert_and_respect_annotation_metadata( - object_=filters, - annotation=typing.Sequence[DashboardWidgetFilter], - direction="write", - ), - "chartType": chart_type, - "chartConfig": convert_and_respect_annotation_metadata( - object_=chart_config, - annotation=DashboardWidgetChartConfig, - direction="write", - ), - "minVersion": min_version, + method="GET", + params={ + "page": page, + "limit": limit, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - DashboardWidget, + DashboardWidgetList, parse_obj_as( - type_=DashboardWidget, # type: ignore + type_=DashboardWidgetList, # type: ignore object_=_response.json(), ), ) @@ -255,7 +217,7 @@ def create( ), ) if _response.status_code == 404: - raise NotFoundError( + raise commons_errors_not_found_error_NotFoundError( headers=dict(_response.headers), body=typing.cast( typing.Any, @@ -278,32 +240,33 @@ def create( body=_response_json, ) - -class AsyncRawDashboardWidgetsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def create( + def create( self, *, name: str, - description: str, view: DashboardWidgetView, dimensions: typing.Sequence[DashboardWidgetDimension], metrics: typing.Sequence[DashboardWidgetMetric], filters: typing.Sequence[DashboardWidgetFilter], chart_type: DashboardWidgetChartType, - chart_config: DashboardWidgetChartConfig, - min_version: typing.Optional[int] = OMIT, + description: typing.Optional[str] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[DashboardWidget]: + ) -> HttpResponse[DashboardWidget]: """ - Create a reusable dashboard widget. + Create a dashboard widget (a standalone chart definition you place on + any dashboard). - This endpoint creates the widget. It does not place the widget on a dashboard grid, this has to be done in the UI. + This endpoint creates the widget only; place it on a dashboard via + `POST /dashboards/{dashboardId}/placements`. Supported views are `observations`, `scores-numeric`, and `scores-categorical`. - The legacy `traces` view is not supported by this unstable API, `minVersion` defaults to `2`; values below `2` are rejected. + The legacy `traces` view is not supported by this unstable API. + Widgets are created as v2 internally. + + `chartConfig` is optional and defaults to the plain config for + `chartType`; when `chartConfig.type` is given it must match + `chartType`. Unstable API note: - This surface may evolve while dashboard/widget APIs are being finalized. @@ -312,8 +275,6 @@ async def create( ---------- name : str - description : str - view : DashboardWidgetView dimensions : typing.Sequence[DashboardWidgetDimension] @@ -324,18 +285,20 @@ async def create( chart_type : DashboardWidgetChartType - chart_config : DashboardWidgetChartConfig + description : typing.Optional[str] + Defaults to an empty string. - min_version : typing.Optional[int] + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + Defaults to the plain config for `chartType`. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[DashboardWidget] + HttpResponse[DashboardWidget] """ - _response = await self._client_wrapper.httpx_client.request( + _response = self._client_wrapper.httpx_client.request( "api/public/unstable/dashboard-widgets", method="POST", json={ @@ -360,10 +323,9 @@ async def create( "chartType": chart_type, "chartConfig": convert_and_respect_annotation_metadata( object_=chart_config, - annotation=DashboardWidgetChartConfig, + annotation=DashboardWidgetChartConfigInput, direction="write", ), - "minVersion": min_version, }, request_options=request_options, omit=OMIT, @@ -377,7 +339,175 @@ async def create( object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def get( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DashboardWidget]: + """ + Get a dashboard widget by id. + + The response may use `view: traces` for legacy widgets. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DashboardWidget] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidget, + parse_obj_as( + type_=DashboardWidget, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -411,6 +541,17 @@ async def create( ), ), ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 405: raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( headers=dict(_response.headers), @@ -489,7 +630,1467 @@ async def create( ), ) if _response.status_code == 404: - raise NotFoundError( + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def update( + self, + widget_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + view: typing.Optional[DashboardWidgetView] = OMIT, + dimensions: typing.Optional[typing.Sequence[DashboardWidgetDimension]] = OMIT, + metrics: typing.Optional[typing.Sequence[DashboardWidgetMetric]] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + chart_type: typing.Optional[DashboardWidgetChartType] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DashboardWidget]: + """ + Update a dashboard widget. + + All fields are optional; at least one field is required. + Changing `chartType` without sending `chartConfig` resets the config + to the new chart type's defaults. When `chartConfig.type` is given + it must match the widget's (possibly updated) `chartType`. + + `view` cannot be changed to the legacy `traces` value. Existing + `traces` widgets may be updated on other fields. + + Parameters + ---------- + widget_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + view : typing.Optional[DashboardWidgetView] + + dimensions : typing.Optional[typing.Sequence[DashboardWidgetDimension]] + + metrics : typing.Optional[typing.Sequence[DashboardWidgetMetric]] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + chart_type : typing.Optional[DashboardWidgetChartType] + + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DashboardWidget] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="PATCH", + json={ + "name": name, + "description": description, + "view": view, + "dimensions": convert_and_respect_annotation_metadata( + object_=dimensions, + annotation=typing.Sequence[DashboardWidgetDimension], + direction="write", + ), + "metrics": convert_and_respect_annotation_metadata( + object_=metrics, + annotation=typing.Sequence[DashboardWidgetMetric], + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + "chartType": chart_type, + "chartConfig": convert_and_respect_annotation_metadata( + object_=chart_config, + annotation=DashboardWidgetChartConfigInput, + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidget, + parse_obj_as( + type_=DashboardWidget, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def delete( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteDashboardWidgetResponse]: + """ + Delete a dashboard widget. + + The API returns `409` while the widget is still placed on a dashboard. + Remove those placements first. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteDashboardWidgetResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardWidgetResponse, + parse_obj_as( + type_=DeleteDashboardWidgetResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + +class AsyncRawDashboardWidgetsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardWidgetList]: + """ + List dashboard widgets in the project, ordered by most recently + updated first. + + Responses may include legacy `traces` widgets created before this + API existed. New widgets cannot be created with `view: traces`. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardWidgetList] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboard-widgets", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidgetList, + parse_obj_as( + type_=DashboardWidgetList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def create( + self, + *, + name: str, + view: DashboardWidgetView, + dimensions: typing.Sequence[DashboardWidgetDimension], + metrics: typing.Sequence[DashboardWidgetMetric], + filters: typing.Sequence[DashboardWidgetFilter], + chart_type: DashboardWidgetChartType, + description: typing.Optional[str] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardWidget]: + """ + Create a dashboard widget (a standalone chart definition you place on + any dashboard). + + This endpoint creates the widget only; place it on a dashboard via + `POST /dashboards/{dashboardId}/placements`. + + Supported views are `observations`, `scores-numeric`, and `scores-categorical`. + The legacy `traces` view is not supported by this unstable API. + Widgets are created as v2 internally. + + `chartConfig` is optional and defaults to the plain config for + `chartType`; when `chartConfig.type` is given it must match + `chartType`. + + Unstable API note: + - This surface may evolve while dashboard/widget APIs are being finalized. + + Parameters + ---------- + name : str + + view : DashboardWidgetView + + dimensions : typing.Sequence[DashboardWidgetDimension] + + metrics : typing.Sequence[DashboardWidgetMetric] + + filters : typing.Sequence[DashboardWidgetFilter] + + chart_type : DashboardWidgetChartType + + description : typing.Optional[str] + Defaults to an empty string. + + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + Defaults to the plain config for `chartType`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardWidget] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboard-widgets", + method="POST", + json={ + "name": name, + "description": description, + "view": view, + "dimensions": convert_and_respect_annotation_metadata( + object_=dimensions, + annotation=typing.Sequence[DashboardWidgetDimension], + direction="write", + ), + "metrics": convert_and_respect_annotation_metadata( + object_=metrics, + annotation=typing.Sequence[DashboardWidgetMetric], + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + "chartType": chart_type, + "chartConfig": convert_and_respect_annotation_metadata( + object_=chart_config, + annotation=DashboardWidgetChartConfigInput, + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidget, + parse_obj_as( + type_=DashboardWidget, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def get( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DashboardWidget]: + """ + Get a dashboard widget by id. + + The response may use `view: traces` for legacy widgets. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardWidget] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidget, + parse_obj_as( + type_=DashboardWidget, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def update( + self, + widget_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + view: typing.Optional[DashboardWidgetView] = OMIT, + dimensions: typing.Optional[typing.Sequence[DashboardWidgetDimension]] = OMIT, + metrics: typing.Optional[typing.Sequence[DashboardWidgetMetric]] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + chart_type: typing.Optional[DashboardWidgetChartType] = OMIT, + chart_config: typing.Optional[DashboardWidgetChartConfigInput] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardWidget]: + """ + Update a dashboard widget. + + All fields are optional; at least one field is required. + Changing `chartType` without sending `chartConfig` resets the config + to the new chart type's defaults. When `chartConfig.type` is given + it must match the widget's (possibly updated) `chartType`. + + `view` cannot be changed to the legacy `traces` value. Existing + `traces` widgets may be updated on other fields. + + Parameters + ---------- + widget_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + view : typing.Optional[DashboardWidgetView] + + dimensions : typing.Optional[typing.Sequence[DashboardWidgetDimension]] + + metrics : typing.Optional[typing.Sequence[DashboardWidgetMetric]] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + chart_type : typing.Optional[DashboardWidgetChartType] + + chart_config : typing.Optional[DashboardWidgetChartConfigInput] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardWidget] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="PATCH", + json={ + "name": name, + "description": description, + "view": view, + "dimensions": convert_and_respect_annotation_metadata( + object_=dimensions, + annotation=typing.Sequence[DashboardWidgetDimension], + direction="write", + ), + "metrics": convert_and_respect_annotation_metadata( + object_=metrics, + annotation=typing.Sequence[DashboardWidgetMetric], + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + "chartType": chart_type, + "chartConfig": convert_and_respect_annotation_metadata( + object_=chart_config, + annotation=DashboardWidgetChartConfigInput, + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardWidget, + parse_obj_as( + type_=DashboardWidget, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def delete( + self, widget_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteDashboardWidgetResponse]: + """ + Delete a dashboard widget. + + The API returns `409` while the widget is still placed on a dashboard. + Remove those placements first. + + Parameters + ---------- + widget_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteDashboardWidgetResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboard-widgets/{jsonable_encoder(widget_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardWidgetResponse, + parse_obj_as( + type_=DeleteDashboardWidgetResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( headers=dict(_response.headers), body=typing.cast( typing.Any, diff --git a/langfuse/api/unstable/dashboard_widgets/types/__init__.py b/langfuse/api/unstable/dashboard_widgets/types/__init__.py index 7771c98c2..cfa7c949f 100644 --- a/langfuse/api/unstable/dashboard_widgets/types/__init__.py +++ b/langfuse/api/unstable/dashboard_widgets/types/__init__.py @@ -9,26 +9,36 @@ from .create_dashboard_widget_request import CreateDashboardWidgetRequest from .dashboard_widget import DashboardWidget from .dashboard_widget_chart_config import DashboardWidgetChartConfig + from .dashboard_widget_chart_config_input import DashboardWidgetChartConfigInput from .dashboard_widget_chart_type import DashboardWidgetChartType from .dashboard_widget_default_sort import DashboardWidgetDefaultSort from .dashboard_widget_dimension import DashboardWidgetDimension from .dashboard_widget_filter import DashboardWidgetFilter + from .dashboard_widget_list import DashboardWidgetList from .dashboard_widget_metric import DashboardWidgetMetric from .dashboard_widget_metric_aggregation import DashboardWidgetMetricAggregation from .dashboard_widget_sort_order import DashboardWidgetSortOrder from .dashboard_widget_view import DashboardWidgetView + from .dashboard_widget_view_with_legacy import DashboardWidgetViewWithLegacy + from .delete_dashboard_widget_response import DeleteDashboardWidgetResponse + from .update_dashboard_widget_request import UpdateDashboardWidgetRequest _dynamic_imports: typing.Dict[str, str] = { "CreateDashboardWidgetRequest": ".create_dashboard_widget_request", "DashboardWidget": ".dashboard_widget", "DashboardWidgetChartConfig": ".dashboard_widget_chart_config", + "DashboardWidgetChartConfigInput": ".dashboard_widget_chart_config_input", "DashboardWidgetChartType": ".dashboard_widget_chart_type", "DashboardWidgetDefaultSort": ".dashboard_widget_default_sort", "DashboardWidgetDimension": ".dashboard_widget_dimension", "DashboardWidgetFilter": ".dashboard_widget_filter", + "DashboardWidgetList": ".dashboard_widget_list", "DashboardWidgetMetric": ".dashboard_widget_metric", "DashboardWidgetMetricAggregation": ".dashboard_widget_metric_aggregation", "DashboardWidgetSortOrder": ".dashboard_widget_sort_order", "DashboardWidgetView": ".dashboard_widget_view", + "DashboardWidgetViewWithLegacy": ".dashboard_widget_view_with_legacy", + "DeleteDashboardWidgetResponse": ".delete_dashboard_widget_response", + "UpdateDashboardWidgetRequest": ".update_dashboard_widget_request", } @@ -63,12 +73,17 @@ def __dir__(): "CreateDashboardWidgetRequest", "DashboardWidget", "DashboardWidgetChartConfig", + "DashboardWidgetChartConfigInput", "DashboardWidgetChartType", "DashboardWidgetDefaultSort", "DashboardWidgetDimension", "DashboardWidgetFilter", + "DashboardWidgetList", "DashboardWidgetMetric", "DashboardWidgetMetricAggregation", "DashboardWidgetSortOrder", "DashboardWidgetView", + "DashboardWidgetViewWithLegacy", + "DeleteDashboardWidgetResponse", + "UpdateDashboardWidgetRequest", ] diff --git a/langfuse/api/unstable/dashboard_widgets/types/create_dashboard_widget_request.py b/langfuse/api/unstable/dashboard_widgets/types/create_dashboard_widget_request.py index edba543ea..77abf05b9 100644 --- a/langfuse/api/unstable/dashboard_widgets/types/create_dashboard_widget_request.py +++ b/langfuse/api/unstable/dashboard_widgets/types/create_dashboard_widget_request.py @@ -6,7 +6,7 @@ import typing_extensions from ....core.pydantic_utilities import UniversalBaseModel from ....core.serialization import FieldMetadata -from .dashboard_widget_chart_config import DashboardWidgetChartConfig +from .dashboard_widget_chart_config_input import DashboardWidgetChartConfigInput from .dashboard_widget_chart_type import DashboardWidgetChartType from .dashboard_widget_dimension import DashboardWidgetDimension from .dashboard_widget_filter import DashboardWidgetFilter @@ -16,7 +16,11 @@ class CreateDashboardWidgetRequest(UniversalBaseModel): name: str - description: str + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Defaults to an empty string. + """ + view: DashboardWidgetView dimensions: typing.List[DashboardWidgetDimension] metrics: typing.List[DashboardWidgetMetric] @@ -25,11 +29,12 @@ class CreateDashboardWidgetRequest(UniversalBaseModel): DashboardWidgetChartType, FieldMetadata(alias="chartType") ] chart_config: typing_extensions.Annotated[ - DashboardWidgetChartConfig, FieldMetadata(alias="chartConfig") - ] - min_version: typing_extensions.Annotated[ - typing.Optional[int], FieldMetadata(alias="minVersion") - ] = None + typing.Optional[DashboardWidgetChartConfigInput], + FieldMetadata(alias="chartConfig"), + ] = pydantic.Field(default=None) + """ + Defaults to the plain config for `chartType`. + """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget.py b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget.py index 24826bb52..7c2f89425 100644 --- a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget.py +++ b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget.py @@ -12,7 +12,7 @@ from .dashboard_widget_dimension import DashboardWidgetDimension from .dashboard_widget_filter import DashboardWidgetFilter from .dashboard_widget_metric import DashboardWidgetMetric -from .dashboard_widget_view import DashboardWidgetView +from .dashboard_widget_view_with_legacy import DashboardWidgetViewWithLegacy class DashboardWidget(UniversalBaseModel): @@ -25,7 +25,7 @@ class DashboardWidget(UniversalBaseModel): ] name: str description: str - view: DashboardWidgetView + view: DashboardWidgetViewWithLegacy dimensions: typing.List[DashboardWidgetDimension] metrics: typing.List[DashboardWidgetMetric] filters: typing.List[DashboardWidgetFilter] @@ -35,7 +35,6 @@ class DashboardWidget(UniversalBaseModel): chart_config: typing_extensions.Annotated[ DashboardWidgetChartConfig, FieldMetadata(alias="chartConfig") ] - min_version: typing_extensions.Annotated[int, FieldMetadata(alias="minVersion")] model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_chart_config_input.py b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_chart_config_input.py new file mode 100644 index 000000000..26dd45e05 --- /dev/null +++ b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_chart_config_input.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata +from .dashboard_widget_chart_type import DashboardWidgetChartType +from .dashboard_widget_default_sort import DashboardWidgetDefaultSort + + +class DashboardWidgetChartConfigInput(UniversalBaseModel): + """ + Input-side chart config. `type` is optional and defaults to the + widget's `chartType`; when given it must match. + """ + + type: typing.Optional[DashboardWidgetChartType] = None + row_limit: typing.Optional[int] = None + show_value_labels: typing.Optional[bool] = None + bins: typing.Optional[int] = None + default_sort: typing_extensions.Annotated[ + typing.Optional[DashboardWidgetDefaultSort], FieldMetadata(alias="defaultSort") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_filter.py b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_filter.py index 161fa0b1f..df623c2fa 100644 --- a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_filter.py +++ b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_filter.py @@ -8,10 +8,22 @@ class DashboardWidgetFilter(UniversalBaseModel): """ - A dashboard widget filter in Langfuse filter-state shape. + A filter in Langfuse filter-state shape. The `value` shape and the + allowed operators depend on `type`: - Filter shapes depend on `type`, for example string filters use a string `value`, - option filters use a list of strings, and object filters include `key`. + | `type` | `value` | operators | + |---|---|---| + | `string` | string | `=`, `contains`, `does not contain`, `starts with`, `ends with` | + | `number` | number | `=`, `>`, `<`, `>=`, `<=` | + | `datetime` | ISO datetime string | `>`, `<`, `>=`, `<=` | + | `boolean` | boolean | `=`, `<>` | + | `null` | `""` | `is null`, `is not null` | + | `stringOptions` | list of strings | `any of`, `none of` | + | `arrayOptions` | list of strings | `any of`, `none of`, `all of` | + | `categoryOptions` | list of strings (requires `key`) | `any of`, `none of` | + | `stringObject` | string (requires `key`, e.g. a metadata key) | same as `string` | + | `numberObject` | number (requires `key`, e.g. a score name) | same as `number` | + | `booleanObject` | boolean (requires `key`) | `=`, `<>` | """ column: str diff --git a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_list.py b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_list.py new file mode 100644 index 000000000..c32f87075 --- /dev/null +++ b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_list.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel +from ....utils.pagination.types.meta_response import MetaResponse +from .dashboard_widget import DashboardWidget + + +class DashboardWidgetList(UniversalBaseModel): + data: typing.List[DashboardWidget] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_view_with_legacy.py b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_view_with_legacy.py new file mode 100644 index 000000000..511eda046 --- /dev/null +++ b/langfuse/api/unstable/dashboard_widgets/types/dashboard_widget_view_with_legacy.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class DashboardWidgetViewWithLegacy(enum.StrEnum): + """ + Widget data view. Responses may include the legacy `traces` value for + widgets created before this API existed. + """ + + OBSERVATIONS = "observations" + SCORES_NUMERIC = "scores-numeric" + SCORES_CATEGORICAL = "scores-categorical" + TRACES = "traces" + + def visit( + self, + observations: typing.Callable[[], T_Result], + scores_numeric: typing.Callable[[], T_Result], + scores_categorical: typing.Callable[[], T_Result], + traces: typing.Callable[[], T_Result], + ) -> T_Result: + if self is DashboardWidgetViewWithLegacy.OBSERVATIONS: + return observations() + if self is DashboardWidgetViewWithLegacy.SCORES_NUMERIC: + return scores_numeric() + if self is DashboardWidgetViewWithLegacy.SCORES_CATEGORICAL: + return scores_categorical() + if self is DashboardWidgetViewWithLegacy.TRACES: + return traces() diff --git a/langfuse/api/unstable/dashboard_widgets/types/delete_dashboard_widget_response.py b/langfuse/api/unstable/dashboard_widgets/types/delete_dashboard_widget_response.py new file mode 100644 index 000000000..facd166c6 --- /dev/null +++ b/langfuse/api/unstable/dashboard_widgets/types/delete_dashboard_widget_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel + + +class DeleteDashboardWidgetResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboard_widgets/types/update_dashboard_widget_request.py b/langfuse/api/unstable/dashboard_widgets/types/update_dashboard_widget_request.py new file mode 100644 index 000000000..24b7df50d --- /dev/null +++ b/langfuse/api/unstable/dashboard_widgets/types/update_dashboard_widget_request.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata +from .dashboard_widget_chart_config_input import DashboardWidgetChartConfigInput +from .dashboard_widget_chart_type import DashboardWidgetChartType +from .dashboard_widget_dimension import DashboardWidgetDimension +from .dashboard_widget_filter import DashboardWidgetFilter +from .dashboard_widget_metric import DashboardWidgetMetric +from .dashboard_widget_view import DashboardWidgetView + + +class UpdateDashboardWidgetRequest(UniversalBaseModel): + name: typing.Optional[str] = None + description: typing.Optional[str] = None + view: typing.Optional[DashboardWidgetView] = None + dimensions: typing.Optional[typing.List[DashboardWidgetDimension]] = None + metrics: typing.Optional[typing.List[DashboardWidgetMetric]] = None + filters: typing.Optional[typing.List[DashboardWidgetFilter]] = None + chart_type: typing_extensions.Annotated[ + typing.Optional[DashboardWidgetChartType], FieldMetadata(alias="chartType") + ] = None + chart_config: typing_extensions.Annotated[ + typing.Optional[DashboardWidgetChartConfigInput], + FieldMetadata(alias="chartConfig"), + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/__init__.py b/langfuse/api/unstable/dashboards/__init__.py new file mode 100644 index 000000000..0a96d9314 --- /dev/null +++ b/langfuse/api/unstable/dashboards/__init__.py @@ -0,0 +1,97 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + CreateDashboardPlacementRequest, + CreateDashboardPlacementRequest_Preset, + CreateDashboardPlacementRequest_Widget, + CreateDashboardRequest, + CreatePresetPlacement, + CreateWidgetPlacement, + Dashboard, + DashboardDefinition, + DashboardList, + DashboardPlacement, + DashboardPlacement_Preset, + DashboardPlacement_Widget, + DeleteDashboardPlacementResponse, + DeleteDashboardResponse, + PresetPlacement, + UpdateDashboardPlacementRequest, + UpdateDashboardRequest, + WidgetPlacement, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateDashboardPlacementRequest": ".types", + "CreateDashboardPlacementRequest_Preset": ".types", + "CreateDashboardPlacementRequest_Widget": ".types", + "CreateDashboardRequest": ".types", + "CreatePresetPlacement": ".types", + "CreateWidgetPlacement": ".types", + "Dashboard": ".types", + "DashboardDefinition": ".types", + "DashboardList": ".types", + "DashboardPlacement": ".types", + "DashboardPlacement_Preset": ".types", + "DashboardPlacement_Widget": ".types", + "DeleteDashboardPlacementResponse": ".types", + "DeleteDashboardResponse": ".types", + "PresetPlacement": ".types", + "UpdateDashboardPlacementRequest": ".types", + "UpdateDashboardRequest": ".types", + "WidgetPlacement": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CreateDashboardPlacementRequest", + "CreateDashboardPlacementRequest_Preset", + "CreateDashboardPlacementRequest_Widget", + "CreateDashboardRequest", + "CreatePresetPlacement", + "CreateWidgetPlacement", + "Dashboard", + "DashboardDefinition", + "DashboardList", + "DashboardPlacement", + "DashboardPlacement_Preset", + "DashboardPlacement_Widget", + "DeleteDashboardPlacementResponse", + "DeleteDashboardResponse", + "PresetPlacement", + "UpdateDashboardPlacementRequest", + "UpdateDashboardRequest", + "WidgetPlacement", +] diff --git a/langfuse/api/unstable/dashboards/client.py b/langfuse/api/unstable/dashboards/client.py new file mode 100644 index 000000000..9c3ada438 --- /dev/null +++ b/langfuse/api/unstable/dashboards/client.py @@ -0,0 +1,934 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..dashboard_widgets.types.dashboard_widget_filter import DashboardWidgetFilter +from .raw_client import AsyncRawDashboardsClient, RawDashboardsClient +from .types.create_dashboard_placement_request import CreateDashboardPlacementRequest +from .types.dashboard import Dashboard +from .types.dashboard_definition import DashboardDefinition +from .types.dashboard_list import DashboardList +from .types.dashboard_placement import DashboardPlacement +from .types.delete_dashboard_placement_response import DeleteDashboardPlacementResponse +from .types.delete_dashboard_response import DeleteDashboardResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DashboardsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDashboardsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDashboardsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDashboardsClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardList: + """ + List dashboards in the project, ordered by most recently updated + first. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardList + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.list() + """ + _response = self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Create a dashboard. + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.create( + name="name", + ) + """ + _response = self._raw_client.create( + name=name, + description=description, + definition=definition, + filters=filters, + request_options=request_options, + ) + return _response.data + + def get( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Get a dashboard by id. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.get( + dashboard_id="dashboardId", + ) + """ + _response = self._raw_client.get(dashboard_id, request_options=request_options) + return _response.data + + def update( + self, + dashboard_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Update a dashboard's name, description, definition, or filters. + + Parameters + ---------- + dashboard_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.update( + dashboard_id="dashboardId", + ) + """ + _response = self._raw_client.update( + dashboard_id, + name=name, + description=description, + definition=definition, + filters=filters, + request_options=request_options, + ) + return _response.data + + def delete( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDashboardResponse: + """ + Delete a dashboard. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardResponse + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.delete( + dashboard_id="dashboardId", + ) + """ + _response = self._raw_client.delete( + dashboard_id, request_options=request_options + ) + return _response.data + + def add_placement( + self, + dashboard_id: str, + *, + request: CreateDashboardPlacementRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardPlacement: + """ + Add a placement to a dashboard grid (see `DashboardPlacement` for + grid semantics). + + `id` and the position fields are optional: when omitted, the + placement gets a server-generated id and is appended below all + existing tiles as a 6x6 tile. Returns the created placement. + + The referenced widget must exist in the same project or be a + Langfuse-managed widget. The API returns `409` if a placement with + the same `id` already exists on the dashboard. + + Parameters + ---------- + dashboard_id : str + + request : CreateDashboardPlacementRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardPlacement + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.unstable.dashboards import CreateDashboardPlacementRequest_Widget + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.add_placement( + dashboard_id="dashboardId", + request=CreateDashboardPlacementRequest_Widget( + widget_id="widgetId", + ), + ) + """ + _response = self._raw_client.add_placement( + dashboard_id, request=request, request_options=request_options + ) + return _response.data + + def update_placement( + self, + dashboard_id: str, + placement_id: str, + *, + x: typing.Optional[int] = OMIT, + y: typing.Optional[int] = OMIT, + width: typing.Optional[int] = OMIT, + height: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardPlacement: + """ + Move or resize a placement. All fields are optional; at least one is + required. Omitted fields keep their current value. The placement's + content (widget/preset reference) and id cannot change — delete and + re-add the placement to swap content. Returns the updated placement. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + x : typing.Optional[int] + Grid column (12-column grid). + + y : typing.Optional[int] + Grid row. + + width : typing.Optional[int] + Width in grid columns. + + height : typing.Optional[int] + Height in grid rows. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardPlacement + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.update_placement( + dashboard_id="dashboardId", + placement_id="placementId", + ) + """ + _response = self._raw_client.update_placement( + dashboard_id, + placement_id, + x=x, + y=y, + width=width, + height=height, + request_options=request_options, + ) + return _response.data + + def delete_placement( + self, + dashboard_id: str, + placement_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDashboardPlacementResponse: + """ + Remove a placement from a dashboard grid without deleting the referenced widget. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardPlacementResponse + + Examples + -------- + from langfuse import LangfuseAPI + + client = LangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.unstable.dashboards.delete_placement( + dashboard_id="dashboardId", + placement_id="placementId", + ) + """ + _response = self._raw_client.delete_placement( + dashboard_id, placement_id, request_options=request_options + ) + return _response.data + + +class AsyncDashboardsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDashboardsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDashboardsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDashboardsClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardList: + """ + List dashboards in the project, ordered by most recently updated + first. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardList + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Create a dashboard. + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.create( + name="name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + description=description, + definition=definition, + filters=filters, + request_options=request_options, + ) + return _response.data + + async def get( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Get a dashboard by id. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.get( + dashboard_id="dashboardId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + dashboard_id, request_options=request_options + ) + return _response.data + + async def update( + self, + dashboard_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Dashboard: + """ + Update a dashboard's name, description, definition, or filters. + + Parameters + ---------- + dashboard_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Dashboard + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.update( + dashboard_id="dashboardId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + dashboard_id, + name=name, + description=description, + definition=definition, + filters=filters, + request_options=request_options, + ) + return _response.data + + async def delete( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDashboardResponse: + """ + Delete a dashboard. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.delete( + dashboard_id="dashboardId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete( + dashboard_id, request_options=request_options + ) + return _response.data + + async def add_placement( + self, + dashboard_id: str, + *, + request: CreateDashboardPlacementRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardPlacement: + """ + Add a placement to a dashboard grid (see `DashboardPlacement` for + grid semantics). + + `id` and the position fields are optional: when omitted, the + placement gets a server-generated id and is appended below all + existing tiles as a 6x6 tile. Returns the created placement. + + The referenced widget must exist in the same project or be a + Langfuse-managed widget. The API returns `409` if a placement with + the same `id` already exists on the dashboard. + + Parameters + ---------- + dashboard_id : str + + request : CreateDashboardPlacementRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardPlacement + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.unstable.dashboards import CreateDashboardPlacementRequest_Widget + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.add_placement( + dashboard_id="dashboardId", + request=CreateDashboardPlacementRequest_Widget( + widget_id="widgetId", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.add_placement( + dashboard_id, request=request, request_options=request_options + ) + return _response.data + + async def update_placement( + self, + dashboard_id: str, + placement_id: str, + *, + x: typing.Optional[int] = OMIT, + y: typing.Optional[int] = OMIT, + width: typing.Optional[int] = OMIT, + height: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> DashboardPlacement: + """ + Move or resize a placement. All fields are optional; at least one is + required. Omitted fields keep their current value. The placement's + content (widget/preset reference) and id cannot change — delete and + re-add the placement to swap content. Returns the updated placement. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + x : typing.Optional[int] + Grid column (12-column grid). + + y : typing.Optional[int] + Grid row. + + width : typing.Optional[int] + Width in grid columns. + + height : typing.Optional[int] + Height in grid rows. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DashboardPlacement + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.update_placement( + dashboard_id="dashboardId", + placement_id="placementId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_placement( + dashboard_id, + placement_id, + x=x, + y=y, + width=width, + height=height, + request_options=request_options, + ) + return _response.data + + async def delete_placement( + self, + dashboard_id: str, + placement_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteDashboardPlacementResponse: + """ + Remove a placement from a dashboard grid without deleting the referenced widget. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteDashboardPlacementResponse + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + + client = AsyncLangfuseAPI( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.unstable.dashboards.delete_placement( + dashboard_id="dashboardId", + placement_id="placementId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_placement( + dashboard_id, placement_id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/unstable/dashboards/raw_client.py b/langfuse/api/unstable/dashboards/raw_client.py new file mode 100644 index 000000000..2cbd62ea6 --- /dev/null +++ b/langfuse/api/unstable/dashboards/raw_client.py @@ -0,0 +1,3165 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...commons.errors.access_denied_error import ( + AccessDeniedError as commons_errors_access_denied_error_AccessDeniedError, +) +from ...commons.errors.error import Error +from ...commons.errors.method_not_allowed_error import ( + MethodNotAllowedError as commons_errors_method_not_allowed_error_MethodNotAllowedError, +) +from ...commons.errors.not_found_error import ( + NotFoundError as commons_errors_not_found_error_NotFoundError, +) +from ...commons.errors.unauthorized_error import ( + UnauthorizedError as commons_errors_unauthorized_error_UnauthorizedError, +) +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import jsonable_encoder +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ..dashboard_widgets.types.dashboard_widget_filter import DashboardWidgetFilter +from ..errors.errors.access_denied_error import ( + AccessDeniedError as unstable_errors_errors_access_denied_error_AccessDeniedError, +) +from ..errors.errors.bad_request_error import BadRequestError +from ..errors.errors.conflict_error import ConflictError +from ..errors.errors.internal_server_error import InternalServerError +from ..errors.errors.method_not_allowed_error import ( + MethodNotAllowedError as unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError, +) +from ..errors.errors.not_found_error import ( + NotFoundError as unstable_errors_errors_not_found_error_NotFoundError, +) +from ..errors.errors.too_many_requests_error import TooManyRequestsError +from ..errors.errors.unauthorized_error import ( + UnauthorizedError as unstable_errors_errors_unauthorized_error_UnauthorizedError, +) +from ..errors.types.public_api_error import PublicApiError +from .types.create_dashboard_placement_request import CreateDashboardPlacementRequest +from .types.dashboard import Dashboard +from .types.dashboard_definition import DashboardDefinition +from .types.dashboard_list import DashboardList +from .types.dashboard_placement import DashboardPlacement +from .types.delete_dashboard_placement_response import DeleteDashboardPlacementResponse +from .types.delete_dashboard_response import DeleteDashboardResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDashboardsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DashboardList]: + """ + List dashboards in the project, ordered by most recently updated + first. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DashboardList] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboards", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardList, + parse_obj_as( + type_=DashboardList, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Dashboard]: + """ + Create a dashboard. + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Dashboard] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboards", + method="POST", + json={ + "name": name, + "description": description, + "definition": convert_and_respect_annotation_metadata( + object_=definition, + annotation=DashboardDefinition, + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def get( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Dashboard]: + """ + Get a dashboard by id. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Dashboard] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def update( + self, + dashboard_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Dashboard]: + """ + Update a dashboard's name, description, definition, or filters. + + Parameters + ---------- + dashboard_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Dashboard] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="PATCH", + json={ + "name": name, + "description": description, + "definition": convert_and_respect_annotation_metadata( + object_=definition, + annotation=DashboardDefinition, + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def delete( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteDashboardResponse]: + """ + Delete a dashboard. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteDashboardResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardResponse, + parse_obj_as( + type_=DeleteDashboardResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def add_placement( + self, + dashboard_id: str, + *, + request: CreateDashboardPlacementRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DashboardPlacement]: + """ + Add a placement to a dashboard grid (see `DashboardPlacement` for + grid semantics). + + `id` and the position fields are optional: when omitted, the + placement gets a server-generated id and is appended below all + existing tiles as a 6x6 tile. Returns the created placement. + + The referenced widget must exist in the same project or be a + Langfuse-managed widget. The API returns `409` if a placement with + the same `id` already exists on the dashboard. + + Parameters + ---------- + dashboard_id : str + + request : CreateDashboardPlacementRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DashboardPlacement] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, + annotation=CreateDashboardPlacementRequest, + direction="write", + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardPlacement, + parse_obj_as( + type_=DashboardPlacement, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def update_placement( + self, + dashboard_id: str, + placement_id: str, + *, + x: typing.Optional[int] = OMIT, + y: typing.Optional[int] = OMIT, + width: typing.Optional[int] = OMIT, + height: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DashboardPlacement]: + """ + Move or resize a placement. All fields are optional; at least one is + required. Omitted fields keep their current value. The placement's + content (widget/preset reference) and id cannot change — delete and + re-add the placement to swap content. Returns the updated placement. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + x : typing.Optional[int] + Grid column (12-column grid). + + y : typing.Optional[int] + Grid row. + + width : typing.Optional[int] + Width in grid columns. + + height : typing.Optional[int] + Height in grid rows. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DashboardPlacement] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements/{jsonable_encoder(placement_id)}", + method="PATCH", + json={ + "x": x, + "y": y, + "width": width, + "height": height, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardPlacement, + parse_obj_as( + type_=DashboardPlacement, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + def delete_placement( + self, + dashboard_id: str, + placement_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteDashboardPlacementResponse]: + """ + Remove a placement from a dashboard grid without deleting the referenced widget. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteDashboardPlacementResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements/{jsonable_encoder(placement_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardPlacementResponse, + parse_obj_as( + type_=DeleteDashboardPlacementResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + +class AsyncRawDashboardsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardList]: + """ + List dashboards in the project, ordered by most recently updated + first. + + Parameters + ---------- + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardList] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboards", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardList, + parse_obj_as( + type_=DashboardList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Dashboard]: + """ + Create a dashboard. + + Parameters + ---------- + name : str + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Dashboard] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/dashboards", + method="POST", + json={ + "name": name, + "description": description, + "definition": convert_and_respect_annotation_metadata( + object_=definition, + annotation=DashboardDefinition, + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def get( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Dashboard]: + """ + Get a dashboard by id. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Dashboard] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def update( + self, + dashboard_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + definition: typing.Optional[DashboardDefinition] = OMIT, + filters: typing.Optional[typing.Sequence[DashboardWidgetFilter]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Dashboard]: + """ + Update a dashboard's name, description, definition, or filters. + + Parameters + ---------- + dashboard_id : str + + name : typing.Optional[str] + + description : typing.Optional[str] + + definition : typing.Optional[DashboardDefinition] + + filters : typing.Optional[typing.Sequence[DashboardWidgetFilter]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Dashboard] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="PATCH", + json={ + "name": name, + "description": description, + "definition": convert_and_respect_annotation_metadata( + object_=definition, + annotation=DashboardDefinition, + direction="write", + ), + "filters": convert_and_respect_annotation_metadata( + object_=filters, + annotation=typing.Sequence[DashboardWidgetFilter], + direction="write", + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Dashboard, + parse_obj_as( + type_=Dashboard, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def delete( + self, + dashboard_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteDashboardResponse]: + """ + Delete a dashboard. + + Parameters + ---------- + dashboard_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteDashboardResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardResponse, + parse_obj_as( + type_=DeleteDashboardResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def add_placement( + self, + dashboard_id: str, + *, + request: CreateDashboardPlacementRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardPlacement]: + """ + Add a placement to a dashboard grid (see `DashboardPlacement` for + grid semantics). + + `id` and the position fields are optional: when omitted, the + placement gets a server-generated id and is appended below all + existing tiles as a 6x6 tile. Returns the created placement. + + The referenced widget must exist in the same project or be a + Langfuse-managed widget. The API returns `409` if a placement with + the same `id` already exists on the dashboard. + + Parameters + ---------- + dashboard_id : str + + request : CreateDashboardPlacementRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardPlacement] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, + annotation=CreateDashboardPlacementRequest, + direction="write", + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardPlacement, + parse_obj_as( + type_=DashboardPlacement, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def update_placement( + self, + dashboard_id: str, + placement_id: str, + *, + x: typing.Optional[int] = OMIT, + y: typing.Optional[int] = OMIT, + width: typing.Optional[int] = OMIT, + height: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DashboardPlacement]: + """ + Move or resize a placement. All fields are optional; at least one is + required. Omitted fields keep their current value. The placement's + content (widget/preset reference) and id cannot change — delete and + re-add the placement to swap content. Returns the updated placement. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + x : typing.Optional[int] + Grid column (12-column grid). + + y : typing.Optional[int] + Grid row. + + width : typing.Optional[int] + Width in grid columns. + + height : typing.Optional[int] + Height in grid rows. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DashboardPlacement] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements/{jsonable_encoder(placement_id)}", + method="PATCH", + json={ + "x": x, + "y": y, + "width": width, + "height": height, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DashboardPlacement, + parse_obj_as( + type_=DashboardPlacement, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) + + async def delete_placement( + self, + dashboard_id: str, + placement_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteDashboardPlacementResponse]: + """ + Remove a placement from a dashboard grid without deleting the referenced widget. + + Parameters + ---------- + dashboard_id : str + + placement_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteDashboardPlacementResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/dashboards/{jsonable_encoder(dashboard_id)}/placements/{jsonable_encoder(placement_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteDashboardPlacementResponse, + parse_obj_as( + type_=DeleteDashboardPlacementResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise unstable_errors_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise unstable_errors_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise unstable_errors_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise unstable_errors_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + PublicApiError, + parse_obj_as( + type_=PublicApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 400: + raise Error( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise commons_errors_unauthorized_error_UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise commons_errors_access_denied_error_AccessDeniedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 405: + raise commons_errors_method_not_allowed_error_MethodNotAllowedError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise commons_errors_not_found_error_NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.text, + ) + raise ApiError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response_json, + ) diff --git a/langfuse/api/unstable/dashboards/types/__init__.py b/langfuse/api/unstable/dashboards/types/__init__.py new file mode 100644 index 000000000..997aa9e77 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/__init__.py @@ -0,0 +1,99 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_dashboard_placement_request import ( + CreateDashboardPlacementRequest, + CreateDashboardPlacementRequest_Preset, + CreateDashboardPlacementRequest_Widget, + ) + from .create_dashboard_request import CreateDashboardRequest + from .create_preset_placement import CreatePresetPlacement + from .create_widget_placement import CreateWidgetPlacement + from .dashboard import Dashboard + from .dashboard_definition import DashboardDefinition + from .dashboard_list import DashboardList + from .dashboard_placement import ( + DashboardPlacement, + DashboardPlacement_Preset, + DashboardPlacement_Widget, + ) + from .delete_dashboard_placement_response import DeleteDashboardPlacementResponse + from .delete_dashboard_response import DeleteDashboardResponse + from .preset_placement import PresetPlacement + from .update_dashboard_placement_request import UpdateDashboardPlacementRequest + from .update_dashboard_request import UpdateDashboardRequest + from .widget_placement import WidgetPlacement +_dynamic_imports: typing.Dict[str, str] = { + "CreateDashboardPlacementRequest": ".create_dashboard_placement_request", + "CreateDashboardPlacementRequest_Preset": ".create_dashboard_placement_request", + "CreateDashboardPlacementRequest_Widget": ".create_dashboard_placement_request", + "CreateDashboardRequest": ".create_dashboard_request", + "CreatePresetPlacement": ".create_preset_placement", + "CreateWidgetPlacement": ".create_widget_placement", + "Dashboard": ".dashboard", + "DashboardDefinition": ".dashboard_definition", + "DashboardList": ".dashboard_list", + "DashboardPlacement": ".dashboard_placement", + "DashboardPlacement_Preset": ".dashboard_placement", + "DashboardPlacement_Widget": ".dashboard_placement", + "DeleteDashboardPlacementResponse": ".delete_dashboard_placement_response", + "DeleteDashboardResponse": ".delete_dashboard_response", + "PresetPlacement": ".preset_placement", + "UpdateDashboardPlacementRequest": ".update_dashboard_placement_request", + "UpdateDashboardRequest": ".update_dashboard_request", + "WidgetPlacement": ".widget_placement", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__}" + ) + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CreateDashboardPlacementRequest", + "CreateDashboardPlacementRequest_Preset", + "CreateDashboardPlacementRequest_Widget", + "CreateDashboardRequest", + "CreatePresetPlacement", + "CreateWidgetPlacement", + "Dashboard", + "DashboardDefinition", + "DashboardList", + "DashboardPlacement", + "DashboardPlacement_Preset", + "DashboardPlacement_Widget", + "DeleteDashboardPlacementResponse", + "DeleteDashboardResponse", + "PresetPlacement", + "UpdateDashboardPlacementRequest", + "UpdateDashboardRequest", + "WidgetPlacement", +] diff --git a/langfuse/api/unstable/dashboards/types/create_dashboard_placement_request.py b/langfuse/api/unstable/dashboards/types/create_dashboard_placement_request.py new file mode 100644 index 000000000..b3ad436be --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/create_dashboard_placement_request.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class CreateDashboardPlacementRequest_Widget(UniversalBaseModel): + type: typing.Literal["widget"] = "widget" + id: typing.Optional[str] = None + widget_id: typing_extensions.Annotated[str, FieldMetadata(alias="widgetId")] + x: typing.Optional[int] = None + y: typing.Optional[int] = None + width: typing.Optional[int] = None + height: typing.Optional[int] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class CreateDashboardPlacementRequest_Preset(UniversalBaseModel): + type: typing.Literal["preset"] = "preset" + id: typing.Optional[str] = None + preset_id: typing_extensions.Annotated[str, FieldMetadata(alias="presetId")] + x: typing.Optional[int] = None + y: typing.Optional[int] = None + width: typing.Optional[int] = None + height: typing.Optional[int] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +CreateDashboardPlacementRequest = typing_extensions.Annotated[ + typing.Union[ + CreateDashboardPlacementRequest_Widget, CreateDashboardPlacementRequest_Preset + ], + pydantic.Field(discriminator="type"), +] diff --git a/langfuse/api/unstable/dashboards/types/create_dashboard_request.py b/langfuse/api/unstable/dashboards/types/create_dashboard_request.py new file mode 100644 index 000000000..33f53d5b1 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/create_dashboard_request.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel +from ...dashboard_widgets.types.dashboard_widget_filter import DashboardWidgetFilter +from .dashboard_definition import DashboardDefinition + + +class CreateDashboardRequest(UniversalBaseModel): + name: str + description: typing.Optional[str] = None + definition: typing.Optional[DashboardDefinition] = None + filters: typing.Optional[typing.List[DashboardWidgetFilter]] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/create_preset_placement.py b/langfuse/api/unstable/dashboards/types/create_preset_placement.py new file mode 100644 index 000000000..bb3758e11 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/create_preset_placement.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class CreatePresetPlacement(UniversalBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Server-generated when omitted. + """ + + preset_id: typing_extensions.Annotated[str, FieldMetadata(alias="presetId")] + x: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid column (12-column grid). Defaults to `0`. + """ + + y: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid row. Defaults to the first row below all existing tiles. + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + Width in grid columns. Defaults to `6`. + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + Height in grid rows. Defaults to `6`. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/create_widget_placement.py b/langfuse/api/unstable/dashboards/types/create_widget_placement.py new file mode 100644 index 000000000..ff83a67ab --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/create_widget_placement.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class CreateWidgetPlacement(UniversalBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Server-generated when omitted. + """ + + widget_id: typing_extensions.Annotated[str, FieldMetadata(alias="widgetId")] + x: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid column (12-column grid). Defaults to `0`. + """ + + y: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid row. Defaults to the first row below all existing tiles. + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + Width in grid columns. Defaults to `6`. + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + Height in grid rows. Defaults to `6`. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/dashboard.py b/langfuse/api/unstable/dashboards/types/dashboard.py new file mode 100644 index 000000000..92bc26bf4 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/dashboard.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata +from ...dashboard_widgets.types.dashboard_widget_filter import DashboardWidgetFilter +from .dashboard_definition import DashboardDefinition + + +class Dashboard(UniversalBaseModel): + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + name: str + description: str + definition: DashboardDefinition + filters: typing.List[DashboardWidgetFilter] = pydantic.Field() + """ + Dashboard-level filters applied to all widgets on the dashboard. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/dashboard_definition.py b/langfuse/api/unstable/dashboards/types/dashboard_definition.py new file mode 100644 index 000000000..3543d506c --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/dashboard_definition.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel +from .dashboard_placement import DashboardPlacement + + +class DashboardDefinition(UniversalBaseModel): + widgets: typing.List[DashboardPlacement] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/dashboard_list.py b/langfuse/api/unstable/dashboards/types/dashboard_list.py new file mode 100644 index 000000000..76b3b8a0d --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/dashboard_list.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel +from ....utils.pagination.types.meta_response import MetaResponse +from .dashboard import Dashboard + + +class DashboardList(UniversalBaseModel): + data: typing.List[Dashboard] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/dashboard_placement.py b/langfuse/api/unstable/dashboards/types/dashboard_placement.py new file mode 100644 index 000000000..ee66b85f0 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/dashboard_placement.py @@ -0,0 +1,60 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class DashboardPlacement_Widget(UniversalBaseModel): + """ + A tile on the dashboard's 12-column grid. `x`/`y` are the tile's + top-left cell (0-based; `y` grows downward), `width`/`height` its size + in cells. The UI default tile is 6x6 (half width). Overlapping tiles + are not rejected; prefer appending below existing tiles (or omit the + position on create to let the server do it). + """ + + type: typing.Literal["widget"] = "widget" + id: str + widget_id: typing_extensions.Annotated[str, FieldMetadata(alias="widgetId")] + x: int + y: int + width: int + height: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +class DashboardPlacement_Preset(UniversalBaseModel): + """ + A tile on the dashboard's 12-column grid. `x`/`y` are the tile's + top-left cell (0-based; `y` grows downward), `width`/`height` its size + in cells. The UI default tile is 6x6 (half width). Overlapping tiles + are not rejected; prefer appending below existing tiles (or omit the + position on create to let the server do it). + """ + + type: typing.Literal["preset"] = "preset" + id: str + preset_id: typing_extensions.Annotated[str, FieldMetadata(alias="presetId")] + x: int + y: int + width: int + height: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) + + +DashboardPlacement = typing_extensions.Annotated[ + typing.Union[DashboardPlacement_Widget, DashboardPlacement_Preset], + pydantic.Field(discriminator="type"), +] diff --git a/langfuse/api/unstable/dashboards/types/delete_dashboard_placement_response.py b/langfuse/api/unstable/dashboards/types/delete_dashboard_placement_response.py new file mode 100644 index 000000000..df493aed5 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/delete_dashboard_placement_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel + + +class DeleteDashboardPlacementResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/delete_dashboard_response.py b/langfuse/api/unstable/dashboards/types/delete_dashboard_response.py new file mode 100644 index 000000000..d98f1e7a8 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/delete_dashboard_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel + + +class DeleteDashboardResponse(UniversalBaseModel): + message: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/preset_placement.py b/langfuse/api/unstable/dashboards/types/preset_placement.py new file mode 100644 index 000000000..da6bfbf9a --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/preset_placement.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class PresetPlacement(UniversalBaseModel): + id: str + preset_id: typing_extensions.Annotated[str, FieldMetadata(alias="presetId")] + x: int + y: int + width: int + height: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/update_dashboard_placement_request.py b/langfuse/api/unstable/dashboards/types/update_dashboard_placement_request.py new file mode 100644 index 000000000..9649b36af --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/update_dashboard_placement_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel + + +class UpdateDashboardPlacementRequest(UniversalBaseModel): + x: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid column (12-column grid). + """ + + y: typing.Optional[int] = pydantic.Field(default=None) + """ + Grid row. + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + Width in grid columns. + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + Height in grid rows. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/update_dashboard_request.py b/langfuse/api/unstable/dashboards/types/update_dashboard_request.py new file mode 100644 index 000000000..696d5cf47 --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/update_dashboard_request.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel +from ...dashboard_widgets.types.dashboard_widget_filter import DashboardWidgetFilter +from .dashboard_definition import DashboardDefinition + + +class UpdateDashboardRequest(UniversalBaseModel): + name: typing.Optional[str] = None + description: typing.Optional[str] = None + definition: typing.Optional[DashboardDefinition] = None + filters: typing.Optional[typing.List[DashboardWidgetFilter]] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/dashboards/types/widget_placement.py b/langfuse/api/unstable/dashboards/types/widget_placement.py new file mode 100644 index 000000000..1734edcfc --- /dev/null +++ b/langfuse/api/unstable/dashboards/types/widget_placement.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import UniversalBaseModel +from ....core.serialization import FieldMetadata + + +class WidgetPlacement(UniversalBaseModel): + id: str + widget_id: typing_extensions.Annotated[str, FieldMetadata(alias="widgetId")] + x: int + y: int + width: int + height: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + )