From e6d165421d076fd292a157a3c5d76574042ff399 Mon Sep 17 00:00:00 2001 From: Tobias Klesel Date: Mon, 26 Oct 2026 18:44:48 +0100 Subject: [PATCH 1/3] feat(api): expose stable skills client --- langfuse/api/__init__.py | 47 + langfuse/api/client.py | 19 + langfuse/api/skills/__init__.py | 85 + langfuse/api/skills/client.py | 824 ++++++++ langfuse/api/skills/raw_client.py | 1655 +++++++++++++++++ langfuse/api/skills/types/__init__.py | 83 + .../types/create_skill_version_request.py | 20 + .../types/delete_skill_version_response.py | 14 + langfuse/api/skills/types/skill_file.py | 27 + .../api/skills/types/skill_file_content.py | 17 + .../types/skill_file_contents_response.py | 15 + .../api/skills/types/skill_list_pagination.py | 16 + langfuse/api/skills/types/skill_meta.py | 34 + .../skills/types/skill_meta_list_response.py | 17 + langfuse/api/skills/types/skill_version.py | 36 + .../types/skill_version_file_create_input.py | 10 + .../skills/types/skill_version_file_input.py | 18 + .../types/skill_version_file_reference.py | 22 + .../types/update_skill_labels_request.py | 14 + .../api/skills/types/update_skill_request.py | 17 + tests/unit/test_api_skills.py | 86 + 21 files changed, 3076 insertions(+) create mode 100644 langfuse/api/skills/__init__.py create mode 100644 langfuse/api/skills/client.py create mode 100644 langfuse/api/skills/raw_client.py create mode 100644 langfuse/api/skills/types/__init__.py create mode 100644 langfuse/api/skills/types/create_skill_version_request.py create mode 100644 langfuse/api/skills/types/delete_skill_version_response.py create mode 100644 langfuse/api/skills/types/skill_file.py create mode 100644 langfuse/api/skills/types/skill_file_content.py create mode 100644 langfuse/api/skills/types/skill_file_contents_response.py create mode 100644 langfuse/api/skills/types/skill_list_pagination.py create mode 100644 langfuse/api/skills/types/skill_meta.py create mode 100644 langfuse/api/skills/types/skill_meta_list_response.py create mode 100644 langfuse/api/skills/types/skill_version.py create mode 100644 langfuse/api/skills/types/skill_version_file_create_input.py create mode 100644 langfuse/api/skills/types/skill_version_file_input.py create mode 100644 langfuse/api/skills/types/skill_version_file_reference.py create mode 100644 langfuse/api/skills/types/update_skill_labels_request.py create mode 100644 langfuse/api/skills/types/update_skill_request.py create mode 100644 tests/unit/test_api_skills.py 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/skills/__init__.py b/langfuse/api/skills/__init__.py new file mode 100644 index 000000000..710cf53a6 --- /dev/null +++ b/langfuse/api/skills/__init__.py @@ -0,0 +1,85 @@ +# 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 ( + CreateSkillVersionRequest, + DeleteSkillVersionResponse, + SkillFile, + SkillFileContent, + SkillFileContentsResponse, + SkillListPagination, + SkillMeta, + SkillMetaListResponse, + SkillVersion, + SkillVersionFileCreateInput, + SkillVersionFileInput, + SkillVersionFileReference, + UpdateSkillLabelsRequest, + UpdateSkillRequest, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateSkillVersionRequest": ".types", + "DeleteSkillVersionResponse": ".types", + "SkillFile": ".types", + "SkillFileContent": ".types", + "SkillFileContentsResponse": ".types", + "SkillListPagination": ".types", + "SkillMeta": ".types", + "SkillMetaListResponse": ".types", + "SkillVersion": ".types", + "SkillVersionFileCreateInput": ".types", + "SkillVersionFileInput": ".types", + "SkillVersionFileReference": ".types", + "UpdateSkillLabelsRequest": ".types", + "UpdateSkillRequest": ".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__ = [ + "CreateSkillVersionRequest", + "DeleteSkillVersionResponse", + "SkillFile", + "SkillFileContent", + "SkillFileContentsResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", +] diff --git a/langfuse/api/skills/client.py b/langfuse/api/skills/client.py new file mode 100644 index 000000000..6d0ba9739 --- /dev/null +++ b/langfuse/api/skills/client.py @@ -0,0 +1,824 @@ +# 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 AsyncRawSkillsClient, RawSkillsClient +from .types.delete_skill_version_response import DeleteSkillVersionResponse +from .types.skill_file_contents_response import SkillFileContentsResponse +from .types.skill_meta_list_response import SkillMetaListResponse +from .types.skill_version import SkillVersion +from .types.skill_version_file_create_input import SkillVersionFileCreateInput + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class SkillsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSkillsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSkillsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSkillsClient + """ + return self._raw_client + + def list( + self, + *, + name: typing.Optional[str] = None, + search: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillMetaListResponse: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillMetaListResponse + + 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.skills.list() + """ + _response = self._raw_client.list( + name=name, + search=search, + tag=tag, + page=page, + limit=limit, + from_updated_at=from_updated_at, + to_updated_at=to_updated_at, + request_options=request_options, + ) + return _response.data + + def create_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + Examples + -------- + from langfuse import LangfuseAPI + from langfuse.skills import SkillVersionFileInput + + 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.skills.create_version( + files=[ + SkillVersionFileInput( + path="path", + content="content", + ), + SkillVersionFileInput( + path="path", + content="content", + ), + ], + ) + """ + _response = self._raw_client.create_version( + files=files, commit_message=commit_message, request_options=request_options + ) + return _response.data + + def get( + self, + skill_name: str, + *, + 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. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + 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. + + Returns + ------- + SkillVersion + + 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.skills.get( + skill_name="skillName", + ) + """ + _response = self._raw_client.get( + skill_name, + version=version, + label=label, + include_content=include_content, + request_options=request_options, + ) + return _response.data + + def update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.skills.update( + skill_name="skillName", + tags=["tags", "tags"], + ) + """ + _response = self._raw_client.update( + skill_name, tags=tags, request_options=request_options + ) + return _response.data + + def get_file_contents( + self, + *, + sha256hashes: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillFileContentsResponse: + """ + Read a batch of text contents by canonical base64-encoded SHA-256 hashes. + + Parameters + ---------- + sha256hashes : str + Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillFileContentsResponse + + 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.skills.get_file_contents( + sha256hashes="sha256Hashes", + ) + """ + _response = self._raw_client.get_file_contents( + sha256hashes=sha256hashes, request_options=request_options + ) + return _response.data + + def set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.skills.set_labels( + skill_name="skillName", + skill_version=1, + labels=["labels", "labels"], + ) + """ + _response = self._raw_client.set_labels( + skill_name, skill_version, labels=labels, request_options=request_options + ) + return _response.data + + def delete_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteSkillVersionResponse: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSkillVersionResponse + + 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.skills.delete_version( + skill_name="skillName", + skill_version=1, + ) + """ + _response = self._raw_client.delete_version( + skill_name, skill_version, request_options=request_options + ) + return _response.data + + +class AsyncSkillsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSkillsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSkillsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSkillsClient + """ + return self._raw_client + + async def list( + self, + *, + name: typing.Optional[str] = None, + search: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillMetaListResponse: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillMetaListResponse + + 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.skills.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + name=name, + search=search, + tag=tag, + page=page, + limit=limit, + from_updated_at=from_updated_at, + to_updated_at=to_updated_at, + request_options=request_options, + ) + return _response.data + + async def create_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + Examples + -------- + import asyncio + + from langfuse import AsyncLangfuseAPI + from langfuse.skills import SkillVersionFileInput + + 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.skills.create_version( + files=[ + SkillVersionFileInput( + path="path", + content="content", + ), + SkillVersionFileInput( + path="path", + content="content", + ), + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_version( + files=files, commit_message=commit_message, request_options=request_options + ) + return _response.data + + async def get( + self, + skill_name: str, + *, + 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. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + 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. + + Returns + ------- + SkillVersion + + 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.skills.get( + skill_name="skillName", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + skill_name, + version=version, + label=label, + include_content=include_content, + request_options=request_options, + ) + return _response.data + + async def update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.skills.update( + skill_name="skillName", + tags=["tags", "tags"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + skill_name, tags=tags, request_options=request_options + ) + return _response.data + + async def get_file_contents( + self, + *, + sha256hashes: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillFileContentsResponse: + """ + Read a batch of text contents by canonical base64-encoded SHA-256 hashes. + + Parameters + ---------- + sha256hashes : str + Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillFileContentsResponse + + 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.skills.get_file_contents( + sha256hashes="sha256Hashes", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_file_contents( + sha256hashes=sha256hashes, request_options=request_options + ) + return _response.data + + async def set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> SkillVersion: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SkillVersion + + 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.skills.set_labels( + skill_name="skillName", + skill_version=1, + labels=["labels", "labels"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.set_labels( + skill_name, skill_version, labels=labels, request_options=request_options + ) + return _response.data + + async def delete_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> DeleteSkillVersionResponse: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteSkillVersionResponse + + 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.skills.delete_version( + skill_name="skillName", + skill_version=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_version( + skill_name, skill_version, request_options=request_options + ) + return _response.data diff --git a/langfuse/api/skills/raw_client.py b/langfuse/api/skills/raw_client.py new file mode 100644 index 000000000..bf7efa0cd --- /dev/null +++ b/langfuse/api/skills/raw_client.py @@ -0,0 +1,1655 @@ +# 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 +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 +from .types.skill_version import SkillVersion +from .types.skill_version_file_create_input import SkillVersionFileCreateInput + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawSkillsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + name: typing.Optional[str] = None, + search: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillMetaListResponse]: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillMetaListResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/skills", + method="GET", + params={ + "name": name, + "search": search, + "tag": tag, + "page": page, + "limit": limit, + "fromUpdatedAt": serialize_datetime(from_updated_at) + if from_updated_at is not None + else None, + "toUpdatedAt": serialize_datetime(to_updated_at) + if to_updated_at is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillMetaListResponse, + parse_obj_as( + type_=SkillMetaListResponse, # 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 create_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/skills", + method="POST", + json={ + "files": convert_and_respect_annotation_metadata( + object_=files, + annotation=typing.Sequence[SkillVersionFileCreateInput], + direction="write", + ), + "commitMessage": commit_message, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 get( + self, + skill_name: str, + *, + 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. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + 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. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}", + method="GET", + params={ + "version": version, + "label": label, + "includeContent": include_content, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}", + method="PATCH", + json={ + "tags": tags, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 get_file_contents( + self, + *, + sha256hashes: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillFileContentsResponse]: + """ + Read a batch of text contents by canonical base64-encoded SHA-256 hashes. + + Parameters + ---------- + sha256hashes : str + Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillFileContentsResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/skills/files/content", + method="GET", + params={ + "sha256Hashes": sha256hashes, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillFileContentsResponse, + parse_obj_as( + type_=SkillFileContentsResponse, # 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 set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SkillVersion]: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SkillVersion] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="PATCH", + json={ + "labels": labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DeleteSkillVersionResponse]: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteSkillVersionResponse] + """ + _response = self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSkillVersionResponse, + parse_obj_as( + type_=DeleteSkillVersionResponse, # 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 AsyncRawSkillsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + name: typing.Optional[str] = None, + search: typing.Optional[str] = None, + tag: typing.Optional[str] = None, + page: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + from_updated_at: typing.Optional[dt.datetime] = None, + to_updated_at: typing.Optional[dt.datetime] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillMetaListResponse]: + """ + List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. + + Parameters + ---------- + name : typing.Optional[str] + Filter by exact skill name. + + search : typing.Optional[str] + Case-insensitive search across skill names and latest-version descriptions. + + tag : typing.Optional[str] + Filter by a shared skill tag. + + page : typing.Optional[int] + + limit : typing.Optional[int] + + from_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated at or after this timestamp. + + to_updated_at : typing.Optional[dt.datetime] + Include skills whose latest version was updated before this timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillMetaListResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/skills", + method="GET", + params={ + "name": name, + "search": search, + "tag": tag, + "page": page, + "limit": limit, + "fromUpdatedAt": serialize_datetime(from_updated_at) + if from_updated_at is not None + else None, + "toUpdatedAt": serialize_datetime(to_updated_at) + if to_updated_at is not None + else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillMetaListResponse, + parse_obj_as( + type_=SkillMetaListResponse, # 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 create_version( + self, + *, + files: typing.Sequence[SkillVersionFileCreateInput], + commit_message: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Create skill version + + Parameters + ---------- + files : typing.Sequence[SkillVersionFileCreateInput] + + commit_message : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/skills", + method="POST", + json={ + "files": convert_and_respect_annotation_metadata( + object_=files, + annotation=typing.Sequence[SkillVersionFileCreateInput], + direction="write", + ), + "commitMessage": commit_message, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 get( + self, + skill_name: str, + *, + 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. Set includeContent to return file contents inline, or use each file's sha256Hash with getFileContents. + + Parameters + ---------- + skill_name : str + + version : typing.Optional[int] + + 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. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}", + method="GET", + params={ + "version": version, + "label": label, + "includeContent": include_content, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 update( + self, + skill_name: str, + *, + tags: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. + + Parameters + ---------- + skill_name : str + + tags : typing.Sequence[str] + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}", + method="PATCH", + json={ + "tags": tags, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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 get_file_contents( + self, + *, + sha256hashes: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillFileContentsResponse]: + """ + Read a batch of text contents by canonical base64-encoded SHA-256 hashes. + + Parameters + ---------- + sha256hashes : str + Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillFileContentsResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/skills/files/content", + method="GET", + params={ + "sha256Hashes": sha256hashes, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillFileContentsResponse, + parse_obj_as( + type_=SkillFileContentsResponse, # 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 set_labels( + self, + skill_name: str, + skill_version: int, + *, + labels: typing.Sequence[str], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[SkillVersion]: + """ + Replace the labels on a skill version and atomically move them from other versions. + + Parameters + ---------- + skill_name : str + + skill_version : int + + labels : typing.Sequence[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SkillVersion] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="PATCH", + json={ + "labels": labels, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SkillVersion, + parse_obj_as( + type_=SkillVersion, # 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_version( + self, + skill_name: str, + skill_version: int, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DeleteSkillVersionResponse]: + """ + Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. + + Parameters + ---------- + skill_name : str + + skill_version : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteSkillVersionResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/public/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteSkillVersionResponse, + parse_obj_as( + type_=DeleteSkillVersionResponse, # 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, + ) diff --git a/langfuse/api/skills/types/__init__.py b/langfuse/api/skills/types/__init__.py new file mode 100644 index 000000000..bafccc040 --- /dev/null +++ b/langfuse/api/skills/types/__init__.py @@ -0,0 +1,83 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_skill_version_request import CreateSkillVersionRequest + from .delete_skill_version_response import DeleteSkillVersionResponse + from .skill_file import SkillFile + from .skill_file_content import SkillFileContent + from .skill_file_contents_response import SkillFileContentsResponse + from .skill_list_pagination import SkillListPagination + from .skill_meta import SkillMeta + from .skill_meta_list_response import SkillMetaListResponse + from .skill_version import SkillVersion + from .skill_version_file_create_input import SkillVersionFileCreateInput + from .skill_version_file_input import SkillVersionFileInput + from .skill_version_file_reference import SkillVersionFileReference + from .update_skill_labels_request import UpdateSkillLabelsRequest + from .update_skill_request import UpdateSkillRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateSkillVersionRequest": ".create_skill_version_request", + "DeleteSkillVersionResponse": ".delete_skill_version_response", + "SkillFile": ".skill_file", + "SkillFileContent": ".skill_file_content", + "SkillFileContentsResponse": ".skill_file_contents_response", + "SkillListPagination": ".skill_list_pagination", + "SkillMeta": ".skill_meta", + "SkillMetaListResponse": ".skill_meta_list_response", + "SkillVersion": ".skill_version", + "SkillVersionFileCreateInput": ".skill_version_file_create_input", + "SkillVersionFileInput": ".skill_version_file_input", + "SkillVersionFileReference": ".skill_version_file_reference", + "UpdateSkillLabelsRequest": ".update_skill_labels_request", + "UpdateSkillRequest": ".update_skill_request", +} + + +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__ = [ + "CreateSkillVersionRequest", + "DeleteSkillVersionResponse", + "SkillFile", + "SkillFileContent", + "SkillFileContentsResponse", + "SkillListPagination", + "SkillMeta", + "SkillMetaListResponse", + "SkillVersion", + "SkillVersionFileCreateInput", + "SkillVersionFileInput", + "SkillVersionFileReference", + "UpdateSkillLabelsRequest", + "UpdateSkillRequest", +] diff --git a/langfuse/api/skills/types/create_skill_version_request.py b/langfuse/api/skills/types/create_skill_version_request.py new file mode 100644 index 000000000..5f2997b69 --- /dev/null +++ b/langfuse/api/skills/types/create_skill_version_request.py @@ -0,0 +1,20 @@ +# 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 .skill_version_file_create_input import SkillVersionFileCreateInput + + +class CreateSkillVersionRequest(UniversalBaseModel): + files: typing.List[SkillVersionFileCreateInput] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/delete_skill_version_response.py b/langfuse/api/skills/types/delete_skill_version_response.py new file mode 100644 index 000000000..77551f981 --- /dev/null +++ b/langfuse/api/skills/types/delete_skill_version_response.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class DeleteSkillVersionResponse(UniversalBaseModel): + deleted: bool + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_file.py b/langfuse/api/skills/types/skill_file.py new file mode 100644 index 000000000..3c3e71252 --- /dev/null +++ b/langfuse/api/skills/types/skill_file.py @@ -0,0 +1,27 @@ +# 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 SkillFile(UniversalBaseModel): + id: str + path: str + blob_id: typing_extensions.Annotated[str, FieldMetadata(alias="blobId")] + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] + content_type: typing_extensions.Annotated[str, FieldMetadata(alias="contentType")] + 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/skills/types/skill_file_content.py b/langfuse/api/skills/types/skill_file_content.py new file mode 100644 index 000000000..6ad17badc --- /dev/null +++ b/langfuse/api/skills/types/skill_file_content.py @@ -0,0 +1,17 @@ +# 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 SkillFileContent(UniversalBaseModel): + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] + content: str + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_file_contents_response.py b/langfuse/api/skills/types/skill_file_contents_response.py new file mode 100644 index 000000000..51c8fa90b --- /dev/null +++ b/langfuse/api/skills/types/skill_file_contents_response.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel +from .skill_file_content import SkillFileContent + + +class SkillFileContentsResponse(UniversalBaseModel): + data: typing.List[SkillFileContent] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_list_pagination.py b/langfuse/api/skills/types/skill_list_pagination.py new file mode 100644 index 000000000..e671ebb87 --- /dev/null +++ b/langfuse/api/skills/types/skill_list_pagination.py @@ -0,0 +1,16 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.serialization import FieldMetadata +from ...utils.pagination.types.meta_response import MetaResponse + + +class SkillListPagination(MetaResponse): + has_next_page: typing_extensions.Annotated[bool, FieldMetadata(alias="hasNextPage")] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_meta.py b/langfuse/api/skills/types/skill_meta.py new file mode 100644 index 000000000..1fa1361f6 --- /dev/null +++ b/langfuse/api/skills/types/skill_meta.py @@ -0,0 +1,34 @@ +# 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 SkillMeta(UniversalBaseModel): + name: str + description: str + tags: typing.List[str] + latest_version: typing_extensions.Annotated[ + int, FieldMetadata(alias="latestVersion") + ] + latest_version_created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="latestVersionCreatedAt") + ] + latest_version_last_updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="latestVersionLastUpdatedAt") + ] + production_version: typing_extensions.Annotated[ + typing.Optional[int], FieldMetadata(alias="productionVersion") + ] = pydantic.Field(default=None) + """ + Version assigned the production label, or null when no version is assigned. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_meta_list_response.py b/langfuse/api/skills/types/skill_meta_list_response.py new file mode 100644 index 000000000..f2b5da4ab --- /dev/null +++ b/langfuse/api/skills/types/skill_meta_list_response.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 .skill_list_pagination import SkillListPagination +from .skill_meta import SkillMeta + + +class SkillMetaListResponse(UniversalBaseModel): + data: typing.List[SkillMeta] + meta: SkillListPagination + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_version.py b/langfuse/api/skills/types/skill_version.py new file mode 100644 index 000000000..ff40643e4 --- /dev/null +++ b/langfuse/api/skills/types/skill_version.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import UniversalBaseModel +from ...core.serialization import FieldMetadata +from .skill_file import SkillFile + + +class SkillVersion(UniversalBaseModel): + id: str + created_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="createdAt") + ] + updated_at: typing_extensions.Annotated[ + dt.datetime, FieldMetadata(alias="updatedAt") + ] + project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] + created_by: typing_extensions.Annotated[str, FieldMetadata(alias="createdBy")] + name: str + description: str + frontmatter: typing.Any + version: int + tags: typing.List[str] + labels: typing.List[str] + commit_message: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="commitMessage") + ] = None + files: typing.List[SkillFile] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_version_file_create_input.py b/langfuse/api/skills/types/skill_version_file_create_input.py new file mode 100644 index 000000000..495002d8c --- /dev/null +++ b/langfuse/api/skills/types/skill_version_file_create_input.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .skill_version_file_input import SkillVersionFileInput +from .skill_version_file_reference import SkillVersionFileReference + +SkillVersionFileCreateInput = typing.Union[ + SkillVersionFileInput, SkillVersionFileReference +] diff --git a/langfuse/api/skills/types/skill_version_file_input.py b/langfuse/api/skills/types/skill_version_file_input.py new file mode 100644 index 000000000..8b4fade29 --- /dev/null +++ b/langfuse/api/skills/types/skill_version_file_input.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class SkillVersionFileInput(UniversalBaseModel): + path: str + content: str = pydantic.Field() + """ + UTF-8 text content. The server computes the hash and byte length. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/skill_version_file_reference.py b/langfuse/api/skills/types/skill_version_file_reference.py new file mode 100644 index 000000000..5d2cd7a7b --- /dev/null +++ b/langfuse/api/skills/types/skill_version_file_reference.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 + + +class SkillVersionFileReference(UniversalBaseModel): + path: str + sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] = ( + pydantic.Field() + ) + """ + Canonical base64-encoded SHA-256 from a skill file manifest. The content must already exist in this project. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/update_skill_labels_request.py b/langfuse/api/skills/types/update_skill_labels_request.py new file mode 100644 index 000000000..2b6dc9286 --- /dev/null +++ b/langfuse/api/skills/types/update_skill_labels_request.py @@ -0,0 +1,14 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import UniversalBaseModel + + +class UpdateSkillLabelsRequest(UniversalBaseModel): + labels: typing.List[str] + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/langfuse/api/skills/types/update_skill_request.py b/langfuse/api/skills/types/update_skill_request.py new file mode 100644 index 000000000..9d5cce65a --- /dev/null +++ b/langfuse/api/skills/types/update_skill_request.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 + + +class UpdateSkillRequest(UniversalBaseModel): + tags: typing.List[str] = pydantic.Field() + """ + The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. + """ + + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) diff --git a/tests/unit/test_api_skills.py b/tests/unit/test_api_skills.py new file mode 100644 index 000000000..1e1592ed1 --- /dev/null +++ b/tests/unit/test_api_skills.py @@ -0,0 +1,86 @@ +"""The generated skills API can request inline contents without requiring them.""" + +import httpx +import pytest + +from langfuse.api import AsyncLangfuseAPI, LangfuseAPI, SkillVersion + + +@pytest.fixture +def skill_response(): + return { + "id": "skill-1", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + "projectId": "project-1", + "createdBy": "user-1", + "name": "refund-policy", + "description": "Handle refunds", + "frontmatter": {}, + "version": 2, + "tags": [], + "labels": ["production"], + "files": [ + { + "id": "file-1", + "path": "SKILL.md", + "blobId": "blob-1", + "sha256Hash": "hash", + "contentType": "text/markdown", + "contentLength": 9, + } + ], + } + + +def _respond(skill_response, include_content): + def handle(request): + assert request.url.path == "/api/public/skills/refund-policy" + if include_content is None: + assert "includeContent" not in request.url.params + else: + assert request.url.params["includeContent"] == str(include_content).lower() + if include_content: + skill_response["files"][0]["content"] = "# Refunds" + return httpx.Response(200, json=skill_response) + + return handle + + +@pytest.mark.parametrize("include_content", [None, False, True]) +def test_sync_get_skill_inline_content(skill_response, include_content): + with httpx.Client( + transport=httpx.MockTransport(_respond(skill_response, include_content)) + ) as http_client: + api = LangfuseAPI(base_url="https://langfuse.test", httpx_client=http_client) + skill = api.skills.get("refund-policy", include_content=include_content) + + assert isinstance(skill, SkillVersion) + assert skill.files[0].content == ("# Refunds" if include_content else None) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("include_content", [None, False, True]) +async def test_async_get_skill_inline_content(skill_response, include_content): + async with httpx.AsyncClient( + transport=httpx.MockTransport(_respond(skill_response, include_content)) + ) as http_client: + api = AsyncLangfuseAPI( + base_url="https://langfuse.test", httpx_client=http_client + ) + skill = await api.skills.get("refund-policy", include_content=include_content) + + assert isinstance(skill, SkillVersion) + assert skill.files[0].content == ("# Refunds" if include_content else None) + + +def test_unstable_skills_remains_available(skill_response): + def handle(request): + assert request.url.path == "/api/public/unstable/skills/refund-policy" + return httpx.Response(200, json=skill_response) + + with httpx.Client(transport=httpx.MockTransport(handle)) as http_client: + api = LangfuseAPI(base_url="https://langfuse.test", httpx_client=http_client) + skill = api.unstable.skills.get("refund-policy") + + assert skill.name == "refund-policy" From 8d28a50c3ccb600c57dbec549dda676619fad72c Mon Sep 17 00:00:00 2001 From: Tobias Klesel Date: Wed, 28 Oct 2026 15:02:49 +0100 Subject: [PATCH 2/3] fix(api): complete generated skills namespace migration --- langfuse/api/unstable/__init__.py | 48 +- langfuse/api/unstable/client.py | 19 - langfuse/api/unstable/skills/__init__.py | 85 - langfuse/api/unstable/skills/client.py | 808 -------- langfuse/api/unstable/skills/raw_client.py | 1645 ----------------- .../api/unstable/skills/types/__init__.py | 83 - .../types/create_skill_version_request.py | 20 - .../types/delete_skill_version_response.py | 14 - .../api/unstable/skills/types/skill_file.py | 23 - .../skills/types/skill_file_content.py | 17 - .../types/skill_file_contents_response.py | 15 - .../skills/types/skill_list_pagination.py | 16 - .../api/unstable/skills/types/skill_meta.py | 34 - .../skills/types/skill_meta_list_response.py | 17 - .../unstable/skills/types/skill_version.py | 36 - .../types/skill_version_file_create_input.py | 10 - .../skills/types/skill_version_file_input.py | 18 - .../types/skill_version_file_reference.py | 22 - .../types/update_skill_labels_request.py | 14 - .../skills/types/update_skill_request.py | 17 - tests/unit/test_api_skills.py | 12 - 21 files changed, 1 insertion(+), 2972 deletions(-) delete mode 100644 langfuse/api/unstable/skills/__init__.py delete mode 100644 langfuse/api/unstable/skills/client.py delete mode 100644 langfuse/api/unstable/skills/raw_client.py delete mode 100644 langfuse/api/unstable/skills/types/__init__.py delete mode 100644 langfuse/api/unstable/skills/types/create_skill_version_request.py delete mode 100644 langfuse/api/unstable/skills/types/delete_skill_version_response.py delete mode 100644 langfuse/api/unstable/skills/types/skill_file.py delete mode 100644 langfuse/api/unstable/skills/types/skill_file_content.py delete mode 100644 langfuse/api/unstable/skills/types/skill_file_contents_response.py delete mode 100644 langfuse/api/unstable/skills/types/skill_list_pagination.py delete mode 100644 langfuse/api/unstable/skills/types/skill_meta.py delete mode 100644 langfuse/api/unstable/skills/types/skill_meta_list_response.py delete mode 100644 langfuse/api/unstable/skills/types/skill_version.py delete mode 100644 langfuse/api/unstable/skills/types/skill_version_file_create_input.py delete mode 100644 langfuse/api/unstable/skills/types/skill_version_file_input.py delete mode 100644 langfuse/api/unstable/skills/types/skill_version_file_reference.py delete mode 100644 langfuse/api/unstable/skills/types/update_skill_labels_request.py delete mode 100644 langfuse/api/unstable/skills/types/update_skill_request.py diff --git a/langfuse/api/unstable/__init__.py b/langfuse/api/unstable/__init__.py index 331154d29..2a249200a 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 from .dashboard_widgets import ( CreateDashboardWidgetRequest, DashboardWidget, @@ -59,22 +59,6 @@ UpdateDashboardRequest, WidgetPlacement, ) - from .skills import ( - CreateSkillVersionRequest, - DeleteSkillVersionResponse, - SkillFile, - SkillFileContent, - SkillFileContentsResponse, - SkillListPagination, - SkillMeta, - SkillMetaListResponse, - SkillVersion, - SkillVersionFileCreateInput, - SkillVersionFileInput, - SkillVersionFileReference, - UpdateSkillLabelsRequest, - UpdateSkillRequest, - ) _dynamic_imports: typing.Dict[str, str] = { "AccessDeniedError": ".errors", "BadRequestError": ".errors", @@ -85,7 +69,6 @@ "CreateDashboardRequest": ".dashboards", "CreateDashboardWidgetRequest": ".dashboard_widgets", "CreatePresetPlacement": ".dashboards", - "CreateSkillVersionRequest": ".skills", "CreateWidgetPlacement": ".dashboards", "Dashboard": ".dashboards", "DashboardDefinition": ".dashboards", @@ -109,7 +92,6 @@ "DeleteDashboardPlacementResponse": ".dashboards", "DeleteDashboardResponse": ".dashboards", "DeleteDashboardWidgetResponse": ".dashboard_widgets", - "DeleteSkillVersionResponse": ".skills", "InternalServerError": ".errors", "MethodNotAllowedError": ".errors", "NotFoundError": ".errors", @@ -118,28 +100,15 @@ "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", "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", } @@ -180,7 +149,6 @@ def __dir__(): "CreateDashboardRequest", "CreateDashboardWidgetRequest", "CreatePresetPlacement", - "CreateSkillVersionRequest", "CreateWidgetPlacement", "Dashboard", "DashboardDefinition", @@ -204,7 +172,6 @@ def __dir__(): "DeleteDashboardPlacementResponse", "DeleteDashboardResponse", "DeleteDashboardWidgetResponse", - "DeleteSkillVersionResponse", "InternalServerError", "MethodNotAllowedError", "NotFoundError", @@ -213,26 +180,13 @@ def __dir__(): "PublicApiErrorCode", "PublicApiErrorDetails", "PublicApiValidationIssue", - "SkillFile", - "SkillFileContent", - "SkillFileContentsResponse", - "SkillListPagination", - "SkillMeta", - "SkillMetaListResponse", - "SkillVersion", - "SkillVersionFileCreateInput", - "SkillVersionFileInput", - "SkillVersionFileReference", "TooManyRequestsError", "UnauthorizedError", "UpdateDashboardPlacementRequest", "UpdateDashboardRequest", "UpdateDashboardWidgetRequest", - "UpdateSkillLabelsRequest", - "UpdateSkillRequest", "WidgetPlacement", "dashboard_widgets", "dashboards", "errors", - "skills", ] diff --git a/langfuse/api/unstable/client.py b/langfuse/api/unstable/client.py index 3cc2a9298..23050bc80 100644 --- a/langfuse/api/unstable/client.py +++ b/langfuse/api/unstable/client.py @@ -13,7 +13,6 @@ DashboardWidgetsClient, ) from .dashboards.client import AsyncDashboardsClient, DashboardsClient - from .skills.client import AsyncSkillsClient, SkillsClient class UnstableClient: @@ -22,7 +21,6 @@ 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 @property def with_raw_response(self) -> RawUnstableClient: @@ -53,14 +51,6 @@ def dashboards(self): self._dashboards = DashboardsClient(client_wrapper=self._client_wrapper) return self._dashboards - @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 - class AsyncUnstableClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -68,7 +58,6 @@ 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 @property def with_raw_response(self) -> AsyncRawUnstableClient: @@ -100,11 +89,3 @@ def dashboards(self): client_wrapper=self._client_wrapper ) return self._dashboards - - @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 diff --git a/langfuse/api/unstable/skills/__init__.py b/langfuse/api/unstable/skills/__init__.py deleted file mode 100644 index 710cf53a6..000000000 --- a/langfuse/api/unstable/skills/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# 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 ( - CreateSkillVersionRequest, - DeleteSkillVersionResponse, - SkillFile, - SkillFileContent, - SkillFileContentsResponse, - SkillListPagination, - SkillMeta, - SkillMetaListResponse, - SkillVersion, - SkillVersionFileCreateInput, - SkillVersionFileInput, - SkillVersionFileReference, - UpdateSkillLabelsRequest, - UpdateSkillRequest, - ) -_dynamic_imports: typing.Dict[str, str] = { - "CreateSkillVersionRequest": ".types", - "DeleteSkillVersionResponse": ".types", - "SkillFile": ".types", - "SkillFileContent": ".types", - "SkillFileContentsResponse": ".types", - "SkillListPagination": ".types", - "SkillMeta": ".types", - "SkillMetaListResponse": ".types", - "SkillVersion": ".types", - "SkillVersionFileCreateInput": ".types", - "SkillVersionFileInput": ".types", - "SkillVersionFileReference": ".types", - "UpdateSkillLabelsRequest": ".types", - "UpdateSkillRequest": ".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__ = [ - "CreateSkillVersionRequest", - "DeleteSkillVersionResponse", - "SkillFile", - "SkillFileContent", - "SkillFileContentsResponse", - "SkillListPagination", - "SkillMeta", - "SkillMetaListResponse", - "SkillVersion", - "SkillVersionFileCreateInput", - "SkillVersionFileInput", - "SkillVersionFileReference", - "UpdateSkillLabelsRequest", - "UpdateSkillRequest", -] diff --git a/langfuse/api/unstable/skills/client.py b/langfuse/api/unstable/skills/client.py deleted file mode 100644 index ffbd4899e..000000000 --- a/langfuse/api/unstable/skills/client.py +++ /dev/null @@ -1,808 +0,0 @@ -# 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 AsyncRawSkillsClient, RawSkillsClient -from .types.delete_skill_version_response import DeleteSkillVersionResponse -from .types.skill_file_contents_response import SkillFileContentsResponse -from .types.skill_meta_list_response import SkillMetaListResponse -from .types.skill_version import SkillVersion -from .types.skill_version_file_create_input import SkillVersionFileCreateInput - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class SkillsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawSkillsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawSkillsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawSkillsClient - """ - return self._raw_client - - def list( - self, - *, - name: typing.Optional[str] = None, - search: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillMetaListResponse: - """ - List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. - - Parameters - ---------- - name : typing.Optional[str] - Filter by exact skill name. - - search : typing.Optional[str] - Case-insensitive search across skill names and latest-version descriptions. - - tag : typing.Optional[str] - Filter by a shared skill tag. - - page : typing.Optional[int] - - limit : typing.Optional[int] - - from_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated at or after this timestamp. - - to_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated before this timestamp. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillMetaListResponse - - 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.skills.list() - """ - _response = self._raw_client.list( - name=name, - search=search, - tag=tag, - page=page, - limit=limit, - from_updated_at=from_updated_at, - to_updated_at=to_updated_at, - request_options=request_options, - ) - return _response.data - - def create_version( - self, - *, - files: typing.Sequence[SkillVersionFileCreateInput], - commit_message: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Create skill version - - Parameters - ---------- - files : typing.Sequence[SkillVersionFileCreateInput] - - commit_message : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - Examples - -------- - from langfuse import LangfuseAPI - from langfuse.unstable.skills import SkillVersionFileInput - - 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.skills.create_version( - files=[ - SkillVersionFileInput( - path="path", - content="content", - ), - SkillVersionFileInput( - path="path", - content="content", - ), - ], - ) - """ - _response = self._raw_client.create_version( - files=files, commit_message=commit_message, request_options=request_options - ) - return _response.data - - def get( - self, - skill_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = 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. - - Parameters - ---------- - skill_name : str - - version : typing.Optional[int] - - label : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.get( - skill_name="skillName", - ) - """ - _response = self._raw_client.get( - skill_name, version=version, label=label, request_options=request_options - ) - return _response.data - - def update( - self, - skill_name: str, - *, - tags: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. - - Parameters - ---------- - skill_name : str - - tags : typing.Sequence[str] - The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.update( - skill_name="skillName", - tags=["tags", "tags"], - ) - """ - _response = self._raw_client.update( - skill_name, tags=tags, request_options=request_options - ) - return _response.data - - def get_file_contents( - self, - *, - sha256hashes: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillFileContentsResponse: - """ - Read a batch of text contents by canonical base64-encoded SHA-256 hashes. - - Parameters - ---------- - sha256hashes : str - Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillFileContentsResponse - - 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.skills.get_file_contents( - sha256hashes="sha256Hashes", - ) - """ - _response = self._raw_client.get_file_contents( - sha256hashes=sha256hashes, request_options=request_options - ) - return _response.data - - def set_labels( - self, - skill_name: str, - skill_version: int, - *, - labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Replace the labels on a skill version and atomically move them from other versions. - - Parameters - ---------- - skill_name : str - - skill_version : int - - labels : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.set_labels( - skill_name="skillName", - skill_version=1, - labels=["labels", "labels"], - ) - """ - _response = self._raw_client.set_labels( - skill_name, skill_version, labels=labels, request_options=request_options - ) - return _response.data - - def delete_version( - self, - skill_name: str, - skill_version: int, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteSkillVersionResponse: - """ - Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. - - Parameters - ---------- - skill_name : str - - skill_version : int - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteSkillVersionResponse - - 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.skills.delete_version( - skill_name="skillName", - skill_version=1, - ) - """ - _response = self._raw_client.delete_version( - skill_name, skill_version, request_options=request_options - ) - return _response.data - - -class AsyncSkillsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawSkillsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawSkillsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawSkillsClient - """ - return self._raw_client - - async def list( - self, - *, - name: typing.Optional[str] = None, - search: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillMetaListResponse: - """ - List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. - - Parameters - ---------- - name : typing.Optional[str] - Filter by exact skill name. - - search : typing.Optional[str] - Case-insensitive search across skill names and latest-version descriptions. - - tag : typing.Optional[str] - Filter by a shared skill tag. - - page : typing.Optional[int] - - limit : typing.Optional[int] - - from_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated at or after this timestamp. - - to_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated before this timestamp. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillMetaListResponse - - 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.skills.list() - - - asyncio.run(main()) - """ - _response = await self._raw_client.list( - name=name, - search=search, - tag=tag, - page=page, - limit=limit, - from_updated_at=from_updated_at, - to_updated_at=to_updated_at, - request_options=request_options, - ) - return _response.data - - async def create_version( - self, - *, - files: typing.Sequence[SkillVersionFileCreateInput], - commit_message: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Create skill version - - Parameters - ---------- - files : typing.Sequence[SkillVersionFileCreateInput] - - commit_message : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - Examples - -------- - import asyncio - - from langfuse import AsyncLangfuseAPI - from langfuse.unstable.skills import SkillVersionFileInput - - 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.skills.create_version( - files=[ - SkillVersionFileInput( - path="path", - content="content", - ), - SkillVersionFileInput( - path="path", - content="content", - ), - ], - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.create_version( - files=files, commit_message=commit_message, request_options=request_options - ) - return _response.data - - async def get( - self, - skill_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = 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. - - Parameters - ---------- - skill_name : str - - version : typing.Optional[int] - - label : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.get( - skill_name="skillName", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get( - skill_name, version=version, label=label, request_options=request_options - ) - return _response.data - - async def update( - self, - skill_name: str, - *, - tags: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. - - Parameters - ---------- - skill_name : str - - tags : typing.Sequence[str] - The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.update( - skill_name="skillName", - tags=["tags", "tags"], - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.update( - skill_name, tags=tags, request_options=request_options - ) - return _response.data - - async def get_file_contents( - self, - *, - sha256hashes: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillFileContentsResponse: - """ - Read a batch of text contents by canonical base64-encoded SHA-256 hashes. - - Parameters - ---------- - sha256hashes : str - Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillFileContentsResponse - - 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.skills.get_file_contents( - sha256hashes="sha256Hashes", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get_file_contents( - sha256hashes=sha256hashes, request_options=request_options - ) - return _response.data - - async def set_labels( - self, - skill_name: str, - skill_version: int, - *, - labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> SkillVersion: - """ - Replace the labels on a skill version and atomically move them from other versions. - - Parameters - ---------- - skill_name : str - - skill_version : int - - labels : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SkillVersion - - 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.skills.set_labels( - skill_name="skillName", - skill_version=1, - labels=["labels", "labels"], - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.set_labels( - skill_name, skill_version, labels=labels, request_options=request_options - ) - return _response.data - - async def delete_version( - self, - skill_name: str, - skill_version: int, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> DeleteSkillVersionResponse: - """ - Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. - - Parameters - ---------- - skill_name : str - - skill_version : int - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - DeleteSkillVersionResponse - - 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.skills.delete_version( - skill_name="skillName", - skill_version=1, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.delete_version( - skill_name, skill_version, request_options=request_options - ) - return _response.data diff --git a/langfuse/api/unstable/skills/raw_client.py b/langfuse/api/unstable/skills/raw_client.py deleted file mode 100644 index 152cc2781..000000000 --- a/langfuse/api/unstable/skills/raw_client.py +++ /dev/null @@ -1,1645 +0,0 @@ -# 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 -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 -from .types.skill_version import SkillVersion -from .types.skill_version_file_create_input import SkillVersionFileCreateInput - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawSkillsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - *, - name: typing.Optional[str] = None, - search: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SkillMetaListResponse]: - """ - List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. - - Parameters - ---------- - name : typing.Optional[str] - Filter by exact skill name. - - search : typing.Optional[str] - Case-insensitive search across skill names and latest-version descriptions. - - tag : typing.Optional[str] - Filter by a shared skill tag. - - page : typing.Optional[int] - - limit : typing.Optional[int] - - from_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated at or after this timestamp. - - to_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated before this timestamp. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillMetaListResponse] - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", - method="GET", - params={ - "name": name, - "search": search, - "tag": tag, - "page": page, - "limit": limit, - "fromUpdatedAt": serialize_datetime(from_updated_at) - if from_updated_at is not None - else None, - "toUpdatedAt": serialize_datetime(to_updated_at) - if to_updated_at is not None - else None, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillMetaListResponse, - parse_obj_as( - type_=SkillMetaListResponse, # 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 create_version( - self, - *, - files: typing.Sequence[SkillVersionFileCreateInput], - commit_message: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SkillVersion]: - """ - Create skill version - - Parameters - ---------- - files : typing.Sequence[SkillVersionFileCreateInput] - - commit_message : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillVersion] - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", - method="POST", - json={ - "files": convert_and_respect_annotation_metadata( - object_=files, - annotation=typing.Sequence[SkillVersionFileCreateInput], - direction="write", - ), - "commitMessage": commit_message, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 get( - self, - skill_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = 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. - - Parameters - ---------- - skill_name : str - - version : typing.Optional[int] - - label : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillVersion] - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", - method="GET", - params={ - "version": version, - "label": label, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 update( - self, - skill_name: str, - *, - tags: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SkillVersion]: - """ - Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. - - Parameters - ---------- - skill_name : str - - tags : typing.Sequence[str] - The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillVersion] - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", - method="PATCH", - json={ - "tags": tags, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 get_file_contents( - self, - *, - sha256hashes: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SkillFileContentsResponse]: - """ - Read a batch of text contents by canonical base64-encoded SHA-256 hashes. - - Parameters - ---------- - sha256hashes : str - Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillFileContentsResponse] - """ - _response = self._client_wrapper.httpx_client.request( - "api/public/unstable/skills/files/content", - method="GET", - params={ - "sha256Hashes": sha256hashes, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillFileContentsResponse, - parse_obj_as( - type_=SkillFileContentsResponse, # 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 set_labels( - self, - skill_name: str, - skill_version: int, - *, - labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SkillVersion]: - """ - Replace the labels on a skill version and atomically move them from other versions. - - Parameters - ---------- - skill_name : str - - skill_version : int - - labels : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SkillVersion] - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", - method="PATCH", - json={ - "labels": labels, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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_version( - self, - skill_name: str, - skill_version: int, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[DeleteSkillVersionResponse]: - """ - Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. - - Parameters - ---------- - skill_name : str - - skill_version : int - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[DeleteSkillVersionResponse] - """ - _response = self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - DeleteSkillVersionResponse, - parse_obj_as( - type_=DeleteSkillVersionResponse, # 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 AsyncRawSkillsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - *, - name: typing.Optional[str] = None, - search: typing.Optional[str] = None, - tag: typing.Optional[str] = None, - page: typing.Optional[int] = None, - limit: typing.Optional[int] = None, - from_updated_at: typing.Optional[dt.datetime] = None, - to_updated_at: typing.Optional[dt.datetime] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SkillMetaListResponse]: - """ - List skills with metadata and timestamps from their latest version, shared tags, and the version assigned to production when present. - - Parameters - ---------- - name : typing.Optional[str] - Filter by exact skill name. - - search : typing.Optional[str] - Case-insensitive search across skill names and latest-version descriptions. - - tag : typing.Optional[str] - Filter by a shared skill tag. - - page : typing.Optional[int] - - limit : typing.Optional[int] - - from_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated at or after this timestamp. - - to_updated_at : typing.Optional[dt.datetime] - Include skills whose latest version was updated before this timestamp. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillMetaListResponse] - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", - method="GET", - params={ - "name": name, - "search": search, - "tag": tag, - "page": page, - "limit": limit, - "fromUpdatedAt": serialize_datetime(from_updated_at) - if from_updated_at is not None - else None, - "toUpdatedAt": serialize_datetime(to_updated_at) - if to_updated_at is not None - else None, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillMetaListResponse, - parse_obj_as( - type_=SkillMetaListResponse, # 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 create_version( - self, - *, - files: typing.Sequence[SkillVersionFileCreateInput], - commit_message: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SkillVersion]: - """ - Create skill version - - Parameters - ---------- - files : typing.Sequence[SkillVersionFileCreateInput] - - commit_message : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillVersion] - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills", - method="POST", - json={ - "files": convert_and_respect_annotation_metadata( - object_=files, - annotation=typing.Sequence[SkillVersionFileCreateInput], - direction="write", - ), - "commitMessage": commit_message, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 get( - self, - skill_name: str, - *, - version: typing.Optional[int] = None, - label: typing.Optional[str] = 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. - - Parameters - ---------- - skill_name : str - - version : typing.Optional[int] - - label : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillVersion] - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", - method="GET", - params={ - "version": version, - "label": label, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 update( - self, - skill_name: str, - *, - tags: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SkillVersion]: - """ - Replace the shared tags across all versions of a skill. An empty tags list clears all tags. Returns the latest skill version with the updated tags. - - Parameters - ---------- - skill_name : str - - tags : typing.Sequence[str] - The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillVersion] - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}", - method="PATCH", - json={ - "tags": tags, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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 get_file_contents( - self, - *, - sha256hashes: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SkillFileContentsResponse]: - """ - Read a batch of text contents by canonical base64-encoded SHA-256 hashes. - - Parameters - ---------- - sha256hashes : str - Comma-separated list of one to 50 canonical base64-encoded SHA-256 hashes from skill file manifests. URL-encode the value, including +, /, and = characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillFileContentsResponse] - """ - _response = await self._client_wrapper.httpx_client.request( - "api/public/unstable/skills/files/content", - method="GET", - params={ - "sha256Hashes": sha256hashes, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillFileContentsResponse, - parse_obj_as( - type_=SkillFileContentsResponse, # 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 set_labels( - self, - skill_name: str, - skill_version: int, - *, - labels: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SkillVersion]: - """ - Replace the labels on a skill version and atomically move them from other versions. - - Parameters - ---------- - skill_name : str - - skill_version : int - - labels : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SkillVersion] - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", - method="PATCH", - json={ - "labels": labels, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SkillVersion, - parse_obj_as( - type_=SkillVersion, # 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_version( - self, - skill_name: str, - skill_version: int, - *, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[DeleteSkillVersionResponse]: - """ - Delete one immutable skill version. Unreferenced blobs are retained for asynchronous cleanup. - - Parameters - ---------- - skill_name : str - - skill_version : int - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[DeleteSkillVersionResponse] - """ - _response = await self._client_wrapper.httpx_client.request( - f"api/public/unstable/skills/{jsonable_encoder(skill_name)}/versions/{jsonable_encoder(skill_version)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - DeleteSkillVersionResponse, - parse_obj_as( - type_=DeleteSkillVersionResponse, # 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, - ) diff --git a/langfuse/api/unstable/skills/types/__init__.py b/langfuse/api/unstable/skills/types/__init__.py deleted file mode 100644 index bafccc040..000000000 --- a/langfuse/api/unstable/skills/types/__init__.py +++ /dev/null @@ -1,83 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .create_skill_version_request import CreateSkillVersionRequest - from .delete_skill_version_response import DeleteSkillVersionResponse - from .skill_file import SkillFile - from .skill_file_content import SkillFileContent - from .skill_file_contents_response import SkillFileContentsResponse - from .skill_list_pagination import SkillListPagination - from .skill_meta import SkillMeta - from .skill_meta_list_response import SkillMetaListResponse - from .skill_version import SkillVersion - from .skill_version_file_create_input import SkillVersionFileCreateInput - from .skill_version_file_input import SkillVersionFileInput - from .skill_version_file_reference import SkillVersionFileReference - from .update_skill_labels_request import UpdateSkillLabelsRequest - from .update_skill_request import UpdateSkillRequest -_dynamic_imports: typing.Dict[str, str] = { - "CreateSkillVersionRequest": ".create_skill_version_request", - "DeleteSkillVersionResponse": ".delete_skill_version_response", - "SkillFile": ".skill_file", - "SkillFileContent": ".skill_file_content", - "SkillFileContentsResponse": ".skill_file_contents_response", - "SkillListPagination": ".skill_list_pagination", - "SkillMeta": ".skill_meta", - "SkillMetaListResponse": ".skill_meta_list_response", - "SkillVersion": ".skill_version", - "SkillVersionFileCreateInput": ".skill_version_file_create_input", - "SkillVersionFileInput": ".skill_version_file_input", - "SkillVersionFileReference": ".skill_version_file_reference", - "UpdateSkillLabelsRequest": ".update_skill_labels_request", - "UpdateSkillRequest": ".update_skill_request", -} - - -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__ = [ - "CreateSkillVersionRequest", - "DeleteSkillVersionResponse", - "SkillFile", - "SkillFileContent", - "SkillFileContentsResponse", - "SkillListPagination", - "SkillMeta", - "SkillMetaListResponse", - "SkillVersion", - "SkillVersionFileCreateInput", - "SkillVersionFileInput", - "SkillVersionFileReference", - "UpdateSkillLabelsRequest", - "UpdateSkillRequest", -] diff --git a/langfuse/api/unstable/skills/types/create_skill_version_request.py b/langfuse/api/unstable/skills/types/create_skill_version_request.py deleted file mode 100644 index cf7c073ca..000000000 --- a/langfuse/api/unstable/skills/types/create_skill_version_request.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 .skill_version_file_create_input import SkillVersionFileCreateInput - - -class CreateSkillVersionRequest(UniversalBaseModel): - files: typing.List[SkillVersionFileCreateInput] - commit_message: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="commitMessage") - ] = None - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/delete_skill_version_response.py b/langfuse/api/unstable/skills/types/delete_skill_version_response.py deleted file mode 100644 index cf92c2867..000000000 --- a/langfuse/api/unstable/skills/types/delete_skill_version_response.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel - - -class DeleteSkillVersionResponse(UniversalBaseModel): - deleted: bool - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_file.py b/langfuse/api/unstable/skills/types/skill_file.py deleted file mode 100644 index 683b13679..000000000 --- a/langfuse/api/unstable/skills/types/skill_file.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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 SkillFile(UniversalBaseModel): - id: str - path: str - blob_id: typing_extensions.Annotated[str, FieldMetadata(alias="blobId")] - sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] - content_type: typing_extensions.Annotated[str, FieldMetadata(alias="contentType")] - content_length: typing_extensions.Annotated[ - int, FieldMetadata(alias="contentLength") - ] - - 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/unstable/skills/types/skill_file_content.py deleted file mode 100644 index ce324848d..000000000 --- a/langfuse/api/unstable/skills/types/skill_file_content.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 SkillFileContent(UniversalBaseModel): - sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] - content: str - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_file_contents_response.py b/langfuse/api/unstable/skills/types/skill_file_contents_response.py deleted file mode 100644 index 85bb7e2fa..000000000 --- a/langfuse/api/unstable/skills/types/skill_file_contents_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel -from .skill_file_content import SkillFileContent - - -class SkillFileContentsResponse(UniversalBaseModel): - data: typing.List[SkillFileContent] - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_list_pagination.py b/langfuse/api/unstable/skills/types/skill_list_pagination.py deleted file mode 100644 index f7ae797de..000000000 --- a/langfuse/api/unstable/skills/types/skill_list_pagination.py +++ /dev/null @@ -1,16 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ....core.serialization import FieldMetadata -from ....utils.pagination.types.meta_response import MetaResponse - - -class SkillListPagination(MetaResponse): - has_next_page: typing_extensions.Annotated[bool, FieldMetadata(alias="hasNextPage")] - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_meta.py b/langfuse/api/unstable/skills/types/skill_meta.py deleted file mode 100644 index a391972df..000000000 --- a/langfuse/api/unstable/skills/types/skill_meta.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 SkillMeta(UniversalBaseModel): - name: str - description: str - tags: typing.List[str] - latest_version: typing_extensions.Annotated[ - int, FieldMetadata(alias="latestVersion") - ] - latest_version_created_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="latestVersionCreatedAt") - ] - latest_version_last_updated_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="latestVersionLastUpdatedAt") - ] - production_version: typing_extensions.Annotated[ - typing.Optional[int], FieldMetadata(alias="productionVersion") - ] = pydantic.Field(default=None) - """ - Version assigned the production label, or null when no version is assigned. - """ - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_meta_list_response.py b/langfuse/api/unstable/skills/types/skill_meta_list_response.py deleted file mode 100644 index a097e1bc0..000000000 --- a/langfuse/api/unstable/skills/types/skill_meta_list_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel -from .skill_list_pagination import SkillListPagination -from .skill_meta import SkillMeta - - -class SkillMetaListResponse(UniversalBaseModel): - data: typing.List[SkillMeta] - meta: SkillListPagination - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_version.py b/langfuse/api/unstable/skills/types/skill_version.py deleted file mode 100644 index c3486c23d..000000000 --- a/langfuse/api/unstable/skills/types/skill_version.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -import pydantic -import typing_extensions -from ....core.pydantic_utilities import UniversalBaseModel -from ....core.serialization import FieldMetadata -from .skill_file import SkillFile - - -class SkillVersion(UniversalBaseModel): - id: str - created_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="createdAt") - ] - updated_at: typing_extensions.Annotated[ - dt.datetime, FieldMetadata(alias="updatedAt") - ] - project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")] - created_by: typing_extensions.Annotated[str, FieldMetadata(alias="createdBy")] - name: str - description: str - frontmatter: typing.Any - version: int - tags: typing.List[str] - labels: typing.List[str] - commit_message: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="commitMessage") - ] = None - files: typing.List[SkillFile] - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_version_file_create_input.py b/langfuse/api/unstable/skills/types/skill_version_file_create_input.py deleted file mode 100644 index 495002d8c..000000000 --- a/langfuse/api/unstable/skills/types/skill_version_file_create_input.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .skill_version_file_input import SkillVersionFileInput -from .skill_version_file_reference import SkillVersionFileReference - -SkillVersionFileCreateInput = typing.Union[ - SkillVersionFileInput, SkillVersionFileReference -] diff --git a/langfuse/api/unstable/skills/types/skill_version_file_input.py b/langfuse/api/unstable/skills/types/skill_version_file_input.py deleted file mode 100644 index c6f20d51e..000000000 --- a/langfuse/api/unstable/skills/types/skill_version_file_input.py +++ /dev/null @@ -1,18 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel - - -class SkillVersionFileInput(UniversalBaseModel): - path: str - content: str = pydantic.Field() - """ - UTF-8 text content. The server computes the hash and byte length. - """ - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/skill_version_file_reference.py b/langfuse/api/unstable/skills/types/skill_version_file_reference.py deleted file mode 100644 index 4960d9e90..000000000 --- a/langfuse/api/unstable/skills/types/skill_version_file_reference.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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 SkillVersionFileReference(UniversalBaseModel): - path: str - sha256hash: typing_extensions.Annotated[str, FieldMetadata(alias="sha256Hash")] = ( - pydantic.Field() - ) - """ - Canonical base64-encoded SHA-256 from a skill file manifest. The content must already exist in this project. - """ - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/update_skill_labels_request.py b/langfuse/api/unstable/skills/types/update_skill_labels_request.py deleted file mode 100644 index c1931a1e4..000000000 --- a/langfuse/api/unstable/skills/types/update_skill_labels_request.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel - - -class UpdateSkillLabelsRequest(UniversalBaseModel): - labels: typing.List[str] - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/langfuse/api/unstable/skills/types/update_skill_request.py b/langfuse/api/unstable/skills/types/update_skill_request.py deleted file mode 100644 index 3c211b921..000000000 --- a/langfuse/api/unstable/skills/types/update_skill_request.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ....core.pydantic_utilities import UniversalBaseModel - - -class UpdateSkillRequest(UniversalBaseModel): - tags: typing.List[str] = pydantic.Field() - """ - The complete set of tags to apply to every version of the skill. Pass an empty list to clear all tags. - """ - - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( - extra="allow", frozen=True - ) diff --git a/tests/unit/test_api_skills.py b/tests/unit/test_api_skills.py index 1e1592ed1..eab69d2ab 100644 --- a/tests/unit/test_api_skills.py +++ b/tests/unit/test_api_skills.py @@ -72,15 +72,3 @@ async def test_async_get_skill_inline_content(skill_response, include_content): assert isinstance(skill, SkillVersion) assert skill.files[0].content == ("# Refunds" if include_content else None) - - -def test_unstable_skills_remains_available(skill_response): - def handle(request): - assert request.url.path == "/api/public/unstable/skills/refund-policy" - return httpx.Response(200, json=skill_response) - - with httpx.Client(transport=httpx.MockTransport(handle)) as http_client: - api = LangfuseAPI(base_url="https://langfuse.test", httpx_client=http_client) - skill = api.unstable.skills.get("refund-policy") - - assert skill.name == "refund-policy" From bbcd7bc193376498b1df4c7a364319d84fbd71d4 Mon Sep 17 00:00:00 2001 From: Tobias Klesel Date: Wed, 28 Oct 2026 15:06:03 +0100 Subject: [PATCH 3/3] test: remove extra skills API tests --- tests/unit/test_api_skills.py | 74 ----------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 tests/unit/test_api_skills.py diff --git a/tests/unit/test_api_skills.py b/tests/unit/test_api_skills.py deleted file mode 100644 index eab69d2ab..000000000 --- a/tests/unit/test_api_skills.py +++ /dev/null @@ -1,74 +0,0 @@ -"""The generated skills API can request inline contents without requiring them.""" - -import httpx -import pytest - -from langfuse.api import AsyncLangfuseAPI, LangfuseAPI, SkillVersion - - -@pytest.fixture -def skill_response(): - return { - "id": "skill-1", - "createdAt": "2026-01-01T00:00:00Z", - "updatedAt": "2026-01-01T00:00:00Z", - "projectId": "project-1", - "createdBy": "user-1", - "name": "refund-policy", - "description": "Handle refunds", - "frontmatter": {}, - "version": 2, - "tags": [], - "labels": ["production"], - "files": [ - { - "id": "file-1", - "path": "SKILL.md", - "blobId": "blob-1", - "sha256Hash": "hash", - "contentType": "text/markdown", - "contentLength": 9, - } - ], - } - - -def _respond(skill_response, include_content): - def handle(request): - assert request.url.path == "/api/public/skills/refund-policy" - if include_content is None: - assert "includeContent" not in request.url.params - else: - assert request.url.params["includeContent"] == str(include_content).lower() - if include_content: - skill_response["files"][0]["content"] = "# Refunds" - return httpx.Response(200, json=skill_response) - - return handle - - -@pytest.mark.parametrize("include_content", [None, False, True]) -def test_sync_get_skill_inline_content(skill_response, include_content): - with httpx.Client( - transport=httpx.MockTransport(_respond(skill_response, include_content)) - ) as http_client: - api = LangfuseAPI(base_url="https://langfuse.test", httpx_client=http_client) - skill = api.skills.get("refund-policy", include_content=include_content) - - assert isinstance(skill, SkillVersion) - assert skill.files[0].content == ("# Refunds" if include_content else None) - - -@pytest.mark.asyncio -@pytest.mark.parametrize("include_content", [None, False, True]) -async def test_async_get_skill_inline_content(skill_response, include_content): - async with httpx.AsyncClient( - transport=httpx.MockTransport(_respond(skill_response, include_content)) - ) as http_client: - api = AsyncLangfuseAPI( - base_url="https://langfuse.test", httpx_client=http_client - ) - skill = await api.skills.get("refund-policy", include_content=include_content) - - assert isinstance(skill, SkillVersion) - assert skill.files[0].content == ("# Refunds" if include_content else None)