diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index e4763032e..64f5a67be 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -33,6 +33,7 @@ scim, score_configs, scores, + skills, trace, unstable, utils, @@ -384,6 +385,22 @@ ScoreV3_Text, TextScoreV3, ) + from .skills import ( + CreateSkillVersionRequest, + DeleteSkillVersionResponse, + SkillFile, + SkillFileContent, + SkillFileContentsResponse, + SkillListPagination, + SkillMeta, + SkillMetaListResponse, + SkillVersion, + SkillVersionFileCreateInput, + SkillVersionFileInput, + SkillVersionFileReference, + UpdateSkillLabelsRequest, + UpdateSkillRequest, + ) from .trace import DeleteTraceResponse _dynamic_imports: typing.Dict[str, str] = { "AccessDeniedError": ".commons", @@ -462,6 +479,7 @@ "CreateScoreValue": ".commons", "CreateScoresRequest": ".scores", "CreateScoresResponse": ".scores", + "CreateSkillVersionRequest": ".skills", "CreateTextPromptRequest": ".prompts", "CreateTextPromptType": ".prompts", "Creator": ".evaluators", @@ -478,6 +496,7 @@ "DeleteDatasetItemResponse": ".dataset_items", "DeleteLlmConnectionResponse": ".llm_connections", "DeleteMembershipRequest": ".organizations", + "DeleteSkillVersionResponse": ".skills", "DeleteTraceResponse": ".trace", "DeletedEvaluationRule": ".evaluation_rules", "DeletedEvaluator": ".evaluators", @@ -680,6 +699,16 @@ "Score_Text": ".commons", "ServiceProviderConfig": ".scim", "ServiceUnavailableError": ".health", + "SkillFile": ".skills", + "SkillFileContent": ".skills", + "SkillFileContentsResponse": ".skills", + "SkillListPagination": ".skills", + "SkillMeta": ".skills", + "SkillMetaListResponse": ".skills", + "SkillVersion": ".skills", + "SkillVersionFileCreateInput": ".skills", + "SkillVersionFileInput": ".skills", + "SkillVersionFileReference": ".skills", "StringEvaluationRuleFilter": ".evaluation_commons", "StringObjectEvaluationRuleFilter": ".evaluation_commons", "StringOptionsEvaluationRuleFilter": ".evaluation_commons", @@ -697,6 +726,8 @@ "UpdateEvaluatorRequest": ".evaluators", "UpdateLlmAsJudgeEvaluatorRequest": ".evaluators", "UpdateScoreConfigRequest": ".score_configs", + "UpdateSkillLabelsRequest": ".skills", + "UpdateSkillRequest": ".skills", "UpsertLlmConnectionRequest": ".llm_connections", "Usage": ".commons", "UserMeta": ".scim", @@ -726,6 +757,7 @@ "scim": ".scim", "score_configs": ".score_configs", "scores": ".scores", + "skills": ".skills", "trace": ".trace", "unstable": ".unstable", "utils": ".utils", @@ -836,6 +868,7 @@ def __dir__(): "CreateScoreValue", "CreateScoresRequest", "CreateScoresResponse", + "CreateSkillVersionRequest", "CreateTextPromptRequest", "CreateTextPromptType", "Creator", @@ -852,6 +885,7 @@ def __dir__(): "DeleteDatasetItemResponse", "DeleteLlmConnectionResponse", "DeleteMembershipRequest", + "DeleteSkillVersionResponse", "DeleteTraceResponse", "DeletedEvaluationRule", "DeletedEvaluator", @@ -1054,6 +1088,16 @@ def __dir__(): "Score_Text", "ServiceProviderConfig", "ServiceUnavailableError", + "SkillFile", + "SkillFileContent", + "SkillFileContentsResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", "StringEvaluationRuleFilter", "StringObjectEvaluationRuleFilter", "StringOptionsEvaluationRuleFilter", @@ -1071,6 +1115,8 @@ def __dir__(): "UpdateEvaluatorRequest", "UpdateLlmAsJudgeEvaluatorRequest", "UpdateScoreConfigRequest", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", "UpsertLlmConnectionRequest", "Usage", "UserMeta", @@ -1100,6 +1146,7 @@ def __dir__(): "scim", "score_configs", "scores", + "skills", "trace", "unstable", "utils", diff --git a/langfuse/api/client.py b/langfuse/api/client.py index a6d960093..ded3d69f0 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -40,6 +40,7 @@ from .scim.client import AsyncScimClient, ScimClient from .score_configs.client import AsyncScoreConfigsClient, ScoreConfigsClient from .scores.client import AsyncScoresClient, ScoresClient + from .skills.client import AsyncSkillsClient, SkillsClient from .trace.client import AsyncTraceClient, TraceClient from .unstable.client import AsyncUnstableClient, UnstableClient @@ -147,6 +148,7 @@ def __init__( self._scim: typing.Optional[ScimClient] = None self._score_configs: typing.Optional[ScoreConfigsClient] = None self._scores: typing.Optional[ScoresClient] = None + self._skills: typing.Optional[SkillsClient] = None self._trace: typing.Optional[TraceClient] = None self._unstable: typing.Optional[UnstableClient] = None @@ -352,6 +354,14 @@ def scores(self): self._scores = ScoresClient(client_wrapper=self._client_wrapper) return self._scores + @property + def skills(self): + if self._skills is None: + from .skills.client import SkillsClient # noqa: E402 + + self._skills = SkillsClient(client_wrapper=self._client_wrapper) + return self._skills + @property def trace(self): if self._trace is None: @@ -472,6 +482,7 @@ def __init__( self._scim: typing.Optional[AsyncScimClient] = None self._score_configs: typing.Optional[AsyncScoreConfigsClient] = None self._scores: typing.Optional[AsyncScoresClient] = None + self._skills: typing.Optional[AsyncSkillsClient] = None self._trace: typing.Optional[AsyncTraceClient] = None self._unstable: typing.Optional[AsyncUnstableClient] = None @@ -685,6 +696,14 @@ def scores(self): self._scores = AsyncScoresClient(client_wrapper=self._client_wrapper) return self._scores + @property + def skills(self): + if self._skills is None: + from .skills.client import AsyncSkillsClient # noqa: E402 + + self._skills = AsyncSkillsClient(client_wrapper=self._client_wrapper) + return self._skills + @property def trace(self): if self._trace is None: diff --git a/langfuse/api/commons/types/score_config.py b/langfuse/api/commons/types/score_config.py index 36e45e168..008ac6df0 100644 --- a/langfuse/api/commons/types/score_config.py +++ b/langfuse/api/commons/types/score_config.py @@ -17,7 +17,11 @@ class ScoreConfig(UniversalBaseModel): """ id: str - name: str + name: str = pydantic.Field() + """ + Score config name. Maximum 80 characters. + """ + created_at: typing_extensions.Annotated[ dt.datetime, FieldMetadata(alias="createdAt") ] diff --git a/langfuse/api/llm_connections/client.py b/langfuse/api/llm_connections/client.py index 3d4e60d00..5590147e2 100644 --- a/langfuse/api/llm_connections/client.py +++ b/langfuse/api/llm_connections/client.py @@ -190,6 +190,169 @@ def delete( _response = self._raw_client.delete(id, request_options=request_options) return _response.data + def list_organization( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedLlmConnections: + """ + Get all organization-level LLM connections. Requires an organization-scoped API key. + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedLlmConnections + + 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.llm_connections.list_organization() + """ + _response = self._raw_client.list_organization( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + def upsert_organization( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> LlmConnection: + """ + Create or update an organization-level LLM connection. The connection is upserted on provider and inherited by projects without a provider override. Requires an organization-scoped API key. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **OpenAI**: Optional. If provided, must be `{"useResponsesApi": }` to control whether Langfuse routes calls through OpenAI's Responses API. - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LlmConnection + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.llm_connections import LlmAdapter + + 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.llm_connections.upsert_organization( + provider="provider", + adapter=LlmAdapter.ANTHROPIC, + secret_key="secretKey", + ) + """ + _response = self._raw_client.upsert_organization( + provider=provider, + adapter=adapter, + secret_key=secret_key, + base_url=base_url, + custom_models=custom_models, + with_default_models=with_default_models, + extra_headers=extra_headers, + config=config, + request_options=request_options, + ) + return _response.data + + def delete_organization( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteLlmConnectionResponse: + """ + Delete an organization-level LLM connection by id. Evaluators in projects without an override for the provider are automatically paused. Requires an organization-scoped API key. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteLlmConnectionResponse + + 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.llm_connections.delete_organization( + id="id", + ) + """ + _response = self._raw_client.delete_organization( + id, request_options=request_options + ) + return _response.data + class AsyncLlmConnectionsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -390,3 +553,190 @@ async def main() -> None: """ _response = await self._raw_client.delete(id, request_options=request_options) return _response.data + + async def list_organization( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> PaginatedLlmConnections: + """ + Get all organization-level LLM connections. Requires an organization-scoped API key. + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedLlmConnections + + 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.llm_connections.list_organization() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_organization( + page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def upsert_organization( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> LlmConnection: + """ + Create or update an organization-level LLM connection. The connection is upserted on provider and inherited by projects without a provider override. Requires an organization-scoped API key. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **OpenAI**: Optional. If provided, must be `{"useResponsesApi": }` to control whether Langfuse routes calls through OpenAI's Responses API. - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LlmConnection + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.llm_connections import LlmAdapter + + 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.llm_connections.upsert_organization( + provider="provider", + adapter=LlmAdapter.ANTHROPIC, + secret_key="secretKey", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upsert_organization( + provider=provider, + adapter=adapter, + secret_key=secret_key, + base_url=base_url, + custom_models=custom_models, + with_default_models=with_default_models, + extra_headers=extra_headers, + config=config, + request_options=request_options, + ) + return _response.data + + async def delete_organization( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteLlmConnectionResponse: + """ + Delete an organization-level LLM connection by id. Evaluators in projects without an override for the provider are automatically paused. Requires an organization-scoped API key. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteLlmConnectionResponse + + 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.llm_connections.delete_organization( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_organization( + id, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/llm_connections/raw_client.py b/langfuse/api/llm_connections/raw_client.py index 9a6388658..128c57a4c 100644 --- a/langfuse/api/llm_connections/raw_client.py +++ b/langfuse/api/llm_connections/raw_client.py @@ -382,12 +382,722 @@ def delete( body=_response_json, ) + def list_organization( + self, + *, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PaginatedLlmConnections]: + """ + Get all organization-level LLM connections. Requires an organization-scoped API key. + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedLlmConnections] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/llm-connections", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedLlmConnections, + parse_obj_as( + type_=PaginatedLlmConnections, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 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 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 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 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 upsert_organization( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[LlmConnection]: + """ + Create or update an organization-level LLM connection. The connection is upserted on provider and inherited by projects without a provider override. Requires an organization-scoped API key. + + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **OpenAI**: Optional. If provided, must be `{"useResponsesApi": }` to control whether Langfuse routes calls through OpenAI's Responses API. - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[LlmConnection] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/organizations/llm-connections", + method="PUT", + json={ + "provider": provider, + "adapter": adapter, + "secretKey": secret_key, + "baseURL": base_url, + "customModels": custom_models, + "withDefaultModels": with_default_models, + "extraHeaders": extra_headers, + "config": config, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LlmConnection, + parse_obj_as( + type_=LlmConnection, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 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 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 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 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_organization( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteLlmConnectionResponse]: + """ + Delete an organization-level LLM connection by id. Evaluators in projects without an override for the provider are automatically paused. Requires an organization-scoped API key. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteLlmConnectionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/organizations/llm-connections/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteLlmConnectionResponse, + parse_obj_as( + type_=DeleteLlmConnectionResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 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 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 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 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 AsyncRawLlmConnectionsClient: + 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[PaginatedLlmConnections]: + """ + Get all LLM connections in a project + + Parameters + ---------- + page : typing.Optional[int] + page number, starts at 1 + + limit : typing.Optional[int] + limit of items per page + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedLlmConnections] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="GET", + params={ + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedLlmConnections, + parse_obj_as( + type_=PaginatedLlmConnections, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 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 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 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 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 AsyncRawLlmConnectionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper + async def upsert( + self, + *, + provider: str, + adapter: LlmAdapter, + secret_key: str, + base_url: typing.Optional[str] = OMIT, + custom_models: typing.Optional[typing.Sequence[str]] = OMIT, + with_default_models: typing.Optional[bool] = OMIT, + extra_headers: typing.Optional[typing.Dict[str, str]] = OMIT, + config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[LlmConnection]: + """ + Create or update an LLM connection. The connection is upserted on provider. - async def list( + Parameters + ---------- + provider : str + Provider name (e.g., 'openai', 'my-gateway'). Must be unique in project, used for upserting. + + adapter : LlmAdapter + The adapter used to interface with the LLM + + secret_key : str + Secret key for the LLM API. + + base_url : typing.Optional[str] + Custom base URL for the LLM API + + custom_models : typing.Optional[typing.Sequence[str]] + List of custom model names + + with_default_models : typing.Optional[bool] + Whether to include default models. Default is true. + + extra_headers : typing.Optional[typing.Dict[str, str]] + Extra headers to send with requests + + config : typing.Optional[typing.Dict[str, typing.Any]] + Adapter-specific configuration. Validation rules: - **Bedrock**: Required. Must be `{"region": ""}` (e.g., `{"region":"us-east-1"}`) - **OpenAI**: Optional. If provided, must be `{"useResponsesApi": }` to control whether Langfuse routes calls through OpenAI's Responses API. - **VertexAI**: Optional. If provided, must be `{"location": ""}` (e.g., `{"location":"us-central1"}`) - **Other adapters**: Not supported. Omit this field or set to null. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[LlmConnection] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/llm-connections", + method="PUT", + json={ + "provider": provider, + "adapter": adapter, + "secretKey": secret_key, + "baseURL": base_url, + "customModels": custom_models, + "withDefaultModels": with_default_models, + "extraHeaders": extra_headers, + "config": config, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LlmConnection, + parse_obj_as( + type_=LlmConnection, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 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 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 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 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, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteLlmConnectionResponse]: + """ + Delete an LLM connection by id. Evaluators that depend on the deleted connection are automatically paused. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteLlmConnectionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/llm-connections/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteLlmConnectionResponse, + parse_obj_as( + type_=DeleteLlmConnectionResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 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 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 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 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 list_organization( self, *, page: typing.Optional[int] = None, @@ -395,7 +1105,7 @@ async def list( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[PaginatedLlmConnections]: """ - Get all LLM connections in a project + Get all organization-level LLM connections. Requires an organization-scoped API key. Parameters ---------- @@ -413,7 +1123,7 @@ async def list( AsyncHttpResponse[PaginatedLlmConnections] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/llm-connections", + "api/public/organizations/llm-connections", method="GET", params={ "page": page, @@ -499,7 +1209,7 @@ async def list( body=_response_json, ) - async def upsert( + async def upsert_organization( self, *, provider: str, @@ -513,7 +1223,7 @@ async def upsert( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[LlmConnection]: """ - Create or update an LLM connection. The connection is upserted on provider. + Create or update an organization-level LLM connection. The connection is upserted on provider and inherited by projects without a provider override. Requires an organization-scoped API key. Parameters ---------- @@ -549,7 +1259,7 @@ async def upsert( AsyncHttpResponse[LlmConnection] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/llm-connections", + "api/public/organizations/llm-connections", method="PUT", json={ "provider": provider, @@ -642,11 +1352,11 @@ async def upsert( body=_response_json, ) - async def delete( + async def delete_organization( self, id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[DeleteLlmConnectionResponse]: """ - Delete an LLM connection by id. Evaluators that depend on the deleted connection are automatically paused. + Delete an organization-level LLM connection by id. Evaluators in projects without an override for the provider are automatically paused. Requires an organization-scoped API key. Parameters ---------- @@ -660,7 +1370,7 @@ async def delete( AsyncHttpResponse[DeleteLlmConnectionResponse] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/llm-connections/{jsonable_encoder(id)}", + f"api/public/organizations/llm-connections/{jsonable_encoder(id)}", method="DELETE", request_options=request_options, ) diff --git a/langfuse/api/score_configs/client.py b/langfuse/api/score_configs/client.py index b900e3f5d..9db9cfe61 100644 --- a/langfuse/api/score_configs/client.py +++ b/langfuse/api/score_configs/client.py @@ -46,7 +46,7 @@ def create( Parameters ---------- name : str - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. data_type : ScoreConfigDataType @@ -205,7 +205,7 @@ def update( The status of the score config showing if it is archived or not name : typing.Optional[str] - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. categories : typing.Optional[typing.Sequence[ConfigCategory]] Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed @@ -287,7 +287,7 @@ async def create( Parameters ---------- name : str - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. data_type : ScoreConfigDataType @@ -470,7 +470,7 @@ async def update( The status of the score config showing if it is archived or not name : typing.Optional[str] - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. categories : typing.Optional[typing.Sequence[ConfigCategory]] Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed diff --git a/langfuse/api/score_configs/raw_client.py b/langfuse/api/score_configs/raw_client.py index 11de026c6..1f13905f7 100644 --- a/langfuse/api/score_configs/raw_client.py +++ b/langfuse/api/score_configs/raw_client.py @@ -45,7 +45,7 @@ def create( Parameters ---------- name : str - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. data_type : ScoreConfigDataType @@ -401,7 +401,7 @@ def update( The status of the score config showing if it is archived or not name : typing.Optional[str] - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. categories : typing.Optional[typing.Sequence[ConfigCategory]] Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed @@ -540,7 +540,7 @@ async def create( Parameters ---------- name : str - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. data_type : ScoreConfigDataType @@ -896,7 +896,7 @@ async def update( The status of the score config showing if it is archived or not name : typing.Optional[str] - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. categories : typing.Optional[typing.Sequence[ConfigCategory]] Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed diff --git a/langfuse/api/score_configs/types/create_score_config_request.py b/langfuse/api/score_configs/types/create_score_config_request.py index 0edb01407..175e86e46 100644 --- a/langfuse/api/score_configs/types/create_score_config_request.py +++ b/langfuse/api/score_configs/types/create_score_config_request.py @@ -13,7 +13,7 @@ class CreateScoreConfigRequest(UniversalBaseModel): name: str = pydantic.Field() """ - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. """ data_type: typing_extensions.Annotated[ diff --git a/langfuse/api/score_configs/types/update_score_config_request.py b/langfuse/api/score_configs/types/update_score_config_request.py index 28c4248e9..134920fb1 100644 --- a/langfuse/api/score_configs/types/update_score_config_request.py +++ b/langfuse/api/score_configs/types/update_score_config_request.py @@ -19,7 +19,7 @@ class UpdateScoreConfigRequest(UniversalBaseModel): name: typing.Optional[str] = pydantic.Field(default=None) """ - Name of the score config. Max 35 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. + Name of the score config. Max 80 characters. Only letters, numbers, underscores, spaces, periods, parentheses, and hyphens are allowed. """ categories: typing.Optional[typing.List[ConfigCategory]] = pydantic.Field( diff --git a/langfuse/api/unstable/skills/__init__.py b/langfuse/api/skills/__init__.py similarity index 100% rename from langfuse/api/unstable/skills/__init__.py rename to langfuse/api/skills/__init__.py diff --git a/langfuse/api/unstable/skills/client.py b/langfuse/api/skills/client.py similarity index 92% rename from langfuse/api/unstable/skills/client.py rename to langfuse/api/skills/client.py index ffbd4899e..6d0ba9739 100644 --- a/langfuse/api/unstable/skills/client.py +++ b/langfuse/api/skills/client.py @@ -3,8 +3,8 @@ import datetime as dt import typing -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.request_options import RequestOptions +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions from .raw_client import AsyncRawSkillsClient, RawSkillsClient from .types.delete_skill_version_response import DeleteSkillVersionResponse from .types.skill_file_contents_response import SkillFileContentsResponse @@ -86,7 +86,7 @@ def list( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.list() + client.skills.list() """ _response = self._raw_client.list( name=name, @@ -126,7 +126,7 @@ def create_version( Examples -------- from langfuse import LangfuseAPI - from langfuse.unstable.skills import SkillVersionFileInput + from langfuse.skills import SkillVersionFileInput client = LangfuseAPI( x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", @@ -136,7 +136,7 @@ def create_version( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.create_version( + client.skills.create_version( files=[ SkillVersionFileInput( path="path", @@ -160,10 +160,11 @@ def get( *, version: typing.Optional[int] = None, label: typing.Optional[str] = None, + include_content: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SkillVersion: """ - Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's sha256Hash with getFileContents, individually or in batches across manifests. + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. Parameters ---------- @@ -173,6 +174,9 @@ def get( label : typing.Optional[str] + include_content : typing.Optional[bool] + Include UTF-8 text content in each file. Defaults to false. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -192,12 +196,16 @@ def get( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.get( + client.skills.get( skill_name="skillName", ) """ _response = self._raw_client.get( - skill_name, version=version, label=label, request_options=request_options + skill_name, + version=version, + label=label, + include_content=include_content, + request_options=request_options, ) return _response.data @@ -237,7 +245,7 @@ def update( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.update( + client.skills.update( skill_name="skillName", tags=["tags", "tags"], ) @@ -280,7 +288,7 @@ def get_file_contents( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.get_file_contents( + client.skills.get_file_contents( sha256hashes="sha256Hashes", ) """ @@ -327,7 +335,7 @@ def set_labels( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.set_labels( + client.skills.set_labels( skill_name="skillName", skill_version=1, labels=["labels", "labels"], @@ -373,7 +381,7 @@ def delete_version( password="YOUR_PASSWORD", base_url="https://yourhost.com/path/to/api", ) - client.unstable.skills.delete_version( + client.skills.delete_version( skill_name="skillName", skill_version=1, ) @@ -459,7 +467,7 @@ async def list( async def main() -> None: - await client.unstable.skills.list() + await client.skills.list() asyncio.run(main()) @@ -504,7 +512,7 @@ async def create_version( import asyncio from langfuse import AsyncLangfuseAPI - from langfuse.unstable.skills import SkillVersionFileInput + from langfuse.skills import SkillVersionFileInput client = AsyncLangfuseAPI( x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", @@ -517,7 +525,7 @@ async def create_version( async def main() -> None: - await client.unstable.skills.create_version( + await client.skills.create_version( files=[ SkillVersionFileInput( path="path", @@ -544,10 +552,11 @@ async def get( *, version: typing.Optional[int] = None, label: typing.Optional[str] = None, + include_content: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SkillVersion: """ - Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's sha256Hash with getFileContents, individually or in batches across manifests. + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. Parameters ---------- @@ -557,6 +566,9 @@ async def get( label : typing.Optional[str] + include_content : typing.Optional[bool] + Include UTF-8 text content in each file. Defaults to false. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -581,7 +593,7 @@ async def get( async def main() -> None: - await client.unstable.skills.get( + await client.skills.get( skill_name="skillName", ) @@ -589,7 +601,11 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._raw_client.get( - skill_name, version=version, label=label, request_options=request_options + skill_name, + version=version, + label=label, + include_content=include_content, + request_options=request_options, ) return _response.data @@ -634,7 +650,7 @@ async def update( async def main() -> None: - await client.unstable.skills.update( + await client.skills.update( skill_name="skillName", tags=["tags", "tags"], ) @@ -685,7 +701,7 @@ async def get_file_contents( async def main() -> None: - await client.unstable.skills.get_file_contents( + await client.skills.get_file_contents( sha256hashes="sha256Hashes", ) @@ -740,7 +756,7 @@ async def set_labels( async def main() -> None: - await client.unstable.skills.set_labels( + await client.skills.set_labels( skill_name="skillName", skill_version=1, labels=["labels", "labels"], @@ -794,7 +810,7 @@ async def delete_version( async def main() -> None: - await client.unstable.skills.delete_version( + await client.skills.delete_version( skill_name="skillName", skill_version=1, ) diff --git a/langfuse/api/unstable/skills/raw_client.py b/langfuse/api/skills/raw_client.py similarity index 96% rename from langfuse/api/unstable/skills/raw_client.py rename to langfuse/api/skills/raw_client.py index 152cc2781..bf7efa0cd 100644 --- a/langfuse/api/unstable/skills/raw_client.py +++ b/langfuse/api/skills/raw_client.py @@ -4,19 +4,19 @@ import typing from json.decoder import JSONDecodeError -from ...commons.errors.access_denied_error import AccessDeniedError -from ...commons.errors.error import Error -from ...commons.errors.method_not_allowed_error import MethodNotAllowedError -from ...commons.errors.not_found_error import NotFoundError -from ...commons.errors.unauthorized_error import UnauthorizedError -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.datetime_utils import serialize_datetime -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 ..commons.errors.access_denied_error import AccessDeniedError +from ..commons.errors.error import Error +from ..commons.errors.method_not_allowed_error import MethodNotAllowedError +from ..commons.errors.not_found_error import NotFoundError +from ..commons.errors.unauthorized_error import UnauthorizedError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.datetime_utils import serialize_datetime +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 .types.delete_skill_version_response import DeleteSkillVersionResponse from .types.skill_file_contents_response import SkillFileContentsResponse from .types.skill_meta_list_response import SkillMetaListResponse @@ -75,7 +75,7 @@ def list( HttpResponse[SkillMetaListResponse] """ _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", + "api/public/skills", method="GET", params={ "name": name, @@ -194,7 +194,7 @@ def create_version( HttpResponse[SkillVersion] """ _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", + "api/public/skills", method="POST", json={ "files": convert_and_respect_annotation_metadata( @@ -291,10 +291,11 @@ def get( *, version: typing.Optional[int] = None, label: typing.Optional[str] = None, + include_content: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[SkillVersion]: """ - Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's sha256Hash with getFileContents, individually or in batches across manifests. + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. Parameters ---------- @@ -304,6 +305,9 @@ def get( label : typing.Optional[str] + include_content : typing.Optional[bool] + Include UTF-8 text content in each file. Defaults to false. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -312,11 +316,12 @@ def get( HttpResponse[SkillVersion] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + f"api/public/skills/{jsonable_encoder(skill_name)}", method="GET", params={ "version": version, "label": label, + "includeContent": include_content, }, request_options=request_options, ) @@ -423,7 +428,7 @@ def update( HttpResponse[SkillVersion] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + f"api/public/skills/{jsonable_encoder(skill_name)}", method="PATCH", json={ "tags": tags, @@ -531,7 +536,7 @@ def get_file_contents( HttpResponse[SkillFileContentsResponse] """ _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills/files/content", + "api/public/skills/files/content", method="GET", params={ "sha256Hashes": sha256hashes, @@ -643,7 +648,7 @@ def set_labels( HttpResponse[SkillVersion] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", method="PATCH", json={ "labels": labels, @@ -753,7 +758,7 @@ def delete_version( HttpResponse[DeleteSkillVersionResponse] """ _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", method="DELETE", request_options=request_options, ) @@ -884,7 +889,7 @@ async def list( AsyncHttpResponse[SkillMetaListResponse] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", + "api/public/skills", method="GET", params={ "name": name, @@ -1003,7 +1008,7 @@ async def create_version( AsyncHttpResponse[SkillVersion] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", + "api/public/skills", method="POST", json={ "files": convert_and_respect_annotation_metadata( @@ -1100,10 +1105,11 @@ async def get( *, version: typing.Optional[int] = None, label: typing.Optional[str] = None, + include_content: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[SkillVersion]: """ - Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Use each file's sha256Hash with getFileContents, individually or in batches across manifests. + Resolve a skill's metadata and file manifest by version or label. Defaults to the production label. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. Parameters ---------- @@ -1113,6 +1119,9 @@ async def get( label : typing.Optional[str] + include_content : typing.Optional[bool] + Include UTF-8 text content in each file. Defaults to false. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1121,11 +1130,12 @@ async def get( AsyncHttpResponse[SkillVersion] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + f"api/public/skills/{jsonable_encoder(skill_name)}", method="GET", params={ "version": version, "label": label, + "includeContent": include_content, }, request_options=request_options, ) @@ -1232,7 +1242,7 @@ async def update( AsyncHttpResponse[SkillVersion] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", + f"api/public/skills/{jsonable_encoder(skill_name)}", method="PATCH", json={ "tags": tags, @@ -1340,7 +1350,7 @@ async def get_file_contents( AsyncHttpResponse[SkillFileContentsResponse] """ _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills/files/content", + "api/public/skills/files/content", method="GET", params={ "sha256Hashes": sha256hashes, @@ -1452,7 +1462,7 @@ async def set_labels( AsyncHttpResponse[SkillVersion] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", method="PATCH", json={ "labels": labels, @@ -1562,7 +1572,7 @@ async def delete_version( AsyncHttpResponse[DeleteSkillVersionResponse] """ _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", method="DELETE", request_options=request_options, ) diff --git a/langfuse/api/unstable/skills/types/__init__.py b/langfuse/api/skills/types/__init__.py similarity index 100% rename from langfuse/api/unstable/skills/types/__init__.py rename to langfuse/api/skills/types/__init__.py diff --git a/langfuse/api/unstable/skills/types/create_skill_version_request.py b/langfuse/api/skills/types/create_skill_version_request.py similarity index 83% rename from langfuse/api/unstable/skills/types/create_skill_version_request.py rename to langfuse/api/skills/types/create_skill_version_request.py index cf7c073ca..5f2997b69 100644 --- a/langfuse/api/unstable/skills/types/create_skill_version_request.py +++ b/langfuse/api/skills/types/create_skill_version_request.py @@ -4,8 +4,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .skill_version_file_create_input import SkillVersionFileCreateInput diff --git a/langfuse/api/unstable/skills/types/delete_skill_version_response.py b/langfuse/api/skills/types/delete_skill_version_response.py similarity index 83% rename from langfuse/api/unstable/skills/types/delete_skill_version_response.py rename to langfuse/api/skills/types/delete_skill_version_response.py index cf92c2867..77551f981 100644 --- a/langfuse/api/unstable/skills/types/delete_skill_version_response.py +++ b/langfuse/api/skills/types/delete_skill_version_response.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel class DeleteSkillVersionResponse(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/skill_file.py b/langfuse/api/skills/types/skill_file.py similarity index 70% rename from langfuse/api/unstable/skills/types/skill_file.py rename to langfuse/api/skills/types/skill_file.py index 683b13679..3c3e71252 100644 --- a/langfuse/api/unstable/skills/types/skill_file.py +++ b/langfuse/api/skills/types/skill_file.py @@ -4,8 +4,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata class SkillFile(UniversalBaseModel): @@ -17,6 +17,10 @@ class SkillFile(UniversalBaseModel): content_length: typing_extensions.Annotated[ int, FieldMetadata(alias="contentLength") ] + content: typing.Optional[str] = pydantic.Field(default=None) + """ + UTF-8 text content, included when fetching a skill with includeContent enabled. + """ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( extra="allow", frozen=True diff --git a/langfuse/api/unstable/skills/types/skill_file_content.py b/langfuse/api/skills/types/skill_file_content.py similarity index 78% rename from langfuse/api/unstable/skills/types/skill_file_content.py rename to langfuse/api/skills/types/skill_file_content.py index ce324848d..6ad17badc 100644 --- a/langfuse/api/unstable/skills/types/skill_file_content.py +++ b/langfuse/api/skills/types/skill_file_content.py @@ -4,8 +4,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata class SkillFileContent(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/skill_file_contents_response.py b/langfuse/api/skills/types/skill_file_contents_response.py similarity index 85% rename from langfuse/api/unstable/skills/types/skill_file_contents_response.py rename to langfuse/api/skills/types/skill_file_contents_response.py index 85bb7e2fa..51c8fa90b 100644 --- a/langfuse/api/unstable/skills/types/skill_file_contents_response.py +++ b/langfuse/api/skills/types/skill_file_contents_response.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel from .skill_file_content import SkillFileContent diff --git a/langfuse/api/unstable/skills/types/skill_list_pagination.py b/langfuse/api/skills/types/skill_list_pagination.py similarity index 76% rename from langfuse/api/unstable/skills/types/skill_list_pagination.py rename to langfuse/api/skills/types/skill_list_pagination.py index f7ae797de..e671ebb87 100644 --- a/langfuse/api/unstable/skills/types/skill_list_pagination.py +++ b/langfuse/api/skills/types/skill_list_pagination.py @@ -4,8 +4,8 @@ import pydantic import typing_extensions -from ....core.serialization import FieldMetadata -from ....utils.pagination.types.meta_response import MetaResponse +from ...core.serialization import FieldMetadata +from ...utils.pagination.types.meta_response import MetaResponse class SkillListPagination(MetaResponse): diff --git a/langfuse/api/unstable/skills/types/skill_meta.py b/langfuse/api/skills/types/skill_meta.py similarity index 90% rename from langfuse/api/unstable/skills/types/skill_meta.py rename to langfuse/api/skills/types/skill_meta.py index a391972df..1fa1361f6 100644 --- a/langfuse/api/unstable/skills/types/skill_meta.py +++ b/langfuse/api/skills/types/skill_meta.py @@ -5,8 +5,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata class SkillMeta(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/skill_meta_list_response.py b/langfuse/api/skills/types/skill_meta_list_response.py similarity index 87% rename from langfuse/api/unstable/skills/types/skill_meta_list_response.py rename to langfuse/api/skills/types/skill_meta_list_response.py index a097e1bc0..f2b5da4ab 100644 --- a/langfuse/api/unstable/skills/types/skill_meta_list_response.py +++ b/langfuse/api/skills/types/skill_meta_list_response.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel from .skill_list_pagination import SkillListPagination from .skill_meta import SkillMeta diff --git a/langfuse/api/unstable/skills/types/skill_version.py b/langfuse/api/skills/types/skill_version.py similarity index 90% rename from langfuse/api/unstable/skills/types/skill_version.py rename to langfuse/api/skills/types/skill_version.py index c3486c23d..ff40643e4 100644 --- a/langfuse/api/unstable/skills/types/skill_version.py +++ b/langfuse/api/skills/types/skill_version.py @@ -5,8 +5,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata from .skill_file import SkillFile diff --git a/langfuse/api/unstable/skills/types/skill_version_file_create_input.py b/langfuse/api/skills/types/skill_version_file_create_input.py similarity index 100% rename from langfuse/api/unstable/skills/types/skill_version_file_create_input.py rename to langfuse/api/skills/types/skill_version_file_create_input.py diff --git a/langfuse/api/unstable/skills/types/skill_version_file_input.py b/langfuse/api/skills/types/skill_version_file_input.py similarity index 87% rename from langfuse/api/unstable/skills/types/skill_version_file_input.py rename to langfuse/api/skills/types/skill_version_file_input.py index c6f20d51e..8b4fade29 100644 --- a/langfuse/api/unstable/skills/types/skill_version_file_input.py +++ b/langfuse/api/skills/types/skill_version_file_input.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel class SkillVersionFileInput(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/skill_version_file_reference.py b/langfuse/api/skills/types/skill_version_file_reference.py similarity index 83% rename from langfuse/api/unstable/skills/types/skill_version_file_reference.py rename to langfuse/api/skills/types/skill_version_file_reference.py index 4960d9e90..5d2cd7a7b 100644 --- a/langfuse/api/unstable/skills/types/skill_version_file_reference.py +++ b/langfuse/api/skills/types/skill_version_file_reference.py @@ -4,8 +4,8 @@ import pydantic import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata class SkillVersionFileReference(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/update_skill_labels_request.py b/langfuse/api/skills/types/update_skill_labels_request.py similarity index 83% rename from langfuse/api/unstable/skills/types/update_skill_labels_request.py rename to langfuse/api/skills/types/update_skill_labels_request.py index c1931a1e4..2b6dc9286 100644 --- a/langfuse/api/unstable/skills/types/update_skill_labels_request.py +++ b/langfuse/api/skills/types/update_skill_labels_request.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel class UpdateSkillLabelsRequest(UniversalBaseModel): diff --git a/langfuse/api/unstable/skills/types/update_skill_request.py b/langfuse/api/skills/types/update_skill_request.py similarity index 88% rename from langfuse/api/unstable/skills/types/update_skill_request.py rename to langfuse/api/skills/types/update_skill_request.py index 3c211b921..9d5cce65a 100644 --- a/langfuse/api/unstable/skills/types/update_skill_request.py +++ b/langfuse/api/skills/types/update_skill_request.py @@ -3,7 +3,7 @@ import typing import pydantic -from ....core.pydantic_utilities import UniversalBaseModel +from ...core.pydantic_utilities import UniversalBaseModel class UpdateSkillRequest(UniversalBaseModel): diff --git a/langfuse/api/unstable/__init__.py b/langfuse/api/unstable/__init__.py index 331154d29..39fafb6f8 100644 --- a/langfuse/api/unstable/__init__.py +++ b/langfuse/api/unstable/__init__.py @@ -20,7 +20,7 @@ TooManyRequestsError, UnauthorizedError, ) - from . import dashboard_widgets, dashboards, errors, skills + from . import dashboard_widgets, dashboards, errors, topics from .dashboard_widgets import ( CreateDashboardWidgetRequest, DashboardWidget, @@ -59,21 +59,18 @@ UpdateDashboardRequest, WidgetPlacement, ) - from .skills import ( - CreateSkillVersionRequest, - DeleteSkillVersionResponse, - SkillFile, - SkillFileContent, - SkillFileContentsResponse, - SkillListPagination, - SkillMeta, - SkillMetaListResponse, - SkillVersion, - SkillVersionFileCreateInput, - SkillVersionFileInput, - SkillVersionFileReference, - UpdateSkillLabelsRequest, - UpdateSkillRequest, + from .topics import ( + Topic, + TopicFacet, + TopicFacetCounts, + TopicFacetList, + TopicFacetPublishedRun, + TopicFacetRef, + TopicList, + TopicStatusCounts, + TopicWindow, + TopicWithStatus, + TopicsOrderBy, ) _dynamic_imports: typing.Dict[str, str] = { "AccessDeniedError": ".errors", @@ -85,7 +82,6 @@ "CreateDashboardRequest": ".dashboards", "CreateDashboardWidgetRequest": ".dashboard_widgets", "CreatePresetPlacement": ".dashboards", - "CreateSkillVersionRequest": ".skills", "CreateWidgetPlacement": ".dashboards", "Dashboard": ".dashboards", "DashboardDefinition": ".dashboards", @@ -109,7 +105,6 @@ "DeleteDashboardPlacementResponse": ".dashboards", "DeleteDashboardResponse": ".dashboards", "DeleteDashboardWidgetResponse": ".dashboard_widgets", - "DeleteSkillVersionResponse": ".skills", "InternalServerError": ".errors", "MethodNotAllowedError": ".errors", "NotFoundError": ".errors", @@ -118,28 +113,27 @@ "PublicApiErrorCode": ".errors", "PublicApiErrorDetails": ".errors", "PublicApiValidationIssue": ".errors", - "SkillFile": ".skills", - "SkillFileContent": ".skills", - "SkillFileContentsResponse": ".skills", - "SkillListPagination": ".skills", - "SkillMeta": ".skills", - "SkillMetaListResponse": ".skills", - "SkillVersion": ".skills", - "SkillVersionFileCreateInput": ".skills", - "SkillVersionFileInput": ".skills", - "SkillVersionFileReference": ".skills", "TooManyRequestsError": ".errors", + "Topic": ".topics", + "TopicFacet": ".topics", + "TopicFacetCounts": ".topics", + "TopicFacetList": ".topics", + "TopicFacetPublishedRun": ".topics", + "TopicFacetRef": ".topics", + "TopicList": ".topics", + "TopicStatusCounts": ".topics", + "TopicWindow": ".topics", + "TopicWithStatus": ".topics", + "TopicsOrderBy": ".topics", "UnauthorizedError": ".errors", "UpdateDashboardPlacementRequest": ".dashboards", "UpdateDashboardRequest": ".dashboards", "UpdateDashboardWidgetRequest": ".dashboard_widgets", - "UpdateSkillLabelsRequest": ".skills", - "UpdateSkillRequest": ".skills", "WidgetPlacement": ".dashboards", "dashboard_widgets": ".dashboard_widgets", "dashboards": ".dashboards", "errors": ".errors", - "skills": ".skills", + "topics": ".topics", } @@ -180,7 +174,6 @@ def __dir__(): "CreateDashboardRequest", "CreateDashboardWidgetRequest", "CreatePresetPlacement", - "CreateSkillVersionRequest", "CreateWidgetPlacement", "Dashboard", "DashboardDefinition", @@ -204,7 +197,6 @@ def __dir__(): "DeleteDashboardPlacementResponse", "DeleteDashboardResponse", "DeleteDashboardWidgetResponse", - "DeleteSkillVersionResponse", "InternalServerError", "MethodNotAllowedError", "NotFoundError", @@ -213,26 +205,25 @@ def __dir__(): "PublicApiErrorCode", "PublicApiErrorDetails", "PublicApiValidationIssue", - "SkillFile", - "SkillFileContent", - "SkillFileContentsResponse", - "SkillListPagination", - "SkillMeta", - "SkillMetaListResponse", - "SkillVersion", - "SkillVersionFileCreateInput", - "SkillVersionFileInput", - "SkillVersionFileReference", "TooManyRequestsError", + "Topic", + "TopicFacet", + "TopicFacetCounts", + "TopicFacetList", + "TopicFacetPublishedRun", + "TopicFacetRef", + "TopicList", + "TopicStatusCounts", + "TopicWindow", + "TopicWithStatus", + "TopicsOrderBy", "UnauthorizedError", "UpdateDashboardPlacementRequest", "UpdateDashboardRequest", "UpdateDashboardWidgetRequest", - "UpdateSkillLabelsRequest", - "UpdateSkillRequest", "WidgetPlacement", "dashboard_widgets", "dashboards", "errors", - "skills", + "topics", ] diff --git a/langfuse/api/unstable/client.py b/langfuse/api/unstable/client.py index 3cc2a9298..314109b0f 100644 --- a/langfuse/api/unstable/client.py +++ b/langfuse/api/unstable/client.py @@ -13,7 +13,7 @@ DashboardWidgetsClient, ) from .dashboards.client import AsyncDashboardsClient, DashboardsClient - from .skills.client import AsyncSkillsClient, SkillsClient + from .topics.client import AsyncTopicsClient, TopicsClient class UnstableClient: @@ -22,7 +22,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[DashboardWidgetsClient] = None self._dashboards: typing.Optional[DashboardsClient] = None - self._skills: typing.Optional[SkillsClient] = None + self._topics: typing.Optional[TopicsClient] = None @property def with_raw_response(self) -> RawUnstableClient: @@ -54,12 +54,12 @@ def dashboards(self): return self._dashboards @property - def skills(self): - if self._skills is None: - from .skills.client import SkillsClient # noqa: E402 + def topics(self): + if self._topics is None: + from .topics.client import TopicsClient # noqa: E402 - self._skills = SkillsClient(client_wrapper=self._client_wrapper) - return self._skills + self._topics = TopicsClient(client_wrapper=self._client_wrapper) + return self._topics class AsyncUnstableClient: @@ -68,7 +68,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper self._dashboard_widgets: typing.Optional[AsyncDashboardWidgetsClient] = None self._dashboards: typing.Optional[AsyncDashboardsClient] = None - self._skills: typing.Optional[AsyncSkillsClient] = None + self._topics: typing.Optional[AsyncTopicsClient] = None @property def with_raw_response(self) -> AsyncRawUnstableClient: @@ -102,9 +102,9 @@ def dashboards(self): return self._dashboards @property - def skills(self): - if self._skills is None: - from .skills.client import AsyncSkillsClient # noqa: E402 + def topics(self): + if self._topics is None: + from .topics.client import AsyncTopicsClient # noqa: E402 - self._skills = AsyncSkillsClient(client_wrapper=self._client_wrapper) - return self._skills + self._topics = AsyncTopicsClient(client_wrapper=self._client_wrapper) + return self._topics diff --git a/langfuse/api/unstable/topics/__init__.py b/langfuse/api/unstable/topics/__init__.py new file mode 100644 index 000000000..baa5ee5e9 --- /dev/null +++ b/langfuse/api/unstable/topics/__init__.py @@ -0,0 +1,76 @@ +# 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 ( + Topic, + TopicFacet, + TopicFacetCounts, + TopicFacetList, + TopicFacetPublishedRun, + TopicFacetRef, + TopicList, + TopicStatusCounts, + TopicWindow, + TopicWithStatus, + TopicsOrderBy, + ) +_dynamic_imports: typing.Dict[str, str] = { + "Topic": ".types", + "TopicFacet": ".types", + "TopicFacetCounts": ".types", + "TopicFacetList": ".types", + "TopicFacetPublishedRun": ".types", + "TopicFacetRef": ".types", + "TopicList": ".types", + "TopicStatusCounts": ".types", + "TopicWindow": ".types", + "TopicWithStatus": ".types", + "TopicsOrderBy": ".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__ = [ + "Topic", + "TopicFacet", + "TopicFacetCounts", + "TopicFacetList", + "TopicFacetPublishedRun", + "TopicFacetRef", + "TopicList", + "TopicStatusCounts", + "TopicWindow", + "TopicWithStatus", + "TopicsOrderBy", +] diff --git a/langfuse/api/unstable/topics/client.py b/langfuse/api/unstable/topics/client.py new file mode 100644 index 000000000..9ca28537a --- /dev/null +++ b/langfuse/api/unstable/topics/client.py @@ -0,0 +1,543 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawTopicsClient, RawTopicsClient +from .types.topic_facet import TopicFacet +from .types.topic_facet_list import TopicFacetList +from .types.topic_list import TopicList +from .types.topic_with_status import TopicWithStatus +from .types.topics_order_by import TopicsOrderBy + + +class TopicsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTopicsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTopicsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTopicsClient + """ + return self._raw_client + + def list_facets( + self, + *, + filter: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicFacetList: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the project's Topics facets. Only active facets (attached to the + project's Topics rule, or all facets if the project has no rule) are + returned unless `filter` contains an `active` condition. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `id` (stringOptions), + `name` (string, stringOptions), `description` (string), `isBuiltIn` + (boolean), `active` (boolean). + + Parameters + ---------- + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"string","column":"name","operator":"contains","value":"issue"}]`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicFacetList + + 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.topics.list_facets() + """ + _response = self._raw_client.list_facets( + filter=filter, page=page, limit=limit, request_options=request_options + ) + return _response.data + + def get_facet( + self, facet_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> TopicFacet: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one Topics facet by id, including inactive facets. + + Parameters + ---------- + facet_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicFacet + + 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.topics.get_facet( + facet_id="facetId", + ) + """ + _response = self._raw_client.get_facet( + facet_id, request_options=request_options + ) + return _response.data + + def list( + self, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, + order_by: typing.Optional[TopicsOrderBy] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicList: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the topics of each active facet's current published map, with + the number of traces currently assigned to each topic in the window. + Retired topics are not listed. `facets` holds per-facet trace counts + by topic status. + + The window applies to the trace start time. It defaults to the last + 7 days and can span at most 93 days. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `facetId` + (stringOptions), `facetName` (string, stringOptions), `id` + (stringOptions), `name` (string, stringOptions). Facet conditions also + restrict `facets`. + + Parameters + ---------- + from_timestamp : typing.Optional[dt.datetime] + Start of the window (trace start time, inclusive). Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window (trace start time, exclusive). Defaults to now. + + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"stringOptions","column":"facetName","operator":"any of","value":["Issues"]}]`. + + order_by : typing.Optional[TopicsOrderBy] + Defaults to `traceCount.desc`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicList + + 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.topics.list() + """ + _response = self._raw_client.list( + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + filter=filter, + order_by=order_by, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + def get( + self, + topic_id: str, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicWithStatus: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one topic by its stable id. Topics on the current map are always + found. A retired topic is found while traces in the window are still + assigned to it, with `inCurrentMap: false`. + + Parameters + ---------- + topic_id : str + + from_timestamp : typing.Optional[dt.datetime] + Start of the window used for `traceCount`. Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window used for `traceCount`. Defaults to now. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicWithStatus + + 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.topics.get( + topic_id="topicId", + ) + """ + _response = self._raw_client.get( + topic_id, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + request_options=request_options, + ) + return _response.data + + +class AsyncTopicsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTopicsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTopicsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTopicsClient + """ + return self._raw_client + + async def list_facets( + self, + *, + filter: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicFacetList: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the project's Topics facets. Only active facets (attached to the + project's Topics rule, or all facets if the project has no rule) are + returned unless `filter` contains an `active` condition. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `id` (stringOptions), + `name` (string, stringOptions), `description` (string), `isBuiltIn` + (boolean), `active` (boolean). + + Parameters + ---------- + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"string","column":"name","operator":"contains","value":"issue"}]`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicFacetList + + 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.topics.list_facets() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_facets( + filter=filter, page=page, limit=limit, request_options=request_options + ) + return _response.data + + async def get_facet( + self, facet_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> TopicFacet: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one Topics facet by id, including inactive facets. + + Parameters + ---------- + facet_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicFacet + + 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.topics.get_facet( + facet_id="facetId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_facet( + facet_id, request_options=request_options + ) + return _response.data + + async def list( + self, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, + order_by: typing.Optional[TopicsOrderBy] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicList: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the topics of each active facet's current published map, with + the number of traces currently assigned to each topic in the window. + Retired topics are not listed. `facets` holds per-facet trace counts + by topic status. + + The window applies to the trace start time. It defaults to the last + 7 days and can span at most 93 days. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `facetId` + (stringOptions), `facetName` (string, stringOptions), `id` + (stringOptions), `name` (string, stringOptions). Facet conditions also + restrict `facets`. + + Parameters + ---------- + from_timestamp : typing.Optional[dt.datetime] + Start of the window (trace start time, inclusive). Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window (trace start time, exclusive). Defaults to now. + + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"stringOptions","column":"facetName","operator":"any of","value":["Issues"]}]`. + + order_by : typing.Optional[TopicsOrderBy] + Defaults to `traceCount.desc`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicList + + 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.topics.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + filter=filter, + order_by=order_by, + page=page, + limit=limit, + request_options=request_options, + ) + return _response.data + + async def get( + self, + topic_id: str, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> TopicWithStatus: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one topic by its stable id. Topics on the current map are always + found. A retired topic is found while traces in the window are still + assigned to it, with `inCurrentMap: false`. + + Parameters + ---------- + topic_id : str + + from_timestamp : typing.Optional[dt.datetime] + Start of the window used for `traceCount`. Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window used for `traceCount`. Defaults to now. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TopicWithStatus + + 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.topics.get( + topic_id="topicId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + topic_id, + from_timestamp=from_timestamp, + to_timestamp=to_timestamp, + request_options=request_options, + ) + return _response.data diff --git a/langfuse/api/unstable/topics/raw_client.py b/langfuse/api/unstable/topics/raw_client.py new file mode 100644 index 000000000..2a28528a4 --- /dev/null +++ b/langfuse/api/unstable/topics/raw_client.py @@ -0,0 +1,1675 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +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.datetime_utils import serialize_datetime +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 ..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.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.topic_facet import TopicFacet +from .types.topic_facet_list import TopicFacetList +from .types.topic_list import TopicList +from .types.topic_with_status import TopicWithStatus +from .types.topics_order_by import TopicsOrderBy + + +class RawTopicsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_facets( + self, + *, + filter: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TopicFacetList]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the project's Topics facets. Only active facets (attached to the + project's Topics rule, or all facets if the project has no rule) are + returned unless `filter` contains an `active` condition. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `id` (stringOptions), + `name` (string, stringOptions), `description` (string), `isBuiltIn` + (boolean), `active` (boolean). + + Parameters + ---------- + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"string","column":"name","operator":"contains","value":"issue"}]`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TopicFacetList] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/topic-facets", + method="GET", + params={ + "filter": filter, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicFacetList, + parse_obj_as( + type_=TopicFacetList, # 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_facet( + self, facet_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TopicFacet]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one Topics facet by id, including inactive facets. + + Parameters + ---------- + facet_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TopicFacet] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/topic-facets/{jsonable_encoder(facet_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicFacet, + parse_obj_as( + type_=TopicFacet, # 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 list( + self, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, + order_by: typing.Optional[TopicsOrderBy] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TopicList]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the topics of each active facet's current published map, with + the number of traces currently assigned to each topic in the window. + Retired topics are not listed. `facets` holds per-facet trace counts + by topic status. + + The window applies to the trace start time. It defaults to the last + 7 days and can span at most 93 days. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `facetId` + (stringOptions), `facetName` (string, stringOptions), `id` + (stringOptions), `name` (string, stringOptions). Facet conditions also + restrict `facets`. + + Parameters + ---------- + from_timestamp : typing.Optional[dt.datetime] + Start of the window (trace start time, inclusive). Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window (trace start time, exclusive). Defaults to now. + + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"stringOptions","column":"facetName","operator":"any of","value":["Issues"]}]`. + + order_by : typing.Optional[TopicsOrderBy] + Defaults to `traceCount.desc`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TopicList] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/unstable/topics", + method="GET", + params={ + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, + "filter": filter, + "orderBy": order_by, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicList, + parse_obj_as( + type_=TopicList, # 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, + topic_id: str, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TopicWithStatus]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one topic by its stable id. Topics on the current map are always + found. A retired topic is found while traces in the window are still + assigned to it, with `inCurrentMap: false`. + + Parameters + ---------- + topic_id : str + + from_timestamp : typing.Optional[dt.datetime] + Start of the window used for `traceCount`. Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window used for `traceCount`. Defaults to now. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TopicWithStatus] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/unstable/topics/{jsonable_encoder(topic_id)}", + method="GET", + params={ + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicWithStatus, + parse_obj_as( + type_=TopicWithStatus, # 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 AsyncRawTopicsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_facets( + self, + *, + filter: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TopicFacetList]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the project's Topics facets. Only active facets (attached to the + project's Topics rule, or all facets if the project has no rule) are + returned unless `filter` contains an `active` condition. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `id` (stringOptions), + `name` (string, stringOptions), `description` (string), `isBuiltIn` + (boolean), `active` (boolean). + + Parameters + ---------- + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"string","column":"name","operator":"contains","value":"issue"}]`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TopicFacetList] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/topic-facets", + method="GET", + params={ + "filter": filter, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicFacetList, + parse_obj_as( + type_=TopicFacetList, # 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_facet( + self, facet_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TopicFacet]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one Topics facet by id, including inactive facets. + + Parameters + ---------- + facet_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TopicFacet] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/topic-facets/{jsonable_encoder(facet_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicFacet, + parse_obj_as( + type_=TopicFacet, # 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 list( + self, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + filter: typing.Optional[str] = None, + order_by: typing.Optional[TopicsOrderBy] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TopicList]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + List the topics of each active facet's current published map, with + the number of traces currently assigned to each topic in the window. + Retired topics are not listed. `facets` holds per-facet trace counts + by topic status. + + The window applies to the trace start time. It defaults to the last + 7 days and can span at most 93 days. + + `filter` is a JSON array of filter conditions combined with AND. + String matching is case-insensitive. Columns: `facetId` + (stringOptions), `facetName` (string, stringOptions), `id` + (stringOptions), `name` (string, stringOptions). Facet conditions also + restrict `facets`. + + Parameters + ---------- + from_timestamp : typing.Optional[dt.datetime] + Start of the window (trace start time, inclusive). Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window (trace start time, exclusive). Defaults to now. + + filter : typing.Optional[str] + JSON array of filter conditions, for example `[{"type":"stringOptions","column":"facetName","operator":"any of","value":["Issues"]}]`. + + order_by : typing.Optional[TopicsOrderBy] + Defaults to `traceCount.desc`. + + page : typing.Optional[int] + 1-based page number. Defaults to `1`. + + limit : typing.Optional[int] + Maximum number of items per page. Defaults to `50`, at most `100`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TopicList] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/unstable/topics", + method="GET", + params={ + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, + "filter": filter, + "orderBy": order_by, + "page": page, + "limit": limit, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicList, + parse_obj_as( + type_=TopicList, # 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, + topic_id: str, + *, + from_timestamp: typing.Optional[dt.datetime] = None, + to_timestamp: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TopicWithStatus]: + """ + Unstable: Topics is in closed beta. The shape may change without notice. + + Get one topic by its stable id. Topics on the current map are always + found. A retired topic is found while traces in the window are still + assigned to it, with `inCurrentMap: false`. + + Parameters + ---------- + topic_id : str + + from_timestamp : typing.Optional[dt.datetime] + Start of the window used for `traceCount`. Defaults to 7 days before `toTimestamp`. + + to_timestamp : typing.Optional[dt.datetime] + End of the window used for `traceCount`. Defaults to now. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TopicWithStatus] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/unstable/topics/{jsonable_encoder(topic_id)}", + method="GET", + params={ + "fromTimestamp": serialize_datetime(from_timestamp) + if from_timestamp is not None + else None, + "toTimestamp": serialize_datetime(to_timestamp) + if to_timestamp is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TopicWithStatus, + parse_obj_as( + type_=TopicWithStatus, # 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/topics/types/__init__.py b/langfuse/api/unstable/topics/types/__init__.py new file mode 100644 index 000000000..4e6e56333 --- /dev/null +++ b/langfuse/api/unstable/topics/types/__init__.py @@ -0,0 +1,74 @@ +# 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 .topic import Topic + from .topic_facet import TopicFacet + from .topic_facet_counts import TopicFacetCounts + from .topic_facet_list import TopicFacetList + from .topic_facet_published_run import TopicFacetPublishedRun + from .topic_facet_ref import TopicFacetRef + from .topic_list import TopicList + from .topic_status_counts import TopicStatusCounts + from .topic_window import TopicWindow + from .topic_with_status import TopicWithStatus + from .topics_order_by import TopicsOrderBy +_dynamic_imports: typing.Dict[str, str] = { + "Topic": ".topic", + "TopicFacet": ".topic_facet", + "TopicFacetCounts": ".topic_facet_counts", + "TopicFacetList": ".topic_facet_list", + "TopicFacetPublishedRun": ".topic_facet_published_run", + "TopicFacetRef": ".topic_facet_ref", + "TopicList": ".topic_list", + "TopicStatusCounts": ".topic_status_counts", + "TopicWindow": ".topic_window", + "TopicWithStatus": ".topic_with_status", + "TopicsOrderBy": ".topics_order_by", +} + + +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__ = [ + "Topic", + "TopicFacet", + "TopicFacetCounts", + "TopicFacetList", + "TopicFacetPublishedRun", + "TopicFacetRef", + "TopicList", + "TopicStatusCounts", + "TopicWindow", + "TopicWithStatus", + "TopicsOrderBy", +] diff --git a/langfuse/api/unstable/topics/types/topic.py b/langfuse/api/unstable/topics/types/topic.py new file mode 100644 index 000000000..e32938810 --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic.py @@ -0,0 +1,44 @@ +# 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 .topic_facet_ref import TopicFacetRef + + +class Topic(UniversalBaseModel): + id: str = pydantic.Field() + """ + Stable topic id, kept across map updates. + """ + + version_id: typing_extensions.Annotated[str, FieldMetadata(alias="versionId")] = ( + pydantic.Field() + ) + """ + Id of this topic version. + """ + + name: str + description: str + facet: TopicFacetRef + run_id: typing_extensions.Annotated[str, FieldMetadata(alias="runId")] = ( + pydantic.Field() + ) + """ + The run that produced this topic version. + """ + + trace_count: typing_extensions.Annotated[int, FieldMetadata(alias="traceCount")] = ( + pydantic.Field() + ) + """ + Traces in the window whose current result for the facet is this topic. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_facet.py b/langfuse/api/unstable/topics/types/topic_facet.py new file mode 100644 index 000000000..2f3673abf --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_facet.py @@ -0,0 +1,41 @@ +# 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 .topic_facet_published_run import TopicFacetPublishedRun + + +class TopicFacet(UniversalBaseModel): + id: str + name: str + description: str + is_built_in: typing_extensions.Annotated[bool, FieldMetadata(alias="isBuiltIn")] + active: bool = pydantic.Field() + """ + Whether the facet is attached to the project's Topics rule. True for all facets if the project has no rule. + """ + + version: int = pydantic.Field() + """ + Latest facet version. + """ + + prompt: str = pydantic.Field() + """ + Prompt of the latest facet version. + """ + + published_run: typing_extensions.Annotated[ + typing.Optional[TopicFacetPublishedRun], FieldMetadata(alias="publishedRun") + ] = pydantic.Field(default=None) + """ + The run that produced the facet's current map. Null before the first map. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_facet_counts.py b/langfuse/api/unstable/topics/types/topic_facet_counts.py new file mode 100644 index 000000000..0bc83e2cf --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_facet_counts.py @@ -0,0 +1,22 @@ +# 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 .topic_facet_ref import TopicFacetRef +from .topic_status_counts import TopicStatusCounts + + +class TopicFacetCounts(UniversalBaseModel): + facet: TopicFacetRef + published_run_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="publishedRunId") + ] = None + counts: TopicStatusCounts + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_facet_list.py b/langfuse/api/unstable/topics/types/topic_facet_list.py new file mode 100644 index 000000000..7fed41d86 --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_facet_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 .topic_facet import TopicFacet + + +class TopicFacetList(UniversalBaseModel): + data: typing.List[TopicFacet] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_facet_published_run.py b/langfuse/api/unstable/topics/types/topic_facet_published_run.py new file mode 100644 index 000000000..956a385ef --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_facet_published_run.py @@ -0,0 +1,21 @@ +# 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 + + +class TopicFacetPublishedRun(UniversalBaseModel): + id: str + completed_at: typing_extensions.Annotated[ + typing.Optional[dt.datetime], FieldMetadata(alias="completedAt") + ] = None + topic_count: typing_extensions.Annotated[int, FieldMetadata(alias="topicCount")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_facet_ref.py b/langfuse/api/unstable/topics/types/topic_facet_ref.py new file mode 100644 index 000000000..5371a35fb --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_facet_ref.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import UniversalBaseModel + + +class TopicFacetRef(UniversalBaseModel): + id: str + name: str + version: int + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_list.py b/langfuse/api/unstable/topics/types/topic_list.py new file mode 100644 index 000000000..405c2ed3c --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_list.py @@ -0,0 +1,21 @@ +# 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 .topic import Topic +from .topic_facet_counts import TopicFacetCounts +from .topic_window import TopicWindow + + +class TopicList(UniversalBaseModel): + window: TopicWindow + data: typing.List[Topic] + facets: typing.List[TopicFacetCounts] + meta: MetaResponse + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_status_counts.py b/langfuse/api/unstable/topics/types/topic_status_counts.py new file mode 100644 index 000000000..df9fc220c --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_status_counts.py @@ -0,0 +1,28 @@ +# 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 TopicStatusCounts(UniversalBaseModel): + """ + Traces in the window by their current result for one facet. + """ + + assigned: int + outlier: int + awaiting_map: typing_extensions.Annotated[int, FieldMetadata(alias="awaitingMap")] + not_applicable: typing_extensions.Annotated[ + int, FieldMetadata(alias="notApplicable") + ] + insufficient_input: typing_extensions.Annotated[ + int, FieldMetadata(alias="insufficientInput") + ] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_window.py b/langfuse/api/unstable/topics/types/topic_window.py new file mode 100644 index 000000000..956b74df5 --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_window.py @@ -0,0 +1,18 @@ +# 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 + + +class TopicWindow(UniversalBaseModel): + from_: typing_extensions.Annotated[dt.datetime, FieldMetadata(alias="from")] + to: dt.datetime + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topic_with_status.py b/langfuse/api/unstable/topics/types/topic_with_status.py new file mode 100644 index 000000000..ca2749b5a --- /dev/null +++ b/langfuse/api/unstable/topics/types/topic_with_status.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ....core.serialization import FieldMetadata +from .topic import Topic +from .topic_window import TopicWindow + + +class TopicWithStatus(Topic): + in_current_map: typing_extensions.Annotated[ + bool, FieldMetadata(alias="inCurrentMap") + ] = pydantic.Field() + """ + False for retired topics that still have assigned traces in the window. + """ + + window: TopicWindow + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/unstable/topics/types/topics_order_by.py b/langfuse/api/unstable/topics/types/topics_order_by.py new file mode 100644 index 000000000..7ecb2db35 --- /dev/null +++ b/langfuse/api/unstable/topics/types/topics_order_by.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core import enum + +T_Result = typing.TypeVar("T_Result") + + +class TopicsOrderBy(enum.StrEnum): + TRACE_COUNT_DESC = "traceCount.desc" + TRACE_COUNT_ASC = "traceCount.asc" + NAME_ASC = "name.asc" + NAME_DESC = "name.desc" + + def visit( + self, + trace_count_desc: typing.Callable[[], T_Result], + trace_count_asc: typing.Callable[[], T_Result], + name_asc: typing.Callable[[], T_Result], + name_desc: typing.Callable[[], T_Result], + ) -> T_Result: + if self is TopicsOrderBy.TRACE_COUNT_DESC: + return trace_count_desc() + if self is TopicsOrderBy.TRACE_COUNT_ASC: + return trace_count_asc() + if self is TopicsOrderBy.NAME_ASC: + return name_asc() + if self is TopicsOrderBy.NAME_DESC: + return name_desc()